fix(review): align wukong port with upstream behavior and PR #621 review findings

Must-fix: drive permission apply now gates on confirmDangerousAction and
declares confirmation=user_required, matching its help-text promise.

Wukong parity restored: formula-verify --exit-on-error (payload-parsing
exit path) and --targets conflict error, sheet info --include, chat
location/profile message types, search-advanced wukong flag aliases,
and a dedicated drive download-version leaf replacing the removed
polymorphic download --version.

Consistency fixes: transfer-owner --node/--workspace XOR and JSON-aware
dry-run after --yes validation; drive list --versions rejects
--depth/--pattern instead of misleading depth errors; depth BFS resumes
rate-limited folders from the failed page cursor to avoid duplicates;
doc style cover upload honors cmd.Context() and a 20 MiB size cap; chat
user-settings set validates per-item openConversationId and is
risk=medium.

Hardened the skill static audit to scan fenced code blocks and reject
unknown subcommands on group commands, fixing the stale aitable/drive
doc examples it exposed. Added CHANGELOG entry and coverage tests for
all changed statements plus previously untested ported commands.
This commit is contained in:
玉澜 committed 2026-07-29 15:34:59 +08:00
1 parent 6b99685594
commit 9567cfd3d8
35 files changed
+3678 -497

No files matched your search

+4
View File
@@ -6,6 +6,10 @@ The format is inspired by [Keep a Changelog](https://keepachangelog.com/) and th
## [Unreleased] ## [Unreleased]
### Added
- **Wukong internal command sync** (#621) — ports roughly 30 leaf commands from the internal wukong repository into the open-source CLI: Drive gains `star`, `cover`, `revert`, `download-version`, `permission transfer-owner`/`apply-info`/`apply`, and `list --depth`/`--versions`; Doc gains `style cover`/`style background`; Sheet gains the `comment`, `version`, and `formula-verify` command groups plus `info --include`; Chat gains `message update-text-emotion`, the `location`/`profile` message types, and group `user-settings`/`get-mute-config`. Every new leaf is registered in the Agent-visible Runtime Schema.
### Fixed ### Fixed
- **Name→ID resolution kept external contacts** — the shared contact resolver (`chat +dm`, `+broadcast`, …) no longer drops `search_contact_by_key_word` rows that carry only an `openDingTalkId` (external / cross-org contacts have an empty `userId`), so those people are found instead of reported missing or collapsed into a wrong single match; the display name also falls back through `nick`/`showName`/`flowerName`/`staffName`/`userName`. - **Name→ID resolution kept external contacts** — the shared contact resolver (`chat +dm`, `+broadcast`, …) no longer drops `search_contact_by_key_word` rows that carry only an `openDingTalkId` (external / cross-org contacts have an empty `userId`), so those people are found instead of reported missing or collapsed into a wrong single match; the display name also falls back through `nick`/`showName`/`flowerName`/`staffName`/`userName`.
+3 -3
View File
@@ -18667,14 +18667,14 @@
] ]
}, },
"risk": { "risk": {
"value": "low", "value": "medium",
"source": "internal/cli/schema_hints/metadata/chat.json", "source": "internal/cli/schema_hints/metadata/chat.json",
"precedence": "reviewed_explicit", "precedence": "reviewed_explicit",
"resolution": "highest_precedence", "resolution": "highest_precedence",
"review_reason": "dws-tool-metadata/chat marks this tool as reviewed", "review_reason": "dws-tool-metadata/chat marks this tool as reviewed",
"candidates": [ "candidates": [
{ {
"value": "low", "value": "medium",
"source": "internal/cli/schema_hints/metadata/chat.json", "source": "internal/cli/schema_hints/metadata/chat.json",
"precedence": "reviewed_explicit", "precedence": "reviewed_explicit",
"selected": true, "selected": true,
@@ -18707,7 +18707,7 @@
"interface_mode": "composite", "interface_mode": "composite",
"interface_reason": "Reviewed unpinned remote adapter: this executable CLI wrapper calls a remote helper that is absent from the pinned MCP metadata snapshot; no single pinned semantically equivalent interface_ref can represent the command.", "interface_reason": "Reviewed unpinned remote adapter: this executable CLI wrapper calls a remote helper that is absent from the pinned MCP metadata snapshot; no single pinned semantically equivalent interface_ref can represent the command.",
"reviewed": true, "reviewed": true,
"risk": "low", "risk": "medium",
"source_refs": [ "source_refs": [
"CommandRegistry:canonical_path=chat.batch_update_group_chat_settings", "CommandRegistry:canonical_path=chat.batch_update_group_chat_settings",
"cobra-help:dws chat group user-settings set", "cobra-help:dws chat group user-settings set",
+270 -3
View File
@@ -3187,6 +3187,273 @@
"已确认 contentType 非 ALIDOC,需要落盘本地查看时" "已确认 contentType 非 ALIDOC,需要落盘本地查看时"
] ]
}, },
"drive download-version": {
"agent_summary": "下载钉盘普通文件的指定历史版本到本地(两步下载:取签名 URL 后 HTTP GET)",
"agent_summary_source": "dws-agent-selection/drive",
"availability": "available",
"avoid_when": [
"下载最新版本用 drive download",
"在线文档(adoc)历史版本用 doc version 系列命令",
"在线表格(axls)历史版本用 sheet version 系列命令"
],
"confirmation": "not_required",
"effect": "read",
"effect_source": "agent-hint",
"examples": [
"dws drive download-version --node \u003cdentryUuid\u003e --version 3 --output ./report_v3.pdf"
],
"field_provenance": {
"agent_summary": {
"value": "下载钉盘普通文件的指定历史版本到本地(两步下载:取签名 URL 后 HTTP GET)",
"source": "internal/cli/schema_hints/selection/drive.json",
"precedence": "reviewed_explicit",
"resolution": "highest_precedence",
"review_reason": "人工审阅:对照悟空 develop drive download --version 分支实现移植为独立命令;单一 download_file_version 远端调用,参数契约与帮助文档一致。",
"candidates": [
{
"value": "下载钉盘普通文件的指定历史版本到本地(两步下载:取签名 URL 后 HTTP GET)",
"source": "internal/cli/schema_hints/selection/drive.json",
"precedence": "reviewed_explicit",
"selected": true,
"review_reason": "人工审阅:对照悟空 develop drive download --version 分支实现移植为独立命令;单一 download_file_version 远端调用,参数契约与帮助文档一致。"
}
]
},
"availability": {
"value": "available",
"source": "internal/cli/schema_hints/metadata/drive.json",
"precedence": "reviewed_explicit",
"resolution": "highest_precedence",
"review_reason": "dws-tool-metadata/drive marks this tool as reviewed",
"candidates": [
{
"value": "available",
"source": "internal/cli/schema_hints/metadata/drive.json",
"precedence": "reviewed_explicit",
"selected": true,
"review_reason": "dws-tool-metadata/drive marks this tool as reviewed"
}
]
},
"avoid_when": {
"value": [
"下载最新版本用 drive download",
"在线文档(adoc)历史版本用 doc version 系列命令",
"在线表格(axls)历史版本用 sheet version 系列命令"
],
"source": "internal/cli/schema_hints/selection/drive.json",
"precedence": "reviewed_explicit",
"resolution": "highest_precedence",
"review_reason": "人工审阅:对照悟空 develop drive download --version 分支实现移植为独立命令;单一 download_file_version 远端调用,参数契约与帮助文档一致。",
"candidates": [
{
"value": [
"下载最新版本用 drive download",
"在线文档(adoc)历史版本用 doc version 系列命令",
"在线表格(axls)历史版本用 sheet version 系列命令"
],
"source": "internal/cli/schema_hints/selection/drive.json",
"precedence": "reviewed_explicit",
"selected": true,
"review_reason": "人工审阅:对照悟空 develop drive download --version 分支实现移植为独立命令;单一 download_file_version 远端调用,参数契约与帮助文档一致。"
}
]
},
"confirmation": {
"value": "not_required",
"source": "internal/cli/schema_hints/metadata/drive.json",
"precedence": "reviewed_explicit",
"resolution": "highest_precedence",
"review_reason": "dws-tool-metadata/drive marks this tool as reviewed",
"candidates": [
{
"value": "not_required",
"source": "internal/cli/schema_hints/metadata/drive.json",
"precedence": "reviewed_explicit",
"selected": true,
"review_reason": "dws-tool-metadata/drive marks this tool as reviewed"
}
]
},
"effect": {
"value": "read",
"source": "internal/cli/schema_hints/metadata/drive.json",
"precedence": "reviewed_explicit",
"resolution": "highest_precedence",
"review_reason": "dws-tool-metadata/drive marks this tool as reviewed",
"candidates": [
{
"value": "read",
"source": "internal/cli/schema_hints/metadata/drive.json",
"precedence": "reviewed_explicit",
"selected": true,
"review_reason": "dws-tool-metadata/drive marks this tool as reviewed"
}
]
},
"examples": {
"value": [
"dws drive download-version --node \u003cdentryUuid\u003e --version 3 --output ./report_v3.pdf"
],
"source": "internal/cli/schema_hints/selection/drive.json",
"precedence": "reviewed_explicit",
"resolution": "highest_precedence",
"review_reason": "人工审阅:对照悟空 develop drive download --version 分支实现移植为独立命令;单一 download_file_version 远端调用,参数契约与帮助文档一致。",
"candidates": [
{
"value": [
"dws drive download-version --node \u003cdentryUuid\u003e --version 3 --output ./report_v3.pdf"
],
"source": "internal/cli/schema_hints/selection/drive.json",
"precedence": "reviewed_explicit",
"selected": true,
"review_reason": "人工审阅:对照悟空 develop drive download --version 分支实现移植为独立命令;单一 download_file_version 远端调用,参数契约与帮助文档一致。"
}
]
},
"idempotency": {
"value": "idempotent",
"source": "internal/cli/schema_hints/metadata/drive.json",
"precedence": "reviewed_explicit",
"resolution": "highest_precedence",
"review_reason": "dws-tool-metadata/drive marks this tool as reviewed",
"candidates": [
{
"value": "idempotent",
"source": "internal/cli/schema_hints/metadata/drive.json",
"precedence": "reviewed_explicit",
"selected": true,
"review_reason": "dws-tool-metadata/drive marks this tool as reviewed"
}
]
},
"interface_mode": {
"value": "composite",
"source": "internal/cli/schema_hints/metadata/drive.json",
"precedence": "reviewed_explicit",
"resolution": "highest_precedence",
"review_reason": "dws-tool-metadata/drive marks this tool as reviewed",
"candidates": [
{
"value": "composite",
"source": "internal/cli/schema_hints/metadata/drive.json",
"precedence": "reviewed_explicit",
"selected": true,
"review_reason": "dws-tool-metadata/drive marks this tool as reviewed"
}
]
},
"interface_reason": {
"value": "Reviewed unpinned remote adapter: this executable CLI wrapper calls a remote helper that is absent from the pinned MCP metadata snapshot; no single pinned semantically equivalent interface_ref can represent the command.",
"source": "internal/cli/schema_hints/metadata/drive.json",
"precedence": "reviewed_explicit",
"resolution": "highest_precedence",
"review_reason": "dws-tool-metadata/drive marks this tool as reviewed",
"candidates": [
{
"value": "Reviewed unpinned remote adapter: this executable CLI wrapper calls a remote helper that is absent from the pinned MCP metadata snapshot; no single pinned semantically equivalent interface_ref can represent the command.",
"source": "internal/cli/schema_hints/metadata/drive.json",
"precedence": "reviewed_explicit",
"selected": true,
"review_reason": "dws-tool-metadata/drive marks this tool as reviewed"
}
]
},
"interface_ref": {
"value": null,
"source": "internal/cli/schema_hints/metadata/drive.json",
"precedence": "reviewed_explicit",
"resolution": "interface_disposition_matrix",
"review_reason": "final interface mode composite forbids a direct MCP interface_ref",
"candidates": [
{
"value": null,
"source": "internal/cli/schema_hints/metadata/drive.json",
"precedence": "reviewed_explicit",
"selected": true,
"review_reason": "final interface mode composite forbids a direct MCP interface_ref"
}
]
},
"reviewed": {
"value": true,
"source": "internal/cli/schema_hints/metadata/drive.json",
"precedence": "reviewed_explicit",
"resolution": "highest_precedence",
"review_reason": "dws-tool-metadata/drive marks this tool as reviewed",
"candidates": [
{
"value": true,
"source": "internal/cli/schema_hints/metadata/drive.json",
"precedence": "reviewed_explicit",
"selected": true,
"review_reason": "dws-tool-metadata/drive marks this tool as reviewed"
},
{
"value": true,
"source": "internal/cli/schema_hints/selection/drive.json",
"precedence": "reviewed_explicit",
"selected": false,
"review_reason": "人工审阅:对照悟空 develop drive download --version 分支实现移植为独立命令;单一 download_file_version 远端调用,参数契约与帮助文档一致。"
}
]
},
"risk": {
"value": "low",
"source": "internal/cli/schema_hints/metadata/drive.json",
"precedence": "reviewed_explicit",
"resolution": "highest_precedence",
"review_reason": "dws-tool-metadata/drive marks this tool as reviewed",
"candidates": [
{
"value": "low",
"source": "internal/cli/schema_hints/metadata/drive.json",
"precedence": "reviewed_explicit",
"selected": true,
"review_reason": "dws-tool-metadata/drive marks this tool as reviewed"
}
]
},
"use_when": {
"value": [
"用户要下载文件的历史版本/旧版本",
"版本号已通过 drive list --versions 获取"
],
"source": "internal/cli/schema_hints/selection/drive.json",
"precedence": "reviewed_explicit",
"resolution": "highest_precedence",
"review_reason": "人工审阅:对照悟空 develop drive download --version 分支实现移植为独立命令;单一 download_file_version 远端调用,参数契约与帮助文档一致。",
"candidates": [
{
"value": [
"用户要下载文件的历史版本/旧版本",
"版本号已通过 drive list --versions 获取"
],
"source": "internal/cli/schema_hints/selection/drive.json",
"precedence": "reviewed_explicit",
"selected": true,
"review_reason": "人工审阅:对照悟空 develop drive download --version 分支实现移植为独立命令;单一 download_file_version 远端调用,参数契约与帮助文档一致。"
}
]
}
},
"idempotency": "idempotent",
"interface_mode": "composite",
"interface_reason": "Reviewed unpinned remote adapter: this executable CLI wrapper calls a remote helper that is absent from the pinned MCP metadata snapshot; no single pinned semantically equivalent interface_ref can represent the command.",
"reviewed": true,
"risk": "low",
"source_refs": [
"CommandRegistry:canonical_path=drive.download_file_version",
"cobra-help:dws drive download-version",
"internal/cli/schema_hints/metadata/drive.json",
"internal/cli/schema_hints/selection/drive.json",
"wukong-develop:wukong/products/drive.go"
],
"use_when": [
"用户要下载文件的历史版本/旧版本",
"版本号已通过 drive list --versions 获取"
]
},
"drive info": { "drive info": {
"agent_summary": "获取文件元数据信息", "agent_summary": "获取文件元数据信息",
"agent_summary_source": "dws-agent-selection/drive", "agent_summary_source": "dws-agent-selection/drive",
@@ -4839,7 +5106,7 @@
"avoid_when": [ "avoid_when": [
"先用 apply-info 查可申请角色与审批人;未经用户确认不得自行提交" "先用 apply-info 查可申请角色与审批人;未经用户确认不得自行提交"
], ],
"confirmation": "not_required", "confirmation": "user_required",
"effect": "write", "effect": "write",
"effect_source": "agent-hint", "effect_source": "agent-hint",
"examples": [ "examples": [
@@ -4899,14 +5166,14 @@
] ]
}, },
"confirmation": { "confirmation": {
"value": "not_required", "value": "user_required",
"source": "internal/cli/schema_hints/metadata/drive.json", "source": "internal/cli/schema_hints/metadata/drive.json",
"precedence": "reviewed_explicit", "precedence": "reviewed_explicit",
"resolution": "highest_precedence", "resolution": "highest_precedence",
"review_reason": "dws-tool-metadata/drive marks this tool as reviewed", "review_reason": "dws-tool-metadata/drive marks this tool as reviewed",
"candidates": [ "candidates": [
{ {
"value": "not_required", "value": "user_required",
"source": "internal/cli/schema_hints/metadata/drive.json", "source": "internal/cli/schema_hints/metadata/drive.json",
"precedence": "reviewed_explicit", "precedence": "reviewed_explicit",
"selected": true, "selected": true,
@@ -1,17 +1,17 @@
{ {
"version": 1, "version": 1,
"source_hash": "sha256:ced34d4510fddb0b0cd02478f0f5051b0de0aab3d6bc7e1e43aaa1a12717d3b7", "source_hash": "sha256:31a28d1665ebb13fdb131547bda1d84ce1cc3fee0bbff821b76c2fdf6f6904ab",
"surface_hash": "sha256:674e6c00efb4d7f4c177ec0a1c4c097c8fb48b0045d1787c0ed001c32c51f20a", "surface_hash": "sha256:29d4f5f43688cc01bc2277a4528b597de4fbab89a7b2f13fe2e56df85d1c66c4",
"coverage": { "coverage": {
"surface_products": 26, "surface_products": 26,
"products_with_metadata": 26, "products_with_metadata": 26,
"surface_tools": 839, "surface_tools": 840,
"tools_with_metadata": 839, "tools_with_metadata": 840,
"tools_with_agent_summary": 839, "tools_with_agent_summary": 840,
"tools_with_use_when": 839, "tools_with_use_when": 840,
"tools_with_avoid_when": 839, "tools_with_avoid_when": 840,
"tools_with_examples": 839, "tools_with_examples": 840,
"tools_with_interface_mode": 839, "tools_with_interface_mode": 840,
"unmatched_skill_tools": 122, "unmatched_skill_tools": 122,
"unreviewed_skill_tools": 11 "unreviewed_skill_tools": 11
}, },
+10 -10
View File
@@ -1,11 +1,11 @@
{ {
"version": 1, "version": 1,
"source_hash": "sha256:ced34d4510fddb0b0cd02478f0f5051b0de0aab3d6bc7e1e43aaa1a12717d3b7", "source_hash": "sha256:31a28d1665ebb13fdb131547bda1d84ce1cc3fee0bbff821b76c2fdf6f6904ab",
"surface_hash": "sha256:674e6c00efb4d7f4c177ec0a1c4c097c8fb48b0045d1787c0ed001c32c51f20a", "surface_hash": "sha256:29d4f5f43688cc01bc2277a4528b597de4fbab89a7b2f13fe2e56df85d1c66c4",
"source_files": 160, "source_files": 160,
"hint_files": 54, "hint_files": 54,
"hint_products": 43, "hint_products": 43,
"hint_tools": 1830, "hint_tools": 1832,
"interface_metadata": { "interface_metadata": {
"source": "mcp-tools-list+cli-registry", "source": "mcp-tools-list+cli-registry",
"revision": "4574f7022c32cf4c033e9b7b4156e2fec815fed8", "revision": "4574f7022c32cf4c033e9b7b4156e2fec815fed8",
@@ -29,13 +29,13 @@
"coverage": { "coverage": {
"surface_products": 26, "surface_products": 26,
"products_with_metadata": 26, "products_with_metadata": 26,
"surface_tools": 839, "surface_tools": 840,
"tools_with_metadata": 839, "tools_with_metadata": 840,
"tools_with_agent_summary": 839, "tools_with_agent_summary": 840,
"tools_with_use_when": 839, "tools_with_use_when": 840,
"tools_with_avoid_when": 839, "tools_with_avoid_when": 840,
"tools_with_examples": 839, "tools_with_examples": 840,
"tools_with_interface_mode": 839, "tools_with_interface_mode": 840,
"unmatched_skill_tools": 122, "unmatched_skill_tools": 122,
"unreviewed_skill_tools": 11 "unreviewed_skill_tools": 11
}, },
+40 -11
View File
@@ -1,17 +1,17 @@
{ {
"version": 1, "version": 1,
"surface_hash": "sha256:674e6c00efb4d7f4c177ec0a1c4c097c8fb48b0045d1787c0ed001c32c51f20a", "surface_hash": "sha256:29d4f5f43688cc01bc2277a4528b597de4fbab89a7b2f13fe2e56df85d1c66c4",
"source_hash": "sha256:f2cf7fcd3a70748029688ef5653386d9b503ef898bf763acb1da04a0b150d483", "source_hash": "sha256:346ec3ac857b9a740baf03142296b1e4fb0814f713ceab4ed2ff9f46a85d6b4d",
"catalog": { "catalog": {
"agent_metadata": { "agent_metadata": {
"products_with_metadata": 26, "products_with_metadata": 26,
"source": "embedded-skill-metadata", "source": "embedded-skill-metadata",
"source_hash": "sha256:ced34d4510fddb0b0cd02478f0f5051b0de0aab3d6bc7e1e43aaa1a12717d3b7", "source_hash": "sha256:31a28d1665ebb13fdb131547bda1d84ce1cc3fee0bbff821b76c2fdf6f6904ab",
"surface_hash": "sha256:674e6c00efb4d7f4c177ec0a1c4c097c8fb48b0045d1787c0ed001c32c51f20a", "surface_hash": "sha256:29d4f5f43688cc01bc2277a4528b597de4fbab89a7b2f13fe2e56df85d1c66c4",
"surface_products": 26, "surface_products": 26,
"surface_tools": 839, "surface_tools": 840,
"tools_with_agent_summary": 839, "tools_with_agent_summary": 840,
"tools_with_metadata": 839, "tools_with_metadata": 840,
"unmatched_skill_tools": 122, "unmatched_skill_tools": 122,
"version": 1 "version": 1
}, },
@@ -7955,7 +7955,7 @@
"name": "batch_update_group_chat_settings", "name": "batch_update_group_chat_settings",
"primary_cli_path": "chat group user-settings set", "primary_cli_path": "chat group user-settings set",
"reviewed": true, "reviewed": true,
"risk": "low", "risk": "medium",
"title": "批量更新当前用户的群会话设置", "title": "批量更新当前用户的群会话设置",
"use_when": [ "use_when": [
"用户说 把这些群都设为免打扰/置顶" "用户说 把这些群都设为免打扰/置顶"
@@ -16172,7 +16172,7 @@
"id": "drive", "id": "drive",
"name": "钉盘文件管理", "name": "钉盘文件管理",
"runtime": true, "runtime": true,
"tool_count": 40, "tool_count": 41,
"tools": [ "tools": [
{ {
"agent_metadata_source": "embedded-skill-metadata", "agent_metadata_source": "embedded-skill-metadata",
@@ -16219,7 +16219,7 @@
"canonical_path": "drive.apply_permission", "canonical_path": "drive.apply_permission",
"cli_name": "apply", "cli_name": "apply",
"cli_path": "drive permission apply", "cli_path": "drive permission apply",
"confirmation": "not_required", "confirmation": "user_required",
"description": "向指定节点的审批人发起权限申请。建议先用 apply-info 获取可申请角色与审批人。\n\n注意: 本命令会真实通知审批人,Agent 必须先获得用户明确同意后再执行。", "description": "向指定节点的审批人发起权限申请。建议先用 apply-info 获取可申请角色与审批人。\n\n注意: 本命令会真实通知审批人,Agent 必须先获得用户明确同意后再执行。",
"effect": "write", "effect": "write",
"group": "permission", "group": "permission",
@@ -16430,6 +16430,35 @@
"已确认 contentType 非 ALIDOC,需要落盘本地查看时" "已确认 contentType 非 ALIDOC,需要落盘本地查看时"
] ]
}, },
{
"agent_metadata_source": "embedded-skill-metadata",
"agent_summary": "下载钉盘普通文件的指定历史版本到本地(两步下载:取签名 URL 后 HTTP GET)",
"agent_summary_source": "dws-agent-selection/drive",
"availability": "available",
"avoid_when": [
"下载最新版本用 drive download",
"在线文档(adoc)历史版本用 doc version 系列命令",
"在线表格(axls)历史版本用 sheet version 系列命令"
],
"canonical_path": "drive.download_file_version",
"cli_name": "download-version",
"cli_path": "drive download-version",
"confirmation": "not_required",
"description": "下载钉盘文件的指定历史版本到本地(两步下载流程)。\n\n仅适用于普通文件(如 pdf、docx、xlsx、png 等):\n 钉钉在线文档(adoc)请使用 dws doc version 系列命令\n 钉钉在线表格(axls)请使用 dws sheet version 系列命令\n\n流程:\n 1. 获取历史版本下载 URL 和签名请求头 (download_file_version)\n 2. HTTP GET 下载文件二进制内容到本地\n\n版本号从 dws drive list --node \u003cdentryUuid\u003e --versions 获取。",
"effect": "read",
"idempotency": "idempotent",
"interface_mode": "composite",
"interface_reason": "Reviewed unpinned remote adapter: this executable CLI wrapper calls a remote helper that is absent from the pinned MCP metadata snapshot; no single pinned semantically equivalent interface_ref can represent the command.",
"name": "download_file_version",
"primary_cli_path": "drive download-version",
"reviewed": true,
"risk": "low",
"title": "下载文件历史版本到本地",
"use_when": [
"用户要下载文件的历史版本/旧版本",
"版本号已通过 drive list --versions 获取"
]
},
{ {
"agent_metadata_source": "embedded-skill-metadata", "agent_metadata_source": "embedded-skill-metadata",
"agent_summary": "获取节点封面图片地址(文档首图/图片缩略图/类型图标)", "agent_summary": "获取节点封面图片地址(文档首图/图片缩略图/类型图标)",
@@ -26484,6 +26513,6 @@
} }
], ],
"source": "embedded-command-catalog", "source": "embedded-command-catalog",
"tool_count": 839 "tool_count": 840
} }
} }
+462 -7
View File
@@ -5521,14 +5521,14 @@
"review_reason": "dws-tool-metadata/chat marks this tool as reviewed", "review_reason": "dws-tool-metadata/chat marks this tool as reviewed",
"selected": true, "selected": true,
"source": "internal/cli/schema_hints/metadata/chat.json", "source": "internal/cli/schema_hints/metadata/chat.json",
"value": "low" "value": "medium"
} }
], ],
"precedence": "reviewed_explicit", "precedence": "reviewed_explicit",
"resolution": "highest_precedence", "resolution": "highest_precedence",
"review_reason": "dws-tool-metadata/chat marks this tool as reviewed", "review_reason": "dws-tool-metadata/chat marks this tool as reviewed",
"source": "internal/cli/schema_hints/metadata/chat.json", "source": "internal/cli/schema_hints/metadata/chat.json",
"value": "low" "value": "medium"
}, },
"title": { "title": {
"candidates": [ "candidates": [
@@ -5676,7 +5676,7 @@
"primary_cli_path": "chat group user-settings set", "primary_cli_path": "chat group user-settings set",
"product_id": "chat", "product_id": "chat",
"reviewed": true, "reviewed": true,
"risk": "low", "risk": "medium",
"source": "reviewed_command_registry", "source": "reviewed_command_registry",
"title": "批量更新当前用户的群会话设置", "title": "批量更新当前用户的群会话设置",
"use_when": [ "use_when": [
@@ -46156,7 +46156,7 @@
"is_alias": false, "is_alias": false,
"metadata_source": "embedded-mcp-metadata", "metadata_source": "embedded-mcp-metadata",
"name": "send_personal_message", "name": "send_personal_message",
"parameter_count": 12, "parameter_count": 17,
"parameters": { "parameters": {
"ai-tag": { "ai-tag": {
"default": "true", "default": "true",
@@ -46492,6 +46492,97 @@
"required": false, "required": false,
"type": "string" "type": "string"
}, },
"contact-id": {
"description": "联系人名片 openDingTalkId(msgType=profile 时必填)",
"field_provenance": {
"description": {
"candidates": [
{
"precedence": "cobra_contract",
"selected": true,
"source": "cobra_usage",
"value": "联系人名片 openDingTalkId(msgType=profile 时必填)"
},
{
"precedence": "default",
"selected": false,
"source": "default",
"value": ""
}
],
"precedence": "cobra_contract",
"resolution": "highest_precedence",
"source": "cobra_usage",
"value": "联系人名片 openDingTalkId(msgType=profile 时必填)"
},
"property": {
"candidates": [
{
"precedence": "reviewed_mapping_exclusion",
"review_reason": "serialized into the aggregate content payload",
"selected": true,
"source": "reviewed_mapping_exclusion",
"value": ""
},
{
"precedence": "inference",
"selected": false,
"source": "flag_name_inference",
"value": "contactId"
}
],
"precedence": "reviewed_mapping_exclusion",
"resolution": "highest_precedence",
"review_reason": "serialized into the aggregate content payload",
"source": "reviewed_mapping_exclusion",
"value": ""
},
"required": {
"candidates": [
{
"precedence": "default",
"selected": true,
"source": "default",
"value": false
}
],
"precedence": "default",
"resolution": "fallback",
"source": "default",
"value": false
},
"required_when": {
"candidates": [
{
"precedence": "default",
"selected": true,
"source": "default",
"value": ""
}
],
"precedence": "default",
"resolution": "highest_precedence",
"source": "default",
"value": ""
},
"type": {
"candidates": [
{
"precedence": "cobra_contract",
"selected": true,
"source": "cobra_flag_type",
"value": "string"
}
],
"precedence": "cobra_contract",
"resolution": "highest_precedence",
"source": "cobra_flag_type",
"value": "string"
}
},
"required": false,
"type": "string"
},
"file-path": { "file-path": {
"description": "本地文件路径(msgType=file/audio/video 时直接上传并按 file 消息发送)", "description": "本地文件路径(msgType=file/audio/video 时直接上传并按 file 消息发送)",
"field_provenance": { "field_provenance": {
@@ -46714,6 +46805,370 @@
"required": false, "required": false,
"type": "string" "type": "string"
}, },
"latitude": {
"description": "位置消息纬度(msgType=location 时必填)",
"field_provenance": {
"description": {
"candidates": [
{
"precedence": "cobra_contract",
"selected": true,
"source": "cobra_usage",
"value": "位置消息纬度(msgType=location 时必填)"
},
{
"precedence": "default",
"selected": false,
"source": "default",
"value": ""
}
],
"precedence": "cobra_contract",
"resolution": "highest_precedence",
"source": "cobra_usage",
"value": "位置消息纬度(msgType=location 时必填)"
},
"property": {
"candidates": [
{
"precedence": "reviewed_mapping_exclusion",
"review_reason": "serialized into the aggregate content payload",
"selected": true,
"source": "reviewed_mapping_exclusion",
"value": ""
},
{
"precedence": "inference",
"selected": false,
"source": "flag_name_inference",
"value": "latitude"
}
],
"precedence": "reviewed_mapping_exclusion",
"resolution": "highest_precedence",
"review_reason": "serialized into the aggregate content payload",
"source": "reviewed_mapping_exclusion",
"value": ""
},
"required": {
"candidates": [
{
"precedence": "default",
"selected": true,
"source": "default",
"value": false
}
],
"precedence": "default",
"resolution": "fallback",
"source": "default",
"value": false
},
"required_when": {
"candidates": [
{
"precedence": "default",
"selected": true,
"source": "default",
"value": ""
}
],
"precedence": "default",
"resolution": "highest_precedence",
"source": "default",
"value": ""
},
"type": {
"candidates": [
{
"precedence": "cobra_contract",
"selected": true,
"source": "cobra_flag_type",
"value": "string"
}
],
"precedence": "cobra_contract",
"resolution": "highest_precedence",
"source": "cobra_flag_type",
"value": "string"
}
},
"required": false,
"type": "string"
},
"location-name": {
"description": "位置消息地址名称(msgType=location 时必填)",
"field_provenance": {
"description": {
"candidates": [
{
"precedence": "cobra_contract",
"selected": true,
"source": "cobra_usage",
"value": "位置消息地址名称(msgType=location 时必填)"
},
{
"precedence": "default",
"selected": false,
"source": "default",
"value": ""
}
],
"precedence": "cobra_contract",
"resolution": "highest_precedence",
"source": "cobra_usage",
"value": "位置消息地址名称(msgType=location 时必填)"
},
"property": {
"candidates": [
{
"precedence": "reviewed_mapping_exclusion",
"review_reason": "serialized into the aggregate content payload",
"selected": true,
"source": "reviewed_mapping_exclusion",
"value": ""
},
{
"precedence": "inference",
"selected": false,
"source": "flag_name_inference",
"value": "locationName"
}
],
"precedence": "reviewed_mapping_exclusion",
"resolution": "highest_precedence",
"review_reason": "serialized into the aggregate content payload",
"source": "reviewed_mapping_exclusion",
"value": ""
},
"required": {
"candidates": [
{
"precedence": "default",
"selected": true,
"source": "default",
"value": false
}
],
"precedence": "default",
"resolution": "fallback",
"source": "default",
"value": false
},
"required_when": {
"candidates": [
{
"precedence": "default",
"selected": true,
"source": "default",
"value": ""
}
],
"precedence": "default",
"resolution": "highest_precedence",
"source": "default",
"value": ""
},
"type": {
"candidates": [
{
"precedence": "cobra_contract",
"selected": true,
"source": "cobra_flag_type",
"value": "string"
}
],
"precedence": "cobra_contract",
"resolution": "highest_precedence",
"source": "cobra_flag_type",
"value": "string"
}
},
"required": false,
"type": "string"
},
"longitude": {
"description": "位置消息经度(msgType=location 时必填)",
"field_provenance": {
"description": {
"candidates": [
{
"precedence": "cobra_contract",
"selected": true,
"source": "cobra_usage",
"value": "位置消息经度(msgType=location 时必填)"
},
{
"precedence": "default",
"selected": false,
"source": "default",
"value": ""
}
],
"precedence": "cobra_contract",
"resolution": "highest_precedence",
"source": "cobra_usage",
"value": "位置消息经度(msgType=location 时必填)"
},
"property": {
"candidates": [
{
"precedence": "reviewed_mapping_exclusion",
"review_reason": "serialized into the aggregate content payload",
"selected": true,
"source": "reviewed_mapping_exclusion",
"value": ""
},
{
"precedence": "inference",
"selected": false,
"source": "flag_name_inference",
"value": "longitude"
}
],
"precedence": "reviewed_mapping_exclusion",
"resolution": "highest_precedence",
"review_reason": "serialized into the aggregate content payload",
"source": "reviewed_mapping_exclusion",
"value": ""
},
"required": {
"candidates": [
{
"precedence": "default",
"selected": true,
"source": "default",
"value": false
}
],
"precedence": "default",
"resolution": "fallback",
"source": "default",
"value": false
},
"required_when": {
"candidates": [
{
"precedence": "default",
"selected": true,
"source": "default",
"value": ""
}
],
"precedence": "default",
"resolution": "highest_precedence",
"source": "default",
"value": ""
},
"type": {
"candidates": [
{
"precedence": "cobra_contract",
"selected": true,
"source": "cobra_flag_type",
"value": "string"
}
],
"precedence": "cobra_contract",
"resolution": "highest_precedence",
"source": "cobra_flag_type",
"value": "string"
}
},
"required": false,
"type": "string"
},
"map-thumbnail-url": {
"description": "位置消息地图缩略图 mediaId,形如 @mediaId(msgType=location 时必填)",
"field_provenance": {
"description": {
"candidates": [
{
"precedence": "cobra_contract",
"selected": true,
"source": "cobra_usage",
"value": "位置消息地图缩略图 mediaId,形如 @mediaId(msgType=location 时必填)"
},
{
"precedence": "default",
"selected": false,
"source": "default",
"value": ""
}
],
"precedence": "cobra_contract",
"resolution": "highest_precedence",
"source": "cobra_usage",
"value": "位置消息地图缩略图 mediaId,形如 @mediaId(msgType=location 时必填)"
},
"property": {
"candidates": [
{
"precedence": "reviewed_mapping_exclusion",
"review_reason": "serialized into the aggregate content payload",
"selected": true,
"source": "reviewed_mapping_exclusion",
"value": ""
},
{
"precedence": "inference",
"selected": false,
"source": "flag_name_inference",
"value": "mapThumbnailUrl"
}
],
"precedence": "reviewed_mapping_exclusion",
"resolution": "highest_precedence",
"review_reason": "serialized into the aggregate content payload",
"source": "reviewed_mapping_exclusion",
"value": ""
},
"required": {
"candidates": [
{
"precedence": "default",
"selected": true,
"source": "default",
"value": false
}
],
"precedence": "default",
"resolution": "fallback",
"source": "default",
"value": false
},
"required_when": {
"candidates": [
{
"precedence": "default",
"selected": true,
"source": "default",
"value": ""
}
],
"precedence": "default",
"resolution": "highest_precedence",
"source": "default",
"value": ""
},
"type": {
"candidates": [
{
"precedence": "cobra_contract",
"selected": true,
"source": "cobra_flag_type",
"value": "string"
}
],
"precedence": "cobra_contract",
"resolution": "highest_precedence",
"source": "cobra_flag_type",
"value": "string"
}
},
"required": false,
"type": "string"
},
"media-id": { "media-id": {
"description": "上游已提供的图片 mediaId(仅旧版 msgType=image;CLI 不提供本地上传到 mediaId)", "description": "上游已提供的图片 mediaId(仅旧版 msgType=image;CLI 不提供本地上传到 mediaId)",
"example": "@lADP_schema_smoke", "example": "@lADP_schema_smoke",
@@ -46834,7 +47289,7 @@
"type": "string" "type": "string"
}, },
"msg-type": { "msg-type": {
"description": "富媒体消息类型: image/file/audio/video(本地图片/文件推荐 file --file-path;image 仅接受已有 mediaId)", "description": "富媒体消息类型: image/file/audio/video/location/profile(本地图片/文件推荐 file --file-path;image 仅接受已有 mediaId)",
"enum": [ "enum": [
"image", "image",
"file", "file",
@@ -46848,7 +47303,7 @@
"precedence": "cobra_contract", "precedence": "cobra_contract",
"selected": true, "selected": true,
"source": "cobra_usage", "source": "cobra_usage",
"value": "富媒体消息类型: image/file/audio/video(本地图片/文件推荐 file --file-path;image 仅接受已有 mediaId)" "value": "富媒体消息类型: image/file/audio/video/location/profile(本地图片/文件推荐 file --file-path;image 仅接受已有 mediaId)"
}, },
{ {
"precedence": "mcp_metadata", "precedence": "mcp_metadata",
@@ -46866,7 +47321,7 @@
"precedence": "cobra_contract", "precedence": "cobra_contract",
"resolution": "highest_precedence", "resolution": "highest_precedence",
"source": "cobra_usage", "source": "cobra_usage",
"value": "富媒体消息类型: image/file/audio/video(本地图片/文件推荐 file --file-path;image 仅接受已有 mediaId)" "value": "富媒体消息类型: image/file/audio/video/location/profile(本地图片/文件推荐 file --file-path;image 仅接受已有 mediaId)"
}, },
"enum": { "enum": {
"candidates": [ "candidates": [
+622 -3
View File
@@ -828,7 +828,7 @@
"canonical_path": "drive.apply_permission", "canonical_path": "drive.apply_permission",
"cli_name": "apply", "cli_name": "apply",
"cli_path": "drive permission apply", "cli_path": "drive permission apply",
"confirmation": "not_required", "confirmation": "user_required",
"description": "向指定节点的审批人发起权限申请。建议先用 apply-info 获取可申请角色与审批人。\n\n注意: 本命令会真实通知审批人,Agent 必须先获得用户明确同意后再执行。", "description": "向指定节点的审批人发起权限申请。建议先用 apply-info 获取可申请角色与审批人。\n\n注意: 本命令会真实通知审批人,Agent 必须先获得用户明确同意后再执行。",
"display": "钉盘文件管理", "display": "钉盘文件管理",
"effect": "write", "effect": "write",
@@ -912,14 +912,14 @@
"review_reason": "dws-tool-metadata/drive marks this tool as reviewed", "review_reason": "dws-tool-metadata/drive marks this tool as reviewed",
"selected": true, "selected": true,
"source": "internal/cli/schema_hints/metadata/drive.json", "source": "internal/cli/schema_hints/metadata/drive.json",
"value": "not_required" "value": "user_required"
} }
], ],
"precedence": "reviewed_explicit", "precedence": "reviewed_explicit",
"resolution": "highest_precedence", "resolution": "highest_precedence",
"review_reason": "dws-tool-metadata/drive marks this tool as reviewed", "review_reason": "dws-tool-metadata/drive marks this tool as reviewed",
"source": "internal/cli/schema_hints/metadata/drive.json", "source": "internal/cli/schema_hints/metadata/drive.json",
"value": "not_required" "value": "user_required"
}, },
"description": { "description": {
"candidates": [ "candidates": [
@@ -5624,6 +5624,625 @@
"已确认 contentType 非 ALIDOC,需要落盘本地查看时" "已确认 contentType 非 ALIDOC,需要落盘本地查看时"
] ]
}, },
"drive.download_file_version": {
"agent_metadata_source": "embedded-skill-metadata",
"agent_source_refs": [
"CommandRegistry:canonical_path=drive.download_file_version",
"cobra-help:dws drive download-version",
"internal/cli/schema_hints/metadata/drive.json",
"internal/cli/schema_hints/selection/drive.json",
"wukong-develop:wukong/products/drive.go"
],
"agent_summary": "下载钉盘普通文件的指定历史版本到本地(两步下载:取签名 URL 后 HTTP GET)",
"agent_summary_source": "dws-agent-selection/drive",
"availability": "available",
"avoid_when": [
"下载最新版本用 drive download",
"在线文档(adoc)历史版本用 doc version 系列命令",
"在线表格(axls)历史版本用 sheet version 系列命令"
],
"canonical_path": "drive.download_file_version",
"cli_name": "download-version",
"cli_path": "drive download-version",
"confirmation": "not_required",
"description": "下载钉盘文件的指定历史版本到本地(两步下载流程)。\n\n仅适用于普通文件(如 pdf、docx、xlsx、png 等):\n 钉钉在线文档(adoc)请使用 dws doc version 系列命令\n 钉钉在线表格(axls)请使用 dws sheet version 系列命令\n\n流程:\n 1. 获取历史版本下载 URL 和签名请求头 (download_file_version)\n 2. HTTP GET 下载文件二进制内容到本地\n\n版本号从 dws drive list --node \u003cdentryUuid\u003e --versions 获取。",
"display": "钉盘文件管理",
"effect": "read",
"effect_source": "agent-hint",
"examples": [
"dws drive download-version --node \u003cdentryUuid\u003e --version 3 --output ./report_v3.pdf"
],
"field_provenance": {
"agent_summary": {
"candidates": [
{
"precedence": "reviewed_explicit",
"review_reason": "人工审阅:对照悟空 develop drive download --version 分支实现移植为独立命令;单一 download_file_version 远端调用,参数契约与帮助文档一致。",
"selected": true,
"source": "internal/cli/schema_hints/selection/drive.json",
"value": "下载钉盘普通文件的指定历史版本到本地(两步下载:取签名 URL 后 HTTP GET)"
}
],
"precedence": "reviewed_explicit",
"resolution": "highest_precedence",
"review_reason": "人工审阅:对照悟空 develop drive download --version 分支实现移植为独立命令;单一 download_file_version 远端调用,参数契约与帮助文档一致。",
"source": "internal/cli/schema_hints/selection/drive.json",
"value": "下载钉盘普通文件的指定历史版本到本地(两步下载:取签名 URL 后 HTTP GET)"
},
"availability": {
"candidates": [
{
"precedence": "reviewed_explicit",
"review_reason": "dws-tool-metadata/drive marks this tool as reviewed",
"selected": true,
"source": "internal/cli/schema_hints/metadata/drive.json",
"value": "available"
}
],
"precedence": "reviewed_explicit",
"resolution": "highest_precedence",
"review_reason": "dws-tool-metadata/drive marks this tool as reviewed",
"source": "internal/cli/schema_hints/metadata/drive.json",
"value": "available"
},
"avoid_when": {
"candidates": [
{
"precedence": "reviewed_explicit",
"review_reason": "人工审阅:对照悟空 develop drive download --version 分支实现移植为独立命令;单一 download_file_version 远端调用,参数契约与帮助文档一致。",
"selected": true,
"source": "internal/cli/schema_hints/selection/drive.json",
"value": [
"下载最新版本用 drive download",
"在线文档(adoc)历史版本用 doc version 系列命令",
"在线表格(axls)历史版本用 sheet version 系列命令"
]
}
],
"precedence": "reviewed_explicit",
"resolution": "highest_precedence",
"review_reason": "人工审阅:对照悟空 develop drive download --version 分支实现移植为独立命令;单一 download_file_version 远端调用,参数契约与帮助文档一致。",
"source": "internal/cli/schema_hints/selection/drive.json",
"value": [
"下载最新版本用 drive download",
"在线文档(adoc)历史版本用 doc version 系列命令",
"在线表格(axls)历史版本用 sheet version 系列命令"
]
},
"canonical_path": {
"candidates": [
{
"precedence": "command_registry",
"selected": true,
"source": "reviewed_command_registry",
"source_ref": "drive download-version",
"value": "drive.download_file_version"
}
],
"precedence": "command_registry",
"resolution": "registry_identity",
"source": "reviewed_command_registry",
"source_ref": "drive download-version",
"value": "drive.download_file_version"
},
"confirmation": {
"candidates": [
{
"precedence": "reviewed_explicit",
"review_reason": "dws-tool-metadata/drive marks this tool as reviewed",
"selected": true,
"source": "internal/cli/schema_hints/metadata/drive.json",
"value": "not_required"
}
],
"precedence": "reviewed_explicit",
"resolution": "highest_precedence",
"review_reason": "dws-tool-metadata/drive marks this tool as reviewed",
"source": "internal/cli/schema_hints/metadata/drive.json",
"value": "not_required"
},
"description": {
"candidates": [
{
"precedence": "cobra_help",
"selected": true,
"source": "cobra_help",
"value": "下载钉盘文件的指定历史版本到本地(两步下载流程)。\n\n仅适用于普通文件(如 pdf、docx、xlsx、png 等):\n 钉钉在线文档(adoc)请使用 dws doc version 系列命令\n 钉钉在线表格(axls)请使用 dws sheet version 系列命令\n\n流程:\n 1. 获取历史版本下载 URL 和签名请求头 (download_file_version)\n 2. HTTP GET 下载文件二进制内容到本地\n\n版本号从 dws drive list --node \u003cdentryUuid\u003e --versions 获取。"
}
],
"precedence": "cobra_help",
"resolution": "highest_precedence",
"source": "cobra_help",
"value": "下载钉盘文件的指定历史版本到本地(两步下载流程)。\n\n仅适用于普通文件(如 pdf、docx、xlsx、png 等):\n 钉钉在线文档(adoc)请使用 dws doc version 系列命令\n 钉钉在线表格(axls)请使用 dws sheet version 系列命令\n\n流程:\n 1. 获取历史版本下载 URL 和签名请求头 (download_file_version)\n 2. HTTP GET 下载文件二进制内容到本地\n\n版本号从 dws drive list --node \u003cdentryUuid\u003e --versions 获取。"
},
"effect": {
"candidates": [
{
"precedence": "reviewed_explicit",
"review_reason": "dws-tool-metadata/drive marks this tool as reviewed",
"selected": true,
"source": "internal/cli/schema_hints/metadata/drive.json",
"value": "read"
}
],
"precedence": "reviewed_explicit",
"resolution": "highest_precedence",
"review_reason": "dws-tool-metadata/drive marks this tool as reviewed",
"source": "internal/cli/schema_hints/metadata/drive.json",
"value": "read"
},
"examples": {
"candidates": [
{
"precedence": "reviewed_explicit",
"review_reason": "人工审阅:对照悟空 develop drive download --version 分支实现移植为独立命令;单一 download_file_version 远端调用,参数契约与帮助文档一致。",
"selected": true,
"source": "internal/cli/schema_hints/selection/drive.json",
"value": [
"dws drive download-version --node \u003cdentryUuid\u003e --version 3 --output ./report_v3.pdf"
]
}
],
"precedence": "reviewed_explicit",
"resolution": "highest_precedence",
"review_reason": "人工审阅:对照悟空 develop drive download --version 分支实现移植为独立命令;单一 download_file_version 远端调用,参数契约与帮助文档一致。",
"source": "internal/cli/schema_hints/selection/drive.json",
"value": [
"dws drive download-version --node \u003cdentryUuid\u003e --version 3 --output ./report_v3.pdf"
]
},
"idempotency": {
"candidates": [
{
"precedence": "reviewed_explicit",
"review_reason": "dws-tool-metadata/drive marks this tool as reviewed",
"selected": true,
"source": "internal/cli/schema_hints/metadata/drive.json",
"value": "idempotent"
}
],
"precedence": "reviewed_explicit",
"resolution": "highest_precedence",
"review_reason": "dws-tool-metadata/drive marks this tool as reviewed",
"source": "internal/cli/schema_hints/metadata/drive.json",
"value": "idempotent"
},
"interface_mode": {
"candidates": [
{
"precedence": "reviewed_explicit",
"review_reason": "dws-tool-metadata/drive marks this tool as reviewed",
"selected": true,
"source": "internal/cli/schema_hints/metadata/drive.json",
"value": "composite"
}
],
"precedence": "reviewed_explicit",
"resolution": "highest_precedence",
"review_reason": "dws-tool-metadata/drive marks this tool as reviewed",
"source": "internal/cli/schema_hints/metadata/drive.json",
"value": "composite"
},
"interface_reason": {
"candidates": [
{
"precedence": "reviewed_explicit",
"review_reason": "dws-tool-metadata/drive marks this tool as reviewed",
"selected": true,
"source": "internal/cli/schema_hints/metadata/drive.json",
"value": "Reviewed unpinned remote adapter: this executable CLI wrapper calls a remote helper that is absent from the pinned MCP metadata snapshot; no single pinned semantically equivalent interface_ref can represent the command."
}
],
"precedence": "reviewed_explicit",
"resolution": "highest_precedence",
"review_reason": "dws-tool-metadata/drive marks this tool as reviewed",
"source": "internal/cli/schema_hints/metadata/drive.json",
"value": "Reviewed unpinned remote adapter: this executable CLI wrapper calls a remote helper that is absent from the pinned MCP metadata snapshot; no single pinned semantically equivalent interface_ref can represent the command."
},
"interface_ref": {
"candidates": [
{
"precedence": "reviewed_explicit",
"review_reason": "final interface mode composite forbids a direct MCP interface_ref",
"selected": true,
"source": "internal/cli/schema_hints/metadata/drive.json",
"value": null
}
],
"precedence": "reviewed_explicit",
"resolution": "interface_disposition_matrix",
"review_reason": "final interface mode composite forbids a direct MCP interface_ref",
"source": "internal/cli/schema_hints/metadata/drive.json",
"value": null
},
"reviewed": {
"candidates": [
{
"precedence": "reviewed_explicit",
"review_reason": "dws-tool-metadata/drive marks this tool as reviewed",
"selected": true,
"source": "internal/cli/schema_hints/metadata/drive.json",
"value": true
},
{
"precedence": "reviewed_explicit",
"review_reason": "人工审阅:对照悟空 develop drive download --version 分支实现移植为独立命令;单一 download_file_version 远端调用,参数契约与帮助文档一致。",
"selected": false,
"source": "internal/cli/schema_hints/selection/drive.json",
"value": true
}
],
"precedence": "reviewed_explicit",
"resolution": "highest_precedence",
"review_reason": "dws-tool-metadata/drive marks this tool as reviewed",
"source": "internal/cli/schema_hints/metadata/drive.json",
"value": true
},
"risk": {
"candidates": [
{
"precedence": "reviewed_explicit",
"review_reason": "dws-tool-metadata/drive marks this tool as reviewed",
"selected": true,
"source": "internal/cli/schema_hints/metadata/drive.json",
"value": "low"
}
],
"precedence": "reviewed_explicit",
"resolution": "highest_precedence",
"review_reason": "dws-tool-metadata/drive marks this tool as reviewed",
"source": "internal/cli/schema_hints/metadata/drive.json",
"value": "low"
},
"title": {
"candidates": [
{
"precedence": "cobra_help",
"selected": true,
"source": "cobra_help",
"value": "下载文件历史版本到本地"
}
],
"precedence": "cobra_help",
"resolution": "highest_precedence",
"source": "cobra_help",
"value": "下载文件历史版本到本地"
},
"use_when": {
"candidates": [
{
"precedence": "reviewed_explicit",
"review_reason": "人工审阅:对照悟空 develop drive download --version 分支实现移植为独立命令;单一 download_file_version 远端调用,参数契约与帮助文档一致。",
"selected": true,
"source": "internal/cli/schema_hints/selection/drive.json",
"value": [
"用户要下载文件的历史版本/旧版本",
"版本号已通过 drive list --versions 获取"
]
}
],
"precedence": "reviewed_explicit",
"resolution": "highest_precedence",
"review_reason": "人工审阅:对照悟空 develop drive download --version 分支实现移植为独立命令;单一 download_file_version 远端调用,参数契约与帮助文档一致。",
"source": "internal/cli/schema_hints/selection/drive.json",
"value": [
"用户要下载文件的历史版本/旧版本",
"版本号已通过 drive list --versions 获取"
]
}
},
"has_parameters": true,
"idempotency": "idempotent",
"interface_mode": "composite",
"interface_reason": "Reviewed unpinned remote adapter: this executable CLI wrapper calls a remote helper that is absent from the pinned MCP metadata snapshot; no single pinned semantically equivalent interface_ref can represent the command.",
"is_alias": false,
"name": "download_file_version",
"parameter_count": 3,
"parameters": {
"node": {
"description": "文件 ID (dentryUuid) 或 URL (必填)",
"field_provenance": {
"description": {
"candidates": [
{
"precedence": "cobra_contract",
"selected": true,
"source": "cobra_usage",
"value": "文件 ID (dentryUuid) 或 URL (必填)"
},
{
"precedence": "default",
"selected": false,
"source": "default",
"value": ""
}
],
"precedence": "cobra_contract",
"resolution": "highest_precedence",
"source": "cobra_usage",
"value": "文件 ID (dentryUuid) 或 URL (必填)"
},
"property": {
"candidates": [
{
"precedence": "reviewed_mapping_exclusion",
"review_reason": "Reviewed unpinned adapter: drive.download_file_version has no singular pinned interface_ref; --node is a CLI wrapper input and does not publish a direct interface property.",
"selected": true,
"source": "reviewed_mapping_exclusion",
"value": ""
},
{
"precedence": "inference",
"selected": false,
"source": "flag_name_inference",
"value": "node"
}
],
"precedence": "reviewed_mapping_exclusion",
"resolution": "highest_precedence",
"review_reason": "Reviewed unpinned adapter: drive.download_file_version has no singular pinned interface_ref; --node is a CLI wrapper input and does not publish a direct interface property.",
"source": "reviewed_mapping_exclusion",
"value": ""
},
"required": {
"candidates": [
{
"precedence": "inference",
"selected": true,
"source": "usage_required_inference",
"value": true
},
{
"precedence": "default",
"selected": false,
"source": "default",
"value": false
}
],
"precedence": "inference",
"resolution": "highest_precedence",
"source": "usage_required_inference",
"value": true
},
"required_when": {
"candidates": [
{
"precedence": "default",
"selected": true,
"source": "default",
"value": ""
}
],
"precedence": "default",
"resolution": "highest_precedence",
"source": "default",
"value": ""
},
"type": {
"candidates": [
{
"precedence": "cobra_contract",
"selected": true,
"source": "cobra_flag_type",
"value": "string"
}
],
"precedence": "cobra_contract",
"resolution": "highest_precedence",
"source": "cobra_flag_type",
"value": "string"
}
},
"required": true,
"type": "string"
},
"output": {
"description": "本地保存路径 (文件路径或目录,必填)",
"field_provenance": {
"description": {
"candidates": [
{
"precedence": "cobra_contract",
"selected": true,
"source": "cobra_usage",
"value": "本地保存路径 (文件路径或目录,必填)"
},
{
"precedence": "default",
"selected": false,
"source": "default",
"value": ""
}
],
"precedence": "cobra_contract",
"resolution": "highest_precedence",
"source": "cobra_usage",
"value": "本地保存路径 (文件路径或目录,必填)"
},
"property": {
"candidates": [
{
"precedence": "reviewed_mapping_exclusion",
"review_reason": "Reviewed unpinned adapter: drive.download_file_version has no singular pinned interface_ref; --output is a CLI wrapper input and does not publish a direct interface property.",
"selected": true,
"source": "reviewed_mapping_exclusion",
"value": ""
},
{
"precedence": "inference",
"selected": false,
"source": "flag_name_inference",
"value": "output"
}
],
"precedence": "reviewed_mapping_exclusion",
"resolution": "highest_precedence",
"review_reason": "Reviewed unpinned adapter: drive.download_file_version has no singular pinned interface_ref; --output is a CLI wrapper input and does not publish a direct interface property.",
"source": "reviewed_mapping_exclusion",
"value": ""
},
"required": {
"candidates": [
{
"precedence": "inference",
"selected": true,
"source": "usage_required_inference",
"value": true
},
{
"precedence": "default",
"selected": false,
"source": "default",
"value": false
}
],
"precedence": "inference",
"resolution": "highest_precedence",
"source": "usage_required_inference",
"value": true
},
"required_when": {
"candidates": [
{
"precedence": "default",
"selected": true,
"source": "default",
"value": ""
}
],
"precedence": "default",
"resolution": "highest_precedence",
"source": "default",
"value": ""
},
"type": {
"candidates": [
{
"precedence": "cobra_contract",
"selected": true,
"source": "cobra_flag_type",
"value": "string"
}
],
"precedence": "cobra_contract",
"resolution": "highest_precedence",
"source": "cobra_flag_type",
"value": "string"
}
},
"required": true,
"type": "string"
},
"version": {
"description": "历史版本号 (必填,正整数,从 drive list --versions 获取)",
"field_provenance": {
"description": {
"candidates": [
{
"precedence": "cobra_contract",
"selected": true,
"source": "cobra_usage",
"value": "历史版本号 (必填,正整数,从 drive list --versions 获取)"
},
{
"precedence": "default",
"selected": false,
"source": "default",
"value": ""
}
],
"precedence": "cobra_contract",
"resolution": "highest_precedence",
"source": "cobra_usage",
"value": "历史版本号 (必填,正整数,从 drive list --versions 获取)"
},
"property": {
"candidates": [
{
"precedence": "reviewed_mapping_exclusion",
"review_reason": "Reviewed unpinned adapter: drive.download_file_version has no singular pinned interface_ref; --version is a CLI wrapper input and does not publish a direct interface property.",
"selected": true,
"source": "reviewed_mapping_exclusion",
"value": ""
},
{
"precedence": "inference",
"selected": false,
"source": "flag_name_inference",
"value": "version"
}
],
"precedence": "reviewed_mapping_exclusion",
"resolution": "highest_precedence",
"review_reason": "Reviewed unpinned adapter: drive.download_file_version has no singular pinned interface_ref; --version is a CLI wrapper input and does not publish a direct interface property.",
"source": "reviewed_mapping_exclusion",
"value": ""
},
"required": {
"candidates": [
{
"precedence": "inference",
"selected": true,
"source": "usage_required_inference",
"value": true
},
{
"precedence": "default",
"selected": false,
"source": "default",
"value": false
}
],
"precedence": "inference",
"resolution": "highest_precedence",
"source": "usage_required_inference",
"value": true
},
"required_when": {
"candidates": [
{
"precedence": "default",
"selected": true,
"source": "default",
"value": ""
}
],
"precedence": "default",
"resolution": "highest_precedence",
"source": "default",
"value": ""
},
"type": {
"candidates": [
{
"precedence": "cobra_contract",
"selected": true,
"source": "cobra_flag_type",
"value": "integer"
}
],
"precedence": "cobra_contract",
"resolution": "highest_precedence",
"source": "cobra_flag_type",
"value": "integer"
}
},
"required": true,
"type": "integer"
}
},
"path": "drive.download_file_version",
"primary_cli_path": "drive download-version",
"product_id": "drive",
"reviewed": true,
"risk": "low",
"source": "reviewed_command_registry",
"title": "下载文件历史版本到本地",
"use_when": [
"用户要下载文件的历史版本/旧版本",
"版本号已通过 drive list --versions 获取"
]
},
"drive.get_cover": { "drive.get_cover": {
"agent_metadata_source": "embedded-skill-metadata", "agent_metadata_source": "embedded-skill-metadata",
"agent_source_refs": [ "agent_source_refs": [
+190 -2
View File
@@ -26983,8 +26983,99 @@
"interface_reason": "Reviewed unpinned remote adapter: this executable CLI wrapper calls a remote helper that is absent from the pinned MCP metadata snapshot; no single pinned semantically equivalent interface_ref can represent the command.", "interface_reason": "Reviewed unpinned remote adapter: this executable CLI wrapper calls a remote helper that is absent from the pinned MCP metadata snapshot; no single pinned semantically equivalent interface_ref can represent the command.",
"is_alias": false, "is_alias": false,
"name": "formula_verify", "name": "formula_verify",
"parameter_count": 6, "parameter_count": 7,
"parameters": { "parameters": {
"exit-on-error": {
"description": "发现公式错误时返回非 0 退出码,便于 CI/自动化使用",
"field_provenance": {
"description": {
"candidates": [
{
"precedence": "cobra_contract",
"selected": true,
"source": "cobra_usage",
"value": "发现公式错误时返回非 0 退出码,便于 CI/自动化使用"
},
{
"precedence": "default",
"selected": false,
"source": "default",
"value": ""
}
],
"precedence": "cobra_contract",
"resolution": "highest_precedence",
"source": "cobra_usage",
"value": "发现公式错误时返回非 0 退出码,便于 CI/自动化使用"
},
"property": {
"candidates": [
{
"precedence": "reviewed_mapping_exclusion",
"review_reason": "Reviewed unpinned adapter: sheet.formula_verify has no singular pinned interface_ref; --exit-on-error is a CLI wrapper input and does not publish a direct interface property.",
"selected": true,
"source": "reviewed_mapping_exclusion",
"value": ""
},
{
"precedence": "inference",
"selected": false,
"source": "flag_name_inference",
"value": "exitOnError"
}
],
"precedence": "reviewed_mapping_exclusion",
"resolution": "highest_precedence",
"review_reason": "Reviewed unpinned adapter: sheet.formula_verify has no singular pinned interface_ref; --exit-on-error is a CLI wrapper input and does not publish a direct interface property.",
"source": "reviewed_mapping_exclusion",
"value": ""
},
"required": {
"candidates": [
{
"precedence": "default",
"selected": true,
"source": "default",
"value": false
}
],
"precedence": "default",
"resolution": "fallback",
"source": "default",
"value": false
},
"required_when": {
"candidates": [
{
"precedence": "default",
"selected": true,
"source": "default",
"value": ""
}
],
"precedence": "default",
"resolution": "highest_precedence",
"source": "default",
"value": ""
},
"type": {
"candidates": [
{
"precedence": "cobra_contract",
"selected": true,
"source": "cobra_flag_type",
"value": "boolean"
}
],
"precedence": "cobra_contract",
"resolution": "highest_precedence",
"source": "cobra_flag_type",
"value": "boolean"
}
},
"required": false,
"type": "boolean"
},
"max-cells": { "max-cells": {
"description": "最多扫描的单元格数", "description": "最多扫描的单元格数",
"field_provenance": { "field_provenance": {
@@ -34913,8 +35004,105 @@
"is_alias": false, "is_alias": false,
"metadata_source": "embedded-mcp-metadata", "metadata_source": "embedded-mcp-metadata",
"name": "info", "name": "info",
"parameter_count": 2, "parameter_count": 3,
"parameters": { "parameters": {
"include": {
"description": "可选扩展信息,逗号分隔;支持 groups / row_heights / col_widths / hidden_rows / hidden_cols / frozen",
"field_provenance": {
"description": {
"candidates": [
{
"precedence": "cobra_contract",
"selected": true,
"source": "cobra_usage",
"value": "可选扩展信息,逗号分隔;支持 groups / row_heights / col_widths / hidden_rows / hidden_cols / frozen"
},
{
"precedence": "mcp_metadata",
"selected": false,
"source": "mcp_metadata",
"value": "可选扩展信息列表。当前支持 groups,用于返回 rowGroups / columnGroups 分组信息。未指定时只返回基础工作表信息、冻结信息和 mergedRanges。"
},
{
"precedence": "default",
"selected": false,
"source": "default",
"value": ""
}
],
"precedence": "cobra_contract",
"resolution": "highest_precedence",
"source": "cobra_usage",
"value": "可选扩展信息,逗号分隔;支持 groups / row_heights / col_widths / hidden_rows / hidden_cols / frozen"
},
"property": {
"candidates": [
{
"precedence": "versioned_binding",
"selected": true,
"source": "versioned_parameter_binding",
"value": "include"
},
{
"precedence": "inference",
"selected": false,
"source": "flag_name_inference",
"value": "include"
}
],
"precedence": "versioned_binding",
"resolution": "highest_precedence",
"source": "versioned_parameter_binding",
"value": "include"
},
"required": {
"candidates": [
{
"precedence": "default",
"selected": true,
"source": "default",
"value": false
}
],
"precedence": "default",
"resolution": "fallback",
"source": "default",
"value": false
},
"required_when": {
"candidates": [
{
"precedence": "default",
"selected": true,
"source": "default",
"value": ""
}
],
"precedence": "default",
"resolution": "highest_precedence",
"source": "default",
"value": ""
},
"type": {
"candidates": [
{
"precedence": "cobra_contract",
"selected": true,
"source": "cobra_flag_type",
"value": "array"
}
],
"precedence": "cobra_contract",
"resolution": "highest_precedence",
"source": "cobra_flag_type",
"value": "array"
}
},
"interface_description": "可选扩展信息列表。当前支持 groups,用于返回 rowGroups / columnGroups 分组信息。未指定时只返回基础工作表信息、冻结信息和 mergedRanges。",
"property": "include",
"required": false,
"type": "array"
},
"node": { "node": {
"description": "表格文档 ID 或 URL (必填)", "description": "表格文档 ID 或 URL (必填)",
"field_provenance": { "field_provenance": {
@@ -49,6 +49,10 @@
"canonical_path": "drive.download_file", "canonical_path": "drive.download_file",
"cli_path": "drive download" "cli_path": "drive download"
}, },
{
"canonical_path": "drive.download_file_version",
"cli_path": "drive download-version"
},
{ {
"canonical_path": "drive.get_file_info", "canonical_path": "drive.get_file_info",
"cli_path": "drive info" "cli_path": "drive info"
+1 -1
View File
@@ -1382,7 +1382,7 @@
}, },
"chat.batch_update_group_chat_settings": { "chat.batch_update_group_chat_settings": {
"effect": "write", "effect": "write",
"risk": "low", "risk": "medium",
"confirmation": "not_required", "confirmation": "not_required",
"idempotency": "idempotent", "idempotency": "idempotent",
"interface_mode": "composite", "interface_mode": "composite",
+135 -124
View File
@@ -17,6 +17,17 @@
"availability": "available", "availability": "available",
"reviewed": true "reviewed": true
}, },
"drive.apply_permission": {
"effect": "write",
"risk": "medium",
"confirmation": "user_required",
"idempotency": "non_idempotent",
"interface_mode": "composite",
"availability": "available",
"interface_reason": "Reviewed unpinned remote adapter: this executable CLI wrapper calls a remote helper that is absent from the pinned MCP metadata snapshot; no single pinned semantically equivalent interface_ref can represent the command.",
"reviewed": true,
"runtime_gate": "confirm_delete"
},
"drive.commit_upload": { "drive.commit_upload": {
"interface_mode": "mcp", "interface_mode": "mcp",
"availability": "available", "availability": "available",
@@ -64,6 +75,28 @@
"availability": "available", "availability": "available",
"reviewed": true "reviewed": true
}, },
"drive.download_file_version": {
"effect": "read",
"risk": "low",
"confirmation": "not_required",
"idempotency": "idempotent",
"interface_mode": "composite",
"availability": "available",
"interface_reason": "Reviewed unpinned remote adapter: this executable CLI wrapper calls a remote helper that is absent from the pinned MCP metadata snapshot; no single pinned semantically equivalent interface_ref can represent the command.",
"reviewed": true,
"runtime_gate": "none"
},
"drive.get_cover": {
"effect": "read",
"risk": "low",
"confirmation": "not_required",
"idempotency": "idempotent",
"interface_mode": "composite",
"availability": "available",
"interface_reason": "Reviewed unpinned remote adapter: this executable CLI wrapper calls a remote helper that is absent from the pinned MCP metadata snapshot; no single pinned semantically equivalent interface_ref can represent the command.",
"reviewed": true,
"runtime_gate": "none"
},
"drive.get_file_info": { "drive.get_file_info": {
"interface_mode": "mcp", "interface_mode": "mcp",
"availability": "available", "availability": "available",
@@ -75,6 +108,17 @@
"interface_reason": "Reviewed unpinned remote adapter: this executable CLI wrapper calls a remote helper that is absent from the pinned MCP metadata snapshot; no single pinned semantically equivalent interface_ref can represent the command.", "interface_reason": "Reviewed unpinned remote adapter: this executable CLI wrapper calls a remote helper that is absent from the pinned MCP metadata snapshot; no single pinned semantically equivalent interface_ref can represent the command.",
"reviewed": true "reviewed": true
}, },
"drive.get_star_list": {
"effect": "read",
"risk": "low",
"confirmation": "not_required",
"idempotency": "idempotent",
"interface_mode": "composite",
"availability": "available",
"interface_reason": "Reviewed unpinned remote adapter: this executable CLI wrapper calls a remote helper that is absent from the pinned MCP metadata snapshot; no single pinned semantically equivalent interface_ref can represent the command.",
"reviewed": true,
"runtime_gate": "none"
},
"drive.get_upload_info": { "drive.get_upload_info": {
"interface_mode": "mcp", "interface_mode": "mcp",
"availability": "available", "availability": "available",
@@ -100,6 +144,17 @@
"availability": "available", "availability": "available",
"reviewed": true "reviewed": true
}, },
"drive.mark_star": {
"effect": "write",
"risk": "low",
"confirmation": "not_required",
"idempotency": "idempotent",
"interface_mode": "composite",
"availability": "available",
"interface_reason": "Reviewed unpinned remote adapter: this executable CLI wrapper calls a remote helper that is absent from the pinned MCP metadata snapshot; no single pinned semantically equivalent interface_ref can represent the command.",
"reviewed": true,
"runtime_gate": "none"
},
"drive.move_document": { "drive.move_document": {
"interface_ref": { "interface_ref": {
"product_id": "doc", "product_id": "doc",
@@ -164,6 +219,17 @@
"reviewed": true, "reviewed": true,
"runtime_gate": "confirm_dangerous" "runtime_gate": "confirm_dangerous"
}, },
"drive.query_permission_apply_info": {
"effect": "read",
"risk": "low",
"confirmation": "not_required",
"idempotency": "idempotent",
"interface_mode": "composite",
"availability": "available",
"interface_reason": "Reviewed unpinned remote adapter: this executable CLI wrapper calls a remote helper that is absent from the pinned MCP metadata snapshot; no single pinned semantically equivalent interface_ref can represent the command.",
"reviewed": true,
"runtime_gate": "none"
},
"drive.recent": { "drive.recent": {
"effect": "read", "effect": "read",
"risk": "low", "risk": "low",
@@ -212,28 +278,46 @@
"review_reason": "The reviewed CLI reads node metadata before mutation and strips only the exact current extension for non-folder nodes, avoiding both duplicate extensions and dotted-folder corruption without a suffix whitelist.", "review_reason": "The reviewed CLI reads node metadata before mutation and strips only the exact current extension for non-folder nodes, avoiding both duplicate extensions and dotted-folder corruption without a suffix whitelist.",
"cli_path": "drive rename" "cli_path": "drive rename"
}, },
"drive.revert_file_version": {
"effect": "write",
"risk": "medium",
"confirmation": "user_required",
"idempotency": "non_idempotent",
"interface_mode": "composite",
"availability": "available",
"interface_reason": "Reviewed unpinned remote adapter: this executable CLI wrapper calls a remote helper that is absent from the pinned MCP metadata snapshot; no single pinned semantically equivalent interface_ref can represent the command.",
"reviewed": true,
"runtime_gate": "confirm_delete"
},
"drive.search_files": { "drive.search_files": {
"interface_mode": "mcp", "interface_mode": "mcp",
"availability": "available", "availability": "available",
"reviewed": true "reviewed": true
}, },
"drive.upload": { "drive.shortcut_copy": {
"effect": "write", "effect": "write",
"risk": "medium", "risk": "medium",
"confirmation": "not_required", "confirmation": "user_required",
"idempotency": "unknown", "idempotency": "unknown",
"interface_mode": "composite", "interface_mode": "composite",
"availability": "available", "availability": "available",
"interface_reason": "命令包含多个 RPC、条件分派或本地 HTTP/文件步骤,不能绑定为单一 interface_ref", "interface_reason": "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
"reviewed": true, "reviewed": true,
"parameters": { "review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity.",
"node": { "cli_path": "drive +copy",
"property": "nodeId", "runtime_gate": "typed_yes"
"required": false
}
}, },
"review_reason": "The reviewed helper preserves the stable command-level Schema contract for ordinary uploads; --node switches to overwrite mode and the runtime still requires explicit confirmation unless --yes is supplied.", "drive.shortcut_find_file": {
"cli_path": "drive upload", "effect": "read",
"risk": "low",
"confirmation": "not_required",
"idempotency": "idempotent",
"interface_mode": "composite",
"availability": "available",
"interface_reason": "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
"reviewed": true,
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity.",
"cli_path": "drive +find-file",
"runtime_gate": "none" "runtime_gate": "none"
}, },
"drive.shortcut_info": { "drive.shortcut_info": {
@@ -249,6 +333,32 @@
"cli_path": "drive +info", "cli_path": "drive +info",
"runtime_gate": "none" "runtime_gate": "none"
}, },
"drive.shortcut_move": {
"effect": "write",
"risk": "medium",
"confirmation": "user_required",
"idempotency": "unknown",
"interface_mode": "composite",
"availability": "available",
"interface_reason": "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
"reviewed": true,
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity.",
"cli_path": "drive +move",
"runtime_gate": "typed_yes"
},
"drive.shortcut_recent": {
"effect": "read",
"risk": "low",
"confirmation": "not_required",
"idempotency": "idempotent",
"interface_mode": "composite",
"availability": "available",
"interface_reason": "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
"reviewed": true,
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity.",
"cli_path": "drive +recent",
"runtime_gate": "none"
},
"drive.shortcut_search": { "drive.shortcut_search": {
"effect": "read", "effect": "read",
"risk": "low", "risk": "low",
@@ -275,68 +385,16 @@
"cli_path": "drive +search-docs", "cli_path": "drive +search-docs",
"runtime_gate": "none" "runtime_gate": "none"
}, },
"drive.shortcut_copy": { "drive.transfer_owner": {
"effect": "write", "effect": "write",
"risk": "medium", "risk": "high",
"confirmation": "user_required", "confirmation": "user_required",
"idempotency": "unknown", "idempotency": "non_idempotent",
"interface_mode": "composite",
"availability": "available",
"interface_reason": "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
"reviewed": true,
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity.",
"cli_path": "drive +copy",
"runtime_gate": "typed_yes"
},
"drive.shortcut_move": {
"effect": "write",
"risk": "medium",
"confirmation": "user_required",
"idempotency": "unknown",
"interface_mode": "composite",
"availability": "available",
"interface_reason": "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
"reviewed": true,
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity.",
"cli_path": "drive +move",
"runtime_gate": "typed_yes"
},
"drive.shortcut_recent": {
"effect": "read",
"risk": "low",
"confirmation": "not_required",
"idempotency": "idempotent",
"interface_mode": "composite",
"availability": "available",
"interface_reason": "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
"reviewed": true,
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity.",
"cli_path": "drive +recent",
"runtime_gate": "none"
},
"drive.shortcut_find_file": {
"effect": "read",
"risk": "low",
"confirmation": "not_required",
"idempotency": "idempotent",
"interface_mode": "composite",
"availability": "available",
"interface_reason": "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
"reviewed": true,
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity.",
"cli_path": "drive +find-file",
"runtime_gate": "none"
},
"drive.mark_star": {
"effect": "write",
"risk": "low",
"confirmation": "not_required",
"idempotency": "idempotent",
"interface_mode": "composite", "interface_mode": "composite",
"availability": "available", "availability": "available",
"interface_reason": "Reviewed unpinned remote adapter: this executable CLI wrapper calls a remote helper that is absent from the pinned MCP metadata snapshot; no single pinned semantically equivalent interface_ref can represent the command.", "interface_reason": "Reviewed unpinned remote adapter: this executable CLI wrapper calls a remote helper that is absent from the pinned MCP metadata snapshot; no single pinned semantically equivalent interface_ref can represent the command.",
"reviewed": true, "reviewed": true,
"runtime_gate": "none" "runtime_gate": "confirm_delete"
}, },
"drive.unmark_star": { "drive.unmark_star": {
"effect": "write", "effect": "write",
@@ -349,70 +407,23 @@
"reviewed": true, "reviewed": true,
"runtime_gate": "none" "runtime_gate": "none"
}, },
"drive.get_star_list": { "drive.upload": {
"effect": "read",
"risk": "low",
"confirmation": "not_required",
"idempotency": "idempotent",
"interface_mode": "composite",
"availability": "available",
"interface_reason": "Reviewed unpinned remote adapter: this executable CLI wrapper calls a remote helper that is absent from the pinned MCP metadata snapshot; no single pinned semantically equivalent interface_ref can represent the command.",
"reviewed": true,
"runtime_gate": "none"
},
"drive.get_cover": {
"effect": "read",
"risk": "low",
"confirmation": "not_required",
"idempotency": "idempotent",
"interface_mode": "composite",
"availability": "available",
"interface_reason": "Reviewed unpinned remote adapter: this executable CLI wrapper calls a remote helper that is absent from the pinned MCP metadata snapshot; no single pinned semantically equivalent interface_ref can represent the command.",
"reviewed": true,
"runtime_gate": "none"
},
"drive.revert_file_version": {
"effect": "write",
"risk": "medium",
"confirmation": "user_required",
"idempotency": "non_idempotent",
"interface_mode": "composite",
"availability": "available",
"interface_reason": "Reviewed unpinned remote adapter: this executable CLI wrapper calls a remote helper that is absent from the pinned MCP metadata snapshot; no single pinned semantically equivalent interface_ref can represent the command.",
"reviewed": true,
"runtime_gate": "confirm_delete"
},
"drive.transfer_owner": {
"effect": "write",
"risk": "high",
"confirmation": "user_required",
"idempotency": "non_idempotent",
"interface_mode": "composite",
"availability": "available",
"interface_reason": "Reviewed unpinned remote adapter: this executable CLI wrapper calls a remote helper that is absent from the pinned MCP metadata snapshot; no single pinned semantically equivalent interface_ref can represent the command.",
"reviewed": true,
"runtime_gate": "confirm_delete"
},
"drive.query_permission_apply_info": {
"effect": "read",
"risk": "low",
"confirmation": "not_required",
"idempotency": "idempotent",
"interface_mode": "composite",
"availability": "available",
"interface_reason": "Reviewed unpinned remote adapter: this executable CLI wrapper calls a remote helper that is absent from the pinned MCP metadata snapshot; no single pinned semantically equivalent interface_ref can represent the command.",
"reviewed": true,
"runtime_gate": "none"
},
"drive.apply_permission": {
"effect": "write", "effect": "write",
"risk": "medium", "risk": "medium",
"confirmation": "not_required", "confirmation": "not_required",
"idempotency": "non_idempotent", "idempotency": "unknown",
"interface_mode": "composite", "interface_mode": "composite",
"availability": "available", "availability": "available",
"interface_reason": "Reviewed unpinned remote adapter: this executable CLI wrapper calls a remote helper that is absent from the pinned MCP metadata snapshot; no single pinned semantically equivalent interface_ref can represent the command.", "interface_reason": "命令包含多个 RPC、条件分派或本地 HTTP/文件步骤,不能绑定为单一 interface_ref",
"reviewed": true, "reviewed": true,
"parameters": {
"node": {
"property": "nodeId",
"required": false
}
},
"review_reason": "The reviewed helper preserves the stable command-level Schema contract for ordinary uploads; --node switches to overwrite mode and the runtime still requires explicit confirmation unless --yes is supplied.",
"cli_path": "drive upload",
"runtime_gate": "none" "runtime_gate": "none"
} }
} }
@@ -7,7 +7,7 @@
"channel": "open-source" "channel": "open-source"
}, },
"coverage": { "coverage": {
"source_tools": 839, "source_tools": 840,
"matched_tools": 71 "matched_tools": 71
}, },
"tools": { "tools": {
+218 -196
View File
@@ -33,6 +33,25 @@
"live-mcp:dws schema doc.add_permission" "live-mcp:dws schema doc.add_permission"
] ]
}, },
"drive.apply_permission": {
"agent_summary": "向审批人发起文档权限申请(会真实通知审批人)",
"use_when": [
"用户确认要为无权限文档发起权限申请"
],
"avoid_when": [
"先用 apply-info 查可申请角色与审批人;未经用户确认不得自行提交"
],
"examples": [
"dws drive permission apply --node <DOC_ID> --role READER --users <审批人userId> --format json"
],
"reviewed": true,
"review_reason": "人工审阅:对照悟空 develop 同名命令实现、Cobra Long/Example 与 Runtime 确认门禁移植;不改变命令身份、参数契约或接口绑定。",
"source_refs": [
"CommandRegistry:canonical_path=drive.apply_permission",
"cobra-help:dws drive permission apply",
"wukong-develop:wukong/products"
]
},
"drive.commit_upload": { "drive.commit_upload": {
"agent_summary": "提交文件上传", "agent_summary": "提交文件上传",
"use_when": [ "use_when": [
@@ -187,6 +206,47 @@
"live-mcp:dws schema drive.download_file" "live-mcp:dws schema drive.download_file"
] ]
}, },
"drive.download_file_version": {
"agent_summary": "下载钉盘普通文件的指定历史版本到本地(两步下载:取签名 URL 后 HTTP GET)",
"use_when": [
"用户要下载文件的历史版本/旧版本",
"版本号已通过 drive list --versions 获取"
],
"avoid_when": [
"下载最新版本用 drive download",
"在线文档(adoc)历史版本用 doc version 系列命令",
"在线表格(axls)历史版本用 sheet version 系列命令"
],
"examples": [
"dws drive download-version --node <dentryUuid> --version 3 --output ./report_v3.pdf"
],
"reviewed": true,
"review_reason": "人工审阅:对照悟空 develop drive download --version 分支实现移植为独立命令;单一 download_file_version 远端调用,参数契约与帮助文档一致。",
"source_refs": [
"CommandRegistry:canonical_path=drive.download_file_version",
"cobra-help:dws drive download-version",
"wukong-develop:wukong/products/drive.go"
]
},
"drive.get_cover": {
"agent_summary": "获取节点封面图片地址(文档首图/图片缩略图/类型图标)",
"use_when": [
"用户说 封面/封面图/缩略图/预览图"
],
"avoid_when": [
"设置文档封面用 doc style cover set"
],
"examples": [
"dws drive cover --node <dentryUuid> --format json"
],
"reviewed": true,
"review_reason": "人工审阅:对照悟空 develop 同名命令实现、Cobra Long/Example 与 Runtime 确认门禁移植;不改变命令身份、参数契约或接口绑定。",
"source_refs": [
"CommandRegistry:canonical_path=drive.get_cover",
"cobra-help:dws drive cover",
"wukong-develop:wukong/products"
]
},
"drive.get_file_info": { "drive.get_file_info": {
"agent_summary": "获取文件元数据信息", "agent_summary": "获取文件元数据信息",
"use_when": [ "use_when": [
@@ -235,6 +295,26 @@
"live-mcp:none (Cobra+Skill; composite/unpinned)" "live-mcp:none (Cobra+Skill; composite/unpinned)"
] ]
}, },
"drive.get_star_list": {
"agent_summary": "获取当前用户的收藏列表,支持分页与按内容类型筛选",
"use_when": [
"用户说 我的收藏/收藏列表/收藏了哪些文档"
],
"avoid_when": [
"操作单个收藏用 mark_star/unmark_star"
],
"examples": [
"dws drive star list --format json",
"dws drive star list --content-types doc,sheet --limit 10 --format json"
],
"reviewed": true,
"review_reason": "人工审阅:对照悟空 develop 同名命令实现、Cobra Long/Example 与 Runtime 确认门禁移植;不改变命令身份、参数契约或接口绑定。",
"source_refs": [
"CommandRegistry:canonical_path=drive.get_star_list",
"cobra-help:dws drive star list",
"wukong-develop:wukong/products"
]
},
"drive.get_upload_info": { "drive.get_upload_info": {
"agent_summary": "获取文件上传信息", "agent_summary": "获取文件上传信息",
"use_when": [ "use_when": [
@@ -334,6 +414,25 @@
"live-mcp:dws schema drive.list_spaces" "live-mcp:dws schema drive.list_spaces"
] ]
}, },
"drive.mark_star": {
"agent_summary": "收藏文档/文件到当前用户收藏列表",
"use_when": [
"用户说 收藏这个文档/加个收藏/标星"
],
"avoid_when": [
"取消收藏用 unmark_star;查看收藏列表用 get_star_list"
],
"examples": [
"dws drive star add --node <nodeId> --format json"
],
"reviewed": true,
"review_reason": "人工审阅:对照悟空 develop 同名命令实现、Cobra Long/Example 与 Runtime 确认门禁移植;不改变命令身份、参数契约或接口绑定。",
"source_refs": [
"CommandRegistry:canonical_path=drive.mark_star",
"cobra-help:dws drive star add",
"wukong-develop:wukong/products"
]
},
"drive.move_document": { "drive.move_document": {
"agent_summary": "将文件或文档移动到目标文件夹或知识库(原位置不再保留)", "agent_summary": "将文件或文档移动到目标文件夹或知识库(原位置不再保留)",
"use_when": [ "use_when": [
@@ -475,6 +574,25 @@
"live-mcp:none (Cobra+Skill; composite/unpinned)" "live-mcp:none (Cobra+Skill; composite/unpinned)"
] ]
}, },
"drive.query_permission_apply_info": {
"agent_summary": "查询节点可申请的权限角色列表与审批人列表",
"use_when": [
"无权限访问文档时,先查可申请角色与审批人"
],
"avoid_when": [
"实际发起申请用 apply_permission"
],
"examples": [
"dws drive permission apply-info --node <DOC_ID> --format json"
],
"reviewed": true,
"review_reason": "人工审阅:对照悟空 develop 同名命令实现、Cobra Long/Example 与 Runtime 确认门禁移植;不改变命令身份、参数契约或接口绑定。",
"source_refs": [
"CommandRegistry:canonical_path=drive.query_permission_apply_info",
"cobra-help:dws drive permission apply-info",
"wukong-develop:wukong/products"
]
},
"drive.recent": { "drive.recent": {
"agent_summary": "获取当前用户最近访问或编辑过的文档列表", "agent_summary": "获取当前用户最近访问或编辑过的文档列表",
"use_when": [ "use_when": [
@@ -568,6 +686,25 @@
"live-mcp:dws schema doc.rename_document" "live-mcp:dws schema doc.rename_document"
] ]
}, },
"drive.revert_file_version": {
"agent_summary": "回滚普通文件到指定历史版本(生成新最新版本,历史不丢失)",
"use_when": [
"用户说 回滚版本/恢复到某个版本/版本回退,且目标是普通文件"
],
"avoid_when": [
"在线文档回滚用 doc version revert;在线表格用 sheet version revert"
],
"examples": [
"dws drive revert --node <dentryUuid> --version 3 --format json"
],
"reviewed": true,
"review_reason": "人工审阅:对照悟空 develop 同名命令实现、Cobra Long/Example 与 Runtime 确认门禁移植;不改变命令身份、参数契约或接口绑定。",
"source_refs": [
"CommandRegistry:canonical_path=drive.revert_file_version",
"cobra-help:dws drive revert",
"wukong-develop:wukong/products"
]
},
"drive.search_files": { "drive.search_files": {
"agent_summary": "全局搜索文件,默认同时搜索钉盘和文档空间,合并返回结果", "agent_summary": "全局搜索文件,默认同时搜索钉盘和文档空间,合并返回结果",
"use_when": [ "use_when": [
@@ -595,32 +732,43 @@
"live-mcp:dws schema drive.search_files" "live-mcp:dws schema drive.search_files"
] ]
}, },
"drive.upload": { "drive.shortcut_copy": {
"agent_summary": "上传本地文件到钉盘或文档空间,或按节点 ID 确认覆盖已有文件", "agent_summary": "复制文件/文档到指定位置",
"use_when": [ "use_when": [
"用户要把本地文件上传到钉盘/我的文件(首选一条命令自动完成凭证+PUT+提交)时", "当你想保留原件、把某个文件/文档拷贝一份到指定文件夹或知识库时使用;输入源节点 node 及目标 folder/workspace,会实际生成一个副本,原文件位置不变。"
"上传到知识库/文档空间时加 --workspace;需要转在线文档时加 --convert",
"用户明确要求用本地文件替换已有钉盘/文档空间文件时传 --node;该模式会覆盖远端内容并要求确认"
], ],
"avoid_when": [ "avoid_when": [
"常规场景不要拆成 upload-info + 手动 PUT + commit;仅自定义流式上传才用三步", "需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
"要把文件作为文档正文附件插入改用 dws doc media insert",
"用户明确说文档空间且走 doc 兼容入口时可用 dws doc upload,默认仍推荐本命令",
"用户没有明确同意替换目标文件时不要使用 --node;新建上传应使用 --folder 或目标根目录"
], ],
"examples": [ "examples": [
"dws drive upload --file ./report.pdf --format json", "dws drive +copy --node <nodeId> --folder <targetFolderId>"
"dws drive upload --file ./README.md --node <dentryUuid> --format json"
], ],
"reviewed": true, "reviewed": true,
"review_reason": "人工审阅:结合本机 dws schema 实时 MCP description/parameters(经 interface_ref)、产品 Skill、Cobra Long 与 Runtime 确认门禁,按飞书风格重写选型文案;不改变命令身份、参数契约或接口绑定。", "review_reason": "Agent-authored and reviewed from the built-in Shortcut intent, executable Cobra path, and declared outcome; it affects selection only and does not alter execution or safety facts.",
"source_refs": [ "source_refs": [
"CommandRegistry:canonical_path=drive.upload", "internal/cli/schema_command_registry.json#drive.shortcut_copy",
"cobra-help:dws drive upload", "cobra-help:dws drive +copy",
"Skill:skills/mono/references/products/drive.md", "ShortcutRegistry:drive +copy"
"structured-hint:internal/cli/schema_hints/selection-review.json#drive.upload", ]
"structured-hint:internal/cli/schema_hints/products/drive.json", },
"live-mcp:none (Cobra+Skill; composite/unpinned)" "drive.shortcut_find_file": {
"agent_summary": "按名称关键词搜索钉盘文件并投影关键字段(只读)",
"use_when": [
"当你只记得钉盘文件的名字(或其中一部分),想快速按文件名关键词找到它、拿到它的 dentryId 以便后续下载/查看,却不想手动翻目录或写复杂过滤条件时使用;内部调用钉盘的 search_files 工具,把 --query 作为文件名关键词(keyword) 并限定搜索范围为钉盘文件(searchTarget=file),再在本地把每条命中结果精简为「文件名、类型、dentryId、大小」四个字段后打印。这是纯只读操作,只做搜索与本地投影,不会创建、移动或删除任何文件;未命中时返回空列表。"
],
"avoid_when": [
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
],
"examples": [
"dws drive +find-file --query 季度汇报",
"dws drive +find-file --query 合同"
],
"reviewed": true,
"review_reason": "Agent-authored and reviewed from the built-in Shortcut intent, executable Cobra path, and declared outcome; it affects selection only and does not alter execution or safety facts.",
"source_refs": [
"internal/cli/schema_command_registry.json#drive.shortcut_find_file",
"cobra-help:dws drive +find-file",
"ShortcutRegistry:drive +find-file"
] ]
}, },
"drive.shortcut_info": { "drive.shortcut_info": {
@@ -642,64 +790,6 @@
"ShortcutRegistry:drive +info" "ShortcutRegistry:drive +info"
] ]
}, },
"drive.shortcut_search": {
"agent_summary": "搜索钉盘文件",
"use_when": [
"当你只记得文件名或内容关键词、不知道它在哪个目录时用它全局检索钉盘文件;输入 query,可按文件类型、扩展名、创建者、创建/修改时间范围过滤,返回匹配文件及其 ID,便于再做下载或整理。"
],
"avoid_when": [
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
],
"examples": [
"dws drive +search --query \"季度汇报\"",
"dws drive +search --query \"合同\" --target file --extensions pdf,docx"
],
"reviewed": true,
"review_reason": "Agent-authored and reviewed from the built-in Shortcut intent, executable Cobra path, and declared outcome; it affects selection only and does not alter execution or safety facts.",
"source_refs": [
"internal/cli/schema_command_registry.json#drive.shortcut_search",
"cobra-help:dws drive +search",
"ShortcutRegistry:drive +search"
]
},
"drive.shortcut_search_docs": {
"agent_summary": "搜索文档空间文档",
"use_when": [
"当你只记得文档标题或关键词、想在文档空间/知识库中检索在线文档(区别于 +search 检索钉盘文件)时使用;输入 query 关键词,返回匹配的文档及其节点信息。"
],
"avoid_when": [
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
],
"examples": [
"dws drive +search-docs --query \"季度汇报\""
],
"reviewed": true,
"review_reason": "Agent-authored and reviewed from the built-in Shortcut intent, executable Cobra path, and declared outcome; it affects selection only and does not alter execution or safety facts.",
"source_refs": [
"internal/cli/schema_command_registry.json#drive.shortcut_search_docs",
"cobra-help:dws drive +search-docs",
"ShortcutRegistry:drive +search-docs"
]
},
"drive.shortcut_copy": {
"agent_summary": "复制文件/文档到指定位置",
"use_when": [
"当你想保留原件、把某个文件/文档拷贝一份到指定文件夹或知识库时使用;输入源节点 node 及目标 folder/workspace,会实际生成一个副本,原文件位置不变。"
],
"avoid_when": [
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
],
"examples": [
"dws drive +copy --node <nodeId> --folder <targetFolderId>"
],
"reviewed": true,
"review_reason": "Agent-authored and reviewed from the built-in Shortcut intent, executable Cobra path, and declared outcome; it affects selection only and does not alter execution or safety facts.",
"source_refs": [
"internal/cli/schema_command_registry.json#drive.shortcut_copy",
"cobra-help:dws drive +copy",
"ShortcutRegistry:drive +copy"
]
},
"drive.shortcut_move": { "drive.shortcut_move": {
"agent_summary": "移动文件/文档到指定位置", "agent_summary": "移动文件/文档到指定位置",
"use_when": [ "use_when": [
@@ -739,42 +829,61 @@
"ShortcutRegistry:drive +recent" "ShortcutRegistry:drive +recent"
] ]
}, },
"drive.shortcut_find_file": { "drive.shortcut_search": {
"agent_summary": "按名称关键词搜索钉盘文件并投影关键字段(只读)", "agent_summary": "搜索钉盘文件",
"use_when": [ "use_when": [
"当你只记得钉盘文件的名字(或其中一部分),想快速按文件名关键词找到它、拿到它的 dentryId 以便后续下载/查看,却不想手动翻目录或写复杂过滤条件时使用;内部调用钉盘的 search_files 工具,把 --query 作为文件名关键词(keyword) 并限定搜索范围为钉盘文件(searchTarget=file),再在本地把每条命中结果精简为「文件名、类型、dentryId、大小」四个字段后打印。这是纯只读操作,只做搜索与本地投影,不会创建、移动或删除任何文件;未命中时返回空列表。" "当你只记得文件名或内容关键词、不知道它在哪个目录时用它全局检索钉盘文件;输入 query,可按文件类型、扩展名、创建者、创建/修改时间范围过滤,返回匹配文件及其 ID,便于再做下载或整理。"
], ],
"avoid_when": [ "avoid_when": [
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令" "需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
], ],
"examples": [ "examples": [
"dws drive +find-file --query 季度汇报", "dws drive +search --query \"季度汇报\"",
"dws drive +find-file --query 合同" "dws drive +search --query \"合同\" --target file --extensions pdf,docx"
], ],
"reviewed": true, "reviewed": true,
"review_reason": "Agent-authored and reviewed from the built-in Shortcut intent, executable Cobra path, and declared outcome; it affects selection only and does not alter execution or safety facts.", "review_reason": "Agent-authored and reviewed from the built-in Shortcut intent, executable Cobra path, and declared outcome; it affects selection only and does not alter execution or safety facts.",
"source_refs": [ "source_refs": [
"internal/cli/schema_command_registry.json#drive.shortcut_find_file", "internal/cli/schema_command_registry.json#drive.shortcut_search",
"cobra-help:dws drive +find-file", "cobra-help:dws drive +search",
"ShortcutRegistry:drive +find-file" "ShortcutRegistry:drive +search"
] ]
}, },
"drive.mark_star": { "drive.shortcut_search_docs": {
"agent_summary": "收藏文档/文件到当前用户收藏列表", "agent_summary": "搜索文档空间文档",
"use_when": [ "use_when": [
"用户说 收藏这个文档/加个收藏/标星" "当你只记得文档标题或关键词、想在文档空间/知识库中检索在线文档(区别于 +search 检索钉盘文件)时使用;输入 query 关键词,返回匹配的文档及其节点信息。"
], ],
"avoid_when": [ "avoid_when": [
"取消收藏用 unmark_star;查看收藏列表用 get_star_list" "需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
], ],
"examples": [ "examples": [
"dws drive star add --node <nodeId> --format json" "dws drive +search-docs --query \"季度汇报\""
],
"reviewed": true,
"review_reason": "Agent-authored and reviewed from the built-in Shortcut intent, executable Cobra path, and declared outcome; it affects selection only and does not alter execution or safety facts.",
"source_refs": [
"internal/cli/schema_command_registry.json#drive.shortcut_search_docs",
"cobra-help:dws drive +search-docs",
"ShortcutRegistry:drive +search-docs"
]
},
"drive.transfer_owner": {
"agent_summary": "转交文档或知识库所有者给指定用户(不可逆)",
"use_when": [
"用户明确要求转交文档/知识库所有权"
],
"avoid_when": [
"普通协作权限变更用 permission add/update/remove"
],
"examples": [
"dws drive permission transfer-owner --node <DOC_ID> --new-owner <userId> --reserve-role EDITOR --recursive=false --format json"
], ],
"reviewed": true, "reviewed": true,
"review_reason": "人工审阅:对照悟空 develop 同名命令实现、Cobra Long/Example 与 Runtime 确认门禁移植;不改变命令身份、参数契约或接口绑定。", "review_reason": "人工审阅:对照悟空 develop 同名命令实现、Cobra Long/Example 与 Runtime 确认门禁移植;不改变命令身份、参数契约或接口绑定。",
"source_refs": [ "source_refs": [
"CommandRegistry:canonical_path=drive.mark_star", "CommandRegistry:canonical_path=drive.transfer_owner",
"cobra-help:dws drive star add", "cobra-help:dws drive permission transfer-owner",
"wukong-develop:wukong/products" "wukong-develop:wukong/products"
] ]
}, },
@@ -797,119 +906,32 @@
"wukong-develop:wukong/products" "wukong-develop:wukong/products"
] ]
}, },
"drive.get_star_list": { "drive.upload": {
"agent_summary": "获取当前用户的收藏列表,支持分页与按内容类型筛选", "agent_summary": "上传本地文件到钉盘或文档空间,或按节点 ID 确认覆盖已有文件",
"use_when": [ "use_when": [
"用户说 我的收藏/收藏列表/收藏了哪些文档" "用户要把本地文件上传到钉盘/我的文件(首选一条命令自动完成凭证+PUT+提交)时",
"上传到知识库/文档空间时加 --workspace;需要转在线文档时加 --convert",
"用户明确要求用本地文件替换已有钉盘/文档空间文件时传 --node;该模式会覆盖远端内容并要求确认"
], ],
"avoid_when": [ "avoid_when": [
"操作单个收藏用 mark_star/unmark_star" "常规场景不要拆成 upload-info + 手动 PUT + commit;仅自定义流式上传才用三步",
"要把文件作为文档正文附件插入改用 dws doc media insert",
"用户明确说文档空间且走 doc 兼容入口时可用 dws doc upload,默认仍推荐本命令",
"用户没有明确同意替换目标文件时不要使用 --node;新建上传应使用 --folder 或目标根目录"
], ],
"examples": [ "examples": [
"dws drive star list --format json", "dws drive upload --file ./report.pdf --format json",
"dws drive star list --content-types doc,sheet --limit 10 --format json" "dws drive upload --file ./README.md --node <dentryUuid> --format json"
], ],
"reviewed": true, "reviewed": true,
"review_reason": "人工审阅:对照悟空 develop 同名命令实现、Cobra Long/Example 与 Runtime 确认门禁移植;不改变命令身份、参数契约或接口绑定。", "review_reason": "人工审阅:结合本机 dws schema 实时 MCP description/parameters(经 interface_ref)、产品 Skill、Cobra Long 与 Runtime 确认门禁,按飞书风格重写选型文案;不改变命令身份、参数契约或接口绑定。",
"source_refs": [ "source_refs": [
"CommandRegistry:canonical_path=drive.get_star_list", "CommandRegistry:canonical_path=drive.upload",
"cobra-help:dws drive star list", "cobra-help:dws drive upload",
"wukong-develop:wukong/products" "Skill:skills/mono/references/products/drive.md",
] "structured-hint:internal/cli/schema_hints/selection-review.json#drive.upload",
}, "structured-hint:internal/cli/schema_hints/products/drive.json",
"drive.get_cover": { "live-mcp:none (Cobra+Skill; composite/unpinned)"
"agent_summary": "获取节点封面图片地址(文档首图/图片缩略图/类型图标)",
"use_when": [
"用户说 封面/封面图/缩略图/预览图"
],
"avoid_when": [
"设置文档封面用 doc style cover set"
],
"examples": [
"dws drive cover --node <dentryUuid> --format json"
],
"reviewed": true,
"review_reason": "人工审阅:对照悟空 develop 同名命令实现、Cobra Long/Example 与 Runtime 确认门禁移植;不改变命令身份、参数契约或接口绑定。",
"source_refs": [
"CommandRegistry:canonical_path=drive.get_cover",
"cobra-help:dws drive cover",
"wukong-develop:wukong/products"
]
},
"drive.revert_file_version": {
"agent_summary": "回滚普通文件到指定历史版本(生成新最新版本,历史不丢失)",
"use_when": [
"用户说 回滚版本/恢复到某个版本/版本回退,且目标是普通文件"
],
"avoid_when": [
"在线文档回滚用 doc version revert;在线表格用 sheet version revert"
],
"examples": [
"dws drive revert --node <dentryUuid> --version 3 --format json"
],
"reviewed": true,
"review_reason": "人工审阅:对照悟空 develop 同名命令实现、Cobra Long/Example 与 Runtime 确认门禁移植;不改变命令身份、参数契约或接口绑定。",
"source_refs": [
"CommandRegistry:canonical_path=drive.revert_file_version",
"cobra-help:dws drive revert",
"wukong-develop:wukong/products"
]
},
"drive.transfer_owner": {
"agent_summary": "转交文档或知识库所有者给指定用户(不可逆)",
"use_when": [
"用户明确要求转交文档/知识库所有权"
],
"avoid_when": [
"普通协作权限变更用 permission add/update/remove"
],
"examples": [
"dws drive permission transfer-owner --node <DOC_ID> --new-owner <userId> --reserve-role EDITOR --recursive=false --format json"
],
"reviewed": true,
"review_reason": "人工审阅:对照悟空 develop 同名命令实现、Cobra Long/Example 与 Runtime 确认门禁移植;不改变命令身份、参数契约或接口绑定。",
"source_refs": [
"CommandRegistry:canonical_path=drive.transfer_owner",
"cobra-help:dws drive permission transfer-owner",
"wukong-develop:wukong/products"
]
},
"drive.query_permission_apply_info": {
"agent_summary": "查询节点可申请的权限角色列表与审批人列表",
"use_when": [
"无权限访问文档时,先查可申请角色与审批人"
],
"avoid_when": [
"实际发起申请用 apply_permission"
],
"examples": [
"dws drive permission apply-info --node <DOC_ID> --format json"
],
"reviewed": true,
"review_reason": "人工审阅:对照悟空 develop 同名命令实现、Cobra Long/Example 与 Runtime 确认门禁移植;不改变命令身份、参数契约或接口绑定。",
"source_refs": [
"CommandRegistry:canonical_path=drive.query_permission_apply_info",
"cobra-help:dws drive permission apply-info",
"wukong-develop:wukong/products"
]
},
"drive.apply_permission": {
"agent_summary": "向审批人发起文档权限申请(会真实通知审批人)",
"use_when": [
"用户确认要为无权限文档发起权限申请"
],
"avoid_when": [
"先用 apply-info 查可申请角色与审批人;未经用户确认不得自行提交"
],
"examples": [
"dws drive permission apply --node <DOC_ID> --role READER --users <审批人userId> --format json"
],
"reviewed": true,
"review_reason": "人工审阅:对照悟空 develop 同名命令实现、Cobra Long/Example 与 Runtime 确认门禁移植;不改变命令身份、参数契约或接口绑定。",
"source_refs": [
"CommandRegistry:canonical_path=drive.apply_permission",
"cobra-help:dws drive permission apply",
"wukong-develop:wukong/products"
] ]
} }
}, },
+92 -82
View File
@@ -2,8 +2,8 @@
"version": 3, "version": 3,
"baseline": { "baseline": {
"manifest": "schema-parameter-bindings-v3", "manifest": "schema-parameter-bindings-v3",
"sha256": "sha256:f48d7b55b6ca9719c466475ee32d678eab0deaee1d79ac4e4790c7ee441ebe7b", "sha256": "sha256:ff0f76145dd27da5429434a348344c9cdf74c44eaf6f4ea5411d29d904a9066f",
"reason": "Reviewed v3 baseline after exposing the pinned chat.search_messages messageType, onlyRobotMessages, and searchConvType properties through exact Cobra flags.", "reason": "Reviewed v3 baseline after binding sheet.info --include to the include property while porting the wukong sheet info expansion flag.",
"reviewed": true "reviewed": true
}, },
"removals": { "removals": {
@@ -1112,7 +1112,8 @@
"node": "nodeId" "node": "nodeId"
}, },
"sheet.info": { "sheet.info": {
"node": "nodeId" "node": "nodeId",
"include": "include"
}, },
"sheet.insert_dimension": { "sheet.insert_dimension": {
"node": "nodeId" "node": "nodeId"
@@ -1594,7 +1595,10 @@
"attendance.vacation_update_type --when-can-leave": "Reviewed unpinned adapter: attendance.vacation_update_type has no singular pinned interface_ref; --when-can-leave is a CLI wrapper input and does not publish a direct interface property.", "attendance.vacation_update_type --when-can-leave": "Reviewed unpinned adapter: attendance.vacation_update_type has no singular pinned interface_ref; --when-can-leave is a CLI wrapper input and does not publish a direct interface property.",
"chat.add_message_favorite --open-conversation-id": "Reviewed unpinned adapter: chat.add_message_favorite has no singular pinned interface_ref; --open-conversation-id is a CLI wrapper input and does not publish a direct interface property.", "chat.add_message_favorite --open-conversation-id": "Reviewed unpinned adapter: chat.add_message_favorite has no singular pinned interface_ref; --open-conversation-id is a CLI wrapper input and does not publish a direct interface property.",
"chat.add_message_favorite --open-message-id": "Reviewed unpinned adapter: chat.add_message_favorite has no singular pinned interface_ref; --open-message-id is a CLI wrapper input and does not publish a direct interface property.", "chat.add_message_favorite --open-message-id": "Reviewed unpinned adapter: chat.add_message_favorite has no singular pinned interface_ref; --open-message-id is a CLI wrapper input and does not publish a direct interface property.",
"chat.batch_query_group_chat_settings --groups": "Reviewed unpinned adapter: chat.batch_query_group_chat_settings has no singular pinned interface_ref; --groups is a CLI wrapper input and does not publish a direct interface property.",
"chat.batch_update_group_chat_settings --items": "Reviewed unpinned adapter: chat.batch_update_group_chat_settings has no singular pinned interface_ref; --items is a CLI wrapper input and does not publish a direct interface property.",
"chat.create_text_emotion --background-id": "Runtime extension: the executable helper forwards backgroundId to im/create_text_emotion, but the pinned source-revision metadata does not declare that optional property; preserve the compatibility flag without advertising it as a pinned RPC field.", "chat.create_text_emotion --background-id": "Runtime extension: the executable helper forwards backgroundId to im/create_text_emotion, but the pinned source-revision metadata does not declare that optional property; preserve the compatibility flag without advertising it as a pinned RPC field.",
"chat.get_group_mute_config --group": "Reviewed unpinned adapter: chat.get_group_mute_config has no singular pinned interface_ref; --group is a CLI wrapper input and does not publish a direct interface property.",
"chat.list_conversation_message_v2 --open-dingtalk-id": "selects the alternate list_individual_chat_message branch", "chat.list_conversation_message_v2 --open-dingtalk-id": "selects the alternate list_individual_chat_message branch",
"chat.list_conversation_message_v2 --user": "selects the alternate list_individual_chat_message branch", "chat.list_conversation_message_v2 --user": "selects the alternate list_individual_chat_message branch",
"chat.list_message_favorites --cursor": "Reviewed unpinned adapter: chat.list_message_favorites has no singular pinned interface_ref; --cursor is a CLI wrapper input and does not publish a direct interface property.", "chat.list_message_favorites --cursor": "Reviewed unpinned adapter: chat.list_message_favorites has no singular pinned interface_ref; --cursor is a CLI wrapper input and does not publish a direct interface property.",
@@ -1611,7 +1615,12 @@
"chat.search_groups --limit": "Reviewed unpinned adapter: chat.search_groups has no singular pinned interface_ref; --limit is a CLI wrapper input and does not publish a direct interface property.", "chat.search_groups --limit": "Reviewed unpinned adapter: chat.search_groups has no singular pinned interface_ref; --limit is a CLI wrapper input and does not publish a direct interface property.",
"chat.search_messages --user": "conditional wrapper: parseCSVValues + appendChatIDArgs routes each supplied identifier to senderUserIds or senderOpenDingTakIds according to its runtime ID shape; there is no single RPC property for this flag", "chat.search_messages --user": "conditional wrapper: parseCSVValues + appendChatIDArgs routes each supplied identifier to senderUserIds or senderOpenDingTakIds according to its runtime ID shape; there is no single RPC property for this flag",
"chat.search_messages --users": "conditional wrapper/alias of --user: parseCSVValues + appendChatIDArgs routes each supplied identifier to senderUserIds or senderOpenDingTakIds according to its runtime ID shape; there is no single RPC property for this flag", "chat.search_messages --users": "conditional wrapper/alias of --user: parseCSVValues + appendChatIDArgs routes each supplied identifier to senderUserIds or senderOpenDingTakIds according to its runtime ID shape; there is no single RPC property for this flag",
"chat.send_personal_message --contact-id": "serialized into the aggregate content payload",
"chat.send_personal_message --file-path": "local upload/preprocessing input", "chat.send_personal_message --file-path": "local upload/preprocessing input",
"chat.send_personal_message --latitude": "serialized into the aggregate content payload",
"chat.send_personal_message --location-name": "serialized into the aggregate content payload",
"chat.send_personal_message --longitude": "serialized into the aggregate content payload",
"chat.send_personal_message --map-thumbnail-url": "serialized into the aggregate content payload",
"chat.send_personal_message --media-id": "serialized into the aggregate content payload", "chat.send_personal_message --media-id": "serialized into the aggregate content payload",
"chat.send_personal_message --text": "serialized into the aggregate content payload", "chat.send_personal_message --text": "serialized into the aggregate content payload",
"chat.send_personal_message --title": "serialized into the aggregate content payload", "chat.send_personal_message --title": "serialized into the aggregate content payload",
@@ -1619,6 +1628,16 @@
"chat.set_group_member_mute_list --user": "conditional wrapper/alias of --users: userIds are remotely resolved to openDingTalkIds outside dry-run and otherwise may be sent as uids; openDingTalkId inputs are sent as openDingTalkIds, so one exact property would misdescribe execution", "chat.set_group_member_mute_list --user": "conditional wrapper/alias of --users: userIds are remotely resolved to openDingTalkIds outside dry-run and otherwise may be sent as uids; openDingTalkId inputs are sent as openDingTalkIds, so one exact property would misdescribe execution",
"chat.transfer_group_owner --user": "conditional compatibility input: a userId is sent as newOwnerUid, while an openDingTalkId is sent as newOwnerOpenDingTalkId; the runtime ID-shape branch has no single RPC property", "chat.transfer_group_owner --user": "conditional compatibility input: a userId is sent as newOwnerUid, while an openDingTalkId is sent as newOwnerOpenDingTalkId; the runtime ID-shape branch has no single RPC property",
"chat.update_conv_member_roles --user": "conditional wrapper/alias of --users: splitChatIDValues sends userIds as uids and openDingTalkIds as openDingTalkIds; there is no single RPC property for this flag", "chat.update_conv_member_roles --user": "conditional wrapper/alias of --users: splitChatIDValues sends userIds as uids and openDingTalkIds as openDingTalkIds; there is no single RPC property for this flag",
"chat.update_text_emotion --background-id": "Reviewed unpinned adapter: chat.update_text_emotion has no singular pinned interface_ref; --background-id is a CLI wrapper input and does not publish a direct interface property.",
"chat.update_text_emotion --chat": "Reviewed unpinned adapter: chat.update_text_emotion has no singular pinned interface_ref; --chat is a CLI wrapper input and does not publish a direct interface property.",
"chat.update_text_emotion --conversation-id": "Reviewed unpinned adapter: chat.update_text_emotion has no singular pinned interface_ref; --conversation-id is a CLI wrapper input and does not publish a direct interface property.",
"chat.update_text_emotion --emotion-id": "Reviewed unpinned adapter: chat.update_text_emotion has no singular pinned interface_ref; --emotion-id is a CLI wrapper input and does not publish a direct interface property.",
"chat.update_text_emotion --emotion-name": "Reviewed unpinned adapter: chat.update_text_emotion has no singular pinned interface_ref; --emotion-name is a CLI wrapper input and does not publish a direct interface property.",
"chat.update_text_emotion --group": "Reviewed unpinned adapter: chat.update_text_emotion has no singular pinned interface_ref; --group is a CLI wrapper input and does not publish a direct interface property.",
"chat.update_text_emotion --id": "Reviewed unpinned adapter: chat.update_text_emotion has no singular pinned interface_ref; --id is a CLI wrapper input and does not publish a direct interface property.",
"chat.update_text_emotion --msg-id": "Reviewed unpinned adapter: chat.update_text_emotion has no singular pinned interface_ref; --msg-id is a CLI wrapper input and does not publish a direct interface property.",
"chat.update_text_emotion --old-emotion-id": "Reviewed unpinned adapter: chat.update_text_emotion has no singular pinned interface_ref; --old-emotion-id is a CLI wrapper input and does not publish a direct interface property.",
"chat.update_text_emotion --text": "Reviewed unpinned adapter: chat.update_text_emotion has no singular pinned interface_ref; --text is a CLI wrapper input and does not publish a direct interface property.",
"dev.add_dev_app_members --member-type": "Reviewed unpinned adapter: dev.add_dev_app_members has no singular pinned interface_ref; --member-type is a CLI wrapper input and does not publish a direct interface property.", "dev.add_dev_app_members --member-type": "Reviewed unpinned adapter: dev.add_dev_app_members has no singular pinned interface_ref; --member-type is a CLI wrapper input and does not publish a direct interface property.",
"dev.add_dev_app_members --unified-app-id": "Reviewed unpinned adapter: dev.add_dev_app_members has no singular pinned interface_ref; --unified-app-id is a CLI wrapper input and does not publish a direct interface property.", "dev.add_dev_app_members --unified-app-id": "Reviewed unpinned adapter: dev.add_dev_app_members has no singular pinned interface_ref; --unified-app-id is a CLI wrapper input and does not publish a direct interface property.",
"dev.add_dev_app_members --user-ids": "Reviewed unpinned adapter: dev.add_dev_app_members has no singular pinned interface_ref; --user-ids is a CLI wrapper input and does not publish a direct interface property.", "dev.add_dev_app_members --user-ids": "Reviewed unpinned adapter: dev.add_dev_app_members has no singular pinned interface_ref; --user-ids is a CLI wrapper input and does not publish a direct interface property.",
@@ -1636,8 +1655,8 @@
"dev.disable_dev_app_robot --unified-app-id": "Reviewed unpinned adapter: dev.disable_dev_app_robot has no singular pinned interface_ref; --unified-app-id is a CLI wrapper input and does not publish a direct interface property.", "dev.disable_dev_app_robot --unified-app-id": "Reviewed unpinned adapter: dev.disable_dev_app_robot has no singular pinned interface_ref; --unified-app-id is a CLI wrapper input and does not publish a direct interface property.",
"dev.enable_dev_app --unified-app-id": "Reviewed unpinned adapter: dev.enable_dev_app has no singular pinned interface_ref; --unified-app-id is a CLI wrapper input and does not publish a direct interface property.", "dev.enable_dev_app --unified-app-id": "Reviewed unpinned adapter: dev.enable_dev_app has no singular pinned interface_ref; --unified-app-id is a CLI wrapper input and does not publish a direct interface property.",
"dev.enable_dev_app_robot --unified-app-id": "Reviewed unpinned adapter: dev.enable_dev_app_robot has no singular pinned interface_ref; --unified-app-id is a CLI wrapper input and does not publish a direct interface property.", "dev.enable_dev_app_robot --unified-app-id": "Reviewed unpinned adapter: dev.enable_dev_app_robot has no singular pinned interface_ref; --unified-app-id is a CLI wrapper input and does not publish a direct interface property.",
"dev.get_dev_app --unified-app-id": "Reviewed unpinned adapter: dev.get_dev_app has no singular pinned interface_ref; --unified-app-id is a CLI wrapper input and does not publish a direct interface property.",
"dev.get_dev_app --app-key": "Reviewed unpinned adapter: dev.get_dev_app has no singular pinned interface_ref; --app-key is a CLI wrapper input and does not publish a direct interface property.", "dev.get_dev_app --app-key": "Reviewed unpinned adapter: dev.get_dev_app has no singular pinned interface_ref; --app-key is a CLI wrapper input and does not publish a direct interface property.",
"dev.get_dev_app --unified-app-id": "Reviewed unpinned adapter: dev.get_dev_app has no singular pinned interface_ref; --unified-app-id is a CLI wrapper input and does not publish a direct interface property.",
"dev.get_dev_app_credentials --unified-app-id": "Reviewed unpinned adapter: dev.get_dev_app_credentials has no singular pinned interface_ref; --unified-app-id is a CLI wrapper input and does not publish a direct interface property.", "dev.get_dev_app_credentials --unified-app-id": "Reviewed unpinned adapter: dev.get_dev_app_credentials has no singular pinned interface_ref; --unified-app-id is a CLI wrapper input and does not publish a direct interface property.",
"dev.get_dev_app_version_detail --unified-app-id": "Reviewed unpinned adapter: dev.get_dev_app_version_detail has no singular pinned interface_ref; --unified-app-id is a CLI wrapper input and does not publish a direct interface property.", "dev.get_dev_app_version_detail --unified-app-id": "Reviewed unpinned adapter: dev.get_dev_app_version_detail has no singular pinned interface_ref; --unified-app-id is a CLI wrapper input and does not publish a direct interface property.",
"dev.get_dev_app_version_detail --version-id": "Reviewed unpinned adapter: dev.get_dev_app_version_detail has no singular pinned interface_ref; --version-id is a CLI wrapper input and does not publish a direct interface property.", "dev.get_dev_app_version_detail --version-id": "Reviewed unpinned adapter: dev.get_dev_app_version_detail has no singular pinned interface_ref; --version-id is a CLI wrapper input and does not publish a direct interface property.",
@@ -1732,6 +1751,7 @@
"doc.get_document_content --scope": "Runtime extension sends scope for scoped JSONML reads, which is absent from the immutable pinned get_document_content metadata at its declared source revision.", "doc.get_document_content --scope": "Runtime extension sends scope for scoped JSONML reads, which is absent from the immutable pinned get_document_content metadata at its declared source revision.",
"doc.get_document_content --start-block-id": "Runtime extension sends startBlockId for scoped JSONML reads, which is absent from the immutable pinned get_document_content metadata at its declared source revision.", "doc.get_document_content --start-block-id": "Runtime extension sends startBlockId for scoped JSONML reads, which is absent from the immutable pinned get_document_content metadata at its declared source revision.",
"doc.get_document_content --tags": "Runtime extension sends tags for scoped JSONML reads, which is absent from the immutable pinned get_document_content metadata at its declared source revision.", "doc.get_document_content --tags": "Runtime extension sends tags for scoped JSONML reads, which is absent from the immutable pinned get_document_content metadata at its declared source revision.",
"doc.get_document_style --node": "Reviewed unpinned adapter: doc.get_document_style has no singular pinned interface_ref; --node is a CLI wrapper input and does not publish a direct interface property.",
"doc.import_get --task-id": "Reviewed unpinned adapter: doc.import_get has no singular pinned interface_ref; --task-id is a CLI wrapper input and does not publish a direct interface property.", "doc.import_get --task-id": "Reviewed unpinned adapter: doc.import_get has no singular pinned interface_ref; --task-id is a CLI wrapper input and does not publish a direct interface property.",
"doc.insert_document_block --fix-jsonml": "local JSONML normalization control", "doc.insert_document_block --fix-jsonml": "local JSONML normalization control",
"doc.insert_document_block --heading": "aggregate convenience input used to build element", "doc.insert_document_block --heading": "aggregate convenience input used to build element",
@@ -1739,6 +1759,14 @@
"doc.insert_document_block --text": "aggregate convenience input used to build element", "doc.insert_document_block --text": "aggregate convenience input used to build element",
"doc.list_document_blocks --block-id": "runtime extension sends blockId, which is absent from the pinned list_document_blocks metadata", "doc.list_document_blocks --block-id": "runtime extension sends blockId, which is absent from the pinned list_document_blocks metadata",
"doc.reply_comment --mentioned-open-conversation-id": "Runtime extension sends mentionedOpenConversationIds, which is absent from the immutable pinned reply_comment metadata at its declared source revision.", "doc.reply_comment --mentioned-open-conversation-id": "Runtime extension sends mentionedOpenConversationIds, which is absent from the immutable pinned reply_comment metadata at its declared source revision.",
"doc.style_background_clear --node": "Reviewed unpinned adapter: doc.style_background_clear has no singular pinned interface_ref; --node is a CLI wrapper input and does not publish a direct interface property.",
"doc.style_background_set --color": "Reviewed unpinned adapter: doc.style_background_set has no singular pinned interface_ref; --color is a CLI wrapper input and does not publish a direct interface property.",
"doc.style_background_set --node": "Reviewed unpinned adapter: doc.style_background_set has no singular pinned interface_ref; --node is a CLI wrapper input and does not publish a direct interface property.",
"doc.style_cover_clear --node": "Reviewed unpinned adapter: doc.style_cover_clear has no singular pinned interface_ref; --node is a CLI wrapper input and does not publish a direct interface property.",
"doc.style_cover_set --file": "Reviewed unpinned adapter: doc.style_cover_set has no singular pinned interface_ref; --file is a CLI wrapper input and does not publish a direct interface property.",
"doc.style_cover_set --image": "Reviewed unpinned adapter: doc.style_cover_set has no singular pinned interface_ref; --image is a CLI wrapper input and does not publish a direct interface property.",
"doc.style_cover_set --node": "Reviewed unpinned adapter: doc.style_cover_set has no singular pinned interface_ref; --node is a CLI wrapper input and does not publish a direct interface property.",
"doc.style_cover_set --position": "Reviewed unpinned adapter: doc.style_cover_set has no singular pinned interface_ref; --position is a CLI wrapper input and does not publish a direct interface property.",
"doc.template_apply --folder": "Reviewed unpinned adapter: doc.template_apply has no singular pinned interface_ref; --folder is a CLI wrapper input and does not publish a direct interface property.", "doc.template_apply --folder": "Reviewed unpinned adapter: doc.template_apply has no singular pinned interface_ref; --folder is a CLI wrapper input and does not publish a direct interface property.",
"doc.template_apply --name": "Reviewed unpinned adapter: doc.template_apply has no singular pinned interface_ref; --name is a CLI wrapper input and does not publish a direct interface property.", "doc.template_apply --name": "Reviewed unpinned adapter: doc.template_apply has no singular pinned interface_ref; --name is a CLI wrapper input and does not publish a direct interface property.",
"doc.template_apply --template-id": "Reviewed unpinned adapter: doc.template_apply has no singular pinned interface_ref; --template-id is a CLI wrapper input and does not publish a direct interface property.", "doc.template_apply --template-id": "Reviewed unpinned adapter: doc.template_apply has no singular pinned interface_ref; --template-id is a CLI wrapper input and does not publish a direct interface property.",
@@ -1767,7 +1795,22 @@
"doc.version_revert --node": "Reviewed unpinned adapter: doc.version_revert has no singular pinned interface_ref; --node is a CLI wrapper input and does not publish a direct interface property.", "doc.version_revert --node": "Reviewed unpinned adapter: doc.version_revert has no singular pinned interface_ref; --node is a CLI wrapper input and does not publish a direct interface property.",
"doc.version_revert --version": "Reviewed unpinned adapter: doc.version_revert has no singular pinned interface_ref; --version is a CLI wrapper input and does not publish a direct interface property.", "doc.version_revert --version": "Reviewed unpinned adapter: doc.version_revert has no singular pinned interface_ref; --version is a CLI wrapper input and does not publish a direct interface property.",
"doc.version_save --node": "Reviewed unpinned adapter: doc.version_save has no singular pinned interface_ref; --node is a CLI wrapper input and does not publish a direct interface property.", "doc.version_save --node": "Reviewed unpinned adapter: doc.version_save has no singular pinned interface_ref; --node is a CLI wrapper input and does not publish a direct interface property.",
"drive.apply_permission --node": "Reviewed unpinned adapter: drive.apply_permission has no singular pinned interface_ref; --node is a CLI wrapper input and does not publish a direct interface property.",
"drive.apply_permission --notify-mode": "Reviewed unpinned adapter: drive.apply_permission has no singular pinned interface_ref; --notify-mode is a CLI wrapper input and does not publish a direct interface property.",
"drive.apply_permission --reason": "Reviewed unpinned adapter: drive.apply_permission has no singular pinned interface_ref; --reason is a CLI wrapper input and does not publish a direct interface property.",
"drive.apply_permission --role": "Reviewed unpinned adapter: drive.apply_permission has no singular pinned interface_ref; --role is a CLI wrapper input and does not publish a direct interface property.",
"drive.apply_permission --users": "Reviewed unpinned adapter: drive.apply_permission has no singular pinned interface_ref; --users is a CLI wrapper input and does not publish a direct interface property.",
"drive.download_file --output": "local output path", "drive.download_file --output": "local output path",
"drive.download_file_version --node": "Reviewed unpinned adapter: drive.download_file_version has no singular pinned interface_ref; --node is a CLI wrapper input and does not publish a direct interface property.",
"drive.download_file_version --output": "Reviewed unpinned adapter: drive.download_file_version has no singular pinned interface_ref; --output is a CLI wrapper input and does not publish a direct interface property.",
"drive.download_file_version --version": "Reviewed unpinned adapter: drive.download_file_version has no singular pinned interface_ref; --version is a CLI wrapper input and does not publish a direct interface property.",
"drive.get_cover --node": "Reviewed unpinned adapter: drive.get_cover has no singular pinned interface_ref; --node is a CLI wrapper input and does not publish a direct interface property.",
"drive.get_star_list --content-types": "Reviewed unpinned adapter: drive.get_star_list has no singular pinned interface_ref; --content-types is a CLI wrapper input and does not publish a direct interface property.",
"drive.get_star_list --cursor": "Reviewed unpinned adapter: drive.get_star_list has no singular pinned interface_ref; --cursor is a CLI wrapper input and does not publish a direct interface property.",
"drive.get_star_list --limit": "Reviewed unpinned adapter: drive.get_star_list has no singular pinned interface_ref; --limit is a CLI wrapper input and does not publish a direct interface property.",
"drive.get_star_list --order-by": "Reviewed unpinned adapter: drive.get_star_list has no singular pinned interface_ref; --order-by is a CLI wrapper input and does not publish a direct interface property.",
"drive.get_star_list --resource-types": "Reviewed unpinned adapter: drive.get_star_list has no singular pinned interface_ref; --resource-types is a CLI wrapper input and does not publish a direct interface property.",
"drive.get_star_list --sort": "Reviewed unpinned adapter: drive.get_star_list has no singular pinned interface_ref; --sort is a CLI wrapper input and does not publish a direct interface property.",
"drive.list_files --cursor": "composite route maps to nextToken for drive.list_files or pageToken for doc.list_nodes", "drive.list_files --cursor": "composite route maps to nextToken for drive.list_files or pageToken for doc.list_nodes",
"drive.list_files --folder": "composite route maps to parentId for drive.list_files or folderId for doc.list_nodes", "drive.list_files --folder": "composite route maps to parentId for drive.list_files or folderId for doc.list_nodes",
"drive.list_files --limit": "composite route maps to maxResults for drive.list_files or pageSize for doc.list_nodes", "drive.list_files --limit": "composite route maps to maxResults for drive.list_files or pageSize for doc.list_nodes",
@@ -1776,10 +1819,12 @@
"drive.list_files --space-id": "drive-branch-only route input on a composite drive/doc command; no singular interface property is advertised", "drive.list_files --space-id": "drive-branch-only route input on a composite drive/doc command; no singular interface property is advertised",
"drive.list_files --thumbnail": "drive-branch-only option on a composite drive/doc command; no singular interface property is advertised", "drive.list_files --thumbnail": "drive-branch-only option on a composite drive/doc command; no singular interface property is advertised",
"drive.list_files --workspace": "selects the doc.list_nodes branch of the composite drive/doc command", "drive.list_files --workspace": "selects the doc.list_nodes branch of the composite drive/doc command",
"drive.mark_star --node": "Reviewed unpinned adapter: drive.mark_star has no singular pinned interface_ref; --node is a CLI wrapper input and does not publish a direct interface property.",
"drive.publish_get --node": "Reviewed unpinned adapter: drive.publish_get has no singular pinned interface_ref; --node is a CLI wrapper input and does not publish a direct interface property.", "drive.publish_get --node": "Reviewed unpinned adapter: drive.publish_get has no singular pinned interface_ref; --node is a CLI wrapper input and does not publish a direct interface property.",
"drive.publish_set --node": "Reviewed unpinned adapter: drive.publish_set has no singular pinned interface_ref; --node is a CLI wrapper input and does not publish a direct interface property.", "drive.publish_set --node": "Reviewed unpinned adapter: drive.publish_set has no singular pinned interface_ref; --node is a CLI wrapper input and does not publish a direct interface property.",
"drive.publish_set --permission": "Reviewed unpinned adapter: drive.publish_set has no singular pinned interface_ref; --permission is a CLI wrapper input and does not publish a direct interface property.", "drive.publish_set --permission": "Reviewed unpinned adapter: drive.publish_set has no singular pinned interface_ref; --permission is a CLI wrapper input and does not publish a direct interface property.",
"drive.publish_unset --node": "Reviewed unpinned adapter: drive.publish_unset has no singular pinned interface_ref; --node is a CLI wrapper input and does not publish a direct interface property.", "drive.publish_unset --node": "Reviewed unpinned adapter: drive.publish_unset has no singular pinned interface_ref; --node is a CLI wrapper input and does not publish a direct interface property.",
"drive.query_permission_apply_info --node": "Reviewed unpinned adapter: drive.query_permission_apply_info has no singular pinned interface_ref; --node is a CLI wrapper input and does not publish a direct interface property.",
"drive.recent --creator-type": "Reviewed unpinned adapter: drive.recent has no singular pinned interface_ref; --creator-type is a CLI wrapper input and does not publish a direct interface property.", "drive.recent --creator-type": "Reviewed unpinned adapter: drive.recent has no singular pinned interface_ref; --creator-type is a CLI wrapper input and does not publish a direct interface property.",
"drive.recent --cursor": "Reviewed unpinned adapter: drive.recent has no singular pinned interface_ref; --cursor is a CLI wrapper input and does not publish a direct interface property.", "drive.recent --cursor": "Reviewed unpinned adapter: drive.recent has no singular pinned interface_ref; --cursor is a CLI wrapper input and does not publish a direct interface property.",
"drive.recent --file-types": "Reviewed unpinned adapter: drive.recent has no singular pinned interface_ref; --file-types is a CLI wrapper input and does not publish a direct interface property.", "drive.recent --file-types": "Reviewed unpinned adapter: drive.recent has no singular pinned interface_ref; --file-types is a CLI wrapper input and does not publish a direct interface property.",
@@ -1790,6 +1835,14 @@
"drive.recycle_list --limit": "Reviewed unpinned adapter: drive.recycle_list has no singular pinned interface_ref; --limit is a CLI wrapper input and does not publish a direct interface property.", "drive.recycle_list --limit": "Reviewed unpinned adapter: drive.recycle_list has no singular pinned interface_ref; --limit is a CLI wrapper input and does not publish a direct interface property.",
"drive.recycle_list --space-id": "Reviewed unpinned adapter: drive.recycle_list has no singular pinned interface_ref; --space-id is a CLI wrapper input and does not publish a direct interface property.", "drive.recycle_list --space-id": "Reviewed unpinned adapter: drive.recycle_list has no singular pinned interface_ref; --space-id is a CLI wrapper input and does not publish a direct interface property.",
"drive.recycle_restore --id": "Reviewed unpinned adapter: drive.recycle_restore has no singular pinned interface_ref; --id is a CLI wrapper input and does not publish a direct interface property.", "drive.recycle_restore --id": "Reviewed unpinned adapter: drive.recycle_restore has no singular pinned interface_ref; --id is a CLI wrapper input and does not publish a direct interface property.",
"drive.revert_file_version --node": "Reviewed unpinned adapter: drive.revert_file_version has no singular pinned interface_ref; --node is a CLI wrapper input and does not publish a direct interface property.",
"drive.revert_file_version --version": "Reviewed unpinned adapter: drive.revert_file_version has no singular pinned interface_ref; --version is a CLI wrapper input and does not publish a direct interface property.",
"drive.transfer_owner --new-owner": "Reviewed unpinned adapter: drive.transfer_owner has no singular pinned interface_ref; --new-owner is a CLI wrapper input and does not publish a direct interface property.",
"drive.transfer_owner --node": "Reviewed unpinned adapter: drive.transfer_owner has no singular pinned interface_ref; --node is a CLI wrapper input and does not publish a direct interface property.",
"drive.transfer_owner --recursive": "Reviewed unpinned adapter: drive.transfer_owner has no singular pinned interface_ref; --recursive is a CLI wrapper input and does not publish a direct interface property.",
"drive.transfer_owner --reserve-role": "Reviewed unpinned adapter: drive.transfer_owner has no singular pinned interface_ref; --reserve-role is a CLI wrapper input and does not publish a direct interface property.",
"drive.transfer_owner --workspace": "Reviewed unpinned adapter: drive.transfer_owner has no singular pinned interface_ref; --workspace is a CLI wrapper input and does not publish a direct interface property.",
"drive.unmark_star --node": "Reviewed unpinned adapter: drive.unmark_star has no singular pinned interface_ref; --node is a CLI wrapper input and does not publish a direct interface property.",
"mail.create_download_session --name": "local download filename", "mail.create_download_session --name": "local download filename",
"mail.create_download_session --output": "local output path", "mail.create_download_session --output": "local output path",
"mail.create_draft --attachment": "reviewed local attachment preprocessing input", "mail.create_draft --attachment": "reviewed local attachment preprocessing input",
@@ -1835,9 +1888,16 @@
"sheet.create_pivot_table --source": "Reviewed unpinned adapter: sheet.create_pivot_table has no singular pinned interface_ref; --source is a CLI wrapper input and does not publish a direct interface property.", "sheet.create_pivot_table --source": "Reviewed unpinned adapter: sheet.create_pivot_table has no singular pinned interface_ref; --source is a CLI wrapper input and does not publish a direct interface property.",
"sheet.create_pivot_table --target-position": "Reviewed unpinned adapter: sheet.create_pivot_table has no singular pinned interface_ref; --target-position is a CLI wrapper input and does not publish a direct interface property.", "sheet.create_pivot_table --target-position": "Reviewed unpinned adapter: sheet.create_pivot_table has no singular pinned interface_ref; --target-position is a CLI wrapper input and does not publish a direct interface property.",
"sheet.create_pivot_table --target-sheet-id": "Reviewed unpinned adapter: sheet.create_pivot_table has no singular pinned interface_ref; --target-sheet-id is a CLI wrapper input and does not publish a direct interface property.", "sheet.create_pivot_table --target-sheet-id": "Reviewed unpinned adapter: sheet.create_pivot_table has no singular pinned interface_ref; --target-sheet-id is a CLI wrapper input and does not publish a direct interface property.",
"sheet.create_sheet_comment --content": "Reviewed unpinned adapter: sheet.create_sheet_comment has no singular pinned interface_ref; --content is a CLI wrapper input and does not publish a direct interface property.",
"sheet.create_sheet_comment --mention": "Reviewed unpinned adapter: sheet.create_sheet_comment has no singular pinned interface_ref; --mention is a CLI wrapper input and does not publish a direct interface property.",
"sheet.create_sheet_comment --node": "Reviewed unpinned adapter: sheet.create_sheet_comment has no singular pinned interface_ref; --node is a CLI wrapper input and does not publish a direct interface property.",
"sheet.create_sheet_comment --range": "Reviewed unpinned adapter: sheet.create_sheet_comment has no singular pinned interface_ref; --range is a CLI wrapper input and does not publish a direct interface property.",
"sheet.create_sheet_comment --sheet-id": "Reviewed unpinned adapter: sheet.create_sheet_comment has no singular pinned interface_ref; --sheet-id is a CLI wrapper input and does not publish a direct interface property.",
"sheet.delete_cond_format --yes": "local confirmation control", "sheet.delete_cond_format --yes": "local confirmation control",
"sheet.delete_pivot_table --pivot-table-id": "Reviewed unpinned adapter: sheet.delete_pivot_table has no singular pinned interface_ref; --pivot-table-id is a CLI wrapper input and does not publish a direct interface property.", "sheet.delete_pivot_table --pivot-table-id": "Reviewed unpinned adapter: sheet.delete_pivot_table has no singular pinned interface_ref; --pivot-table-id is a CLI wrapper input and does not publish a direct interface property.",
"sheet.delete_pivot_table --sheet-id": "Reviewed unpinned adapter: sheet.delete_pivot_table has no singular pinned interface_ref; --sheet-id is a CLI wrapper input and does not publish a direct interface property.", "sheet.delete_pivot_table --sheet-id": "Reviewed unpinned adapter: sheet.delete_pivot_table has no singular pinned interface_ref; --sheet-id is a CLI wrapper input and does not publish a direct interface property.",
"sheet.delete_sheet_comment --comment-key": "Reviewed unpinned adapter: sheet.delete_sheet_comment has no singular pinned interface_ref; --comment-key is a CLI wrapper input and does not publish a direct interface property.",
"sheet.delete_sheet_comment --node": "Reviewed unpinned adapter: sheet.delete_sheet_comment has no singular pinned interface_ref; --node is a CLI wrapper input and does not publish a direct interface property.",
"sheet.filter_view_get_criteria --column": "local post-filter over get_filter_views response", "sheet.filter_view_get_criteria --column": "local post-filter over get_filter_views response",
"sheet.filter_view_get_criteria --filter-view-id": "local post-filter over get_filter_views response", "sheet.filter_view_get_criteria --filter-view-id": "local post-filter over get_filter_views response",
"sheet.filter_view_info --filter-view-id": "local post-filter over get_filter_views response", "sheet.filter_view_info --filter-view-id": "local post-filter over get_filter_views response",
@@ -1847,6 +1907,13 @@
"sheet.filter_view_update_criteria --filter-view-id": "Reviewed unpinned adapter: sheet.filter_view_update_criteria has no singular pinned interface_ref; --filter-view-id is a CLI wrapper input and does not publish a direct interface property.", "sheet.filter_view_update_criteria --filter-view-id": "Reviewed unpinned adapter: sheet.filter_view_update_criteria has no singular pinned interface_ref; --filter-view-id is a CLI wrapper input and does not publish a direct interface property.",
"sheet.filter_view_update_criteria --node": "Reviewed unpinned adapter: sheet.filter_view_update_criteria has no singular pinned interface_ref; --node is a CLI wrapper input and does not publish a direct interface property.", "sheet.filter_view_update_criteria --node": "Reviewed unpinned adapter: sheet.filter_view_update_criteria has no singular pinned interface_ref; --node is a CLI wrapper input and does not publish a direct interface property.",
"sheet.filter_view_update_criteria --sheet-id": "Reviewed unpinned adapter: sheet.filter_view_update_criteria has no singular pinned interface_ref; --sheet-id is a CLI wrapper input and does not publish a direct interface property.", "sheet.filter_view_update_criteria --sheet-id": "Reviewed unpinned adapter: sheet.filter_view_update_criteria has no singular pinned interface_ref; --sheet-id is a CLI wrapper input and does not publish a direct interface property.",
"sheet.formula_verify --exit-on-error": "Reviewed unpinned adapter: sheet.formula_verify has no singular pinned interface_ref; --exit-on-error is a CLI wrapper input and does not publish a direct interface property.",
"sheet.formula_verify --max-cells": "Reviewed unpinned adapter: sheet.formula_verify has no singular pinned interface_ref; --max-cells is a CLI wrapper input and does not publish a direct interface property.",
"sheet.formula_verify --max-locations-per-error": "Reviewed unpinned adapter: sheet.formula_verify has no singular pinned interface_ref; --max-locations-per-error is a CLI wrapper input and does not publish a direct interface property.",
"sheet.formula_verify --node": "Reviewed unpinned adapter: sheet.formula_verify has no singular pinned interface_ref; --node is a CLI wrapper input and does not publish a direct interface property.",
"sheet.formula_verify --range": "Reviewed unpinned adapter: sheet.formula_verify has no singular pinned interface_ref; --range is a CLI wrapper input and does not publish a direct interface property.",
"sheet.formula_verify --sheet-id": "Reviewed unpinned adapter: sheet.formula_verify has no singular pinned interface_ref; --sheet-id is a CLI wrapper input and does not publish a direct interface property.",
"sheet.formula_verify --targets": "Reviewed unpinned adapter: sheet.formula_verify has no singular pinned interface_ref; --targets is a CLI wrapper input and does not publish a direct interface property.",
"sheet.group_dimension --group-state": "Reviewed unpinned adapter: sheet.group_dimension has no singular pinned interface_ref; --group-state is a CLI wrapper input and does not publish a direct interface property.", "sheet.group_dimension --group-state": "Reviewed unpinned adapter: sheet.group_dimension has no singular pinned interface_ref; --group-state is a CLI wrapper input and does not publish a direct interface property.",
"sheet.group_dimension --node": "Reviewed unpinned adapter: sheet.group_dimension has no singular pinned interface_ref; --node is a CLI wrapper input and does not publish a direct interface property.", "sheet.group_dimension --node": "Reviewed unpinned adapter: sheet.group_dimension has no singular pinned interface_ref; --node is a CLI wrapper input and does not publish a direct interface property.",
"sheet.group_dimension --range": "Reviewed unpinned adapter: sheet.group_dimension has no singular pinned interface_ref; --range is a CLI wrapper input and does not publish a direct interface property.", "sheet.group_dimension --range": "Reviewed unpinned adapter: sheet.group_dimension has no singular pinned interface_ref; --range is a CLI wrapper input and does not publish a direct interface property.",
@@ -1854,6 +1921,12 @@
"sheet.hide_gridline --sheet-id": "Reviewed unpinned adapter: sheet.hide_gridline has no singular pinned interface_ref; --sheet-id is a CLI wrapper input and does not publish a direct interface property.", "sheet.hide_gridline --sheet-id": "Reviewed unpinned adapter: sheet.hide_gridline has no singular pinned interface_ref; --sheet-id is a CLI wrapper input and does not publish a direct interface property.",
"sheet.list_pivot_tables --pivot-table-id": "Reviewed unpinned adapter: sheet.list_pivot_tables has no singular pinned interface_ref; --pivot-table-id is a CLI wrapper input and does not publish a direct interface property.", "sheet.list_pivot_tables --pivot-table-id": "Reviewed unpinned adapter: sheet.list_pivot_tables has no singular pinned interface_ref; --pivot-table-id is a CLI wrapper input and does not publish a direct interface property.",
"sheet.list_pivot_tables --sheet-id": "Reviewed unpinned adapter: sheet.list_pivot_tables has no singular pinned interface_ref; --sheet-id is a CLI wrapper input and does not publish a direct interface property.", "sheet.list_pivot_tables --sheet-id": "Reviewed unpinned adapter: sheet.list_pivot_tables has no singular pinned interface_ref; --sheet-id is a CLI wrapper input and does not publish a direct interface property.",
"sheet.list_sheet_comments --cursor": "Reviewed unpinned adapter: sheet.list_sheet_comments has no singular pinned interface_ref; --cursor is a CLI wrapper input and does not publish a direct interface property.",
"sheet.list_sheet_comments --limit": "Reviewed unpinned adapter: sheet.list_sheet_comments has no singular pinned interface_ref; --limit is a CLI wrapper input and does not publish a direct interface property.",
"sheet.list_sheet_comments --node": "Reviewed unpinned adapter: sheet.list_sheet_comments has no singular pinned interface_ref; --node is a CLI wrapper input and does not publish a direct interface property.",
"sheet.list_sheet_comments --range": "Reviewed unpinned adapter: sheet.list_sheet_comments has no singular pinned interface_ref; --range is a CLI wrapper input and does not publish a direct interface property.",
"sheet.list_sheet_comments --resolve-status": "Reviewed unpinned adapter: sheet.list_sheet_comments has no singular pinned interface_ref; --resolve-status is a CLI wrapper input and does not publish a direct interface property.",
"sheet.list_sheet_comments --sheet-id": "Reviewed unpinned adapter: sheet.list_sheet_comments has no singular pinned interface_ref; --sheet-id is a CLI wrapper input and does not publish a direct interface property.",
"sheet.range_batch_clear --node": "Reviewed unpinned adapter: sheet.range_batch_clear has no singular pinned interface_ref; --node is a CLI wrapper input and does not publish a direct interface property.", "sheet.range_batch_clear --node": "Reviewed unpinned adapter: sheet.range_batch_clear has no singular pinned interface_ref; --node is a CLI wrapper input and does not publish a direct interface property.",
"sheet.range_batch_clear --ranges": "Reviewed unpinned adapter: sheet.range_batch_clear has no singular pinned interface_ref; --ranges is a CLI wrapper input and does not publish a direct interface property.", "sheet.range_batch_clear --ranges": "Reviewed unpinned adapter: sheet.range_batch_clear has no singular pinned interface_ref; --ranges is a CLI wrapper input and does not publish a direct interface property.",
"sheet.range_batch_clear --type": "Reviewed unpinned adapter: sheet.range_batch_clear has no singular pinned interface_ref; --type is a CLI wrapper input and does not publish a direct interface property.", "sheet.range_batch_clear --type": "Reviewed unpinned adapter: sheet.range_batch_clear has no singular pinned interface_ref; --type is a CLI wrapper input and does not publish a direct interface property.",
@@ -1861,6 +1934,11 @@
"sheet.range_batch_set_style --continue-on-error": "Composite wrapper consumes this flag in its local multi-call error loop and never sends it to update_range.", "sheet.range_batch_set_style --continue-on-error": "Composite wrapper consumes this flag in its local multi-call error loop and never sends it to update_range.",
"sheet.range_read --range": "Reviewed unpinned adapter: sheet.range_read has no singular pinned interface_ref; --range is a CLI wrapper input and does not publish a direct interface property.", "sheet.range_read --range": "Reviewed unpinned adapter: sheet.range_read has no singular pinned interface_ref; --range is a CLI wrapper input and does not publish a direct interface property.",
"sheet.range_read --sheet-id": "Reviewed unpinned adapter: sheet.range_read has no singular pinned interface_ref; --sheet-id is a CLI wrapper input and does not publish a direct interface property.", "sheet.range_read --sheet-id": "Reviewed unpinned adapter: sheet.range_read has no singular pinned interface_ref; --sheet-id is a CLI wrapper input and does not publish a direct interface property.",
"sheet.reply_sheet_comment --comment-key": "Reviewed unpinned adapter: sheet.reply_sheet_comment has no singular pinned interface_ref; --comment-key is a CLI wrapper input and does not publish a direct interface property.",
"sheet.reply_sheet_comment --content": "Reviewed unpinned adapter: sheet.reply_sheet_comment has no singular pinned interface_ref; --content is a CLI wrapper input and does not publish a direct interface property.",
"sheet.reply_sheet_comment --emoji": "Reviewed unpinned adapter: sheet.reply_sheet_comment has no singular pinned interface_ref; --emoji is a CLI wrapper input and does not publish a direct interface property.",
"sheet.reply_sheet_comment --mention": "Reviewed unpinned adapter: sheet.reply_sheet_comment has no singular pinned interface_ref; --mention is a CLI wrapper input and does not publish a direct interface property.",
"sheet.reply_sheet_comment --node": "Reviewed unpinned adapter: sheet.reply_sheet_comment has no singular pinned interface_ref; --node is a CLI wrapper input and does not publish a direct interface property.",
"sheet.show_gridline --sheet-id": "Reviewed unpinned adapter: sheet.show_gridline has no singular pinned interface_ref; --sheet-id is a CLI wrapper input and does not publish a direct interface property.", "sheet.show_gridline --sheet-id": "Reviewed unpinned adapter: sheet.show_gridline has no singular pinned interface_ref; --sheet-id is a CLI wrapper input and does not publish a direct interface property.",
"sheet.submit_export_job --output": "local destination consumed after export job completion", "sheet.submit_export_job --output": "local destination consumed after export job completion",
"sheet.table_get --no-header": "Reviewed unpinned adapter: sheet.table_get has no singular pinned interface_ref; --no-header is a CLI wrapper input and does not publish a direct interface property.", "sheet.table_get --no-header": "Reviewed unpinned adapter: sheet.table_get has no singular pinned interface_ref; --no-header is a CLI wrapper input and does not publish a direct interface property.",
@@ -1885,6 +1963,15 @@
"sheet.update_pivot_table --pivot-table-id": "Reviewed unpinned adapter: sheet.update_pivot_table has no singular pinned interface_ref; --pivot-table-id is a CLI wrapper input and does not publish a direct interface property.", "sheet.update_pivot_table --pivot-table-id": "Reviewed unpinned adapter: sheet.update_pivot_table has no singular pinned interface_ref; --pivot-table-id is a CLI wrapper input and does not publish a direct interface property.",
"sheet.update_pivot_table --properties": "Reviewed unpinned adapter: sheet.update_pivot_table has no singular pinned interface_ref; --properties is a CLI wrapper input and does not publish a direct interface property.", "sheet.update_pivot_table --properties": "Reviewed unpinned adapter: sheet.update_pivot_table has no singular pinned interface_ref; --properties is a CLI wrapper input and does not publish a direct interface property.",
"sheet.update_pivot_table --sheet-id": "Reviewed unpinned adapter: sheet.update_pivot_table has no singular pinned interface_ref; --sheet-id is a CLI wrapper input and does not publish a direct interface property.", "sheet.update_pivot_table --sheet-id": "Reviewed unpinned adapter: sheet.update_pivot_table has no singular pinned interface_ref; --sheet-id is a CLI wrapper input and does not publish a direct interface property.",
"sheet.update_sheet_comment --comment-key": "Reviewed unpinned adapter: sheet.update_sheet_comment has no singular pinned interface_ref; --comment-key is a CLI wrapper input and does not publish a direct interface property.",
"sheet.update_sheet_comment --content": "Reviewed unpinned adapter: sheet.update_sheet_comment has no singular pinned interface_ref; --content is a CLI wrapper input and does not publish a direct interface property.",
"sheet.update_sheet_comment --node": "Reviewed unpinned adapter: sheet.update_sheet_comment has no singular pinned interface_ref; --node is a CLI wrapper input and does not publish a direct interface property.",
"sheet.version_list --cursor": "Reviewed unpinned adapter: sheet.version_list has no singular pinned interface_ref; --cursor is a CLI wrapper input and does not publish a direct interface property.",
"sheet.version_list --limit": "Reviewed unpinned adapter: sheet.version_list has no singular pinned interface_ref; --limit is a CLI wrapper input and does not publish a direct interface property.",
"sheet.version_list --node": "Reviewed unpinned adapter: sheet.version_list has no singular pinned interface_ref; --node is a CLI wrapper input and does not publish a direct interface property.",
"sheet.version_revert --node": "Reviewed unpinned adapter: sheet.version_revert has no singular pinned interface_ref; --node is a CLI wrapper input and does not publish a direct interface property.",
"sheet.version_revert --version": "Reviewed unpinned adapter: sheet.version_revert has no singular pinned interface_ref; --version is a CLI wrapper input and does not publish a direct interface property.",
"sheet.version_save --node": "Reviewed unpinned adapter: sheet.version_save has no singular pinned interface_ref; --node is a CLI wrapper input and does not publish a direct interface property.",
"sheet.write_image --file": "local upload input used to obtain resourceId/resourceUrl", "sheet.write_image --file": "local upload input used to obtain resourceId/resourceUrl",
"sheet.write_image --mime-type": "local upload metadata", "sheet.write_image --mime-type": "local upload metadata",
"sheet.write_image --name": "local upload metadata", "sheet.write_image --name": "local upload metadata",
@@ -1896,83 +1983,6 @@
"wiki.list_wikiSpaces --cursor": "composite route maps to pageToken for wiki.list_wikiSpaces or nextToken for drive.list_spaces", "wiki.list_wikiSpaces --cursor": "composite route maps to pageToken for wiki.list_wikiSpaces or nextToken for drive.list_spaces",
"wiki.list_wikiSpaces --limit": "composite route maps to pageSize for wiki.list_wikiSpaces or maxResults for drive.list_spaces", "wiki.list_wikiSpaces --limit": "composite route maps to pageSize for wiki.list_wikiSpaces or maxResults for drive.list_spaces",
"wiki.list_wikiSpaces --type": "route selector maps to wikiSpaceType or spaceType on different composite branches", "wiki.list_wikiSpaces --type": "route selector maps to wikiSpaceType or spaceType on different composite branches",
"wiki.search_wikiSpaces --type": "local route selector; myWikiSpace invokes list_wikiSpaces instead", "wiki.search_wikiSpaces --type": "local route selector; myWikiSpace invokes list_wikiSpaces instead"
"chat.batch_query_group_chat_settings --groups": "Reviewed unpinned adapter: chat.batch_query_group_chat_settings has no singular pinned interface_ref; --groups is a CLI wrapper input and does not publish a direct interface property.",
"chat.batch_update_group_chat_settings --items": "Reviewed unpinned adapter: chat.batch_update_group_chat_settings has no singular pinned interface_ref; --items is a CLI wrapper input and does not publish a direct interface property.",
"chat.get_group_mute_config --group": "Reviewed unpinned adapter: chat.get_group_mute_config has no singular pinned interface_ref; --group is a CLI wrapper input and does not publish a direct interface property.",
"chat.update_text_emotion --background-id": "Reviewed unpinned adapter: chat.update_text_emotion has no singular pinned interface_ref; --background-id is a CLI wrapper input and does not publish a direct interface property.",
"chat.update_text_emotion --chat": "Reviewed unpinned adapter: chat.update_text_emotion has no singular pinned interface_ref; --chat is a CLI wrapper input and does not publish a direct interface property.",
"chat.update_text_emotion --conversation-id": "Reviewed unpinned adapter: chat.update_text_emotion has no singular pinned interface_ref; --conversation-id is a CLI wrapper input and does not publish a direct interface property.",
"chat.update_text_emotion --emotion-id": "Reviewed unpinned adapter: chat.update_text_emotion has no singular pinned interface_ref; --emotion-id is a CLI wrapper input and does not publish a direct interface property.",
"chat.update_text_emotion --emotion-name": "Reviewed unpinned adapter: chat.update_text_emotion has no singular pinned interface_ref; --emotion-name is a CLI wrapper input and does not publish a direct interface property.",
"chat.update_text_emotion --group": "Reviewed unpinned adapter: chat.update_text_emotion has no singular pinned interface_ref; --group is a CLI wrapper input and does not publish a direct interface property.",
"chat.update_text_emotion --id": "Reviewed unpinned adapter: chat.update_text_emotion has no singular pinned interface_ref; --id is a CLI wrapper input and does not publish a direct interface property.",
"chat.update_text_emotion --msg-id": "Reviewed unpinned adapter: chat.update_text_emotion has no singular pinned interface_ref; --msg-id is a CLI wrapper input and does not publish a direct interface property.",
"chat.update_text_emotion --old-emotion-id": "Reviewed unpinned adapter: chat.update_text_emotion has no singular pinned interface_ref; --old-emotion-id is a CLI wrapper input and does not publish a direct interface property.",
"chat.update_text_emotion --text": "Reviewed unpinned adapter: chat.update_text_emotion has no singular pinned interface_ref; --text is a CLI wrapper input and does not publish a direct interface property.",
"doc.get_document_style --node": "Reviewed unpinned adapter: doc.get_document_style has no singular pinned interface_ref; --node is a CLI wrapper input and does not publish a direct interface property.",
"doc.style_background_clear --node": "Reviewed unpinned adapter: doc.style_background_clear has no singular pinned interface_ref; --node is a CLI wrapper input and does not publish a direct interface property.",
"doc.style_background_set --color": "Reviewed unpinned adapter: doc.style_background_set has no singular pinned interface_ref; --color is a CLI wrapper input and does not publish a direct interface property.",
"doc.style_background_set --node": "Reviewed unpinned adapter: doc.style_background_set has no singular pinned interface_ref; --node is a CLI wrapper input and does not publish a direct interface property.",
"doc.style_cover_clear --node": "Reviewed unpinned adapter: doc.style_cover_clear has no singular pinned interface_ref; --node is a CLI wrapper input and does not publish a direct interface property.",
"doc.style_cover_set --file": "Reviewed unpinned adapter: doc.style_cover_set has no singular pinned interface_ref; --file is a CLI wrapper input and does not publish a direct interface property.",
"doc.style_cover_set --image": "Reviewed unpinned adapter: doc.style_cover_set has no singular pinned interface_ref; --image is a CLI wrapper input and does not publish a direct interface property.",
"doc.style_cover_set --node": "Reviewed unpinned adapter: doc.style_cover_set has no singular pinned interface_ref; --node is a CLI wrapper input and does not publish a direct interface property.",
"doc.style_cover_set --position": "Reviewed unpinned adapter: doc.style_cover_set has no singular pinned interface_ref; --position is a CLI wrapper input and does not publish a direct interface property.",
"drive.apply_permission --node": "Reviewed unpinned adapter: drive.apply_permission has no singular pinned interface_ref; --node is a CLI wrapper input and does not publish a direct interface property.",
"drive.apply_permission --notify-mode": "Reviewed unpinned adapter: drive.apply_permission has no singular pinned interface_ref; --notify-mode is a CLI wrapper input and does not publish a direct interface property.",
"drive.apply_permission --reason": "Reviewed unpinned adapter: drive.apply_permission has no singular pinned interface_ref; --reason is a CLI wrapper input and does not publish a direct interface property.",
"drive.apply_permission --role": "Reviewed unpinned adapter: drive.apply_permission has no singular pinned interface_ref; --role is a CLI wrapper input and does not publish a direct interface property.",
"drive.apply_permission --users": "Reviewed unpinned adapter: drive.apply_permission has no singular pinned interface_ref; --users is a CLI wrapper input and does not publish a direct interface property.",
"drive.get_cover --node": "Reviewed unpinned adapter: drive.get_cover has no singular pinned interface_ref; --node is a CLI wrapper input and does not publish a direct interface property.",
"drive.get_star_list --content-types": "Reviewed unpinned adapter: drive.get_star_list has no singular pinned interface_ref; --content-types is a CLI wrapper input and does not publish a direct interface property.",
"drive.get_star_list --cursor": "Reviewed unpinned adapter: drive.get_star_list has no singular pinned interface_ref; --cursor is a CLI wrapper input and does not publish a direct interface property.",
"drive.get_star_list --limit": "Reviewed unpinned adapter: drive.get_star_list has no singular pinned interface_ref; --limit is a CLI wrapper input and does not publish a direct interface property.",
"drive.get_star_list --order-by": "Reviewed unpinned adapter: drive.get_star_list has no singular pinned interface_ref; --order-by is a CLI wrapper input and does not publish a direct interface property.",
"drive.get_star_list --resource-types": "Reviewed unpinned adapter: drive.get_star_list has no singular pinned interface_ref; --resource-types is a CLI wrapper input and does not publish a direct interface property.",
"drive.get_star_list --sort": "Reviewed unpinned adapter: drive.get_star_list has no singular pinned interface_ref; --sort is a CLI wrapper input and does not publish a direct interface property.",
"drive.mark_star --node": "Reviewed unpinned adapter: drive.mark_star has no singular pinned interface_ref; --node is a CLI wrapper input and does not publish a direct interface property.",
"drive.query_permission_apply_info --node": "Reviewed unpinned adapter: drive.query_permission_apply_info has no singular pinned interface_ref; --node is a CLI wrapper input and does not publish a direct interface property.",
"drive.revert_file_version --node": "Reviewed unpinned adapter: drive.revert_file_version has no singular pinned interface_ref; --node is a CLI wrapper input and does not publish a direct interface property.",
"drive.revert_file_version --version": "Reviewed unpinned adapter: drive.revert_file_version has no singular pinned interface_ref; --version is a CLI wrapper input and does not publish a direct interface property.",
"drive.transfer_owner --new-owner": "Reviewed unpinned adapter: drive.transfer_owner has no singular pinned interface_ref; --new-owner is a CLI wrapper input and does not publish a direct interface property.",
"drive.transfer_owner --node": "Reviewed unpinned adapter: drive.transfer_owner has no singular pinned interface_ref; --node is a CLI wrapper input and does not publish a direct interface property.",
"drive.transfer_owner --recursive": "Reviewed unpinned adapter: drive.transfer_owner has no singular pinned interface_ref; --recursive is a CLI wrapper input and does not publish a direct interface property.",
"drive.transfer_owner --reserve-role": "Reviewed unpinned adapter: drive.transfer_owner has no singular pinned interface_ref; --reserve-role is a CLI wrapper input and does not publish a direct interface property.",
"drive.transfer_owner --workspace": "Reviewed unpinned adapter: drive.transfer_owner has no singular pinned interface_ref; --workspace is a CLI wrapper input and does not publish a direct interface property.",
"drive.unmark_star --node": "Reviewed unpinned adapter: drive.unmark_star has no singular pinned interface_ref; --node is a CLI wrapper input and does not publish a direct interface property.",
"sheet.create_sheet_comment --content": "Reviewed unpinned adapter: sheet.create_sheet_comment has no singular pinned interface_ref; --content is a CLI wrapper input and does not publish a direct interface property.",
"sheet.create_sheet_comment --mention": "Reviewed unpinned adapter: sheet.create_sheet_comment has no singular pinned interface_ref; --mention is a CLI wrapper input and does not publish a direct interface property.",
"sheet.create_sheet_comment --node": "Reviewed unpinned adapter: sheet.create_sheet_comment has no singular pinned interface_ref; --node is a CLI wrapper input and does not publish a direct interface property.",
"sheet.create_sheet_comment --range": "Reviewed unpinned adapter: sheet.create_sheet_comment has no singular pinned interface_ref; --range is a CLI wrapper input and does not publish a direct interface property.",
"sheet.create_sheet_comment --sheet-id": "Reviewed unpinned adapter: sheet.create_sheet_comment has no singular pinned interface_ref; --sheet-id is a CLI wrapper input and does not publish a direct interface property.",
"sheet.delete_sheet_comment --comment-key": "Reviewed unpinned adapter: sheet.delete_sheet_comment has no singular pinned interface_ref; --comment-key is a CLI wrapper input and does not publish a direct interface property.",
"sheet.delete_sheet_comment --node": "Reviewed unpinned adapter: sheet.delete_sheet_comment has no singular pinned interface_ref; --node is a CLI wrapper input and does not publish a direct interface property.",
"sheet.formula_verify --max-cells": "Reviewed unpinned adapter: sheet.formula_verify has no singular pinned interface_ref; --max-cells is a CLI wrapper input and does not publish a direct interface property.",
"sheet.formula_verify --max-locations-per-error": "Reviewed unpinned adapter: sheet.formula_verify has no singular pinned interface_ref; --max-locations-per-error is a CLI wrapper input and does not publish a direct interface property.",
"sheet.formula_verify --node": "Reviewed unpinned adapter: sheet.formula_verify has no singular pinned interface_ref; --node is a CLI wrapper input and does not publish a direct interface property.",
"sheet.formula_verify --range": "Reviewed unpinned adapter: sheet.formula_verify has no singular pinned interface_ref; --range is a CLI wrapper input and does not publish a direct interface property.",
"sheet.formula_verify --sheet-id": "Reviewed unpinned adapter: sheet.formula_verify has no singular pinned interface_ref; --sheet-id is a CLI wrapper input and does not publish a direct interface property.",
"sheet.formula_verify --targets": "Reviewed unpinned adapter: sheet.formula_verify has no singular pinned interface_ref; --targets is a CLI wrapper input and does not publish a direct interface property.",
"sheet.list_sheet_comments --cursor": "Reviewed unpinned adapter: sheet.list_sheet_comments has no singular pinned interface_ref; --cursor is a CLI wrapper input and does not publish a direct interface property.",
"sheet.list_sheet_comments --limit": "Reviewed unpinned adapter: sheet.list_sheet_comments has no singular pinned interface_ref; --limit is a CLI wrapper input and does not publish a direct interface property.",
"sheet.list_sheet_comments --node": "Reviewed unpinned adapter: sheet.list_sheet_comments has no singular pinned interface_ref; --node is a CLI wrapper input and does not publish a direct interface property.",
"sheet.list_sheet_comments --range": "Reviewed unpinned adapter: sheet.list_sheet_comments has no singular pinned interface_ref; --range is a CLI wrapper input and does not publish a direct interface property.",
"sheet.list_sheet_comments --resolve-status": "Reviewed unpinned adapter: sheet.list_sheet_comments has no singular pinned interface_ref; --resolve-status is a CLI wrapper input and does not publish a direct interface property.",
"sheet.list_sheet_comments --sheet-id": "Reviewed unpinned adapter: sheet.list_sheet_comments has no singular pinned interface_ref; --sheet-id is a CLI wrapper input and does not publish a direct interface property.",
"sheet.reply_sheet_comment --comment-key": "Reviewed unpinned adapter: sheet.reply_sheet_comment has no singular pinned interface_ref; --comment-key is a CLI wrapper input and does not publish a direct interface property.",
"sheet.reply_sheet_comment --content": "Reviewed unpinned adapter: sheet.reply_sheet_comment has no singular pinned interface_ref; --content is a CLI wrapper input and does not publish a direct interface property.",
"sheet.reply_sheet_comment --emoji": "Reviewed unpinned adapter: sheet.reply_sheet_comment has no singular pinned interface_ref; --emoji is a CLI wrapper input and does not publish a direct interface property.",
"sheet.reply_sheet_comment --mention": "Reviewed unpinned adapter: sheet.reply_sheet_comment has no singular pinned interface_ref; --mention is a CLI wrapper input and does not publish a direct interface property.",
"sheet.reply_sheet_comment --node": "Reviewed unpinned adapter: sheet.reply_sheet_comment has no singular pinned interface_ref; --node is a CLI wrapper input and does not publish a direct interface property.",
"sheet.update_sheet_comment --comment-key": "Reviewed unpinned adapter: sheet.update_sheet_comment has no singular pinned interface_ref; --comment-key is a CLI wrapper input and does not publish a direct interface property.",
"sheet.update_sheet_comment --content": "Reviewed unpinned adapter: sheet.update_sheet_comment has no singular pinned interface_ref; --content is a CLI wrapper input and does not publish a direct interface property.",
"sheet.update_sheet_comment --node": "Reviewed unpinned adapter: sheet.update_sheet_comment has no singular pinned interface_ref; --node is a CLI wrapper input and does not publish a direct interface property.",
"sheet.version_list --cursor": "Reviewed unpinned adapter: sheet.version_list has no singular pinned interface_ref; --cursor is a CLI wrapper input and does not publish a direct interface property.",
"sheet.version_list --limit": "Reviewed unpinned adapter: sheet.version_list has no singular pinned interface_ref; --limit is a CLI wrapper input and does not publish a direct interface property.",
"sheet.version_list --node": "Reviewed unpinned adapter: sheet.version_list has no singular pinned interface_ref; --node is a CLI wrapper input and does not publish a direct interface property.",
"sheet.version_revert --node": "Reviewed unpinned adapter: sheet.version_revert has no singular pinned interface_ref; --node is a CLI wrapper input and does not publish a direct interface property.",
"sheet.version_revert --version": "Reviewed unpinned adapter: sheet.version_revert has no singular pinned interface_ref; --version is a CLI wrapper input and does not publish a direct interface property.",
"sheet.version_save --node": "Reviewed unpinned adapter: sheet.version_save has no singular pinned interface_ref; --node is a CLI wrapper input and does not publish a direct interface property."
} }
} }
+36 -3
View File
@@ -1582,8 +1582,23 @@ func newChatCommand() *cobra.Command {
contentJSON = fmt.Sprintf(`{"dentryId":%d,"spaceId":%d,"fileName":"%s","fileType":"%s","filePath":"%s","fileSize":%d}`, contentJSON = fmt.Sprintf(`{"dentryId":%d,"spaceId":%d,"fileName":"%s","fileType":"%s","filePath":"%s","fileSize":%d}`,
dentryId, spaceId, fileName, fileType, filePath, fileSize) dentryId, spaceId, fileName, fileType, filePath, fileSize)
} }
case "location":
latitude, _ := cmd.Flags().GetString("latitude")
longitude, _ := cmd.Flags().GetString("longitude")
locationName, _ := cmd.Flags().GetString("location-name")
mapThumbnailUrl, _ := cmd.Flags().GetString("map-thumbnail-url")
if latitude == "" || longitude == "" || locationName == "" || mapThumbnailUrl == "" {
return fmt.Errorf("--latitude, --longitude, --location-name, --map-thumbnail-url are all required for msgType=location")
}
contentJSON = fmt.Sprintf(`{"locationName":"%s","longitude":"%s","latitude":"%s","mapThumbnailUrl":"%s"}`, locationName, longitude, latitude, mapThumbnailUrl)
case "profile":
contactID, _ := cmd.Flags().GetString("contact-id")
if contactID == "" {
return fmt.Errorf("--contact-id is required for msgType=profile")
}
contentJSON = fmt.Sprintf(`{"openDingTalkId":"%s"}`, contactID)
default: default:
return fmt.Errorf("unsupported --msg-type: %s (supported: image, file, audio, video)", msgType) return fmt.Errorf("unsupported --msg-type: %s (supported: image, file, audio, video, location, profile)", msgType)
} }
params := map[string]any{ params := map[string]any{
@@ -2184,8 +2199,10 @@ func newChatCommand() *cobra.Command {
} }
if cmd.Flags().Changed("only-robot") { if cmd.Flags().Changed("only-robot") {
toolArgs["onlyRobotMessages"], _ = cmd.Flags().GetBool("only-robot") toolArgs["onlyRobotMessages"], _ = cmd.Flags().GetBool("only-robot")
} else if cmd.Flags().Changed("only-robot-messages") {
toolArgs["onlyRobotMessages"], _ = cmd.Flags().GetBool("only-robot-messages")
} }
if v, _ := cmd.Flags().GetString("conversation-type"); v != "" { if v := flagOrFallback(cmd, "conversation-type", "search-conv-type"); v != "" {
toolArgs["searchConvType"] = v toolArgs["searchConvType"] = v
} }
@@ -2492,7 +2509,12 @@ func newChatCommand() *cobra.Command {
chatMessageSendCmd.Flags().Bool("at-all", false, "@所有人(仅群聊时生效,可选),设置时,消息内容中一定要包含对应的占位符<@all>") chatMessageSendCmd.Flags().Bool("at-all", false, "@所有人(仅群聊时生效,可选),设置时,消息内容中一定要包含对应的占位符<@all>")
chatMessageSendCmd.Flags().String("at-open-dingtalk-ids", "", "@指定成员的 openDingTalkId 列表,逗号分隔(仅群聊时生效,可选),设置--at-open-dingtalk-ids openDingTalkId1,openDingTalkId2时,消息内容中一定要包含对应格式的占位符<@openDingTalkId1> <@openDingTalkId2>") chatMessageSendCmd.Flags().String("at-open-dingtalk-ids", "", "@指定成员的 openDingTalkId 列表,逗号分隔(仅群聊时生效,可选),设置--at-open-dingtalk-ids openDingTalkId1,openDingTalkId2时,消息内容中一定要包含对应格式的占位符<@openDingTalkId1> <@openDingTalkId2>")
chatMessageSendCmd.Flags().String("media-id", "", "上游已提供的图片 mediaId(仅旧版 msgType=image;CLI 不提供本地上传到 mediaId)") chatMessageSendCmd.Flags().String("media-id", "", "上游已提供的图片 mediaId(仅旧版 msgType=image;CLI 不提供本地上传到 mediaId)")
chatMessageSendCmd.Flags().String("msg-type", "", "富媒体消息类型: image/file/audio/video(本地图片/文件推荐 file --file-path;image 仅接受已有 mediaId)") chatMessageSendCmd.Flags().String("msg-type", "", "富媒体消息类型: image/file/audio/video/location/profile(本地图片/文件推荐 file --file-path;image 仅接受已有 mediaId)")
chatMessageSendCmd.Flags().String("latitude", "", "位置消息纬度(msgType=location 时必填)")
chatMessageSendCmd.Flags().String("longitude", "", "位置消息经度(msgType=location 时必填)")
chatMessageSendCmd.Flags().String("location-name", "", "位置消息地址名称(msgType=location 时必填)")
chatMessageSendCmd.Flags().String("map-thumbnail-url", "", "位置消息地图缩略图 mediaId,形如 @mediaId(msgType=location 时必填)")
chatMessageSendCmd.Flags().String("contact-id", "", "联系人名片 openDingTalkId(msgType=profile 时必填)")
chatMessageSendCmd.Flags().Int64("dentry-id", 0, "文件 dentryId(与 --space-id 成对传入时跳过自动上传)") chatMessageSendCmd.Flags().Int64("dentry-id", 0, "文件 dentryId(与 --space-id 成对传入时跳过自动上传)")
chatMessageSendCmd.Flags().Int64("space-id", 0, "空间 ID(与 --dentry-id 成对传入时跳过自动上传)") chatMessageSendCmd.Flags().Int64("space-id", 0, "空间 ID(与 --dentry-id 成对传入时跳过自动上传)")
chatMessageSendCmd.Flags().String("file-name", "", "文件名") chatMessageSendCmd.Flags().String("file-name", "", "文件名")
@@ -2686,7 +2708,11 @@ func newChatCommand() *cobra.Command {
_ = chatMessageSearchAdvancedCmd.Flags().MarkHidden("group") _ = chatMessageSearchAdvancedCmd.Flags().MarkHidden("group")
chatMessageSearchAdvancedCmd.Flags().String("message-type", "", "下层消息类型过滤值(可选,以当前 IM Schema 支持值为准)") chatMessageSearchAdvancedCmd.Flags().String("message-type", "", "下层消息类型过滤值(可选,以当前 IM Schema 支持值为准)")
chatMessageSearchAdvancedCmd.Flags().Bool("only-robot", false, "只搜索机器人消息(可选;显式传 false 时也会传给下层)") chatMessageSearchAdvancedCmd.Flags().Bool("only-robot", false, "只搜索机器人消息(可选;显式传 false 时也会传给下层)")
chatMessageSearchAdvancedCmd.Flags().Bool("only-robot-messages", false, "--only-robot 的别名")
_ = chatMessageSearchAdvancedCmd.Flags().MarkHidden("only-robot-messages")
chatMessageSearchAdvancedCmd.Flags().String("conversation-type", "", "下层会话类型过滤值(可选,以当前 IM Schema 支持值为准)") chatMessageSearchAdvancedCmd.Flags().String("conversation-type", "", "下层会话类型过滤值(可选,以当前 IM Schema 支持值为准)")
chatMessageSearchAdvancedCmd.Flags().String("search-conv-type", "", "--conversation-type 的别名")
_ = chatMessageSearchAdvancedCmd.Flags().MarkHidden("search-conv-type")
chatMessageSearchAdvancedCmd.Flags().String("start", "", "开始时间,ISO-8601 格式(可选)") chatMessageSearchAdvancedCmd.Flags().String("start", "", "开始时间,ISO-8601 格式(可选)")
chatMessageSearchAdvancedCmd.Flags().String("end", "", "结束时间,ISO-8601 格式(可选)") chatMessageSearchAdvancedCmd.Flags().String("end", "", "结束时间,ISO-8601 格式(可选)")
chatMessageSearchAdvancedCmd.Flags().String("cursor", "0", "分页游标(默认 \"0\")") chatMessageSearchAdvancedCmd.Flags().String("cursor", "0", "分页游标(默认 \"0\")")
@@ -5409,6 +5435,13 @@ pl_PL, sv_SE, fi_FI, cs_CZ, ar_SA, tl_PH, he_IL, nl_NL, lo_LA, it_IT`,
if len(items) > 100 { if len(items) > 100 {
return fmt.Errorf("--items batch size %d exceeds limit 100", len(items)) return fmt.Errorf("--items batch size %d exceeds limit 100", len(items))
} }
// 每项必须携带非空 openConversationId,缺失时 fail-fast,避免下发无效批量更新。
for i, item := range items {
cid, _ := item["openConversationId"].(string)
if strings.TrimSpace(cid) == "" {
return fmt.Errorf("--items[%d] 缺少非空 openConversationId", i)
}
}
return callMCPToolOnServer("im", "batch_update_group_chat_settings", map[string]any{ return callMCPToolOnServer("im", "batch_update_group_chat_settings", map[string]any{
"items": items, "items": items,
}) })
@@ -0,0 +1,209 @@
package helpers
import (
"io"
"reflect"
"strings"
"testing"
"github.com/DingTalk-Real-AI/dingtalk-workspace-cli/pkg/edition"
)
func executeChatPortsCoverageCommand(t *testing.T, caller edition.ToolCaller, args ...string) error {
t.Helper()
previousDeps := deps
t.Cleanup(func() { deps = previousDeps })
InitDeps(caller)
deps.Out.w = io.Discard
deps.Out.errW = io.Discard
root := newChatCommand()
root.SilenceErrors = true
root.SilenceUsage = true
root.SetArgs(args)
return root.Execute()
}
func TestCrossPlatformCoverageChatUpdateTextEmotion(t *testing.T) {
tests := []struct {
name string
args []string
want guardedMutationCall
}{
{
name: "conversation-id flag",
args: []string{
"message", "update-text-emotion",
"--conversation-id", "conv-1",
"--msg-id", "msg-1",
"--old-emotion-id", "old-1",
"--emotion-id", "new-1",
"--emotion-name", "like",
"--text", "nice",
"--background-id", "im_bg_5",
},
want: guardedMutationCall{
productID: "im",
toolName: "update_text_emotion",
args: map[string]any{
"openConversationId": "conv-1",
"openMsgId": "msg-1",
"oldEmotionId": "old-1",
"emotionId": "new-1",
"emotionName": "like",
"text": "nice",
"backgroundId": "im_bg_5",
},
},
},
{
name: "group alias for conversation-id",
args: []string{
"message", "update-text-emotion",
"--group", "conv-2",
"--msg-id", "msg-2",
"--old-emotion-id", "old-2",
"--emotion-id", "new-2",
"--emotion-name", "heart",
"--text", "great",
"--background-id", "im_bg_1",
},
want: guardedMutationCall{
productID: "im",
toolName: "update_text_emotion",
args: map[string]any{
"openConversationId": "conv-2",
"openMsgId": "msg-2",
"oldEmotionId": "old-2",
"emotionId": "new-2",
"emotionName": "heart",
"text": "great",
"backgroundId": "im_bg_1",
},
},
},
}
for _, test := range tests {
test := test
t.Run(test.name, func(t *testing.T) {
caller := &guardedMutationCaller{}
err := executeGuardedMutationCommand(t, caller, newChatCommand, test.args...)
if err != nil {
t.Fatalf("chat %s returned error: %v", strings.Join(test.args, " "), err)
}
if len(caller.calls) != 1 || !reflect.DeepEqual(caller.calls[0], test.want) {
t.Fatalf("tool calls = %#v, want %#v", caller.calls, test.want)
}
})
}
}
func TestCrossPlatformCoverageChatUpdateTextEmotionRequiredFlags(t *testing.T) {
fullArgs := []string{
"message", "update-text-emotion",
"--conversation-id", "conv-1",
"--msg-id", "msg-1",
"--old-emotion-id", "old-1",
"--emotion-id", "new-1",
"--emotion-name", "like",
"--text", "nice",
"--background-id", "im_bg_5",
}
dropFlag := func(name string) []string {
out := make([]string, 0, len(fullArgs))
for i := 0; i < len(fullArgs); i++ {
if fullArgs[i] == name {
i++ // skip the flag value too
continue
}
out = append(out, fullArgs[i])
}
return out
}
tests := []struct {
name string
args []string
wantErr string
}{
{
name: "missing conversation-id and aliases",
args: dropFlag("--conversation-id"),
wantErr: "--conversation-id",
},
{
name: "missing old-emotion-id",
args: dropFlag("--old-emotion-id"),
wantErr: "--old-emotion-id",
},
{
name: "missing msg-id and background-id",
args: []string{
"message", "update-text-emotion",
"--conversation-id", "conv-1",
"--old-emotion-id", "old-1",
"--emotion-id", "new-1",
"--emotion-name", "like",
"--text", "nice",
},
wantErr: "--msg-id, --background-id",
},
}
for _, test := range tests {
test := test
t.Run(test.name, func(t *testing.T) {
caller := &guardedMutationCaller{}
err := executeGuardedMutationCommand(t, caller, newChatCommand, test.args...)
if err == nil || !strings.Contains(err.Error(), test.wantErr) {
t.Fatalf("err = %v, want message containing %q", err, test.wantErr)
}
if len(caller.calls) != 0 {
t.Fatalf("tool calls = %#v, want none", caller.calls)
}
})
}
}
func TestCrossPlatformCoverageChatGroupGetMuteConfig(t *testing.T) {
caller := &guardedMutationCaller{}
err := executeGuardedMutationCommand(t, caller, newChatCommand,
"group", "get-mute-config", "--group", "conv-1")
if err != nil {
t.Fatalf("get-mute-config returned error: %v", err)
}
want := guardedMutationCall{
productID: "im",
toolName: "get_group_mute_config",
args: map[string]any{"openConversationId": "conv-1"},
}
if len(caller.calls) != 1 || !reflect.DeepEqual(caller.calls[0], want) {
t.Fatalf("tool calls = %#v, want %#v", caller.calls, want)
}
}
func TestCrossPlatformCoverageChatGroupGetMuteConfigRecordsRawArgs(t *testing.T) {
caller := &depthArgsRecordingCaller{steps: []scriptedToolStep{
{text: `{"muteBlackList":[],"muteWhiteList":[]}`},
}}
err := executeChatPortsCoverageCommand(t, caller,
"group", "get-mute-config", "--group", "conv-9")
if err != nil {
t.Fatal(err)
}
if len(caller.calls) != 1 {
t.Fatalf("calls = %#v, want 1", caller.calls)
}
if caller.calls[0]["openConversationId"] != "conv-9" {
t.Fatalf("raw args = %#v", caller.calls[0])
}
}
func TestCrossPlatformCoverageChatGroupGetMuteConfigRequiresGroup(t *testing.T) {
caller := &guardedMutationCaller{}
err := executeGuardedMutationCommand(t, caller, newChatCommand,
"group", "get-mute-config")
if err == nil || !strings.Contains(err.Error(), "--group") {
t.Fatalf("err = %v, want message containing --group", err)
}
if len(caller.calls) != 0 {
t.Fatalf("tool calls = %#v, want none", caller.calls)
}
}
+101
View File
@@ -44,6 +44,36 @@ func TestCrossPlatformCoverageChatGroupUserSettingsSetValidation(t *testing.T) {
} }
} }
func TestCrossPlatformCoverageChatGroupUserSettingsSetItemValidation(t *testing.T) {
caller := &guardedMutationCaller{}
err := executeGuardedMutationCommand(t, caller, newChatCommand,
"group", "user-settings", "set", "--items", `[{"top":true}]`)
if err == nil || !strings.Contains(err.Error(), "openConversationId") || !strings.Contains(err.Error(), "--items[0]") {
t.Fatalf("err = %v, want missing openConversationId error", err)
}
if len(caller.calls) != 0 {
t.Fatalf("calls = %#v, want no MCP call on invalid item", caller.calls)
}
// 非首项缺失时错误需带正确下标;空白字符串同样视为缺失。
caller = &guardedMutationCaller{}
err = executeGuardedMutationCommand(t, caller, newChatCommand,
"group", "user-settings", "set", "--items", `[{"openConversationId":"cid1"},{"openConversationId":" "}]`)
if err == nil || !strings.Contains(err.Error(), "--items[1]") {
t.Fatalf("err = %v, want --items[1] error", err)
}
// 合法条目仍然成功下发。
caller = &guardedMutationCaller{}
if err := executeGuardedMutationCommand(t, caller, newChatCommand,
"group", "user-settings", "set", "--items", `[{"openConversationId":"cid1","top":true,"mute":false}]`); err != nil {
t.Fatal(err)
}
if len(caller.calls) != 1 || caller.calls[0].toolName != "batch_update_group_chat_settings" {
t.Fatalf("calls = %#v", caller.calls)
}
}
func TestCrossPlatformCoverageChatGroupUserSettingsSetHappyPath(t *testing.T) { func TestCrossPlatformCoverageChatGroupUserSettingsSetHappyPath(t *testing.T) {
caller := &guardedMutationCaller{} caller := &guardedMutationCaller{}
err := executeGuardedMutationCommand(t, caller, newChatCommand, err := executeGuardedMutationCommand(t, caller, newChatCommand,
@@ -59,3 +89,74 @@ func TestCrossPlatformCoverageChatGroupUserSettingsSetHappyPath(t *testing.T) {
t.Fatalf("call = %#v", call) t.Fatalf("call = %#v", call)
} }
} }
func TestCrossPlatformCoverageChatMessageSendLocation(t *testing.T) {
caller := &guardedMutationCaller{}
err := executeGuardedMutationCommand(t, caller, newChatCommand,
"message", "send", "--group", "cid1", "--msg-type", "location", "--latitude", "39.9")
if err == nil || !strings.Contains(err.Error(), "are all required for msgType=location") {
t.Fatalf("err = %v, want missing location params", err)
}
caller = &guardedMutationCaller{}
err = executeGuardedMutationCommand(t, caller, newChatCommand,
"message", "send", "--group", "cid1", "--msg-type", "location",
"--latitude", "39.9", "--longitude", "116.4", "--location-name", "国贸", "--map-thumbnail-url", "@media1")
if err != nil {
t.Fatal(err)
}
if len(caller.calls) != 1 {
t.Fatalf("calls = %#v", caller.calls)
}
call := caller.calls[0]
if call.args["msgType"] != "location" {
t.Fatalf("msgType = %#v", call.args["msgType"])
}
content, _ := call.args["content"].(string)
if !strings.Contains(content, `"latitude":"39.9"`) || !strings.Contains(content, `"mapThumbnailUrl":"@media1"`) {
t.Fatalf("content = %s", content)
}
}
func TestCrossPlatformCoverageChatMessageSendProfile(t *testing.T) {
caller := &guardedMutationCaller{}
err := executeGuardedMutationCommand(t, caller, newChatCommand,
"message", "send", "--group", "cid1", "--msg-type", "profile")
if err == nil || !strings.Contains(err.Error(), "--contact-id is required") {
t.Fatalf("err = %v, want contact-id required", err)
}
caller = &guardedMutationCaller{}
err = executeGuardedMutationCommand(t, caller, newChatCommand,
"message", "send", "--group", "cid1", "--msg-type", "profile", "--contact-id", "od123")
if err != nil {
t.Fatal(err)
}
if len(caller.calls) != 1 {
t.Fatalf("calls = %#v", caller.calls)
}
content, _ := caller.calls[0].args["content"].(string)
if !strings.Contains(content, `"openDingTalkId":"od123"`) {
t.Fatalf("content = %s", content)
}
}
func TestCrossPlatformCoverageChatSearchAdvancedWukongAliases(t *testing.T) {
caller := &guardedMutationCaller{}
err := executeGuardedMutationCommand(t, caller, newChatCommand,
"message", "search-advanced", "--query", "通知",
"--only-robot-messages", "--search-conv-type", "group_chat")
if err != nil {
t.Fatal(err)
}
if len(caller.calls) != 1 {
t.Fatalf("calls = %#v", caller.calls)
}
args := caller.calls[0].args
if args["onlyRobotMessages"] != true {
t.Fatalf("onlyRobotMessages = %#v", args["onlyRobotMessages"])
}
if args["searchConvType"] != "group_chat" {
t.Fatalf("searchConvType = %#v", args["searchConvType"])
}
}
+15 -3
View File
@@ -177,7 +177,12 @@ func runDocStyleCoverSet(cmd *cobra.Command, _ []string) error {
if deps.Caller.DryRun() { if deps.Caller.DryRun() {
cover["resourceId"] = fmt.Sprintf("(pending upload from --file %s)", filePath) cover["resourceId"] = fmt.Sprintf("(pending upload from --file %s)", filePath)
} else { } else {
resourceID, err := uploadDocStyleImage(context.Background(), nodeID, filePath, filepath.Base(filePath), mimeType, fileSize) // 使用命令上下文,让 Ctrl-C 能够取消上传凭证获取与 OSS 上传两次网络请求。
ctx := cmd.Context()
if ctx == nil {
ctx = context.Background()
}
resourceID, err := uploadDocStyleImage(ctx, nodeID, filePath, filepath.Base(filePath), mimeType, fileSize)
if err != nil { if err != nil {
return err return err
} }
@@ -247,9 +252,13 @@ func runDocStyleGet(cmd *cobra.Command, _ []string) error {
}) })
} }
// docStyleCoverMaxBytes 是 cover set --file 本地图片的大小上限(20 MiB)。
// 悟空参考实现未设上限,此处补充客户端 fail-fast,避免超大文件被先行上传。
const docStyleCoverMaxBytes = 20 << 20
// validateCoverImageFile 对 cover set --file 做本地校验(不上传/不读取文件内容): // validateCoverImageFile 对 cover set --file 做本地校验(不上传/不读取文件内容):
// 文件须存在、非目录,且按扩展名推断的 MIME 必须为 image/*。返回 mimeType 与文件大小, // 文件须存在、非目录、不超过大小上限,且按扩展名推断的 MIME 必须为 image/*。
// 供 dry-run 预览与真实上传前 fail-fast,避免非法文件被先行上传。 // 返回 mimeType 与文件大小,供 dry-run 预览与真实上传前 fail-fast,避免非法文件被先行上传。
func validateCoverImageFile(filePath string) (mimeType string, fileSize int64, err error) { func validateCoverImageFile(filePath string) (mimeType string, fileSize int64, err error) {
fileInfo, err := os.Stat(filePath) fileInfo, err := os.Stat(filePath)
if err != nil { if err != nil {
@@ -258,6 +267,9 @@ func validateCoverImageFile(filePath string) (mimeType string, fileSize int64, e
if fileInfo.IsDir() { if fileInfo.IsDir() {
return "", 0, fmt.Errorf("%s is a directory, not a file", filePath) return "", 0, fmt.Errorf("%s is a directory, not a file", filePath)
} }
if fileInfo.Size() > docStyleCoverMaxBytes {
return "", 0, fmt.Errorf("封面图片文件过大:上限 %d 字节 (20 MiB),实际 %d 字节 (%s)", int64(docStyleCoverMaxBytes), fileInfo.Size(), filepath.Base(filePath))
}
mimeType = inferMimeType(filepath.Base(filePath)) mimeType = inferMimeType(filepath.Base(filePath))
if !strings.HasPrefix(mimeType, "image/") { if !strings.HasPrefix(mimeType, "image/") {
return "", 0, fmt.Errorf("cover --file must be an image (png/jpg/jpeg/gif/svg/webp), got %s", filepath.Base(filePath)) return "", 0, fmt.Errorf("cover --file must be an image (png/jpg/jpeg/gif/svg/webp), got %s", filepath.Base(filePath))
+93
View File
@@ -135,3 +135,96 @@ func TestCrossPlatformCoverageUploadDocStyleImageFailures(t *testing.T) {
t.Fatal("invalid upload info returned nil") t.Fatal("invalid upload info returned nil")
} }
} }
func TestCrossPlatformCoverageValidateCoverImageFileSizeLimit(t *testing.T) {
// 上限以内的正常图片通过校验。
path := writeTempImage(t, "cover.png")
if _, _, err := validateCoverImageFile(path); err != nil {
t.Fatal(err)
}
// os.Truncate 生成超过上限 1 字节的稀疏文件(瞬时创建,不占实际磁盘)。
if err := os.Truncate(path, docStyleCoverMaxBytes+1); err != nil {
t.Fatal(err)
}
_, _, err := validateCoverImageFile(path)
if err == nil || !strings.Contains(err.Error(), "封面图片文件过大") || !strings.Contains(err.Error(), "20 MiB") {
t.Fatalf("err = %v, want size limit error", err)
}
// 恰好等于上限时仍应通过。
if err := os.Truncate(path, docStyleCoverMaxBytes); err != nil {
t.Fatal(err)
}
if _, _, err := validateCoverImageFile(path); err != nil {
t.Fatalf("size == limit should pass, got %v", err)
}
}
func TestCrossPlatformCoverageDocStyleCoverSetSizeLimitViaCommand(t *testing.T) {
path := writeTempImage(t, "cover.png")
if err := os.Truncate(path, docStyleCoverMaxBytes+1); err != nil {
t.Fatal(err)
}
caller := &scriptedToolCaller{}
err := executeDocStyleCommand(t, caller, "cover", "set", "--node", "n1", "--file", path)
if err == nil || !strings.Contains(err.Error(), "封面图片文件过大") {
t.Fatalf("err = %v, want size limit error", err)
}
if caller.calls != 0 {
t.Fatalf("calls = %d, want 0 (fail before any network request)", caller.calls)
}
}
func TestCrossPlatformCoverageDocStyleCoverSetNilContextFallback(t *testing.T) {
// 直接调用 RunE(不经 Execute),cmd.Context() 为 nil,覆盖 context.Background() 兜底分支。
path := writeTempImage(t, "cover.png")
stubHTTPPutFile(t, nil)
caller := &scriptedToolCaller{steps: []scriptedToolStep{
{text: `{"uploadUrl":"https://oss.test/put","resourceId":"res-1"}`},
{text: `{}`},
}}
installScriptedCaller(t, caller)
root := newDocStyleCommand()
coverSet, _, err := root.Find([]string{"cover", "set"})
if err != nil {
t.Fatal(err)
}
if coverSet.Context() != nil {
t.Fatal("expected nil context on unexecuted command")
}
if err := coverSet.Flags().Set("node", "n1"); err != nil {
t.Fatal(err)
}
if err := coverSet.Flags().Set("file", path); err != nil {
t.Fatal(err)
}
if err := runDocStyleCoverSet(coverSet, nil); err != nil {
t.Fatal(err)
}
if caller.calls != 2 {
t.Fatalf("calls = %d, want 2 (upload info + style set)", caller.calls)
}
}
func TestCrossPlatformCoverageDocStyleCoverSetUsesCommandContext(t *testing.T) {
// 经 ExecuteContext 执行时,上传流程应使用命令上下文;scriptedToolCaller 忽略 ctx,
// 故此处做行为性断言:正常上下文下上传路径成功走通(覆盖 ctx := cmd.Context() 分支)。
path := writeTempImage(t, "cover.png")
stubHTTPPutFile(t, nil)
caller := &scriptedToolCaller{steps: []scriptedToolStep{
{text: `{"uploadUrl":"https://oss.test/put","resourceId":"res-1"}`},
{text: `{}`},
}}
installScriptedCaller(t, caller)
root := newDocStyleCommand()
root.SilenceErrors = true
root.SilenceUsage = true
root.SetArgs([]string{"cover", "set", "--node", "n1", "--file", path})
if err := root.ExecuteContext(context.Background()); err != nil {
t.Fatal(err)
}
if caller.calls != 2 {
t.Fatalf("calls = %d, want 2 (upload info + style set)", caller.calls)
}
}
+123 -11
View File
@@ -305,14 +305,23 @@ func newDriveCommand() *cobra.Command {
pattern, _ := cmd.Flags().GetString("pattern") pattern, _ := cmd.Flags().GetString("pattern")
depth, _ := cmd.Flags().GetInt("depth") depth, _ := cmd.Flags().GetInt("depth")
if cmd.Flags().Changed("depth") {
if err := validateDriveListDepth(cmd, depth); err != nil {
return err
}
}
// --versions 模式:列出文件历史版本(仅普通文件) // --versions 模式:列出文件历史版本(仅普通文件)
// 先于 --depth 校验执行:versions 模式合法使用 --limit,
// 不应被「--limit 与 --depth 不兼容」的误导性报错拦截。
if cmd.Flags().Changed("versions") { if cmd.Flags().Changed("versions") {
if cmd.Flags().Changed("depth") && depth > 1 {
return &CLIError{
Code: CodeInvalidParam,
Message: "--versions 与 --depth 不能同时使用",
}
}
if pattern != "" {
return &CLIError{
Code: CodeInvalidParam,
Message: "--versions 与 --pattern 不能同时使用",
}
}
nodeID, err := mustFlagOrFallback(cmd, "node", "url", "id", "node-id", "doc-id", "file-id") nodeID, err := mustFlagOrFallback(cmd, "node", "url", "id", "node-id", "doc-id", "file-id")
if err != nil { if err != nil {
return err return err
@@ -327,6 +336,12 @@ func newDriveCommand() *cobra.Command {
return callMCPToolOnServer("drive", "list_file_versions", toolArgs) return callMCPToolOnServer("drive", "list_file_versions", toolArgs)
} }
if cmd.Flags().Changed("depth") {
if err := validateDriveListDepth(cmd, depth); err != nil {
return err
}
}
// 如果指定了 --workspace,路由到文档空间(doc MCP server) // 如果指定了 --workspace,路由到文档空间(doc MCP server)
workspaceID := flagOrFallback(cmd, "workspace", "workspace-id") workspaceID := flagOrFallback(cmd, "workspace", "workspace-id")
if workspaceID != "" { if workspaceID != "" {
@@ -519,6 +534,73 @@ func newDriveCommand() *cobra.Command {
}, },
} }
driveDownloadVersionCmd := &cobra.Command{
Use: "download-version",
Short: "下载文件历史版本到本地",
Long: `下载钉盘文件的指定历史版本到本地(两步下载流程)。
仅适用于普通文件(如 pdf、docx、xlsx、png 等):
钉钉在线文档(adoc)请使用 dws doc version 系列命令
钉钉在线表格(axls)请使用 dws sheet version 系列命令
流程:
1. 获取历史版本下载 URL 和签名请求头 (download_file_version)
2. HTTP GET 下载文件二进制内容到本地
版本号从 dws drive list --node <dentryUuid> --versions 获取。`,
Example: ` dws drive download-version --node <dentryUuid> --version 3 --output ./report_v3.pdf
dws drive download-version --node <dentryUuid> --version 3 --output ~/downloads/`,
RunE: func(cmd *cobra.Command, args []string) error {
fileID, err := mustFlagOrFallback(cmd, "node", "url", "id", "node-id", "doc-id", "file-id")
if err != nil {
return err
}
versionNum, _ := cmd.Flags().GetInt("version")
if versionNum <= 0 {
return fmt.Errorf("--version 必须为正整数,当前值: %d(版本号从 drive list --versions 获取)", versionNum)
}
outputPath, _ := cmd.Flags().GetString("output")
if outputPath == "" {
return fmt.Errorf("flag --output is required")
}
if deps.Caller.DryRun() {
deps.Out.PrintKeyValue("操作", "下载文件历史版本")
deps.Out.PrintKeyValue("节点ID", fileID)
deps.Out.PrintKeyValue("版本号", fmt.Sprintf("%d", versionNum))
deps.Out.PrintKeyValue("输出", outputPath)
return nil
}
ctx := context.Background()
deps.Out.PrintInfo("[1/2] 获取历史版本下载链接...")
text, err := callMCPToolReturnTextOnServer(ctx, "drive", "download_file_version", map[string]any{
"nodeId": fileID,
"version": versionNum,
})
if err != nil {
return err
}
resourceURL, dlHeaders, err := parseDownloadInfo(text)
if err != nil {
return err
}
if fi, statErr := os.Stat(outputPath); statErr == nil && fi.IsDir() {
filename := extractFileNameFromResponse(text)
if filename == "" {
filename = inferFilename(resourceURL)
}
outputPath = filepath.Join(outputPath, filename)
}
deps.Out.PrintInfo(fmt.Sprintf("[2/2] 下载文件到 %s ...", outputPath))
if err := httpGetFile(ctx, resourceURL, dlHeaders, outputPath); err != nil {
return err
}
deps.Out.PrintInfo(fmt.Sprintf("下载完成: %s", outputPath))
return nil
},
}
driveMkdirCmd := &cobra.Command{ driveMkdirCmd := &cobra.Command{
Use: "mkdir", Use: "mkdir",
Short: "创建文件夹", Short: "创建文件夹",
@@ -628,6 +710,14 @@ func newDriveCommand() *cobra.Command {
driveDownloadCmd.Flags().String("space-id", "", "文件所属空间 ID (可选)") driveDownloadCmd.Flags().String("space-id", "", "文件所属空间 ID (可选)")
driveDownloadCmd.Flags().String("output", "", "本地保存路径 (文件路径或目录,必填)") driveDownloadCmd.Flags().String("output", "", "本地保存路径 (文件路径或目录,必填)")
driveDownloadVersionCmd.Flags().String("node", "", "文件 ID (dentryUuid) 或 URL (必填)")
driveDownloadVersionCmd.Flags().Int("version", 0, "历史版本号 (必填,正整数,从 drive list --versions 获取)")
driveDownloadVersionCmd.Flags().String("output", "", "本地保存路径 (文件路径或目录,必填)")
for _, alias := range []string{"url", "id", "node-id", "doc-id", "file-id"} {
driveDownloadVersionCmd.Flags().String(alias, "", "")
_ = driveDownloadVersionCmd.Flags().MarkHidden(alias)
}
driveMkdirCmd.Flags().String("name", "", "文件夹名称,最长 50 字符 (必填)") driveMkdirCmd.Flags().String("name", "", "文件夹名称,最长 50 字符 (必填)")
driveMkdirCmd.Flags().String("space-id", "", "目标空间 ID,不传则使用「我的文件」 (可选)") driveMkdirCmd.Flags().String("space-id", "", "目标空间 ID,不传则使用「我的文件」 (可选)")
driveMkdirCmd.Flags().String("folder", "", "父节点 ID (dentryUuid),不传则在空间根目录下创建 (可选)") driveMkdirCmd.Flags().String("folder", "", "父节点 ID (dentryUuid),不传则在空间根目录下创建 (可选)")
@@ -1211,17 +1301,15 @@ func newDriveCommand() *cobra.Command {
if nodeID == "" && workspaceID == "" { if nodeID == "" && workspaceID == "" {
return fmt.Errorf("--node or --workspace is required") return fmt.Errorf("--node or --workspace is required")
} }
// 不可逆高危操作:帮助文档承诺二选一,两者同传直接失败,绝不静默择一。
if nodeID != "" && workspaceID != "" {
return fmt.Errorf("--node and --workspace are mutually exclusive; specify exactly one")
}
if err := validateRequiredFlags(cmd, "new-owner"); err != nil { if err := validateRequiredFlags(cmd, "new-owner"); err != nil {
return err return err
} }
newOwnerID := mustGetFlag(cmd, "new-owner") newOwnerID := mustGetFlag(cmd, "new-owner")
if commandDryRun(cmd) {
deps.Out.PrintKeyValue("操作", "转交所有者")
deps.Out.PrintKeyValue("新所有者", newOwnerID)
return nil
}
yesMode, _ := cmd.Flags().GetBool("yes") yesMode, _ := cmd.Flags().GetBool("yes")
if yesMode { if yesMode {
if !cmd.Flags().Changed("reserve-role") { if !cmd.Flags().Changed("reserve-role") {
@@ -1232,6 +1320,26 @@ func newDriveCommand() *cobra.Command {
} }
} }
if commandDryRun(cmd) {
if deps.Caller.Format() == "json" {
payload := map[string]any{
"dry_run": true,
"executed": false,
"operation": "转交所有者",
"newOwnerId": newOwnerID,
}
if nodeID != "" {
payload["nodeId"] = nodeID
} else {
payload["workspaceId"] = workspaceID
}
return deps.Out.PrintJSON(payload)
}
deps.Out.PrintKeyValue("操作", "转交所有者")
deps.Out.PrintKeyValue("新所有者", newOwnerID)
return nil
}
reserveRole := mustGetFlag(cmd, "reserve-role") reserveRole := mustGetFlag(cmd, "reserve-role")
recursive, _ := cmd.Flags().GetBool("recursive") recursive, _ := cmd.Flags().GetBool("recursive")
@@ -1309,6 +1417,9 @@ func newDriveCommand() *cobra.Command {
if v := mustGetFlag(cmd, "reason"); v != "" { if v := mustGetFlag(cmd, "reason"); v != "" {
toolArgs["reason"] = v toolArgs["reason"] = v
} }
if !commandDryRun(cmd) && !confirmDangerousAction(cmd, "发起权限申请", fmt.Sprintf("节点 %s(将真实通知审批人 %s)", nodeID, strings.Join(userIds, ","))) {
return nil
}
return callMCPToolOnServer("drive", "apply_permission", toolArgs) return callMCPToolOnServer("drive", "apply_permission", toolArgs)
}, },
} }
@@ -1710,6 +1821,7 @@ func newDriveCommand() *cobra.Command {
driveListSpacesCmd, driveListSpacesCmd,
driveInfoCmd, driveInfoCmd,
driveDownloadCmd, driveDownloadCmd,
driveDownloadVersionCmd,
driveMkdirCmd, driveMkdirCmd,
driveUploadInfoCmd, driveUploadInfoCmd,
driveCommitCmd, driveCommitCmd,
+7 -2
View File
@@ -43,6 +43,9 @@ type driveDepthFolder struct {
depth int depth int
relPath string relPath string
retried bool // 已因 rate_limited 重新入队过一次,不二次补偿 retried bool // 已因 rate_limited 重新入队过一次,不二次补偿
// 限流重入队时保留失败页游标:已成功页的条目已进 collected,
// 从头重扫会产生重复条目并提前耗尽全局 2000 上限。
pageToken string
} }
type driveDepthError struct { type driveDepthError struct {
@@ -213,7 +216,7 @@ bfs:
start := time.Now() start := time.Now()
var folderErr error var folderErr error
pageToken := "" pageToken := folder.pageToken
pages := 0 pages := 0
for { for {
if ctx.Err() != nil { if ctx.Err() != nil {
@@ -294,8 +297,10 @@ bfs:
return folderErr return folderErr
} }
if driveDepthErrorCode(folderErr) == driveDepthRateLimitedCode && !folder.retried { if driveDepthErrorCode(folderErr) == driveDepthRateLimitedCode && !folder.retried {
// 限流命中在入口处、业务逻辑未执行,重试零副作用;不加 sleep/退避。 // 限流命中在入口处、失败页本身无副作用;但此前成功页已进 collected,
// 必须从失败页游标续扫而非整目录重扫。不加 sleep/退避。
folder.retried = true folder.retried = true
folder.pageToken = pageToken
queue = append(queue, folder) queue = append(queue, folder)
continue continue
} }
+29
View File
@@ -534,6 +534,35 @@ func TestCrossPlatformCoverageRunDriveListDepthRateLimitRetry(t *testing.T) {
} }
} }
func TestCrossPlatformCoverageRunDriveListDepthRateLimitResumesFromFailedPage(t *testing.T) {
useDriveDepthArgs(t)
caller := &depthArgsRecordingCaller{steps: []scriptedToolStep{
{text: `{"items":[{"fileId":"f1","name":"page1.txt","type":"FILE"}],"nextToken":"p2"}`},
{text: `{"errorCode":"invalidRequest.rateLimited","errorMsg":"slow down"}`},
{text: `{"items":[{"fileId":"f2","name":"page2.txt","type":"FILE"}]}`},
}}
previousDeps := deps
t.Cleanup(func() { deps = previousDeps })
InitDeps(caller)
out := &bytes.Buffer{}
deps.Out.w = out
deps.Out.errW = io.Discard
cmd := &cobra.Command{Use: "list"}
if err := runDriveListDepth(cmd, newDrivePanDepthRoute(), map[string]any{}, "", 3, "", true); err != nil {
t.Fatal(err)
}
if len(caller.calls) != 3 {
t.Fatalf("calls = %d, want 3", len(caller.calls))
}
if tok, _ := caller.calls[2]["nextToken"].(string); tok != "p2" {
t.Fatalf("retry call args = %#v, want resume with nextToken=p2", caller.calls[2])
}
items := decodeDepthResult(t, out)["items"].([]any)
if len(items) != 2 {
t.Fatalf("items = %#v, want 2 without duplicates", items)
}
}
func TestCrossPlatformCoverageRunDriveListDepthRateLimitExhausted(t *testing.T) { func TestCrossPlatformCoverageRunDriveListDepthRateLimitExhausted(t *testing.T) {
useDriveDepthArgs(t) useDriveDepthArgs(t)
caller := &scriptedToolCaller{steps: []scriptedToolStep{ caller := &scriptedToolCaller{steps: []scriptedToolStep{
+12
View File
@@ -213,3 +213,15 @@ func TestCrossPlatformCoverageDriveRevertDeclined(t *testing.T) {
t.Fatalf("declined revert calls = %#v", caller.calls) t.Fatalf("declined revert calls = %#v", caller.calls)
} }
} }
func TestCrossPlatformCoverageDriveTransferOwnerRejectsBothTargets(t *testing.T) {
caller := &guardedMutationCaller{}
err := executeGuardedMutationCommand(t, caller, newDriveCommand,
"permission", "transfer-owner", "--node", "node-1", "--workspace", "ws-1", "--new-owner", "user-1")
if err == nil || !strings.Contains(err.Error(), "mutually exclusive") {
t.Fatalf("err = %v, want mutual exclusion", err)
}
if len(caller.calls) != 0 {
t.Fatalf("calls = %#v, want none", caller.calls)
}
}
+263
View File
@@ -0,0 +1,263 @@
// 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.
package helpers
import (
"bytes"
"context"
"encoding/json"
"io"
"reflect"
"strings"
"testing"
"github.com/DingTalk-Real-AI/dingtalk-workspace-cli/pkg/edition"
"github.com/spf13/cobra"
)
// executeDriveCommandCapture 执行 drive 命令并捕获 deps.Out 的 stdout 输出,
// 供需要断言 JSON 输出形态的测试使用(等价于 installDepthCaller + Execute)。
func executeDriveCommandCapture(t *testing.T, caller edition.ToolCaller, args ...string) (*bytes.Buffer, error) {
t.Helper()
previousDeps := deps
t.Cleanup(func() { deps = previousDeps })
InitDeps(caller)
buf := &bytes.Buffer{}
deps.Out.w = buf
deps.Out.errW = io.Discard
root := newDriveCommand()
if root.PersistentFlags().Lookup("yes") == nil {
root.PersistentFlags().Bool("yes", false, "confirm high-risk operation")
}
if root.PersistentFlags().Lookup("dry-run") == nil {
root.PersistentFlags().Bool("dry-run", false, "preview without executing")
}
root.SilenceErrors = true
root.SilenceUsage = true
root.SetArgs(args)
err := root.Execute()
return buf, err
}
// ── drive permission apply:确认门禁与帮助文案契约一致 ──
func TestCrossPlatformCoverageDrivePermissionApplyDeclined(t *testing.T) {
caller := &guardedMutationCaller{}
root := newDriveCommand()
root.SetIn(strings.NewReader("no\n"))
err := executeGuardedMutationCommand(t, caller, func() *cobra.Command { return root },
"permission", "apply", "--node", "node-1", "--role", "READER", "--users", "u1")
if err != nil {
t.Fatal(err)
}
if len(caller.calls) != 0 {
t.Fatalf("declined apply calls = %#v, want none", caller.calls)
}
}
func TestCrossPlatformCoverageDrivePermissionApplyYesProceeds(t *testing.T) {
caller := &guardedMutationCaller{}
err := executeGuardedMutationCommand(t, caller, newDriveCommand,
"permission", "apply", "--node", "node-1", "--role", "reader", "--users", "u1,u2", "--yes")
if err != nil {
t.Fatal(err)
}
if len(caller.calls) != 1 {
t.Fatalf("calls = %#v, want exactly one", caller.calls)
}
call := caller.calls[0]
if call.productID != "drive" || call.toolName != "apply_permission" {
t.Fatalf("call = %#v", call)
}
if call.args["nodeId"] != "node-1" || call.args["roleId"] != "READER" {
t.Fatalf("args = %#v", call.args)
}
if !reflect.DeepEqual(call.args["receivers"], []string{"u1", "u2"}) {
t.Fatalf("receivers = %#v", call.args["receivers"])
}
}
func TestCrossPlatformCoverageDrivePermissionApplyDryRunNoPromptNoCall(t *testing.T) {
caller := &guardedMutationCaller{dryRun: true}
root := newDriveCommand()
promptOut := &bytes.Buffer{}
root.SetErr(promptOut)
root.SetIn(strings.NewReader("no\n"))
err := executeGuardedMutationCommand(t, caller, func() *cobra.Command { return root },
"permission", "apply", "--node", "node-1", "--role", "READER", "--users", "u1", "--dry-run")
if err != nil {
t.Fatal(err)
}
if len(caller.calls) != 0 {
t.Fatalf("dry-run apply calls = %#v, want none", caller.calls)
}
if strings.Contains(promptOut.String(), "Confirm action") {
t.Fatalf("dry-run prompted for confirmation: %q", promptOut.String())
}
}
// ── drive permission transfer-owner:dry-run JSON 输出且校验先于 dry-run ──
func TestCrossPlatformCoverageDriveTransferOwnerDryRunJSONNode(t *testing.T) {
caller := &scriptedToolCaller{format: "json", dry: true}
out, err := executeDriveCommandCapture(t, caller,
"permission", "transfer-owner", "--node", "node-1", "--new-owner", "user-1", "--dry-run")
if err != nil {
t.Fatal(err)
}
if caller.calls != 0 {
t.Fatalf("dry-run tool calls = %d, want none", caller.calls)
}
var payload map[string]any
if err := json.Unmarshal(out.Bytes(), &payload); err != nil {
t.Fatalf("dry-run output is not JSON: %v\n%s", err, out.String())
}
if payload["dry_run"] != true || payload["executed"] != false {
t.Fatalf("payload = %#v", payload)
}
if payload["operation"] != "转交所有者" || payload["newOwnerId"] != "user-1" || payload["nodeId"] != "node-1" {
t.Fatalf("payload = %#v", payload)
}
if _, ok := payload["workspaceId"]; ok {
t.Fatalf("unexpected workspaceId in %#v", payload)
}
}
func TestCrossPlatformCoverageDriveTransferOwnerDryRunJSONWorkspace(t *testing.T) {
caller := &scriptedToolCaller{format: "json", dry: true}
out, err := executeDriveCommandCapture(t, caller,
"permission", "transfer-owner", "--workspace", "ws-1", "--new-owner", "user-1", "--dry-run")
if err != nil {
t.Fatal(err)
}
var payload map[string]any
if err := json.Unmarshal(out.Bytes(), &payload); err != nil {
t.Fatalf("dry-run output is not JSON: %v\n%s", err, out.String())
}
if payload["workspaceId"] != "ws-1" || payload["newOwnerId"] != "user-1" {
t.Fatalf("payload = %#v", payload)
}
if _, ok := payload["nodeId"]; ok {
t.Fatalf("unexpected nodeId in %#v", payload)
}
}
func TestCrossPlatformCoverageDriveTransferOwnerDryRunYesValidatesFirst(t *testing.T) {
caller := &guardedMutationCaller{dryRun: true}
err := executeGuardedMutationCommand(t, caller, newDriveCommand,
"permission", "transfer-owner", "--node", "node-1", "--new-owner", "user-1", "--yes", "--dry-run")
if err == nil || !strings.Contains(err.Error(), "--reserve-role is required") {
t.Fatalf("err = %v, want --reserve-role required even under --dry-run", err)
}
if len(caller.calls) != 0 {
t.Fatalf("calls = %#v, want none", caller.calls)
}
}
func TestCrossPlatformCoverageDriveTransferOwnerDryRunNonJSON(t *testing.T) {
caller := &scriptedToolCaller{format: "table", dry: true}
_, err := executeDriveCommandCapture(t, caller,
"permission", "transfer-owner", "--node", "node-1", "--new-owner", "user-1", "--dry-run")
if err != nil {
t.Fatal(err)
}
if caller.calls != 0 {
t.Fatalf("dry-run tool calls = %d, want none", caller.calls)
}
}
// ── drive list:--versions 与 --depth/--pattern 的交互 ──
func TestCrossPlatformCoverageDriveListVersionsRejectsDepth(t *testing.T) {
caller := &guardedMutationCaller{}
err := executeGuardedMutationCommand(t, caller, newDriveCommand,
"list", "--versions", "--node", "node-1", "--depth", "2", "--limit", "10")
if err == nil || !strings.Contains(err.Error(), "--depth") {
t.Fatalf("err = %v, want explicit --versions/--depth conflict", err)
}
if len(caller.calls) != 0 {
t.Fatalf("calls = %#v, want none", caller.calls)
}
}
func TestCrossPlatformCoverageDriveListVersionsRejectsPattern(t *testing.T) {
caller := &guardedMutationCaller{}
err := executeGuardedMutationCommand(t, caller, newDriveCommand,
"list", "--versions", "--node", "node-1", "--pattern", "x")
if err == nil || !strings.Contains(err.Error(), "--pattern") {
t.Fatalf("err = %v, want explicit --versions/--pattern conflict", err)
}
if len(caller.calls) != 0 {
t.Fatalf("calls = %#v, want none", caller.calls)
}
}
func TestCrossPlatformCoverageDriveListVersionsAllowsDepthOne(t *testing.T) {
caller := &guardedMutationCaller{}
err := executeGuardedMutationCommand(t, caller, newDriveCommand,
"list", "--versions", "--node", "node-1", "--depth", "1")
if err != nil {
t.Fatal(err)
}
if len(caller.calls) != 1 || caller.calls[0].toolName != "list_file_versions" {
t.Fatalf("calls = %#v", caller.calls)
}
}
func TestCrossPlatformCoverageDriveListVersionsWithLimit(t *testing.T) {
caller := &guardedMutationCaller{}
err := executeGuardedMutationCommand(t, caller, newDriveCommand,
"list", "--versions", "--node", "node-1", "--limit", "10")
if err != nil {
t.Fatal(err)
}
if len(caller.calls) != 1 {
t.Fatalf("calls = %#v, want exactly one", caller.calls)
}
call := caller.calls[0]
if call.productID != "drive" || call.toolName != "list_file_versions" {
t.Fatalf("call = %#v", call)
}
if call.args["nodeId"] != "node-1" || call.args["maxResults"] != 10 {
t.Fatalf("args = %#v", call.args)
}
}
func TestCrossPlatformCoverageDriveDownloadVersionDryRun(t *testing.T) {
caller := &guardedMutationCaller{dryRun: true}
err := executeGuardedMutationCommand(t, caller, newDriveCommand,
"download-version", "--node", "node-1", "--version", "3", "--output", "./x.pdf", "--dry-run")
if err != nil {
t.Fatal(err)
}
if len(caller.calls) != 0 {
t.Fatalf("dry-run calls = %#v, want none", caller.calls)
}
}
func TestCrossPlatformCoverageDriveDownloadVersionDirectoryOutput(t *testing.T) {
SetHTTPGetFile(func(context.Context, string, map[string]string, string) error { return nil })
t.Cleanup(func() { SetHTTPGetFile(nil) })
dir := t.TempDir()
for _, resp := range []string{
`{"downloadUrl":"https://oss.test/get/report_v3.pdf","fileName":"报告v3.pdf"}`,
`{"downloadUrl":"https://oss.test/get/inferred_v3.pdf"}`,
} {
caller := &scriptedToolCaller{steps: []scriptedToolStep{{text: resp}}}
installScriptedCaller(t, caller)
root := newDriveCommand()
root.PersistentFlags().Bool("dry-run", false, "")
root.SilenceErrors = true
root.SilenceUsage = true
root.SetArgs([]string{"download-version", "--node", "node-1", "--version", "3", "--output", dir})
if err := root.Execute(); err != nil {
t.Fatal(err)
}
if caller.calls != 1 {
t.Fatalf("calls = %d, want 1", caller.calls)
}
}
}
@@ -0,0 +1,168 @@
package helpers
import (
"reflect"
"strings"
"testing"
)
func TestCrossPlatformCoverageDriveStarCommands(t *testing.T) {
tests := []struct {
name string
args []string
want guardedMutationCall
}{
{
name: "add",
args: []string{"star", "add", "--node", "node-1"},
want: guardedMutationCall{
productID: "drive",
toolName: "mark_star",
args: map[string]any{"nodeId": "node-1"},
},
},
{
name: "add with url value passed through",
args: []string{"star", "add", "--node", "https://alidocs.dingtalk.com/i/nodes/node-2"},
want: guardedMutationCall{
productID: "drive",
toolName: "mark_star",
args: map[string]any{"nodeId": "https://alidocs.dingtalk.com/i/nodes/node-2"},
},
},
{
name: "remove",
args: []string{"star", "remove", "--node", "node-3"},
want: guardedMutationCall{
productID: "drive",
toolName: "unmark_star",
args: map[string]any{"nodeId": "node-3"},
},
},
{
name: "remove rm alias",
args: []string{"star", "rm", "--node", "node-4"},
want: guardedMutationCall{
productID: "drive",
toolName: "unmark_star",
args: map[string]any{"nodeId": "node-4"},
},
},
{
name: "list without filters",
args: []string{"star", "list"},
want: guardedMutationCall{
productID: "drive",
toolName: "get_star_list",
args: map[string]any{},
},
},
{
name: "list with all filters",
args: []string{
"star", "list",
"--limit", "10",
"--cursor", "cur-1",
"--order-by", "createTime",
"--sort", "desc",
"--resource-types", "DENTRY,WORKSPACE",
"--content-types", "doc,sheet",
},
want: guardedMutationCall{
productID: "drive",
toolName: "get_star_list",
args: map[string]any{
"limit": 10,
"cursor": "cur-1",
"orderBy": "createTime",
"sortType": "desc",
"supportResourceTypes": []string{"DENTRY", "WORKSPACE"},
"contentTypes": []string{"doc", "sheet"},
},
},
},
}
for _, test := range tests {
test := test
t.Run(test.name, func(t *testing.T) {
caller := &guardedMutationCaller{}
err := executeGuardedMutationCommand(t, caller, newDriveCommand, test.args...)
if err != nil {
t.Fatalf("drive %s returned error: %v", strings.Join(test.args, " "), err)
}
if len(caller.calls) != 1 || !reflect.DeepEqual(caller.calls[0], test.want) {
t.Fatalf("tool calls = %#v, want %#v", caller.calls, test.want)
}
})
}
}
func TestCrossPlatformCoverageDriveStarRequiredFlags(t *testing.T) {
tests := []struct {
name string
args []string
wantErr string
}{
{
name: "add without node",
args: []string{"star", "add"},
wantErr: "--node",
},
{
name: "remove without node",
args: []string{"star", "remove"},
wantErr: "--node",
},
}
for _, test := range tests {
test := test
t.Run(test.name, func(t *testing.T) {
caller := &guardedMutationCaller{}
err := executeGuardedMutationCommand(t, caller, newDriveCommand, test.args...)
if err == nil || !strings.Contains(err.Error(), test.wantErr) {
t.Fatalf("err = %v, want message containing %q", err, test.wantErr)
}
if len(caller.calls) != 0 {
t.Fatalf("tool calls = %#v, want none", caller.calls)
}
})
}
}
func TestCrossPlatformCoverageDriveStarListRecordsRawArgs(t *testing.T) {
caller := &depthArgsRecordingCaller{steps: []scriptedToolStep{
{text: `{"items":[{"nodeId":"n1","name":"doc1"}],"hasMore":false}`},
}}
err := executeDriveCommand(t, caller,
"star", "list", "--limit", "5", "--cursor", "cur-9", "--sort", "asc")
if err != nil {
t.Fatal(err)
}
if len(caller.calls) != 1 {
t.Fatalf("calls = %#v, want 1", caller.calls)
}
args := caller.calls[0]
if args["limit"] != 5 || args["cursor"] != "cur-9" || args["sortType"] != "asc" {
t.Fatalf("star list raw args = %#v", args)
}
if _, ok := args["orderBy"]; ok {
t.Fatalf("unexpected orderBy in %#v", args)
}
}
func TestCrossPlatformCoverageDriveStarAddScriptedSuccess(t *testing.T) {
caller := &scriptedToolCaller{format: "json", steps: []scriptedToolStep{
{text: `{"success":true}`},
}}
installScriptedCaller(t, caller)
root := newDriveCommand()
root.SilenceErrors = true
root.SilenceUsage = true
root.SetArgs([]string{"star", "add", "--node", "node-1"})
if err := root.Execute(); err != nil {
t.Fatalf("star add returned error: %v", err)
}
if caller.calls != 1 {
t.Fatalf("tool calls = %d, want 1", caller.calls)
}
}
+57 -4
View File
@@ -1,6 +1,7 @@
package helpers package helpers
import ( import (
"context"
"encoding/json" "encoding/json"
"fmt" "fmt"
"io" "io"
@@ -48,7 +49,8 @@ func newSheetFormulaVerifyCmd() *cobra.Command {
} }
toolArgs["maxCells"] = v toolArgs["maxCells"] = v
} }
return callMCPToolOnServer("sheet", "formula_verify", toolArgs) exitOnError, _ := cmd.Flags().GetBool("exit-on-error")
return callMCPToolFormulaVerify(toolArgs, exitOnError)
}, },
} }
cmd.Flags().String("node", "", "表格文档 ID 或 URL (必填)") cmd.Flags().String("node", "", "表格文档 ID 或 URL (必填)")
@@ -57,15 +59,66 @@ func newSheetFormulaVerifyCmd() *cobra.Command {
cmd.Flags().String("targets", "", `扫描目标 JSON 数组、@文件路径 或 - 表示 stdin;每项 {"sheetId":"Sheet1","range":"A1:D100"}`) cmd.Flags().String("targets", "", `扫描目标 JSON 数组、@文件路径 或 - 表示 stdin;每项 {"sheetId":"Sheet1","range":"A1:D100"}`)
cmd.Flags().Int("max-locations-per-error", 0, "每种错误类型最多返回的位置数") cmd.Flags().Int("max-locations-per-error", 0, "每种错误类型最多返回的位置数")
cmd.Flags().Int("max-cells", 0, "最多扫描的单元格数") cmd.Flags().Int("max-cells", 0, "最多扫描的单元格数")
cmd.Flags().Bool("exit-on-error", false, "发现公式错误时返回非 0 退出码,便于 CI/自动化使用")
return cmd return cmd
} }
func formulaVerifyTargetsFromFlags(cmd *cobra.Command) ([]map[string]any, error) { // callMCPToolFormulaVerify 与常规打印路径的差异:--exit-on-error 需要读取
if v, _ := cmd.Flags().GetString("targets"); v != "" { // 返回 payload 判定是否存在公式错误,故走 ReturnText 再自行 PrintJSON。
return parseFormulaVerifyTargets(cmd, v) func callMCPToolFormulaVerify(toolArgs map[string]any, exitOnError bool) error {
if deps.Caller.DryRun() {
return callMCPToolOnServer("sheet", "formula_verify", toolArgs)
} }
text, err := callMCPToolReturnTextOnServer(context.Background(), "sheet", "formula_verify", toolArgs)
if err != nil {
return err
}
if text == "" {
return nil
}
var parsed map[string]any
if err := json.Unmarshal([]byte(text), &parsed); err != nil {
deps.Out.PrintRaw(text)
return nil
}
if parsed == nil {
return fmt.Errorf("formula_verify returned empty result")
}
if err := deps.Out.PrintJSON(parsed); err != nil {
return err
}
if exitOnError && formulaVerifyHasErrors(parsed) {
return fmt.Errorf("formula errors found")
}
return nil
}
func formulaVerifyHasErrors(parsed map[string]any) bool {
result := formulaVerifyResultObject(parsed)
status := strings.ToLower(strings.TrimSpace(fmt.Sprint(result["status"])))
if status == "errors_found" {
return true
}
totalErrors, ok := nonNegativeJSONInt(result["totalErrors"])
return ok && totalErrors > 0
}
func formulaVerifyResultObject(parsed map[string]any) map[string]any {
if result, ok := parsed["result"].(map[string]any); ok {
return result
}
return parsed
}
func formulaVerifyTargetsFromFlags(cmd *cobra.Command) ([]map[string]any, error) {
sheetID, _ := cmd.Flags().GetString("sheet-id") sheetID, _ := cmd.Flags().GetString("sheet-id")
rangeStr, _ := cmd.Flags().GetString("range") rangeStr, _ := cmd.Flags().GetString("range")
if v, _ := cmd.Flags().GetString("targets"); v != "" {
if strings.TrimSpace(sheetID) != "" || strings.TrimSpace(rangeStr) != "" {
return nil, fmt.Errorf("--targets 不能与 --sheet-id 或 --range 同时使用")
}
return parseFormulaVerifyTargets(cmd, v)
}
if sheetID == "" && rangeStr != "" { if sheetID == "" && rangeStr != "" {
return nil, fmt.Errorf("--range 必须与 --sheet-id 配合使用") return nil, fmt.Errorf("--range 必须与 --sheet-id 配合使用")
} }
@@ -89,3 +89,87 @@ func TestCrossPlatformCoverageSheetFormulaVerifyTargetsStdinFailure(t *testing.T
t.Fatalf("err = %v, want stdin failure", err) t.Fatalf("err = %v, want stdin failure", err)
} }
} }
func TestCrossPlatformCoverageSheetFormulaVerifyTargetsConflict(t *testing.T) {
for _, extra := range [][]string{
{"--sheet-id", "Sheet1"},
{"--range", "A1:B2"},
} {
args := append([]string{"--node", "n1", "--targets", `[{"sheetId":"S1"}]`}, extra...)
err := executeFormulaVerify(t, &scriptedToolCaller{}, nil, args...)
if err == nil || !strings.Contains(err.Error(), "--targets 不能与 --sheet-id 或 --range 同时使用") {
t.Fatalf("args %v err = %v, want conflict error", extra, err)
}
}
}
func TestCrossPlatformCoverageSheetFormulaVerifyExitOnError(t *testing.T) {
caller := &scriptedToolCaller{steps: []scriptedToolStep{
{text: `{"status":"ERRORS_FOUND","totalErrors":2}`},
}}
err := executeFormulaVerify(t, caller, nil, "--node", "n1", "--exit-on-error")
if err == nil || !strings.Contains(err.Error(), "formula errors found") {
t.Fatalf("err = %v, want formula errors found", err)
}
caller = &scriptedToolCaller{steps: []scriptedToolStep{
{text: `{"result":{"status":"partial","totalErrors":1}}`},
}}
err = executeFormulaVerify(t, caller, nil, "--node", "n1", "--exit-on-error")
if err == nil || !strings.Contains(err.Error(), "formula errors found") {
t.Fatalf("nested totalErrors err = %v, want formula errors found", err)
}
caller = &scriptedToolCaller{steps: []scriptedToolStep{
{text: `{"status":"clean","totalErrors":0}`},
}}
if err := executeFormulaVerify(t, caller, nil, "--node", "n1", "--exit-on-error"); err != nil {
t.Fatalf("clean result err = %v", err)
}
caller = &scriptedToolCaller{steps: []scriptedToolStep{
{text: `{"status":"errors_found"}`},
}}
if err := executeFormulaVerify(t, caller, nil, "--node", "n1"); err != nil {
t.Fatalf("errors without --exit-on-error err = %v", err)
}
}
func TestCrossPlatformCoverageSheetFormulaVerifyPayloadEdges(t *testing.T) {
caller := &scriptedToolCaller{steps: []scriptedToolStep{{text: `plain text result`}}}
if err := executeFormulaVerify(t, caller, nil, "--node", "n1", "--exit-on-error"); err != nil {
t.Fatalf("non-JSON payload err = %v", err)
}
caller = &scriptedToolCaller{steps: []scriptedToolStep{{text: `null`}}}
err := executeFormulaVerify(t, caller, nil, "--node", "n1")
if err == nil || !strings.Contains(err.Error(), "empty result") {
t.Fatalf("null payload err = %v, want empty result", err)
}
caller = &scriptedToolCaller{steps: []scriptedToolStep{{text: `{"errorCode":"forbidden.x","errorMsg":"denied"}`}}}
if err := executeFormulaVerify(t, caller, nil, "--node", "n1"); err == nil {
t.Fatal("business error returned nil")
}
caller = &scriptedToolCaller{steps: []scriptedToolStep{{text: `{"status":"clean"}`}}}
installScriptedCaller(t, caller)
deps.Out.w = failingWriter{}
cmd := newSheetFormulaVerifyCmd()
cmd.SilenceErrors = true
cmd.SilenceUsage = true
cmd.SetArgs([]string{"--node", "n1"})
if err := cmd.Execute(); err == nil || !strings.Contains(err.Error(), "write failed") {
t.Fatalf("print failure err = %v, want write failed", err)
}
}
func TestCrossPlatformCoverageSheetFormulaVerifyDryRunShortCircuit(t *testing.T) {
caller := &scriptedToolCaller{dry: true}
if err := executeFormulaVerify(t, caller, nil, "--node", "n1", "--exit-on-error"); err != nil {
t.Fatal(err)
}
if caller.calls != 0 {
t.Fatalf("dry-run calls = %d, want 0 (preview only)", caller.calls)
}
}
@@ -0,0 +1,40 @@
package helpers
import (
"io"
"os"
"testing"
)
func TestCrossPlatformCoverageSheetInfoIncludePassthrough(t *testing.T) {
oldArgs := os.Args
os.Args = []string{"dws", "sheet"}
t.Cleanup(func() { os.Args = oldArgs })
caller := &depthArgsRecordingCaller{steps: []scriptedToolStep{
{text: `{"sheetId":"s1"}`},
}}
previousDeps := deps
t.Cleanup(func() { deps = previousDeps })
InitDeps(caller)
deps.Out.w = io.Discard
deps.Out.errW = io.Discard
root := newSheetCommand()
if root.PersistentFlags().Lookup("dry-run") == nil {
root.PersistentFlags().Bool("dry-run", false, "")
}
root.SilenceErrors = true
root.SilenceUsage = true
root.SetArgs([]string{"info", "--node", "n1", "--sheet-id", "Sheet1", "--include", "row_heights,col_widths"})
if err := root.Execute(); err != nil {
t.Fatal(err)
}
if len(caller.calls) != 1 {
t.Fatalf("calls = %#v", caller.calls)
}
include, _ := caller.calls[0]["include"].([]string)
if len(include) != 2 || include[0] != "row_heights" || include[1] != "col_widths" {
t.Fatalf("include = %#v", caller.calls[0]["include"])
}
}
@@ -0,0 +1,298 @@
package helpers
import (
"reflect"
"strings"
"testing"
)
func TestCrossPlatformCoverageSheetCommentCommands(t *testing.T) {
tests := []struct {
name string
args []string
want guardedMutationCall
}{
{
name: "list minimal",
args: []string{"comment", "list", "--node", "node-1"},
want: guardedMutationCall{
productID: "doc-comment",
toolName: "list_sheet_comments",
args: map[string]any{"nodeId": "node-1"},
},
},
{
name: "list with cell and status filters",
args: []string{
"comment", "list",
"--node", "node-1",
"--sheet-id", "Sheet1",
"--range", "A2",
"--resolve-status", "unresolved",
"--limit", "10",
"--cursor", "cur-1",
},
want: guardedMutationCall{
productID: "doc-comment",
toolName: "list_sheet_comments",
args: map[string]any{
"nodeId": "node-1",
"sheetId": "Sheet1",
"range": "A2",
"resolveStatus": "unresolved",
"pageSize": 10,
"nextToken": "cur-1",
},
},
},
{
name: "create with mention parsing",
args: []string{
"comment", "create",
"--node", "node-1",
"--sheet-id", "Sheet1",
"--range", "A2",
"--content", "check this",
"--mention", "uid1, uid2 ,,uid3",
},
want: guardedMutationCall{
productID: "doc-comment",
toolName: "create_sheet_comment",
args: map[string]any{
"nodeId": "node-1",
"content": "check this",
"sheetId": "Sheet1",
"range": "A2",
"mentionedUserIds": []string{"uid1", "uid2", "uid3"},
},
},
},
{
name: "create without mention",
args: []string{
"comment", "create",
"--node", "node-1",
"--sheet-id", "Sheet1",
"--range", "B3",
"--content", "plain",
},
want: guardedMutationCall{
productID: "doc-comment",
toolName: "create_sheet_comment",
args: map[string]any{
"nodeId": "node-1",
"content": "plain",
"sheetId": "Sheet1",
"range": "B3",
},
},
},
{
name: "reply with emoji and mention",
args: []string{
"comment", "reply",
"--node", "node-1",
"--comment-key", "ck-1",
"--content", "heart",
"--emoji",
"--mention", "uid1",
},
want: guardedMutationCall{
productID: "doc-comment",
toolName: "reply_comment",
args: map[string]any{
"nodeId": "node-1",
"content": "heart",
"replyCommentKey": "ck-1",
"emoji": true,
"mentionedUserIds": []string{"uid1"},
},
},
},
{
name: "reply plain text",
args: []string{
"comment", "reply",
"--node", "node-1",
"--comment-key", "ck-2",
"--content", "confirmed",
},
want: guardedMutationCall{
productID: "doc-comment",
toolName: "reply_comment",
args: map[string]any{
"nodeId": "node-1",
"content": "confirmed",
"replyCommentKey": "ck-2",
},
},
},
{
name: "update",
args: []string{
"comment", "update",
"--node", "node-1",
"--comment-key", "ck-3",
"--content", "revised",
},
want: guardedMutationCall{
productID: "doc-comment",
toolName: "update_comment",
args: map[string]any{
"nodeId": "node-1",
"commentKey": "ck-3",
"content": "revised",
},
},
},
}
for _, test := range tests {
test := test
t.Run(test.name, func(t *testing.T) {
caller := &guardedMutationCaller{}
err := executeGuardedMutationCommand(t, caller, newSheetCommand, test.args...)
if err != nil {
t.Fatalf("sheet %s returned error: %v", strings.Join(test.args, " "), err)
}
if len(caller.calls) != 1 || !reflect.DeepEqual(caller.calls[0], test.want) {
t.Fatalf("tool calls = %#v, want %#v", caller.calls, test.want)
}
})
}
}
func TestCrossPlatformCoverageSheetCommentRequiredFlags(t *testing.T) {
tests := []struct {
name string
args []string
wantErr string
}{
{
name: "list without node",
args: []string{"comment", "list"},
wantErr: "--node",
},
{
name: "create without content sheet-id range",
args: []string{"comment", "create", "--node", "node-1"},
wantErr: "--content, --sheet-id, --range",
},
{
name: "reply without comment-key",
args: []string{"comment", "reply", "--node", "node-1", "--content", "text"},
wantErr: "--comment-key",
},
{
name: "update without content",
args: []string{"comment", "update", "--node", "node-1", "--comment-key", "ck-1"},
wantErr: "--content",
},
}
for _, test := range tests {
test := test
t.Run(test.name, func(t *testing.T) {
caller := &guardedMutationCaller{}
err := executeGuardedMutationCommand(t, caller, newSheetCommand, test.args...)
if err == nil || !strings.Contains(err.Error(), test.wantErr) {
t.Fatalf("err = %v, want message containing %q", err, test.wantErr)
}
if len(caller.calls) != 0 {
t.Fatalf("tool calls = %#v, want none", caller.calls)
}
})
}
}
func TestCrossPlatformCoverageSheetVersionCommands(t *testing.T) {
tests := []struct {
name string
args []string
want guardedMutationCall
}{
{
name: "save",
args: []string{"version", "save", "--node", "node-1"},
want: guardedMutationCall{
productID: "doc",
toolName: "save_doc_version",
args: map[string]any{"nodeId": "node-1"},
},
},
{
name: "list minimal",
args: []string{"version", "list", "--node", "node-1"},
want: guardedMutationCall{
productID: "doc",
toolName: "list_doc_versions",
args: map[string]any{"nodeId": "node-1"},
},
},
{
name: "list with pagination pass-through",
args: []string{"version", "list", "--node", "node-1", "--limit", "10", "--cursor", "cur-1"},
want: guardedMutationCall{
productID: "doc",
toolName: "list_doc_versions",
args: map[string]any{
"nodeId": "node-1",
"maxResults": 10,
"nextCursor": "cur-1",
},
},
},
{
name: "list ls alias",
args: []string{"version", "ls", "--node", "node-2"},
want: guardedMutationCall{
productID: "doc",
toolName: "list_doc_versions",
args: map[string]any{"nodeId": "node-2"},
},
},
}
for _, test := range tests {
test := test
t.Run(test.name, func(t *testing.T) {
caller := &guardedMutationCaller{}
err := executeGuardedMutationCommand(t, caller, newSheetCommand, test.args...)
if err != nil {
t.Fatalf("sheet %s returned error: %v", strings.Join(test.args, " "), err)
}
if len(caller.calls) != 1 || !reflect.DeepEqual(caller.calls[0], test.want) {
t.Fatalf("tool calls = %#v, want %#v", caller.calls, test.want)
}
})
}
}
func TestCrossPlatformCoverageSheetVersionRequiredFlags(t *testing.T) {
tests := []struct {
name string
args []string
wantErr string
}{
{
name: "save without node",
args: []string{"version", "save"},
wantErr: "--node",
},
{
name: "list without node",
args: []string{"version", "list"},
wantErr: "--node",
},
}
for _, test := range tests {
test := test
t.Run(test.name, func(t *testing.T) {
caller := &guardedMutationCaller{}
err := executeGuardedMutationCommand(t, caller, newSheetCommand, test.args...)
if err == nil || !strings.Contains(err.Error(), test.wantErr) {
t.Fatalf("err = %v, want message containing %q", err, test.wantErr)
}
if len(caller.calls) != 0 {
t.Fatalf("tool calls = %#v, want none", caller.calls)
}
})
}
}
+6 -1
View File
@@ -69,7 +69,8 @@ sheetId 支持传入工作表 ID 或工作表名称,可通过 sheet list 获
不传 --sheet-id 时默认返回第一个工作表。`, 不传 --sheet-id 时默认返回第一个工作表。`,
Example: ` dws sheet info --node NODE_ID Example: ` dws sheet info --node NODE_ID
dws sheet info --node NODE_ID --sheet-id SHEET_ID dws sheet info --node NODE_ID --sheet-id SHEET_ID
dws sheet info --node NODE_ID --sheet-id "Sheet1"`, dws sheet info --node NODE_ID --sheet-id "Sheet1"
dws sheet info --node NODE_ID --sheet-id SHEET_ID --include row_heights,col_widths`,
RunE: func(cmd *cobra.Command, args []string) error { RunE: func(cmd *cobra.Command, args []string) error {
toolArgs := map[string]any{ toolArgs := map[string]any{
"nodeId": mustGetFlag(cmd, "node"), "nodeId": mustGetFlag(cmd, "node"),
@@ -77,11 +78,15 @@ sheetId 支持传入工作表 ID 或工作表名称,可通过 sheet list 获
if v, _ := cmd.Flags().GetString("sheet-id"); v != "" { if v, _ := cmd.Flags().GetString("sheet-id"); v != "" {
toolArgs["sheetId"] = v toolArgs["sheetId"] = v
} }
if v, _ := cmd.Flags().GetStringSlice("include"); len(v) > 0 {
toolArgs["include"] = v
}
return callMCPToolSheetInfo(toolArgs) return callMCPToolSheetInfo(toolArgs)
}, },
} }
infoCmd.Flags().String("node", "", "表格文档 ID 或 URL (必填)") infoCmd.Flags().String("node", "", "表格文档 ID 或 URL (必填)")
infoCmd.Flags().String("sheet-id", "", "工作表 ID 或名称 (不传则返回第一个工作表)") infoCmd.Flags().String("sheet-id", "", "工作表 ID 或名称 (不传则返回第一个工作表)")
infoCmd.Flags().StringSlice("include", nil, "可选扩展信息,逗号分隔;支持 groups / row_heights / col_widths / hidden_rows / hidden_cols / frozen")
newCmd := &cobra.Command{ newCmd := &cobra.Command{
Use: "new", Use: "new",
@@ -97,7 +97,7 @@ Flags:
| `view get` | 获取视图配置(不传子命令) | `--base-id` `--table-id` | 不传 `--view-ids` 返回全部视图 | | `view get` | 获取视图配置(不传子命令) | `--base-id` `--table-id` | 不传 `--view-ids` 返回全部视图 |
| `view get <attr>` | 获取视图某个属性 | `--view-id` | 12 个:card/timebar/aggregate/filter/sort/group/visible-fields/field-widths(详见 [aitable-view-config.md](./aitable/aitable-view-config.md))+ lock/frozen-cols/row-height/fill-color-rule(详见 [aitable-view-extras.md](./aitable/aitable-view-extras.md)) | | `view get <attr>` | 获取视图某个属性 | `--view-id` | 12 个:card/timebar/aggregate/filter/sort/group/visible-fields/field-widths(详见 [aitable-view-config.md](./aitable/aitable-view-config.md))+ lock/frozen-cols/row-height/fill-color-rule(详见 [aitable-view-extras.md](./aitable/aitable-view-extras.md)) |
| `view list` | 列出全部视图(`view get` 的别名) | `--base-id` `--table-id` | 与 `view get` 完全等价 | | `view list` | 列出全部视图(`view get` 的别名) | `--base-id` `--table-id` | 与 `view get` 完全等价 |
| `view create` | 创建视图 | `--base-id` `--table-id` `--view-type` | 类型: Grid/Kanban/Gantt/Calendar/Gallery/FormDesigner;可用 `--visible-field-ids`,或用 `--config` 传 visibleFieldIds/filter/sort/group;**Gantt 创建后必须 `view update timebar` 绑定日期字段** | | `view create` | 创建视图 | `--base-id` `--table-id` `--view-type` | 类型: Grid/Kanban/Gantt/Calendar/Gallery/FormDesigner;用 `--config` 传 visibleFieldIds/filter/sort/group;**Gantt 创建后必须 `view update timebar` 绑定日期字段** |
| `view update` | 整体更新视图 / 多属性合并更新 | `--base-id` `--table-id` `--view-id` | 可传 `--name --desc --config '{...}'`,**`--config` 路径继续保留** | | `view update` | 整体更新视图 / 多属性合并更新 | `--base-id` `--table-id` `--view-id` | 可传 `--name --desc --config '{...}'`,**`--config` 路径继续保留** |
| `view update <attr>` | 按属性局部更新(推荐)| `--view-id` + typed flag / `--json` | 12 个:card/timebar/aggregate/field-widths/visible-fields/filter/sort/group/name + frozen-cols/row-height/fill-color-rule | | `view update <attr>` | 按属性局部更新(推荐)| `--view-id` + typed flag / `--json` | 12 个:card/timebar/aggregate/field-widths/visible-fields/filter/sort/group/name + frozen-cols/row-height/fill-color-rule |
| `view lock [--off]` | 锁定/解锁视图 | `--base-id` `--table-id` `--view-id` | 默认锁定;`--off` 解锁。详见 [aitable-view-extras.md](./aitable/aitable-view-extras.md) | | `view lock [--off]` | 锁定/解锁视图 | `--base-id` `--table-id` `--view-id` | 默认锁定;`--off` 解锁。详见 [aitable-view-extras.md](./aitable/aitable-view-extras.md) |
@@ -20,15 +20,10 @@
## 创建:view create ## 创建:view create
`--view-type` 支持 `Grid`、`Kanban`、`Gantt`、`Calendar`、`Gallery`、`FormDesigner`。创建时有两种方式设置可见字段: `--view-type` 支持 `Grid`、`Kanban`、`Gantt`、`Calendar`、`Gallery`、`FormDesigner`。创建时通过 `--config` JSON 设置可见字段:
```bash ```bash
# 推荐:CSV typed flag;主字段必须排第一 # --config JSON:可同时配置可见字段、筛选、排序和分组;主字段必须排第一
dws aitable view create --base-id BASE_ID --table-id TABLE_ID \
--view-type Grid --name "任务视图" \
--visible-field-ids fldPrimary,fldStatus,fldOwner
# JSON 方式:可同时配置可见字段、筛选、排序和分组
dws aitable view create --base-id BASE_ID --table-id TABLE_ID \ dws aitable view create --base-id BASE_ID --table-id TABLE_ID \
--view-type Grid --name "任务视图" \ --view-type Grid --name "任务视图" \
--config '{"visibleFieldIds":["fldPrimary","fldStatus","fldOwner"],"sort":[{"fieldId":"fldStatus","direction":"asc"}]}' --config '{"visibleFieldIds":["fldPrimary","fldStatus","fldOwner"],"sort":[{"fieldId":"fldStatus","direction":"asc"}]}'
@@ -43,7 +38,7 @@ dws aitable view create --base-id BASE_ID --table-id TABLE_ID \
| `sort` | `object[]` | 排序规则数组;兼容单个 object,CLI 会自动包装为数组 | | `sort` | `object[]` | 排序规则数组;兼容单个 object,CLI 会自动包装为数组 |
| `group` | `object[]` | 分组规则数组;兼容单个 object,CLI 会自动包装为数组 | | `group` | `object[]` | 分组规则数组;兼容单个 object,CLI 会自动包装为数组 |
`--visible-field-ids` 与 `config.visibleFieldIds` 互斥。描述使用独立的 `--desc '{"content":[]}'`;`description`、`fieldWidths`、`aggregate`、`kanbanCard`、`ganttTimebar`、`galleryCard` 等其他 key 会在调用服务端前被拒绝,并提示对应的 `view update` 子命令。 描述使用独立的 `--desc '{"content":[]}'`;`description`、`fieldWidths`、`aggregate`、`kanbanCard`、`ganttTimebar`、`galleryCard` 等其他 key 会在调用服务端前被拒绝,并提示对应的 `view update` 子命令。
## 读取:view get <attr> ## 读取:view get <attr>
@@ -514,18 +514,18 @@ Flags:
``` ```
Usage: Usage:
dws drive download [flags] dws drive download-version [flags]
Example: Example:
dws drive download --node <dentryUuid> --version 3 --output ./report_v1.pdf --format json dws drive download-version --node <dentryUuid> --version 3 --output ./report_v3.pdf --format json
dws drive download --node <dentryUuid> --version 3 --output <DOWNLOAD_DIR> --format json dws drive download-version --node <dentryUuid> --version 3 --output <DOWNLOAD_DIR> --format json
Flags: Flags:
--node string 文件 ID (dentryUuid) 或 URL (必填) --node string 文件 ID (dentryUuid) 或 URL (必填)
--version int 历史版本号 (必填,正整数,从 list --versions 获取) --version int 历史版本号 (必填,正整数,从 list --versions 获取)
--output string 本地保存路径 (文件路径或目录,不传则保存到当前目录) (可选) --output string 本地保存路径 (文件路径或目录,必填)
``` ```
> **两步下载流程**:先调用 MCP 工具获取下载 URL 和签名头,再 HTTP GET 下载文件内容到本地。 > **两步下载流程**:先调用 MCP 工具获取历史版本下载 URL 和签名头,再 HTTP GET 下载文件内容到本地。
> 不传 `--output` 或指定目录时,优先从文件信息中获取原始文件名,获取不到时从下载 URL 推断;均不可用时使用默认名 `download.<扩展名>`。 > `--output` 指定目录时,优先从文件信息中获取原始文件名,获取不到时从下载 URL 推断。
#### 回滚文件到指定历史版本 #### 回滚文件到指定历史版本
+66 -6
View File
@@ -56,6 +56,14 @@ var (
"dws skill find": true, // backward-compat stub "dws skill find": true, // backward-compat stub
} }
// Full command lines intentionally shown as wrong usage inside fenced
// 「错误示例」 blocks (kept in docs to steer LLMs away from hallucinated
// flags). Matched against the exact extracted command text.
antiPatternExactCommands = map[string]bool{
"dws report inbox list --start-date 2026-05-04 --end-date 2026-05-11 --format json": true, // report.md: 禁止 --start-date/--end-date
"dws report inbox list --date 2026-05-11 --format json": true, // report.md: 禁止 --date
}
// Commands in this set are runnable leaves whose following token is a // Commands in this set are runnable leaves whose following token is a
// positional argument rather than another Cobra subcommand. // positional argument rather than another Cobra subcommand.
positionalLeafCommands = map[string]bool{ positionalLeafCommands = map[string]bool{
@@ -100,7 +108,11 @@ type cmdRef struct {
Cmd string Cmd string
} }
// extractCommands scans skills/**/*.md for every backtick `dws ...` command. // extractCommands scans skills/**/*.md for every `dws ...` command reference:
// inline-backtick commands in prose, plus command lines inside fenced ``` code
// blocks (lines whose trimmed text starts with an optional "$ " prefix
// followed by "dws "). Backslash-continuation lines inside fenced blocks are
// joined before parsing so multi-line invocations audit as one command.
func extractCommands(root string) ([]cmdRef, error) { func extractCommands(root string) ([]cmdRef, error) {
skillsDir := filepath.Join(root, "skills") skillsDir := filepath.Join(root, "skills")
var refs []cmdRef var refs []cmdRef
@@ -126,12 +138,22 @@ func extractCommands(root string) ([]cmdRef, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
lineNo := 0 var lines []string
scanner := bufio.NewScanner(strings.NewReader(data)) scanner := bufio.NewScanner(strings.NewReader(data))
scanner.Buffer(make([]byte, 1024*1024), 1024*1024*4) scanner.Buffer(make([]byte, 1024*1024), 1024*1024*4)
for scanner.Scan() { for scanner.Scan() {
lineNo++ lines = append(lines, scanner.Text())
matches := backtickCmd.FindAllStringSubmatch(scanner.Text(), -1) }
inFence := false
for i := 0; i < len(lines); i++ {
lineNo := i + 1
trimmed := strings.TrimSpace(lines[i])
if strings.HasPrefix(trimmed, "```") {
inFence = !inFence
continue
}
if !inFence {
matches := backtickCmd.FindAllStringSubmatch(lines[i], -1)
for _, m := range matches { for _, m := range matches {
cmd := strings.TrimSpace(m[1]) cmd := strings.TrimSpace(m[1])
if shouldSkip(cmd) { if shouldSkip(cmd) {
@@ -139,6 +161,35 @@ func extractCommands(root string) ([]cmdRef, error) {
} }
refs = append(refs, cmdRef{File: f, Line: lineNo, Cmd: cmd}) refs = append(refs, cmdRef{File: f, Line: lineNo, Cmd: cmd})
} }
continue
}
// Inside a fenced block: only lines that *start* with an
// (optionally "$ "-prefixed) dws invocation are commands;
// everything else is illustrative output.
cmdText := strings.TrimPrefix(trimmed, "$ ")
if !strings.HasPrefix(cmdText, "dws ") {
continue
}
// Join backslash-continuation lines into one logical command.
for strings.HasSuffix(cmdText, "\\") && i+1 < len(lines) {
next := strings.TrimSpace(lines[i+1])
if strings.HasPrefix(next, "```") {
break
}
cmdText = strings.TrimSuffix(cmdText, "\\")
cmdText = strings.TrimSpace(cmdText) + " " + next
i++
}
cmdText = strings.TrimSpace(cmdText)
// Strip a trailing inline shell comment ("dws ... # 说明") so the
// annotation text is not parsed as flags or positional arguments.
if idx := strings.Index(cmdText, " # "); idx >= 0 {
cmdText = strings.TrimSpace(cmdText[:idx])
}
if shouldSkip(cmdText) {
continue
}
refs = append(refs, cmdRef{File: f, Line: lineNo, Cmd: cmdText})
} }
} }
return refs, nil return refs, nil
@@ -276,9 +327,15 @@ func exactCommand(root *cobra.Command, sub string) *cobra.Command {
} }
if next == nil { if next == nil {
// Once a runnable command is reached, remaining non-flag tokens are // Once a runnable command is reached, remaining non-flag tokens are
// positional arguments rather than descendants. Validate them with // positional arguments rather than descendants. A command that has
// the command's Cobra Args contract when one is present. // registered child commands treats an unknown next token as a
// misspelled subcommand, not a positional argument. For true
// leaves, validate positionals with the command's Cobra Args
// contract when one is present.
if current.Runnable() { if current.Runnable() {
if len(current.Commands()) > 0 {
return nil
}
args := tokens[i:] args := tokens[i:]
if current.Args == nil || current.Args(current, args) == nil { if current.Args == nil || current.Args(current, args) == nil {
return current return current
@@ -350,6 +407,9 @@ func TestSkillCommandsDispatch(t *testing.T) {
seen := map[string]bool{} // dedupe identical sub-path and flag sets seen := map[string]bool{} // dedupe identical sub-path and flag sets
for _, r := range refs { for _, r := range refs {
if antiPatternExactCommands[r.Cmd] {
continue
}
sub, _, flags := parseSubPath(r.Cmd) sub, _, flags := parseSubPath(r.Cmd)
if sub == "" { if sub == "" {
continue continue