diff --git a/.changes/doc-drive-param-aliases.md b/.changes/doc-drive-param-aliases.md new file mode 100644 index 00000000..0bc4bdcb --- /dev/null +++ b/.changes/doc-drive-param-aliases.md @@ -0,0 +1,5 @@ +--- +category: Fixed +--- + +- **Doc and Drive parameter aliases** — normalizes reviewed identifier, pagination, path, version, and role synonyms while blocking ambiguous values before dispatch. diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b2c76446..b8c1007d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -153,6 +153,12 @@ jobs: filename.startsWith('internal/helpers/') || filename.startsWith('internal/generator/') || filename.startsWith('internal/cli/schema') || + // Parameter aliases are reduced against the live command tree. + // Their reverse-dependency set is too large for one focused + // race job, so use the existing full-suite shards. + filename === 'internal/cli/param_concepts.json' || + filename === 'internal/cli/param_concepts.schema.json' || + filename === 'internal/cli/param_aliases_generated.go' || filename.startsWith('internal/interfacesnapshot/') || filename.startsWith('internal/app/upgrade') || filename.startsWith('internal/transport/') || @@ -559,7 +565,8 @@ jobs: needs: lint if: ${{ needs.lint.outputs.changelog_only != 'true' && needs.lint.outputs.docs_only != 'true' && needs.lint.outputs.full_suite == 'true' }} runs-on: ubuntu-latest - # cli/smoke shards need headroom beyond go test -timeout for setup + assembly. + # app runs several independently bounded processes; cli/smoke need headroom + # beyond go test -timeout for setup + assembly. timeout-minutes: 20 strategy: fail-fast: false @@ -595,10 +602,19 @@ jobs: test -n "$package_output" mapfile -t packages <<< "$package_output" test "${#packages[@]}" -gt 0 - # cli/smoke own heavy NewRootCommand / Schema assembly under -race; give - # them a dedicated budget so remaining is not SIGTERM'd by OOM/timeout. + if [ "$TEST_SHARD" = "app" ]; then + # A single long-lived app test process retains every constructed + # command tree in framework registries. Isolate Schema assembly and + # bounded name ranges so each process releases that state on exit. + test "${#packages[@]}" -eq 1 + ./scripts/ci/run-app-race-tests.sh run "${packages[0]}" + exit 0 + fi + # cli/smoke own heavy NewRootCommand / Schema assembly under -race; + # give them a dedicated package timeout on slower hosted runners. timeout_budget=12m - if [ "$TEST_SHARD" = "cli" ] || [ "$TEST_SHARD" = "smoke" ]; then + if [ "$TEST_SHARD" = "cli" ] || + [ "$TEST_SHARD" = "smoke" ]; then timeout_budget=15m fi go test -v -race -count=1 -timeout="$timeout_budget" "${packages[@]}" diff --git a/internal/app/param_alias_e2e_test.go b/internal/app/param_alias_e2e_test.go index 8267390c..77b9ac5b 100644 --- a/internal/app/param_alias_e2e_test.go +++ b/internal/app/param_alias_e2e_test.go @@ -68,6 +68,45 @@ func (c *paramAliasCaptureCaller) paramAliasResponseForTool(tool string) string return `{"result":[{"templateId":"fixture-template-id"}]}` case "create_document": return `{"nodeId":"fixture-node"}` + case "list_files": + return `{"success":true,"result":{"files":[],"hasMore":false}}` + case "list_recycle_items": + return `{"success":true,"result":{"recycleItems":[{"recycleItemId":"recycle-1","originalName":"Fixture Node"}],"hasMore":false}}` + case "get_star_list": + return `{"success":true,"result":{"starList":[],"hasMore":false}}` + case "list_file_versions": + return `{"success":true,"result":{"versions":[{"version":3,"name":"Fixture Version"}],"hasMore":false}}` + case "get_file_info": + name := "Fixture Node" + for index := len(c.calls) - 2; index >= 0; index-- { + call := c.calls[index] + switch call.tool { + case "create_folder": + if value, ok := call.args["name"].(string); ok { + name = value + } + index = -1 + case "rename_document": + if value, ok := call.args["newName"].(string); ok { + name = value + } + index = -1 + } + } + encoded, _ := json.Marshal(map[string]any{"success": true, "result": map[string]any{"fileId": "node-1", "name": name}}) + return string(encoded) + case "get_cover", "get_node_stats": + return `{"success":true,"result":{"nodeId":"node-1"}}` + case "get_file_publish_status": + return `{"success":true,"result":{"fileId":"node-1","published":false}}` + case "create_folder", "create_shortcut": + return `{"success":true,"fileId":"node-1"}` + case "delete_document", "mark_star", "unmark_star", "restore_recycle_item", "rename_document", "revert_file_version": + return `{"success":true,"fileId":"node-1"}` + case "set_file_publish": + return `{"success":true}` + case "download_file", "download_file_version": + return `{"success":true,"result":{"downloadUrl":"http://invalid.test/fixture.bin","fileName":"fixture.bin"}}` case "get_document_content": for index := len(c.calls) - 2; index >= 0; index-- { call := c.calls[index] diff --git a/internal/app/param_alias_payload_equivalence_test.go b/internal/app/param_alias_payload_equivalence_test.go index 494a8b6c..2db173ef 100644 --- a/internal/app/param_alias_payload_equivalence_test.go +++ b/internal/app/param_alias_payload_equivalence_test.go @@ -4,11 +4,13 @@ package app import ( + "errors" "reflect" "strings" "testing" "github.com/DingTalk-Real-AI/dingtalk-workspace-cli/internal/cli" + apperrors "github.com/DingTalk-Real-AI/dingtalk-workspace-cli/internal/errors" "github.com/DingTalk-Real-AI/dingtalk-workspace-cli/internal/pipeline" ) @@ -124,6 +126,7 @@ var paramAliasCompleteCommands = map[string][]string{ "doc +version-revert": {"doc", "+version-revert", "--node", "node-1", "--version", "3", "--yes"}, "doc +version-save": {"doc", "+version-save", "--node", "node-1", "--yes"}, "doc +update": {"doc", "+update", "--node", "node-1", "--command", "overwrite", "--content", `["root",{}]`, "--doc-format", "jsonml", "--expected-revision", "1", "--yes"}, + "doc +export": {"doc", "+export", "--node", "node-1", "--export-format", "docx", "--output", "exports/fixture.docx"}, "doc block insert": {"doc", "block", "insert", "--node", "node-1", "--text", "fixture paragraph", "--yes"}, "doc block update": {"doc", "block", "update", "--node", "node-1", "--block-id", "block-1", "--text", "fixture paragraph", "--yes"}, "doc comment create": {"doc", "comment", "create", "--node", "node-1", "--content", "fixture comment", "--yes"}, @@ -131,9 +134,43 @@ var paramAliasCompleteCommands = map[string][]string{ "doc comment delete": {"doc", "comment", "delete", "--node", "node-1", "--comment-key", "comment-1", "--yes"}, "doc comment reply": {"doc", "comment", "reply", "--node", "node-1", "--comment-key", "comment-1", "--content", "fixture reply", "--mentioned-open-conversation-id", "cid-1,cid-2", "--yes"}, "doc comment update": {"doc", "comment", "update", "--node", "node-1", "--comment-key", "comment-1", "--content", "fixture update", "--yes"}, + "doc create": {"doc", "create", "--name", "Fixture Document", "--workspace", "workspace-1"}, "doc version revert": {"doc", "version", "revert", "--node", "node-1", "--version", "3", "--yes"}, + "drive +cover": {"drive", "+cover", "--node", "node-1"}, + "drive +create-folder": {"drive", "+create-folder", "--name", "Fixture Folder", "--space-id", "space-1", "--folder", "folder-1"}, + "drive +create-shortcut": {"drive", "+create-shortcut", "--node", "node-1", "--folder", "folder-1", "--workspace", "workspace-1"}, + "drive +delete": {"drive", "+delete", "--node", "node-1", "--yes"}, + "drive +download": {"drive", "+download", "--node", "node-1", "--space-id", "space-1", "--output", "downloads/fixture.bin"}, + "drive +info": {"drive", "+info", "--node", "node-1", "--space-id", "space-1"}, + "drive +inspect": {"drive", "+inspect", "--node", "node-1", "--space-id", "space-1", "--include-stats"}, + "drive +list": {"drive", "+list", "--space-id", "space-1", "--folder", "folder-1", "--limit", "7", "--cursor", "cursor-1", "--order-by", "name", "--order", "asc"}, + "drive +publish-get": {"drive", "+publish-get", "--node", "node-1"}, + "drive +publish-unset": {"drive", "+publish-unset", "--node", "node-1", "--yes"}, + "drive +recycle-list": {"drive", "+recycle-list", "--space-id", "space-1", "--limit", "7", "--cursor", "cursor-1"}, + "drive +recycle-restore": {"drive", "+recycle-restore", "--id", "recycle-1", "--yes"}, + "drive +rename": {"drive", "+rename", "--node", "node-1", "--name", "Fixture Renamed", "--yes"}, + "drive +search": {"drive", "+search", "--query", "fixture"}, + "drive +star-add": {"drive", "+star-add", "--node", "node-1"}, + "drive +star-list": {"drive", "+star-list", "--limit", "7", "--cursor", "cursor-1"}, + "drive +star-remove": {"drive", "+star-remove", "--node", "node-1"}, + "drive +stats": {"drive", "+stats", "--node", "node-1"}, + "drive +upload": {"drive", "+upload", "--file", "param_alias_payload_equivalence_test.go", "--file-name", "fixture.txt", "--mime-type", "text/plain", "--space-id", "space-1", "--node", "node-1", "--yes"}, + "drive +version-download": {"drive", "+version-download", "--node", "node-1", "--version", "3", "--output", "downloads/fixture-v3.bin"}, + "drive +version-get": {"drive", "+version-get", "--node", "node-1", "--version", "3"}, + "drive +version-history": {"drive", "+version-history", "--node", "node-1", "--limit", "7", "--cursor", "cursor-1"}, + "drive +version-revert": {"drive", "+version-revert", "--node", "node-1", "--version", "3", "--yes"}, + "drive commit": {"drive", "commit", "--file-name", "fixture.txt", "--file-size", "7", "--upload-id", "upload-1", "--space-id", "space-1"}, + "drive copy": {"drive", "copy", "--node", "node-1", "--folder", "folder-1"}, + "drive download": {"drive", "download", "--node", "node-1", "--output", "downloads/fixture.bin", "--version", "3", "--space-id", "space-1"}, "drive info": {"drive", "info", "--node", "node-1", "--space-id", "space-1"}, "drive list": {"drive", "list", "--folder", "folder-1", "--limit", "7"}, + "drive mkdir": {"drive", "mkdir", "--name", "Fixture Folder", "--space-id", "space-1"}, + "drive permission add": {"drive", "permission", "add", "--node", "node-1", "--users", "user-1,user-2", "--role", "READER"}, + "drive recycle list": {"drive", "recycle", "list", "--space-id", "space-1", "--limit", "7"}, + "drive recycle restore": {"drive", "recycle", "restore", "--id", "recycle-1"}, + "drive search": {"drive", "search", "--query", "fixture", "--created-from", "1", "--created-to", "2", "--modified-from", "3", "--modified-to", "4", "--creator-uids", "user-1,user-2"}, + "drive upload": {"drive", "upload", "--file", "../../go.mod", "--space-id", "space-1"}, + "drive upload-info": {"drive", "upload-info", "--file-name", "fixture.txt", "--file-size", "7", "--space-id", "space-1"}, "mail +find-mail-user": {"mail", "+find-mail-user", "--query", "fixture", "--limit", "7"}, "mail folder update": {"mail", "folder", "update", "--email", "fixture@example.com", "--id", "folder-1", "--name", "Fixture Folder", "--yes"}, "mail message search": {"mail", "message", "search", "--email", "fixture@example.com", "--query", "subject:fixture"}, @@ -156,6 +193,20 @@ var paramAliasCompleteCommandVariants = map[string]map[string][]string{ "doc block insert": { "parent-block": {"doc", "block", "insert", "--node", "node-1", "--parent-block", "parent-block-1", "--index", "0", "--text", "fixture paragraph", "--yes"}, }, + "doc +inspect": { + "include-permissions": {"doc", "+inspect", "--node", "node-1", "--include-permissions"}, + }, + "doc +search": { + "created-from": {"doc", "+search", "--query", "fixture", "--created-from", "1"}, + "created-to": {"doc", "+search", "--query", "fixture", "--created-to", "2"}, + "creator-uids": {"doc", "+search", "--query", "fixture", "--creator-uids", "user-1,user-2"}, + }, + "drive list": { + "workspace": {"drive", "list", "--workspace", "workspace-1", "--limit", "7"}, + "order-by": {"drive", "list", "--folder", "folder-1", "--order-by", "name", "--limit", "7"}, + "space-id": {"drive", "list", "--space-id", "space-1", "--limit", "7"}, + "order": {"drive", "list", "--folder", "folder-1", "--order", "asc", "--limit", "7"}, + }, "chat message list": { "user": {"chat", "message", "list", "--user", "user-1", "--time", "2026-03-10 00:00:00", "--limit", "7"}, }, @@ -241,12 +292,105 @@ var paramAliasNewIMCases = []struct { {command: "chat +messages-set-pin", emitted: "conversation-id", canonical: "open-conversation-id"}, } +// paramAliasNewDriveCases is the exact executable-alias set introduced by the +// reviewed Drive expansion. Guard fixtures are covered separately by the +// exhaustive runtime-contract tests; every entry here must preserve the final +// transport payload of its canonical spelling. +var paramAliasNewDriveCases = []struct { + command string + emitted string + canonical string +}{ + {command: "drive +cover", emitted: "dentry-uuid", canonical: "node"}, + {command: "drive +create-folder", emitted: "folder-name", canonical: "name"}, + {command: "drive +create-folder", emitted: "storage-space-id", canonical: "space-id"}, + {command: "drive +create-shortcut", emitted: "source-file-id", canonical: "node"}, + {command: "drive +create-shortcut", emitted: "target-folder-id", canonical: "folder"}, + {command: "drive +create-shortcut", emitted: "target-workspace-id", canonical: "workspace"}, + {command: "drive +delete", emitted: "file-id", canonical: "node"}, + {command: "drive +download", emitted: "dentry-uuid", canonical: "node"}, + {command: "drive +download", emitted: "destination-path", canonical: "output"}, + {command: "drive +inspect", emitted: "include-statistics", canonical: "include-stats"}, + {command: "drive +list", emitted: "folder-id", canonical: "folder"}, + {command: "drive +list", emitted: "page-size", canonical: "limit"}, + {command: "drive +list", emitted: "next-token", canonical: "cursor"}, + {command: "drive +list", emitted: "sort-direction", canonical: "order"}, + {command: "drive +list", emitted: "sort-by", canonical: "order-by"}, + {command: "drive +publish-get", emitted: "dentry-uuid", canonical: "node"}, + {command: "drive +publish-unset", emitted: "file-id", canonical: "node"}, + {command: "drive +recycle-list", emitted: "storage-space-id", canonical: "space-id"}, + {command: "drive +recycle-list", emitted: "page-token", canonical: "cursor"}, + {command: "drive +recycle-restore", emitted: "recycle-item-id", canonical: "id"}, + {command: "drive +rename", emitted: "file-id", canonical: "node"}, + {command: "drive +rename", emitted: "new-name", canonical: "name"}, + {command: "drive +star-add", emitted: "dentry-uuid", canonical: "node"}, + {command: "drive +star-list", emitted: "max-results", canonical: "limit"}, + {command: "drive +star-remove", emitted: "file-id", canonical: "node"}, + {command: "drive +stats", emitted: "dentry-uuid", canonical: "node"}, + {command: "drive +upload", emitted: "source-file", canonical: "file"}, + {command: "drive +upload", emitted: "name", canonical: "file-name"}, + {command: "drive +upload", emitted: "overwrite-node-id", canonical: "node"}, + {command: "drive +version-download", emitted: "version-number", canonical: "version"}, + {command: "drive +version-download", emitted: "save-path", canonical: "output"}, + {command: "drive +version-get", emitted: "version-no", canonical: "version"}, + {command: "drive +version-history", emitted: "next-cursor", canonical: "cursor"}, + {command: "drive +version-history", emitted: "page-size", canonical: "limit"}, + {command: "drive +version-revert", emitted: "version-number", canonical: "version"}, + {command: "drive +cover", emitted: "node-id", canonical: "node"}, + {command: "drive +create-shortcut", emitted: "node-id", canonical: "node"}, + {command: "drive +delete", emitted: "node-id", canonical: "node"}, + {command: "drive +download", emitted: "node-id", canonical: "node"}, + {command: "drive +inspect", emitted: "node-id", canonical: "node"}, + {command: "drive +publish-get", emitted: "node-id", canonical: "node"}, + {command: "drive +publish-unset", emitted: "node-id", canonical: "node"}, + {command: "drive +rename", emitted: "node-id", canonical: "node"}, + {command: "drive +star-add", emitted: "node-id", canonical: "node"}, + {command: "drive +star-remove", emitted: "node-id", canonical: "node"}, + {command: "drive +stats", emitted: "node-id", canonical: "node"}, + {command: "drive +upload", emitted: "node-id", canonical: "node"}, + {command: "drive +version-download", emitted: "node-id", canonical: "node"}, + {command: "drive +version-get", emitted: "node-id", canonical: "node"}, + {command: "drive +version-history", emitted: "node-id", canonical: "node"}, + {command: "drive +version-revert", emitted: "node-id", canonical: "node"}, + {command: "drive +cover", emitted: "url", canonical: "node"}, + {command: "drive +create-shortcut", emitted: "document-id", canonical: "node"}, + {command: "drive +delete", emitted: "folder-id", canonical: "node"}, + {command: "drive +inspect", emitted: "document-id", canonical: "node"}, + {command: "drive +inspect", emitted: "folder-id", canonical: "node"}, + {command: "drive +publish-get", emitted: "url", canonical: "node"}, + {command: "drive +publish-unset", emitted: "document-url", canonical: "node"}, + {command: "drive +rename", emitted: "document-id", canonical: "node"}, + {command: "drive +rename", emitted: "folder-id", canonical: "node"}, + {command: "drive +star-add", emitted: "url", canonical: "node"}, + {command: "drive +star-remove", emitted: "doc-id", canonical: "node"}, + {command: "drive +stats", emitted: "document-url", canonical: "node"}, + {command: "drive +upload", emitted: "file-id", canonical: "node"}, +} + +// paramAliasNewDriveConfirmationCases selects one newly reviewed alias for +// every Drive command in the expansion whose declared runtime safety requires +// confirmation. The full matrix below proves all spellings preserve the +// confirmed payload; this smaller matrix proves aliases cannot cross the +// confirmation boundary before any transport call is made. +var paramAliasNewDriveConfirmationCases = []struct { + command string + emitted string + canonical string +}{ + {command: "drive +delete", emitted: "file-id", canonical: "node"}, + {command: "drive +publish-unset", emitted: "document-url", canonical: "node"}, + {command: "drive +recycle-restore", emitted: "recycle-item-id", canonical: "id"}, + {command: "drive +rename", emitted: "new-name", canonical: "name"}, + {command: "drive +upload", emitted: "source-file", canonical: "file"}, + {command: "drive +version-revert", emitted: "version-number", canonical: "version"}, +} + // paramAliasRepresentativePayloadCases keeps final transport coverage across // old concept aliases, command overrides, native compatibility flags, read and // write commands, and different products. Every reviewed alias is still // checked through the embedded PreParse delivery path and against a complete -// business-valid command template. The separate IM gate below continues to -// execute every alias introduced by the current IM optimization. +// business-valid command template. The dedicated product gates below continue +// to execute every alias introduced by the reviewed IM and Drive expansions. // // Keeping the older 100+ aliases at the contract layer avoids rebuilding and // executing the complete 800+ command Root twice per spelling under -race. @@ -261,6 +405,7 @@ var paramAliasRepresentativePayloadCases = map[string]bool{ paramAliasPayloadCaseKey("devdoc article search", "current-page"): true, // command override paramAliasPayloadCaseKey("doc +comment-create", "body"): true, // write shortcut content alias paramAliasPayloadCaseKey("doc +copy", "parent-folder-id"): true, // Doc folder role on a write shortcut + paramAliasPayloadCaseKey("doc create", "space-id"): true, // published Doc workspace compatibility remains payload-equivalent paramAliasPayloadCaseKey("doc +create", "content-format"): true, // shortcut format alias preserves markdown/jsonml enum paramAliasPayloadCaseKey("doc +create-from-template", "keyword"): true, // template search alias composes with a write workflow paramAliasPayloadCaseKey("doc +create-from-template", "workspace-id"): true, // template target workspace identifier @@ -269,6 +414,8 @@ var paramAliasRepresentativePayloadCases = map[string]bool{ paramAliasPayloadCaseKey("doc +fetch", "start-block"): true, // section boundary role remains exact paramAliasPayloadCaseKey("doc +history-revert", "version-number"): true, // destructive history version alias keeps confirmation paramAliasPayloadCaseKey("doc +inspect", "include-versions"): true, // boolean section alias preserves value + paramAliasPayloadCaseKey("doc +search", "create-time-start"): true, // observed lower-bound spelling preserves milliseconds + paramAliasPayloadCaseKey("doc +search", "create-time-end"): true, // observed upper-bound spelling preserves milliseconds paramAliasPayloadCaseKey("doc +template-list", "next-token"): true, // Doc cursor alias on a read shortcut paramAliasPayloadCaseKey("doc +update", "mode"): true, // write operation selector alias paramAliasPayloadCaseKey("doc +update", "revision"): true, // optimistic edit revision alias @@ -278,6 +425,7 @@ var paramAliasRepresentativePayloadCases = map[string]bool{ paramAliasPayloadCaseKey("doc block insert", "parent-block-id"): true, // scoped block-role alias paramAliasPayloadCaseKey("doc comment delete", "comment-id"): true, // destructive comment-key alias paramAliasPayloadCaseKey("doc comment reply", "mentioned-open-conversation-ids"): true, // list-valued group mention role + paramAliasPayloadCaseKey("drive info", "workspace"): true, // published numeric storage-space compatibility remains payload-equivalent paramAliasPayloadCaseKey("mail folder update", "folder-id"): true, // write-command identifier alias paramAliasPayloadCaseKey("report list", "from-date"): true, // date-range concept alias } @@ -421,14 +569,118 @@ func TestCrossPlatformCoverageNewIMParamAliasesReachCanonicalEquivalentFinalPayl } } -// Resource download deliberately continues from the transport call into a -// local HTTPS download. The generic capture caller returns an empty object, so -// this command's stable post-transport validation error is the expected test -// boundary; canonical and alias calls must still produce the same request and -// the same error. +func TestCrossPlatformCoverageNewDriveParamAliasesReachCanonicalEquivalentFinalPayloads(t *testing.T) { + activeAliases := 0 + for _, test := range paramAliasNewDriveCases { + test := test + t.Run(test.command+"/"+test.emitted, func(t *testing.T) { + complete, ok := paramAliasCompleteCommand(test.command, test.canonical) + if !ok { + t.Fatal("reviewed Drive alias has no complete-command E2E template") + } + canonicalArgs := append([]string(nil), complete...) + aliasArgs, replacements := replaceLongFlag(canonicalArgs, test.canonical, test.emitted) + if replacements != 1 { + t.Fatalf("complete command must contain canonical --%s exactly once; replacements=%d args=%v", test.canonical, replacements, canonicalArgs) + } + + canonicalCaller := ¶mAliasCaptureCaller{} + _, canonicalErr := executeParamAliasPayloadE2E(t, canonicalCaller, canonicalArgs...) + if canonicalErr != nil && !paramAliasExpectedCaptureBoundaryError(test.command, canonicalErr) { + t.Fatalf("complete canonical command failed: %v\nargs=%v\ncalls=%#v", canonicalErr, canonicalArgs, canonicalCaller.calls) + } + if len(canonicalCaller.calls) == 0 { + t.Fatalf("complete canonical command reached no final transport payload: args=%v", canonicalArgs) + } + + entry, exists := cli.LookupParamAlias(test.command) + target, active := entry.ResolveAlias(test.emitted) + if !exists || !active { + return + } + if target != test.canonical { + t.Fatalf("active reviewed Drive alias --%s resolves to --%s, want --%s", test.emitted, target, test.canonical) + } + activeAliases++ + + aliasCaller := ¶mAliasCaptureCaller{} + ctx, aliasErr := executeParamAliasPayloadE2E(t, aliasCaller, aliasArgs...) + if aliasErr != nil && !paramAliasExpectedCaptureBoundaryError(test.command, aliasErr) { + t.Fatalf("complete alias command failed: %v\nargs=%v\ncalls=%#v", aliasErr, aliasArgs, aliasCaller.calls) + } + if ctx == nil { + t.Fatal("complete alias command skipped PreParse") + } + if (canonicalErr == nil) != (aliasErr == nil) || (canonicalErr != nil && canonicalErr.Error() != aliasErr.Error()) { + t.Fatalf("canonical and alias completion errors differ: canonical=%v alias=%v", canonicalErr, aliasErr) + } + if !reflect.DeepEqual(aliasCaller.calls, canonicalCaller.calls) { + t.Fatalf("final transport calls differ\ncanonical args: %v\nalias args: %v\ncanonical calls: %#v\nalias calls: %#v", canonicalArgs, aliasArgs, canonicalCaller.calls, aliasCaller.calls) + } + }) + } + if activeAliases != len(paramAliasNewDriveCases) { + t.Fatalf("new Drive aliases active in embedded table = %d, want %d", activeAliases, len(paramAliasNewDriveCases)) + } +} + +func TestCrossPlatformCoverageNewDriveParamAliasesCannotBypassConfirmation(t *testing.T) { + for _, test := range paramAliasNewDriveConfirmationCases { + test := test + t.Run(test.command+"/"+test.emitted, func(t *testing.T) { + complete, ok := paramAliasCompleteCommand(test.command, test.canonical) + if !ok { + t.Fatal("reviewed Drive confirmation alias has no complete-command E2E template") + } + aliasArgs, replacements := replaceLongFlag(complete, test.canonical, test.emitted) + if replacements != 1 { + t.Fatalf("complete command must contain canonical --%s exactly once; replacements=%d args=%v", test.canonical, replacements, complete) + } + unconfirmedArgs, removals := removeExactArg(aliasArgs, "--yes") + if removals != 1 { + t.Fatalf("confirmation template must contain --yes exactly once; removals=%d args=%v", removals, aliasArgs) + } + + entry, exists := cli.LookupParamAlias(test.command) + target, active := entry.ResolveAlias(test.emitted) + if !exists || !active || target != test.canonical { + t.Fatalf("reviewed Drive alias --%s resolution = exists:%v active:%v target:%q, want --%s", test.emitted, exists, active, target, test.canonical) + } + + caller := ¶mAliasCaptureCaller{} + ctx, err := executeParamAliasPayloadE2E(t, caller, unconfirmedArgs...) + if ctx == nil { + t.Fatal("unconfirmed alias command skipped PreParse") + } + var appErr *apperrors.Error + if !errors.As(err, &appErr) || appErr.Reason != "confirmation_required" { + t.Fatalf("unconfirmed alias command error = %#v, want confirmation_required\nargs=%v", err, unconfirmedArgs) + } + if len(caller.calls) != 0 { + t.Fatalf("unconfirmed alias command crossed the transport boundary: args=%v calls=%#v", unconfirmedArgs, caller.calls) + } + }) + } +} + +// Some artifact commands deliberately continue past the final captured +// transport call into local download/upload handling. Deterministic invalid +// resource responses form their expected post-transport test boundary; +// canonical and alias calls must still produce the same request and error. func paramAliasExpectedCaptureBoundaryError(command string, err error) bool { - return command == "chat +messages-resource-download" && err != nil && - strings.Contains(err.Error(), "资源下载接口未返回合法的 HTTPS 下载地址") + if err == nil { + return false + } + switch command { + case "chat +messages-resource-download": + return strings.Contains(err.Error(), "资源下载接口未返回合法的 HTTPS 下载地址") + case "drive +download", "drive +version-download": + return strings.Contains(err.Error(), "下载地址必须是受信任域名上的 HTTPS URL") + case "drive +upload": + return strings.Contains(err.Error(), "incomplete drive upload credentials") + default: + return false + } } // +chat-messages supplies the current wall-clock time when callers omit @@ -483,3 +735,16 @@ func replaceLongFlag(args []string, canonical, emitted string) ([]string, int) { } return out, replacements } + +func removeExactArg(args []string, target string) ([]string, int) { + out := make([]string, 0, len(args)) + removals := 0 + for _, arg := range args { + if arg == target { + removals++ + continue + } + out = append(out, arg) + } + return out, removals +} diff --git a/internal/app/pat_auth_retry_test.go b/internal/app/pat_auth_retry_test.go index 11b8eae5..2d803e37 100644 --- a/internal/app/pat_auth_retry_test.go +++ b/internal/app/pat_auth_retry_test.go @@ -30,6 +30,7 @@ import ( apperrors "github.com/DingTalk-Real-AI/dingtalk-workspace-cli/internal/errors" "github.com/DingTalk-Real-AI/dingtalk-workspace-cli/internal/executor" "github.com/DingTalk-Real-AI/dingtalk-workspace-cli/internal/pat" + "github.com/DingTalk-Real-AI/dingtalk-workspace-cli/internal/testseam" "github.com/DingTalk-Real-AI/dingtalk-workspace-cli/pkg/agentproduct" ) @@ -218,8 +219,16 @@ func TestPatScopeError_Error(t *testing.T) { // /cli/oauth/device/poll?flowId= with the given status sequence. // It also writes the server URL into a temp DWS_CONFIG_DIR/mcp_url so that // GetMCPBaseURL() returns the test server address. +func stubPATPollAccessToken(t *testing.T) { + t.Helper() + testseam.Swap(t, &patResolveAccessToken, func(context.Context, string, string) (string, error) { + return "", authpkg.ErrTokenDataNotFound + }) +} + func setupPollServer(t *testing.T, statuses []authpkg.DevicePollResponse) (*httptest.Server, string) { t.Helper() + stubPATPollAccessToken(t) var callCount atomic.Int32 server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { @@ -381,6 +390,7 @@ func TestPollPatDeviceFlow_ServerErrorFallback(t *testing.T) { } func TestPollPatDeviceFlow_RedirectSkipped(t *testing.T) { + stubPATPollAccessToken(t) // When server returns 302 (SSO redirect), poll should continue until real response. var callCount int32 server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { @@ -540,6 +550,7 @@ func (m *mockRunner) Run(ctx context.Context, inv executor.Invocation) (executor // It responds to device poll requests with the given status after the first poll. func setupHandlePATServer(t *testing.T, terminalStatus string, authCode string) (*httptest.Server, string) { t.Helper() + stubPATPollAccessToken(t) var pollCount atomic.Int32 server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { diff --git a/internal/app/testmain_test.go b/internal/app/testmain_test.go index 5bed65f4..34e1adaa 100644 --- a/internal/app/testmain_test.go +++ b/internal/app/testmain_test.go @@ -83,6 +83,10 @@ func TestMain(m *testing.M) { // remove a TempDir while the audit lock is still open. setupAuditSink() openBrowserFunc = func(string) error { return nil } + // App tests may run in filtered CI processes. Install the same lazy Schema + // source factory as NewRootCommand without constructing a root so ResolveMeta + // tests never depend on an earlier test having initialized process state. + registerSchemaRuntimeDelivery() code := m.Run() StopAllStdioClients() CloseAuditSink() diff --git a/internal/cli/param_aliases_generated.go b/internal/cli/param_aliases_generated.go index d5229552..dc98598e 100644 --- a/internal/cli/param_aliases_generated.go +++ b/internal/cli/param_aliases_generated.go @@ -1883,51 +1883,68 @@ var generatedParamAliases = []ParamAliasEntry{ { CLIPath: "doc +access-change", Aliases: map[string]string{ - "doc": "node", - "doc-id": "node", - "document-id": "node", - "file-id": "node", - "node-id": "node", - "space": "workspace", - "space-id": "workspace", - "url": "node", - "workspace-id": "workspace", + "access-role": "role", + "dentry-uuid": "node", + "doc": "node", + "doc-id": "node", + "document-id": "node", + "file-id": "node", + "knowledge-base-id": "workspace", + "member-role": "role", + "node-id": "node", + "permission-role": "role", + "space": "workspace", + "space-id": "workspace", + "url": "node", + "wiki-workspace-id": "workspace", + "workspace-id": "workspace", }, - Blocked: []string{"block-id", "comment-id", "comment-key", "folder", "folder-id", "id", "job-id", "parent-id", "revision", "task-id", "template-id", "version"}, + Blocked: []string{"block-id", "comment-id", "comment-key", "dentry-id", "dingdrive-space-id", "drive-space-id", "filter-role", "folder", "folder-id", "id", "job-id", "name", "parent-id", "permission", "public-permission", "reserve-role", "revision", "storage-space-id", "task-id", "template-id", "version"}, }, { CLIPath: "doc +access-grant", Aliases: map[string]string{ - "doc": "node", - "doc-id": "node", - "document-id": "node", - "file-id": "node", - "node-id": "node", - "space": "workspace", - "space-id": "workspace", - "url": "node", - "workspace-id": "workspace", + "access-role": "role", + "dentry-uuid": "node", + "doc": "node", + "doc-id": "node", + "document-id": "node", + "file-id": "node", + "knowledge-base-id": "workspace", + "member-role": "role", + "node-id": "node", + "permission-role": "role", + "space": "workspace", + "space-id": "workspace", + "url": "node", + "wiki-workspace-id": "workspace", + "workspace-id": "workspace", }, - Blocked: []string{"block-id", "comment-id", "comment-key", "folder", "folder-id", "id", "job-id", "parent-id", "revision", "task-id", "template-id", "version"}, + Blocked: []string{"block-id", "comment-id", "comment-key", "dentry-id", "dingdrive-space-id", "drive-space-id", "filter-role", "folder", "folder-id", "id", "job-id", "name", "parent-id", "permission", "public-permission", "reserve-role", "revision", "storage-space-id", "target-user-id", "target-user-ids", "task-id", "template-id", "user-id", "user-ids", "version"}, + Ambiguous: []string{"target-user", "user", "users"}, }, { CLIPath: "doc +access-revoke", Aliases: map[string]string{ - "doc": "node", - "doc-id": "node", - "document-id": "node", - "file-id": "node", - "node-id": "node", - "space": "workspace", - "space-id": "workspace", - "url": "node", - "workspace-id": "workspace", + "dentry-uuid": "node", + "doc": "node", + "doc-id": "node", + "document-id": "node", + "file-id": "node", + "knowledge-base-id": "workspace", + "node-id": "node", + "space": "workspace", + "space-id": "workspace", + "url": "node", + "wiki-workspace-id": "workspace", + "workspace-id": "workspace", }, - Blocked: []string{"block-id", "comment-id", "comment-key", "folder", "folder-id", "id", "job-id", "parent-id", "revision", "task-id", "template-id", "version"}, + Blocked: []string{"block-id", "comment-id", "comment-key", "dentry-id", "dingdrive-space-id", "drive-space-id", "folder", "folder-id", "id", "job-id", "name", "parent-id", "revision", "role", "storage-space-id", "task-id", "template-id", "version"}, }, { CLIPath: "doc +background-delete", Aliases: map[string]string{ + "dentry-uuid": "node", "doc": "node", "doc-id": "node", "document-id": "node", @@ -1935,11 +1952,12 @@ var generatedParamAliases = []ParamAliasEntry{ "node-id": "node", "url": "node", }, - Blocked: []string{"block-id", "comment-id", "comment-key", "folder", "folder-id", "id", "job-id", "parent-id", "revision", "task-id", "template-id", "version", "workspace", "workspace-id"}, + Blocked: []string{"block-id", "comment-id", "comment-key", "dentry-id", "folder", "folder-id", "id", "job-id", "name", "parent-id", "revision", "role", "space-id", "task-id", "template-id", "version", "workspace", "workspace-id"}, }, { CLIPath: "doc +background-update", Aliases: map[string]string{ + "dentry-uuid": "node", "doc": "node", "doc-id": "node", "document-id": "node", @@ -1947,12 +1965,13 @@ var generatedParamAliases = []ParamAliasEntry{ "node-id": "node", "url": "node", }, - Blocked: []string{"block-id", "comment-id", "comment-key", "folder", "folder-id", "id", "job-id", "parent-id", "revision", "task-id", "template-id", "version", "workspace", "workspace-id"}, + Blocked: []string{"block-id", "comment-id", "comment-key", "dentry-id", "folder", "folder-id", "id", "job-id", "name", "parent-id", "revision", "role", "space-id", "task-id", "template-id", "version", "workspace", "workspace-id"}, }, { CLIPath: "doc +checkpoint-update", Aliases: map[string]string{ "body": "content", + "dentry-uuid": "node", "doc": "node", "doc-id": "node", "document-id": "node", @@ -1961,12 +1980,13 @@ var generatedParamAliases = []ParamAliasEntry{ "text": "content", "url": "node", }, - Blocked: []string{"block-id", "comment-id", "comment-key", "folder", "folder-id", "id", "job-id", "name", "parent-id", "revision", "task-id", "template-id", "title", "version", "workspace", "workspace-id"}, + Blocked: []string{"block-id", "comment-id", "comment-key", "dentry-id", "folder", "folder-id", "id", "job-id", "name", "parent-id", "revision", "role", "space-id", "task-id", "template-id", "title", "version", "workspace", "workspace-id"}, }, { CLIPath: "doc +comment-create", Aliases: map[string]string{ "body": "content", + "dentry-uuid": "node", "doc": "node", "doc-id": "node", "document-id": "node", @@ -1975,12 +1995,13 @@ var generatedParamAliases = []ParamAliasEntry{ "text": "content", "url": "node", }, - Blocked: []string{"chat-id", "chat-ids", "comment-id", "comment-key", "conversation-id", "conversation-ids", "folder", "folder-id", "group-id", "group-ids", "id", "job-id", "mentioned-open-conversation-id", "mentioned-open-conversation-ids", "name", "open-conversation-id", "open-conversation-ids", "parent-id", "revision", "task-id", "template-id", "title", "version", "workspace", "workspace-id"}, + Blocked: []string{"chat-id", "chat-ids", "comment-id", "comment-key", "conversation-id", "conversation-ids", "dentry-id", "folder", "folder-id", "group-id", "group-ids", "id", "job-id", "mentioned-open-conversation-id", "mentioned-open-conversation-ids", "name", "open-conversation-id", "open-conversation-ids", "parent-id", "revision", "role", "space-id", "task-id", "template-id", "title", "version", "workspace", "workspace-id"}, }, { CLIPath: "doc +comment-delete", Aliases: map[string]string{ "comment-id": "comment-key", + "dentry-uuid": "node", "doc": "node", "doc-id": "node", "document-id": "node", @@ -1988,11 +2009,12 @@ var generatedParamAliases = []ParamAliasEntry{ "node-id": "node", "url": "node", }, - Blocked: []string{"block-id", "folder", "folder-id", "id", "job-id", "parent-id", "revision", "task-id", "template-id", "version", "workspace", "workspace-id"}, + Blocked: []string{"block-id", "dentry-id", "folder", "folder-id", "id", "job-id", "name", "parent-id", "revision", "role", "space-id", "task-id", "template-id", "version", "workspace", "workspace-id"}, }, { CLIPath: "doc +comment-list", Aliases: map[string]string{ + "dentry-uuid": "node", "doc": "node", "doc-id": "node", "document-id": "node", @@ -2010,13 +2032,14 @@ var generatedParamAliases = []ParamAliasEntry{ "top": "limit", "url": "node", }, - Blocked: []string{"block-id", "comment-id", "comment-key", "count", "folder", "folder-id", "id", "job-id", "offset", "page", "parent-id", "revision", "task-id", "template-id", "version", "workspace", "workspace-id"}, + Blocked: []string{"block-id", "comment-id", "comment-key", "count", "dentry-id", "folder", "folder-id", "id", "job-id", "name", "offset", "page", "parent-id", "revision", "role", "space-id", "task-id", "template-id", "version", "workspace", "workspace-id"}, }, { CLIPath: "doc +comment-reply", Aliases: map[string]string{ "body": "content", "comment-id": "comment-key", + "dentry-uuid": "node", "doc": "node", "doc-id": "node", "document-id": "node", @@ -2025,13 +2048,14 @@ var generatedParamAliases = []ParamAliasEntry{ "text": "content", "url": "node", }, - Blocked: []string{"block-id", "chat-id", "chat-ids", "conversation-id", "conversation-ids", "folder", "folder-id", "group-id", "group-ids", "id", "job-id", "mentioned-open-conversation-id", "mentioned-open-conversation-ids", "name", "open-conversation-id", "open-conversation-ids", "parent-id", "revision", "task-id", "template-id", "title", "version", "workspace", "workspace-id"}, + Blocked: []string{"block-id", "chat-id", "chat-ids", "conversation-id", "conversation-ids", "dentry-id", "folder", "folder-id", "group-id", "group-ids", "id", "job-id", "mentioned-open-conversation-id", "mentioned-open-conversation-ids", "name", "open-conversation-id", "open-conversation-ids", "parent-id", "revision", "role", "space-id", "task-id", "template-id", "title", "version", "workspace", "workspace-id"}, }, { CLIPath: "doc +comment-update", Aliases: map[string]string{ "body": "content", "comment-id": "comment-key", + "dentry-uuid": "node", "doc": "node", "doc-id": "node", "document-id": "node", @@ -2040,65 +2064,73 @@ var generatedParamAliases = []ParamAliasEntry{ "text": "content", "url": "node", }, - Blocked: []string{"block-id", "folder", "folder-id", "id", "job-id", "name", "parent-id", "revision", "task-id", "template-id", "title", "version", "workspace", "workspace-id"}, + Blocked: []string{"block-id", "dentry-id", "folder", "folder-id", "id", "job-id", "name", "parent-id", "revision", "role", "space-id", "task-id", "template-id", "title", "version", "workspace", "workspace-id"}, }, { CLIPath: "doc +copy", Aliases: map[string]string{ - "doc": "node", - "doc-id": "node", - "document-id": "node", - "file-id": "node", - "folder-id": "folder", - "node-id": "node", - "parent-folder": "folder", - "parent-folder-id": "folder", - "parent-node-id": "folder", - "space": "workspace", - "space-id": "workspace", - "url": "node", - "workspace-id": "workspace", + "dentry-uuid": "node", + "doc": "node", + "doc-id": "node", + "document-id": "node", + "file-id": "node", + "folder-id": "folder", + "knowledge-base-id": "workspace", + "node-id": "node", + "parent-folder": "folder", + "parent-folder-id": "folder", + "parent-node-id": "folder", + "space": "workspace", + "space-id": "workspace", + "url": "node", + "wiki-workspace-id": "workspace", + "workspace-id": "workspace", }, - Blocked: []string{"block-id", "comment-id", "comment-key", "id", "job-id", "parent-id", "revision", "task-id", "template-id", "version"}, + Blocked: []string{"block-id", "comment-id", "comment-key", "dentry-id", "dingdrive-space-id", "drive-space-id", "id", "job-id", "name", "parent-id", "revision", "role", "storage-space-id", "task-id", "template-id", "version"}, }, { CLIPath: "doc +create", Aliases: map[string]string{ - "body": "content", - "content-format": "doc-format", - "folder-id": "folder", - "parent-folder": "folder", - "parent-folder-id": "folder", - "parent-node-id": "folder", - "space": "workspace", - "space-id": "workspace", - "text": "content", - "workspace-id": "workspace", + "body": "content", + "content-format": "doc-format", + "folder-id": "folder", + "knowledge-base-id": "workspace", + "parent-folder": "folder", + "parent-folder-id": "folder", + "parent-node-id": "folder", + "space": "workspace", + "space-id": "workspace", + "text": "content", + "wiki-workspace-id": "workspace", + "workspace-id": "workspace", }, - Blocked: []string{"content-file", "export-format", "mime-type", "node", "parent-id", "title"}, + Blocked: []string{"content-file", "dentry-id", "dingdrive-space-id", "drive-space-id", "export-format", "mime-type", "node", "node-id", "parent-id", "storage-space-id", "title"}, }, { CLIPath: "doc +create-from-template", Aliases: map[string]string{ - "folder-id": "folder", - "keyword": "query", - "keywords": "query", - "parent-folder": "folder", - "parent-folder-id": "folder", - "parent-node-id": "folder", - "q": "query", - "search-word": "query", - "space": "workspace", - "space-id": "workspace", - "workspace-id": "workspace", + "folder-id": "folder", + "keyword": "query", + "keywords": "query", + "knowledge-base-id": "workspace", + "parent-folder": "folder", + "parent-folder-id": "folder", + "parent-node-id": "folder", + "q": "query", + "search-word": "query", + "space": "workspace", + "space-id": "workspace", + "wiki-workspace-id": "workspace", + "workspace-id": "workspace", }, - Blocked: []string{"node", "parent-id", "subject", "text", "title"}, + Blocked: []string{"dentry-id", "dingdrive-space-id", "drive-space-id", "node", "node-id", "parent-id", "storage-space-id", "subject", "text", "title"}, }, { CLIPath: "doc +doc-append", Aliases: map[string]string{ "body": "text", "content": "text", + "dentry-uuid": "doc", "doc-id": "doc", "document-id": "doc", "file-id": "doc", @@ -2106,30 +2138,38 @@ var generatedParamAliases = []ParamAliasEntry{ "node-id": "doc", "url": "doc", }, - Blocked: []string{"block-id", "comment-id", "comment-key", "folder", "folder-id", "id", "job-id", "name", "parent-id", "revision", "task-id", "template-id", "title", "version", "workspace", "workspace-id"}, + Blocked: []string{"block-id", "comment-id", "comment-key", "dentry-id", "folder", "folder-id", "id", "job-id", "name", "parent-id", "revision", "role", "space-id", "task-id", "template-id", "title", "version", "workspace", "workspace-id"}, }, { CLIPath: "doc +export", Aliases: map[string]string{ - "doc": "node", - "doc-id": "node", - "document-id": "node", - "file-id": "node", - "node-id": "node", - "url": "node", + "dentry-uuid": "node", + "destination-path": "output", + "doc": "node", + "doc-id": "node", + "document-id": "node", + "file-id": "node", + "node-id": "node", + "output-path": "output", + "save-path": "output", + "url": "node", }, - Blocked: []string{"block-id", "comment-id", "comment-key", "folder", "folder-id", "id", "job-id", "parent-id", "revision", "task-id", "template-id", "version", "workspace", "workspace-id"}, + Blocked: []string{"block-id", "comment-id", "comment-key", "content-file", "dentry-id", "file", "file-path", "folder", "folder-id", "id", "job-id", "name", "parent-id", "revision", "role", "space-id", "task-id", "template-id", "version", "wait", "workspace", "workspace-id"}, }, { CLIPath: "doc +export-get", Aliases: map[string]string{ - "export-job-id": "job-id", + "destination-path": "output", + "export-job-id": "job-id", + "output-path": "output", + "save-path": "output", }, - Blocked: []string{"doc-id", "document-id", "file-id", "node", "node-id", "task-id", "url"}, + Blocked: []string{"content-file", "doc-id", "document-id", "file", "file-id", "file-path", "folder", "folder-id", "node", "node-id", "task-id", "url"}, }, { CLIPath: "doc +export-submit", Aliases: map[string]string{ + "dentry-uuid": "node", "doc": "node", "doc-id": "node", "document-id": "node", @@ -2137,11 +2177,12 @@ var generatedParamAliases = []ParamAliasEntry{ "node-id": "node", "url": "node", }, - Blocked: []string{"block-id", "comment-id", "comment-key", "folder", "folder-id", "id", "job-id", "parent-id", "revision", "task-id", "template-id", "version", "workspace", "workspace-id"}, + Blocked: []string{"block-id", "comment-id", "comment-key", "dentry-id", "folder", "folder-id", "id", "job-id", "name", "parent-id", "revision", "role", "space-id", "task-id", "template-id", "version", "workspace", "workspace-id"}, }, { CLIPath: "doc +fetch", Aliases: map[string]string{ + "dentry-uuid": "node", "doc": "node", "doc-id": "node", "document-id": "node", @@ -2151,7 +2192,7 @@ var generatedParamAliases = []ParamAliasEntry{ "start-block": "start-block-id", "url": "node", }, - Blocked: []string{"comment-id", "comment-key", "folder", "folder-id", "id", "job-id", "parent-id", "task-id", "template-id", "version", "workspace", "workspace-id"}, + Blocked: []string{"comment-id", "comment-key", "content-format", "dentry-id", "doc-format", "folder", "folder-id", "id", "job-id", "name", "parent-id", "range", "role", "space-id", "task-id", "template-id", "version", "workspace", "workspace-id"}, Ambiguous: []string{"block-id"}, }, { @@ -2174,20 +2215,26 @@ var generatedParamAliases = []ParamAliasEntry{ { CLIPath: "doc +grant-and-share", Aliases: map[string]string{ - "doc": "node", - "doc-id": "node", - "document-id": "node", - "file-id": "node", - "node-id": "node", - "space": "workspace", - "space-id": "workspace", - "workspace-id": "workspace", + "access-role": "role", + "doc": "node", + "doc-id": "node", + "document-id": "node", + "file-id": "node", + "knowledge-base-id": "workspace", + "member-role": "role", + "node-id": "node", + "permission-role": "role", + "space": "workspace", + "space-id": "workspace", + "wiki-workspace-id": "workspace", + "workspace-id": "workspace", }, - Blocked: []string{"folder"}, + Blocked: []string{"dentry-id", "dingdrive-space-id", "drive-space-id", "filter-role", "folder", "folder-id", "permission", "public-permission", "reserve-role", "storage-space-id"}, }, { CLIPath: "doc +history-list", Aliases: map[string]string{ + "dentry-uuid": "node", "doc": "node", "doc-id": "node", "document-id": "node", @@ -2195,11 +2242,12 @@ var generatedParamAliases = []ParamAliasEntry{ "node-id": "node", "url": "node", }, - Blocked: []string{"block-id", "comment-id", "comment-key", "folder", "folder-id", "id", "job-id", "parent-id", "revision", "task-id", "template-id", "version", "workspace", "workspace-id"}, + Blocked: []string{"block-id", "comment-id", "comment-key", "dentry-id", "folder", "folder-id", "id", "job-id", "name", "parent-id", "revision", "role", "space-id", "task-id", "template-id", "version", "workspace", "workspace-id"}, }, { CLIPath: "doc +history-revert", Aliases: map[string]string{ + "dentry-uuid": "node", "doc": "node", "doc-id": "node", "document-id": "node", @@ -2209,11 +2257,12 @@ var generatedParamAliases = []ParamAliasEntry{ "version-no": "version", "version-number": "version", }, - Blocked: []string{"block-id", "comment-id", "comment-key", "folder", "folder-id", "id", "job-id", "parent-id", "revision", "task-id", "template-id", "workspace", "workspace-id"}, + Blocked: []string{"block-id", "comment-id", "comment-key", "dentry-id", "folder", "folder-id", "id", "job-id", "name", "parent-id", "revision", "role", "space-id", "task-id", "template-id", "workspace", "workspace-id"}, }, { CLIPath: "doc +history-save", Aliases: map[string]string{ + "dentry-uuid": "node", "doc": "node", "doc-id": "node", "document-id": "node", @@ -2221,66 +2270,78 @@ var generatedParamAliases = []ParamAliasEntry{ "node-id": "node", "url": "node", }, - Blocked: []string{"block-id", "comment-id", "comment-key", "folder", "folder-id", "id", "job-id", "parent-id", "revision", "task-id", "template-id", "version", "workspace", "workspace-id"}, + Blocked: []string{"block-id", "comment-id", "comment-key", "dentry-id", "folder", "folder-id", "id", "job-id", "name", "parent-id", "revision", "role", "space-id", "task-id", "template-id", "version", "workspace", "workspace-id"}, }, { CLIPath: "doc +import", Aliases: map[string]string{ - "folder-id": "folder", - "parent-folder": "folder", - "parent-folder-id": "folder", - "parent-node-id": "folder", - "space": "workspace", - "space-id": "workspace", - "workspace-id": "workspace", + "folder-id": "folder", + "knowledge-base-id": "workspace", + "parent-folder": "folder", + "parent-folder-id": "folder", + "parent-node-id": "folder", + "space": "workspace", + "space-id": "workspace", + "wiki-workspace-id": "workspace", + "workspace-id": "workspace", }, - Blocked: []string{"node", "parent-id"}, + Blocked: []string{"dentry-id", "dingdrive-space-id", "drive-space-id", "node", "node-id", "parent-id", "storage-space-id"}, }, { CLIPath: "doc +inspect", Aliases: map[string]string{ + "dentry-uuid": "node", "doc": "node", "doc-id": "node", "document-id": "node", "file-id": "node", + "include-access": "include-permissions", + "include-member": "include-permissions", + "include-members": "include-permissions", "include-versions": "include-history", "node-id": "node", "url": "node", }, - Blocked: []string{"block-id", "comment-id", "comment-key", "folder", "folder-id", "id", "include", "include-info", "job-id", "parent-id", "revision", "task-id", "template-id", "version", "workspace", "workspace-id"}, + Blocked: []string{"block-id", "comment-id", "comment-key", "dentry-id", "folder", "folder-id", "id", "include", "include-blocks", "include-content", "include-info", "include-meta", "include-metadata", "job-id", "name", "parent-id", "revision", "role", "space-id", "task-id", "template-id", "version", "workspace", "workspace-id"}, }, { CLIPath: "doc +list", Aliases: map[string]string{ - "folder-id": "folder", - "max-result": "limit", - "max-results": "limit", - "next-cursor": "cursor", - "next-page-token": "cursor", - "next-token": "cursor", - "page-size": "limit", - "page-token": "cursor", - "parent-folder": "folder", - "parent-folder-id": "folder", - "parent-node-id": "folder", - "per-page": "limit", - "size": "limit", - "space": "workspace", - "space-id": "workspace", - "take": "limit", - "top": "limit", - "workspace-id": "workspace", + "folder-id": "folder", + "knowledge-base-id": "workspace", + "max-result": "limit", + "max-results": "limit", + "next-cursor": "cursor", + "next-page-token": "cursor", + "next-token": "cursor", + "page-size": "limit", + "page-token": "cursor", + "parent-folder": "folder", + "parent-folder-id": "folder", + "parent-node-id": "folder", + "per-page": "limit", + "size": "limit", + "space": "workspace", + "space-id": "workspace", + "take": "limit", + "top": "limit", + "wiki-workspace-id": "workspace", + "workspace-id": "workspace", }, - Blocked: []string{"count", "node", "offset", "page", "parent-id"}, + Blocked: []string{"count", "dentry-id", "dingdrive-space-id", "drive-space-id", "node", "node-id", "offset", "page", "parent-id", "storage-space-id"}, }, { CLIPath: "doc +media-download", Aliases: map[string]string{ - "doc": "node", - "doc-id": "node", - "document-id": "node", - "node-id": "node", + "destination-path": "output", + "doc": "node", + "doc-id": "node", + "document-id": "node", + "node-id": "node", + "output-path": "output", + "save-path": "output", }, + Blocked: []string{"content-file", "file", "file-path", "folder", "folder-id"}, Ambiguous: []string{"file-id", "url"}, }, { @@ -2291,6 +2352,7 @@ var generatedParamAliases = []ParamAliasEntry{ "document-id": "node", "node-id": "node", }, + Blocked: []string{"after-block-id", "before-block-id"}, Ambiguous: []string{"file-id", "url"}, }, { @@ -2316,21 +2378,24 @@ var generatedParamAliases = []ParamAliasEntry{ { CLIPath: "doc +move", Aliases: map[string]string{ - "doc": "node", - "doc-id": "node", - "document-id": "node", - "file-id": "node", - "folder-id": "folder", - "node-id": "node", - "parent-folder": "folder", - "parent-folder-id": "folder", - "parent-node-id": "folder", - "space": "workspace", - "space-id": "workspace", - "url": "node", - "workspace-id": "workspace", + "dentry-uuid": "node", + "doc": "node", + "doc-id": "node", + "document-id": "node", + "file-id": "node", + "folder-id": "folder", + "knowledge-base-id": "workspace", + "node-id": "node", + "parent-folder": "folder", + "parent-folder-id": "folder", + "parent-node-id": "folder", + "space": "workspace", + "space-id": "workspace", + "url": "node", + "wiki-workspace-id": "workspace", + "workspace-id": "workspace", }, - Blocked: []string{"block-id", "comment-id", "comment-key", "id", "job-id", "parent-id", "revision", "task-id", "template-id", "version"}, + Blocked: []string{"block-id", "comment-id", "comment-key", "dentry-id", "dingdrive-space-id", "drive-space-id", "id", "job-id", "name", "parent-id", "revision", "role", "storage-space-id", "task-id", "template-id", "version"}, }, { CLIPath: "doc +resource-delete", @@ -2345,11 +2410,15 @@ var generatedParamAliases = []ParamAliasEntry{ { CLIPath: "doc +resource-download", Aliases: map[string]string{ - "doc": "node", - "doc-id": "node", - "document-id": "node", - "node-id": "node", + "destination-path": "output", + "doc": "node", + "doc-id": "node", + "document-id": "node", + "node-id": "node", + "output-path": "output", + "save-path": "output", }, + Blocked: []string{"content-file", "file", "file-path", "folder", "folder-id"}, Ambiguous: []string{"file-id", "url"}, }, { @@ -2365,6 +2434,7 @@ var generatedParamAliases = []ParamAliasEntry{ { CLIPath: "doc +review", Aliases: map[string]string{ + "dentry-uuid": "node", "doc": "node", "doc-id": "node", "document-id": "node", @@ -2372,28 +2442,39 @@ var generatedParamAliases = []ParamAliasEntry{ "node-id": "node", "url": "node", }, - Blocked: []string{"block-id", "comment-id", "comment-key", "folder", "folder-id", "id", "job-id", "parent-id", "revision", "task-id", "template-id", "version", "workspace", "workspace-id"}, + Blocked: []string{"block-id", "comment-id", "comment-key", "dentry-id", "folder", "folder-id", "id", "job-id", "name", "parent-id", "revision", "role", "space-id", "task-id", "template-id", "version", "workspace", "workspace-id"}, }, { CLIPath: "doc +search", Aliases: map[string]string{ - "keyword": "query", - "keywords": "query", - "max-result": "limit", - "max-results": "limit", - "next-cursor": "cursor", - "next-page-token": "cursor", - "next-token": "cursor", - "page-size": "limit", - "page-token": "cursor", - "per-page": "limit", - "q": "query", - "search-word": "query", - "size": "limit", - "take": "limit", - "top": "limit", + "create-time-end": "created-to", + "create-time-from": "created-from", + "create-time-start": "created-from", + "create-time-to": "created-to", + "created-after": "created-from", + "created-before": "created-to", + "created-by-user-ids": "creator-uids", + "created-time-end": "created-to", + "created-time-start": "created-from", + "creator-ids": "creator-uids", + "creator-user-ids": "creator-uids", + "keyword": "query", + "keywords": "query", + "max-result": "limit", + "max-results": "limit", + "next-cursor": "cursor", + "next-page-token": "cursor", + "next-token": "cursor", + "page-size": "limit", + "page-token": "cursor", + "per-page": "limit", + "q": "query", + "search-word": "query", + "size": "limit", + "take": "limit", + "top": "limit", }, - Blocked: []string{"count", "name", "offset", "page", "subject", "text", "title"}, + Blocked: []string{"count", "end", "from", "modified-from", "modified-to", "modifier-uids", "name", "offset", "owner-id", "page", "start", "subject", "text", "title", "to", "user", "user-id", "user-ids", "users"}, }, { CLIPath: "doc +share", @@ -2453,11 +2534,13 @@ var generatedParamAliases = []ParamAliasEntry{ "revision": "expected-revision", "url": "node", }, - Blocked: []string{"content-file", "export-format", "mime-type", "version", "version-no", "version-number"}, + Blocked: []string{"content-file", "export-format", "mime-type", "version", "version-no", "version-number"}, + Ambiguous: []string{"element"}, }, { CLIPath: "doc +version-list", Aliases: map[string]string{ + "dentry-uuid": "node", "doc": "node", "doc-id": "node", "document-id": "node", @@ -2465,12 +2548,13 @@ var generatedParamAliases = []ParamAliasEntry{ "node-id": "node", "url": "node", }, - Blocked: []string{"block-id", "comment-id", "comment-key", "folder", "folder-id", "id", "job-id", "parent-id", "revision", "task-id", "template-id", "version", "workspace", "workspace-id"}, + Blocked: []string{"block-id", "comment-id", "comment-key", "dentry-id", "folder", "folder-id", "id", "job-id", "name", "parent-id", "revision", "role", "space-id", "task-id", "template-id", "version", "workspace", "workspace-id"}, Ambiguous: []string{"max-result", "max-results", "next-cursor", "next-page-token", "next-token", "per-page", "size", "take", "top"}, }, { CLIPath: "doc +version-revert", Aliases: map[string]string{ + "dentry-uuid": "node", "doc": "node", "doc-id": "node", "document-id": "node", @@ -2480,11 +2564,12 @@ var generatedParamAliases = []ParamAliasEntry{ "version-no": "version", "version-number": "version", }, - Blocked: []string{"block-id", "comment-id", "comment-key", "folder", "folder-id", "id", "job-id", "parent-id", "revision", "task-id", "template-id", "workspace", "workspace-id"}, + Blocked: []string{"block-id", "comment-id", "comment-key", "dentry-id", "folder", "folder-id", "id", "job-id", "name", "parent-id", "revision", "role", "space-id", "task-id", "template-id", "workspace", "workspace-id"}, }, { CLIPath: "doc +version-save", Aliases: map[string]string{ + "dentry-uuid": "node", "doc": "node", "doc-id": "node", "document-id": "node", @@ -2492,52 +2577,57 @@ var generatedParamAliases = []ParamAliasEntry{ "node-id": "node", "url": "node", }, - Blocked: []string{"block-id", "comment-id", "comment-key", "folder", "folder-id", "id", "job-id", "parent-id", "revision", "task-id", "template-id", "version", "workspace", "workspace-id"}, + Blocked: []string{"block-id", "comment-id", "comment-key", "dentry-id", "folder", "folder-id", "id", "job-id", "message", "name", "parent-id", "revision", "role", "space-id", "task-id", "template-id", "title", "version", "workspace", "workspace-id"}, }, { CLIPath: "doc block delete", Aliases: map[string]string{ + "dentry-uuid": "node", "doc": "node", "document-id": "node", }, - Blocked: []string{"comment-id", "comment-key", "folder", "folder-id", "index", "job-id", "parent-id", "revision", "task-id", "template-id", "version", "workspace", "workspace-id"}, + Blocked: []string{"comment-id", "comment-key", "dentry-id", "folder", "folder-id", "index", "job-id", "name", "parent-id", "revision", "role", "space-id", "task-id", "template-id", "version", "workspace", "workspace-id"}, }, { CLIPath: "doc block insert", Aliases: map[string]string{ "body": "text", "content": "text", + "dentry-uuid": "node", "doc": "node", "document-id": "node", "parent-block-id": "parent-block", "ref-block-id": "ref-block", "reference-block-id": "ref-block", }, - Blocked: []string{"before-block-id", "comment-id", "comment-key", "folder", "folder-id", "job-id", "name", "parent-id", "revision", "task-id", "template-id", "title", "version", "workspace", "workspace-id"}, + Blocked: []string{"before-block-id", "comment-id", "comment-key", "dentry-id", "folder", "folder-id", "job-id", "name", "parent-id", "revision", "role", "space-id", "task-id", "template-id", "title", "version", "workspace", "workspace-id"}, Ambiguous: []string{"block-id"}, }, { CLIPath: "doc block list", Aliases: map[string]string{ + "dentry-uuid": "node", "doc": "node", "document-id": "node", }, - Blocked: []string{"comment-id", "comment-key", "folder", "folder-id", "job-id", "parent-id", "revision", "task-id", "template-id", "version", "workspace", "workspace-id"}, + Blocked: []string{"comment-id", "comment-key", "dentry-id", "folder", "folder-id", "job-id", "name", "parent-id", "revision", "role", "space-id", "task-id", "template-id", "version", "workspace", "workspace-id"}, }, { CLIPath: "doc block update", Aliases: map[string]string{ "body": "text", "content": "text", + "dentry-uuid": "node", "doc": "node", "document-id": "node", }, - Blocked: []string{"comment-id", "comment-key", "folder", "folder-id", "job-id", "name", "parent-id", "revision", "task-id", "template-id", "title", "version", "workspace", "workspace-id"}, + Blocked: []string{"comment-id", "comment-key", "dentry-id", "folder", "folder-id", "job-id", "name", "parent-id", "revision", "role", "space-id", "task-id", "template-id", "title", "version", "workspace", "workspace-id"}, }, { CLIPath: "doc comment create", Aliases: map[string]string{ "body": "content", + "dentry-uuid": "node", "doc": "node", "document-id": "node", "mentioned-open-conversation-ids": "mentioned-open-conversation-id", @@ -2545,30 +2635,33 @@ var generatedParamAliases = []ParamAliasEntry{ "open-conversation-ids": "mentioned-open-conversation-id", "text": "content", }, - Blocked: []string{"block-id", "chat-id", "chat-ids", "comment-id", "comment-key", "conversation-id", "conversation-ids", "folder", "folder-id", "group-id", "group-ids", "job-id", "name", "parent-id", "revision", "task-id", "template-id", "title", "version", "workspace", "workspace-id"}, + Blocked: []string{"block-id", "chat-id", "chat-ids", "comment-id", "comment-key", "conversation-id", "conversation-ids", "dentry-id", "folder", "folder-id", "group-id", "group-ids", "job-id", "name", "parent-id", "revision", "role", "space-id", "task-id", "template-id", "title", "version", "workspace", "workspace-id"}, }, { CLIPath: "doc comment create-inline", Aliases: map[string]string{ "body": "content", + "dentry-uuid": "node", "doc": "node", "document-id": "node", "text": "content", }, - Blocked: []string{"comment-id", "comment-key", "folder", "folder-id", "job-id", "name", "parent-id", "revision", "task-id", "template-id", "title", "version", "workspace", "workspace-id"}, + Blocked: []string{"comment-id", "comment-key", "dentry-id", "folder", "folder-id", "job-id", "name", "parent-id", "revision", "role", "space-id", "task-id", "template-id", "title", "version", "workspace", "workspace-id"}, }, { CLIPath: "doc comment delete", Aliases: map[string]string{ "comment-id": "comment-key", + "dentry-uuid": "node", "doc": "node", "document-id": "node", }, - Blocked: []string{"block-id", "folder", "folder-id", "job-id", "parent-id", "revision", "task-id", "template-id", "version", "workspace", "workspace-id"}, + Blocked: []string{"block-id", "dentry-id", "folder", "folder-id", "job-id", "name", "parent-id", "revision", "role", "space-id", "task-id", "template-id", "version", "workspace", "workspace-id"}, }, { CLIPath: "doc comment list", Aliases: map[string]string{ + "dentry-uuid": "node", "doc": "node", "document-id": "node", "max-result": "limit", @@ -2580,13 +2673,14 @@ var generatedParamAliases = []ParamAliasEntry{ "take": "limit", "top": "limit", }, - Blocked: []string{"block-id", "comment-id", "comment-key", "count", "folder", "folder-id", "job-id", "offset", "page", "parent-id", "revision", "task-id", "template-id", "version", "workspace", "workspace-id"}, + Blocked: []string{"block-id", "comment-id", "comment-key", "count", "dentry-id", "folder", "folder-id", "job-id", "name", "offset", "page", "parent-id", "revision", "role", "space-id", "task-id", "template-id", "version", "workspace", "workspace-id"}, }, { CLIPath: "doc comment reply", Aliases: map[string]string{ "body": "content", "comment-id": "comment-key", + "dentry-uuid": "node", "doc": "node", "document-id": "node", "mentioned-open-conversation-ids": "mentioned-open-conversation-id", @@ -2594,13 +2688,14 @@ var generatedParamAliases = []ParamAliasEntry{ "open-conversation-ids": "mentioned-open-conversation-id", "text": "content", }, - Blocked: []string{"block-id", "chat-id", "chat-ids", "conversation-id", "conversation-ids", "folder", "folder-id", "group-id", "group-ids", "job-id", "name", "parent-id", "revision", "task-id", "template-id", "title", "version", "workspace", "workspace-id"}, + Blocked: []string{"block-id", "chat-id", "chat-ids", "conversation-id", "conversation-ids", "dentry-id", "folder", "folder-id", "group-id", "group-ids", "job-id", "name", "parent-id", "revision", "role", "space-id", "task-id", "template-id", "title", "version", "workspace", "workspace-id"}, }, { CLIPath: "doc comment update", Aliases: map[string]string{ "body": "content", "comment-id": "comment-key", + "dentry-uuid": "node", "doc": "node", "document-id": "node", "mentioned-open-conversation-ids": "mentioned-open-conversation-id", @@ -2608,26 +2703,29 @@ var generatedParamAliases = []ParamAliasEntry{ "open-conversation-ids": "mentioned-open-conversation-id", "text": "content", }, - Blocked: []string{"block-id", "chat-id", "chat-ids", "conversation-id", "conversation-ids", "folder", "folder-id", "group-id", "group-ids", "job-id", "name", "parent-id", "revision", "task-id", "template-id", "title", "version", "workspace", "workspace-id"}, + Blocked: []string{"block-id", "chat-id", "chat-ids", "conversation-id", "conversation-ids", "dentry-id", "folder", "folder-id", "group-id", "group-ids", "job-id", "name", "parent-id", "revision", "role", "space-id", "task-id", "template-id", "title", "version", "workspace", "workspace-id"}, }, { CLIPath: "doc create", Aliases: map[string]string{ - "body": "content", - "doc-format": "content-format", - "space": "workspace", - "space-id": "workspace", - "text": "content", + "body": "content", + "doc-format": "content-format", + "knowledge-base-id": "workspace", + "space": "workspace", + "space-id": "workspace", + "text": "content", + "wiki-workspace-id": "workspace", }, - Blocked: []string{"export-format", "mime-type", "node"}, + Blocked: []string{"dentry-id", "dingdrive-space-id", "drive-space-id", "export-format", "folder-id", "mime-type", "node", "node-id", "storage-space-id"}, }, { CLIPath: "doc export", Aliases: map[string]string{ + "dentry-uuid": "node", "doc": "node", "document-id": "node", }, - Blocked: []string{"block-id", "comment-id", "comment-key", "folder", "folder-id", "job-id", "parent-id", "revision", "task-id", "template-id", "version", "workspace", "workspace-id"}, + Blocked: []string{"block-id", "comment-id", "comment-key", "dentry-id", "folder", "folder-id", "job-id", "name", "parent-id", "revision", "role", "space-id", "task-id", "template-id", "version", "workspace", "workspace-id"}, }, { CLIPath: "doc export get", @@ -2639,18 +2737,22 @@ var generatedParamAliases = []ParamAliasEntry{ { CLIPath: "doc file create", Aliases: map[string]string{ - "space": "workspace", - "space-id": "workspace", + "knowledge-base-id": "workspace", + "space": "workspace", + "space-id": "workspace", + "wiki-workspace-id": "workspace", }, - Blocked: []string{"node"}, + Blocked: []string{"dentry-id", "dingdrive-space-id", "drive-space-id", "folder-id", "node", "node-id", "storage-space-id"}, }, { CLIPath: "doc import", Aliases: map[string]string{ - "space": "workspace", - "space-id": "workspace", + "knowledge-base-id": "workspace", + "space": "workspace", + "space-id": "workspace", + "wiki-workspace-id": "workspace", }, - Blocked: []string{"node"}, + Blocked: []string{"dentry-id", "dingdrive-space-id", "drive-space-id", "node", "node-id", "storage-space-id"}, }, { CLIPath: "doc import get", @@ -2662,94 +2764,109 @@ var generatedParamAliases = []ParamAliasEntry{ { CLIPath: "doc info", Aliases: map[string]string{ + "dentry-uuid": "node", "doc": "node", "document-id": "node", }, - Blocked: []string{"block-id", "comment-id", "comment-key", "folder", "folder-id", "job-id", "parent-id", "revision", "task-id", "template-id", "version", "workspace", "workspace-id"}, + Blocked: []string{"block-id", "comment-id", "comment-key", "dentry-id", "folder", "folder-id", "job-id", "name", "parent-id", "revision", "role", "space-id", "task-id", "template-id", "version", "workspace", "workspace-id"}, }, { CLIPath: "doc media download", Aliases: map[string]string{ + "dentry-uuid": "node", "doc": "node", "document-id": "node", }, - Blocked: []string{"block-id", "comment-id", "comment-key", "folder", "folder-id", "job-id", "parent-id", "revision", "task-id", "template-id", "version", "workspace", "workspace-id"}, + Blocked: []string{"block-id", "comment-id", "comment-key", "dentry-id", "folder", "folder-id", "job-id", "name", "parent-id", "revision", "role", "space-id", "task-id", "template-id", "version", "workspace", "workspace-id"}, }, { CLIPath: "doc media insert", Aliases: map[string]string{ + "dentry-uuid": "node", "doc": "node", "document-id": "node", "ref-block-id": "ref-block", "reference-block-id": "ref-block", }, - Blocked: []string{"before-block-id", "comment-id", "comment-key", "folder", "folder-id", "job-id", "parent-block", "parent-block-id", "parent-id", "revision", "task-id", "template-id", "version", "workspace", "workspace-id"}, + Blocked: []string{"before-block-id", "comment-id", "comment-key", "dentry-id", "folder", "folder-id", "job-id", "parent-block", "parent-block-id", "parent-id", "revision", "role", "space-id", "task-id", "template-id", "version", "workspace", "workspace-id"}, Ambiguous: []string{"block-id"}, }, { CLIPath: "doc media upload", Aliases: map[string]string{ + "dentry-uuid": "node", "doc": "node", "document-id": "node", }, - Blocked: []string{"block-id", "comment-id", "comment-key", "folder", "folder-id", "job-id", "parent-id", "revision", "task-id", "template-id", "version", "workspace", "workspace-id"}, + Blocked: []string{"block-id", "comment-id", "comment-key", "dentry-id", "folder", "folder-id", "job-id", "parent-id", "revision", "role", "space-id", "task-id", "template-id", "version", "workspace", "workspace-id"}, }, { CLIPath: "doc read", Aliases: map[string]string{ - "doc": "node", - "document-id": "node", + "dentry-uuid": "node", + "destination-path": "output", + "doc": "node", + "document-id": "node", + "output-path": "output", + "save-path": "output", }, - Blocked: []string{"before-block-id", "comment-id", "comment-key", "folder", "folder-id", "job-id", "parent-block-id", "parent-id", "ref-block-id", "reference-block-id", "revision", "task-id", "template-id", "version", "workspace", "workspace-id"}, + Blocked: []string{"before-block-id", "comment-id", "comment-key", "content-file", "dentry-id", "file", "file-path", "folder", "folder-id", "job-id", "name", "parent-block-id", "parent-id", "ref-block-id", "reference-block-id", "revision", "role", "space-id", "task-id", "template-id", "version", "workspace", "workspace-id"}, Ambiguous: []string{"block-id"}, }, { CLIPath: "doc style background clear", Aliases: map[string]string{ + "dentry-uuid": "node", "doc": "node", "document-id": "node", }, - Blocked: []string{"block-id", "comment-id", "comment-key", "folder", "folder-id", "job-id", "parent-id", "revision", "task-id", "template-id", "version", "workspace", "workspace-id"}, + Blocked: []string{"block-id", "comment-id", "comment-key", "dentry-id", "folder", "folder-id", "job-id", "name", "parent-id", "revision", "role", "space-id", "task-id", "template-id", "version", "workspace", "workspace-id"}, }, { CLIPath: "doc style background set", Aliases: map[string]string{ + "dentry-uuid": "node", "doc": "node", "document-id": "node", }, - Blocked: []string{"block-id", "comment-id", "comment-key", "folder", "folder-id", "job-id", "parent-id", "revision", "task-id", "template-id", "version", "workspace", "workspace-id"}, + Blocked: []string{"block-id", "comment-id", "comment-key", "dentry-id", "folder", "folder-id", "job-id", "name", "parent-id", "revision", "role", "space-id", "task-id", "template-id", "version", "workspace", "workspace-id"}, }, { CLIPath: "doc style cover clear", Aliases: map[string]string{ + "dentry-uuid": "node", "doc": "node", "document-id": "node", }, - Blocked: []string{"block-id", "comment-id", "comment-key", "folder", "folder-id", "job-id", "parent-id", "revision", "task-id", "template-id", "version", "workspace", "workspace-id"}, + Blocked: []string{"block-id", "comment-id", "comment-key", "dentry-id", "folder", "folder-id", "job-id", "name", "parent-id", "revision", "role", "space-id", "task-id", "template-id", "version", "workspace", "workspace-id"}, }, { CLIPath: "doc style cover set", Aliases: map[string]string{ + "dentry-uuid": "node", "doc": "node", "document-id": "node", }, - Blocked: []string{"block-id", "comment-id", "comment-key", "folder", "folder-id", "job-id", "parent-id", "revision", "task-id", "template-id", "version", "workspace", "workspace-id"}, + Blocked: []string{"block-id", "comment-id", "comment-key", "dentry-id", "folder", "folder-id", "job-id", "name", "parent-id", "revision", "role", "space-id", "task-id", "template-id", "version", "workspace", "workspace-id"}, }, { CLIPath: "doc style get", Aliases: map[string]string{ + "dentry-uuid": "node", "doc": "node", "document-id": "node", }, - Blocked: []string{"block-id", "comment-id", "comment-key", "folder", "folder-id", "job-id", "parent-id", "revision", "task-id", "template-id", "version", "workspace", "workspace-id"}, + Blocked: []string{"block-id", "comment-id", "comment-key", "dentry-id", "folder", "folder-id", "job-id", "name", "parent-id", "revision", "role", "space-id", "task-id", "template-id", "version", "workspace", "workspace-id"}, }, { CLIPath: "doc template apply", Aliases: map[string]string{ - "space": "workspace", - "space-id": "workspace", + "knowledge-base-id": "workspace", + "space": "workspace", + "space-id": "workspace", + "wiki-workspace-id": "workspace", }, - Blocked: []string{"node"}, + Blocked: []string{"dentry-id", "dingdrive-space-id", "drive-space-id", "folder-id", "node", "node-id", "storage-space-id"}, }, { CLIPath: "doc template list", @@ -2785,16 +2902,18 @@ var generatedParamAliases = []ParamAliasEntry{ { CLIPath: "doc update", Aliases: map[string]string{ + "dentry-uuid": "node", "doc": "node", "doc-format": "content-format", "document-id": "node", "expected-revision": "revision", }, - Blocked: []string{"block-id", "comment-id", "comment-key", "export-format", "folder", "folder-id", "job-id", "mime-type", "parent-id", "task-id", "template-id", "version", "version-no", "version-number", "workspace", "workspace-id"}, + Blocked: []string{"block-id", "comment-id", "comment-key", "dentry-id", "export-format", "folder", "folder-id", "job-id", "mime-type", "name", "parent-id", "role", "space-id", "stdin", "task-id", "template-id", "version", "version-no", "version-number", "workspace", "workspace-id"}, }, { CLIPath: "doc version list", Aliases: map[string]string{ + "dentry-uuid": "node", "doc": "node", "document-id": "node", "max-result": "limit", @@ -2806,53 +2925,1036 @@ var generatedParamAliases = []ParamAliasEntry{ "take": "limit", "top": "limit", }, - Blocked: []string{"block-id", "comment-id", "comment-key", "count", "folder", "folder-id", "job-id", "offset", "page", "parent-id", "revision", "task-id", "template-id", "version", "workspace", "workspace-id"}, + Blocked: []string{"block-id", "comment-id", "comment-key", "count", "dentry-id", "folder", "folder-id", "job-id", "name", "offset", "page", "parent-id", "revision", "role", "space-id", "task-id", "template-id", "version", "workspace", "workspace-id"}, }, { CLIPath: "doc version revert", Aliases: map[string]string{ + "dentry-uuid": "node", "doc": "node", "document-id": "node", "version-no": "version", "version-number": "version", }, - Blocked: []string{"block-id", "comment-id", "comment-key", "folder", "folder-id", "job-id", "parent-id", "revision", "task-id", "template-id", "workspace", "workspace-id"}, + Blocked: []string{"block-id", "comment-id", "comment-key", "dentry-id", "folder", "folder-id", "job-id", "name", "parent-id", "revision", "role", "space-id", "task-id", "template-id", "workspace", "workspace-id"}, }, { CLIPath: "doc version save", Aliases: map[string]string{ + "dentry-uuid": "node", "doc": "node", "document-id": "node", }, - Blocked: []string{"block-id", "comment-id", "comment-key", "folder", "folder-id", "job-id", "parent-id", "revision", "task-id", "template-id", "version", "workspace", "workspace-id"}, + Blocked: []string{"block-id", "comment-id", "comment-key", "dentry-id", "folder", "folder-id", "job-id", "name", "parent-id", "revision", "role", "space-id", "task-id", "template-id", "version", "workspace", "workspace-id"}, }, { CLIPath: "doc whiteboard insert", Aliases: map[string]string{ + "dentry-uuid": "node", "doc": "node", "document-id": "node", }, - Blocked: []string{"block-id", "comment-id", "comment-key", "folder", "folder-id", "job-id", "parent-id", "revision", "task-id", "template-id", "version", "workspace", "workspace-id"}, + Blocked: []string{"block-id", "comment-id", "comment-key", "dentry-id", "folder", "folder-id", "job-id", "name", "parent-id", "revision", "role", "space-id", "task-id", "template-id", "version", "workspace", "workspace-id"}, + }, + { + CLIPath: "drive +copy", + Aliases: map[string]string{ + "dentry-uuid": "node", + "destination-folder-id": "folder", + "destination-workspace-id": "workspace", + "directory-id": "folder", + "folder-id": "folder", + "knowledge-base-id": "workspace", + "source-file-id": "node", + "source-node-id": "node", + "target-folder-id": "folder", + "target-workspace-id": "workspace", + "wiki-workspace-id": "workspace", + "workspace-id": "workspace", + }, + Blocked: []string{"dentry-id", "destination-node-id", "dingdrive-space-id", "document-url", "drive-space-id", "name", "node-id", "source-folder-id", "space", "space-id", "storage-space-id"}, + Ambiguous: []string{"destination-id", "target-id"}, + }, + { + CLIPath: "drive +cover", + Aliases: map[string]string{ + "dentry-uuid": "node", + "doc-id": "node", + "document-id": "node", + "document-url": "node", + "file-id": "node", + "id": "node", + "node-id": "node", + "url": "node", + }, + Blocked: []string{"dentry-id", "doc", "folder", "folder-id", "name"}, + }, + { + CLIPath: "drive +create-folder", + Aliases: map[string]string{ + "dingdrive-space-id": "space-id", + "directory-id": "folder", + "display-name": "name", + "drive-space-id": "space-id", + "folder-id": "folder", + "folder-name": "name", + "new-name": "name", + "parent-directory-id": "folder", + "parent-folder-id": "folder", + "storage-space-id": "space-id", + }, + Blocked: []string{"dentry-id", "knowledge-base-id", "parent-id", "space", "wiki-workspace-id", "workspace", "workspace-id"}, + }, + { + CLIPath: "drive +create-shortcut", + Aliases: map[string]string{ + "dentry-uuid": "node", + "destination-folder-id": "folder", + "destination-workspace-id": "workspace", + "directory-id": "folder", + "doc-id": "node", + "document-id": "node", + "document-url": "node", + "file-id": "node", + "folder-id": "folder", + "knowledge-base-id": "workspace", + "node-id": "node", + "source-file-id": "node", + "source-node-id": "node", + "target-folder-id": "folder", + "target-workspace-id": "workspace", + "url": "node", + "wiki-workspace-id": "workspace", + "workspace-id": "workspace", + }, + Blocked: []string{"dentry-id", "destination-node-id", "dingdrive-space-id", "doc", "drive-space-id", "name", "source-folder-id", "space", "space-id", "storage-space-id"}, + Ambiguous: []string{"destination-id", "id", "target-id"}, + }, + { + CLIPath: "drive +delete", + Aliases: map[string]string{ + "dentry-uuid": "node", + "file-id": "node", + "folder": "node", + "folder-id": "node", + "node-id": "node", + }, + Blocked: []string{"dentry-id", "doc", "doc-id", "document-id", "document-url", "id", "name", "url"}, + }, + { + CLIPath: "drive +download", + Aliases: map[string]string{ + "dentry-uuid": "node", + "destination-path": "output", + "dingdrive-space-id": "space-id", + "drive-space-id": "space-id", + "file-id": "node", + "node-id": "node", + "output-path": "output", + "save-path": "output", + "storage-space-id": "space-id", + }, + Blocked: []string{"content-file", "dentry-id", "doc", "doc-id", "document-id", "document-url", "file", "file-path", "folder", "folder-id", "id", "knowledge-base-id", "name", "space", "url", "wiki-workspace-id", "workspace", "workspace-id"}, + }, + { + CLIPath: "drive +find-file", + Aliases: map[string]string{ + "keyword": "query", + "keywords": "query", + "q": "query", + "search-word": "query", + }, + Blocked: []string{"name", "subject", "text", "title"}, + }, + { + CLIPath: "drive +info", + Aliases: map[string]string{ + "dentry-uuid": "node", + "dingdrive-space-id": "space-id", + "drive-space-id": "space-id", + "storage-space-id": "space-id", + }, + Blocked: []string{"dentry-id", "document-url", "knowledge-base-id", "space", "wiki-workspace-id", "workspace", "workspace-id"}, + }, + { + CLIPath: "drive +inspect", + Aliases: map[string]string{ + "dentry-uuid": "node", + "dingdrive-space-id": "space-id", + "doc-id": "node", + "document-id": "node", + "drive-space-id": "space-id", + "file-id": "node", + "folder": "node", + "folder-id": "node", + "include-public-status": "include-publish", + "include-publish-status": "include-publish", + "include-statistics": "include-stats", + "include-thumbnail": "include-cover", + "node-id": "node", + "storage-space-id": "space-id", + }, + Blocked: []string{"dentry-id", "doc", "document-url", "id", "include", "include-content", "include-history", "include-permissions", "knowledge-base-id", "name", "space", "url", "wiki-workspace-id", "workspace", "workspace-id"}, + }, + { + CLIPath: "drive +list", + Aliases: map[string]string{ + "dingdrive-space-id": "space-id", + "directory-id": "folder", + "drive-space-id": "space-id", + "folder-id": "folder", + "max-result": "limit", + "max-results": "limit", + "next-cursor": "cursor", + "next-page-token": "cursor", + "next-token": "cursor", + "order-direction": "order", + "order-field": "order-by", + "page-size": "limit", + "page-token": "cursor", + "parent-folder-id": "folder", + "per-page": "limit", + "size": "limit", + "sort": "order", + "sort-by": "order-by", + "sort-direction": "order", + "sort-field": "order-by", + "storage-space-id": "space-id", + "take": "limit", + "top": "limit", + }, + Blocked: []string{"count", "dentry-id", "knowledge-base-id", "offset", "page", "wiki-workspace-id", "workspace", "workspace-id"}, + Ambiguous: []string{"root-id", "space"}, + }, + { + CLIPath: "drive +move", + Aliases: map[string]string{ + "dentry-uuid": "node", + "destination-folder-id": "folder", + "destination-workspace-id": "workspace", + "directory-id": "folder", + "folder-id": "folder", + "knowledge-base-id": "workspace", + "source-file-id": "node", + "source-node-id": "node", + "target-folder-id": "folder", + "target-workspace-id": "workspace", + "wiki-workspace-id": "workspace", + "workspace-id": "workspace", + }, + Blocked: []string{"dentry-id", "destination-node-id", "dingdrive-space-id", "document-url", "drive-space-id", "name", "node-id", "source-folder-id", "space", "space-id", "storage-space-id"}, + Ambiguous: []string{"destination-id", "target-id"}, + }, + { + CLIPath: "drive +publish-get", + Aliases: map[string]string{ + "dentry-uuid": "node", + "doc-id": "node", + "document-id": "node", + "document-url": "node", + "file-id": "node", + "id": "node", + "node-id": "node", + "url": "node", + }, + Blocked: []string{"dentry-id", "doc", "folder", "folder-id", "name"}, + }, + { + CLIPath: "drive +publish-unset", + Aliases: map[string]string{ + "dentry-uuid": "node", + "doc-id": "node", + "document-id": "node", + "document-url": "node", + "file-id": "node", + "id": "node", + "node-id": "node", + "url": "node", + }, + Blocked: []string{"dentry-id", "doc", "folder", "folder-id", "name"}, + }, + { + CLIPath: "drive +recent", + Aliases: map[string]string{ + "max-result": "limit", + "max-results": "limit", + "next-cursor": "cursor", + "next-page-token": "cursor", + "next-token": "cursor", + "page-size": "limit", + "page-token": "cursor", + "per-page": "limit", + "size": "limit", + "take": "limit", + "top": "limit", + }, + Blocked: []string{"count", "offset", "page"}, + Ambiguous: []string{"type", "types"}, + }, + { + CLIPath: "drive +recycle-list", + Aliases: map[string]string{ + "dingdrive-space-id": "space-id", + "drive-space-id": "space-id", + "max-result": "limit", + "max-results": "limit", + "next-cursor": "cursor", + "next-page-token": "cursor", + "next-token": "cursor", + "page-size": "limit", + "page-token": "cursor", + "per-page": "limit", + "size": "limit", + "storage-space-id": "space-id", + "take": "limit", + "top": "limit", + }, + Blocked: []string{"count", "knowledge-base-id", "offset", "page", "space", "wiki-workspace-id", "workspace", "workspace-id"}, + }, + { + CLIPath: "drive +recycle-restore", + Aliases: map[string]string{ + "deleted-item-id": "id", + "recycle-item-id": "id", + "trash-item-id": "id", + }, + Blocked: []string{"file-id", "folder-id", "node", "node-id", "space-id", "workspace-id"}, + }, + { + CLIPath: "drive +rename", + Aliases: map[string]string{ + "dentry-uuid": "node", + "display-name": "name", + "doc-id": "node", + "document-id": "node", + "document-url": "node", + "file-id": "node", + "file-name": "name", + "folder": "node", + "folder-id": "node", + "id": "node", + "new-name": "name", + "node-id": "node", + "url": "node", + }, + Blocked: []string{"dentry-id"}, + }, + { + CLIPath: "drive +search", + Aliases: map[string]string{ + "create-time-end": "created-to", + "create-time-from": "created-from", + "create-time-start": "created-from", + "create-time-to": "created-to", + "created-after": "created-from", + "created-before": "created-to", + "created-by-user-ids": "creator-uids", + "created-time-end": "created-to", + "created-time-start": "created-from", + "creator-ids": "creator-uids", + "creator-user-ids": "creator-uids", + "keyword": "query", + "keywords": "query", + "max-result": "limit", + "max-results": "limit", + "modified-after": "modified-from", + "modified-before": "modified-to", + "modified-time-end": "modified-to", + "modified-time-start": "modified-from", + "modify-time-from": "modified-from", + "modify-time-to": "modified-to", + "next-cursor": "cursor", + "next-page-token": "cursor", + "next-token": "cursor", + "page-size": "limit", + "page-token": "cursor", + "per-page": "limit", + "q": "query", + "search-word": "query", + "size": "limit", + "take": "limit", + "top": "limit", + }, + Blocked: []string{"count", "modifier-uids", "name", "offset", "owner-id", "page", "subject", "text", "title", "user", "user-id", "user-ids", "users"}, + Ambiguous: []string{"end", "from", "start", "time-from", "time-to", "to", "types"}, + }, + { + CLIPath: "drive +search-docs", + Aliases: map[string]string{ + "keyword": "query", + "keywords": "query", + "max-result": "limit", + "max-results": "limit", + "page-size": "limit", + "per-page": "limit", + "q": "query", + "search-word": "query", + "size": "limit", + "take": "limit", + "top": "limit", + }, + Blocked: []string{"count", "cursor", "name", "page", "subject", "text", "title"}, + }, + { + CLIPath: "drive +star-add", + Aliases: map[string]string{ + "dentry-uuid": "node", + "doc-id": "node", + "document-id": "node", + "document-url": "node", + "file-id": "node", + "id": "node", + "node-id": "node", + "url": "node", + }, + Blocked: []string{"dentry-id", "doc", "folder", "folder-id", "name"}, + }, + { + CLIPath: "drive +star-list", + Aliases: map[string]string{ + "max-result": "limit", + "max-results": "limit", + "next-cursor": "cursor", + "next-page-token": "cursor", + "next-token": "cursor", + "page-size": "limit", + "page-token": "cursor", + "per-page": "limit", + "size": "limit", + "take": "limit", + "top": "limit", + }, + Blocked: []string{"count", "offset", "page"}, + Ambiguous: []string{"type", "types"}, + }, + { + CLIPath: "drive +star-remove", + Aliases: map[string]string{ + "dentry-uuid": "node", + "doc-id": "node", + "document-id": "node", + "document-url": "node", + "file-id": "node", + "id": "node", + "node-id": "node", + "url": "node", + }, + Blocked: []string{"dentry-id", "doc", "folder", "folder-id", "name"}, + }, + { + CLIPath: "drive +stats", + Aliases: map[string]string{ + "dentry-uuid": "node", + "doc-id": "node", + "document-id": "node", + "document-url": "node", + "file-id": "node", + "id": "node", + "node-id": "node", + "url": "node", + }, + Blocked: []string{"dentry-id", "doc", "folder", "folder-id", "name"}, + }, + { + CLIPath: "drive +upload", + Aliases: map[string]string{ + "content-type": "mime-type", + "dentry-uuid": "node", + "dingdrive-space-id": "space-id", + "directory-id": "folder", + "display-name": "file-name", + "drive-space-id": "space-id", + "file-id": "node", + "file-path": "file", + "filename": "file-name", + "folder-id": "folder", + "local-file": "file", + "name": "file-name", + "node-id": "node", + "overwrite-node-id": "node", + "parent-directory-id": "folder", + "parent-folder-id": "folder", + "source-file": "file", + "storage-space-id": "space-id", + "target-folder-id": "folder", + "upload-name": "file-name", + }, + Blocked: []string{"dentry-id", "document-url", "knowledge-base-id", "output-path", "wiki-workspace-id", "workspace", "workspace-id"}, + Ambiguous: []string{"destination-id", "space", "target-id"}, + }, + { + CLIPath: "drive +version-download", + Aliases: map[string]string{ + "dentry-uuid": "node", + "destination-path": "output", + "file-id": "node", + "node-id": "node", + "output-path": "output", + "save-path": "output", + "version-no": "version", + "version-number": "version", + }, + Blocked: []string{"content-file", "dentry-id", "doc", "doc-id", "document-id", "document-url", "file", "file-path", "folder", "folder-id", "id", "name", "revision", "url"}, + }, + { + CLIPath: "drive +version-get", + Aliases: map[string]string{ + "dentry-uuid": "node", + "file-id": "node", + "node-id": "node", + "version-no": "version", + "version-number": "version", + }, + Blocked: []string{"dentry-id", "doc", "doc-id", "document-id", "document-url", "folder", "folder-id", "id", "name", "revision", "url"}, + }, + { + CLIPath: "drive +version-history", + Aliases: map[string]string{ + "dentry-uuid": "node", + "file-id": "node", + "max-result": "limit", + "max-results": "limit", + "next-cursor": "cursor", + "next-page-token": "cursor", + "next-token": "cursor", + "node-id": "node", + "page-size": "limit", + "page-token": "cursor", + "per-page": "limit", + "size": "limit", + "take": "limit", + "top": "limit", + }, + Blocked: []string{"count", "dentry-id", "doc", "doc-id", "document-id", "document-url", "folder", "folder-id", "id", "name", "offset", "page", "url"}, + }, + { + CLIPath: "drive +version-revert", + Aliases: map[string]string{ + "dentry-uuid": "node", + "file-id": "node", + "node-id": "node", + "version-no": "version", + "version-number": "version", + }, + Blocked: []string{"dentry-id", "doc", "doc-id", "document-id", "document-url", "folder", "folder-id", "id", "name", "revision", "url"}, + }, + { + CLIPath: "drive commit", + Aliases: map[string]string{ + "content-length": "file-size", + "dingdrive-space-id": "space-id", + "directory-id": "folder", + "display-name": "file-name", + "drive-space-id": "space-id", + "file-size-bytes": "file-size", + "filename": "file-name", + "folder-id": "folder", + "name": "file-name", + "parent-directory-id": "folder", + "size-bytes": "file-size", + "storage-space-id": "space-id", + "upload-name": "file-name", + "upload-session-id": "upload-id", + }, + Blocked: []string{"knowledge-base-id", "limit", "page-size", "part-size", "size", "space", "wiki-workspace-id", "workspace", "workspace-id"}, + }, + { + CLIPath: "drive copy", + Aliases: map[string]string{ + "dentry-uuid": "node", + "destination-folder-id": "folder", + "destination-workspace-id": "workspace", + "directory-id": "folder", + "document-id": "node", + "folder-id": "folder", + "knowledge-base-id": "workspace", + "source-file-id": "node", + "source-node-id": "node", + "target-folder-id": "folder", + "target-workspace-id": "workspace", + "wiki-workspace-id": "workspace", + }, + Blocked: []string{"dentry-id", "destination-node-id", "dingdrive-space-id", "drive-space-id", "name", "source-folder-id", "space", "space-id", "storage-space-id"}, + Ambiguous: []string{"destination-id", "target-id"}, + }, + { + CLIPath: "drive cover", + Aliases: map[string]string{ + "dentry-uuid": "node", + "document-id": "node", + }, + Blocked: []string{"dentry-id"}, + }, + { + CLIPath: "drive delete", + Aliases: map[string]string{ + "dentry-uuid": "node", + }, + Blocked: []string{"dentry-id", "document-url"}, + }, + { + CLIPath: "drive download", + Aliases: map[string]string{ + "chunk-size": "part-size", + "concurrency": "parallel", + "dentry-uuid": "node", + "destination-path": "output", + "dingdrive-space-id": "space-id", + "drive-space-id": "space-id", + "output-path": "output", + "parallelism": "parallel", + "save-path": "output", + "storage-space-id": "space-id", + "version-no": "version", + "version-number": "version", + }, + Blocked: []string{"content-file", "dentry-id", "document-url", "file", "file-path", "folder", "folder-id", "id", "knowledge-base-id", "revision", "space", "wiki-workspace-id", "workspace", "workspace-id"}, + }, + { + CLIPath: "drive download-version", + Aliases: map[string]string{ + "chunk-size": "part-size", + "concurrency": "parallel", + "dentry-uuid": "node", + "destination-path": "output", + "document-id": "node", + "output-path": "output", + "parallelism": "parallel", + "save-path": "output", + "version-no": "version", + "version-number": "version", + }, + Blocked: []string{"content-file", "dentry-id", "file", "file-path", "folder", "folder-id", "revision"}, }, { CLIPath: "drive info", Aliases: map[string]string{ - "space": "space-id", - "workspace": "space-id", - "workspace-id": "space-id", + "dentry-uuid": "node", + "dingdrive-space-id": "space-id", + "drive-space-id": "space-id", + "space": "space-id", + "storage-space-id": "space-id", + "workspace": "space-id", + "workspace-id": "space-id", }, - Blocked: []string{"folder"}, + Blocked: []string{"dentry-id", "document-url", "knowledge-base-id", "wiki-workspace-id"}, }, { CLIPath: "drive list", Aliases: map[string]string{ - "folder-id": "folder", + "dentry-uuid": "node", + "dingdrive-space-id": "space-id", + "directory-id": "folder", + "document-id": "node", + "drive-space-id": "space-id", + "folder-id": "folder", + "knowledge-base-id": "workspace", + "max-result": "limit", + "max-results": "limit", + "next-cursor": "cursor", + "next-page-token": "cursor", + "order-direction": "order", + "order-field": "order-by", + "per-page": "limit", + "size": "limit", + "sort": "order", + "sort-by": "order-by", + "sort-direction": "order", + "sort-field": "order-by", + "storage-space-id": "space-id", + "take": "limit", + "top": "limit", + "wiki-workspace-id": "workspace", }, - Ambiguous: []string{"space"}, + Blocked: []string{"count", "dentry-id", "offset", "page"}, + Ambiguous: []string{"root-id", "space"}, }, { - CLIPath: "drive upload", - Ambiguous: []string{"space"}, + CLIPath: "drive list-spaces", + Aliases: map[string]string{ + "max-result": "limit", + "max-results": "limit", + "next-cursor": "cursor", + "next-page-token": "cursor", + "per-page": "limit", + "size": "limit", + "take": "limit", + "top": "limit", + }, + Blocked: []string{"count", "offset", "page"}, + }, + { + CLIPath: "drive mkdir", + Aliases: map[string]string{ + "dingdrive-space-id": "space-id", + "directory-id": "folder", + "display-name": "name", + "drive-space-id": "space-id", + "folder-id": "folder", + "folder-name": "name", + "new-name": "name", + "parent-directory-id": "folder", + "storage-space-id": "space-id", + }, + Blocked: []string{"knowledge-base-id", "space", "wiki-workspace-id", "workspace", "workspace-id"}, + }, + { + CLIPath: "drive move", + Aliases: map[string]string{ + "dentry-uuid": "node", + "destination-folder-id": "folder", + "destination-workspace-id": "workspace", + "directory-id": "folder", + "document-id": "node", + "folder-id": "folder", + "knowledge-base-id": "workspace", + "source-file-id": "node", + "source-node-id": "node", + "target-folder-id": "folder", + "target-workspace-id": "workspace", + "wiki-workspace-id": "workspace", + }, + Blocked: []string{"dentry-id", "destination-node-id", "dingdrive-space-id", "drive-space-id", "name", "source-folder-id", "space", "space-id", "storage-space-id"}, + Ambiguous: []string{"destination-id", "target-id"}, + }, + { + CLIPath: "drive permission add", + Aliases: map[string]string{ + "access-role": "role", + "collaborator-ids": "users", + "dentry-uuid": "node", + "document-id": "node", + "knowledge-base-id": "workspace", + "member-role": "role", + "member-user-ids": "users", + "permission-role": "role", + "target-node-id": "node", + "target-user-ids": "users", + "wiki-workspace-id": "workspace", + }, + Blocked: []string{"dentry-id", "dingdrive-space-id", "drive-space-id", "filter-role", "folder", "folder-id", "permission", "public-permission", "reserve-role", "space", "space-id", "storage-space-id"}, + }, + { + CLIPath: "drive permission apply", + Aliases: map[string]string{ + "access-role": "role", + "apply-reason": "reason", + "approver-ids": "users", + "approver-user-ids": "users", + "dentry-uuid": "node", + "document-id": "node", + "member-role": "role", + "notification-mode": "notify-mode", + "permission-role": "role", + "target-node-id": "node", + }, + Blocked: []string{"dentry-id", "filter-role", "permission", "public-permission", "reserve-role"}, + }, + { + CLIPath: "drive permission apply-info", + Aliases: map[string]string{ + "dentry-uuid": "node", + "document-id": "node", + }, + Blocked: []string{"dentry-id"}, + }, + { + CLIPath: "drive permission list", + Aliases: map[string]string{ + "dentry-uuid": "node", + "document-id": "node", + "knowledge-base-id": "workspace", + "max-result": "limit", + "per-page": "limit", + "permission-role-filter": "filter-role", + "role-filter": "filter-role", + "size": "limit", + "take": "limit", + "target-node-id": "node", + "top": "limit", + "wiki-workspace-id": "workspace", + }, + Blocked: []string{"count", "cursor", "dentry-id", "dingdrive-space-id", "drive-space-id", "folder", "folder-id", "page", "permission", "role", "space", "space-id", "storage-space-id"}, + }, + { + CLIPath: "drive permission remove", + Aliases: map[string]string{ + "collaborator-ids": "users", + "dentry-uuid": "node", + "document-id": "node", + "knowledge-base-id": "workspace", + "member-user-ids": "users", + "target-node-id": "node", + "target-user-ids": "users", + "wiki-workspace-id": "workspace", + }, + Blocked: []string{"dentry-id", "dingdrive-space-id", "drive-space-id", "folder", "folder-id", "space", "space-id", "storage-space-id"}, + }, + { + CLIPath: "drive permission transfer-owner", + Aliases: map[string]string{ + "dentry-uuid": "node", + "document-id": "node", + "keep-role": "reserve-role", + "knowledge-base-id": "workspace", + "new-owner-id": "new-owner", + "new-owner-user-id": "new-owner", + "old-owner-role": "reserve-role", + "target-node-id": "node", + "target-workspace-id": "workspace", + "wiki-workspace-id": "workspace", + }, + Blocked: []string{"current-owner", "dentry-id", "dingdrive-space-id", "drive-space-id", "folder", "folder-id", "space", "space-id", "storage-space-id", "user-ids", "users"}, + Ambiguous: []string{"owner", "owner-user-id", "target-id", "user-id"}, + }, + { + CLIPath: "drive permission update", + Aliases: map[string]string{ + "access-role": "role", + "collaborator-ids": "users", + "dentry-uuid": "node", + "document-id": "node", + "knowledge-base-id": "workspace", + "member-role": "role", + "member-user-ids": "users", + "permission-role": "role", + "target-node-id": "node", + "target-user-ids": "users", + "wiki-workspace-id": "workspace", + }, + Blocked: []string{"dentry-id", "dingdrive-space-id", "drive-space-id", "filter-role", "folder", "folder-id", "permission", "public-permission", "reserve-role", "space", "space-id", "storage-space-id"}, + }, + { + CLIPath: "drive publish get", + Aliases: map[string]string{ + "dentry-uuid": "node", + "document-id": "node", + }, + Blocked: []string{"dentry-id"}, + }, + { + CLIPath: "drive publish set", + Aliases: map[string]string{ + "dentry-uuid": "node", + "document-id": "node", + "public-permission": "permission", + "public-role": "permission", + }, + Blocked: []string{"access-role", "dentry-id", "permission-role", "role"}, + }, + { + CLIPath: "drive publish unset", + Aliases: map[string]string{ + "dentry-uuid": "node", + "document-id": "node", + }, + Blocked: []string{"dentry-id"}, + }, + { + CLIPath: "drive recent", + Aliases: map[string]string{ + "max-result": "limit", + "next-cursor": "cursor", + "next-page-token": "cursor", + "next-token": "cursor", + "page-size": "limit", + "per-page": "limit", + "size": "limit", + "take": "limit", + "top": "limit", + }, + Blocked: []string{"count", "offset", "page"}, + Ambiguous: []string{"type", "types"}, + }, + { + CLIPath: "drive recycle list", + Aliases: map[string]string{ + "dingdrive-space-id": "space-id", + "drive-space-id": "space-id", + "max-result": "limit", + "max-results": "limit", + "next-cursor": "cursor", + "next-page-token": "cursor", + "per-page": "limit", + "size": "limit", + "storage-space-id": "space-id", + "take": "limit", + "top": "limit", + }, + Blocked: []string{"count", "knowledge-base-id", "offset", "page", "space", "wiki-workspace-id", "workspace", "workspace-id"}, + }, + { + CLIPath: "drive recycle restore", + Aliases: map[string]string{ + "deleted-item-id": "id", + "recycle-item-id": "id", + "trash-item-id": "id", + }, + Blocked: []string{"file-id", "folder-id", "node", "node-id", "space-id", "workspace-id"}, + }, + { + CLIPath: "drive rename", + Aliases: map[string]string{ + "dentry-uuid": "node", + "display-name": "name", + "document-id": "node", + "file-name": "name", + "new-name": "name", + }, + Blocked: []string{"dentry-id"}, + }, + { + CLIPath: "drive revert", + Aliases: map[string]string{ + "dentry-uuid": "node", + "document-id": "node", + "version-no": "version", + "version-number": "version", + }, + Blocked: []string{"dentry-id", "revision"}, + }, + { + CLIPath: "drive search", + Aliases: map[string]string{ + "create-time-end": "created-to", + "create-time-from": "created-from", + "create-time-start": "created-from", + "create-time-to": "created-to", + "created-after": "created-from", + "created-before": "created-to", + "created-by-user-ids": "creator-uids", + "created-time-end": "created-to", + "created-time-start": "created-from", + "creator-ids": "creator-uids", + "creator-user-ids": "creator-uids", + "keywords": "query", + "max-result": "limit", + "max-results": "limit", + "modified-after": "modified-from", + "modified-before": "modified-to", + "modified-time-end": "modified-to", + "modified-time-start": "modified-from", + "modify-time-from": "modified-from", + "modify-time-to": "modified-to", + "next-cursor": "cursor", + "next-page-token": "cursor", + "per-page": "limit", + "q": "query", + "search-word": "query", + "size": "limit", + "take": "limit", + "top": "limit", + }, + Blocked: []string{"count", "modifier-uids", "name", "offset", "owner-id", "page", "subject", "text", "title", "user", "user-id", "user-ids", "users"}, + Ambiguous: []string{"end", "from", "start", "time-from", "time-to", "to", "types"}, + }, + { + CLIPath: "drive shortcut", + Aliases: map[string]string{ + "dentry-uuid": "node", + "destination-folder-id": "folder", + "destination-workspace-id": "workspace", + "directory-id": "folder", + "document-id": "node", + "folder-id": "folder", + "knowledge-base-id": "workspace", + "source-file-id": "node", + "source-node-id": "node", + "target-folder-id": "folder", + "target-workspace-id": "workspace", + "wiki-workspace-id": "workspace", + }, + Blocked: []string{"dentry-id", "destination-node-id", "dingdrive-space-id", "drive-space-id", "name", "source-folder-id", "space", "space-id", "storage-space-id"}, + Ambiguous: []string{"destination-id", "target-id"}, + }, + { + CLIPath: "drive star add", + Aliases: map[string]string{ + "dentry-uuid": "node", + "document-id": "node", + }, + Blocked: []string{"dentry-id"}, + }, + { + CLIPath: "drive star list", + Aliases: map[string]string{ + "max-result": "limit", + "max-results": "limit", + "next-cursor": "cursor", + "next-page-token": "cursor", + "order": "sort", + "order-direction": "sort", + "order-field": "order-by", + "per-page": "limit", + "size": "limit", + "sort-by": "order-by", + "sort-direction": "sort", + "sort-field": "order-by", + "take": "limit", + "top": "limit", + }, + Blocked: []string{"count", "offset", "page"}, + Ambiguous: []string{"type", "types"}, + }, + { + CLIPath: "drive star remove", + Aliases: map[string]string{ + "dentry-uuid": "node", + "document-id": "node", + }, + Blocked: []string{"dentry-id"}, + }, + { + CLIPath: "drive stats", + Aliases: map[string]string{ + "dentry-uuid": "node", + "document-id": "node", + }, + Blocked: []string{"dentry-id"}, + }, + { + CLIPath: "drive upload", + Aliases: map[string]string{ + "content-type": "mime-type", + "dentry-uuid": "node", + "dingdrive-space-id": "space-id", + "directory-id": "folder", + "display-name": "file-name", + "drive-space-id": "space-id", + "filename": "file-name", + "folder-id": "folder", + "knowledge-base-id": "workspace", + "name": "file-name", + "overwrite-node-id": "node", + "source-file": "file", + "storage-space-id": "space-id", + "target-folder-id": "folder", + "target-workspace-id": "workspace", + "upload-name": "file-name", + "wiki-workspace-id": "workspace", + }, + Blocked: []string{"dentry-id", "document-url", "output-path"}, + Ambiguous: []string{"destination-id", "space", "target-id"}, + }, + { + CLIPath: "drive upload-info", + Aliases: map[string]string{ + "content-length": "file-size", + "content-type": "mime-type", + "dingdrive-space-id": "space-id", + "directory-id": "folder", + "display-name": "file-name", + "drive-space-id": "space-id", + "file-size-bytes": "file-size", + "filename": "file-name", + "folder-id": "folder", + "name": "file-name", + "parent-directory-id": "folder", + "size-bytes": "file-size", + "storage-space-id": "space-id", + "upload-name": "file-name", + }, + Blocked: []string{"knowledge-base-id", "limit", "page-size", "part-size", "size", "space", "wiki-workspace-id", "workspace", "workspace-id"}, }, { CLIPath: "mail +find-mail-user", diff --git a/internal/cli/param_aliases_test.go b/internal/cli/param_aliases_test.go index aab959b7..64f76daa 100644 --- a/internal/cli/param_aliases_test.go +++ b/internal/cli/param_aliases_test.go @@ -515,3 +515,68 @@ func TestGeneratedParamAliasesAreWellFormed(t *testing.T) { } } } + +func TestCrossPlatformCoveragePublishedSpaceWorkspaceAliasesRemainExecutable(t *testing.T) { + docCommands := []string{ + "doc +access-change", + "doc +access-grant", + "doc +access-revoke", + "doc +copy", + "doc +create", + "doc +create-from-template", + "doc +grant-and-share", + "doc +import", + "doc +list", + "doc +move", + "doc create", + "doc file create", + "doc import", + "doc template apply", + } + for _, command := range docCommands { + entry, ok := LookupParamAlias(command) + if !ok { + t.Errorf("published Doc command %q has no generated parameter-alias entry", command) + continue + } + for _, emitted := range []string{"space", "space-id"} { + target, active := entry.ResolveAlias(emitted) + if !active || target != "workspace" { + t.Errorf("%s --%s resolution = active:%v target:%q, want --workspace", command, emitted, active, target) + } + if entry.IsBlocked(emitted) || entry.IsAmbiguous(emitted) { + t.Errorf("%s --%s remains protected after restoring its published alias", command, emitted) + } + } + } + + driveInfo, ok := LookupParamAlias("drive info") + if !ok { + t.Fatal("published drive info command has no generated parameter-alias entry") + } + for _, emitted := range []string{"space", "workspace", "workspace-id"} { + target, active := driveInfo.ResolveAlias(emitted) + if !active || target != "space-id" { + t.Errorf("drive info --%s resolution = active:%v target:%q, want --space-id", emitted, active, target) + } + if driveInfo.IsBlocked(emitted) || driveInfo.IsAmbiguous(emitted) { + t.Errorf("drive info --%s remains protected after restoring its published alias", emitted) + } + } + + // Compatibility remains exact-path scoped. Strong type spellings introduced + // after the split continue to guard the two value domains elsewhere. + for _, test := range []struct { + command string + emitted string + }{ + {command: "doc create", emitted: "storage-space-id"}, + {command: "drive +upload", emitted: "workspace-id"}, + {command: "drive info", emitted: "knowledge-base-id"}, + } { + entry, ok := LookupParamAlias(test.command) + if !ok || !entry.IsBlocked(test.emitted) { + t.Errorf("%s --%s must remain blocked outside the published compatibility set", test.command, test.emitted) + } + } +} diff --git a/internal/cli/param_concepts.json b/internal/cli/param_concepts.json index db93b507..eb8f5d17 100644 --- a/internal/cli/param_concepts.json +++ b/internal/cli/param_concepts.json @@ -10,10 +10,10 @@ }, "concepts": { - "search_query": {"denotes": "search keyword string", "canonical_hint": "query", "members": ["query", "keyword", "keywords", "q", "search-word"], "excludes": ["name", "subject", "text", "title"], "commands": ["aitable +base-search", "contact +dept-members", "contact +resolve-dept", "contact +search-user", "doc +create-from-template", "doc +find-doc", "doc +search", "doc +template-search", "doc template search", "mail +find-mail-user", "mail user search", "oa +search-forms", "oa approval search-forms"], "risk": "green"}, - "pagination_size": {"denotes": "returned item count upper bound", "canonical_hint": "limit", "members": ["limit", "size", "page-size", "max-results", "max-result", "take", "top", "per-page"], "excludes": ["count", "page", "cursor"], "commands": ["aitable record query", "calendar event list", "chat message list", "devdoc article search", "doc +comment-list", "doc +find-doc", "doc +list", "doc +search", "doc +template-list", "doc +template-search", "doc +version-list", "doc comment list", "doc template list", "doc template search", "doc version list", "mail thread list", "oa +list-executed"], "risk": "green"}, + "search_query": {"denotes": "search keyword string", "canonical_hint": "query", "members": ["query", "keyword", "keywords", "q", "search-word"], "excludes": ["name", "subject", "text", "title"], "commands": ["aitable +base-search", "contact +dept-members", "contact +resolve-dept", "contact +search-user", "doc +create-from-template", "doc +find-doc", "doc +search", "doc +template-search", "doc template search", "drive +find-file", "drive +search", "drive +search-docs", "drive search", "mail +find-mail-user", "mail user search", "oa +search-forms", "oa approval search-forms"], "risk": "green"}, + "pagination_size": {"denotes": "returned item count upper bound", "canonical_hint": "limit", "members": ["limit", "size", "page-size", "max-results", "max-result", "take", "top", "per-page"], "excludes": ["count", "page", "cursor"], "commands": ["aitable record query", "calendar event list", "chat message list", "devdoc article search", "doc +comment-list", "doc +find-doc", "doc +list", "doc +search", "doc +template-list", "doc +template-search", "doc +version-list", "doc comment list", "doc template list", "doc template search", "doc version list", "drive +list", "drive +recent", "drive +recycle-list", "drive +search", "drive +search-docs", "drive +star-list", "drive +version-history", "drive list", "drive list-spaces", "drive permission list", "drive recent", "drive recycle list", "drive search", "drive star list", "mail thread list", "oa +list-executed"], "risk": "green"}, "page_number": {"denotes": "one-based page number", "canonical_hint": "page", "members": ["page", "page-no", "current-page", "page-num"], "excludes": ["cursor", "page-index", "page-size", "page-token"], "commands": ["devdoc article search"], "risk": "green"}, - "page_cursor": {"denotes": "pagination cursor/token", "canonical_hint": "cursor", "members": ["cursor", "next-cursor", "page-token", "next-token", "next-page-token"], "excludes": ["page", "offset"], "commands": ["calendar event list", "doc +comment-list", "doc +list", "doc +search", "doc +template-list", "doc +template-search", "doc +version-list", "doc comment list", "doc template list", "doc template search", "doc version list"], "risk": "green"}, + "page_cursor": {"denotes": "pagination cursor/token", "canonical_hint": "cursor", "members": ["cursor", "next-cursor", "page-token", "next-token", "next-page-token"], "excludes": ["page", "offset"], "commands": ["calendar event list", "doc +comment-list", "doc +list", "doc +search", "doc +template-list", "doc +template-search", "doc +version-list", "doc comment list", "doc template list", "doc template search", "doc version list", "drive +list", "drive +recent", "drive +recycle-list", "drive +search", "drive +star-list", "drive +version-history", "drive list", "drive list-spaces", "drive recent", "drive recycle list", "drive search", "drive star list"], "risk": "green"}, "content_text": {"denotes": "text body content", "canonical_hint": "text", "members": ["text", "content", "body"], "excludes": ["title", "name"], "commands": ["doc +checkpoint-update", "doc +comment-create", "doc +comment-reply", "doc +comment-update", "doc +create", "doc +doc-append", "doc block insert", "doc block update", "doc comment create", "doc comment create-inline", "doc comment reply", "doc comment update", "doc create"], "risk": "green"}, "time_start": {"denotes": "start time point with unchanged value format and unit", "canonical_hint": "start", "members": ["start", "start-time", "start-date", "from", "from-date", "begin", "since", "time-min", "min-time"], "excludes": ["date", "time", "end"], "commands": ["calendar event list", "chat message list-all", "report list"], "risk": "yellow"}, "time_end": {"denotes": "end time point with unchanged value format and unit", "canonical_hint": "end", "members": ["end", "end-time", "end-date", "time-max", "max-time"], "excludes": ["date", "time", "start"], "commands": ["calendar event list"], "risk": "yellow"}, @@ -31,20 +31,31 @@ "user_ids": {"denotes": "user id list", "canonical_hint": "user-ids", "members": ["users", "user-ids"], "excludes": ["user", "user-id", "userid", "uid", "staff-id", "at-user-ids"], "commands": ["attendance +check-result", "attendance check result", "chat +messages-batch-send-by-bot", "chat group members remove", "chat group set-admin", "chat group-mute-member", "chat message read-status", "chat message search-advanced", "chat message send-by-bot"], "risk": "yellow"}, "open_dingtalk_ids": {"denotes": "DingTalk openDingTalkId list with unchanged element values", "canonical_hint": "open-dingtalk-ids", "members": ["open-dingtalk-ids"], "excludes": ["user", "user-id", "user-ids", "staff-id", "users"], "commands": ["chat +chat-members-get", "chat category create-smart", "chat group members list-by-ids", "chat message send-by-bot"], "risk": "yellow"}, "ding_id": {"denotes": "DING id", "canonical_hint": "ding-id", "members": ["ding-id", "open-ding-id"], "excludes": ["id"], "commands": ["ding message receiver-status"], "risk": "yellow"}, - "folder_id": {"denotes": "drive folder id", "canonical_hint": "folder", "members": ["folder", "folder-id"], "excludes": ["space-id"], "commands": ["drive list", "mail folder update"], "risk": "green"}, - "space_id": {"denotes": "drive/wiki/Doc workspace id with unchanged value", "canonical_hint": "space-id", "members": ["space-id", "space", "workspace", "workspace-id"], "excludes": ["folder", "node"], "commands": ["doc +access-change", "doc +access-grant", "doc +access-revoke", "doc +copy", "doc +create", "doc +create-from-template", "doc +grant-and-share", "doc +import", "doc +list", "doc +move", "doc create", "doc file create", "doc import", "doc template apply", "drive info"], "risk": "yellow"}, + "folder_id": {"denotes": "drive folder id", "canonical_hint": "folder", "members": ["folder", "folder-id"], "excludes": ["space-id"], "commands": ["drive +copy", "drive +create-folder", "drive +create-shortcut", "drive +list", "drive +move", "drive +upload", "drive commit", "drive copy", "drive list", "drive mkdir", "drive move", "drive shortcut", "drive upload", "drive upload-info", "mail folder update"], "risk": "green"}, + "drive_storage_space_id": {"denotes": "single numeric DingDrive storage space ID with unchanged value", "canonical_hint": "space-id", "members": ["space-id", "drive-space-id", "storage-space-id", "dingdrive-space-id"], "excludes": ["space", "workspace", "workspace-id", "knowledge-base-id", "wiki-workspace-id"], "commands": ["drive +create-folder", "drive +download", "drive +info", "drive +inspect", "drive +list", "drive +recycle-list", "drive +upload", "drive commit", "drive download", "drive info", "drive list", "drive mkdir", "drive recycle list", "drive upload", "drive upload-info"], "risk": "yellow"}, "app_id": {"denotes": "application id", "canonical_hint": "unified-app-id", "members": ["app-id", "unified-app-id", "application-id"], "excludes": ["app-key", "app-secret", "agent-id"], "commands": ["dev app get"], "risk": "yellow"}, "robot_code": {"denotes": "robot code", "canonical_hint": "robot-code", "members": ["robot-code", "robot"], "excludes": ["robot-id", "bot-id", "open-bot-id", "bot-code"], "commands": ["chat +chat-add-bot", "chat +messages-batch-recall-by-bot", "chat +messages-batch-send-by-bot", "chat +messages-recall-by-bot", "chat +messages-send-by-bot", "chat group members add-bot", "chat message recall-by-bot", "chat message send-by-bot", "ding message send"], "risk": "yellow"}, "open_bot_id": {"denotes": "single DingTalk openBotId with unchanged value", "canonical_hint": "bot-id", "members": ["bot-id", "open-bot-id"], "excludes": ["robot-code", "robot", "robot-id", "bot-code"], "commands": ["chat +chat-remove-bot", "chat group members remove-bot"], "risk": "yellow"}, - "doc_node_id": {"denotes": "single DingTalk document nodeId or accepted document URL/token with unchanged value", "canonical_hint": "node", "members": ["node", "node-id", "doc", "doc-id", "file-id", "document-id", "url"], "excludes": ["id", "folder", "folder-id", "parent-id", "workspace", "workspace-id", "block-id", "comment-id", "comment-key", "job-id", "task-id", "template-id", "version", "revision"], "commands": ["doc +access-change", "doc +access-grant", "doc +access-revoke", "doc +background-delete", "doc +background-update", "doc +checkpoint-update", "doc +comment-create", "doc +comment-delete", "doc +comment-list", "doc +comment-reply", "doc +comment-update", "doc +copy", "doc +doc-append", "doc +export", "doc +export-submit", "doc +fetch", "doc +history-list", "doc +history-revert", "doc +history-save", "doc +inspect", "doc +move", "doc +review", "doc +version-list", "doc +version-revert", "doc +version-save", "doc block delete", "doc block insert", "doc block list", "doc block update", "doc comment create", "doc comment create-inline", "doc comment delete", "doc comment list", "doc comment reply", "doc comment update", "doc export", "doc info", "doc media download", "doc media insert", "doc media upload", "doc read", "doc style background clear", "doc style background set", "doc style cover clear", "doc style cover set", "doc style get", "doc update", "doc version list", "doc version revert", "doc version save", "doc whiteboard insert"], "risk": "yellow"}, + "doc_node_id": {"denotes": "single DingTalk document nodeId or accepted document URL/token with unchanged value", "canonical_hint": "node", "members": ["node", "node-id", "doc", "doc-id", "file-id", "document-id", "url", "dentry-uuid"], "excludes": ["id", "folder", "folder-id", "parent-id", "workspace", "workspace-id", "block-id", "comment-id", "comment-key", "job-id", "task-id", "template-id", "version", "revision", "dentry-id", "space-id", "name", "role"], "commands": ["doc +access-change", "doc +access-grant", "doc +access-revoke", "doc +background-delete", "doc +background-update", "doc +checkpoint-update", "doc +comment-create", "doc +comment-delete", "doc +comment-list", "doc +comment-reply", "doc +comment-update", "doc +copy", "doc +doc-append", "doc +export", "doc +export-submit", "doc +fetch", "doc +history-list", "doc +history-revert", "doc +history-save", "doc +inspect", "doc +move", "doc +review", "doc +version-list", "doc +version-revert", "doc +version-save", "doc block delete", "doc block insert", "doc block list", "doc block update", "doc comment create", "doc comment create-inline", "doc comment delete", "doc comment list", "doc comment reply", "doc comment update", "doc export", "doc info", "doc media download", "doc media insert", "doc media upload", "doc read", "doc style background clear", "doc style background set", "doc style cover clear", "doc style cover set", "doc style get", "doc update", "doc version list", "doc version revert", "doc version save", "doc whiteboard insert"], "risk": "yellow"}, "doc_comment_key": {"denotes": "single DingTalk document commentKey with unchanged value", "canonical_hint": "comment-key", "members": ["comment-key", "comment-id"], "excludes": ["id", "node", "node-id", "doc-id", "block-id"], "commands": ["doc +comment-delete", "doc +comment-reply", "doc +comment-update", "doc comment delete", "doc comment reply", "doc comment update"], "risk": "yellow"}, - "doc_version_number": {"denotes": "single DingTalk document historical version number with unchanged integer value", "canonical_hint": "version", "members": ["version", "version-number", "version-no"], "excludes": ["revision", "id", "node", "node-id", "doc-id"], "commands": ["doc +history-revert", "doc +version-revert", "doc version revert"], "risk": "yellow"}, + "doc_version_number": {"denotes": "single document or Drive ordinary-file historical version number with unchanged positive integer value", "canonical_hint": "version", "members": ["version", "version-number", "version-no"], "excludes": ["revision", "id", "node", "node-id", "doc-id"], "commands": ["doc +history-revert", "doc +version-revert", "doc version revert", "drive +version-download", "drive +version-get", "drive +version-revert", "drive download", "drive download-version", "drive revert"], "risk": "yellow"}, "doc_content_format": {"denotes": "DingTalk document body format with unchanged markdown/jsonml value", "canonical_hint": "content-format", "members": ["content-format", "doc-format"], "excludes": ["format", "export-format", "mime-type"], "commands": ["doc +create", "doc +update", "doc create", "doc update"], "risk": "green"}, - "doc_edit_revision": {"denotes": "single optimistic-concurrency revision for a document edit", "canonical_hint": "revision", "members": ["revision", "expected-revision"], "excludes": ["version", "version-number", "version-no"], "commands": ["doc +update", "doc update"], "risk": "yellow"} + "doc_edit_revision": {"denotes": "single optimistic-concurrency revision for a document edit", "canonical_hint": "revision", "members": ["revision", "expected-revision"], "excludes": ["version", "version-number", "version-no"], "commands": ["doc +update", "doc update"], "risk": "yellow"}, + "drive_recycle_item_id": {"denotes": "single Drive recycle-bin item ID returned by recycle list", "canonical_hint": "id", "members": ["id", "recycle-item-id", "trash-item-id", "deleted-item-id"], "excludes": ["node", "node-id", "file-id", "folder-id", "space-id", "workspace-id"], "commands": ["drive +recycle-restore", "drive recycle restore"], "risk": "yellow"}, + "drive_modified_time_start": {"denotes": "Drive search modified-time lower bound in unchanged millisecond timestamp unit", "canonical_hint": "modified-from", "members": ["modified-from", "modified-after", "modify-time-from", "modified-time-start"], "excludes": ["modified-to", "created-from", "created-to", "start", "from"], "commands": ["drive +search", "drive search"], "risk": "yellow"}, + "drive_modified_time_end": {"denotes": "Drive search modified-time upper bound in unchanged millisecond timestamp unit", "canonical_hint": "modified-to", "members": ["modified-to", "modified-before", "modify-time-to", "modified-time-end"], "excludes": ["modified-from", "created-from", "created-to", "end", "to"], "commands": ["drive +search", "drive search"], "risk": "yellow"}, + "drive_file_size_bytes": {"denotes": "file size in bytes passed unchanged", "canonical_hint": "file-size", "members": ["file-size", "file-size-bytes", "size-bytes", "content-length"], "excludes": ["part-size", "page-size", "limit", "size"], "commands": ["drive commit", "drive upload-info"], "risk": "yellow"}, + "drive_sort_direction": {"denotes": "Drive result sort direction with unchanged asc/desc enum", "canonical_hint": "order", "members": ["order", "sort", "sort-direction", "order-direction"], "excludes": ["order-by", "sort-by", "order-field"], "commands": ["drive +list", "drive list", "drive star list"], "risk": "green"}, + "workspace_id": {"denotes": "single knowledge-base or document-space workspace ID/URL passed unchanged; never a numeric DingDrive storage space ID", "canonical_hint": "workspace", "members": ["workspace", "workspace-id", "knowledge-base-id", "wiki-workspace-id"], "excludes": ["space", "space-id", "drive-space-id", "storage-space-id", "dingdrive-space-id", "folder", "folder-id", "node", "node-id", "dentry-id"], "commands": ["doc +access-change", "doc +access-grant", "doc +access-revoke", "doc +copy", "doc +create", "doc +create-from-template", "doc +grant-and-share", "doc +import", "doc +list", "doc +move", "doc create", "doc file create", "doc import", "doc template apply", "drive +copy", "drive +create-shortcut", "drive +move", "drive copy", "drive list", "drive move", "drive permission add", "drive permission list", "drive permission remove", "drive permission transfer-owner", "drive permission update", "drive shortcut", "drive upload"], "risk": "yellow"}, + "created_time_start": {"denotes": "Doc/Drive search created-time lower bound in unchanged millisecond timestamp unit", "canonical_hint": "created-from", "members": ["created-from", "created-after", "create-time-from", "created-time-start", "create-time-start"], "excludes": ["created-to", "modified-from", "modified-to", "start", "from"], "commands": ["doc +search", "drive +search", "drive search"], "risk": "yellow"}, + "created_time_end": {"denotes": "Doc/Drive search created-time upper bound in unchanged millisecond timestamp unit", "canonical_hint": "created-to", "members": ["created-to", "created-before", "create-time-to", "created-time-end", "create-time-end"], "excludes": ["created-from", "modified-from", "modified-to", "end", "to"], "commands": ["doc +search", "drive +search", "drive search"], "risk": "yellow"}, + "document_permission_role": {"denotes": "direct document-space permission role on grant/apply/update, passed unchanged", "canonical_hint": "role", "members": ["role", "permission-role", "access-role", "member-role"], "excludes": ["filter-role", "reserve-role", "permission", "public-permission"], "commands": ["doc +access-change", "doc +access-grant", "doc +grant-and-share", "drive permission add", "drive permission apply", "drive permission update"], "risk": "yellow"}, + "creator_user_ids": {"denotes": "creator userId list used to filter Doc/Drive search results, passed unchanged", "canonical_hint": "creator-uids", "members": ["creator-uids", "creator-user-ids", "creator-ids", "created-by-user-ids"], "excludes": ["user", "user-id", "user-ids", "users", "owner-id", "modifier-uids"], "commands": ["doc +search", "drive +search", "drive search"], "risk": "yellow"}, + "local_output_path": {"denotes": "local destination file or directory path for a download/export result, passed unchanged", "canonical_hint": "output", "members": ["output", "output-path", "destination-path", "save-path"], "excludes": ["file", "file-path", "folder", "folder-id", "content-file"], "commands": ["doc +export", "doc +export-get", "doc +media-download", "doc +resource-download", "doc read", "drive +download", "drive +version-download", "drive download", "drive download-version"], "risk": "green"} }, "command_overrides": { - "doc +version-list": {"ambiguous": ["size", "max-results", "max-result", "take", "top", "per-page", "next-cursor", "next-token", "next-page-token"], "note": "--limit/--cursor and the shipped visible compatibility flags --page-size/--page-token remain native. Other pagination spellings cannot choose between two visible real flags and must stop before execution."}, + "doc +version-list": {"ambiguous": ["size", "max-results", "max-result", "take", "top", "per-page", "next-cursor", "next-token", "next-page-token"], "note": "--limit/--cursor and the shipped visible compatibility flags --page-size/--page-token remain native. Other pagination spellings cannot choose between two visible real flags and must stop before execution."}, "chat group rename": {"bind": {"id": "open_conversation_id"}, "note": "This command's real --id carries one openConversationId; aliases reduce to --id without changing the value."}, "chat group members": {"bind": {"id": "open_conversation_id"}}, "chat group members add": {"bind": {"id": "open_conversation_id"}, "block": ["user-id", "open-dingtalk-id"], "note": "The real --users is a list and may contain mixed userId/openDingTalkId values; singular inputs are not promoted automatically."}, @@ -54,8 +65,8 @@ "chat message remove-emoji": {"scoped_aliases": {"chat-id": "conversation-id", "open-conversation-id": "conversation-id"}, "block": ["group-id", "group-ids", "conversation-ids", "open-conversation-ids"], "note": "Native --chat/--group/--id/--conversation-id stay native; numeric groupId and list spellings are rejected."}, "chat message remove-text-emotion": {"scoped_aliases": {"chat-id": "conversation-id", "open-conversation-id": "conversation-id"}, "block": ["group-id", "group-ids", "conversation-ids", "open-conversation-ids"], "note": "Native --chat/--group/--id/--conversation-id stay native; numeric groupId and list spellings are rejected."}, "chat mute": {"scoped_aliases": {"group": "conversation-id", "chat-id": "conversation-id", "open-conversation-id": "conversation-id"}, "block": ["group-id", "group-ids", "conversation-ids", "open-conversation-ids"], "note": "Native --conversation-id/--id/--chat remain unchanged; other reviewed openConversationId spellings reduce to --conversation-id."}, - "drive list": {"ambiguous": ["space"], "note": "both --space-id and native compatibility --workspace-id/--workspace exist; bare --space cannot choose one"}, - "drive upload": {"ambiguous": ["space"], "note": "both --space-id and native compatibility --workspace-id/--workspace exist; bare --space cannot choose one"}, + "drive list": {"ambiguous": ["root-id", "space"], "note": "Numeric --space-id and knowledge-base --workspace are distinct routes; bare --space/--root-id cannot select a domain or folder.", "scoped_aliases": {"document-id": "node", "dentry-uuid": "node", "directory-id": "folder", "order-field": "order-by", "sort-by": "order-by", "sort-field": "order-by"}, "scope_strict": true, "block": ["dentry-id"]}, + "drive upload": {"ambiguous": ["destination-id", "space", "target-id"], "note": "Local file, display name, MIME, overwrite node, folder, storage space, and knowledge-base workspace remain distinct roles.", "scoped_aliases": {"dentry-uuid": "node", "directory-id": "folder", "overwrite-node-id": "node", "target-folder-id": "folder", "target-workspace-id": "workspace", "source-file": "file", "content-type": "mime-type", "filename": "file-name", "name": "file-name", "display-name": "file-name", "upload-name": "file-name"}, "block": ["dentry-id", "document-url", "output-path"], "scope_strict": true}, "ding +receiver-status": {"scoped_aliases": {"id": "ding-id"}, "note": "generic id reduces to ding-id"}, "ding message receiver-status": {"scoped_aliases": {"id": "ding-id"}}, "contact user profile get": {"scoped_aliases": {"id": "staff-id", "ids": "staff-id"}, "note": "user-id is reduced by the user_id concept; generic id/ids bound explicitly"}, @@ -101,9 +112,9 @@ "chat message send-by-bot": {"scoped_aliases": {"at-users": "at-user-ids"}, "block": ["user-id", "to-user-id"], "ambiguous": ["at-ids"], "note": "The reviewed @ userId-list alias is exact; singular recipients are not promoted, and bare --at-ids cannot choose an identifier domain."}, "doc +export-get": {"block": ["doc-id", "document-id", "file-id", "node", "node-id", "task-id", "url"], "note": "This command queries one export jobId. Document node identifiers and import taskId spellings are different entities and are rejected.", "scoped_aliases": {"export-job-id": "job-id"}, "scope_strict": true}, "doc block delete": {"block": ["index"], "note": "index (position) vs node (node id) are different"}, - "doc +copy": {"scoped_aliases": {"folder-id": "folder", "parent-folder": "folder", "parent-folder-id": "folder", "parent-node-id": "folder"}, "block": ["parent-id"], "scope_strict": true, "note": "This exact Doc command expects a Doc folder nodeId/dentryUuid/URL. Reviewed folder spellings preserve that value; generic --parent-id stays blocked because it may carry a numeric Drive dentryId."}, - "doc +list": {"scoped_aliases": {"folder-id": "folder", "parent-folder": "folder", "parent-folder-id": "folder", "parent-node-id": "folder"}, "block": ["parent-id"], "scope_strict": true, "note": "This exact Doc command expects a Doc folder nodeId/dentryUuid/URL. Reviewed folder spellings preserve that value; generic --parent-id stays blocked because it may carry a numeric Drive dentryId."}, - "doc +move": {"scoped_aliases": {"folder-id": "folder", "parent-folder": "folder", "parent-folder-id": "folder", "parent-node-id": "folder"}, "block": ["parent-id"], "scope_strict": true, "note": "This exact Doc command expects a Doc folder nodeId/dentryUuid/URL. Reviewed folder spellings preserve that value; generic --parent-id stays blocked because it may carry a numeric Drive dentryId."}, + "doc +copy": {"scoped_aliases": {"folder-id": "folder", "parent-folder": "folder", "parent-folder-id": "folder", "parent-node-id": "folder", "space": "workspace", "space-id": "workspace"}, "block": ["parent-id"], "scope_strict": true, "note": "This exact Doc command expects a Doc folder nodeId/dentryUuid/URL. Reviewed folder spellings preserve that value; generic --parent-id stays blocked because it may carry a numeric Drive dentryId. The command-scoped --space/--space-id aliases preserve the compatibility published before workspace and numeric DingDrive storage-space concepts were split; they do not make those value domains globally equivalent."}, + "doc +list": {"scoped_aliases": {"folder-id": "folder", "parent-folder": "folder", "parent-folder-id": "folder", "parent-node-id": "folder", "space": "workspace", "space-id": "workspace"}, "block": ["parent-id"], "scope_strict": true, "note": "This exact Doc command expects a Doc folder nodeId/dentryUuid/URL. Reviewed folder spellings preserve that value; generic --parent-id stays blocked because it may carry a numeric Drive dentryId. The command-scoped --space/--space-id aliases preserve the compatibility published before workspace and numeric DingDrive storage-space concepts were split; they do not make those value domains globally equivalent."}, + "doc +move": {"scoped_aliases": {"folder-id": "folder", "parent-folder": "folder", "parent-folder-id": "folder", "parent-node-id": "folder", "space": "workspace", "space-id": "workspace"}, "block": ["parent-id"], "scope_strict": true, "note": "This exact Doc command expects a Doc folder nodeId/dentryUuid/URL. Reviewed folder spellings preserve that value; generic --parent-id stays blocked because it may carry a numeric Drive dentryId. The command-scoped --space/--space-id aliases preserve the compatibility published before workspace and numeric DingDrive storage-space concepts were split; they do not make those value domains globally equivalent."}, "doc comment create": {"scoped_aliases": {"mentioned-open-conversation-ids": "mentioned-open-conversation-id", "open-conversation-id": "mentioned-open-conversation-id", "open-conversation-ids": "mentioned-open-conversation-id"}, "block": ["chat-id", "chat-ids", "conversation-id", "conversation-ids", "group-id", "group-ids"], "scope_strict": true, "note": "The target is a list of mentioned openConversationIds. Explicit open-conversation spellings preserve the list value; numeric groupId and role-free conversation spellings are rejected."}, "doc comment reply": {"scoped_aliases": {"mentioned-open-conversation-ids": "mentioned-open-conversation-id", "open-conversation-id": "mentioned-open-conversation-id", "open-conversation-ids": "mentioned-open-conversation-id"}, "block": ["chat-id", "chat-ids", "conversation-id", "conversation-ids", "group-id", "group-ids"], "scope_strict": true, "note": "The target is a list of mentioned openConversationIds. Explicit open-conversation spellings preserve the list value; numeric groupId and role-free conversation spellings are rejected."}, "doc comment update": {"scoped_aliases": {"mentioned-open-conversation-ids": "mentioned-open-conversation-id", "open-conversation-id": "mentioned-open-conversation-id", "open-conversation-ids": "mentioned-open-conversation-id"}, "block": ["chat-id", "chat-ids", "conversation-id", "conversation-ids", "group-id", "group-ids"], "scope_strict": true, "note": "The target is a list of mentioned openConversationIds. Explicit open-conversation spellings preserve the list value; numeric groupId and role-free conversation spellings are rejected."}, @@ -114,7 +125,7 @@ "doc export get": {"scoped_aliases": {"export-job-id": "job-id"}, "block": ["doc-id", "document-id", "file-id", "node", "node-id", "url"], "scope_strict": true, "note": "This command queries one export jobId. Document node identifiers are rejected; native hidden --task-id remains the command's reviewed add-only compatibility alias for --job-id."}, "doc import get": {"scoped_aliases": {"import-task-id": "task-id"}, "block": ["doc-id", "document-id", "file-id", "job-id", "node", "node-id", "url"], "scope_strict": true, "note": "This command queries one import taskId. Document node identifiers and export jobId spellings are different entities and are rejected."}, "doc +share-doc": {"block": ["doc", "doc-id", "document-id", "file-id", "id", "node", "node-id"], "note": "The real --url requires a shareable document link. Name-only normalization cannot turn a document nodeId into a URL, so identifier spellings are rejected with guidance to provide --url."}, - "doc update": {"block": ["version", "version-no", "version-number"], "note": "--revision is an optimistic-concurrency revision, not a historical document version number. Version spellings must not reduce to --revision."}, + "doc update": {"block": ["stdin", "version", "version-no", "version-number"], "note": "--revision is an optimistic-concurrency revision, not a historical document version number. Version spellings must not reduce to --revision. --stdin is not a real switch: stdin input is expressed as --content -, which requires a value-form transformation outside central name aliases."}, "report outbox list": {"block": ["template-type"], "note": "type vs name are different fields"}, "chat group members add-bot": {"bind": {"id": "open_conversation_id"}}, "chat group members list-by-ids": {"bind": {"id": "open_conversation_id", "users": "open_dingtalk_ids"}, "block": ["user-id", "user-ids"], "note": "This command's --id carries openConversationId, while --users carries an openDingTalkId list."}, @@ -158,21 +169,86 @@ "chat +messages-recall-by-bot": {"block": ["msg-id", "message-id", "open-message-id", "msg-ids", "message-ids", "open-message-ids"], "note": "--keys carries processQueryKey values, not openMessageId values."}, "chat +messages-reply": {"scoped_aliases": {"msg-id": "ref-msg-id", "open-message-id": "ref-msg-id"}, "block": ["group", "msg-ids", "message-ids", "open-message-ids"], "scope_strict": true, "note": "The observed --group spelling carried a natural group name and is blocked. The only message role is the referenced message; plural IDs are not accepted, while --chat remains a CID alias and native --message-id stays native."}, "chat +messages-resource-download": {"block": ["download-dir"], "note": "--output may be a file or directory under workspace safety rules; a download directory cannot be assumed equivalent."}, - "doc +create": {"scoped_aliases": {"folder-id": "folder", "parent-folder": "folder", "parent-folder-id": "folder", "parent-node-id": "folder"}, "block": ["content-file", "parent-id"], "scope_strict": true, "note": "--content-format is value-preservingly normalized to --doc-format. A raw --content-file path cannot become --content without adding the required @file transform, so it is blocked with guidance to use @relative-path or stable doc create."}, - "doc +create-from-template": {"scoped_aliases": {"folder-id": "folder", "parent-folder": "folder", "parent-folder-id": "folder", "parent-node-id": "folder"}, "block": ["parent-id"], "scope_strict": true, "note": "This exact Doc command expects a Doc folder nodeId/dentryUuid/URL. Reviewed folder spellings preserve that value; generic --parent-id stays blocked because it may carry a numeric Drive dentryId."}, - "doc +import": {"scoped_aliases": {"folder-id": "folder", "parent-folder": "folder", "parent-folder-id": "folder", "parent-node-id": "folder"}, "block": ["parent-id"], "scope_strict": true, "note": "This exact Doc command expects a Doc folder nodeId/dentryUuid/URL. Reviewed folder spellings preserve that value; generic --parent-id stays blocked because it may carry a numeric Drive dentryId."}, - "doc +update": {"scoped_aliases": {"mode": "command", "doc-id": "node", "document-id": "node", "file-id": "node", "node-id": "node", "url": "node"}, "block": ["content-file"], "scope_strict": true, "note": "--mode append/overwrite is the same operation selector subset as --command and preserves its value. --content-file is blocked because +update requires @relative-path or stdin and central aliases cannot read/transform a file value."}, - "doc +inspect": {"scoped_aliases": {"include-versions": "include-history"}, "block": ["include", "include-info"], "scope_strict": true, "note": "Historical versions and history are the same optional section on this exact shortcut. Generic --include needs value-dependent flag expansion, while base document info is always returned, so those spellings are rejected with precise guidance."}, - "doc +fetch": {"scoped_aliases": {"start-block": "start-block-id", "end-block": "end-block-id"}, "ambiguous": ["block-id"], "scope_strict": true, "note": "Start/end block roles are preserved. A role-free --block-id cannot choose a range/section boundary and must stop before execution."}, + "doc +access-change": {"scoped_aliases": {"space": "workspace", "space-id": "workspace"}, "scope_strict": true, "note": "Command-scoped compatibility published before the workspace/storage-space concept split: these spellings keep passing their value unchanged to this Doc command's --workspace flag and do not establish global equivalence with a numeric DingDrive storage space."}, + "doc +access-grant": {"scoped_aliases": {"space": "workspace", "space-id": "workspace"}, "block": ["target-user-id", "target-user-ids", "user-id", "user-ids"], "ambiguous": ["target-user", "user", "users"], "scope_strict": true, "note": "The real --to accepts collaborator names and resolves them before granting access. Explicit ID spellings cannot be passed through unchanged, while role-free user spellings do not prove whether their values are names or IDs. The command-scoped --space/--space-id aliases preserve previously published Doc workspace compatibility without making workspace and numeric DingDrive storage-space values globally equivalent."}, + "doc +access-revoke": {"scoped_aliases": {"space": "workspace", "space-id": "workspace"}, "scope_strict": true, "note": "Command-scoped compatibility published before the workspace/storage-space concept split: these spellings keep passing their value unchanged to this Doc command's --workspace flag and do not establish global equivalence with a numeric DingDrive storage space."}, + "doc +create": {"scoped_aliases": {"folder-id": "folder", "parent-folder": "folder", "parent-folder-id": "folder", "parent-node-id": "folder", "space": "workspace", "space-id": "workspace"}, "block": ["content-file", "parent-id"], "scope_strict": true, "note": "--content-format is value-preservingly normalized to --doc-format. A raw --content-file path cannot become --content without adding the required @file transform, so it is blocked with guidance to use @relative-path or stable doc create. The command-scoped --space/--space-id aliases preserve previously published Doc workspace compatibility without making workspace and numeric DingDrive storage-space values globally equivalent."}, + "doc +create-from-template": {"scoped_aliases": {"folder-id": "folder", "parent-folder": "folder", "parent-folder-id": "folder", "parent-node-id": "folder", "space": "workspace", "space-id": "workspace"}, "block": ["parent-id"], "scope_strict": true, "note": "This exact Doc command expects a Doc folder nodeId/dentryUuid/URL. Reviewed folder spellings preserve that value; generic --parent-id stays blocked because it may carry a numeric Drive dentryId. The command-scoped --space/--space-id aliases preserve previously published Doc workspace compatibility without making workspace and numeric DingDrive storage-space values globally equivalent."}, + "doc +import": {"scoped_aliases": {"folder-id": "folder", "parent-folder": "folder", "parent-folder-id": "folder", "parent-node-id": "folder", "space": "workspace", "space-id": "workspace"}, "block": ["parent-id"], "scope_strict": true, "note": "This exact Doc command expects a Doc folder nodeId/dentryUuid/URL. Reviewed folder spellings preserve that value; generic --parent-id stays blocked because it may carry a numeric Drive dentryId. The command-scoped --space/--space-id aliases preserve previously published Doc workspace compatibility without making workspace and numeric DingDrive storage-space values globally equivalent."}, + "doc create": {"scoped_aliases": {"space": "workspace", "space-id": "workspace"}, "scope_strict": true, "note": "Command-scoped compatibility published before the workspace/storage-space concept split: these spellings keep passing their value unchanged to this Doc command's --workspace flag and do not establish global equivalence with a numeric DingDrive storage space."}, + "doc file create": {"scoped_aliases": {"space": "workspace", "space-id": "workspace"}, "scope_strict": true, "note": "Command-scoped compatibility published before the workspace/storage-space concept split: these spellings keep passing their value unchanged to this Doc command's --workspace flag and do not establish global equivalence with a numeric DingDrive storage space."}, + "doc import": {"scoped_aliases": {"space": "workspace", "space-id": "workspace"}, "scope_strict": true, "note": "Command-scoped compatibility published before the workspace/storage-space concept split: these spellings keep passing their value unchanged to this Doc command's --workspace flag and do not establish global equivalence with a numeric DingDrive storage space."}, + "doc template apply": {"scoped_aliases": {"space": "workspace", "space-id": "workspace"}, "scope_strict": true, "note": "Command-scoped compatibility published before the workspace/storage-space concept split: these spellings keep passing their value unchanged to this Doc command's --workspace flag and do not establish global equivalence with a numeric DingDrive storage space."}, + "doc +update": {"scoped_aliases": {"mode": "command", "doc-id": "node", "document-id": "node", "file-id": "node", "node-id": "node", "url": "node"}, "block": ["content-file"], "ambiguous": ["element"], "scope_strict": true, "note": "--mode append/overwrite is the same operation selector subset as --command and preserves its value. --content-file is blocked because +update requires @relative-path or stdin and central aliases cannot read/transform a file value. --element cannot choose between document content, a block target, or an insertion reference."}, + "doc +inspect": {"scoped_aliases": {"include-access": "include-permissions", "include-member": "include-permissions", "include-members": "include-permissions", "include-versions": "include-history"}, "block": ["include", "include-blocks", "include-content", "include-info", "include-meta", "include-metadata"], "scope_strict": true, "note": "Access/member spellings denote the same optional permission list, and versions/history denote the same history section. Generic --include needs value-dependent expansion; base metadata is already returned; block/content reads belong to +fetch, so those spellings stop before fuzzy correction or dispatch."}, + "doc +fetch": {"scoped_aliases": {"start-block": "start-block-id", "end-block": "end-block-id"}, "block": ["content-format", "doc-format", "range"], "ambiguous": ["block-id"], "scope_strict": true, "note": "Start/end block roles are preserved. A role-free --block-id cannot choose a range/section boundary. --range is an invented composite argument observed alongside --scope range and cannot be split into block IDs by name-only normalization; content-format spellings do not map to the independent --detail contract."}, + "doc +export": {"block": ["wait"], "scope_strict": true, "note": "The shortcut already submits, polls, and downloads in one workflow. A generic --wait value cannot be converted into the distinct integer --max-polls contract by parameter-name normalization."}, "doc +media-download": {"scoped_aliases": {"doc": "node", "doc-id": "node", "document-id": "node", "node-id": "node"}, "ambiguous": ["file-id", "url"], "scope_strict": true, "note": "This command has both document-node and attachment-resource roles. Strong document spellings map to --node; --file-id and --url cannot safely choose between document and media identities."}, - "doc +media-insert": {"scoped_aliases": {"doc": "node", "doc-id": "node", "document-id": "node", "node-id": "node"}, "ambiguous": ["file-id", "url"], "scope_strict": true, "note": "This command has both document-node and local-media roles. Strong document spellings map to --node; --file-id and --url cannot safely choose a role."}, + "doc +media-insert": {"scoped_aliases": {"doc": "node", "doc-id": "node", "document-id": "node", "node-id": "node"}, "block": ["after-block-id", "before-block-id"], "ambiguous": ["file-id", "url"], "scope_strict": true, "note": "This command has both document-node and local-media roles. Strong document spellings map to --node; --file-id and --url cannot safely choose a role. Before/after block spellings each require expansion into both --ref-block and --where, which name-only normalization cannot perform."}, "doc +media-list": {"scoped_aliases": {"doc": "node", "doc-id": "node", "document-id": "node", "node-id": "node"}, "ambiguous": ["file-id", "url"], "scope_strict": true, "note": "The command lists media inside one document, so only strong document spellings map to --node. File and URL spellings remain role-ambiguous."}, "doc +media-preview": {"scoped_aliases": {"doc": "node", "doc-id": "node", "document-id": "node", "node-id": "node"}, "ambiguous": ["file-id", "url"], "scope_strict": true, "note": "This command has both document-node and attachment-resource roles. Strong document spellings map to --node; --file-id and --url cannot safely choose a role."}, "doc +resource-delete": {"scoped_aliases": {"doc": "node", "doc-id": "node", "document-id": "node", "node-id": "node"}, "ambiguous": ["file-id", "url"], "scope_strict": true, "note": "This command removes a document resource while targeting the document by --node. File and URL spellings do not uniquely identify that document role."}, "doc +resource-download": {"scoped_aliases": {"doc": "node", "doc-id": "node", "document-id": "node", "node-id": "node"}, "ambiguous": ["file-id", "url"], "scope_strict": true, "note": "This command downloads a document resource while targeting the document by --node. File and URL spellings do not uniquely identify that document role."}, "doc +resource-update": {"scoped_aliases": {"doc": "node", "doc-id": "node", "document-id": "node", "node-id": "node"}, "ambiguous": ["file-id", "url"], "scope_strict": true, "note": "This command has document-node, local-file, and HTTPS image URL roles. Only strong document spellings map to --node; --file-id and --url must stop as ambiguous."}, "doc +share": {"block": ["doc", "doc-id", "document-id", "file-id", "id", "node", "node-id"], "note": "The real --url requires a shareable document link. Name-only normalization cannot turn a node identifier into a URL, so identifier spellings are rejected with guidance to provide --url."}, - "doc +grant-and-share": {"scoped_aliases": {"doc": "node", "doc-id": "node", "document-id": "node", "file-id": "node", "node-id": "node"}, "scope_strict": true, "note": "This workflow has two different real URL roles: --node selects the document for access control and --url is the shareable link sent to recipients. Explicit document-ID spellings map only to --node; --url remains native."} + "doc +grant-and-share": {"scoped_aliases": {"doc": "node", "doc-id": "node", "document-id": "node", "file-id": "node", "node-id": "node", "space": "workspace", "space-id": "workspace"}, "scope_strict": true, "note": "This workflow has two different real URL roles: --node selects the document for access control and --url is the shareable link sent to recipients. Explicit document-ID spellings map only to --node; --url remains native. The command-scoped --space/--space-id aliases preserve previously published Doc workspace compatibility without making workspace and numeric DingDrive storage-space values globally equivalent."}, + "doc +version-save": {"block": ["message", "title"], "scope_strict": true, "note": "The current snapshot API accepts only the document target. Version title/message metadata are unsupported and cannot be represented by another existing flag."}, + "drive copy": {"scoped_aliases": {"document-id": "node", "dentry-uuid": "node", "directory-id": "folder", "source-node-id": "node", "source-file-id": "node", "target-folder-id": "folder", "destination-folder-id": "folder", "target-workspace-id": "workspace", "destination-workspace-id": "workspace"}, "scope_strict": true, "note": "Source node and destination folder/workspace are different roles; role-free target IDs stop before write dispatch. --name is not a rename field on this command and must not be fuzzy-corrected to --node.", "block": ["dentry-id", "destination-node-id", "dingdrive-space-id", "drive-space-id", "name", "source-folder-id", "space-id", "storage-space-id"], "ambiguous": ["destination-id", "target-id"]}, + "drive cover": {"scoped_aliases": {"document-id": "node", "dentry-uuid": "node"}, "scope_strict": true, "note": "The command accepts a Drive node ID or URL; native file-id/node-id/doc-id/url aliases stay native and are not duplicated centrally.", "block": ["dentry-id"]}, + "drive download-version": {"scoped_aliases": {"document-id": "node", "dentry-uuid": "node", "chunk-size": "part-size", "concurrency": "parallel", "parallelism": "parallel"}, "scope_strict": true, "note": "Output path, chunk size, and concurrency names preserve values; local input and remote folder roles remain blocked.", "block": ["dentry-id", "file", "file-path", "folder", "folder-id"]}, + "drive move": {"scoped_aliases": {"document-id": "node", "dentry-uuid": "node", "directory-id": "folder", "source-node-id": "node", "source-file-id": "node", "target-folder-id": "folder", "destination-folder-id": "folder", "target-workspace-id": "workspace", "destination-workspace-id": "workspace"}, "scope_strict": true, "note": "Source node and destination folder/workspace are different roles; role-free target IDs stop before write dispatch. --name is not a rename field on this command and must not be fuzzy-corrected to --node.", "block": ["dentry-id", "destination-node-id", "dingdrive-space-id", "drive-space-id", "name", "source-folder-id", "space-id", "storage-space-id"], "ambiguous": ["destination-id", "target-id"]}, + "drive permission add": {"scoped_aliases": {"document-id": "node", "dentry-uuid": "node", "member-user-ids": "users", "collaborator-ids": "users", "target-user-ids": "users", "target-node-id": "node"}, "scope_strict": true, "note": "The user list denotes target collaborators on this exact node permission command; the native singular compatibility spellings remain native.", "block": ["dentry-id", "dingdrive-space-id", "drive-space-id", "space-id", "storage-space-id"]}, + "drive permission apply": {"scoped_aliases": {"document-id": "node", "dentry-uuid": "node", "approver-user-ids": "users", "approver-ids": "users", "target-node-id": "node", "apply-reason": "reason", "notification-mode": "notify-mode"}, "scope_strict": true, "note": "The user list denotes approvers, not target collaborators; values and notification enum are passed unchanged.", "block": ["dentry-id"]}, + "drive permission apply-info": {"scoped_aliases": {"document-id": "node", "dentry-uuid": "node"}, "scope_strict": true, "note": "The command accepts a Drive node ID or URL; native file-id/node-id/doc-id/url aliases stay native and are not duplicated centrally.", "block": ["dentry-id"]}, + "drive permission list": {"scoped_aliases": {"document-id": "node", "dentry-uuid": "node", "role-filter": "filter-role", "permission-role-filter": "filter-role", "target-node-id": "node"}, "scope_strict": true, "note": "Filtering by a role is not the same as granting/updating a role.", "block": ["dentry-id", "dingdrive-space-id", "drive-space-id", "permission", "role", "space-id", "storage-space-id"]}, + "drive permission remove": {"scoped_aliases": {"document-id": "node", "dentry-uuid": "node", "member-user-ids": "users", "collaborator-ids": "users", "target-user-ids": "users", "target-node-id": "node"}, "scope_strict": true, "note": "The user list denotes target collaborators on this exact node permission command; the native singular compatibility spellings remain native.", "block": ["dentry-id", "dingdrive-space-id", "drive-space-id", "space-id", "storage-space-id"]}, + "drive permission transfer-owner": {"scoped_aliases": {"document-id": "node", "dentry-uuid": "node", "target-node-id": "node", "target-workspace-id": "workspace", "old-owner-role": "reserve-role", "keep-role": "reserve-role", "new-owner-id": "new-owner", "new-owner-user-id": "new-owner"}, "scope_strict": true, "note": "Node/workspace target and new/old owner roles are distinct on this irreversible command; role-free names stop before dispatch.", "block": ["current-owner", "dentry-id", "dingdrive-space-id", "drive-space-id", "space-id", "storage-space-id", "user-ids", "users"], "ambiguous": ["owner", "owner-user-id", "target-id", "user-id"]}, + "drive permission update": {"scoped_aliases": {"document-id": "node", "dentry-uuid": "node", "member-user-ids": "users", "collaborator-ids": "users", "target-user-ids": "users", "target-node-id": "node"}, "scope_strict": true, "note": "The user list denotes target collaborators on this exact node permission command; the native singular compatibility spellings remain native.", "block": ["dentry-id", "dingdrive-space-id", "drive-space-id", "space-id", "storage-space-id"]}, + "drive publish get": {"scoped_aliases": {"document-id": "node", "dentry-uuid": "node"}, "scope_strict": true, "note": "The command accepts a Drive node ID or URL; native file-id/node-id/doc-id/url aliases stay native and are not duplicated centrally.", "block": ["dentry-id"]}, + "drive publish set": {"scoped_aliases": {"document-id": "node", "dentry-uuid": "node", "public-permission": "permission", "public-role": "permission"}, "scope_strict": true, "note": "Internet-public permission is not the same as a direct collaborator role.", "block": ["access-role", "dentry-id", "permission-role", "role"]}, + "drive publish unset": {"scoped_aliases": {"document-id": "node", "dentry-uuid": "node"}, "scope_strict": true, "note": "The command accepts a Drive node ID or URL; native file-id/node-id/doc-id/url aliases stay native and are not duplicated centrally.", "block": ["dentry-id"]}, + "drive rename": {"scoped_aliases": {"document-id": "node", "dentry-uuid": "node", "file-name": "name", "display-name": "name", "new-name": "name"}, "scope_strict": true, "note": "The name is this exact folder/node display name; search query and local path are different roles.", "block": ["dentry-id"]}, + "drive revert": {"scoped_aliases": {"document-id": "node", "dentry-uuid": "node"}, "scope_strict": true, "note": "The command accepts a Drive node ID or URL; native file-id/node-id/doc-id/url aliases stay native and are not duplicated centrally.", "block": ["dentry-id"]}, + "drive shortcut": {"scoped_aliases": {"document-id": "node", "dentry-uuid": "node", "directory-id": "folder", "source-node-id": "node", "source-file-id": "node", "target-folder-id": "folder", "destination-folder-id": "folder", "target-workspace-id": "workspace", "destination-workspace-id": "workspace"}, "scope_strict": true, "note": "Source node and destination folder/workspace are different roles; role-free target IDs stop before write dispatch. --name is not a rename field on this command and must not be fuzzy-corrected to --node.", "block": ["dentry-id", "destination-node-id", "dingdrive-space-id", "drive-space-id", "name", "source-folder-id", "space-id", "storage-space-id"], "ambiguous": ["destination-id", "target-id"]}, + "drive star add": {"scoped_aliases": {"document-id": "node", "dentry-uuid": "node"}, "scope_strict": true, "note": "The command accepts a Drive node ID or URL; native file-id/node-id/doc-id/url aliases stay native and are not duplicated centrally.", "block": ["dentry-id"]}, + "drive star remove": {"scoped_aliases": {"document-id": "node", "dentry-uuid": "node"}, "scope_strict": true, "note": "The command accepts a Drive node ID or URL; native file-id/node-id/doc-id/url aliases stay native and are not duplicated centrally.", "block": ["dentry-id"]}, + "drive stats": {"scoped_aliases": {"document-id": "node", "dentry-uuid": "node"}, "scope_strict": true, "note": "The command accepts a Drive node ID or URL; native file-id/node-id/doc-id/url aliases stay native and are not duplicated centrally.", "block": ["dentry-id"]}, + "drive +cover": {"scoped_aliases": {"dentry-uuid": "node", "file-id": "node", "node-id": "node", "doc-id": "node", "document-id": "node", "url": "node", "document-url": "node", "id": "node"}, "block": ["dentry-id", "doc", "folder", "folder-id", "name"], "scope_strict": true, "note": "This shortcut calls the same get_cover nodeId interface as drive cover, so its reviewed ID/URL aliases are preserved unchanged. Numeric dentryId, folder-role spellings and --name remain protected."}, + "drive +copy": {"scoped_aliases": {"dentry-uuid": "node", "directory-id": "folder", "source-node-id": "node", "source-file-id": "node", "target-folder-id": "folder", "destination-folder-id": "folder", "target-workspace-id": "workspace", "destination-workspace-id": "workspace"}, "block": ["dentry-id", "destination-node-id", "dingdrive-space-id", "document-url", "drive-space-id", "name", "source-folder-id", "space-id", "storage-space-id"], "scope_strict": true, "note": "Source node and destination folder/workspace are different roles; role-free target IDs stop before write dispatch. --name is not a rename field on this command and must not be fuzzy-corrected to --node.", "ambiguous": ["destination-id", "target-id"]}, + "drive +create-folder": {"scoped_aliases": {"directory-id": "folder", "parent-directory-id": "folder", "parent-folder-id": "folder", "folder-name": "name", "display-name": "name", "new-name": "name"}, "block": ["dentry-id", "parent-id", "knowledge-base-id", "wiki-workspace-id", "workspace", "workspace-id"], "scope_strict": true, "note": "The new shortcut creates a numeric DingDrive-space folder. Folder display name, parent dentryUuid and numeric storage space are separate roles; knowledge-base workspace spellings are not accepted."}, + "drive +create-shortcut": {"scoped_aliases": {"dentry-uuid": "node", "file-id": "node", "node-id": "node", "doc-id": "node", "document-id": "node", "url": "node", "document-url": "node", "directory-id": "folder", "source-node-id": "node", "source-file-id": "node", "target-folder-id": "folder", "destination-folder-id": "folder", "target-workspace-id": "workspace", "destination-workspace-id": "workspace"}, "block": ["dentry-id", "destination-node-id", "dingdrive-space-id", "doc", "drive-space-id", "name", "source-folder-id", "space-id", "storage-space-id"], "scope_strict": true, "note": "Source node and destination folder/workspace are different roles. The source ID/URL aliases match drive shortcut and pass through unchanged; generic target/id spellings remain ambiguous, and storage-space IDs are not knowledge-base workspace IDs.", "ambiguous": ["destination-id", "id", "target-id"]}, + "drive +delete": {"scoped_aliases": {"dentry-uuid": "node", "file-id": "node", "node-id": "node", "folder": "node", "folder-id": "node"}, "block": ["dentry-id", "doc", "doc-id", "document-id", "document-url", "id", "name", "url"], "scope_strict": true, "note": "Delete targets one already confirmed Drive file or folder dentryUuid. Folder spellings are the same single target role; numeric dentryId, unreviewed Doc URL spellings and --name stop before confirmation or write dispatch."}, + "drive +download": {"scoped_aliases": {"dentry-uuid": "node", "file-id": "node", "node-id": "node"}, "block": ["dentry-id", "doc", "doc-id", "document-id", "document-url", "file", "file-path", "folder", "folder-id", "id", "knowledge-base-id", "name", "url", "wiki-workspace-id", "workspace", "workspace-id"], "scope_strict": true, "note": "The remote ordinary-file node and local output path are different roles. Node-ID spelling is accepted, while Doc URLs, local input paths, folders, knowledge-base workspaces and numeric dentryId values are not interchangeable."}, + "drive +info": {"scoped_aliases": {"dentry-uuid": "node"}, "block": ["dentry-id", "document-url", "knowledge-base-id", "wiki-workspace-id", "workspace", "workspace-id"], "scope_strict": true, "note": "This command has only a numeric DingDrive space target; knowledge-base workspace spellings are a different value domain."}, + "drive +inspect": {"scoped_aliases": {"dentry-uuid": "node", "file-id": "node", "node-id": "node", "doc-id": "node", "document-id": "node", "folder": "node", "folder-id": "node", "include-statistics": "include-stats", "include-publish-status": "include-publish", "include-public-status": "include-publish", "include-thumbnail": "include-cover"}, "block": ["dentry-id", "doc", "document-url", "id", "include", "include-content", "include-history", "include-permissions", "name", "url"], "scope_strict": true, "note": "Drive inspect accepts one file, folder or document node ID and can aggregate only stats, public-publish status and cover. URL spellings, Doc inspect section names and a generic --include remain protected because this shortcut does not declare URL input or value splitting."}, + "drive +list": {"ambiguous": ["root-id", "space"], "scoped_aliases": {"directory-id": "folder", "parent-folder-id": "folder", "order-field": "order-by", "sort-by": "order-by", "sort-field": "order-by"}, "block": ["dentry-id", "knowledge-base-id", "wiki-workspace-id", "workspace", "workspace-id"], "scope_strict": true, "note": "This shortcut accepts one numeric DingDrive space and an optional parent dentryUuid; it does not accept a knowledge-base workspace. Sort field and direction remain separate roles."}, + "drive +move": {"scoped_aliases": {"dentry-uuid": "node", "directory-id": "folder", "source-node-id": "node", "source-file-id": "node", "target-folder-id": "folder", "destination-folder-id": "folder", "target-workspace-id": "workspace", "destination-workspace-id": "workspace"}, "block": ["dentry-id", "destination-node-id", "dingdrive-space-id", "document-url", "drive-space-id", "name", "source-folder-id", "space-id", "storage-space-id"], "scope_strict": true, "note": "Source node and destination folder/workspace are different roles; role-free target IDs stop before write dispatch. --name is not a rename field on this command and must not be fuzzy-corrected to --node.", "ambiguous": ["destination-id", "target-id"]}, + "drive +publish-get": {"scoped_aliases": {"dentry-uuid": "node", "file-id": "node", "node-id": "node", "doc-id": "node", "document-id": "node", "url": "node", "document-url": "node", "id": "node"}, "block": ["dentry-id", "doc", "folder", "folder-id", "name"], "scope_strict": true, "note": "The shortcut calls the same publication-status fileId interface as drive publish get, so its reviewed ID/URL aliases are preserved unchanged. Numeric dentryId and folder/name roles remain protected."}, + "drive +publish-unset": {"scoped_aliases": {"dentry-uuid": "node", "file-id": "node", "node-id": "node", "doc-id": "node", "document-id": "node", "url": "node", "document-url": "node", "id": "node"}, "block": ["dentry-id", "doc", "folder", "folder-id", "name"], "scope_strict": true, "note": "The shortcut calls the same set_file_publish fileId interface as drive publish unset, so its reviewed ID/URL aliases are preserved unchanged. Numeric dentryId and folder/name roles remain protected before confirmation."}, + "drive +recycle-restore": {"bind": {"id": "drive_recycle_item_id"}, "block": ["file-id", "folder-id", "node", "node-id", "space-id", "workspace-id"], "scope_strict": true, "note": "The real --id is a recycleItemId returned by drive +recycle-list, not a normal Drive node ID."}, + "drive +rename": {"scoped_aliases": {"dentry-uuid": "node", "file-id": "node", "node-id": "node", "doc-id": "node", "document-id": "node", "url": "node", "document-url": "node", "id": "node", "folder": "node", "folder-id": "node", "file-name": "name", "display-name": "name", "new-name": "name"}, "block": ["dentry-id"], "scope_strict": true, "note": "The existing document, file or folder node and the requested new display name are separate required roles. Node ID/URL aliases match drive rename and pass through unchanged; numeric dentryId remains protected."}, + "drive delete": {"scoped_aliases": {"dentry-uuid": "node"}, "block": ["dentry-id", "document-url"], "scope_strict": true, "note": "This command publicly accepts an ID-only Drive node; dentry spellings preserve the value and URL-specific spellings remain protected."}, + "drive download": {"scoped_aliases": {"dentry-uuid": "node", "chunk-size": "part-size", "concurrency": "parallel", "parallelism": "parallel"}, "block": ["dentry-id", "document-url", "file", "file-path", "folder", "folder-id", "knowledge-base-id", "wiki-workspace-id", "workspace", "workspace-id"], "scope_strict": true, "note": "Output path, chunk size, and concurrency names preserve values; local input and remote folder roles remain blocked."}, + "drive info": {"scoped_aliases": {"dentry-uuid": "node", "space": "space-id", "workspace": "space-id", "workspace-id": "space-id"}, "block": ["dentry-id", "document-url", "knowledge-base-id", "wiki-workspace-id"], "scope_strict": true, "note": "This command has only a numeric DingDrive space target. Its command-scoped --space/--workspace/--workspace-id aliases preserve compatibility published before the workspace/storage-space concept split and pass the value unchanged to --space-id; new commands must not infer that knowledge-base workspace IDs and numeric storage-space IDs are globally interchangeable."}, + "drive commit": {"scoped_aliases": {"directory-id": "folder", "parent-directory-id": "folder", "upload-session-id": "upload-id", "size-bytes": "file-size", "name": "file-name", "display-name": "file-name", "filename": "file-name", "upload-name": "file-name"}, "scope_strict": true, "note": "Upload session, file name, file size in bytes, parent folder, and storage space remain separate roles.", "block": ["knowledge-base-id", "wiki-workspace-id", "workspace", "workspace-id"]}, + "drive mkdir": {"scoped_aliases": {"directory-id": "folder", "parent-directory-id": "folder", "folder-name": "name", "display-name": "name", "new-name": "name"}, "scope_strict": true, "note": "The name is this exact folder/node display name; search query and local path are different roles.", "block": ["knowledge-base-id", "wiki-workspace-id", "workspace", "workspace-id"]}, + "drive upload-info": {"scoped_aliases": {"directory-id": "folder", "parent-directory-id": "folder", "content-type": "mime-type", "size-bytes": "file-size", "name": "file-name", "display-name": "file-name", "filename": "file-name", "upload-name": "file-name"}, "scope_strict": true, "note": "File metadata aliases preserve MIME and byte units; file path and upload session are different stages.", "block": ["knowledge-base-id", "wiki-workspace-id", "workspace", "workspace-id"]}, + "drive recycle list": {"block": ["knowledge-base-id", "wiki-workspace-id", "workspace", "workspace-id"], "scope_strict": true, "note": "This command has only a numeric DingDrive space target; knowledge-base workspace spellings are a different value domain."}, + "drive search": {"ambiguous": ["end", "from", "start", "time-from", "time-to", "to", "types"], "block": ["offset", "page"], "scope_strict": true, "note": "Created/modified ranges and file/content type arrays are separate roles; generic time/type names are not guessed."}, + "drive +search": {"ambiguous": ["end", "from", "start", "time-from", "time-to", "to", "types"], "block": ["offset", "page"], "scope_strict": true, "note": "Created/modified ranges and file/content type arrays are separate roles; generic time/type names are not guessed."}, + "drive +star-add": {"scoped_aliases": {"dentry-uuid": "node", "file-id": "node", "node-id": "node", "doc-id": "node", "document-id": "node", "url": "node", "document-url": "node", "id": "node"}, "block": ["dentry-id", "doc", "folder", "folder-id", "name"], "scope_strict": true, "note": "The shortcut calls the same mark_star nodeId interface as drive star add, so its reviewed document/node ID and URL aliases are preserved unchanged. Numeric dentryId and folder/name roles remain protected."}, + "drive +star-remove": {"scoped_aliases": {"dentry-uuid": "node", "file-id": "node", "node-id": "node", "doc-id": "node", "document-id": "node", "url": "node", "document-url": "node", "id": "node"}, "block": ["dentry-id", "doc", "folder", "folder-id", "name"], "scope_strict": true, "note": "The shortcut calls the same unmark_star nodeId interface as drive star remove, so its reviewed document/node ID and URL aliases are preserved unchanged. Numeric dentryId and folder/name roles remain protected."}, + "drive +stats": {"scoped_aliases": {"dentry-uuid": "node", "file-id": "node", "node-id": "node", "doc-id": "node", "document-id": "node", "url": "node", "document-url": "node", "id": "node"}, "block": ["dentry-id", "doc", "folder", "folder-id", "name"], "scope_strict": true, "note": "The shortcut calls the same get_node_stats nodeId interface as drive stats, so its reviewed ID/URL aliases are preserved unchanged. Numeric dentryId and folder/name roles remain protected."}, + "drive +version-download": {"scoped_aliases": {"dentry-uuid": "node", "file-id": "node", "node-id": "node"}, "block": ["dentry-id", "doc", "doc-id", "document-id", "document-url", "file", "file-path", "folder", "folder-id", "id", "name", "url"], "scope_strict": true, "note": "Ordinary-file node, positive historical version number and local output path are three distinct roles; revision and Doc URL spellings must not be guessed."}, + "drive +version-get": {"scoped_aliases": {"dentry-uuid": "node", "file-id": "node", "node-id": "node"}, "block": ["dentry-id", "doc", "doc-id", "document-id", "document-url", "folder", "folder-id", "id", "name", "url"], "scope_strict": true, "note": "This command reads one ordinary-file historical version by node ID and positive version number; document revision and URL roles are different."}, + "drive +version-history": {"scoped_aliases": {"dentry-uuid": "node", "file-id": "node", "node-id": "node"}, "block": ["dentry-id", "doc", "doc-id", "document-id", "document-url", "folder", "folder-id", "id", "name", "url"], "scope_strict": true, "note": "This command pages versions for one ordinary-file Drive node; document URLs and numeric dentryId are not accepted."}, + "drive +version-revert": {"scoped_aliases": {"dentry-uuid": "node", "file-id": "node", "node-id": "node"}, "block": ["dentry-id", "doc", "doc-id", "document-id", "document-url", "folder", "folder-id", "id", "name", "url"], "scope_strict": true, "note": "The high-risk revert targets one ordinary-file Drive node and a positive historical version number. Doc revisions and URLs must stop before confirmation or write dispatch."}, + "drive recycle restore": {"bind": {"id": "drive_recycle_item_id"}, "block": ["file-id", "folder-id", "node", "node-id", "space-id", "workspace-id"], "scope_strict": true, "note": "The real --id is a recycle-item ID from recycle list, not a normal Drive node ID."}, + "drive star list": {"ambiguous": ["type", "types"], "scope_strict": true, "note": "Content types and resource types are separate arrays; a generic type list cannot choose one.", "scoped_aliases": {"order-field": "order-by", "sort-by": "order-by", "sort-field": "order-by"}}, + "drive recent": {"ambiguous": ["type", "types"], "scope_strict": true, "note": "Creator type, operation type, and file type filters are distinct and keep their enum/list forms."}, + "drive +recent": {"ambiguous": ["type", "types"], "scope_strict": true, "note": "Creator type, operation type, and file type filters are distinct and keep their enum/list forms."}, + "drive +star-list": {"ambiguous": ["type", "types"], "scope_strict": true, "note": "The shortcut exposes only the API contentTypes filter. Generic type spellings may denote file extensions or node/resource types, so the current name-only layer must not guess the value domain."}, + "drive +upload": {"ambiguous": ["destination-id", "space", "target-id"], "scoped_aliases": {"dentry-uuid": "node", "file-id": "node", "node-id": "node", "overwrite-node-id": "node", "directory-id": "folder", "parent-directory-id": "folder", "parent-folder-id": "folder", "target-folder-id": "folder", "source-file": "file", "local-file": "file", "file-path": "file", "content-type": "mime-type", "filename": "file-name", "name": "file-name", "display-name": "file-name", "upload-name": "file-name"}, "block": ["dentry-id", "document-url", "knowledge-base-id", "wiki-workspace-id", "workspace", "workspace-id", "output-path"], "scope_strict": true, "note": "Local source path, remote display name, MIME type, parent folder, numeric storage space and optional overwrite node are distinct roles. ID-suffixed file/node spellings denote the overwrite target; the shortcut does not expose a knowledge-base workspace route."} }, "validation_fixture": { @@ -243,7 +319,7 @@ {"command": "doc +export-get", "emitted": "node", "expect": "did-you-mean:blocked", "via": "override:block", "occ": 2}, {"command": "doc block delete", "emitted": "index", "expect": "did-you-mean:blocked", "via": "override:block", "occ": 2}, {"command": "doc block insert", "emitted": "before-block-id", "expect": "did-you-mean:blocked", "via": "guard:requires-multi-parameter-transform", "occ": 2}, - {"command": "drive info", "emitted": "workspace", "expect": "space-id", "via": "concept:space_id", "occ": 2}, + {"command": "drive info", "emitted": "workspace", "expect": "space-id", "via": "override:published-storage-space-compatibility", "occ": 2}, {"command": "mail folder update", "emitted": "folder-id", "expect": "id", "via": "override:bind(folder_id)", "occ": 2}, {"command": "report outbox list", "emitted": "template-type", "expect": "did-you-mean:blocked", "via": "override:block", "occ": 2}, {"command": "chat +group-members", "emitted": "group-name", "expect": "group", "via": "concept:group_name+bind"}, @@ -325,7 +401,7 @@ {"command": "doc +search", "emitted": "q", "expect": "query", "via": "concept:search_query"}, {"command": "doc +comment-list", "emitted": "max-results", "expect": "limit", "via": "concept:pagination_size"}, {"command": "doc +list", "emitted": "page-token", "expect": "cursor", "via": "concept:page_cursor"}, - {"command": "doc +copy", "emitted": "workspace-id", "expect": "workspace", "via": "concept:space_id"}, + {"command": "doc +copy", "emitted": "workspace-id", "expect": "workspace", "via": "concept:workspace_id"}, {"command": "doc +copy", "emitted": "parent-folder-id", "expect": "folder", "via": "override:scoped-doc-folder"}, {"command": "doc +copy", "emitted": "parent-id", "expect": "did-you-mean:blocked", "via": "guard:doc-folder-value-domain"}, {"command": "doc +comment-reply", "emitted": "comment-id", "expect": "comment-key", "via": "concept:doc_comment_key"}, @@ -415,9 +491,9 @@ {"command": "doc +create", "emitted": "content-format", "expect": "doc-format", "via": "concept:doc_content_format"}, {"command": "doc +create", "emitted": "content-file", "expect": "did-you-mean:blocked", "via": "guard:requires-file-read-transform"}, {"command": "doc +inspect", "emitted": "include-versions", "expect": "include-history", "via": "override:scoped-section"}, - {"command": "doc +inspect", "emitted": "include", "expect": "did-you-mean:blocked", "via": "guard:requires-value-dependent-flag-expansion"}, - {"command": "doc +inspect", "emitted": "include-info", "expect": "did-you-mean:blocked", "via": "guard:base-info-always-returned"}, - {"command": "doc +update", "emitted": "mode", "expect": "command", "via": "override:scoped-operation"}, + {"command": "doc +inspect", "emitted": "include", "expect": "did-you-mean:blocked", "via": "guard:requires-value-dependent-flag-expansion", "occ": 1}, + {"command": "doc +inspect", "emitted": "include-info", "expect": "did-you-mean:blocked", "via": "guard:base-info-always-returned", "occ": 1}, + {"command": "doc +update", "emitted": "mode", "expect": "command", "via": "override:scoped-operation", "occ": 5}, {"command": "doc +update", "emitted": "revision", "expect": "expected-revision", "via": "concept:doc_edit_revision"}, {"command": "doc +update", "emitted": "version", "expect": "did-you-mean:blocked", "via": "guard:historical-version-vs-edit-revision"}, {"command": "doc +fetch", "emitted": "start-block", "expect": "start-block-id", "via": "override:scoped-boundary-role"}, @@ -425,11 +501,154 @@ {"command": "doc +access-grant", "emitted": "doc-id", "expect": "node", "via": "concept:doc_node_id"}, {"command": "doc +history-revert", "emitted": "version-number", "expect": "version", "via": "concept:doc_version_number"}, {"command": "doc +create-from-template", "emitted": "keyword", "expect": "query", "via": "concept:search_query"}, - {"command": "doc +create-from-template", "emitted": "workspace-id", "expect": "workspace", "via": "concept:space_id"}, + {"command": "doc +create-from-template", "emitted": "workspace-id", "expect": "workspace", "via": "concept:workspace_id"}, {"command": "doc +create-from-template", "emitted": "parent-folder-id", "expect": "folder", "via": "override:scoped-doc-folder"}, {"command": "doc +media-download", "emitted": "file-id", "expect": "did-you-mean:ambiguous", "via": "guard:document-node-vs-attachment-resource-role"}, {"command": "doc +resource-update", "emitted": "url", "expect": "did-you-mean:ambiguous", "via": "guard:document-url-vs-image-url-role"}, - {"command": "doc +share", "emitted": "node-id", "expect": "did-you-mean:blocked", "via": "guard:node-id-needs-url-conversion"} + {"command": "doc +share", "emitted": "node-id", "expect": "did-you-mean:blocked", "via": "guard:node-id-needs-url-conversion"}, + {"command": "doc +copy", "emitted": "dentry-uuid", "expect": "node", "via": "concept:doc_node_id"}, + {"command": "doc info", "emitted": "dentry-id", "expect": "did-you-mean:blocked", "via": "guard:dentry-id-vs-dentry-uuid"}, + {"command": "doc create", "emitted": "knowledge-base-id", "expect": "workspace", "via": "concept:workspace_id"}, + {"command": "doc create", "emitted": "space-id", "expect": "workspace", "via": "override:published-workspace-compatibility"}, + {"command": "drive list", "emitted": "knowledge-base-id", "expect": "workspace", "via": "concept:workspace_id"}, + {"command": "drive list", "emitted": "order-field", "expect": "order-by", "via": "override:scoped-sort-field"}, + {"command": "drive info", "emitted": "dentry-uuid", "expect": "node", "via": "override:scoped-node-id"}, + {"command": "drive info", "emitted": "dentry-id", "expect": "did-you-mean:blocked", "via": "guard:dentry-id-vs-dentry-uuid"}, + {"command": "drive copy", "emitted": "target-folder-id", "expect": "folder", "via": "override:scoped-destination-folder"}, + {"command": "drive copy", "emitted": "target-id", "expect": "did-you-mean:ambiguous", "via": "guard:destination-role-required"}, + {"command": "drive search", "emitted": "created-after", "expect": "created-from", "via": "concept:created_time_start"}, + {"command": "drive search", "emitted": "created-before", "expect": "created-to", "via": "concept:created_time_end"}, + {"command": "drive search", "emitted": "modified-after", "expect": "modified-from", "via": "concept:drive_modified_time_start"}, + {"command": "drive search", "emitted": "modified-before", "expect": "modified-to", "via": "concept:drive_modified_time_end"}, + {"command": "drive search", "emitted": "creator-user-ids", "expect": "creator-uids", "via": "concept:creator_user_ids"}, + {"command": "drive permission add", "emitted": "permission-role", "expect": "role", "via": "concept:document_permission_role"}, + {"command": "drive permission list", "emitted": "role", "expect": "did-you-mean:blocked", "via": "guard:permission-role-vs-filter-role"}, + {"command": "drive upload", "emitted": "source-file", "expect": "file", "via": "override:scoped-local-file"}, + {"command": "doc +search", "emitted": "created-after", "expect": "created-from", "via": "concept:created_time_start"}, + {"command": "doc +search", "emitted": "create-time-start", "expect": "created-from", "via": "concept:created_time_start", "occ": 1}, + {"command": "doc +search", "emitted": "create-time-end", "expect": "created-to", "via": "concept:created_time_end", "occ": 1}, + {"command": "doc +search", "emitted": "creator-user-ids", "expect": "creator-uids", "via": "concept:creator_user_ids"}, + {"command": "doc +access-grant", "emitted": "permission-role", "expect": "role", "via": "concept:document_permission_role"}, + {"command": "doc +export", "emitted": "output-path", "expect": "output", "via": "concept:local_output_path"}, + {"command": "drive download", "emitted": "destination-path", "expect": "output", "via": "concept:local_output_path"}, + {"command": "drive download", "emitted": "version-number", "expect": "version", "via": "concept:doc_version_number"}, + {"command": "drive recycle restore", "emitted": "recycle-item-id", "expect": "id", "via": "concept:drive_recycle_item_id"}, + {"command": "drive upload-info", "emitted": "size-bytes", "expect": "file-size", "via": "concept:drive_file_size_bytes"}, + {"command": "drive +info", "emitted": "drive-space-id", "expect": "space-id", "via": "concept:drive_storage_space_id"}, + {"command": "drive commit", "emitted": "storage-space-id", "expect": "space-id", "via": "concept:drive_storage_space_id"}, + {"command": "drive download", "emitted": "dingdrive-space-id", "expect": "space-id", "via": "concept:drive_storage_space_id"}, + {"command": "drive info", "emitted": "drive-space-id", "expect": "space-id", "via": "concept:drive_storage_space_id"}, + {"command": "drive list", "emitted": "storage-space-id", "expect": "space-id", "via": "concept:drive_storage_space_id"}, + {"command": "drive list", "emitted": "sort-direction", "expect": "order", "via": "concept:drive_sort_direction"}, + {"command": "drive mkdir", "emitted": "dingdrive-space-id", "expect": "space-id", "via": "concept:drive_storage_space_id"}, + {"command": "drive recycle list", "emitted": "drive-space-id", "expect": "space-id", "via": "concept:drive_storage_space_id"}, + {"command": "drive upload", "emitted": "storage-space-id", "expect": "space-id", "via": "concept:drive_storage_space_id"}, + {"command": "drive upload-info", "emitted": "dingdrive-space-id", "expect": "space-id", "via": "concept:drive_storage_space_id"}, + {"command": "doc +access-grant", "emitted": "user", "expect": "did-you-mean:ambiguous", "via": "guard:collaborator-name-vs-id-value-domain", "occ": 6}, + {"command": "doc +access-grant", "emitted": "target-user", "expect": "did-you-mean:ambiguous", "via": "guard:collaborator-name-vs-id-value-domain", "occ": 1}, + {"command": "doc +access-grant", "emitted": "user-id", "expect": "did-you-mean:blocked", "via": "guard:user-id-cannot-feed-name-resolver", "occ": 1}, + {"command": "doc +access-grant", "emitted": "user-ids", "expect": "did-you-mean:blocked", "via": "guard:user-id-cannot-feed-name-resolver", "occ": 1}, + {"command": "doc +access-grant", "emitted": "target-user-id", "expect": "did-you-mean:blocked", "via": "guard:user-id-cannot-feed-name-resolver"}, + {"command": "doc +access-grant", "emitted": "target-user-ids", "expect": "did-you-mean:blocked", "via": "guard:user-id-cannot-feed-name-resolver"}, + {"command": "doc +fetch", "emitted": "content-format", "expect": "did-you-mean:blocked", "via": "guard:content-format-vs-detail-contract", "occ": 3}, + {"command": "doc +fetch", "emitted": "doc-format", "expect": "did-you-mean:blocked", "via": "guard:content-format-vs-detail-contract", "occ": 1}, + {"command": "doc +fetch", "emitted": "range", "expect": "did-you-mean:blocked", "via": "guard:composite-range-needs-block-ids", "occ": 1}, + {"command": "doc +inspect", "emitted": "include-access", "expect": "include-permissions", "via": "override:scoped-permission-section", "occ": 3}, + {"command": "doc +inspect", "emitted": "include-member", "expect": "include-permissions", "via": "override:scoped-permission-section", "occ": 1}, + {"command": "doc +inspect", "emitted": "include-members", "expect": "include-permissions", "via": "override:scoped-permission-section", "occ": 2}, + {"command": "doc +inspect", "emitted": "include-meta", "expect": "did-you-mean:blocked", "via": "guard:base-metadata-already-returned", "occ": 2}, + {"command": "doc +inspect", "emitted": "include-metadata", "expect": "did-you-mean:blocked", "via": "guard:base-metadata-already-returned", "occ": 1}, + {"command": "doc +inspect", "emitted": "include-blocks", "expect": "did-you-mean:blocked", "via": "guard:content-read-belongs-to-fetch", "occ": 1}, + {"command": "doc +inspect", "emitted": "include-content", "expect": "did-you-mean:blocked", "via": "guard:content-read-belongs-to-fetch", "occ": 1}, + {"command": "doc +inspect", "emitted": "role", "expect": "did-you-mean:blocked", "via": "guard:permission-role-vs-document-node", "occ": 1}, + {"command": "doc +copy", "emitted": "name", "expect": "did-you-mean:blocked", "via": "guard:fuzzy-name-vs-node-role", "occ": 2}, + {"command": "doc +export", "emitted": "wait", "expect": "did-you-mean:blocked", "via": "guard:workflow-wait-vs-max-polls", "occ": 1}, + {"command": "doc +media-insert", "emitted": "after-block-id", "expect": "did-you-mean:blocked", "via": "guard:requires-ref-block-and-where-expansion", "occ": 1}, + {"command": "doc +media-insert", "emitted": "before-block-id", "expect": "did-you-mean:blocked", "via": "guard:requires-ref-block-and-where-expansion"}, + {"command": "doc +update", "emitted": "content-file", "expect": "did-you-mean:blocked", "via": "guard:requires-file-read-transform", "occ": 1}, + {"command": "doc +update", "emitted": "element", "expect": "did-you-mean:ambiguous", "via": "guard:content-vs-block-vs-reference-role", "occ": 1}, + {"command": "doc update", "emitted": "stdin", "expect": "did-you-mean:blocked", "via": "guard:stdin-requires-content-dash-transform", "occ": 1}, + {"command": "doc +version-save", "emitted": "title", "expect": "did-you-mean:blocked", "via": "guard:unsupported-version-metadata", "occ": 1}, + {"command": "doc +version-save", "emitted": "message", "expect": "did-you-mean:blocked", "via": "guard:unsupported-version-metadata", "occ": 1}, + {"command": "drive +search", "emitted": "keyword", "expect": "query", "via": "concept:search_query", "occ": 1}, + {"command": "contact +search-user", "emitted": "name", "expect": "did-you-mean:blocked", "via": "guard:person-name-vs-search-query", "occ": 1}, + {"command": "drive copy", "emitted": "name", "expect": "did-you-mean:blocked", "via": "guard:fuzzy-name-vs-node-role"}, + {"command": "drive +copy", "emitted": "name", "expect": "did-you-mean:blocked", "via": "guard:fuzzy-name-vs-node-role"}, + {"command": "drive move", "emitted": "name", "expect": "did-you-mean:blocked", "via": "guard:fuzzy-name-vs-node-role"}, + {"command": "drive +move", "emitted": "name", "expect": "did-you-mean:blocked", "via": "guard:fuzzy-name-vs-node-role"}, + {"command": "drive shortcut", "emitted": "name", "expect": "did-you-mean:blocked", "via": "guard:fuzzy-name-vs-node-role"}, + {"command": "drive +cover", "emitted": "dentry-uuid", "expect": "node", "via": "override:scoped-drive-node"}, + {"command": "drive +cover", "emitted": "name", "expect": "did-you-mean:blocked", "via": "guard:fuzzy-name-vs-drive-node"}, + {"command": "drive +create-folder", "emitted": "folder-name", "expect": "name", "via": "override:scoped-folder-name"}, + {"command": "drive +create-folder", "emitted": "storage-space-id", "expect": "space-id", "via": "concept:drive_storage_space_id"}, + {"command": "drive +create-shortcut", "emitted": "source-file-id", "expect": "node", "via": "override:scoped-source-node"}, + {"command": "drive +create-shortcut", "emitted": "target-folder-id", "expect": "folder", "via": "override:scoped-target-folder"}, + {"command": "drive +create-shortcut", "emitted": "target-workspace-id", "expect": "workspace", "via": "override:scoped-target-workspace"}, + {"command": "drive +create-shortcut", "emitted": "name", "expect": "did-you-mean:blocked", "via": "guard:fuzzy-name-vs-drive-node"}, + {"command": "drive +delete", "emitted": "file-id", "expect": "node", "via": "override:scoped-drive-node"}, + {"command": "drive +delete", "emitted": "dentry-id", "expect": "did-you-mean:blocked", "via": "guard:dentry-id-vs-node-id"}, + {"command": "drive +download", "emitted": "dentry-uuid", "expect": "node", "via": "override:scoped-drive-node"}, + {"command": "drive +download", "emitted": "destination-path", "expect": "output", "via": "concept:local_output_path"}, + {"command": "drive +inspect", "emitted": "include-statistics", "expect": "include-stats", "via": "override:scoped-inspect-section"}, + {"command": "drive +inspect", "emitted": "include-history", "expect": "did-you-mean:blocked", "via": "guard:doc-inspect-section"}, + {"command": "drive +list", "emitted": "folder-id", "expect": "folder", "via": "concept:folder_id"}, + {"command": "drive +list", "emitted": "page-size", "expect": "limit", "via": "concept:pagination_size"}, + {"command": "drive +list", "emitted": "next-token", "expect": "cursor", "via": "concept:page_cursor"}, + {"command": "drive +list", "emitted": "sort-direction", "expect": "order", "via": "concept:drive_sort_direction"}, + {"command": "drive +list", "emitted": "sort-by", "expect": "order-by", "via": "override:scoped-sort-field"}, + {"command": "drive +publish-get", "emitted": "dentry-uuid", "expect": "node", "via": "override:scoped-drive-node"}, + {"command": "drive +publish-unset", "emitted": "file-id", "expect": "node", "via": "override:scoped-drive-node"}, + {"command": "drive +recycle-list", "emitted": "storage-space-id", "expect": "space-id", "via": "concept:drive_storage_space_id"}, + {"command": "drive +recycle-list", "emitted": "page-token", "expect": "cursor", "via": "concept:page_cursor"}, + {"command": "drive +recycle-restore", "emitted": "recycle-item-id", "expect": "id", "via": "override:bind(drive_recycle_item_id)"}, + {"command": "drive +recycle-restore", "emitted": "node-id", "expect": "did-you-mean:blocked", "via": "guard:recycle-item-vs-node-id"}, + {"command": "drive +rename", "emitted": "file-id", "expect": "node", "via": "override:scoped-drive-node"}, + {"command": "drive +rename", "emitted": "new-name", "expect": "name", "via": "override:scoped-display-name"}, + {"command": "drive +star-add", "emitted": "dentry-uuid", "expect": "node", "via": "override:scoped-drive-node"}, + {"command": "drive +star-list", "emitted": "max-results", "expect": "limit", "via": "concept:pagination_size"}, + {"command": "drive +star-list", "emitted": "types", "expect": "did-you-mean:ambiguous", "via": "guard:content-type-value-domain"}, + {"command": "drive +star-remove", "emitted": "file-id", "expect": "node", "via": "override:scoped-drive-node"}, + {"command": "drive +stats", "emitted": "dentry-uuid", "expect": "node", "via": "override:scoped-drive-node"}, + {"command": "drive +upload", "emitted": "source-file", "expect": "file", "via": "override:scoped-local-file"}, + {"command": "drive +upload", "emitted": "name", "expect": "file-name", "via": "override:scoped-remote-name"}, + {"command": "drive +upload", "emitted": "overwrite-node-id", "expect": "node", "via": "override:scoped-overwrite-node"}, + {"command": "drive +upload", "emitted": "workspace-id", "expect": "did-you-mean:blocked", "via": "guard:unsupported-workspace-route"}, + {"command": "drive +version-download", "emitted": "version-number", "expect": "version", "via": "concept:doc_version_number"}, + {"command": "drive +version-download", "emitted": "save-path", "expect": "output", "via": "concept:local_output_path"}, + {"command": "drive +version-get", "emitted": "version-no", "expect": "version", "via": "concept:doc_version_number"}, + {"command": "drive +version-history", "emitted": "next-cursor", "expect": "cursor", "via": "concept:page_cursor"}, + {"command": "drive +version-history", "emitted": "page-size", "expect": "limit", "via": "concept:pagination_size"}, + {"command": "drive +version-revert", "emitted": "version-number", "expect": "version", "via": "concept:doc_version_number"}, + {"command": "drive +version-revert", "emitted": "revision", "expect": "did-you-mean:blocked", "via": "guard:document-revision-vs-file-version"}, + {"command": "drive +cover", "emitted": "node-id", "expect": "node", "via": "override:node-id-parity"}, + {"command": "drive +create-shortcut", "emitted": "node-id", "expect": "node", "via": "override:node-id-parity"}, + {"command": "drive +delete", "emitted": "node-id", "expect": "node", "via": "override:node-id-parity"}, + {"command": "drive +download", "emitted": "node-id", "expect": "node", "via": "override:node-id-parity"}, + {"command": "drive +inspect", "emitted": "node-id", "expect": "node", "via": "override:node-id-parity"}, + {"command": "drive +publish-get", "emitted": "node-id", "expect": "node", "via": "override:node-id-parity"}, + {"command": "drive +publish-unset", "emitted": "node-id", "expect": "node", "via": "override:node-id-parity"}, + {"command": "drive +rename", "emitted": "node-id", "expect": "node", "via": "override:node-id-parity"}, + {"command": "drive +star-add", "emitted": "node-id", "expect": "node", "via": "override:node-id-parity"}, + {"command": "drive +star-remove", "emitted": "node-id", "expect": "node", "via": "override:node-id-parity"}, + {"command": "drive +stats", "emitted": "node-id", "expect": "node", "via": "override:node-id-parity"}, + {"command": "drive +upload", "emitted": "node-id", "expect": "node", "via": "override:node-id-parity"}, + {"command": "drive +version-download", "emitted": "node-id", "expect": "node", "via": "override:node-id-parity"}, + {"command": "drive +version-get", "emitted": "node-id", "expect": "node", "via": "override:node-id-parity"}, + {"command": "drive +version-history", "emitted": "node-id", "expect": "node", "via": "override:node-id-parity"}, + {"command": "drive +version-revert", "emitted": "node-id", "expect": "node", "via": "override:node-id-parity"}, + {"command": "drive +cover", "emitted": "url", "expect": "node", "via": "override:existing-command-parity"}, + {"command": "drive +create-shortcut", "emitted": "document-id", "expect": "node", "via": "override:existing-command-parity"}, + {"command": "drive +delete", "emitted": "folder-id", "expect": "node", "via": "override:single-folder-node-role"}, + {"command": "drive +inspect", "emitted": "document-id", "expect": "node", "via": "override:document-node-id"}, + {"command": "drive +inspect", "emitted": "folder-id", "expect": "node", "via": "override:single-folder-node-role"}, + {"command": "drive +publish-get", "emitted": "url", "expect": "node", "via": "override:existing-command-parity"}, + {"command": "drive +publish-unset", "emitted": "document-url", "expect": "node", "via": "override:existing-command-parity"}, + {"command": "drive +rename", "emitted": "document-id", "expect": "node", "via": "override:existing-command-parity"}, + {"command": "drive +rename", "emitted": "folder-id", "expect": "node", "via": "override:single-folder-node-role"}, + {"command": "drive +star-add", "emitted": "url", "expect": "node", "via": "override:existing-command-parity"}, + {"command": "drive +star-remove", "emitted": "doc-id", "expect": "node", "via": "override:existing-command-parity"}, + {"command": "drive +stats", "emitted": "document-url", "expect": "node", "via": "override:existing-command-parity"}, + {"command": "drive +upload", "emitted": "file-id", "expect": "node", "via": "override:overwrite-node-id-role"} ] } } diff --git a/scripts/ci/run-app-race-tests.sh b/scripts/ci/run-app-race-tests.sh new file mode 100755 index 00000000..9922e9ab --- /dev/null +++ b/scripts/ci/run-app-race-tests.sh @@ -0,0 +1,126 @@ +#!/bin/sh +set -eu + +ROOT="$(CDPATH= cd -- "$(dirname -- "$0")/../.." && pwd)" + +usage() { + printf '%s\n' \ + "usage: $0 verify " \ + " $0 run " >&2 + exit 2 +} + +[ "$#" -eq 2 ] || usage +mode="$1" +app_package="$2" + +case "$mode" in + verify|run) ;; + *) usage ;; +esac + +workdir="$(mktemp -d "${TMPDIR:-/tmp}/dws-app-race-tests.XXXXXX")" +trap 'rm -rf "$workdir"' EXIT HUP INT TERM + +tests="$workdir/tests" +duplicates="$workdir/duplicates" +list_output="$workdir/list-output" + +cd "$ROOT" +if ! go test "$app_package" -list '^(Test|Example|Fuzz)' > "$list_output"; then + printf 'app race partition discovery failed for %s\n' "$app_package" >&2 + exit 1 +fi +awk '/^(Test|Example|Fuzz)/ { print $1 }' "$list_output" > "$tests" + +if [ ! -s "$tests" ]; then + printf 'app race partition discovery found no tests in %s\n' "$app_package" >&2 + exit 1 +fi + +LC_ALL=C sort "$tests" | uniq -d > "$duplicates" +if [ -s "$duplicates" ]; then + printf '%s\n' 'app race partition discovery found duplicate top-level tests:' >&2 + sed 's/^/ /' "$duplicates" >&2 + exit 1 +fi + +schema_count=0 +ab_count=0 +c_count=0 +dr_count=0 +sz_count=0 +unmatched_count=0 + +while IFS= read -r test_name; do + case "$test_name" in + Test*Schema*) schema_count=$((schema_count + 1)) ;; + Test[A-B]*) ab_count=$((ab_count + 1)) ;; + TestC*) c_count=$((c_count + 1)) ;; + Test[D-R]*) dr_count=$((dr_count + 1)) ;; + Test[S-Z]*|Example*|Fuzz*) sz_count=$((sz_count + 1)) ;; + *) + printf 'unmatched app race test: %s\n' "$test_name" >&2 + unmatched_count=$((unmatched_count + 1)) + ;; + esac +done < "$tests" + +if [ "$unmatched_count" -ne 0 ]; then + exit 1 +fi + +for partition in \ + "schema:$schema_count" \ + "a-b:$ab_count" \ + "c:$c_count" \ + "d-r:$dr_count" \ + "s-z-example-fuzz:$sz_count" +do + name="${partition%%:*}" + count="${partition#*:}" + if [ "$count" -eq 0 ]; then + printf 'app race partition %s is empty\n' "$name" >&2 + exit 1 + fi +done + +total_count="$(wc -l < "$tests" | tr -d ' ')" +assigned_count=$((schema_count + ab_count + c_count + dr_count + sz_count)) +if [ "$assigned_count" -ne "$total_count" ]; then + printf 'app race partitions assigned %s tests, want %s\n' "$assigned_count" "$total_count" >&2 + exit 1 +fi + +printf 'app race partitions cover %s top-level tests exactly once: schema=%s a-b=%s c=%s d-r=%s s-z-example-fuzz=%s\n' \ + "$total_count" "$schema_count" "$ab_count" "$c_count" "$dr_count" "$sz_count" + +if [ "$mode" = "verify" ]; then + exit 0 +fi + +run_partition() { + name="$1" + run_pattern="$2" + skip_pattern="${3:-}" + + printf 'running internal/app race partition %s\n' "$name" + if [ -n "$skip_pattern" ]; then + go test -v -race -count=1 -timeout=15m \ + -run "$run_pattern" -skip "$skip_pattern" "$app_package" + else + go test -v -race -count=1 -timeout=15m \ + -run "$run_pattern" "$app_package" + fi +} + +# Schema assembly has the largest transient memory footprint. Run those tests +# in a fresh process, then keep each remaining name range in its own process so +# command trees retained by process-global registries are released between +# partitions. The complementary run/skip patterns preserve the full test set. +schema_pattern='^Test.*Schema' +run_partition schema "$schema_pattern" +run_partition a-b '^Test[A-B]' "$schema_pattern" +run_partition c '^TestC' "$schema_pattern" +run_partition d-r '^Test[D-R]' "$schema_pattern" +run_partition s-z-example-fuzz '^(Test[S-Z]|Example|Fuzz)' "$schema_pattern" diff --git a/test/scripts/changelog_pr_gate_test.go b/test/scripts/changelog_pr_gate_test.go index 3dca2ca0..b94b25c3 100644 --- a/test/scripts/changelog_pr_gate_test.go +++ b/test/scripts/changelog_pr_gate_test.go @@ -6,6 +6,7 @@ import ( "path/filepath" "strings" "testing" + "time" ) type changelogGateRepo struct { @@ -71,6 +72,20 @@ func newChangelogGateRepo(t *testing.T) *changelogGateRepo { t.Fatalf("Abs(repo root) error = %v", err) } root := t.TempDir() + // Git may briefly continue automatic repository maintenance after a command + // exits on hosted runners. Proactively remove the fixture with bounded + // retries before testing.TempDir performs its strict final cleanup. + t.Cleanup(func() { + var cleanupErr error + for attempt := 0; attempt < 10; attempt++ { + cleanupErr = os.RemoveAll(root) + if cleanupErr == nil { + return + } + time.Sleep(time.Duration(attempt+1) * 10 * time.Millisecond) + } + t.Errorf("RemoveAll changelog gate repository after retries: %v", cleanupErr) + }) for _, path := range []string{ "LICENSE", @@ -108,6 +123,8 @@ func newChangelogGateRepo(t *testing.T) *changelogGateRepo { changelogGateWrite(t, root, "CHANGELOG.md", changelogGateBase, 0o644) changelogGateGit(t, root, "init", "-b", "main") + changelogGateGit(t, root, "config", "gc.auto", "0") + changelogGateGit(t, root, "config", "maintenance.auto", "false") changelogGateGit(t, root, "config", "user.name", "Changelog Gate Test") changelogGateGit(t, root, "config", "user.email", "changelog-gate@example.com") changelogGateGit(t, root, "add", ".") @@ -973,7 +990,7 @@ if (!isInterfaceSensitive("internal/corecmd/corecmd.go")) { } } -func TestHighRiskClassificationShardsHelperChanges(t *testing.T) { +func TestHighRiskClassificationShardsFanoutChanges(t *testing.T) { root, err := filepath.Abs(filepath.Join("..", "..")) if err != nil { t.Fatalf("Abs(repo root) error = %v", err) @@ -996,6 +1013,18 @@ if (!isHighRisk("internal/helpers/minutes.go")) { if (isHighRisk("internal/helpersx/minutes.go")) { throw new Error("helper high-risk classification must respect the path boundary"); } +for (const filename of [ + "internal/cli/param_concepts.json", + "internal/cli/param_concepts.schema.json", + "internal/cli/param_aliases_generated.go", +]) { + if (!isHighRisk(filename)) { + throw new Error(filename + " must use the sharded full suite"); + } +} +if (isHighRisk("internal/cli/param_concepts.json.bak")) { + throw new Error("parameter-alias high-risk classification must use exact paths"); +} ` cmd := exec.Command("node", "-e", probe) if output, err := cmd.CombinedOutput(); err != nil { @@ -1191,11 +1220,17 @@ func TestChangelogPRFastPathWorkflowContract(t *testing.T) { t.Fatal("Code Admission workflow missing race test job boundaries") } raceJob := admission[raceStart:raceEnd] - // Full race shards use a dynamic package-level timeout: default/floor 12m, - // with cli/smoke raised to 15m for NewRootCommand / Schema assembly under -race. + // The app shard uses independently bounded test processes so process-global + // command registries are released before the Schema assembly peak. Other full + // race shards retain the dynamic package timeout: default/floor 12m, with + // cli/smoke raised to 15m on slower hosted runners. for _, want := range []string{ + `if [ "$TEST_SHARD" = "app" ]; then`, + `test "${#packages[@]}" -eq 1`, + `./scripts/ci/run-app-race-tests.sh run "${packages[0]}"`, "timeout_budget=12m", - `if [ "$TEST_SHARD" = "cli" ] || [ "$TEST_SHARD" = "smoke" ]; then`, + `if [ "$TEST_SHARD" = "cli" ] ||`, + `[ "$TEST_SHARD" = "smoke" ]; then`, "timeout_budget=15m", `go test -v -race -count=1 -timeout="$timeout_budget" "${packages[@]}"`, "- smoke", diff --git a/test/scripts/ci_test_package_plan_test.go b/test/scripts/ci_test_package_plan_test.go index 51a1943a..136ce8ec 100644 --- a/test/scripts/ci_test_package_plan_test.go +++ b/test/scripts/ci_test_package_plan_test.go @@ -47,6 +47,25 @@ func TestCITestPackagePlanRoutesPublicTestSuites(t *testing.T) { } } +func TestCIAppRacePartitionsCoverTopLevelTestsExactlyOnce(t *testing.T) { + root := testPackagePlanRoot(t) + packages := strings.Fields(runTestPackagePlan(t, root, "list", "app")) + if len(packages) != 1 { + t.Fatalf("app package shard = %v, want exactly one package", packages) + } + + script := filepath.Join(root, "scripts", "ci", "run-app-race-tests.sh") + cmd := exec.Command("sh", script, "verify", packages[0]) + cmd.Dir = root + output, err := cmd.CombinedOutput() + if err != nil { + t.Fatalf("%s verify %s failed: %v\n%s", script, packages[0], err, output) + } + if !strings.Contains(string(output), "top-level tests exactly once") { + t.Fatalf("verify output = %q, want exact coverage summary", output) + } +} + func TestCITestPackagePlanFailsClosedWhenGoListFails(t *testing.T) { root := testPackagePlanRoot(t) fakeBin := t.TempDir()