Compare commits

..
Author SHA1 Message Date
Dennis 258e7ed872 fix: align doc rename schema contract 2026-07-28 14:30:29 +08:00
Dennis 69d813afef fix: address contract review feedback 2026-07-28 12:09:57 +08:00
Dennis 00305d941d fix: preserve document info schema compatibility 2026-07-28 11:37:32 +08:00
Dennis ec7fdb0f0d fix: harden dws contract edge cases 2026-07-28 11:36:44 +08:00
github-actions[bot] a8e83e5e7e Merge pull request #804 from wxianfeng/feature/aone84760010-qwenwork-agent-host
feat: add agent host observation metadata
2026-07-28 03:02:43 +00:00
修雨 488d90b73c Merge branch 'main' into feature/aone84760010-qwenwork-agent-host 2026-07-28 10:51:27 +08:00
修雨 2c10be2a1a feat(schema): publish 210 built-in shortcuts (#802)
Publishes all 210 public built-in shortcuts as reviewed Agent-visible
leaf tools across 16 product groups, with stable canonical identities,
executable +shortcut CLI paths, parameter and cross-parameter
constraints, selection guidance, interface metadata, and runtime-aligned
safety/confirmation semantics. Catalog grows from 603 to 813 tools.
2026-07-28 00:11:29 +08:00
wxianfeng 3985c4c98f feat: add agent host observation metadata (Aone 84760010) 2026-07-27 22:09:58 +08:00
wxianfeng 196bf929c1 Merge remote-tracking branch 'upstream/main' 2026-07-27 20:50:49 +08:00
github-actions[bot] 2dfc39f0d3 Merge pull request #790 from wxianfeng/feature/dws-event-im-phase3
feat(event): add multi-event and group lifecycle subscriptions
2026-07-27 10:00:25 +00:00
wxianfeng 97a16c43b4 fix(event): harden targeted consumer stop 2026-07-27 17:50:31 +08:00
wxianfeng afe7d860ff Merge remote-tracking branch 'upstream/main' 2026-07-27 15:57:39 +08:00
wxianfeng 63b3e72fad test(event): close coverage gate gaps 2026-07-27 15:35:49 +08:00
wxianfeng 984529b4cb test(event): cover multi-event edge paths 2026-07-27 14:59:00 +08:00
wxianfeng 298ea5b341 Merge remote-tracking branch 'upstream/main' into feature/dws-event-im-phase3
# Conflicts:
#	CHANGELOG.md
2026-07-27 13:47:23 +08:00
github-actions[bot] 3e4886fc71 chore: update beta formula for v1.0.55-beta.4 [skip ci] 2026-07-27 03:32:08 +00:00
修雨 72cb8f188e ci: trigger code admission on main after bot merges 2026-07-27 11:16:24 +08:00
github-actions[bot] 2f8614aa1f Merge pull request #798 from DingTalk-Real-AI/changelog-v1.0.55-beta.4
chore(release): prepare v1.0.55-beta.4
2026-07-27 10:58:01 +08:00
修雨 0e60d09980 chore(release): prepare v1.0.55-beta.4 2026-07-27 10:26:32 +08:00
github-actions[bot] 4d182dea45 Merge pull request #795 from DingTalk-Real-AI/codex/fix-chat-bots-projection
fix(shortcut): prevent projection data loss
2026-07-27 10:18:14 +08:00
修雨 f56d3263e8 chore: extend changelog entry and align npm propagation test with workflow
- CHANGELOG [Unreleased] entry now covers all three projection fixes
- npm dist-tag propagation test expects 60 attempts, matching release.yml
2026-07-26 21:49:12 +08:00
Dennis 22c94900d7 docs(changelog): note shortcut projection fix 2026-07-26 16:58:10 +08:00
Dennis 0871c5d88c fix(shortcut): preserve bot search and mail thread fields 2026-07-26 16:19:21 +08:00
Dennis 15a15a1c12 fix(shortcut): preserve chat bots projection 2026-07-26 15:48:21 +08:00
修雨 5c01ae845f fix: move event changelog entry to [Unreleased] + update npm propagation test
- Add missing ## [Unreleased] heading required by Policy CI check
- Update npm test assertion (12 → 60) to match extended propagation wait
2026-07-25 09:44:39 +08:00
修雨 36b58d08b0 Merge branch 'main' into feature/dws-event-im-phase3 2026-07-25 09:33:53 +08:00
修雨 0cc049eaa1 fix(release): handle Gitee API 200 null response for missing releases
Gitee API returns HTTP 200 with null body when a release tag doesn't
exist, unlike GitHub which returns 404. Treat empty release_id as
"no release exists" instead of erroring out.
2026-07-24 18:58:30 +08:00
修雨 dd2d91ae7e feat(ci): add release asset sync to Gitee mirror workflow
Add `sync_release_version` input to mirror-to-gitee.yml for ad-hoc
release asset synchronization that bypasses the release.yml verify
gate when tag metadata cannot be updated.
2026-07-24 18:54:47 +08:00
修雨 98309fa239 fix(ci): increase npm CDN propagation timeout with incremental backoff
npm registry behind CDN can take 1-5 minutes for dist-tag to propagate
to edge nodes. Extend max attempts from 12 to 60 and use incremental
backoff: 5s for first 12 attempts, then 10s.
2026-07-24 18:48:08 +08:00
修雨 b4e92f4e65 chore(release): prepare v1.0.55-beta.3 2026-07-24 18:48:03 +08:00
修雨 b34bbc9aa0 ci: enforce beta and stable release roles (#791) 2026-07-24 18:15:55 +08:00
wxianfeng 3749c6b793 docs: update changelog for personal events 2026-07-24 17:59:19 +08:00
github-actions[bot] 40444a6f78 chore: update beta formula for v1.0.55-beta.3 [skip ci] 2026-07-24 09:35:06 +00:00
wxianfeng e2390c3385 Merge remote-tracking branch 'upstream/main' into feature/dws-event-im-phase3
# Conflicts:
#	internal/cli/schema_agent_metadata/index.json
#	internal/cli/schema_agent_metadata_audit.json
#	internal/cli/schema_catalog.json
#	skills/mono/SKILL.md
2026-07-24 16:31:29 +08:00
wxianfeng 125bc88d52 fix(event): switch personal defaults to production 2026-07-24 15:40:37 +08:00
炳昱 6b9fcf9289 fix(event): preserve nested message context when flattened 2026-07-24 14:26:34 +08:00
wxianfeng 5a3617c21d feat(event): flatten group member events 2026-07-23 15:30:49 +08:00
wxianfeng 1c9b09b23f Merge remote-tracking branch 'upstream/main' into feature/dws-event-im-phase3
# Conflicts:
#	internal/app/event_command.go
#	internal/app/event_personal_command.go
#	internal/cli/schema_agent_metadata/event.json
#	internal/cli/schema_agent_metadata/index.json
#	internal/cli/schema_agent_metadata_audit.json
#	internal/cli/schema_catalog.json
#	internal/cli/schema_hints/selection/event.json
#	internal/event/personal/registry_test.go
#	skills/mono/references/products/event.md
#	skills/multi/dingtalk-event/SKILL.md
#	skills/multi/dingtalk-event/references/event-im.md
2026-07-22 11:54:32 +08:00
wxianfeng 4cded1ef6c Merge remote-tracking branch 'upstream/main' 2026-07-21 19:24:43 +08:00
炳昱 0182060757 fix(skill): advertise group member event triggers 2026-07-21 17:05:43 +08:00
wxianfeng c9cf1cc9c1 feat(event): support multi-event consume 2026-07-21 16:59:36 +08:00
炳昱 b234015e7f feat(event): add group member lifecycle events 2026-07-20 16:10:47 +08:00
wxianfeng ca6e520610 chore(event): default personal events to pre-release 2026-07-20 11:47:55 +08:00
wxianfeng b731050dad feat(event): add all-message and group lifecycle events 2026-07-20 11:32:46 +08:00
wxianfeng bb2dd2ba76 Merge remote-tracking branch 'upstream/main' into feature/dws-event-im-phase3 2026-07-20 10:08:19 +08:00
wxianfeng 049af9fc30 Merge remote-tracking branch 'upstream/main' 2026-07-17 17:38:04 +08:00
wxianfeng d19a15a6ed Merge branch 'main' of github.com:wxianfeng/dingtalk-workspace-cli
# Conflicts:
#	.github/badges/coverage.svg
2026-07-17 17:36:26 +08:00
github-actions[bot] e85d9bc314 chore: update coverage badge [skip ci] 2026-07-13 02:36:36 +00:00
155 changed files with 190448 additions and 1097 deletions
+44 -1
View File
@@ -14,6 +14,11 @@ on:
schedule:
- cron: '0 18 * * *'
workflow_dispatch:
inputs:
sync_release_version:
description: "Sync a specific release version's assets to Gitee (e.g. v1.0.55-beta.3)"
required: false
type: string
concurrency:
group: gitee-code-mirror
@@ -23,7 +28,6 @@ jobs:
mirror:
runs-on: ubuntu-latest
if: ${{ github.ref_name == github.event.repository.default_branch && github.repository_owner == 'DingTalk-Real-AI' }}
# GitHub Actions 不允许在 job-level if 直接引用 secrets,故先用 env 暴露再在 step 守卫。
env:
GITEE_TOKEN: ${{ secrets.GITEE_TOKEN }}
GITEE_USER: ${{ secrets.GITEE_USER }}
@@ -76,3 +80,42 @@ jobs:
# main 镜像对齐;release tag 由 release.yml 单独校验后创建,禁止在这里 force。
git push --force "$REMOTE" 'gitee-main:refs/heads/main'
echo "✅ 已镜像 main(含 Gitee README 本地化)到 Gitee ${GITEE_REPO}"
- name: Download GitHub Release assets
if: ${{ inputs.sync_release_version != '' }}
env:
VERSION: ${{ inputs.sync_release_version }}
GH_TOKEN: ${{ github.token }}
run: |
set -eu
echo "📥 Downloading release assets for ${VERSION}"
mkdir -p dist
gh release download "$VERSION" \
--repo "$GITHUB_REPOSITORY" \
--dir dist \
--pattern 'dws-*' \
--pattern 'checksums.txt' \
--clobber
ls -la dist/
- name: Verify release artifacts
if: ${{ inputs.sync_release_version != '' }}
env:
VERSION: ${{ inputs.sync_release_version }}
run: |
set -eu
DWS_PACKAGE_DIST_DIR="$GITHUB_WORKSPACE/dist" \
./scripts/release/verify-release-artifacts.sh "$VERSION"
- name: Sync release assets to Gitee
if: ${{ inputs.sync_release_version != '' }}
env:
VERSION: ${{ inputs.sync_release_version }}
GITEE_TOKEN: ${{ secrets.GITEE_TOKEN }}
GITEE_USER: ${{ secrets.GITEE_USER }}
GITEE_REPO: ${{ secrets.GITEE_REPO }}
DIST_DIR: ${{ github.workspace }}/dist
run: |
set -eu
echo "📦 Syncing release assets for ${VERSION} to Gitee ${GITEE_REPO}"
./scripts/release/sync-to-gitee.sh
+137 -18
View File
@@ -2,9 +2,6 @@ name: Release
run-name: ${{ github.event_name == 'workflow_dispatch' && inputs.release_operation != 'none' && format('Release {0} {1} ({2})', inputs.release_operation, inputs.release_channel, inputs.release_bump) || github.event_name == 'workflow_dispatch' && inputs.governance_preflight_nonce != '' && format('Release governance preflight {0}', inputs.governance_preflight_nonce) || github.event_name == 'workflow_dispatch' && inputs.recover_release_version != '' && format('Release recovery {0} at {1} {2}', inputs.recover_release_version, inputs.recover_release_commit, inputs.recover_release_nonce) || github.event_name == 'workflow_dispatch' && inputs.repair_npm_version != '' && format('Release npm repair {0}', inputs.repair_npm_version) || github.event_name == 'workflow_dispatch' && inputs.repair_gitee_version != '' && format('Release Gitee repair {0}', inputs.repair_gitee_version) || github.event_name == 'workflow_dispatch' && inputs.repair_oss_version != '' && format('Release OSS repair {0}', inputs.repair_oss_version) || github.workflow }}
on:
push:
tags:
- "v*"
workflow_dispatch:
inputs:
release_operation:
@@ -33,10 +30,6 @@ on:
- patch
- minor
- major
release_confirmation:
description: "For publish, type PUBLISH beta or PUBLISH stable"
required: false
type: string
repair_npm_version:
description: "Recover npm for a public immutable release without moving latest/beta"
required: false
@@ -122,7 +115,6 @@ jobs:
RELEASE_OPERATION: ${{ inputs.release_operation }}
RELEASE_CHANNEL: ${{ inputs.release_channel }}
RELEASE_BUMP: ${{ inputs.release_bump }}
RELEASE_CONFIRMATION: ${{ inputs.release_confirmation }}
run: |
set -eu
release_flow=0
@@ -157,16 +149,8 @@ jobs:
exit 1
esac
if test "$RELEASE_OPERATION" = plan; then
test -z "$RELEASE_CONFIRMATION" || {
echo "release_confirmation must be empty for a plan" >&2
exit 1
}
echo "mode=plan_release" >> "$GITHUB_OUTPUT"
elif test "$RELEASE_OPERATION" = publish; then
test "$RELEASE_CONFIRMATION" = "PUBLISH $RELEASE_CHANNEL" || {
echo "release_confirmation must be exactly: PUBLISH $RELEASE_CHANNEL" >&2
exit 1
}
echo "mode=create_release" >> "$GITHUB_OUTPUT"
else
echo "release_operation must be none, plan, or publish" >&2
@@ -219,6 +203,53 @@ jobs:
echo "mode=recover_release" >> "$GITHUB_OUTPUT"
fi
- name: Authorize repository release requester
if: ${{ steps.mode.outputs.mode == 'plan_release' || steps.mode.outputs.mode == 'create_release' }}
uses: actions/github-script@v7
env:
EXPECTED_REPOSITORY: DingTalk-Real-AI/dingtalk-workspace-cli
REQUESTED_BY: ${{ github.actor }}
TRIGGERED_BY: ${{ github.triggering_actor }}
with:
script: |
const expectedRepository = process.env.EXPECTED_REPOSITORY;
if (context.payload.repository.full_name !== expectedRepository) {
core.setFailed(`release requests are restricted to ${expectedRepository}`);
return;
}
const actors = new Set([
process.env.REQUESTED_BY,
process.env.TRIGGERED_BY,
]);
for (const username of actors) {
if (!/^[A-Za-z0-9](?:[A-Za-z0-9-]{0,38})$/.test(username || "")) {
core.setFailed("release request contains an invalid GitHub actor");
return;
}
let permission;
try {
const response = await github.rest.repos.getCollaboratorPermissionLevel({
...context.repo,
username,
});
permission = response.data.permission;
} catch (error) {
if (error.status === 404) {
core.setFailed(`${username} is not an authorized repository collaborator`);
return;
}
throw error;
}
// GitHub maps maintain to the legacy write permission here.
if (!["write", "admin"].includes(permission)) {
core.setFailed(
`${username} needs repository write, maintain, or admin access to release`,
);
return;
}
}
governance-preflight:
name: Release governance preflight
needs: dispatch-contract
@@ -1836,7 +1867,7 @@ jobs:
set -eu
. ./scripts/release/release-lib.sh
attempt=1
max_attempts=12
max_attempts=60
while :; do
delivered="$(npm view dingtalk-workspace-cli "dist-tags.$NPM_TAG" \
--registry=https://registry.npmjs.org --prefer-online)"
@@ -1854,7 +1885,12 @@ jobs:
exit 1
fi
echo "npm $NPM_TAG still reports older v$delivered; waiting for registry propagation ($attempt/$max_attempts)."
sleep 5
# incremental backoff: 5s for first 12 attempts (1 min), then 10s
if [ "$attempt" -lt 12 ]; then
sleep 5
else
sleep 10
fi
attempt=$((attempt + 1))
done
@@ -2841,15 +2877,98 @@ jobs:
name: Seal cloud release tag
needs: [dispatch-contract, governance-preflight, release-plan]
if: ${{ !cancelled() && needs.dispatch-contract.result == 'success' && needs.dispatch-contract.outputs.mode == 'create_release' && needs.governance-preflight.result == 'success' && needs.release-plan.result == 'success' }}
environment:
name: ${{ inputs.release_channel == 'stable' && 'release-stable' || 'release-beta' }}
runs-on: ubuntu-latest
timeout-minutes: 5
permissions:
actions: read
contents: write
outputs:
release_version: ${{ steps.seal.outputs.release_version }}
release_commit: ${{ steps.seal.outputs.release_commit }}
release_tag_object: ${{ steps.seal.outputs.release_tag_object }}
steps:
- name: Verify release environment governance before sealing
uses: actions/github-script@v7
env:
RELEASE_ENVIRONMENT: ${{ inputs.release_channel == 'stable' && 'release-stable' || 'release-beta' }}
with:
script: |
const { owner, repo } = context.repo;
const environmentName = process.env.RELEASE_ENVIRONMENT;
const expectedRepository = "DingTalk-Real-AI/dingtalk-workspace-cli";
if (
context.payload.repository.full_name !== expectedRepository ||
!["release-beta", "release-stable"].includes(environmentName)
) {
core.setFailed("release environment validation is not bound to an official channel");
return;
}
const response = await github.request(
"GET /repos/{owner}/{repo}/environments/{environment_name}",
{ owner, repo, environment_name: environmentName },
);
const environment = response.data;
const branchPolicy = environment.deployment_branch_policy;
const protectionRules = environment.protection_rules || [];
const reviewerRules = protectionRules.filter(
(rule) => rule.type === "required_reviewers",
);
const waitRules = protectionRules.filter(
(rule) => rule.type === "wait_timer",
);
if (
environment.name !== environmentName ||
environment.can_admins_bypass !== false ||
branchPolicy?.protected_branches !== true ||
branchPolicy?.custom_branch_policies !== false ||
waitRules.length !== 0
) {
core.setFailed(`${environmentName} does not match the required release policy`);
return;
}
if (environmentName === "release-beta") {
if (reviewerRules.length !== 0) {
core.setFailed("release-beta must not require a manual reviewer");
}
return;
}
if (
reviewerRules.length !== 1 ||
reviewerRules[0].prevent_self_review !== true ||
!Array.isArray(reviewerRules[0].reviewers) ||
reviewerRules[0].reviewers.length === 0
) {
core.setFailed(
"release-stable requires a non-self reviewing administrator",
);
return;
}
for (const reviewer of reviewerRules[0].reviewers) {
if (
reviewer.type !== "User" ||
!reviewer.reviewer?.login
) {
core.setFailed("release-stable reviewers must be repository administrators");
return;
}
const permission = await github.rest.repos.getCollaboratorPermissionLevel({
owner,
repo,
username: reviewer.reviewer.login,
});
if (permission.data.permission !== "admin") {
core.setFailed(
`${reviewer.reviewer.login} is no longer a repository administrator`,
);
return;
}
}
- name: Create one immutable annotated release tag
id: seal
uses: actions/github-script@v7
+21 -13
View File
@@ -4,6 +4,27 @@ All notable changes to this project will be documented in this file.
The format is inspired by [Keep a Changelog](https://keepachangelog.com/) and this project follows [Semantic Versioning](https://semver.org/).
## [Unreleased]
### Added
- **Expanded personal event consumption** (#790) — adds eight IM personal event keys, supports subscribing to and consuming multiple event keys in one `dws event consume` invocation, and adds targeted local-consumer shutdown when a subscription is stopped so other consumers can continue on the shared event bus.
- **Shortcut Runtime Schema delivery** — publishes all 210 public built-in shortcuts as reviewed Agent-visible leaf tools across 16 product groups, with stable canonical identities, executable `+shortcut` CLI paths, parameter and cross-parameter constraints, selection guidance, interface metadata, and runtime-aligned safety/confirmation semantics. `dws shortcut list` remains the lightweight batch-discovery view, while leaf Schema now carries the complete Agent contract; declared string-slice defaults are also preserved consistently in Cobra and Schema.
## [1.0.55-beta.4] - 2026-07-27
This beta validates the shortcut projection fixes for group bots, bot search,
and mail threads, together with hardened release delivery to Gitee and npm on
top of the `v1.0.55-beta.3` baseline.
### Fixed
- **Shortcut projection fixes** (#795) — `chat +chat-bots` no longer projects a non-empty `list_group_bots` response to an empty list, `+bot-find` recognizes the `search_bots` response shape (`result.bots` entries with `botOpenDingTalkId`), and mail thread listings keep `lastUpdated` when the backend returns `lastModifiedDateTime`.
### Changed
- **Hardened release delivery** — the Gitee mirror workflow can synchronize a specific release's assets on demand, release lookup tolerates Gitee's HTTP 200 null-body response for missing releases, npm dist-tag verification waits through slow registry CDN propagation with incremental backoff, and beta/stable release operations are role-enforced (#791).
## [1.0.55-beta.3] - 2026-07-24
This beta validates the HR Brain command surface, smoother guarded release
@@ -22,19 +43,6 @@ automation, and deterministic Markdown test coverage on top of the
- **Deterministic Markdown coverage** — replaces timing-dependent temporary-file deletion tests with synchronized file-stat failures so release admission no longer flakes on scheduler timing.
## [1.0.55-beta.2] - 2026-07-23
This beta validates Wukong capability parity across Chat, Contacts, documents,
Drive, Markdown, and Todos, together with faster guarded releases and legacy
authentication migration.
### Added
- **Chat editing and group management** — adds `chat message edit`, conversation-category lookups through `chat category list-by-conv` and `chat category batch-info`, and the confirmed, irreversible `chat group upgrade-to-external` flow.
- **Contact maintenance commands** — adds `contact user update`, `contact user update-self`, `contact dept create`, `contact dept update`, and `contact account update`, all with guarded write behavior.
- **Markdown file workflows** — adds the `markdown` product with `fetch`, `create`, `overwrite`, and `patch` commands for Drive-native `.md` files, including explicit routing, dry-run previews, and confirmation for destructive writes.
- **Todo labels** — adds `todo tag add`, `delete`, `update`, `list`, and `create`.
### Changed
- **Faster guarded releases** — trusts an independently revalidated, exact `CHANGELOG.md`-only successor of an already admitted `main` commit, runs cloud planning alongside governance, and executes sealed-release automation, compatibility, and multi-profile validation in parallel with artifact compilation. Normal cloud publication no longer requires an unshareable local packaging preflight.
+7
View File
@@ -91,3 +91,10 @@ coverage is additionally selected for platform-sensitive code.
contexts are current and green, auto-merge completes the PR. If `main`
advances first, strict status checks revalidate the branch; no separate
routine merge request is needed.
Contributors without repository write access stop at the PR flow. Explicitly
authorized collaborators with `write`, `maintain`, or `admin` access can use
[Actions → Release](https://github.com/DingTalk-Real-AI/dingtalk-workspace-cli/actions/workflows/release.yml)
to publish beta releases without manual approval. The same internal roles may
start a stable release, but a different repository administrator must approve
the `release-stable` Environment deployment before publication continues.
+11 -11
View File
@@ -1,33 +1,33 @@
class DingtalkWorkspaceCliBeta < Formula
desc "Automate DingTalk workspace tasks from the terminal (beta channel)"
homepage "https://github.com/DingTalk-Real-AI/dingtalk-workspace-cli"
version "1.0.55-beta.2"
version "1.0.55-beta.4"
license "Apache-2.0"
keg_only "it is the beta channel and conflicts with dingtalk-workspace-cli"
on_macos do
if Hardware::CPU.arm?
url "https://github.com/DingTalk-Real-AI/dingtalk-workspace-cli/releases/download/v1.0.55-beta.2/dws-darwin-arm64.tar.gz"
sha256 "fd23353c473419ce8cfaf316e7afff757cc4606291f4ad41a62c6f5625f45c8f"
url "https://github.com/DingTalk-Real-AI/dingtalk-workspace-cli/releases/download/v1.0.55-beta.4/dws-darwin-arm64.tar.gz"
sha256 "05b269fe44a125ee8b368d6228c5229950b8216872fb569741d1e30a83ce952a"
else
url "https://github.com/DingTalk-Real-AI/dingtalk-workspace-cli/releases/download/v1.0.55-beta.2/dws-darwin-amd64.tar.gz"
sha256 "0b429950a6449736338b504386d8009a4f2cdb0668f64f72a61cd503a25df193"
url "https://github.com/DingTalk-Real-AI/dingtalk-workspace-cli/releases/download/v1.0.55-beta.4/dws-darwin-amd64.tar.gz"
sha256 "b0d7604299336c83b7805d3b1a47a90668f2e2f3fc54702b0e846bb2907b6170"
end
end
on_linux do
if Hardware::CPU.arm?
url "https://github.com/DingTalk-Real-AI/dingtalk-workspace-cli/releases/download/v1.0.55-beta.2/dws-linux-arm64.tar.gz"
sha256 "c213a22d6187a8f68596b9f25d6277f5fd58cea5120f5b7caeef011fb41f1104"
url "https://github.com/DingTalk-Real-AI/dingtalk-workspace-cli/releases/download/v1.0.55-beta.4/dws-linux-arm64.tar.gz"
sha256 "a9c1dd5c6171091a84fc18e5081c9f75d037826cd3966726545d715ce832ae31"
else
url "https://github.com/DingTalk-Real-AI/dingtalk-workspace-cli/releases/download/v1.0.55-beta.2/dws-linux-amd64.tar.gz"
sha256 "4e041ba7b1d8038fa34deba3a3f78af8377f4361131d8bfdb1f1913e5f9454dc"
url "https://github.com/DingTalk-Real-AI/dingtalk-workspace-cli/releases/download/v1.0.55-beta.4/dws-linux-amd64.tar.gz"
sha256 "5e97ba398f5a3e15b9d235bc53f596d31a4d6b7af7bb2185b7b48beeda6a2ebb"
end
end
resource "skills" do
url "https://github.com/DingTalk-Real-AI/dingtalk-workspace-cli/releases/download/v1.0.55-beta.2/dws-skills.zip"
sha256 "1cec445c73b9ff569ca002b6548f4ee8383712447bc4cebd7701c6065774524a"
url "https://github.com/DingTalk-Real-AI/dingtalk-workspace-cli/releases/download/v1.0.55-beta.4/dws-skills.zip"
sha256 "4ebc0294b65d90adb5c5d639a548b528af240e4117ccca3d388e2efb6030170a"
end
def install
+2 -2
View File
@@ -39,8 +39,8 @@ help:
@printf " make package - Build all release artifacts locally\n"
@printf " make changelog-pre VERSION=vX.Y.Z-beta.N - Prepare prerelease notes\n"
@printf " make changelog-stable VERSION=vX.Y.Z FROM_BETA=vX.Y.Z-beta.N - Prepare stable notes\n"
@printf " make release-pre VERSION=vX.Y.Z-beta.N [PUBLISH=1] - Validate or publish prerelease\n"
@printf " make release-stable VERSION=vX.Y.Z FROM_BETA=vX.Y.Z-beta.N [PUBLISH=1] - Validate or publish stable\n"
@printf " make release-pre VERSION=vX.Y.Z-beta.N - Validate prerelease; publish official releases from Actions\n"
@printf " make release-stable VERSION=vX.Y.Z FROM_BETA=vX.Y.Z-beta.N - Validate stable; publish official releases from Actions\n"
@printf " make publish-homebrew-formula - Push dist/homebrew/dingtalk-workspace-cli.rb to a tap repo\n"
build:
+21 -1
View File
@@ -474,7 +474,7 @@ Env vars: `DWS_SKILL_MODE=mono|multi` (also honored by `install.sh` / `install.p
<details>
<summary><strong>Personal Event Subscription</strong> — real-time DingTalk messages for event-driven agents</summary>
`dws event consume` subscribes as the currently logged-in user over a managed Stream WebSocket and emits each event as one NDJSON line on stdout. The public catalog currently covers messages that mention the current user, one-to-one messages with a specified user, and messages in a specified group.
`dws event consume` subscribes as the currently logged-in user over a managed Stream WebSocket and emits each event as one NDJSON line on stdout. The public catalog covers scoped and all one-to-one/group messages, specified senders, read/recall/reaction events, and group title/disband lifecycle events.
The default `ndjson`, `json`, and `pretty` output preserves the transport envelope (`type`, `event_type`, string `data`, and `headers`) for existing scripts; `compact` retains its existing processor. Add `--flatten` to emit the stable top-level business fields used by Agent workflows. `--format` controls JSON serialization; `--flatten` controls the data structure and cannot be combined with `-f raw` or `--debug-raw-events`.
@@ -503,6 +503,25 @@ dws event consume user_im_message_receive_o2o --open-dingtalk-id <openDingtalkId
# Listen for messages in a specified group
dws event consume user_im_message_receive_group --group <openConversationId> --flatten -f ndjson
# Listen for all one-to-one or all group messages
dws event consume user_im_message_receive_o2o_all --flatten -f ndjson
dws event consume user_im_message_receive_group_all --flatten -f ndjson
# Listen for a specified group's title changes, member changes, or disband event
dws event consume user_im_group_updated --group <openConversationId> --flatten -f ndjson
dws event consume user_im_group_member_added --group <openConversationId> --flatten -f ndjson
dws event consume user_im_group_member_exited --group <openConversationId> --flatten -f ndjson
dws event consume user_im_group_disbanded --group <openConversationId> --flatten -f ndjson
# Listen for multiple events for the same user in one process
dws event consume \
user_im_message_receive_o2o \
user_im_message_read_o2o \
user_im_message_recall_o2o \
--user <userId> \
--flatten \
-f ndjson
# Inspect local consumers and cancel a subscription
dws event status
dws event stop <subscribe_id>
@@ -514,6 +533,7 @@ For one-to-one and specified-sender events, use exactly one target identity: `--
|---------|---------|
| Managed lifecycle | `consume` creates or reuses the personal subscription; `stop` cancels it and cleans local state |
| Shared connection | Consumers for the same user share one local bus and cloud connection |
| Multi-event process | One consume process can listen for compatible events for the same target while retaining one subscription per event |
| Subscription isolation | Normal consumers match both event type and `subscribe_id` |
| Agent-friendly output | Stream events are written to stdout as NDJSON; status and diagnostics use stderr |
| Observability | `status` shows remote subscriptions, the personal bus, and local consumers |
+21 -1
View File
@@ -468,7 +468,7 @@ DWS_SKILL_SOURCE=/path/to/skills dws skill setup --mode multi
<details>
<summary><strong>个人事件订阅</strong> — 实时接收钉钉消息,驱动事件触发的 Agent</summary>
`dws event consume` 使用当前 OAuth 登录用户建立托管的 Stream WebSocket 长连接,并把每条事件以 NDJSON 一行输出到 stdout。当前公开目录包括:当前用户被 @ 的消息、与指定用户的单聊消息、指定群的消息。
`dws event consume` 使用当前 OAuth 登录用户建立托管的 Stream WebSocket 长连接,并把每条事件以 NDJSON 一行输出到 stdout。当前公开目录覆盖指定范围和全量单聊/群消息、指定发送人、已读/撤回/表情回应,以及群标题变更和群解散事件。
默认 `ndjson`、`json`、`pretty` 输出保留兼容 transport envelope(`type`、`event_type`、字符串 `data`、`headers`),`compact` 继续沿用原 processor。Agent 或新脚本显式加 `--flatten` 后,输出稳定的顶层业务字段。`--format` 控制 JSON 序列化,`--flatten` 控制数据结构,且不能与 `-f raw` 或 `--debug-raw-events` 同时使用。
@@ -497,6 +497,25 @@ dws event consume user_im_message_receive_o2o --open-dingtalk-id <openDingtalkId
# 监听指定群的消息
dws event consume user_im_message_receive_group --group <openConversationId> --flatten -f ndjson
# 监听所有单聊或所有群消息
dws event consume user_im_message_receive_o2o_all --flatten -f ndjson
dws event consume user_im_message_receive_group_all --flatten -f ndjson
# 监听指定群标题变更、成员进退群或群解散
dws event consume user_im_group_updated --group <openConversationId> --flatten -f ndjson
dws event consume user_im_group_member_added --group <openConversationId> --flatten -f ndjson
dws event consume user_im_group_member_exited --group <openConversationId> --flatten -f ndjson
dws event consume user_im_group_disbanded --group <openConversationId> --flatten -f ndjson
# 一个进程监听同一用户的多个事件
dws event consume \
user_im_message_receive_o2o \
user_im_message_read_o2o \
user_im_message_recall_o2o \
--user <userId> \
--flatten \
-f ndjson
# 查看本地 consume,并取消指定订阅
dws event status
dws event stop <subscribe_id>
@@ -508,6 +527,7 @@ dws event stop <subscribe_id>
|------|------|
| 自动编排 | `consume` 创建或复用个人订阅,`stop` 取消订阅并清理本地状态 |
| 共享连接 | 同一用户的多个 consumer 共享本地 bus 和云端长连接 |
| 多事件进程 | 同一目标的兼容事件可由一个 consume 进程监听,每个事件仍有独立订阅 |
| 订阅隔离 | 正常 consumer 同时按事件类型和 `subscribe_id` 匹配 |
| Agent 友好输出 | Stream 事件写入 stdout,连接状态和诊断信息写入 stderr |
| 状态可观测 | `status` 同时显示服务端订阅、personal bus 和本地 consumers |
+203
View File
@@ -0,0 +1,203 @@
# 5分钟搭好团队知识库:DWS 让 IT 服务台自己开口答疑,你值得拥有
> 💡 **省流版摘要:**
> 别再用鼠标一个个建文档了,真的没必要!本文带你用 DWS(钉钉命令行工具),5 分钟搭好一个团队知识库:建空间、搭目录、批量导入 FAQ、配权限,全程命令行一步到位。最后再花 30 秒把它挂到钉钉机器人上——同事在群里 @ 一下,知识库自己开口答疑。知识管理员从"搬运工"升级为"甩手掌柜",就是这么简单。
>
> (温馨提示:本文内容可以直接丢给智能体,让智能体一次性逐步安装和使用)
## 一、痛点吐槽:管知识库有多累?
身为企业 IT / 知识管理员,你肯定经历过这些"九九八十一难"……
- **点击马拉松**:建空间点 5 下、建文件夹点 3 下、建一篇文档再点 4 下。一个季度下来,鼠标点击次数比写的字还多。
- **搬运工噩梦**:几百篇历史 FAQ 散落在本地 Word / Markdown 里,要搬进钉钉知识库?复制粘贴到天荒地老,格式还经常翻车。
- **权限苦差**:新同事入职要加权限、转岗要改角色、离职要移除。逐个空间点进去操作,漏一个就是安全隐患。
- **知识沉睡**:库是建好了,可同事还是习惯私聊问你"VPN 又连不上了怎么办"。知识库躺着吃灰,你继续当人肉客服。😭
今天,DWS(DingTalk Workspace CLI)闪亮登场!🌟
你不需要写一行代码,只要在终端敲几行命令,知识库的"建、搬、管、用"全链路一次搞定。更香的是:搭好的知识库可以直接挂到钉钉机器人上,让知识自己开口答疑。
## 二、DWS 是个啥?知识库的"遥控指挥中心"
DWS 是钉钉能力的原子化封装,把复杂的 OpenAPI 打包成简单指令。管知识库这件事,主要靠它的"三驾马车":
| 命令族 | 能干什么 |
|---|---|
| 🗂️ `dws wiki` | 知识库空间、目录节点、成员权限的全生命周期管理 |
| 📄 `dws doc` | 文档内容读写、本地文件批量导入、模板套用 |
| 📁 `dws drive` | 钉盘文件上传下载、全局搜索、归档备份 |
你可以把它想象成知识库的"遥控指挥中心" 🎮——既能你手动按(终端敲命令,比点界面快 10 倍),也能让 AI 帮你按(Claude Code、Qoder 等智能体直接听懂并调用)。
**适合谁用:**
- **企业 IT / 知识管理员**:批量建库、批量导入、批量管权限,脚本化解放双手
- **开发者 / AI 玩家**:把知识库挂到机器人上,打造 24 小时答疑小能手
- **重度钉钉用户 / 效率党**:一条命令搜全库,比在界面里翻目录快得多
## 三、搭好的知识库能帮你做什么?
| 场景 | 玩法 |
|---|---|
| 🛟 IT 服务台 FAQ 库 | VPN、邮箱、打印机常见问题集中沉淀,机器人自动答疑 |
| 📜 制度流程库 | 报销、请假、采购制度批量导入,全文秒搜 |
| 🎓 新人上岗手册 | 按部门建目录,入职即授权限,自助通关 |
| 🤖 知识库 + 机器人 | `--knowledge-source wiki:<spaceId>` 一挂,群里 @ 它就答 |
所想即所得,拒绝画饼,直接上菜!🍽️
## 四、5分钟倒计时,搭好你的团队知识库
> 以下命令全部经过真实环境跑通验证,放心照抄。
### 0. 安装并登录 DWS(约 1 分钟)
把以下指令复制给你的智能体(Claude Code、Qoder、Codex 等)执行,或手动在终端跑:
macOS / Linux:
```bash
curl -fsSL https://raw.githubusercontent.com/DingTalk-Real-AI/dingtalk-workspace-cli/main/scripts/install.sh | sh
```
Windows(PowerShell):
```powershell
irm https://raw.githubusercontent.com/DingTalk-Real-AI/dingtalk-workspace-cli/main/scripts/install.ps1 | iex
```
登录(提示授权请扫码):
```bash
dws auth login
```
【截图位:dws auth status 显示 token_valid: true】
### 1. 建一个知识库空间(10 秒)
```bash
dws wiki space create --name "IT服务台知识库" --desc "IT 常见问题与制度流程"
```
返回里的 `workspaceId` 就是空间的身份证号,后面每步都要用它。
【截图位:返回 workspaceId 与 spaceUrl】
### 2. 搭目录结构(20 秒)
知识库的结构 = 文件夹节点 + 文档节点。先建分类文件夹:
```bash
dws wiki node create --workspace <workspaceId> --name "常见问题FAQ" --type folder
dws wiki node create --workspace <workspaceId> --name "制度流程" --type folder
```
在文件夹下建一篇空文档(不加 `--folder` 就建在根目录):
```bash
dws wiki node create --workspace <workspaceId> --name "VPN连接失败排查指南" --folder <文件夹nodeId>
```
### 3. 批量导入历史文档(1 分钟,重头戏!)
几百篇本地 FAQ 不用复制粘贴,`doc import` 直接整批灌进知识库,Word、Excel、Markdown、txt 通吃:
```bash
# 单篇导入到指定文件夹
dws doc import --file ./vpn-faq.md --workspace <workspaceId> --folder <文件夹nodeId> --name "VPN连接失败排查指南"
# 批量导入整个目录(bash 一把梭)
for f in ./faq/*.md; do
dws doc import --file "$f" --workspace <workspaceId> --folder <文件夹nodeId>
done
```
已有在线文档想补内容?Markdown 直接写入:
```bash
dws doc update --node <文档nodeId> --content-file ./补充内容.md --mode append
```
【截图位:终端批量导入的滚动输出 + 知识库里齐刷刷的文档列表】
### 4. 配权限:把人拉进来(30 秒)
```bash
# 先用通讯录查到同事的 userId
dws contact user search --query "张三"
# 加为编辑者(--users 支持逗号分隔批量加)
dws wiki member add --workspace <workspaceId> --users <userId1>,<userId2> --role EDITOR
# 随时盘点成员
dws wiki member list --workspace <workspaceId>
```
### 5. 验收:搜一下,秒级命中(10 秒)
```bash
# 库内全文搜索
dws wiki node search --workspace <workspaceId> --query "VPN"
# 全局搜知识库空间
dws wiki space search --query "IT服务台"
```
【截图位:搜索结果命中文档标题】
### 6. 封神一步:挂到机器人上,知识自己开口答疑(30 秒)
如果你已经按《5分钟抱走你的嘴替机器人》建好了钉钉机器人,只需加一个参数:
```bash
dws dev connect --channel claudecode \
--robot-client-id <你的机器人ID> --robot-client-secret <你的机器人密钥> \
--knowledge-source wiki:<workspaceId>
```
机器人会自动从知识库拉取知识并缓存。同事在群里 @ 它问"VPN 连不上怎么办",它直接引用你刚导入的排查指南回答——你,终于不用当复读机了。😎
## 五、进阶使用技巧
### 知识库管理速查表
| 操作 | 命令 |
|---|---|
| 列出我的个人空间 | `dws wiki space list --type myWikiSpace` |
| 列出组织知识库 | `dws wiki space list --type orgWikiSpace` |
| 浏览库内节点树 | `dws wiki node list --workspace <ID> [--folder <nodeId>]` |
| 移动 / 复制节点 | `dws wiki node move` / `dws wiki node copy` |
| 改成员角色 | `dws wiki member update --users <UID> --role VIEWER` |
| 移除成员 | `dws wiki member remove --users <UID>` |
| 删除整个空间 | `dws wiki space delete --workspace <ID>`(进回收站,可恢复) |
### 老手避坑指南 ⛳
- 建在线表格用 `--type axls`,**`asheet` 服务端不支持**,别踩坑。
- `member list` 只返回姓名和角色、**不返回 userId**;要串联 `update` / `remove`,先用 `dws contact user search --query "<姓名>"` 反查。
- 搜索关键词的 flag 是 `--query`,`--keyword` 是遗留别名,新脚本请用 `--query`。
- 所有命令加 `--format json`,配合 `--jq` 过滤字段,写脚本时稳得一批。
- 破坏性操作(删空间、覆盖写文档)前加 `--dry-run` 先预览,确认无误再执行。
### 让机器人答得更好
| 开关 | 作用 |
|---|---|
| `--knowledge-source wiki:<spaceId>` | 从钉钉知识库拉知识作为答疑来源(本文主角) |
| `--knowledge-dir <目录>` | 挂本地 .md/.txt 知识目录,可与上面并存 |
| `--allowed-groups / --allowed-users` | 白名单,只让指定群或人触发 |
| `--daemon` | 后台常驻,关掉终端也不断线 |
## 六、更多 DWS 官方信息
- 钉钉 CLI 官网:https://open.dingtalk.com/dingtalk-cli
- 开源仓库:https://github.com/DingTalk-Real-AI/dingtalk-workspace-cli
- 上一篇姊妹篇:《5分钟抱走你的嘴替机器人:启动钉钉DWS,你值得拥有》
## 七、欢迎加入交流群
【截图位:DWS 交流群二维码】
遇到问题来群里喊一声,官方同学在线答疑。下一篇想看什么?批量备份知识库?给知识库做权限审计?留言区点菜!🍻
+1
View File
@@ -5,6 +5,7 @@
| Variable | Purpose / 用途 |
|---------|---------|
| `DWS_CONFIG_DIR` | Override default config directory / 覆盖默认配置目录 |
| `DWS_AGENT_HOST` | Optional Agent host observation label sent as `x-dws-agent-host` (for example `qwenwork_cloud`). Values are trimmed and must match `^[a-z0-9][a-z0-9_-]*$`; unset values are omitted. Used only for logs and BI, never for authentication or routing. / 可选 Agent 宿主观测标识,经裁剪并校验后作为 `x-dws-agent-host` 发送;仅用于日志与 BI,不参与鉴权或路由 |
| `DWS_<PRODUCT>_MCP_URL` | Override a product MCP endpoint for local development / 本地开发时覆盖指定产品 MCP endpoint |
| `DWS_CLIENT_ID` | OAuth client ID (DingTalk AppKey) |
| `DWS_CLIENT_SECRET` | OAuth client secret (DingTalk AppSecret) |
+24 -19
View File
@@ -2,16 +2,23 @@
发布只走一条受控链路:GitHub Actions 的 `Release` workflow 负责版本分配、封板、构建、签名和下游发布;Homebrew Formula 在不可变 Release 资产及 checksum 通过校验后,由同一 workflow 直接写入 `main`,不再创建二次 PR。本地 `dws-release` 仍是兼容入口,但不再要求某一台固定电脑承担打包;不要直接运行 `goreleaser release`,也不要手工补打、移动或复用 tag。
发布前必须完成平台治理:目标 GitHub 仓库已启用 immutable releases,`main` 精确要求 `CI` workflow 的九个 context:`Lint`、`Test`、`Coverage`、`Policy`、`Edition`、`Interface Integrity`、`AI Behavior`、`CLI Smoke`、`Mock MCP`。云端和本地入口都会在封 tag 前检查 immutable releases、当前 SHA 的全部九个 context 和在途 Release;`v*` tag ruleset 仍需仓库管理员预先配置。
发布前必须完成平台治理:目标 GitHub 仓库已启用 immutable releases,`main` 精确要求 `CI` workflow 的九个 context:`Lint`、`Test`、`Coverage`、`Policy`、`Edition`、`Interface Integrity`、`AI Behavior`、`CLI Smoke`、`Mock MCP`。云端入口会在封 tag 前检查 immutable releases、当前 SHA 的全部九个 context、Environment 保护规则和在途 Release;`v*` tag ruleset 仍需仓库管理员预先配置。
## 推荐入口:GitHub 云端发布
任何具有仓库写权限、因而可以手动运行 Actions workflow 的成员,都可以基于当时最新的 `main` 发起发布:
入口页面是 [GitHub Actions → Release](https://github.com/DingTalk-Real-AI/dingtalk-workspace-cli/actions/workflows/release.yml)。在仓库页面依次点击 `Actions` → `Release` → `Run workflow` 即可操作;不需要通过 Agent 或本地机器触发。
1. 在 GitHub Actions 打开 `Release`,选择 `Run workflow`,分支必须是默认分支 `main`。
只有得到该仓库明确授权、最终权限为 `write`、`maintain` 或 `admin` 的协作者可以运行发布操作,workflow 还会对发起人和重新运行者做同样的权限复核。没有仓库写权限的外部贡献者以及仅有 `read` / `triage` 权限的成员不能规划或发布版本。两个渠道的授权边界如下:
- beta:上述任一内部成员都可以直接规划和发布,不需要人工审批。
- stable:上述任一内部成员都可以规划并发起发布;完成只读规划和治理检查后,workflow 会在 `release-stable` Environment 等待另一名仓库管理员通过 `Review deployments` 签收。申请人不能批准自己的请求,批准后原 run 自动继续,无需重新触发。
基于当时最新的 `main` 发起发布:
1. 在上述 `Release` 页面选择 `Run workflow`,分支必须是默认分支 `main`。
2. `release_operation=plan`,选择 `release_channel=beta|stable`;仅在开始新 beta 线时选择 `release_bump=patch|minor|major`。
3. workflow summary 会给出唯一的下一版本。把对应的精确 `CHANGELOG.md` 章节通过 PR 合入 `main`。
4. 再次运行,改为 `release_operation=publish`,并输入 `PUBLISH beta` 或 `PUBLISH stable`。
4. 再次运行,改为 `release_operation=publish`。beta 会直接进入自动化发布;stable 会在封 tag 前等待管理员签收。
`plan` 是纯只读操作,不创建 tag、预留版本号或生成包。CHANGELOG 合入期间若另一个发布先占用了该版本,`publish` 会重新分配并因 CHANGELOG 章节不匹配而拒绝,需要重新 plan。`publish` 会先再次确认 dispatch SHA 仍是当前 `main`、Code Admission 和平台治理均通过,再由唯一的 write job 使用 GitHub API 原子创建 annotated tag;同一次 run 随即进入既有的跨平台构建、GitHub/npm、可选 OSS/Gitee 发布和 Homebrew 直交付 DAG。内置 `GITHUB_TOKEN` 创建的 tag 不依赖第二条 workflow 被再次触发。
@@ -64,7 +71,9 @@ dws-release
dws-release config --remote origin
```
之后命令按仓库状态自动走到正确步骤:缺少精确 CHANGELOG 章节时只生成模板并停止;补全、提交并合入 `main` 后,再运行同一条命令就会安全快进本地 `main` 并执行完整预检。若同名 remote 后续被改指向其他仓库会直接拒绝。只有显式增加 `--publish` 才会进入 tag 发布,且底层仍要求最终版本确认。
之后命令按仓库状态自动走到正确步骤:缺少精确 CHANGELOG 章节时只生成模板并停止;补全、提交并合入 `main` 后,再运行同一条命令就会安全快进本地 `main` 并执行完整预检。若同名 remote 后续被改指向其他仓库会直接拒绝。官方仓库不再接受本地 `--publish`,命令会直接提示上述 Actions 页面;本地入口不能绕过 beta/stable 的统一授权。
Release workflow 不再监听新建的 `v*` tag;直接推 tag 不会发布 GitHub Release、npm 或镜像渠道。所有新 beta/stable 都必须从云端页面进入统一授权和审计链路;历史失败版本仍可通过受保护的 recovery 兼容处理。
## 发布模型
@@ -91,13 +100,7 @@ dws-release v1.2.3-beta.1
dws-release v1.2.3-beta.1
```
预检包含测试、策略检查、旧正式版命令树兼容检查、全平台打包、npm 安装验证,以及 macOS 环境下的 Homebrew 安装验证。它还会从默认分支触发一次无发布权限的 `Release governance preflight`,用正式流水线相同的身份检查该精确 commit 的九个 Code Admission context 和 immutable releases。通过后会在当前 Git worktree 的私有 Git 状态目录写入一个有效期六小时的证明,绑定版本、精确 commit、发布仓库、beta/stable 基线和远端 `main`:
```bash
dws-release v1.2.3-beta.1 --publish
```
若源码、版本、远端身份和 stable 基线均未变化,`--publish` 会复用该证明,只执行远端契约、发布身份和最终治理复核,不再重复测试与打包。也可以直接运行 `--publish`;没有可复用证明时只会完整执行一次预检。命令在封 tag 前仍要求再次输入完整版本号,统一入口不提供跳过确认的参数。
预检包含测试、策略检查、旧正式版命令树兼容检查、全平台打包、npm 安装验证,以及 macOS 环境下的 Homebrew 安装验证。它还会从默认分支触发一次无发布权限的 `Release governance preflight`,用正式流水线相同的身份检查该精确 commit 的九个 Code Admission context 和 immutable releases。通过后回到上述 Actions 页面选择 beta 和 `release_operation=publish`;云端会重新绑定当前 `main`,然后直接进入 beta 自动发布,不需要人工审批或输入确认短语。
## 正式发布
@@ -107,14 +110,13 @@ beta 验证通过后,运行正式版入口:
dws-release v1.2.3 --from-beta v1.2.3-beta.1
```
首次运行只生成正式版 CHANGELOG 并停止。补全内容、删除 `TODO`,提交后通过 PR 合入 `main`;重新运行同一条命令做完整预检,确认后增加 `--publish`:
首次运行只生成正式版 CHANGELOG 并停止。补全内容、删除 `TODO`,提交后通过 PR 合入 `main`;重新运行同一条命令做完整预检:
```bash
dws-release v1.2.3 --from-beta v1.2.3-beta.1
dws-release v1.2.3 --from-beta v1.2.3-beta.1 --publish
```
本地入口的 `FROM_BETA` 不会自动推断;云端入口会按上述规则唯一选择。两种入口都会把它写入 stable annotated tag 的 `From-Beta` 元数据,CI 会再次读取和验证。
预检通过后,在 Actions 页面选择 stable 和 `release_operation=publish`。云端入口会按上述规则唯一选择 beta,并把它写入 stable annotated tag 的 `From-Beta` 元数据;在创建 tag 前必须由另一名仓库管理员签收。
## CHANGELOG 契约
@@ -133,13 +135,13 @@ dws-release v1.2.3 --from-beta v1.2.3-beta.1 --publish
## CI/CD 保证
- 只接受 `vX.Y.Z-beta.N` 和 `vX.Y.Z`,且新版本必须高于上一正式版。这里的“上一正式版”必须同时具备公开非草稿 GitHub Release 和同 tag/commit 的成功 Release workflow;只有 tag、没有交付成功的孤儿版本会阻断后续发布,要求走机器核验恢复补齐。云端 tag 会固定 `Release-Run`、requester、commit 和版本分配指纹,交付验证按该精确 run/attempt 及完整 job graph 取证,不接受任意 `workflow_dispatch`。历史版本若曾通过专用 recovery workflow 完成交付,只能使用仓库内 `delivered-stable-recoveries.json` 中精确到 tag、commit、run、workflow SHA 与 attempt 的 reviewed 证据。
- tag 必须是 annotated tag;本地脚本要求封板提交已通过 PR 合入并包含在远端 `main` 历史中,发布只推送 tag。CI 允许其后 `main` 继续前进,但始终要求封板提交位于 `main` 历史中。
- tag 必须由云端 seal job 创建为 annotated tag;封板提交必须已通过 PR 合入并包含在远端 `main` 历史中。流水线允许其后 `main` 继续前进,但始终要求封板提交位于 `main` 历史中。
- 日常 CI 和发布前都会对比“最新已交付正式版”的完整命令树;若长时间预检期间该 baseline 发生变化,会针对新的 baseline 重新比较。
- GoReleaser 只构建;Darwin 重签、checksums 重算和 npm 安装验证通过后,才统一上传 GitHub Release 的最终产物。
- 六个平台归档会逐个解包并核验二进制内嵌版本;公开资产集合、checksums 集合和 npm tarball integrity 都必须精确一致。npm tarball 固定由 npm `10.9.2` 打包,避免重跑时因 runner 自带 npm 漂移产生不同字节。
- stable 发布到 npm `latest`;prerelease 发布到 npm `beta`。启用 `ENABLE_OSS_MIRROR=true` 后,stable 同步 OSS `latest.txt` 和共享安装脚本,prerelease 只同步 OSS `beta.txt`,不会覆盖稳定入口。
- Release workflow 使用一个最多容纳 100 个 pending run 的串行 publication queue;版本规划、云端封板、发布、恢复、修复和撤回共享同一发布锁。
- 本地 tag push 失败时会删除本次新建的本地 tag。远端 tag 一旦创建,后续发布归 CI 所有;发布中途失败时先重跑同一 run 的失败 jobs,必须跨 run 时走机器核验恢复,禁止改 tag 指向或复用版本号。只有已经公开版本经过受保护的全渠道撤回并留下永久 `withdrawn/...` 墓碑后,撤回 workflow 才会在最后一步删除原 tag。
- 云端 seal 创建远端 tag 后,后续发布归同一 run 所有;发布中途失败时先重跑同一 run 的失败 jobs,必须跨 run 时走机器核验恢复,禁止改 tag 指向或复用版本号。只有已经公开版本经过受保护的全渠道撤回并留下永久 `withdrawn/...` 墓碑后,撤回 workflow 才会在最后一步删除原 tag。
npm 补发只允许从默认分支触发 Release workflow 的 `repair_npm_version`。它只支持启用 immutable releases 后、由本流水线成功产出的公开 immutable release:目标必须是 `main` 历史中的 annotated tag,并且同 commit 的 `Build immutable GitHub Release` job 已成功。即使后续 npm 分发失败,这个独立的产物封存边界仍可作为补发依据。补发会用目标 commit 的 npm 模板重组包,逐平台核验资产和二进制版本,再发布到隔离的 `backfill` dist-tag,不会回滚 `latest` / `beta`。历史 mutable release 不进入自动补发路径,避免把可被替换的资产带入 npm。
@@ -182,15 +184,18 @@ Release workflow 会生成 Darwin/Linux 双架构 Formula,并在不可变资
仓库管理员还需要在 GitHub 平台配置以下不可由脚本替代的规则:
- `main` 必须精确要求 `Lint`、`Test`、`Coverage`、`Policy`、`Edition`、`Interface Integrity`、`AI Behavior`、`CLI Smoke`、`Mock MCP` 九个 Code Admission context;tag workflow 也会通过 Checks API 再确认该封板 SHA 上九项全部成功。
- `main` 必须精确要求 `Lint`、`Test`、`Coverage`、`Policy`、`Edition`、`Interface Integrity`、`AI Behavior`、`CLI Smoke`、`Mock MCP` 九个 Code Admission context;Release workflow 也会通过 Checks API 再确认该封板 SHA 上九项全部成功。
- 必须启用 immutable releases;它只保护启用后发布的 release,因此应在第一次使用新流水线前配置。为 `v*` 增加 tag ruleset,限制创建权限,并在 release 发布前保护 tag 的短暂窗口。
- tag ruleset 还必须覆盖 `withdrawn/v*`:只允许受保护的撤回 workflow 创建墓碑,禁止更新或删除墓碑;同时应允许 Release workflow 创建新的 `v*`,允许撤回 workflow 在全部渠道回退后删除精确的问题 `v*`。若组织级规则阻止这两个 workflow 的预期动作,发布或撤回会 fail closed,不能靠手工移动 tag 绕过。
- 配置 `RELEASE_GOVERNANCE_TOKEN` Actions secret,只授予目标仓库 `Administration: read`;内置 `GITHUB_TOKEN` 不具备 immutable-releases API 所需的仓库治理权限。每次本地预检和 tag workflow 都使用这一个身份进行 fail-closed 验证。
- 配置 `RELEASE_GOVERNANCE_TOKEN` Actions secret,只授予目标仓库 `Administration: read`;内置 `GITHUB_TOKEN` 不具备 immutable-releases API 所需的仓库治理权限。每次本地预检和云端发布都使用这一个身份进行 fail-closed 验证。
- 配置 `APPLE_CERTIFICATE_P12_BASE64`、`APPLE_CERTIFICATE_PASSWORD` 和具备发布权限的 `NPM_TOKEN`;撤回还要求该 npm 身份能够执行 `deprecate` 和修改 dist-tag。
- 启用 OSS 镜像时,先创建有效 Bucket,再设置仓库变量 `ENABLE_OSS_MIRROR=true`,并配置 `OSS_ACCESS_KEY_ID`、`OSS_ACCESS_KEY_SECRET`、`OSS_ENDPOINT`、`OSS_BUCKET`,按需配置 `OSS_PREFIX`。启用后发布保持 fail-closed;撤回身份必须能够补齐安全版本资产、写 `latest.txt` / `beta.txt` 并删除问题版本前缀。尚未 provision Bucket 时保持该变量未设置或不等于 `true`,新 tag 会封存 `OSS-Mirror: deferred` 并跳过 OSS;该版本不能通过现有 repair 流程事后改成启用。
- 若启用 Gitee fallback,设置 `ENABLE_GITEE_UPLOAD_FALLBACK=true`,并配置 `GITEE_TOKEN`、`GITEE_USER`、`GITEE_REPO`;该身份必须能够创建和删除目标仓库的 Release 与 tag。
- 正常 Homebrew 发布使用现有的 `HOMEBREW_PR_TOKEN` 直接提交 Formula-only commit,不再创建 Homebrew PR,也不跑权限 canary。GitHub 不允许内置 Actions App 作为当前仓库 ruleset 的 bypass actor,因此两个默认分支 ruleset 都只给该 token 所属的指定发布管理员用户 `always` bypass;仓库脚本仍会限制提交路径、校验 Ruby、禁止 force push,并在并发更新时重新基于最新 `main`。
- `HOMEBREW_PR_TOKEN` 应保持仓库范围的 `Contents: write` 与 `Pull requests: write` 权限;后者仅供撤回流程创建回退 PR。不要与 `RELEASE_GOVERNANCE_TOKEN` 复用,并定期审计 token owner 与 ruleset bypass actor 一致。
- 创建 `release-beta` environment,只允许受保护分支且不配置 required reviewer;仓库内部 `write`、`maintain`、`admin` 成员的 beta 发布会直接通过该边界。
- 创建 `release-stable` environment,只允许受保护分支,以仓库管理员为 required reviewer,禁止申请人自审并关闭管理员绕过。内部成员可以发起 stable,但必须由另一名管理员签收后才能封 tag 和写入任何发布渠道。
- Release workflow 会在封 tag 前回读并验证上述两套 Environment 规则;规则缺失、stable reviewer 不再是仓库管理员、或 beta 被误加人工审批时都会 fail closed。
- 创建 `release-withdrawal` environment,只允许受保护分支,设置至少一名 required reviewer、禁止申请人自审并关闭管理员绕过。撤回 workflow 会通过 API 复核这些规则;任何一项缺失都会在触碰 npm、OSS、Gitee、Homebrew 或 GitHub Release 前失败。
- 仓库或组织的 Actions 策略必须允许 `Release` 与 `Withdraw release` workflow 的 `GITHUB_TOKEN` 获得各 job 声明的权限;正常发布由 `HOMEBREW_PR_TOKEN` 更新两个受控 Formula 路径,内置 token 只承担 workflow 自身声明的封板与校验写入。若撤回凭证采用 environment secret,确认 `release-withdrawal` 审批完成后能够读取撤回所需的 npm、OSS、Gitee 和 Homebrew 凭证。
+65
View File
@@ -0,0 +1,65 @@
// Copyright 2026 Alibaba Group
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package app
import (
"regexp"
"strings"
apperrors "github.com/DingTalk-Real-AI/dingtalk-workspace-cli/internal/errors"
"github.com/DingTalk-Real-AI/dingtalk-workspace-cli/pkg/configmeta"
)
const (
envDWSAgentHost = "DWS_AGENT_HOST"
headerDWSAgentHost = "x-dws-agent-host"
)
var agentHostPattern = regexp.MustCompile(`^[a-z0-9][a-z0-9_-]*$`)
func init() {
configmeta.Register(configmeta.ConfigItem{
Name: envDWSAgentHost,
Category: configmeta.CategoryExternal,
Description: "调用 DWS 的 Agent 宿主标识;仅用于日志和 BI 观测",
Example: "qwenwork_cloud",
})
}
// parseAgentHost normalizes and validates the caller-provided observation
// label. CR/LF is rejected before trimming so it can never be hidden at the
// edge of a value. An unset or whitespace-only value means "do not emit".
func parseAgentHost(raw string) (string, error) {
if strings.ContainsAny(raw, "\r\n") {
return "", invalidAgentHostError()
}
value := strings.TrimSpace(raw)
if value == "" {
return "", nil
}
if !agentHostPattern.MatchString(value) {
return "", invalidAgentHostError()
}
return value, nil
}
func invalidAgentHostError() error {
// Do not include the raw environment value in the error: it is an
// untrusted caller-controlled string and may contain sensitive data.
return apperrors.NewValidation(
"DWS_AGENT_HOST must match ^[a-z0-9][a-z0-9_-]*$",
apperrors.WithReason("invalid_agent_host"),
)
}
+196
View File
@@ -0,0 +1,196 @@
// Copyright 2026 Alibaba Group
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package app
import (
"errors"
"io"
"strings"
"testing"
authpkg "github.com/DingTalk-Real-AI/dingtalk-workspace-cli/internal/auth"
apperrors "github.com/DingTalk-Real-AI/dingtalk-workspace-cli/internal/errors"
"github.com/DingTalk-Real-AI/dingtalk-workspace-cli/pkg/edition"
"github.com/spf13/cobra"
)
func TestParseAgentHost(t *testing.T) {
valid := []struct {
name string
raw string
want string
}{
{name: "unset", raw: "", want: ""},
{name: "whitespace only", raw: " \t\u3000", want: ""},
{name: "cloud", raw: "qwenwork_cloud", want: "qwenwork_cloud"},
{name: "desktop", raw: "qwenwork_desktop", want: "qwenwork_desktop"},
{name: "trim", raw: " \tqwenwork_cloud\t ", want: "qwenwork_cloud"},
{name: "generic", raw: "host-2_alpha", want: "host-2_alpha"},
{name: "leading digit", raw: "2nd_host", want: "2nd_host"},
}
for _, tc := range valid {
t.Run(tc.name, func(t *testing.T) {
got, err := parseAgentHost(tc.raw)
if err != nil {
t.Fatalf("parseAgentHost() error = %v", err)
}
if got != tc.want {
t.Fatalf("parseAgentHost() = %q, want %q", got, tc.want)
}
})
}
invalid := []struct {
name string
raw string
}{
{name: "carriage return", raw: "qwenwork_cloud\r"},
{name: "line feed", raw: "\nqwenwork_cloud"},
{name: "uppercase", raw: "Qwenwork_cloud"},
{name: "internal space", raw: "qwenwork cloud"},
{name: "internal tab", raw: "qwenwork\tcloud"},
{name: "unicode", raw: "千问办公"},
{name: "leading dash", raw: "-qwenwork"},
{name: "leading underscore", raw: "_qwenwork"},
{name: "control character", raw: "qwenwork\x00cloud"},
}
for _, tc := range invalid {
t.Run(tc.name, func(t *testing.T) {
got, err := parseAgentHost(tc.raw)
if err == nil {
t.Fatalf("parseAgentHost(%q) = %q, want error", tc.raw, got)
}
var appErr *apperrors.Error
if !errors.As(err, &appErr) {
t.Fatalf("parseAgentHost() error type = %T, want *errors.Error", err)
}
if appErr.Category != apperrors.CategoryValidation {
t.Fatalf("category = %q, want validation", appErr.Category)
}
if appErr.Reason != "invalid_agent_host" {
t.Fatalf("reason = %q, want invalid_agent_host", appErr.Reason)
}
if tc.raw != "" && strings.Contains(err.Error(), tc.raw) {
t.Fatalf("error must not echo invalid value %q: %v", tc.raw, err)
}
})
}
}
func TestResolveIdentityHeadersAddsAgentHostBeforeEditionMerge(t *testing.T) {
t.Setenv("DWS_CONFIG_DIR", t.TempDir())
t.Setenv(envDWSAgentHost, " qwenwork_desktop ")
t.Setenv(envDWSChannel, "channel-test")
t.Setenv(envDingtalkAgent, "agent-test")
t.Setenv(authpkg.AgentCodeEnv, "agent-code-test")
oldEdition := edition.Get()
t.Cleanup(func() { edition.Override(oldEdition) })
mergeSawAgentHost := ""
edition.Override(&edition.Hooks{
MergeHeaders: func(headers map[string]string) map[string]string {
mergeSawAgentHost = headers[headerDWSAgentHost]
headers["claw-type"] = "test-claw"
return headers
},
})
headers := resolveIdentityHeaders()
if got := mergeSawAgentHost; got != "qwenwork_desktop" {
t.Fatalf("MergeHeaders saw agent host %q, want qwenwork_desktop", got)
}
if got := headers[headerDWSAgentHost]; got != "qwenwork_desktop" {
t.Fatalf("%s = %q, want qwenwork_desktop", headerDWSAgentHost, got)
}
if got := headers["x-dingtalk-source"]; got != "github" {
t.Fatalf("x-dingtalk-source = %q, want github", got)
}
if got := headers["x-dingtalk-dws-agent-code"]; got != "agent-code-test" {
t.Fatalf("agentCode header = %q, want agent-code-test", got)
}
if got := headers["x-dws-channel"]; got != "channel-test" {
t.Fatalf("channel header = %q, want channel-test", got)
}
if got := headers["claw-type"]; got != "test-claw" {
t.Fatalf("claw-type = %q, want test-claw", got)
}
}
func TestResolveIdentityHeadersOmitsAbsentOrInvalidAgentHost(t *testing.T) {
t.Setenv("DWS_CONFIG_DIR", t.TempDir())
t.Setenv(envDWSChannel, "channel-test")
oldEdition := edition.Get()
t.Cleanup(func() { edition.Override(oldEdition) })
edition.Override(&edition.Hooks{
MergeHeaders: func(headers map[string]string) map[string]string {
return headers
},
})
for _, raw := range []string{"", " \t ", "DO_NOT_ECHO"} {
t.Setenv(envDWSAgentHost, raw)
headers := resolveIdentityHeaders()
if _, ok := headers[headerDWSAgentHost]; ok {
t.Fatalf("%s must be omitted for %q: %#v", headerDWSAgentHost, raw, headers)
}
if got := headers["x-dingtalk-source"]; got != "github" {
t.Fatalf("x-dingtalk-source = %q, want github", got)
}
if got := headers["x-dws-channel"]; got != "channel-test" {
t.Fatalf("channel header = %q, want channel-test", got)
}
}
}
func TestRootRejectsInvalidAgentHostBeforeEditionHook(t *testing.T) {
t.Setenv("DWS_CONFIG_DIR", t.TempDir())
const invalidValue = "DO_NOT_ECHO"
t.Setenv(envDWSAgentHost, invalidValue)
oldEdition := edition.Get()
t.Cleanup(func() { edition.Override(oldEdition) })
hookCalled := false
edition.Override(&edition.Hooks{
AfterPersistentPreRun: func(_ *cobra.Command, _ []string) error {
hookCalled = true
return nil
},
})
root := NewRootCommand()
root.SetOut(io.Discard)
root.SetErr(io.Discard)
root.SetArgs([]string{"version"})
err := root.Execute()
if err == nil {
t.Fatal("root command accepted invalid DWS_AGENT_HOST")
}
if hookCalled {
t.Fatal("edition AfterPersistentPreRun ran before DWS_AGENT_HOST validation")
}
var appErr *apperrors.Error
if !errors.As(err, &appErr) {
t.Fatalf("root error type = %T, want *errors.Error", err)
}
if appErr.Category != apperrors.CategoryValidation || appErr.Reason != "invalid_agent_host" {
t.Fatalf("root error = category %q reason %q", appErr.Category, appErr.Reason)
}
if strings.Contains(err.Error(), invalidValue) {
t.Fatalf("root error must not echo invalid value: %v", err)
}
}
+36 -8
View File
@@ -118,7 +118,7 @@ func newEventConsumeCommand() *cobra.Command {
)
cmd := &cobra.Command{
Use: "consume [event_key]",
Use: "consume [event_key...]",
Short: "订阅事件流并输出到 stdout",
Long: `订阅 DingTalk 个人事件并将每条事件以 NDJSON 输出到 stdout。
@@ -138,9 +138,14 @@ func newEventConsumeCommand() *cobra.Command {
--profile。连上后 stderr 打就绪行 [event] ready,等它出现再读 stdout;停机用
SIGTERM、关 stdin,或先用 dws event stop <subscribe_id> --dry-run 预览、确认后加
--yes,绝不要 kill -9。
可提供多个 event_key。多事件会各自创建订阅和本地 consumer,但共享同一 bus、
远程连接、输出和 duration/max-events。用户类事件必须共享一个 --user 或
--open-dingtalk-id,群类事件必须共享一个 --group;用户类与群类不能混用。
全部 consumer 就绪后 stderr 输出 [event] ready event_count=<n> bus_pid=<pid>。
--event-types/--filter 只影响本地 bus → consume 这一段投递;普通个人事件消费
通常不需要设置。`,
Args: cobra.MaximumNArgs(1),
Args: cobra.ArbitraryArgs,
DisableAutoGenTag: true,
RunE: func(c *cobra.Command, args []string) error {
as, err := eventNormalizeAs(asIdentity)
@@ -148,8 +153,17 @@ SIGTERM、关 stdin,或先用 dws event stop <subscribe_id> --dry-run 预览
return err
}
if as == "user" {
personalOpts.EventKey = firstArg(args)
personalOpts.EventKeys = dedupePersonalEventKeys(args)
personalOpts.EventKey = firstArg(personalOpts.EventKeys)
personalOpts.Flatten = flatten
if len(personalOpts.EventKeys) > 1 {
if err := rejectPersonalMultiEventFlags(c,
"subscribe-id", "rule", "event-types", "filter",
"foreground", "force", "debug-raw-events",
); err != nil {
return fmt.Errorf("event consume: %w", err)
}
}
personalOpts.Common = commonConsumeOptions{
EventTypes: eventTypes,
Filter: filter,
@@ -329,7 +343,7 @@ SIGTERM、关 stdin,或先用 dws event stop <subscribe_id> --dry-run 预览
f.StringVar(&personalOpts.GroupID, "group", "",
"group 规则:openConversationId")
f.StringVar(&personalOpts.ControlBaseURL, "personal-event-base-url", "",
"个人事件控制面 base URL;默认由 MCP base URL 派生为 /dws")
"个人事件控制面 base URL;默认由 MCP base 派生 /dws")
f.BoolVar(&personalOpts.DebugRawEvents, "debug-raw-events", false,
"个人事件联调:绕过本地 event type/subscribe_id 过滤,输出当前 personal stream bus 收到的所有事件")
f.StringVar(&streamOpts.Mode, "stream-ticket-mode", strings.TrimSpace(os.Getenv("DWS_STREAM_TICKET_MODE")),
@@ -337,13 +351,14 @@ SIGTERM、关 stdin,或先用 dws event stop <subscribe_id> --dry-run 预览
f.StringVar(&streamOpts.SourceID, "stream-source-id", strings.TrimSpace(os.Getenv("DWS_STREAM_SOURCE_ID")),
"个人 Stream sourceId;开源版默认 open,可由 edition 覆盖")
f.StringVar(&streamOpts.TicketURL, "stream-ticket-url", strings.TrimSpace(os.Getenv("DWS_STREAM_TICKET_URL")),
"个人 Stream 取票 URL;默认由 MCP base URL 派生")
"个人 Stream 取票 URL;默认由 MCP base 派生 /stream/connections/ticket")
hideEventInternalFlags(cmd, "as")
cli.AnnotateRuntimePositionals(cmd, cli.RuntimeSchemaPositional{
Name: "event_key",
Type: "string",
Description: "要消费的个人事件码;省略时仅适用于显式配置其它事件来源的兼容模式",
Description: "要消费的一个或多个个人事件码;多个事件必须共享同一目标和过滤上下文",
Required: false,
Variadic: true,
Index: 0,
})
return cmd
@@ -777,7 +792,7 @@ func newEventStatusCommand() *cobra.Command {
cmd.Flags().StringVar(&personalOpts.EventKey, "event", "", "个人事件 event_key 过滤")
cmd.Flags().StringVar(&personalOpts.Status, "status", "active", "个人订阅状态过滤: active|paused|error|deleted|all")
cmd.Flags().StringVar(&personalOpts.SubscribeID, "subscribe-id", "", "个人订阅 ID 过滤")
cmd.Flags().StringVar(&personalOpts.ControlBaseURL, "personal-event-base-url", "", "个人事件控制面 base URL;默认由 MCP base URL 派生为 /dws")
cmd.Flags().StringVar(&personalOpts.ControlBaseURL, "personal-event-base-url", "", "个人事件控制面 base URL;默认由 MCP base 派生 /dws")
cmd.Flags().StringVar(&personalOpts.StreamSourceID, "stream-source-id", strings.TrimSpace(os.Getenv("DWS_STREAM_SOURCE_ID")),
"个人事件 sourceId;开源版默认 open,可由 edition 覆盖")
hideEventInternalFlags(cmd, "as", "all", "all-editions", "client-id", "fail-on-orphan")
@@ -1075,7 +1090,7 @@ func newEventStopCommand() *cobra.Command {
},
}
cmd.Flags().StringVar(&asIdentity, "as", "user", "事件身份: user")
cmd.Flags().StringVar(&opts.ControlBaseURL, "personal-event-base-url", "", "个人事件控制面 base URL;默认由 MCP base URL 派生为 /dws")
cmd.Flags().StringVar(&opts.ControlBaseURL, "personal-event-base-url", "", "个人事件控制面 base URL;默认由 MCP base 派生 /dws")
cmd.Flags().StringVar(&opts.StreamSourceID, "stream-source-id", strings.TrimSpace(os.Getenv("DWS_STREAM_SOURCE_ID")),
"个人事件 sourceId;开源版默认 open,可由 edition 覆盖")
cmd.Flags().BoolVar(&opts.All, "all", false, "取消当前身份下本地记录的所有个人订阅")
@@ -1192,6 +1207,19 @@ func rejectPersonalEventUnsupportedFlags(c *cobra.Command, names ...string) erro
return fmt.Errorf("%s are not supported for personal events", strings.Join(changed, ", "))
}
func rejectPersonalMultiEventFlags(c *cobra.Command, names ...string) error {
changed := make([]string, 0, len(names))
for _, name := range names {
if f := c.Flags().Lookup(name); f != nil && f.Changed {
changed = append(changed, "--"+name)
}
}
if len(changed) == 0 {
return nil
}
return fmt.Errorf("%s are not supported when consuming multiple events", strings.Join(changed, ", "))
}
func rejectChangedFlags(c *cobra.Command, supportedAs string, names ...string) error {
changed := make([]string, 0, len(names))
for _, name := range names {
+309 -2
View File
@@ -61,6 +61,7 @@ type commonConsumeOptions struct {
type personalConsumeOptions struct {
Common commonConsumeOptions
EventKey string
EventKeys []string
Flatten bool
DebugRawEvents bool
SubscribeID string
@@ -112,6 +113,7 @@ type personalStreamSourceOptions struct {
var (
personalResolveEventIdentity = resolvePersonalEventIdentity
personalLookupDefinition = personal.Lookup
personalEnsureSubscription = ensurePersonalSubscription
personalGetSubscription = (*personal.Client).GetSubscription
personalCreateSubscription = (*personal.Client).CreateSubscription
@@ -121,6 +123,7 @@ var (
personalRemoveRunStates = personal.RemoveRunStates
personalLoadRunStates = personal.LoadRunStates
personalConsumeRun = consume.Run
personalConsumeRunMany = consume.RunMany
personalValidateConsumeConfig = consume.ValidateConfig
personalValidateNoOutputConflict = consume.ValidateNoOutputConflict
personalNewStreamSource = newPersonalStreamSource
@@ -129,6 +132,7 @@ var (
personalQueryEntry = busctl.QueryEntry
personalQueryStatus = busctl.QueryStatus
personalStopBus = busctl.Stop
personalStopConsumers = busctl.StopConsumers
personalFindProcess = os.FindProcess
personalSignalProcess = (*os.Process).Signal
personalResolveAuxiliaryAccessToken = ResolveAuxiliaryAccessToken
@@ -206,6 +210,21 @@ func renderPersonalSchema(w io.Writer, def personal.Definition, format string, f
}
func runPersonalEventConsume(c *cobra.Command, opts personalConsumeOptions) error {
keys := dedupePersonalEventKeys(opts.EventKeys)
if len(keys) == 0 && strings.TrimSpace(opts.EventKey) != "" {
keys = []string{strings.TrimSpace(opts.EventKey)}
}
if len(keys) <= 1 {
if len(keys) == 1 {
opts.EventKey = keys[0]
}
return runPersonalEventConsumeSingle(c, opts)
}
opts.EventKeys = keys
return runPersonalEventConsumeMany(c, opts)
}
func runPersonalEventConsumeSingle(c *cobra.Command, opts personalConsumeOptions) error {
ctx := c.Context()
if err := ensurePublicPersonalEvent(opts.EventKey); err != nil {
return err
@@ -375,6 +394,266 @@ func runPersonalEventConsume(c *cobra.Command, opts personalConsumeOptions) erro
return err
}
type personalMultiSubscription struct {
Sub *personal.Subscription
EventKey string
RuleType string
}
func runPersonalEventConsumeMany(c *cobra.Command, opts personalConsumeOptions) error {
plans, err := preparePersonalMultiOptions(opts)
if err != nil {
return fmt.Errorf("event consume --as user: %w", err)
}
rawFormat := ""
if f := c.Flags().Lookup("format"); f != nil && f.Changed {
rawFormat = opts.Common.FormatRaw
}
normalised, fellback := consume.NormalizeFormat(rawFormat)
if fellback && !opts.Common.Quiet {
fmt.Fprintf(c.ErrOrStderr(), "WARN: --format %q has no meaning for event stream; using ndjson\n", rawFormat)
}
if err := validatePersonalEventOutputMode(opts.Flatten, opts.DebugRawEvents, normalised); err != nil {
return fmt.Errorf("event consume --as user: %w", err)
}
projector := personalEventProjector(false, opts.Flatten)
ctx := c.Context()
configDir := defaultConfigDir()
identity, err := personalResolveEventIdentity(ctx, configDir, opts.StreamSourceID)
if err != nil {
return fmt.Errorf("event consume --as user: %w", err)
}
identityHash := dwsevent.IdentityHash(identity.Key())
editionName := editionNameOrDefault()
workDir := eventWorkDir(configDir, editionName, dwsevent.SourceKindPersonalStream, identityHash)
ipcEndpoint := defaultIPCEndpoint(workDir, editionName, dwsevent.SourceKindPersonalStream, identityHash)
routes, err := consume.ParseRoutes(opts.Common.RoutesRaw)
if err != nil {
return fmt.Errorf("event consume --as user: %w", err)
}
baseCfg := consume.Config{
WorkDir: workDir,
IPCEndpoint: ipcEndpoint,
ClientID: identity.ClientID,
SpawnExtraArgs: personalBusSpawnArgs(identity, opts.StreamTicketMode, personalEventStreamTicketURL(opts.StreamTicketURL, configDir)),
Compact: opts.Common.Compact,
MaxEvents: opts.Common.MaxEvents,
Duration: opts.Common.Duration,
Format: normalised,
Flatten: opts.Flatten,
OutputDir: opts.Common.OutputDir,
Routes: routes,
Projector: projector,
Stdout: c.OutOrStdout(),
Stderr: c.ErrOrStderr(),
Quiet: opts.Common.Quiet,
}
applyEventConsumeStdin(&baseCfg, opts.Common.MaxEvents, opts.Common.Duration, c.InOrStdin())
if err := personalValidateConsumeConfig(baseCfg); err != nil {
return err
}
if o := c.Flags().Lookup("output"); o != nil && o.Changed {
if err := personalValidateNoOutputConflict(baseCfg, o.Value.String()); err != nil {
return err
}
}
if opts.Common.DryRun {
printPersonalMultiDryRun(c.ErrOrStderr(), baseCfg, plans)
return nil
}
client := newPersonalEventControlClient(configDir, personalEventControlBaseURL(opts.ControlBaseURL, configDir), identity)
created := make([]personalMultiSubscription, 0, len(plans))
cleanup := func() {
ids := make([]string, 0, len(created))
for i := len(created) - 1; i >= 0; i-- {
id := strings.TrimSpace(created[i].Sub.SubscribeID)
ids = append(ids, id)
if err := personalDeleteSubscription(client, context.Background(), id); err != nil {
fmt.Fprintf(c.ErrOrStderr(), "WARN: failed to clean personal subscription %s: %v\n", id, err)
}
}
if len(ids) > 0 {
if err := personalRemoveRunStates(workDir, ids); err != nil {
fmt.Fprintf(c.ErrOrStderr(), "WARN: failed to clean personal event run state: %v\n", err)
}
}
}
seenSubscribeIDs := make(map[string]struct{}, len(plans))
for _, plan := range plans {
sub, eventKey, ruleType, err := personalEnsureSubscription(ctx, client, identity, plan)
if err != nil {
cleanup()
return fmt.Errorf("event consume --as user: create subscription for %s: %w", plan.EventKey, err)
}
if sub == nil {
cleanup()
return fmt.Errorf("event consume --as user: server returned an empty subscription for %s", plan.EventKey)
}
id := strings.TrimSpace(sub.SubscribeID)
if id == "" {
cleanup()
return fmt.Errorf("event consume --as user: server returned empty subscribe_id for %s", plan.EventKey)
}
if _, exists := seenSubscribeIDs[id]; exists {
_ = personalDeleteSubscription(client, context.Background(), id)
cleanup()
return fmt.Errorf("event consume --as user: server returned duplicate subscribe_id %s", id)
}
seenSubscribeIDs[id] = struct{}{}
item := personalMultiSubscription{Sub: sub, EventKey: eventKey, RuleType: ruleType}
created = append(created, item)
if err := personalUpsertRunState(workDir, personal.RunState{
SubscribeID: id,
EventKey: eventKey,
RuleType: ruleType,
ClientID: identity.ClientID,
SourceID: identity.SourceID,
IdentityHash: identityHash,
}); err != nil {
cleanup()
return fmt.Errorf("event consume --as user: save run state for %s: %w", eventKey, err)
}
}
defer cleanup()
specs := make([]consume.ConsumerSpec, 0, len(created))
for _, item := range created {
specs = append(specs, consume.ConsumerSpec{
EventKey: item.EventKey,
EventTypes: []string{item.EventKey},
SubscribeID: item.Sub.SubscribeID,
ReadySubscribeID: item.Sub.SubscribeID,
})
}
if err := personalConsumeRunMany(ctx, baseCfg, specs); err != nil {
return err
}
return nil
}
func preparePersonalMultiOptions(opts personalConsumeOptions) ([]personalConsumeOptions, error) {
if strings.TrimSpace(opts.SubscribeID) != "" {
return nil, errors.New("--subscribe-id is not supported when consuming multiple events")
}
if strings.TrimSpace(opts.Rule) != "" {
return nil, errors.New("--rule is not supported when consuming multiple events")
}
if len(opts.Common.EventTypes) > 0 {
return nil, errors.New("--event-types is not supported when consuming multiple events; use event_key positionals")
}
if strings.TrimSpace(opts.Common.Filter) != "" {
return nil, errors.New("--filter is not supported when consuming multiple events; use event_key positionals")
}
if opts.Common.Foreground || opts.Common.Force {
return nil, errors.New("--foreground/--force are not supported when consuming multiple events")
}
if opts.DebugRawEvents {
return nil, errors.New("--debug-raw-events is not supported when consuming multiple events")
}
keys := dedupePersonalEventKeys(opts.EventKeys)
if len(keys) < 2 {
return nil, errors.New("multiple event keys are required")
}
hasUserScope := false
hasGroupScope := false
for _, eventKey := range keys {
def, ok := personalLookupDefinition(eventKey)
if !ok {
return nil, fmt.Errorf("unknown personal event key %q", eventKey)
}
if !def.Public {
return nil, personal.PublicAvailabilityError(eventKey)
}
switch def.RuleType {
case "singleChat", "sender":
hasUserScope = true
case "group":
hasGroupScope = true
}
if (strings.TrimSpace(opts.QueryCSV) != "" || strings.TrimSpace(opts.FilterJSON) != "") && !personal.SupportsMessageFilter(eventKey) {
return nil, fmt.Errorf("--query/--filter-json require all selected events to be message receive events; %s is not", eventKey)
}
}
if hasUserScope && hasGroupScope {
return nil, errors.New("user-scoped and group-scoped events cannot be consumed in one command")
}
userID := strings.TrimSpace(opts.UserID)
openID := strings.TrimSpace(opts.OpenDingTalkID)
groupID := strings.TrimSpace(opts.GroupID)
if userID != "" && openID != "" {
return nil, errors.New("--user and --open-dingtalk-id are mutually exclusive")
}
switch {
case hasUserScope:
if groupID != "" {
return nil, errors.New("--group cannot be used with user-scoped events")
}
if userID == "" && openID == "" {
return nil, errors.New("one of --user or --open-dingtalk-id is required for the selected events")
}
case hasGroupScope:
if userID != "" || openID != "" {
return nil, errors.New("--user/--open-dingtalk-id cannot be used with group-scoped events")
}
if groupID == "" {
return nil, errors.New("--group is required for the selected events")
}
default:
if userID != "" || openID != "" || groupID != "" {
return nil, errors.New("the selected events do not use --user, --open-dingtalk-id, or --group")
}
}
plans := make([]personalConsumeOptions, 0, len(keys))
for _, eventKey := range keys {
def, _ := personalLookupDefinition(eventKey)
plan := opts
plan.EventKey = eventKey
plan.EventKeys = nil
switch def.RuleType {
case "at", "all":
plan.UserID = ""
plan.OpenDingTalkID = ""
plan.GroupID = ""
case "singleChat", "sender":
plan.GroupID = ""
case "group":
plan.UserID = ""
plan.OpenDingTalkID = ""
}
if err := validatePersonalSubscriptionOptions(plan); err != nil {
return nil, err
}
plans = append(plans, plan)
}
return plans, nil
}
func printPersonalMultiDryRun(w io.Writer, cfg consume.Config, plans []personalConsumeOptions) {
preview := cfg
preview.EventTypes = make([]string, 0, len(plans))
for _, plan := range plans {
preview.EventTypes = append(preview.EventTypes, plan.EventKey)
}
consume.PrintDryRun(w, preview)
for i, plan := range plans {
ruleType, ruleParam, _ := personal.BuildRuleParam(plan.EventKey, personal.RuleOptions{
UserID: plan.UserID, OpenDingTalkID: plan.OpenDingTalkID, GroupID: plan.GroupID,
})
_, filter, _ := personal.BuildFilter(plan.FilterJSON, plan.QueryCSV)
ruleJSON, _ := personal.CanonicalJSON(ruleParam)
fmt.Fprintf(w, " subscription[%d] : event_key=%s rule_type=%s rule_param=%s",
i, plan.EventKey, ruleType, ruleJSON)
if filter != "" {
fmt.Fprintf(w, " filter=%s", filter)
}
fmt.Fprintln(w)
}
}
func personalEventProjector(debugRawEvents, flatten bool) consume.Projector {
if debugRawEvents {
return func(ev transport.Event) (any, error) { return ev, nil }
@@ -648,7 +927,7 @@ func runPersonalEventStop(c *cobra.Command, opts personalStopOptions) error {
if err := personalRemoveRunStates(workDir, subscribeIDs); err != nil {
return fmt.Errorf("event stop --as user: update local state: %w", err)
}
if err := interruptPersonalConsumers(ipcEndpoint, subscribeIDs); err != nil {
if err := stopPersonalConsumers(c.ErrOrStderr(), ipcEndpoint, subscribeIDs); err != nil {
fmt.Fprintf(c.ErrOrStderr(), "WARN: failed to stop matching local consume process: %v\n", err)
}
@@ -736,6 +1015,17 @@ func interruptPersonalConsumers(ipcEndpoint string, subscribeIDs []string) error
return nil
}
func stopPersonalConsumers(w io.Writer, ipcEndpoint string, subscribeIDs []string) error {
if _, err := personalStopConsumers(ipcEndpoint, subscribeIDs); err == nil {
return nil
} else if !errors.Is(err, busctl.ErrConsumerStopUnsupported) {
return err
} else {
fmt.Fprintf(w, "WARN: running bus does not support targeted consumer stop; falling back to process signal: %v\n", err)
}
return interruptPersonalConsumers(ipcEndpoint, subscribeIDs)
}
func printPersonalStopResult(w io.Writer, subscribeIDs []string, single bool, busState string) {
if single && len(subscribeIDs) == 1 {
fmt.Fprintf(w, "cancelled personal subscription %s; %s\n", subscribeIDs[0], busState)
@@ -968,6 +1258,23 @@ func firstNonEmptyPersonalString(values ...string) string {
return ""
}
func dedupePersonalEventKeys(values []string) []string {
out := make([]string, 0, len(values))
seen := make(map[string]struct{}, len(values))
for _, value := range values {
value = strings.TrimSpace(value)
if value == "" {
continue
}
if _, ok := seen[value]; ok {
continue
}
seen[value] = struct{}{}
out = append(out, value)
}
return out
}
func personalEventControlBaseURL(raw, configDir string) string {
if v := strings.TrimSpace(raw); v != "" {
return strings.TrimRight(v, "/")
@@ -993,7 +1300,7 @@ func personalEventMCPBaseURL(configDir string) string {
if v := configuredMCPBaseURL(configDir); v != "" {
return strings.TrimRight(v, "/")
}
return config.DefaultMCPBaseURL
return strings.TrimRight(config.DefaultMCPBaseURL, "/")
}
func configuredMCPBaseURL(configDir string) string {
+661
View File
@@ -0,0 +1,661 @@
// Copyright 2026 Alibaba Group
// Licensed under the Apache License, Version 2.0 (the "License");
package app
import (
"bytes"
"context"
"errors"
"io"
"os"
"reflect"
"strings"
"testing"
"github.com/DingTalk-Real-AI/dingtalk-workspace-cli/internal/event/busctl"
"github.com/DingTalk-Real-AI/dingtalk-workspace-cli/internal/event/consume"
"github.com/DingTalk-Real-AI/dingtalk-workspace-cli/internal/event/personal"
"github.com/DingTalk-Real-AI/dingtalk-workspace-cli/internal/event/transport"
"github.com/spf13/cobra"
)
func TestEventConsumeAcceptsOrderedVariadicEventKeys(t *testing.T) {
oldRun := eventRunPersonalConsume
defer func() { eventRunPersonalConsume = oldRun }()
var got personalConsumeOptions
eventRunPersonalConsume = func(_ *cobra.Command, opts personalConsumeOptions) error {
got = opts
return nil
}
cmd := newEventConsumeCommand()
cmd.SetOut(io.Discard)
cmd.SetErr(io.Discard)
cmd.SetArgs([]string{
personal.EventMention,
personal.EventSingleChat,
personal.EventMention,
"--user", "test-user-001",
})
if err := cmd.Execute(); err != nil {
t.Fatalf("Execute() error = %v", err)
}
want := []string{personal.EventMention, personal.EventSingleChat}
if !reflect.DeepEqual(got.EventKeys, want) || got.EventKey != personal.EventMention {
t.Fatalf("event keys = %#v, first = %q", got.EventKeys, got.EventKey)
}
}
func TestPreparePersonalMultiOptionsCombinationMatrix(t *testing.T) {
tests := []struct {
name string
opts personalConsumeOptions
wantErr string
}{
{
name: "no target events",
opts: personalConsumeOptions{EventKeys: []string{personal.EventMention, personal.EventAllSingleChat}},
},
{
name: "user and no target",
opts: personalConsumeOptions{
EventKeys: []string{personal.EventSingleChat, personal.EventReadO2O, personal.EventMention},
UserID: "test-user-001",
},
},
{
name: "group and no target",
opts: personalConsumeOptions{
EventKeys: []string{personal.EventInChat, personal.EventGroupUpdated, personal.EventMention},
GroupID: "cid-test",
},
},
{
name: "open dingtalk id",
opts: personalConsumeOptions{
EventKeys: []string{personal.EventSingleChat, personal.EventRecallO2O},
OpenDingTalkID: "open-test-user",
},
},
{
name: "user and group mixed",
opts: personalConsumeOptions{
EventKeys: []string{personal.EventSingleChat, personal.EventInChat},
UserID: "test-user-001",
},
wantErr: "cannot be consumed in one command",
},
{
name: "duplicate keys collapse to one",
opts: personalConsumeOptions{
EventKeys: []string{personal.EventMention, personal.EventMention},
},
wantErr: "multiple event keys are required",
},
{
name: "unknown event",
opts: personalConsumeOptions{
EventKeys: []string{personal.EventMention, "user_im_unknown"},
},
wantErr: "unknown personal event key",
},
{
name: "missing user target",
opts: personalConsumeOptions{EventKeys: []string{personal.EventSingleChat, personal.EventReadO2O}},
wantErr: "one of --user or --open-dingtalk-id",
},
{
name: "missing group target",
opts: personalConsumeOptions{
EventKeys: []string{personal.EventInChat, personal.EventGroupUpdated},
},
wantErr: "--group is required",
},
{
name: "user identity flags conflict",
opts: personalConsumeOptions{
EventKeys: []string{personal.EventSingleChat, personal.EventReadO2O},
UserID: "test-user-001",
OpenDingTalkID: "open-test-user",
},
wantErr: "mutually exclusive",
},
{
name: "group target on user events",
opts: personalConsumeOptions{
EventKeys: []string{personal.EventSingleChat, personal.EventReadO2O},
UserID: "test-user-001",
GroupID: "cid-test",
},
wantErr: "--group cannot be used",
},
{
name: "user target on group events",
opts: personalConsumeOptions{
EventKeys: []string{personal.EventInChat, personal.EventGroupUpdated},
UserID: "test-user-001",
GroupID: "cid-test",
},
wantErr: "cannot be used with group-scoped events",
},
{
name: "target on no target events",
opts: personalConsumeOptions{
EventKeys: []string{personal.EventMention, personal.EventAllSingleChat},
UserID: "test-user-001",
},
wantErr: "do not use --user",
},
{
name: "filter message events",
opts: personalConsumeOptions{
EventKeys: []string{personal.EventMention, personal.EventAllGroupChat},
QueryCSV: "alarm",
},
},
{
name: "filter mixed with action",
opts: personalConsumeOptions{
EventKeys: []string{personal.EventSingleChat, personal.EventReadO2O},
UserID: "test-user-001",
QueryCSV: "alarm",
},
wantErr: "require all selected events to be message receive events",
},
{
name: "invalid message filter",
opts: personalConsumeOptions{
EventKeys: []string{personal.EventMention, personal.EventAllSingleChat},
FilterJSON: "{",
},
wantErr: "filter",
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
plans, err := preparePersonalMultiOptions(test.opts)
if test.wantErr != "" {
if err == nil || !strings.Contains(err.Error(), test.wantErr) {
t.Fatalf("error = %v, want %q", err, test.wantErr)
}
return
}
if err != nil {
t.Fatalf("preparePersonalMultiOptions() error = %v", err)
}
if len(plans) != len(test.opts.EventKeys) {
t.Fatalf("plans = %d, want %d", len(plans), len(test.opts.EventKeys))
}
for _, plan := range plans {
def, _ := personal.Lookup(plan.EventKey)
if def.RuleType == "at" || def.RuleType == "all" {
if plan.UserID != "" || plan.OpenDingTalkID != "" || plan.GroupID != "" {
t.Fatalf("no-target plan retained target: %#v", plan)
}
}
}
})
}
}
func TestPreparePersonalMultiOptionsRejectsNonPublicEvent(t *testing.T) {
oldLookup := personalLookupDefinition
t.Cleanup(func() { personalLookupDefinition = oldLookup })
personalLookupDefinition = func(eventKey string) (personal.Definition, bool) {
def, ok := personal.Lookup(eventKey)
if eventKey == personal.EventAllSingleChat {
def.Public = false
}
return def, ok
}
_, err := preparePersonalMultiOptions(personalConsumeOptions{
EventKeys: []string{personal.EventMention, personal.EventAllSingleChat},
})
if err == nil || !strings.Contains(err.Error(), "not publicly available") {
t.Fatalf("error = %v", err)
}
}
func TestDedupePersonalEventKeysSkipsEmptyValues(t *testing.T) {
got := dedupePersonalEventKeys([]string{"", " event-a ", "event-a", "event-b"})
if !reflect.DeepEqual(got, []string{"event-a", "event-b"}) {
t.Fatalf("deduped keys = %#v", got)
}
}
func TestPreparePersonalMultiOptionsRejectsSingleOnlyFlags(t *testing.T) {
base := personalConsumeOptions{EventKeys: []string{personal.EventMention, personal.EventAllSingleChat}}
tests := []struct {
name string
set func(*personalConsumeOptions)
}{
{name: "subscribe-id", set: func(o *personalConsumeOptions) { o.SubscribeID = "sub" }},
{name: "rule", set: func(o *personalConsumeOptions) { o.Rule = "all" }},
{name: "event-types", set: func(o *personalConsumeOptions) { o.Common.EventTypes = []string{"x"} }},
{name: "filter", set: func(o *personalConsumeOptions) { o.Common.Filter = "x" }},
{name: "foreground", set: func(o *personalConsumeOptions) { o.Common.Foreground = true }},
{name: "force", set: func(o *personalConsumeOptions) { o.Common.Force = true }},
{name: "debug-raw-events", set: func(o *personalConsumeOptions) { o.DebugRawEvents = true }},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
opts := base
test.set(&opts)
if _, err := preparePersonalMultiOptions(opts); err == nil {
t.Fatal("option succeeded")
}
})
}
}
func TestEventConsumeMultiRejectsExplicitSingleOnlyFlagsEvenWhenEmpty(t *testing.T) {
oldRun := eventRunPersonalConsume
defer func() { eventRunPersonalConsume = oldRun }()
eventRunPersonalConsume = func(*cobra.Command, personalConsumeOptions) error {
t.Fatal("personal consume ran after explicit multi-event flag")
return nil
}
flags := []string{
"--subscribe-id=",
"--rule=",
"--event-types=",
"--filter=",
"--foreground=false",
"--force=false",
"--debug-raw-events=false",
}
for _, flag := range flags {
t.Run(flag, func(t *testing.T) {
cmd := newEventConsumeCommand()
cmd.SetOut(io.Discard)
cmd.SetErr(io.Discard)
cmd.SetArgs([]string{personal.EventMention, personal.EventAllSingleChat, flag})
err := cmd.Execute()
if err == nil || !strings.Contains(err.Error(), "not supported when consuming multiple events") {
t.Fatalf("Execute() error = %v", err)
}
})
}
}
func TestRunPersonalEventConsumeManyCreatesAndCleansAllSubscriptions(t *testing.T) {
restore := installPersonalManySeams(t)
defer restore()
t.Setenv("DWS_CONFIG_DIR", t.TempDir())
identity := personal.Identity{AccessToken: "token", CorpID: "corp", UserID: "user", ClientID: "client", SourceID: "open"}
personalResolveEventIdentity = func(context.Context, string, string) (personal.Identity, error) { return identity, nil }
createdKeys := make([]string, 0, 2)
personalEnsureSubscription = func(_ context.Context, _ *personal.Client, _ personal.Identity, opts personalConsumeOptions) (*personal.Subscription, string, string, error) {
createdKeys = append(createdKeys, opts.EventKey)
return &personal.Subscription{SubscribeID: "sub-" + opts.EventKey}, opts.EventKey, "all", nil
}
var states []personal.RunState
personalUpsertRunState = func(_ string, state personal.RunState) error {
states = append(states, state)
return nil
}
var deleted []string
personalDeleteSubscription = func(_ *personal.Client, _ context.Context, id string) error {
deleted = append(deleted, id)
return nil
}
var removed []string
personalRemoveRunStates = func(_ string, ids []string) error {
removed = append(removed, ids...)
return nil
}
personalValidateConsumeConfig = func(consume.Config) error { return nil }
personalConsumeRunMany = func(_ context.Context, cfg consume.Config, specs []consume.ConsumerSpec) error {
if !cfg.Flatten || cfg.Projector == nil || len(specs) != 2 {
t.Fatalf("consume config/specs = %#v / %#v", cfg, specs)
}
for i, spec := range specs {
if spec.EventKey != createdKeys[i] || spec.SubscribeID != "sub-"+createdKeys[i] || !reflect.DeepEqual(spec.EventTypes, []string{createdKeys[i]}) {
t.Fatalf("spec[%d] = %#v", i, spec)
}
}
return nil
}
cmd := newPersonalCoverageCommand()
err := runPersonalEventConsume(cmd, personalConsumeOptions{
EventKeys: []string{personal.EventMention, personal.EventAllSingleChat},
Flatten: true,
})
if err != nil {
t.Fatalf("runPersonalEventConsume() error = %v", err)
}
if len(states) != 2 || len(deleted) != 2 || len(removed) != 2 {
t.Fatalf("states=%#v deleted=%#v removed=%#v", states, deleted, removed)
}
}
func TestRunPersonalEventConsumeManyRollsBackPartialCreation(t *testing.T) {
restore := installPersonalManySeams(t)
defer restore()
t.Setenv("DWS_CONFIG_DIR", t.TempDir())
personalResolveEventIdentity = func(context.Context, string, string) (personal.Identity, error) {
return personal.Identity{AccessToken: "token", ClientID: "client", SourceID: "open", LocalSubject: "subject"}, nil
}
wantErr := errors.New("second subscription failed")
calls := 0
personalEnsureSubscription = func(_ context.Context, _ *personal.Client, _ personal.Identity, opts personalConsumeOptions) (*personal.Subscription, string, string, error) {
calls++
if calls == 2 {
return nil, "", "", wantErr
}
return &personal.Subscription{SubscribeID: "sub-first"}, opts.EventKey, "all", nil
}
personalUpsertRunState = func(string, personal.RunState) error { return nil }
var deleted []string
personalDeleteSubscription = func(_ *personal.Client, _ context.Context, id string) error {
deleted = append(deleted, id)
return nil
}
var removed []string
personalRemoveRunStates = func(_ string, ids []string) error {
removed = append(removed, ids...)
return nil
}
personalValidateConsumeConfig = func(consume.Config) error { return nil }
personalConsumeRunMany = func(context.Context, consume.Config, []consume.ConsumerSpec) error {
t.Fatal("RunMany called after partial creation failure")
return nil
}
err := runPersonalEventConsume(newPersonalCoverageCommand(), personalConsumeOptions{
EventKeys: []string{personal.EventMention, personal.EventAllSingleChat},
})
if !errors.Is(err, wantErr) {
t.Fatalf("error = %v", err)
}
if !reflect.DeepEqual(deleted, []string{"sub-first"}) || !reflect.DeepEqual(removed, []string{"sub-first"}) {
t.Fatalf("rollback deleted=%#v removed=%#v", deleted, removed)
}
}
func TestRunPersonalEventConsumeManyRejectsInvalidSubscriptionResults(t *testing.T) {
for _, test := range []struct {
name string
ensure func(int, personalConsumeOptions) *personal.Subscription
upsertErr error
wantErr string
}{
{
name: "nil subscription",
ensure: func(int, personalConsumeOptions) *personal.Subscription { return nil },
wantErr: "empty subscription",
},
{
name: "empty subscribe id",
ensure: func(int, personalConsumeOptions) *personal.Subscription { return &personal.Subscription{} },
wantErr: "empty subscribe_id",
},
{
name: "duplicate subscribe id",
ensure: func(int, personalConsumeOptions) *personal.Subscription {
return &personal.Subscription{SubscribeID: "sub-duplicate"}
},
wantErr: "duplicate subscribe_id",
},
{
name: "run state write failure",
ensure: func(_ int, opts personalConsumeOptions) *personal.Subscription {
return &personal.Subscription{SubscribeID: "sub-" + opts.EventKey}
},
upsertErr: errors.New("state write failed"),
wantErr: "save run state",
},
} {
t.Run(test.name, func(t *testing.T) {
restore := installPersonalManySeams(t)
defer restore()
t.Setenv("DWS_CONFIG_DIR", t.TempDir())
personalResolveEventIdentity = func(context.Context, string, string) (personal.Identity, error) {
return personal.Identity{AccessToken: "token", ClientID: "client", SourceID: "open", LocalSubject: "subject"}, nil
}
calls := 0
personalEnsureSubscription = func(_ context.Context, _ *personal.Client, _ personal.Identity, opts personalConsumeOptions) (*personal.Subscription, string, string, error) {
calls++
return test.ensure(calls, opts), opts.EventKey, "all", nil
}
personalUpsertRunState = func(string, personal.RunState) error { return test.upsertErr }
personalDeleteSubscription = func(*personal.Client, context.Context, string) error { return nil }
personalRemoveRunStates = func(string, []string) error { return nil }
personalValidateConsumeConfig = func(consume.Config) error { return nil }
personalConsumeRunMany = func(context.Context, consume.Config, []consume.ConsumerSpec) error {
t.Fatal("RunMany called with invalid subscription result")
return nil
}
err := runPersonalEventConsume(newPersonalCoverageCommand(), personalConsumeOptions{
EventKeys: []string{personal.EventMention, personal.EventAllSingleChat},
})
if err == nil || !strings.Contains(err.Error(), test.wantErr) {
t.Fatalf("error = %v, want %q", err, test.wantErr)
}
})
}
}
func TestRunPersonalEventConsumeManyDryRunDoesNotCreateSubscriptions(t *testing.T) {
restore := installPersonalManySeams(t)
defer restore()
t.Setenv("DWS_CONFIG_DIR", t.TempDir())
personalResolveEventIdentity = func(context.Context, string, string) (personal.Identity, error) {
return personal.Identity{AccessToken: "token", ClientID: "client", SourceID: "open", LocalSubject: "subject"}, nil
}
personalEnsureSubscription = func(context.Context, *personal.Client, personal.Identity, personalConsumeOptions) (*personal.Subscription, string, string, error) {
t.Fatal("dry-run created a subscription")
return nil, "", "", nil
}
personalValidateConsumeConfig = func(consume.Config) error { return nil }
cmd := newPersonalCoverageCommand()
var stderr bytes.Buffer
cmd.SetErr(&stderr)
err := runPersonalEventConsume(cmd, personalConsumeOptions{
EventKeys: []string{personal.EventMention, personal.EventAllSingleChat},
QueryCSV: "alarm",
Common: commonConsumeOptions{DryRun: true},
})
if err != nil {
t.Fatalf("dry-run error = %v", err)
}
if !strings.Contains(stderr.String(), "subscription[0]") ||
!strings.Contains(stderr.String(), "subscription[1]") ||
!strings.Contains(stderr.String(), "filter=") {
t.Fatalf("dry-run subscriptions missing:\n%s", stderr.String())
}
}
func TestCrossPlatformCoverageRunPersonalEventConsumeManySetupAndCleanupEdges(t *testing.T) {
valid := personalConsumeOptions{EventKeys: []string{personal.EventMention, personal.EventAllSingleChat}}
t.Run("prepare error", func(t *testing.T) {
err := runPersonalEventConsumeMany(newPersonalCoverageCommand(), personalConsumeOptions{
EventKeys: []string{personal.EventMention},
})
if err == nil || !strings.Contains(err.Error(), "multiple event keys") {
t.Fatalf("error = %v", err)
}
})
t.Run("format warning and identity error", func(t *testing.T) {
restore := installPersonalManySeams(t)
defer restore()
cmd := newPersonalCoverageCommand()
var stderr bytes.Buffer
cmd.SetErr(&stderr)
if err := cmd.Flags().Set("format", "bogus"); err != nil {
t.Fatal(err)
}
wantErr := errors.New("identity")
personalResolveEventIdentity = func(context.Context, string, string) (personal.Identity, error) {
return personal.Identity{}, wantErr
}
opts := valid
opts.Common.FormatRaw = "bogus"
if err := runPersonalEventConsumeMany(cmd, opts); !errors.Is(err, wantErr) {
t.Fatalf("error = %v", err)
}
if !strings.Contains(stderr.String(), "using ndjson") {
t.Fatalf("warning = %q", stderr.String())
}
})
t.Run("flatten raw conflict", func(t *testing.T) {
cmd := newPersonalCoverageCommand()
if err := cmd.Flags().Set("format", "raw"); err != nil {
t.Fatal(err)
}
opts := valid
opts.Flatten = true
opts.Common.FormatRaw = "raw"
if err := runPersonalEventConsumeMany(cmd, opts); err == nil || !strings.Contains(err.Error(), "mutually exclusive") {
t.Fatalf("error = %v", err)
}
})
t.Run("route validation and output conflict", func(t *testing.T) {
restore := installPersonalManySeams(t)
defer restore()
personalResolveEventIdentity = func(context.Context, string, string) (personal.Identity, error) {
return personal.Identity{AccessToken: "token", ClientID: "client", SourceID: "open", LocalSubject: "subject"}, nil
}
opts := valid
opts.Common.RoutesRaw = []string{"bad-route"}
if err := runPersonalEventConsumeMany(newPersonalCoverageCommand(), opts); err == nil {
t.Fatal("invalid route succeeded")
}
wantErr := errors.New("validate")
personalValidateConsumeConfig = func(consume.Config) error { return wantErr }
if err := runPersonalEventConsumeMany(newPersonalCoverageCommand(), valid); !errors.Is(err, wantErr) {
t.Fatalf("config validation error = %v", err)
}
personalValidateConsumeConfig = func(consume.Config) error { return nil }
personalValidateNoOutputConflict = func(consume.Config, string) error { return wantErr }
cmd := newPersonalCoverageCommand()
if err := cmd.Flags().Set("output", "events.json"); err != nil {
t.Fatal(err)
}
if err := runPersonalEventConsumeMany(cmd, valid); !errors.Is(err, wantErr) {
t.Fatalf("output conflict error = %v", err)
}
})
t.Run("runtime error reports cleanup failures", func(t *testing.T) {
restore := installPersonalManySeams(t)
defer restore()
personalResolveEventIdentity = func(context.Context, string, string) (personal.Identity, error) {
return personal.Identity{AccessToken: "token", ClientID: "client", SourceID: "open", LocalSubject: "subject"}, nil
}
personalEnsureSubscription = func(_ context.Context, _ *personal.Client, _ personal.Identity, opts personalConsumeOptions) (*personal.Subscription, string, string, error) {
return &personal.Subscription{SubscribeID: "sub-" + opts.EventKey}, opts.EventKey, "all", nil
}
personalUpsertRunState = func(string, personal.RunState) error { return nil }
personalValidateConsumeConfig = func(consume.Config) error { return nil }
cleanupErr := errors.New("cleanup")
personalDeleteSubscription = func(*personal.Client, context.Context, string) error { return cleanupErr }
personalRemoveRunStates = func(string, []string) error { return cleanupErr }
runErr := errors.New("run many")
personalConsumeRunMany = func(context.Context, consume.Config, []consume.ConsumerSpec) error { return runErr }
cmd := newPersonalCoverageCommand()
var stderr bytes.Buffer
cmd.SetErr(&stderr)
if err := runPersonalEventConsumeMany(cmd, valid); !errors.Is(err, runErr) {
t.Fatalf("runtime error = %v", err)
}
if !strings.Contains(stderr.String(), "failed to clean personal subscription") ||
!strings.Contains(stderr.String(), "failed to clean personal event run state") {
t.Fatalf("cleanup warnings = %q", stderr.String())
}
})
}
func TestStopPersonalConsumersUsesTargetedRPCAndLegacyFallback(t *testing.T) {
oldStop := personalStopConsumers
oldQuery := personalQueryStatus
oldFind := personalFindProcess
oldSignal := personalSignalProcess
defer func() {
personalStopConsumers = oldStop
personalQueryStatus = oldQuery
personalFindProcess = oldFind
personalSignalProcess = oldSignal
}()
personalStopConsumers = func(string, []string) (transport.ConsumerStopResp, error) {
return transport.ConsumerStopResp{Stopped: []string{"sub-a"}}, nil
}
personalQueryStatus = func(string) (*transport.StatusResp, error) {
t.Fatal("legacy status queried after targeted stop succeeded")
return nil, nil
}
if err := stopPersonalConsumers(io.Discard, "endpoint", []string{"sub-a"}); err != nil {
t.Fatal(err)
}
personalStopConsumers = func(string, []string) (transport.ConsumerStopResp, error) {
return transport.ConsumerStopResp{}, busctl.ErrConsumerStopUnsupported
}
personalQueryStatus = func(string) (*transport.StatusResp, error) {
return &transport.StatusResp{Consumers: []transport.StatusConsumer{{PID: 321, SubscribeID: "sub-a"}}}, nil
}
proc := &os.Process{}
personalFindProcess = func(int) (*os.Process, error) { return proc, nil }
signals := 0
personalSignalProcess = func(*os.Process, os.Signal) error { signals++; return nil }
var warning bytes.Buffer
if err := stopPersonalConsumers(&warning, "endpoint", []string{"sub-a"}); err != nil {
t.Fatal(err)
}
if signals != 1 || !strings.Contains(warning.String(), "falling back to process signal") {
t.Fatalf("signals=%d warning=%q", signals, warning.String())
}
wantErr := errors.New("targeted stop transport failed")
personalStopConsumers = func(string, []string) (transport.ConsumerStopResp, error) {
return transport.ConsumerStopResp{}, wantErr
}
personalQueryStatus = func(string) (*transport.StatusResp, error) {
t.Fatal("legacy fallback ran for a non-compatibility error")
return nil, nil
}
if err := stopPersonalConsumers(io.Discard, "endpoint", []string{"sub-a"}); !errors.Is(err, wantErr) {
t.Fatalf("transport error = %v", err)
}
}
func installPersonalManySeams(t *testing.T) func() {
t.Helper()
oldIdentity := personalResolveEventIdentity
oldLookup := personalLookupDefinition
oldEnsure := personalEnsureSubscription
oldUpsert := personalUpsertRunState
oldDelete := personalDeleteSubscription
oldRemove := personalRemoveRunStates
oldRunMany := personalConsumeRunMany
oldValidate := personalValidateConsumeConfig
oldConflict := personalValidateNoOutputConflict
return func() {
personalResolveEventIdentity = oldIdentity
personalLookupDefinition = oldLookup
personalEnsureSubscription = oldEnsure
personalUpsertRunState = oldUpsert
personalDeleteSubscription = oldDelete
personalRemoveRunStates = oldRemove
personalConsumeRunMany = oldRunMany
personalValidateConsumeConfig = oldValidate
personalValidateNoOutputConflict = oldConflict
}
}
+173
View File
@@ -47,12 +47,18 @@ func TestPersonalEventListHidesSchemaIDs(t *testing.T) {
assertPersonalOutputHidesSchemaIDs(t, got)
for _, eventKey := range []string{
personal.EventFromUser,
personal.EventAllSingleChat,
personal.EventAllGroupChat,
personal.EventReadO2O,
personal.EventReadGroup,
personal.EventRecallO2O,
personal.EventRecallGroup,
personal.EventReactionO2O,
personal.EventReactionGroup,
personal.EventGroupUpdated,
personal.EventGroupMemberAdded,
personal.EventGroupMemberExited,
personal.EventGroupDisbanded,
} {
if !strings.Contains(got, eventKey) {
t.Fatalf("list output missing %s: %s", eventKey, got)
@@ -230,6 +236,8 @@ func TestPersonalEventFlattenedSchemaUsesSingleJSONSchema(t *testing.T) {
personal.EventMention,
personal.EventSingleChat,
personal.EventInChat,
personal.EventAllSingleChat,
personal.EventAllGroupChat,
} {
t.Run(eventKey, func(t *testing.T) {
cmd := newEventSchemaCommand()
@@ -265,6 +273,8 @@ func TestPersonalEventFlattenedSchemaUsesSingleJSONSchema(t *testing.T) {
"message_id",
"create_time",
"event_time",
"quoted_message",
"forward_messages",
} {
if !strings.Contains(got, want) {
t.Fatalf("schema output for %s missing %q: %s", eventKey, want, got)
@@ -309,6 +319,103 @@ func TestPersonalEventFlattenedSchemaUsesSingleJSONSchema(t *testing.T) {
if _, ok := props["content"].(map[string]any); !ok {
t.Fatalf("schema.properties.content = %#v, want object", props["content"])
}
quoted, ok := props["quoted_message"].(map[string]any)
if !ok || quoted["type"] != "object" {
t.Fatalf("schema.properties.quoted_message = %#v, want object", props["quoted_message"])
}
forward, ok := props["forward_messages"].(map[string]any)
if !ok || forward["type"] != "array" {
t.Fatalf("schema.properties.forward_messages = %#v, want array", props["forward_messages"])
}
})
}
}
func TestPersonalGroupLifecycleEventSchemaUsesConservativePayload(t *testing.T) {
for _, eventKey := range []string{personal.EventGroupUpdated, personal.EventGroupDisbanded} {
t.Run(eventKey, func(t *testing.T) {
cmd := newEventSchemaCommand()
cmd.SilenceUsage = true
cmd.SilenceErrors = true
var out bytes.Buffer
cmd.SetOut(&out)
cmd.SetArgs([]string{eventKey, "--flatten"})
if err := cmd.Execute(); err != nil {
t.Fatal(err)
}
var doc map[string]any
if err := json.Unmarshal(out.Bytes(), &doc); err != nil {
t.Fatalf("schema output is not JSON: %v\n%s", err, out.String())
}
if doc["event_key"] != eventKey || doc["rule_type"] != "group" || doc["jq_root_path"] != "." {
t.Fatalf("schema metadata = %#v", doc)
}
required, ok := doc["required_params"].([]any)
if !ok || len(required) != 1 || required[0] != "group" {
t.Fatalf("required_params = %#v, want [group]", doc["required_params"])
}
schema := doc["schema"].(map[string]any)
properties := schema["properties"].(map[string]any)
if len(properties) != 5 {
t.Fatalf("schema.properties = %#v, want five conservative fields", properties)
}
payload, ok := properties["payload"].(map[string]any)
if !ok || payload["type"] != "object" || payload["additionalProperties"] != true {
t.Fatalf("schema.properties.payload = %#v", properties["payload"])
}
})
}
}
func TestPersonalGroupMemberEventSchemaMatchesFlatOutput(t *testing.T) {
wantProperties := []string{
"type", "event_id", "timestamp", "subscribe_id", "conversation_id",
"operator", "operator_open_dingtalk_id", "members", "event_time",
}
for _, eventKey := range []string{personal.EventGroupMemberAdded, personal.EventGroupMemberExited} {
t.Run(eventKey, func(t *testing.T) {
cmd := newEventSchemaCommand()
cmd.SilenceUsage = true
cmd.SilenceErrors = true
var out bytes.Buffer
cmd.SetOut(&out)
cmd.SetArgs([]string{eventKey, "--flatten"})
if err := cmd.Execute(); err != nil {
t.Fatal(err)
}
var doc map[string]any
if err := json.Unmarshal(out.Bytes(), &doc); err != nil {
t.Fatalf("schema output is not JSON: %v\n%s", err, out.String())
}
if doc["event_key"] != eventKey || doc["rule_type"] != "group" || doc["jq_root_path"] != "." {
t.Fatalf("schema metadata = %#v", doc)
}
properties := doc["schema"].(map[string]any)["properties"].(map[string]any)
if len(properties) != len(wantProperties) {
t.Fatalf("schema.properties = %#v, want exactly %d flat fields", properties, len(wantProperties))
}
for _, field := range wantProperties {
if _, ok := properties[field].(map[string]any); !ok {
t.Fatalf("schema missing %q: %#v", field, properties)
}
}
members := properties["members"].(map[string]any)
items, ok := members["items"].(map[string]any)
if !ok || members["type"] != "array" || items["type"] != "object" {
t.Fatalf("members schema = %#v", members)
}
memberProperties, ok := items["properties"].(map[string]any)
if !ok {
t.Fatalf("members.items.properties = %#v", items["properties"])
}
for _, field := range []string{"nick", "open_dingtalk_id"} {
if _, ok := memberProperties[field].(map[string]any); !ok {
t.Fatalf("member schema missing %q: %#v", field, memberProperties)
}
}
if _, ok := properties["payload"]; ok {
t.Fatalf("group member schema exposed generic payload: %#v", properties)
}
})
}
}
@@ -485,6 +592,64 @@ func TestPersonalEventFromUserIsPubliclyAvailable(t *testing.T) {
}
}
func TestPersonalNewIMEventsDryRunAndValidation(t *testing.T) {
configDir := setupPersonalIdentityToken(t, &authpkg.TokenData{
AccessToken: "access-1",
RefreshToken: "refresh-1",
ExpiresAt: time.Now().Add(time.Hour),
RefreshExpAt: time.Now().Add(24 * time.Hour),
CorpID: "corp-1",
UserID: "user-1",
ClientID: "client-1",
})
t.Setenv("DWS_CONFIG_DIR", configDir)
for _, test := range []struct {
eventKey string
args []string
}{
{eventKey: personal.EventAllSingleChat},
{eventKey: personal.EventAllGroupChat},
{eventKey: personal.EventGroupUpdated, args: []string{"--group", "cid-test-group"}},
{eventKey: personal.EventGroupMemberAdded, args: []string{"--group", "cid-test-group"}},
{eventKey: personal.EventGroupMemberExited, args: []string{"--group", "cid-test-group"}},
{eventKey: personal.EventGroupDisbanded, args: []string{"--group", "cid-test-group"}},
} {
t.Run(test.eventKey, func(t *testing.T) {
if err := ensurePublicPersonalEvent(test.eventKey); err != nil {
t.Fatalf("ensurePublicPersonalEvent() error = %v", err)
}
cmd := newEventConsumeCommand()
cmd.SilenceUsage = true
cmd.SilenceErrors = true
cmd.SetArgs(append([]string{test.eventKey}, append(test.args, "--dry-run")...))
if err := cmd.Execute(); err != nil {
t.Fatalf("dry-run Execute() error = %v", err)
}
})
}
for _, eventKey := range []string{personal.EventAllSingleChat, personal.EventAllGroupChat} {
cmd := newEventConsumeCommand()
cmd.SilenceUsage = true
cmd.SilenceErrors = true
cmd.SetArgs([]string{eventKey, "--user", "test-user-001", "--dry-run"})
if err := cmd.Execute(); err == nil || !strings.Contains(err.Error(), "--user is not supported for "+eventKey) {
t.Fatalf("%s scoped user error = %v", eventKey, err)
}
}
for _, eventKey := range []string{personal.EventGroupUpdated, personal.EventGroupMemberAdded, personal.EventGroupMemberExited, personal.EventGroupDisbanded} {
cmd := newEventConsumeCommand()
cmd.SilenceUsage = true
cmd.SilenceErrors = true
cmd.SetArgs([]string{eventKey, "--dry-run"})
if err := cmd.Execute(); err == nil || !strings.Contains(err.Error(), "--group is required for "+eventKey) {
t.Fatalf("%s missing group error = %v", eventKey, err)
}
}
}
func TestEventConsumeCobraSchemaIncludesOpenDingTalkID(t *testing.T) {
root := NewRootCommand()
root.SilenceUsage = true
@@ -550,6 +715,14 @@ func TestEventConsumeCobraSchemaIncludesOpenDingTalkID(t *testing.T) {
t.Fatalf("schema constraint %s = %#v, missing %#v", field, groups, want)
}
assertJSONConstraintGroup("require_one_of", []string{"event_key", "subscribe-id"})
positionals, ok := doc["positionals"].([]any)
if !ok || len(positionals) != 1 {
t.Fatalf("schema positionals = %#v", doc["positionals"])
}
eventKey, ok := positionals[0].(map[string]any)
if !ok || eventKey["name"] != "event_key" || eventKey["variadic"] != true {
t.Fatalf("event_key positional = %#v, want variadic", positionals[0])
}
}
func TestPersonalEventSchemaRejectsTableFormat(t *testing.T) {
+7
View File
@@ -353,6 +353,13 @@ func newRootCommandWithEngine(rootCtx context.Context, engine *pipeline.Engine,
return cmd.Help()
},
PersistentPreRunE: func(cmd *cobra.Command, args []string) error {
// Validate the optional observation label before any edition hook
// or command network activity can run. Header-only library callers
// use the best-effort path in resolveIdentityHeaders instead.
if _, err := parseAgentHost(os.Getenv(envDWSAgentHost)); err != nil {
return err
}
authpkg.SetRuntimeProfile(flags.Profile)
// Apply OAuth credential overrides from CLI flags (highest priority).
if flags.ClientID != "" {
+8
View File
@@ -1038,6 +1038,14 @@ func resolveIdentityHeaders() map[string]string {
headers["x-dws-channel"] = v
}
// DWS_AGENT_HOST is a caller-provided observation label only. Root command
// execution validates it strictly in PersistentPreRunE. Library callers
// that bypass the root command keep this best-effort API contract: invalid
// values are omitted rather than changing the public function signature.
if agentHost, err := parseAgentHost(os.Getenv(envDWSAgentHost)); err == nil && agentHost != "" {
headers[headerDWSAgentHost] = agentHost
}
if fn := edition.Get().MergeHeaders; fn != nil {
headers = fn(headers)
}
@@ -0,0 +1,358 @@
// Copyright 2026 Alibaba Group
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
package app
import (
"bytes"
"encoding/json"
"fmt"
"strings"
"testing"
"github.com/DingTalk-Real-AI/dingtalk-workspace-cli/internal/shortcut"
)
const publicShortcutSchemaCount = 210
func TestEmbeddedSchemaPublishesEveryPublicShortcutContract(t *testing.T) {
tools := embeddedSchemaAllToolsForHelpFlagTest(t, NewRootCommand())
public := make([]shortcut.Shortcut, 0, publicShortcutSchemaCount)
for _, candidate := range shortcut.All() {
if candidate.UserDefined || !shortcut.InPublicCatalog(candidate.Service, candidate.Command) {
continue
}
public = append(public, candidate)
}
if got := len(public); got != publicShortcutSchemaCount {
t.Fatalf("public built-in shortcuts = %d, want %d", got, publicShortcutSchemaCount)
}
deliveredShortcuts := 0
for canonical := range tools {
if strings.Contains(canonical, ".shortcut_") {
deliveredShortcuts++
}
}
if deliveredShortcuts != publicShortcutSchemaCount {
t.Fatalf("embedded schema --all shortcut tools = %d, want %d", deliveredShortcuts, publicShortcutSchemaCount)
}
for _, declared := range public {
declared := declared
t.Run(declared.Service+"/"+strings.TrimPrefix(declared.Command, "+"), func(t *testing.T) {
canonical := shortcutSchemaCanonical(declared)
tool := tools[canonical]
if tool == nil {
t.Fatalf("embedded schema --all is missing %s (%s %s)", canonical, declared.Service, declared.Command)
}
assertEmbeddedShortcutIdentityAndSelection(t, tool, declared, canonical)
assertEmbeddedShortcutSafetyAndInterface(t, tool, declared, canonical)
assertEmbeddedShortcutParameters(t, tool, declared, canonical)
assertEmbeddedShortcutConstraints(t, tool, declared, canonical)
})
}
}
func TestEmbeddedShortcutProgressiveQueriesReturnCompleteContracts(t *testing.T) {
leaf := executeShortcutSchemaQuery(t, "--cli-path", "chat +messages-read-status")
if got, want := schemaContractString(leaf["canonical_path"]), "chat.shortcut_messages_read_status"; got != want {
t.Fatalf("shortcut leaf canonical_path = %q, want %q", got, want)
}
if got, want := schemaContractString(leaf["confirmation"]), "not_required"; got != want {
t.Fatalf("shortcut leaf confirmation = %q, want %q", got, want)
}
conversationID := schemaContractMap(leaf["parameters"])["conversation-id"]
if required, _ := conversationID["required"].(bool); !required {
t.Fatal("public --conversation-id must become required after hidden compatibility aliases are removed from Schema")
}
if got := leaf["constraints"]; got != nil {
t.Fatalf("shortcut leaf constraints = %#v, want omitted after hidden compatibility aliases collapse", got)
}
constrainedLeaf := executeShortcutSchemaQuery(t, "--cli-path", "calendar +freebusy")
wantConstraints := map[string]any{
"require_one_of": [][]string{{"users", "rooms"}},
}
if got := constrainedLeaf["constraints"]; !schemaContractJSONEqual(got, wantConstraints) {
t.Fatalf("shortcut leaf constraints = %#v, want %#v", got, wantConstraints)
}
product := executeShortcutSchemaQuery(t, "chat")
productPayload, _ := product["product"].(map[string]any)
if got, want := int(product["count"].(float64)), 120; got != want {
t.Fatalf("schema chat count = %d, want %d", got, want)
}
summaries := schemaContractObjectSlice(productPayload["tools"])
shortcutCount := 0
for _, summary := range summaries {
if strings.HasPrefix(schemaContractString(summary["canonical_path"]), "chat.shortcut_") {
shortcutCount++
}
}
if shortcutCount != 42 {
t.Fatalf("schema chat shortcut summaries = %d, want 42", shortcutCount)
}
}
func executeShortcutSchemaQuery(t testing.TB, args ...string) map[string]any {
t.Helper()
root := NewRootCommand()
var stdout, stderr bytes.Buffer
root.SetOut(&stdout)
root.SetErr(&stderr)
root.SetArgs(append([]string{"schema"}, append(args, "--format", "json")...))
if err := root.Execute(); err != nil {
t.Fatalf("execute dws schema %q: %v; stderr=%s", strings.Join(args, " "), err, stderr.String())
}
var payload map[string]any
if err := json.Unmarshal(stdout.Bytes(), &payload); err != nil {
t.Fatalf("decode dws schema %q: %v", strings.Join(args, " "), err)
}
return payload
}
func shortcutSchemaCanonical(declared shortcut.Shortcut) string {
name := strings.ReplaceAll(strings.TrimPrefix(declared.Command, "+"), "-", "_")
return declared.Service + ".shortcut_" + name
}
func assertEmbeddedShortcutIdentityAndSelection(
t testing.TB,
tool map[string]any,
declared shortcut.Shortcut,
canonical string,
) {
t.Helper()
if got, want := schemaContractString(tool["canonical_path"]), canonical; got != want {
t.Errorf("canonical_path = %q, want %q", got, want)
}
if got, want := schemaContractString(tool["primary_cli_path"]), declared.Service+" "+declared.Command; got != want {
t.Errorf("%s primary_cli_path = %q, want %q", canonical, got, want)
}
if got, want := schemaContractString(tool["agent_summary"]), declared.Description; got != want {
t.Errorf("%s agent_summary = %q, want %q", canonical, got, want)
}
if got, want := schemaContractStringSlice(tool["use_when"]), []string{declared.Intent}; !schemaContractJSONEqual(got, want) {
t.Errorf("%s use_when = %#v, want %#v", canonical, got, want)
}
if len(schemaContractStringSlice(tool["avoid_when"])) == 0 {
t.Errorf("%s has no reviewed avoid_when", canonical)
}
examples := schemaContractStringSlice(tool["examples"])
if len(examples) == 0 || len(examples) > 2 {
t.Errorf("%s examples = %d, want 1..2", canonical, len(examples))
}
for _, example := range examples {
if strings.Contains(example, "--yes") {
t.Errorf("%s stores unsafe example %q", canonical, example)
}
if !strings.HasPrefix(example, "dws "+declared.Service+" "+declared.Command) {
t.Errorf("%s example does not use its primary path: %q", canonical, example)
}
}
}
func assertEmbeddedShortcutSafetyAndInterface(
t testing.TB,
tool map[string]any,
declared shortcut.Shortcut,
canonical string,
) {
t.Helper()
risk := declared.Risk
if risk == "" {
risk = shortcut.RiskRead
}
wantEffect, wantRisk, wantConfirmation, wantIdempotency := "read", "low", "not_required", "idempotent"
switch risk {
case shortcut.RiskWrite:
wantEffect, wantRisk, wantConfirmation, wantIdempotency = "write", "medium", "user_required", "unknown"
case shortcut.RiskHighWrite:
wantEffect, wantRisk, wantConfirmation, wantIdempotency = "destructive", "high", "user_required", "unknown"
}
for field, want := range map[string]string{
"effect": wantEffect,
"risk": wantRisk,
"confirmation": wantConfirmation,
"idempotency": wantIdempotency,
"interface_mode": "composite",
"availability": "available",
} {
if got := schemaContractString(tool[field]); got != want {
t.Errorf("%s %s = %q, want %q", canonical, field, got, want)
}
}
if strings.TrimSpace(schemaContractString(tool["interface_reason"])) == "" {
t.Errorf("%s has no reviewed composite interface reason", canonical)
}
}
func assertEmbeddedShortcutParameters(
t testing.TB,
tool map[string]any,
declared shortcut.Shortcut,
canonical string,
) {
t.Helper()
parameters := schemaContractMap(tool["parameters"])
publicFlags := make([]shortcut.Flag, 0, len(declared.Flags))
for _, flag := range declared.Flags {
if !flag.Hidden {
publicFlags = append(publicFlags, flag)
}
}
if got, want := len(parameters), len(publicFlags); got != want {
t.Errorf("%s parameters = %d, want %d", canonical, got, want)
}
for _, flag := range publicFlags {
parameter := parameters[flag.Name]
if parameter == nil {
t.Errorf("%s is missing parameter --%s", canonical, flag.Name)
continue
}
flagType := flag.Type
if flagType == "" {
flagType = shortcut.FlagString
}
wantType := map[shortcut.FlagType]string{
shortcut.FlagString: "string",
shortcut.FlagBool: "boolean",
shortcut.FlagInt: "integer",
shortcut.FlagStringSlice: "array",
}[flagType]
if got := schemaContractString(parameter["type"]); got != wantType {
t.Errorf("%s --%s type = %q, want %q", canonical, flag.Name, got, wantType)
}
if got, _ := parameter["required"].(bool); got != shortcutSchemaRequired(declared, flag.Name) {
t.Errorf("%s --%s required = %t, want %t", canonical, flag.Name, got, shortcutSchemaRequired(declared, flag.Name))
}
if got, want := schemaContractString(parameter["default"]), shortcutSchemaDefault(flag); got != want {
t.Errorf("%s --%s default = %q, want %q", canonical, flag.Name, got, want)
}
gotEnum := schemaContractStringSlice(parameter["enum"])
if len(flag.Enum) == 0 {
if len(gotEnum) != 0 {
t.Errorf("%s --%s enum = %#v, want empty", canonical, flag.Name, gotEnum)
}
} else if !schemaContractJSONEqual(gotEnum, flag.Enum) {
t.Errorf("%s --%s enum = %#v, want %#v", canonical, flag.Name, gotEnum, flag.Enum)
}
}
}
func shortcutSchemaDefault(flag shortcut.Flag) string {
value := strings.TrimSpace(flag.Default)
switch flag.Type {
case shortcut.FlagBool:
if value != "true" {
return ""
}
case shortcut.FlagInt:
if value == "0" {
return ""
}
case shortcut.FlagStringSlice:
if value != "" {
return "[" + value + "]"
}
}
return value
}
func shortcutSchemaRequired(declared shortcut.Shortcut, flagName string) bool {
for _, flag := range declared.Flags {
if flag.Name == flagName && flag.Required {
return true
}
}
public := make(map[string]bool, len(declared.Flags))
for _, flag := range declared.Flags {
if !flag.Hidden {
public[flag.Name] = true
}
}
for _, constraint := range declared.Constraints {
if constraint.Kind != shortcut.ConstraintAtLeastOne && constraint.Kind != shortcut.ConstraintExactlyOne {
continue
}
visible := make([]string, 0, len(constraint.Flags))
for _, constrained := range constraint.Flags {
if public[constrained] {
visible = append(visible, constrained)
}
}
if len(visible) == 1 && visible[0] == flagName {
return true
}
}
return false
}
func assertEmbeddedShortcutConstraints(
t testing.TB,
tool map[string]any,
declared shortcut.Shortcut,
canonical string,
) {
t.Helper()
public := make(map[string]bool, len(declared.Flags))
for _, flag := range declared.Flags {
if !flag.Hidden {
public[flag.Name] = true
}
}
want := map[string][][]string{}
for _, constraint := range declared.Constraints {
flags := make([]string, 0, len(constraint.Flags))
for _, flagName := range constraint.Flags {
if public[flagName] {
flags = append(flags, flagName)
}
}
switch constraint.Kind {
case shortcut.ConstraintAtLeastOne:
if len(flags) > 1 {
want["require_one_of"] = append(want["require_one_of"], flags)
}
case shortcut.ConstraintExactlyOne:
if len(flags) > 1 {
want["require_one_of"] = append(want["require_one_of"], flags)
want["mutually_exclusive"] = append(want["mutually_exclusive"], flags)
}
case shortcut.ConstraintMutuallyExclusive:
if len(flags) > 1 {
want["mutually_exclusive"] = append(want["mutually_exclusive"], flags)
}
case shortcut.ConstraintCustom:
for _, flagName := range flags {
description := schemaContractString(schemaContractMap(tool["parameters"])[flagName]["description"])
for _, requiredText := range []string{"原文不能为空", "不能重复"} {
if !strings.Contains(description, requiredText) {
t.Errorf("%s --%s description does not publish custom constraint %q: %q", canonical, flagName, requiredText, description)
}
}
}
default:
t.Errorf("%s has unsupported declared shortcut constraint %q", canonical, constraint.Kind)
}
}
if len(want) == 0 {
if got := tool["constraints"]; got != nil {
t.Errorf("%s constraints = %#v, want omitted", canonical, got)
}
return
}
if got := tool["constraints"]; !schemaContractJSONEqual(got, want) {
t.Errorf("%s constraints = %s, want %s", canonical, mustShortcutJSON(got), mustShortcutJSON(want))
}
}
func mustShortcutJSON(value any) string {
encoded, err := json.Marshal(value)
if err != nil {
return fmt.Sprintf("%#v", value)
}
return string(encoded)
}
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+21 -12
View File
@@ -2,7 +2,7 @@
"product_id": "event",
"tools": {
"event consume": {
"agent_summary": "订阅并持续消费指定个人事件;Agent 使用 --flatten 输出顶层业务 NDJSON",
"agent_summary": "订阅并持续消费一个或多个兼容的个人事件;Agent 使用 --flatten 输出顶层业务 NDJSON",
"agent_summary_source": "dws-agent-selection/event",
"availability": "available",
"avoid_when": [
@@ -14,18 +14,18 @@
"effect_source": "agent-hint",
"examples": [
"dws event consume user_im_message_receive_user --open-dingtalk-id open-example --flatten --max-events 1 --format ndjson",
"dws event consume user_im_message_reaction_group --group cid-example --flatten --max-events 1 --format ndjson"
"dws event consume user_im_message_receive_o2o user_im_message_read_o2o --user test-user-001 --flatten --max-events 2 --format ndjson"
],
"field_provenance": {
"agent_summary": {
"value": "订阅并持续消费指定个人事件;Agent 使用 --flatten 输出顶层业务 NDJSON",
"value": "订阅并持续消费一个或多个兼容的个人事件;Agent 使用 --flatten 输出顶层业务 NDJSON",
"source": "internal/cli/schema_hints/selection/event.json",
"precedence": "reviewed_explicit",
"resolution": "highest_precedence",
"review_reason": "人工依据实时 dws schema(或 Skill/Cobra/pinned MCP 对照)决策化选型文案与门禁;不改写命令身份与参数契约;示例不含 --yes。",
"candidates": [
{
"value": "订阅并持续消费指定个人事件;Agent 使用 --flatten 输出顶层业务 NDJSON",
"value": "订阅并持续消费一个或多个兼容的个人事件;Agent 使用 --flatten 输出顶层业务 NDJSON",
"source": "internal/cli/schema_hints/selection/event.json",
"precedence": "reviewed_explicit",
"selected": true,
@@ -106,7 +106,7 @@
"examples": {
"value": [
"dws event consume user_im_message_receive_user --open-dingtalk-id open-example --flatten --max-events 1 --format ndjson",
"dws event consume user_im_message_reaction_group --group cid-example --flatten --max-events 1 --format ndjson"
"dws event consume user_im_message_receive_o2o user_im_message_read_o2o --user test-user-001 --flatten --max-events 2 --format ndjson"
],
"source": "internal/cli/schema_hints/selection/event.json",
"precedence": "reviewed_explicit",
@@ -116,7 +116,7 @@
{
"value": [
"dws event consume user_im_message_receive_user --open-dingtalk-id open-example --flatten --max-events 1 --format ndjson",
"dws event consume user_im_message_reaction_group --group cid-example --flatten --max-events 1 --format ndjson"
"dws event consume user_im_message_receive_o2o user_im_message_read_o2o --user test-user-001 --flatten --max-events 2 --format ndjson"
],
"source": "internal/cli/schema_hints/selection/event.json",
"precedence": "reviewed_explicit",
@@ -231,8 +231,11 @@
"use_when": {
"value": [
"需要实时监听 @我、指定单聊、指定群或指定发送人的后续消息事件",
"用户明确要求监听当前身份的所有单聊或所有群消息",
"需要监听指定单聊或群聊中的消息已读、撤回或表情回应事件",
"监听机器人、外部联系人等以 openDingtalkId 标识的单聊目标"
"需要监听指定群的标题变更、成员进退群或群解散事件",
"监听机器人、外部联系人等以 openDingtalkId 标识的单聊目标",
"同一目标、同一过滤条件需要同时监听多个兼容事件"
],
"source": "internal/cli/schema_hints/selection/event.json",
"precedence": "reviewed_explicit",
@@ -242,8 +245,11 @@
{
"value": [
"需要实时监听 @我、指定单聊、指定群或指定发送人的后续消息事件",
"用户明确要求监听当前身份的所有单聊或所有群消息",
"需要监听指定单聊或群聊中的消息已读、撤回或表情回应事件",
"监听机器人、外部联系人等以 openDingtalkId 标识的单聊目标"
"需要监听指定群的标题变更、成员进退群或群解散事件",
"监听机器人、外部联系人等以 openDingtalkId 标识的单聊目标",
"同一目标、同一过滤条件需要同时监听多个兼容事件"
],
"source": "internal/cli/schema_hints/selection/event.json",
"precedence": "reviewed_explicit",
@@ -270,8 +276,11 @@
],
"use_when": [
"需要实时监听 @我、指定单聊、指定群或指定发送人的后续消息事件",
"用户明确要求监听当前身份的所有单聊或所有群消息",
"需要监听指定单聊或群聊中的消息已读、撤回或表情回应事件",
"监听机器人、外部联系人等以 openDingtalkId 标识的单聊目标"
"需要监听指定群的标题变更、成员进退群或群解散事件",
"监听机器人、外部联系人等以 openDingtalkId 标识的单聊目标",
"同一目标、同一过滤条件需要同时监听多个兼容事件"
]
},
"event list": {
@@ -763,7 +772,7 @@
},
"use_when": {
"value": [
"已知任一公开个人消息 event_key,消费前需要理解扁平输出字段"
"已知任一公开个人 IM event_key,消费前需要理解输出字段或保守 payload 契约"
],
"source": "internal/cli/schema_hints/selection/event.json",
"precedence": "reviewed_explicit",
@@ -772,7 +781,7 @@
"candidates": [
{
"value": [
"已知任一公开个人消息 event_key,消费前需要理解扁平输出字段"
"已知任一公开个人 IM event_key,消费前需要理解输出字段或保守 payload 契约"
],
"source": "internal/cli/schema_hints/selection/event.json",
"precedence": "reviewed_explicit",
@@ -797,7 +806,7 @@
"skills/mono/references/products/event.md"
],
"use_when": [
"已知任一公开个人消息 event_key,消费前需要理解扁平输出字段"
"已知任一公开个人 IM event_key,消费前需要理解输出字段或保守 payload 契约"
]
},
"event status": {
+88 -19
View File
@@ -1,19 +1,19 @@
{
"version": 1,
"source_hash": "sha256:bda812c0b48f221a70f9c710d56a4dead5916bf425a9b153e7b275d15365f1a0",
"surface_hash": "sha256:8eef4235b4391a6a865c180ebd3a65ca3531a84c6985b37811f23fff9daf2e3a",
"source_hash": "sha256:f71480e58eda8f2be041e07d7faf6fb20f5c21f861995d3c4dacc311b46c3e81",
"surface_hash": "sha256:4cf8460240b19f896c3a330c69982cbb5f57aa8576cdf30373172082eea893ed",
"coverage": {
"surface_products": 25,
"products_with_metadata": 25,
"surface_tools": 603,
"tools_with_metadata": 603,
"tools_with_agent_summary": 603,
"tools_with_use_when": 603,
"tools_with_avoid_when": 603,
"tools_with_examples": 603,
"tools_with_interface_mode": 603,
"unmatched_skill_tools": 118,
"unreviewed_skill_tools": 7
"surface_products": 26,
"products_with_metadata": 26,
"surface_tools": 813,
"tools_with_metadata": 813,
"tools_with_agent_summary": 813,
"tools_with_use_when": 813,
"tools_with_avoid_when": 813,
"tools_with_examples": 813,
"tools_with_interface_mode": 813,
"unmatched_skill_tools": 122,
"unreviewed_skill_tools": 11
},
"products": {
"aisearch": {
@@ -585,6 +585,74 @@
"创建/配置开放平台应用、机器人、权限、事件订阅或发布版本"
]
},
"devapp": {
"agent_summary": "管理钉钉开放平台企业内部应用、成员、权限、机器人、事件与版本",
"agent_summary_source": "dws-agent-selection/devapp",
"avoid_when": [
"开放平台接口文档搜索使用 devdoc;普通钉钉业务数据使用对应产品命令"
],
"field_provenance": {
"agent_summary": {
"value": "管理钉钉开放平台企业内部应用、成员、权限、机器人、事件与版本",
"source": "internal/cli/schema_hints/selection/devapp.json",
"precedence": "reviewed_explicit",
"resolution": "highest_precedence",
"candidates": [
{
"value": "管理钉钉开放平台企业内部应用、成员、权限、机器人、事件与版本",
"source": "internal/cli/schema_hints/selection/devapp.json",
"precedence": "reviewed_explicit",
"selected": true
}
]
},
"avoid_when": {
"value": [
"开放平台接口文档搜索使用 devdoc;普通钉钉业务数据使用对应产品命令"
],
"source": "internal/cli/schema_hints/selection/devapp.json",
"precedence": "reviewed_explicit",
"resolution": "highest_precedence",
"candidates": [
{
"value": [
"开放平台接口文档搜索使用 devdoc;普通钉钉业务数据使用对应产品命令"
],
"source": "internal/cli/schema_hints/selection/devapp.json",
"precedence": "reviewed_explicit",
"selected": true
}
]
},
"use_when": {
"value": [
"请求涉及企业内部应用的查询、创建、配置、成员权限、机器人、事件订阅或版本管理"
],
"source": "internal/cli/schema_hints/selection/devapp.json",
"precedence": "reviewed_explicit",
"resolution": "highest_precedence",
"candidates": [
{
"value": [
"请求涉及企业内部应用的查询、创建、配置、成员权限、机器人、事件订阅或版本管理"
],
"source": "internal/cli/schema_hints/selection/devapp.json",
"precedence": "reviewed_explicit",
"selected": true
}
]
}
},
"source_refs": [
"CommandRegistry:product=devapp",
"internal/cli/schema_command_registry.json",
"internal/cli/schema_hints/selection/devapp.json",
"internal/shortcut/devapp"
],
"use_when": [
"请求涉及企业内部应用的查询、创建、配置、成员权限、机器人、事件订阅或版本管理"
]
},
"devdoc": {
"agent_summary": "搜索钉钉开放平台开发文档与错误排查资料",
"agent_summary_source": "dws-agent-selection/devdoc",
@@ -870,20 +938,20 @@
]
},
"event": {
"agent_summary": "订阅/消费个人消息接收、已读、撤回和表情回应事件,并管理订阅生命周期",
"agent_summary": "订阅/消费个人消息、动作与群生命周期事件,并管理订阅生命周期",
"agent_summary_source": "dws-agent-selection/event",
"avoid_when": [
"查历史聊天或主动发消息分别用 chat 查询/发送命令"
],
"field_provenance": {
"agent_summary": {
"value": "订阅/消费个人消息接收、已读、撤回和表情回应事件,并管理订阅生命周期",
"value": "订阅/消费个人消息、动作与群生命周期事件,并管理订阅生命周期",
"source": "internal/cli/schema_hints/selection/event.json",
"precedence": "reviewed_explicit",
"resolution": "highest_precedence",
"candidates": [
{
"value": "订阅/消费个人消息接收、已读、撤回和表情回应事件,并管理订阅生命周期",
"value": "订阅/消费个人消息、动作与群生命周期事件,并管理订阅生命周期",
"source": "internal/cli/schema_hints/selection/event.json",
"precedence": "reviewed_explicit",
"selected": true
@@ -910,7 +978,7 @@
},
"use_when": {
"value": [
"需要实时监听个人消息接收、已读、撤回或表情回应事件,或管理个人事件订阅生命周期"
"需要实时监听个人消息接收、全量消息、已读、撤回、表情回应或群生命周期事件,或管理个人事件订阅生命周期"
],
"source": "internal/cli/schema_hints/selection/event.json",
"precedence": "reviewed_explicit",
@@ -918,7 +986,7 @@
"candidates": [
{
"value": [
"需要实时监听个人消息接收、已读、撤回或表情回应事件,或管理个人事件订阅生命周期"
"需要实时监听个人消息接收、全量消息、已读、撤回、表情回应或群生命周期事件,或管理个人事件订阅生命周期"
],
"source": "internal/cli/schema_hints/selection/event.json",
"precedence": "reviewed_explicit",
@@ -938,7 +1006,7 @@
"skills/mono/references/products/event.md"
],
"use_when": [
"需要实时监听个人消息接收、已读、撤回或表情回应事件,或管理个人事件订阅生命周期"
"需要实时监听个人消息接收、全量消息、已读、撤回、表情回应或群生命周期事件,或管理个人事件订阅生命周期"
]
},
"hrbrain": {
@@ -1806,6 +1874,7 @@
"chat",
"contact",
"dev",
"devapp",
"devdoc",
"ding",
"doc",
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -1,6 +1,525 @@
{
"product_id": "report",
"tools": {
"report +inbox-list": {
"agent_summary": "列出我收到的日报(按时间范围分页)",
"agent_summary_source": "dws-agent-selection/report",
"availability": "available",
"avoid_when": [
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
],
"confirmation": "not_required",
"effect": "read",
"effect_source": "agent-hint",
"examples": [
"dws report +inbox-list --start \"2026-03-10T00:00:00+08:00\" --end \"2026-03-10T23:59:59+08:00\" --cursor 0 --size 20"
],
"field_provenance": {
"agent_summary": {
"value": "列出我收到的日报(按时间范围分页)",
"source": "internal/cli/schema_hints/selection/report.json",
"precedence": "reviewed_explicit",
"resolution": "highest_precedence",
"review_reason": "Agent-authored and reviewed from the built-in Shortcut intent, executable Cobra path, and declared outcome; it affects selection only and does not alter execution or safety facts.",
"candidates": [
{
"value": "列出我收到的日报(按时间范围分页)",
"source": "internal/cli/schema_hints/selection/report.json",
"precedence": "reviewed_explicit",
"selected": true,
"review_reason": "Agent-authored and reviewed from the built-in Shortcut intent, executable Cobra path, and declared outcome; it affects selection only and does not alter execution or safety facts."
}
]
},
"availability": {
"value": "available",
"source": "internal/cli/schema_hints/metadata/report.json",
"precedence": "reviewed_explicit",
"resolution": "highest_precedence",
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity.",
"candidates": [
{
"value": "available",
"source": "internal/cli/schema_hints/metadata/report.json",
"precedence": "reviewed_explicit",
"selected": true,
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity."
}
]
},
"avoid_when": {
"value": [
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
],
"source": "internal/cli/schema_hints/selection/report.json",
"precedence": "reviewed_explicit",
"resolution": "highest_precedence",
"review_reason": "Agent-authored and reviewed from the built-in Shortcut intent, executable Cobra path, and declared outcome; it affects selection only and does not alter execution or safety facts.",
"candidates": [
{
"value": [
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
],
"source": "internal/cli/schema_hints/selection/report.json",
"precedence": "reviewed_explicit",
"selected": true,
"review_reason": "Agent-authored and reviewed from the built-in Shortcut intent, executable Cobra path, and declared outcome; it affects selection only and does not alter execution or safety facts."
}
]
},
"confirmation": {
"value": "not_required",
"source": "internal/cli/schema_hints/metadata/report.json",
"precedence": "reviewed_explicit",
"resolution": "highest_precedence",
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity.",
"candidates": [
{
"value": "not_required",
"source": "internal/cli/schema_hints/metadata/report.json",
"precedence": "reviewed_explicit",
"selected": true,
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity."
}
]
},
"effect": {
"value": "read",
"source": "internal/cli/schema_hints/metadata/report.json",
"precedence": "reviewed_explicit",
"resolution": "highest_precedence",
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity.",
"candidates": [
{
"value": "read",
"source": "internal/cli/schema_hints/metadata/report.json",
"precedence": "reviewed_explicit",
"selected": true,
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity."
}
]
},
"examples": {
"value": [
"dws report +inbox-list --start \"2026-03-10T00:00:00+08:00\" --end \"2026-03-10T23:59:59+08:00\" --cursor 0 --size 20"
],
"source": "internal/cli/schema_hints/selection/report.json",
"precedence": "reviewed_explicit",
"resolution": "highest_precedence",
"review_reason": "Agent-authored and reviewed from the built-in Shortcut intent, executable Cobra path, and declared outcome; it affects selection only and does not alter execution or safety facts.",
"candidates": [
{
"value": [
"dws report +inbox-list --start \"2026-03-10T00:00:00+08:00\" --end \"2026-03-10T23:59:59+08:00\" --cursor 0 --size 20"
],
"source": "internal/cli/schema_hints/selection/report.json",
"precedence": "reviewed_explicit",
"selected": true,
"review_reason": "Agent-authored and reviewed from the built-in Shortcut intent, executable Cobra path, and declared outcome; it affects selection only and does not alter execution or safety facts."
}
]
},
"idempotency": {
"value": "idempotent",
"source": "internal/cli/schema_hints/metadata/report.json",
"precedence": "reviewed_explicit",
"resolution": "highest_precedence",
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity.",
"candidates": [
{
"value": "idempotent",
"source": "internal/cli/schema_hints/metadata/report.json",
"precedence": "reviewed_explicit",
"selected": true,
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity."
}
]
},
"interface_mode": {
"value": "composite",
"source": "internal/cli/schema_hints/metadata/report.json",
"precedence": "reviewed_explicit",
"resolution": "highest_precedence",
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity.",
"candidates": [
{
"value": "composite",
"source": "internal/cli/schema_hints/metadata/report.json",
"precedence": "reviewed_explicit",
"selected": true,
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity."
}
]
},
"interface_reason": {
"value": "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
"source": "internal/cli/schema_hints/metadata/report.json",
"precedence": "reviewed_explicit",
"resolution": "highest_precedence",
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity.",
"candidates": [
{
"value": "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
"source": "internal/cli/schema_hints/metadata/report.json",
"precedence": "reviewed_explicit",
"selected": true,
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity."
}
]
},
"interface_ref": {
"value": null,
"source": "internal/cli/schema_hints/metadata/report.json",
"precedence": "reviewed_explicit",
"resolution": "interface_disposition_matrix",
"review_reason": "final interface mode composite forbids a direct MCP interface_ref",
"candidates": [
{
"value": null,
"source": "internal/cli/schema_hints/metadata/report.json",
"precedence": "reviewed_explicit",
"selected": true,
"review_reason": "final interface mode composite forbids a direct MCP interface_ref"
}
]
},
"reviewed": {
"value": true,
"source": "internal/cli/schema_hints/metadata/report.json",
"precedence": "reviewed_explicit",
"resolution": "highest_precedence",
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity.",
"candidates": [
{
"value": true,
"source": "internal/cli/schema_hints/metadata/report.json",
"precedence": "reviewed_explicit",
"selected": true,
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity."
},
{
"value": true,
"source": "internal/cli/schema_hints/selection/report.json",
"precedence": "reviewed_explicit",
"selected": false,
"review_reason": "Agent-authored and reviewed from the built-in Shortcut intent, executable Cobra path, and declared outcome; it affects selection only and does not alter execution or safety facts."
}
]
},
"risk": {
"value": "low",
"source": "internal/cli/schema_hints/metadata/report.json",
"precedence": "reviewed_explicit",
"resolution": "highest_precedence",
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity.",
"candidates": [
{
"value": "low",
"source": "internal/cli/schema_hints/metadata/report.json",
"precedence": "reviewed_explicit",
"selected": true,
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity."
}
]
},
"use_when": {
"value": [
"当你要查看下属或同事发给自己的日报周报、想在某个时间段内浏览或审阅收到的汇报时使用;输入起止时间(ISO-8601),可按发送人 staffId 过滤,分页返回收到的日报列表及其 reportId,供后续 +entry-get 读正文。"
],
"source": "internal/cli/schema_hints/selection/report.json",
"precedence": "reviewed_explicit",
"resolution": "highest_precedence",
"review_reason": "Agent-authored and reviewed from the built-in Shortcut intent, executable Cobra path, and declared outcome; it affects selection only and does not alter execution or safety facts.",
"candidates": [
{
"value": [
"当你要查看下属或同事发给自己的日报周报、想在某个时间段内浏览或审阅收到的汇报时使用;输入起止时间(ISO-8601),可按发送人 staffId 过滤,分页返回收到的日报列表及其 reportId,供后续 +entry-get 读正文。"
],
"source": "internal/cli/schema_hints/selection/report.json",
"precedence": "reviewed_explicit",
"selected": true,
"review_reason": "Agent-authored and reviewed from the built-in Shortcut intent, executable Cobra path, and declared outcome; it affects selection only and does not alter execution or safety facts."
}
]
}
},
"idempotency": "idempotent",
"interface_mode": "composite",
"interface_reason": "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
"reviewed": true,
"risk": "low",
"source_refs": [
"ShortcutRegistry:report +inbox-list",
"cobra-help:dws report +inbox-list",
"internal/cli/schema_command_registry.json#report.shortcut_inbox_list",
"internal/cli/schema_hints/metadata/report.json",
"internal/cli/schema_hints/selection/report.json"
],
"use_when": [
"当你要查看下属或同事发给自己的日报周报、想在某个时间段内浏览或审阅收到的汇报时使用;输入起止时间(ISO-8601),可按发送人 staffId 过滤,分页返回收到的日报列表及其 reportId,供后续 +entry-get 读正文。"
]
},
"report +outbox-list": {
"agent_summary": "列出我发出的日报(可选时间/模版名过滤)",
"agent_summary_source": "dws-agent-selection/report",
"availability": "available",
"avoid_when": [
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
],
"confirmation": "not_required",
"effect": "read",
"effect_source": "agent-hint",
"examples": [
"dws report +outbox-list --cursor 0 --size 20",
"dws report +outbox-list --cursor 0 --size 20 --template-name \"日报\""
],
"field_provenance": {
"agent_summary": {
"value": "列出我发出的日报(可选时间/模版名过滤)",
"source": "internal/cli/schema_hints/selection/report.json",
"precedence": "reviewed_explicit",
"resolution": "highest_precedence",
"review_reason": "Agent-authored and reviewed from the built-in Shortcut intent, executable Cobra path, and declared outcome; it affects selection only and does not alter execution or safety facts.",
"candidates": [
{
"value": "列出我发出的日报(可选时间/模版名过滤)",
"source": "internal/cli/schema_hints/selection/report.json",
"precedence": "reviewed_explicit",
"selected": true,
"review_reason": "Agent-authored and reviewed from the built-in Shortcut intent, executable Cobra path, and declared outcome; it affects selection only and does not alter execution or safety facts."
}
]
},
"availability": {
"value": "available",
"source": "internal/cli/schema_hints/metadata/report.json",
"precedence": "reviewed_explicit",
"resolution": "highest_precedence",
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity.",
"candidates": [
{
"value": "available",
"source": "internal/cli/schema_hints/metadata/report.json",
"precedence": "reviewed_explicit",
"selected": true,
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity."
}
]
},
"avoid_when": {
"value": [
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
],
"source": "internal/cli/schema_hints/selection/report.json",
"precedence": "reviewed_explicit",
"resolution": "highest_precedence",
"review_reason": "Agent-authored and reviewed from the built-in Shortcut intent, executable Cobra path, and declared outcome; it affects selection only and does not alter execution or safety facts.",
"candidates": [
{
"value": [
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
],
"source": "internal/cli/schema_hints/selection/report.json",
"precedence": "reviewed_explicit",
"selected": true,
"review_reason": "Agent-authored and reviewed from the built-in Shortcut intent, executable Cobra path, and declared outcome; it affects selection only and does not alter execution or safety facts."
}
]
},
"confirmation": {
"value": "not_required",
"source": "internal/cli/schema_hints/metadata/report.json",
"precedence": "reviewed_explicit",
"resolution": "highest_precedence",
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity.",
"candidates": [
{
"value": "not_required",
"source": "internal/cli/schema_hints/metadata/report.json",
"precedence": "reviewed_explicit",
"selected": true,
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity."
}
]
},
"effect": {
"value": "read",
"source": "internal/cli/schema_hints/metadata/report.json",
"precedence": "reviewed_explicit",
"resolution": "highest_precedence",
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity.",
"candidates": [
{
"value": "read",
"source": "internal/cli/schema_hints/metadata/report.json",
"precedence": "reviewed_explicit",
"selected": true,
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity."
}
]
},
"examples": {
"value": [
"dws report +outbox-list --cursor 0 --size 20",
"dws report +outbox-list --cursor 0 --size 20 --template-name \"日报\""
],
"source": "internal/cli/schema_hints/selection/report.json",
"precedence": "reviewed_explicit",
"resolution": "highest_precedence",
"review_reason": "Agent-authored and reviewed from the built-in Shortcut intent, executable Cobra path, and declared outcome; it affects selection only and does not alter execution or safety facts.",
"candidates": [
{
"value": [
"dws report +outbox-list --cursor 0 --size 20",
"dws report +outbox-list --cursor 0 --size 20 --template-name \"日报\""
],
"source": "internal/cli/schema_hints/selection/report.json",
"precedence": "reviewed_explicit",
"selected": true,
"review_reason": "Agent-authored and reviewed from the built-in Shortcut intent, executable Cobra path, and declared outcome; it affects selection only and does not alter execution or safety facts."
}
]
},
"idempotency": {
"value": "idempotent",
"source": "internal/cli/schema_hints/metadata/report.json",
"precedence": "reviewed_explicit",
"resolution": "highest_precedence",
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity.",
"candidates": [
{
"value": "idempotent",
"source": "internal/cli/schema_hints/metadata/report.json",
"precedence": "reviewed_explicit",
"selected": true,
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity."
}
]
},
"interface_mode": {
"value": "composite",
"source": "internal/cli/schema_hints/metadata/report.json",
"precedence": "reviewed_explicit",
"resolution": "highest_precedence",
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity.",
"candidates": [
{
"value": "composite",
"source": "internal/cli/schema_hints/metadata/report.json",
"precedence": "reviewed_explicit",
"selected": true,
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity."
}
]
},
"interface_reason": {
"value": "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
"source": "internal/cli/schema_hints/metadata/report.json",
"precedence": "reviewed_explicit",
"resolution": "highest_precedence",
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity.",
"candidates": [
{
"value": "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
"source": "internal/cli/schema_hints/metadata/report.json",
"precedence": "reviewed_explicit",
"selected": true,
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity."
}
]
},
"interface_ref": {
"value": null,
"source": "internal/cli/schema_hints/metadata/report.json",
"precedence": "reviewed_explicit",
"resolution": "interface_disposition_matrix",
"review_reason": "final interface mode composite forbids a direct MCP interface_ref",
"candidates": [
{
"value": null,
"source": "internal/cli/schema_hints/metadata/report.json",
"precedence": "reviewed_explicit",
"selected": true,
"review_reason": "final interface mode composite forbids a direct MCP interface_ref"
}
]
},
"reviewed": {
"value": true,
"source": "internal/cli/schema_hints/metadata/report.json",
"precedence": "reviewed_explicit",
"resolution": "highest_precedence",
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity.",
"candidates": [
{
"value": true,
"source": "internal/cli/schema_hints/metadata/report.json",
"precedence": "reviewed_explicit",
"selected": true,
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity."
},
{
"value": true,
"source": "internal/cli/schema_hints/selection/report.json",
"precedence": "reviewed_explicit",
"selected": false,
"review_reason": "Agent-authored and reviewed from the built-in Shortcut intent, executable Cobra path, and declared outcome; it affects selection only and does not alter execution or safety facts."
}
]
},
"risk": {
"value": "low",
"source": "internal/cli/schema_hints/metadata/report.json",
"precedence": "reviewed_explicit",
"resolution": "highest_precedence",
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity.",
"candidates": [
{
"value": "low",
"source": "internal/cli/schema_hints/metadata/report.json",
"precedence": "reviewed_explicit",
"selected": true,
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity."
}
]
},
"use_when": {
"value": [
"当你要回顾自己写过、提交过的日报周报,比如确认某天是否已交、找回历史汇报内容或统计提交情况时使用;可按创建/修改时间范围和模版名过滤,分页返回自己发出的日报列表及 reportId,供后续 +entry-get 查看正文。"
],
"source": "internal/cli/schema_hints/selection/report.json",
"precedence": "reviewed_explicit",
"resolution": "highest_precedence",
"review_reason": "Agent-authored and reviewed from the built-in Shortcut intent, executable Cobra path, and declared outcome; it affects selection only and does not alter execution or safety facts.",
"candidates": [
{
"value": [
"当你要回顾自己写过、提交过的日报周报,比如确认某天是否已交、找回历史汇报内容或统计提交情况时使用;可按创建/修改时间范围和模版名过滤,分页返回自己发出的日报列表及 reportId,供后续 +entry-get 查看正文。"
],
"source": "internal/cli/schema_hints/selection/report.json",
"precedence": "reviewed_explicit",
"selected": true,
"review_reason": "Agent-authored and reviewed from the built-in Shortcut intent, executable Cobra path, and declared outcome; it affects selection only and does not alter execution or safety facts."
}
]
}
},
"idempotency": "idempotent",
"interface_mode": "composite",
"interface_reason": "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
"reviewed": true,
"risk": "low",
"source_refs": [
"ShortcutRegistry:report +outbox-list",
"cobra-help:dws report +outbox-list",
"internal/cli/schema_command_registry.json#report.shortcut_outbox_list",
"internal/cli/schema_hints/metadata/report.json",
"internal/cli/schema_hints/selection/report.json"
],
"use_when": [
"当你要回顾自己写过、提交过的日报周报,比如确认某天是否已交、找回历史汇报内容或统计提交情况时使用;可按创建/修改时间范围和模版名过滤,分页返回自己发出的日报列表及 reportId,供后续 +entry-get 查看正文。"
]
},
"report entry get": {
"agent_summary": "获取指定一篇日志的详情信息",
"agent_summary_source": "dws-agent-selection/report",
@@ -1,6 +1,522 @@
{
"product_id": "sheet",
"tools": {
"sheet +list-sheets": {
"agent_summary": "获取表格文档中全部工作表列表",
"agent_summary_source": "dws-agent-selection/sheet",
"availability": "available",
"avoid_when": [
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
],
"confirmation": "not_required",
"effect": "read",
"effect_source": "agent-hint",
"examples": [
"dws sheet +list-sheets --node NODE_ID"
],
"field_provenance": {
"agent_summary": {
"value": "获取表格文档中全部工作表列表",
"source": "internal/cli/schema_hints/selection/sheet.json",
"precedence": "reviewed_explicit",
"resolution": "highest_precedence",
"review_reason": "Agent-authored and reviewed from the built-in Shortcut intent, executable Cobra path, and declared outcome; it affects selection only and does not alter execution or safety facts.",
"candidates": [
{
"value": "获取表格文档中全部工作表列表",
"source": "internal/cli/schema_hints/selection/sheet.json",
"precedence": "reviewed_explicit",
"selected": true,
"review_reason": "Agent-authored and reviewed from the built-in Shortcut intent, executable Cobra path, and declared outcome; it affects selection only and does not alter execution or safety facts."
}
]
},
"availability": {
"value": "available",
"source": "internal/cli/schema_hints/metadata/sheet.json",
"precedence": "reviewed_explicit",
"resolution": "highest_precedence",
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity.",
"candidates": [
{
"value": "available",
"source": "internal/cli/schema_hints/metadata/sheet.json",
"precedence": "reviewed_explicit",
"selected": true,
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity."
}
]
},
"avoid_when": {
"value": [
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
],
"source": "internal/cli/schema_hints/selection/sheet.json",
"precedence": "reviewed_explicit",
"resolution": "highest_precedence",
"review_reason": "Agent-authored and reviewed from the built-in Shortcut intent, executable Cobra path, and declared outcome; it affects selection only and does not alter execution or safety facts.",
"candidates": [
{
"value": [
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
],
"source": "internal/cli/schema_hints/selection/sheet.json",
"precedence": "reviewed_explicit",
"selected": true,
"review_reason": "Agent-authored and reviewed from the built-in Shortcut intent, executable Cobra path, and declared outcome; it affects selection only and does not alter execution or safety facts."
}
]
},
"confirmation": {
"value": "not_required",
"source": "internal/cli/schema_hints/metadata/sheet.json",
"precedence": "reviewed_explicit",
"resolution": "highest_precedence",
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity.",
"candidates": [
{
"value": "not_required",
"source": "internal/cli/schema_hints/metadata/sheet.json",
"precedence": "reviewed_explicit",
"selected": true,
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity."
}
]
},
"effect": {
"value": "read",
"source": "internal/cli/schema_hints/metadata/sheet.json",
"precedence": "reviewed_explicit",
"resolution": "highest_precedence",
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity.",
"candidates": [
{
"value": "read",
"source": "internal/cli/schema_hints/metadata/sheet.json",
"precedence": "reviewed_explicit",
"selected": true,
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity."
}
]
},
"examples": {
"value": [
"dws sheet +list-sheets --node NODE_ID"
],
"source": "internal/cli/schema_hints/selection/sheet.json",
"precedence": "reviewed_explicit",
"resolution": "highest_precedence",
"review_reason": "Agent-authored and reviewed from the built-in Shortcut intent, executable Cobra path, and declared outcome; it affects selection only and does not alter execution or safety facts.",
"candidates": [
{
"value": [
"dws sheet +list-sheets --node NODE_ID"
],
"source": "internal/cli/schema_hints/selection/sheet.json",
"precedence": "reviewed_explicit",
"selected": true,
"review_reason": "Agent-authored and reviewed from the built-in Shortcut intent, executable Cobra path, and declared outcome; it affects selection only and does not alter execution or safety facts."
}
]
},
"idempotency": {
"value": "idempotent",
"source": "internal/cli/schema_hints/metadata/sheet.json",
"precedence": "reviewed_explicit",
"resolution": "highest_precedence",
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity.",
"candidates": [
{
"value": "idempotent",
"source": "internal/cli/schema_hints/metadata/sheet.json",
"precedence": "reviewed_explicit",
"selected": true,
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity."
}
]
},
"interface_mode": {
"value": "composite",
"source": "internal/cli/schema_hints/metadata/sheet.json",
"precedence": "reviewed_explicit",
"resolution": "highest_precedence",
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity.",
"candidates": [
{
"value": "composite",
"source": "internal/cli/schema_hints/metadata/sheet.json",
"precedence": "reviewed_explicit",
"selected": true,
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity."
}
]
},
"interface_reason": {
"value": "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
"source": "internal/cli/schema_hints/metadata/sheet.json",
"precedence": "reviewed_explicit",
"resolution": "highest_precedence",
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity.",
"candidates": [
{
"value": "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
"source": "internal/cli/schema_hints/metadata/sheet.json",
"precedence": "reviewed_explicit",
"selected": true,
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity."
}
]
},
"interface_ref": {
"value": null,
"source": "internal/cli/schema_hints/metadata/sheet.json",
"precedence": "reviewed_explicit",
"resolution": "interface_disposition_matrix",
"review_reason": "final interface mode composite forbids a direct MCP interface_ref",
"candidates": [
{
"value": null,
"source": "internal/cli/schema_hints/metadata/sheet.json",
"precedence": "reviewed_explicit",
"selected": true,
"review_reason": "final interface mode composite forbids a direct MCP interface_ref"
}
]
},
"reviewed": {
"value": true,
"source": "internal/cli/schema_hints/metadata/sheet.json",
"precedence": "reviewed_explicit",
"resolution": "highest_precedence",
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity.",
"candidates": [
{
"value": true,
"source": "internal/cli/schema_hints/metadata/sheet.json",
"precedence": "reviewed_explicit",
"selected": true,
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity."
},
{
"value": true,
"source": "internal/cli/schema_hints/selection/sheet.json",
"precedence": "reviewed_explicit",
"selected": false,
"review_reason": "Agent-authored and reviewed from the built-in Shortcut intent, executable Cobra path, and declared outcome; it affects selection only and does not alter execution or safety facts."
}
]
},
"risk": {
"value": "low",
"source": "internal/cli/schema_hints/metadata/sheet.json",
"precedence": "reviewed_explicit",
"resolution": "highest_precedence",
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity.",
"candidates": [
{
"value": "low",
"source": "internal/cli/schema_hints/metadata/sheet.json",
"precedence": "reviewed_explicit",
"selected": true,
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity."
}
]
},
"use_when": {
"value": [
"当你拿到一个表格文档、想先了解它里面有哪些工作表(sheet)以及各自的 sheetId 时使用,通常作为读写具体数据前的第一步;传入表格文档 ID 或 URL,返回工作表清单。"
],
"source": "internal/cli/schema_hints/selection/sheet.json",
"precedence": "reviewed_explicit",
"resolution": "highest_precedence",
"review_reason": "Agent-authored and reviewed from the built-in Shortcut intent, executable Cobra path, and declared outcome; it affects selection only and does not alter execution or safety facts.",
"candidates": [
{
"value": [
"当你拿到一个表格文档、想先了解它里面有哪些工作表(sheet)以及各自的 sheetId 时使用,通常作为读写具体数据前的第一步;传入表格文档 ID 或 URL,返回工作表清单。"
],
"source": "internal/cli/schema_hints/selection/sheet.json",
"precedence": "reviewed_explicit",
"selected": true,
"review_reason": "Agent-authored and reviewed from the built-in Shortcut intent, executable Cobra path, and declared outcome; it affects selection only and does not alter execution or safety facts."
}
]
}
},
"idempotency": "idempotent",
"interface_mode": "composite",
"interface_reason": "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
"reviewed": true,
"risk": "low",
"source_refs": [
"ShortcutRegistry:sheet +list-sheets",
"cobra-help:dws sheet +list-sheets",
"internal/cli/schema_command_registry.json#sheet.shortcut_list_sheets",
"internal/cli/schema_hints/metadata/sheet.json",
"internal/cli/schema_hints/selection/sheet.json"
],
"use_when": [
"当你拿到一个表格文档、想先了解它里面有哪些工作表(sheet)以及各自的 sheetId 时使用,通常作为读写具体数据前的第一步;传入表格文档 ID 或 URL,返回工作表清单。"
]
},
"sheet +read": {
"agent_summary": "读取工作表指定范围的结构化单元格数据",
"agent_summary_source": "dws-agent-selection/sheet",
"availability": "available",
"avoid_when": [
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
],
"confirmation": "not_required",
"effect": "read",
"effect_source": "agent-hint",
"examples": [
"dws sheet +read --node NODE_ID --sheet-id SHEET_ID --range \"A1:D10\""
],
"field_provenance": {
"agent_summary": {
"value": "读取工作表指定范围的结构化单元格数据",
"source": "internal/cli/schema_hints/selection/sheet.json",
"precedence": "reviewed_explicit",
"resolution": "highest_precedence",
"review_reason": "Agent-authored and reviewed from the built-in Shortcut intent, executable Cobra path, and declared outcome; it affects selection only and does not alter execution or safety facts.",
"candidates": [
{
"value": "读取工作表指定范围的结构化单元格数据",
"source": "internal/cli/schema_hints/selection/sheet.json",
"precedence": "reviewed_explicit",
"selected": true,
"review_reason": "Agent-authored and reviewed from the built-in Shortcut intent, executable Cobra path, and declared outcome; it affects selection only and does not alter execution or safety facts."
}
]
},
"availability": {
"value": "available",
"source": "internal/cli/schema_hints/metadata/sheet.json",
"precedence": "reviewed_explicit",
"resolution": "highest_precedence",
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity.",
"candidates": [
{
"value": "available",
"source": "internal/cli/schema_hints/metadata/sheet.json",
"precedence": "reviewed_explicit",
"selected": true,
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity."
}
]
},
"avoid_when": {
"value": [
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
],
"source": "internal/cli/schema_hints/selection/sheet.json",
"precedence": "reviewed_explicit",
"resolution": "highest_precedence",
"review_reason": "Agent-authored and reviewed from the built-in Shortcut intent, executable Cobra path, and declared outcome; it affects selection only and does not alter execution or safety facts.",
"candidates": [
{
"value": [
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
],
"source": "internal/cli/schema_hints/selection/sheet.json",
"precedence": "reviewed_explicit",
"selected": true,
"review_reason": "Agent-authored and reviewed from the built-in Shortcut intent, executable Cobra path, and declared outcome; it affects selection only and does not alter execution or safety facts."
}
]
},
"confirmation": {
"value": "not_required",
"source": "internal/cli/schema_hints/metadata/sheet.json",
"precedence": "reviewed_explicit",
"resolution": "highest_precedence",
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity.",
"candidates": [
{
"value": "not_required",
"source": "internal/cli/schema_hints/metadata/sheet.json",
"precedence": "reviewed_explicit",
"selected": true,
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity."
}
]
},
"effect": {
"value": "read",
"source": "internal/cli/schema_hints/metadata/sheet.json",
"precedence": "reviewed_explicit",
"resolution": "highest_precedence",
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity.",
"candidates": [
{
"value": "read",
"source": "internal/cli/schema_hints/metadata/sheet.json",
"precedence": "reviewed_explicit",
"selected": true,
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity."
}
]
},
"examples": {
"value": [
"dws sheet +read --node NODE_ID --sheet-id SHEET_ID --range \"A1:D10\""
],
"source": "internal/cli/schema_hints/selection/sheet.json",
"precedence": "reviewed_explicit",
"resolution": "highest_precedence",
"review_reason": "Agent-authored and reviewed from the built-in Shortcut intent, executable Cobra path, and declared outcome; it affects selection only and does not alter execution or safety facts.",
"candidates": [
{
"value": [
"dws sheet +read --node NODE_ID --sheet-id SHEET_ID --range \"A1:D10\""
],
"source": "internal/cli/schema_hints/selection/sheet.json",
"precedence": "reviewed_explicit",
"selected": true,
"review_reason": "Agent-authored and reviewed from the built-in Shortcut intent, executable Cobra path, and declared outcome; it affects selection only and does not alter execution or safety facts."
}
]
},
"idempotency": {
"value": "idempotent",
"source": "internal/cli/schema_hints/metadata/sheet.json",
"precedence": "reviewed_explicit",
"resolution": "highest_precedence",
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity.",
"candidates": [
{
"value": "idempotent",
"source": "internal/cli/schema_hints/metadata/sheet.json",
"precedence": "reviewed_explicit",
"selected": true,
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity."
}
]
},
"interface_mode": {
"value": "composite",
"source": "internal/cli/schema_hints/metadata/sheet.json",
"precedence": "reviewed_explicit",
"resolution": "highest_precedence",
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity.",
"candidates": [
{
"value": "composite",
"source": "internal/cli/schema_hints/metadata/sheet.json",
"precedence": "reviewed_explicit",
"selected": true,
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity."
}
]
},
"interface_reason": {
"value": "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
"source": "internal/cli/schema_hints/metadata/sheet.json",
"precedence": "reviewed_explicit",
"resolution": "highest_precedence",
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity.",
"candidates": [
{
"value": "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
"source": "internal/cli/schema_hints/metadata/sheet.json",
"precedence": "reviewed_explicit",
"selected": true,
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity."
}
]
},
"interface_ref": {
"value": null,
"source": "internal/cli/schema_hints/metadata/sheet.json",
"precedence": "reviewed_explicit",
"resolution": "interface_disposition_matrix",
"review_reason": "final interface mode composite forbids a direct MCP interface_ref",
"candidates": [
{
"value": null,
"source": "internal/cli/schema_hints/metadata/sheet.json",
"precedence": "reviewed_explicit",
"selected": true,
"review_reason": "final interface mode composite forbids a direct MCP interface_ref"
}
]
},
"reviewed": {
"value": true,
"source": "internal/cli/schema_hints/metadata/sheet.json",
"precedence": "reviewed_explicit",
"resolution": "highest_precedence",
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity.",
"candidates": [
{
"value": true,
"source": "internal/cli/schema_hints/metadata/sheet.json",
"precedence": "reviewed_explicit",
"selected": true,
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity."
},
{
"value": true,
"source": "internal/cli/schema_hints/selection/sheet.json",
"precedence": "reviewed_explicit",
"selected": false,
"review_reason": "Agent-authored and reviewed from the built-in Shortcut intent, executable Cobra path, and declared outcome; it affects selection only and does not alter execution or safety facts."
}
]
},
"risk": {
"value": "low",
"source": "internal/cli/schema_hints/metadata/sheet.json",
"precedence": "reviewed_explicit",
"resolution": "highest_precedence",
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity.",
"candidates": [
{
"value": "low",
"source": "internal/cli/schema_hints/metadata/sheet.json",
"precedence": "reviewed_explicit",
"selected": true,
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity."
}
]
},
"use_when": {
"value": [
"当你需要按单元格逐格获取数据(含类型、公式或格式化值等结构化信息)以便程序处理时使用;传入表格与可选范围(A1 表示法,不传则全部),可指定取格式化值/原始值/公式,返回结构化单元格数组。若只想要纯文本可改用 +csv-get。"
],
"source": "internal/cli/schema_hints/selection/sheet.json",
"precedence": "reviewed_explicit",
"resolution": "highest_precedence",
"review_reason": "Agent-authored and reviewed from the built-in Shortcut intent, executable Cobra path, and declared outcome; it affects selection only and does not alter execution or safety facts.",
"candidates": [
{
"value": [
"当你需要按单元格逐格获取数据(含类型、公式或格式化值等结构化信息)以便程序处理时使用;传入表格与可选范围(A1 表示法,不传则全部),可指定取格式化值/原始值/公式,返回结构化单元格数组。若只想要纯文本可改用 +csv-get。"
],
"source": "internal/cli/schema_hints/selection/sheet.json",
"precedence": "reviewed_explicit",
"selected": true,
"review_reason": "Agent-authored and reviewed from the built-in Shortcut intent, executable Cobra path, and declared outcome; it affects selection only and does not alter execution or safety facts."
}
]
}
},
"idempotency": "idempotent",
"interface_mode": "composite",
"interface_reason": "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
"reviewed": true,
"risk": "low",
"source_refs": [
"ShortcutRegistry:sheet +read",
"cobra-help:dws sheet +read",
"internal/cli/schema_command_registry.json#sheet.shortcut_read",
"internal/cli/schema_hints/metadata/sheet.json",
"internal/cli/schema_hints/selection/sheet.json"
],
"use_when": [
"当你需要按单元格逐格获取数据(含类型、公式或格式化值等结构化信息)以便程序处理时使用;传入表格与可选范围(A1 表示法,不传则全部),可指定取格式化值/原始值/公式,返回结构化单元格数组。若只想要纯文本可改用 +csv-get。"
]
},
"sheet add-dimension": {
"agent_summary": "在工作表末尾追加空行或空列。",
"agent_summary_source": "dws-agent-selection/sheet",
File diff suppressed because it is too large Load Diff
@@ -1,6 +1,264 @@
{
"product_id": "wiki",
"tools": {
"wiki +space-search": {
"agent_summary": "搜索知识库",
"agent_summary_source": "dws-agent-selection/wiki",
"availability": "available",
"avoid_when": [
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
],
"confirmation": "not_required",
"effect": "read",
"effect_source": "agent-hint",
"examples": [
"dws wiki +space-search --query \"产品文档\""
],
"field_provenance": {
"agent_summary": {
"value": "搜索知识库",
"source": "internal/cli/schema_hints/selection/wiki.json",
"precedence": "reviewed_explicit",
"resolution": "highest_precedence",
"review_reason": "Agent-authored and reviewed from the built-in Shortcut intent, executable Cobra path, and declared outcome; it affects selection only and does not alter execution or safety facts.",
"candidates": [
{
"value": "搜索知识库",
"source": "internal/cli/schema_hints/selection/wiki.json",
"precedence": "reviewed_explicit",
"selected": true,
"review_reason": "Agent-authored and reviewed from the built-in Shortcut intent, executable Cobra path, and declared outcome; it affects selection only and does not alter execution or safety facts."
}
]
},
"availability": {
"value": "available",
"source": "internal/cli/schema_hints/metadata/wiki.json",
"precedence": "reviewed_explicit",
"resolution": "highest_precedence",
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity.",
"candidates": [
{
"value": "available",
"source": "internal/cli/schema_hints/metadata/wiki.json",
"precedence": "reviewed_explicit",
"selected": true,
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity."
}
]
},
"avoid_when": {
"value": [
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
],
"source": "internal/cli/schema_hints/selection/wiki.json",
"precedence": "reviewed_explicit",
"resolution": "highest_precedence",
"review_reason": "Agent-authored and reviewed from the built-in Shortcut intent, executable Cobra path, and declared outcome; it affects selection only and does not alter execution or safety facts.",
"candidates": [
{
"value": [
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
],
"source": "internal/cli/schema_hints/selection/wiki.json",
"precedence": "reviewed_explicit",
"selected": true,
"review_reason": "Agent-authored and reviewed from the built-in Shortcut intent, executable Cobra path, and declared outcome; it affects selection only and does not alter execution or safety facts."
}
]
},
"confirmation": {
"value": "not_required",
"source": "internal/cli/schema_hints/metadata/wiki.json",
"precedence": "reviewed_explicit",
"resolution": "highest_precedence",
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity.",
"candidates": [
{
"value": "not_required",
"source": "internal/cli/schema_hints/metadata/wiki.json",
"precedence": "reviewed_explicit",
"selected": true,
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity."
}
]
},
"effect": {
"value": "read",
"source": "internal/cli/schema_hints/metadata/wiki.json",
"precedence": "reviewed_explicit",
"resolution": "highest_precedence",
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity.",
"candidates": [
{
"value": "read",
"source": "internal/cli/schema_hints/metadata/wiki.json",
"precedence": "reviewed_explicit",
"selected": true,
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity."
}
]
},
"examples": {
"value": [
"dws wiki +space-search --query \"产品文档\""
],
"source": "internal/cli/schema_hints/selection/wiki.json",
"precedence": "reviewed_explicit",
"resolution": "highest_precedence",
"review_reason": "Agent-authored and reviewed from the built-in Shortcut intent, executable Cobra path, and declared outcome; it affects selection only and does not alter execution or safety facts.",
"candidates": [
{
"value": [
"dws wiki +space-search --query \"产品文档\""
],
"source": "internal/cli/schema_hints/selection/wiki.json",
"precedence": "reviewed_explicit",
"selected": true,
"review_reason": "Agent-authored and reviewed from the built-in Shortcut intent, executable Cobra path, and declared outcome; it affects selection only and does not alter execution or safety facts."
}
]
},
"idempotency": {
"value": "idempotent",
"source": "internal/cli/schema_hints/metadata/wiki.json",
"precedence": "reviewed_explicit",
"resolution": "highest_precedence",
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity.",
"candidates": [
{
"value": "idempotent",
"source": "internal/cli/schema_hints/metadata/wiki.json",
"precedence": "reviewed_explicit",
"selected": true,
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity."
}
]
},
"interface_mode": {
"value": "composite",
"source": "internal/cli/schema_hints/metadata/wiki.json",
"precedence": "reviewed_explicit",
"resolution": "highest_precedence",
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity.",
"candidates": [
{
"value": "composite",
"source": "internal/cli/schema_hints/metadata/wiki.json",
"precedence": "reviewed_explicit",
"selected": true,
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity."
}
]
},
"interface_reason": {
"value": "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
"source": "internal/cli/schema_hints/metadata/wiki.json",
"precedence": "reviewed_explicit",
"resolution": "highest_precedence",
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity.",
"candidates": [
{
"value": "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
"source": "internal/cli/schema_hints/metadata/wiki.json",
"precedence": "reviewed_explicit",
"selected": true,
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity."
}
]
},
"interface_ref": {
"value": null,
"source": "internal/cli/schema_hints/metadata/wiki.json",
"precedence": "reviewed_explicit",
"resolution": "interface_disposition_matrix",
"review_reason": "final interface mode composite forbids a direct MCP interface_ref",
"candidates": [
{
"value": null,
"source": "internal/cli/schema_hints/metadata/wiki.json",
"precedence": "reviewed_explicit",
"selected": true,
"review_reason": "final interface mode composite forbids a direct MCP interface_ref"
}
]
},
"reviewed": {
"value": true,
"source": "internal/cli/schema_hints/metadata/wiki.json",
"precedence": "reviewed_explicit",
"resolution": "highest_precedence",
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity.",
"candidates": [
{
"value": true,
"source": "internal/cli/schema_hints/metadata/wiki.json",
"precedence": "reviewed_explicit",
"selected": true,
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity."
},
{
"value": true,
"source": "internal/cli/schema_hints/selection/wiki.json",
"precedence": "reviewed_explicit",
"selected": false,
"review_reason": "Agent-authored and reviewed from the built-in Shortcut intent, executable Cobra path, and declared outcome; it affects selection only and does not alter execution or safety facts."
}
]
},
"risk": {
"value": "low",
"source": "internal/cli/schema_hints/metadata/wiki.json",
"precedence": "reviewed_explicit",
"resolution": "highest_precedence",
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity.",
"candidates": [
{
"value": "low",
"source": "internal/cli/schema_hints/metadata/wiki.json",
"precedence": "reviewed_explicit",
"selected": true,
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity."
}
]
},
"use_when": {
"value": [
"当你只记得知识库名称的部分关键词、想快速按名称定位某个知识库时使用;输入关键词返回匹配的知识库列表,比逐页 +space-list 更快找到目标 workspaceId。"
],
"source": "internal/cli/schema_hints/selection/wiki.json",
"precedence": "reviewed_explicit",
"resolution": "highest_precedence",
"review_reason": "Agent-authored and reviewed from the built-in Shortcut intent, executable Cobra path, and declared outcome; it affects selection only and does not alter execution or safety facts.",
"candidates": [
{
"value": [
"当你只记得知识库名称的部分关键词、想快速按名称定位某个知识库时使用;输入关键词返回匹配的知识库列表,比逐页 +space-list 更快找到目标 workspaceId。"
],
"source": "internal/cli/schema_hints/selection/wiki.json",
"precedence": "reviewed_explicit",
"selected": true,
"review_reason": "Agent-authored and reviewed from the built-in Shortcut intent, executable Cobra path, and declared outcome; it affects selection only and does not alter execution or safety facts."
}
]
}
},
"idempotency": "idempotent",
"interface_mode": "composite",
"interface_reason": "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
"reviewed": true,
"risk": "low",
"source_refs": [
"ShortcutRegistry:wiki +space-search",
"cobra-help:dws wiki +space-search",
"internal/cli/schema_command_registry.json#wiki.shortcut_space_search",
"internal/cli/schema_hints/metadata/wiki.json",
"internal/cli/schema_hints/selection/wiki.json"
],
"use_when": [
"当你只记得知识库名称的部分关键词、想快速按名称定位某个知识库时使用;输入关键词返回匹配的知识库列表,比逐页 +space-list 更快找到目标 workspaceId。"
]
},
"wiki member add": {
"agent_summary": "为指定知识库添加一个或多个成员,并授予指定角色",
"agent_summary_source": "dws-agent-selection/wiki",
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
-217
View File
@@ -48,223 +48,6 @@
"version"
]
},
{
"id": "shortcut-catalog-surface",
"reason": "Shortcut commands are high-level task entries discovered through dws shortcut list and product skills; they are intentionally separate from the runtime Schema catalog of base Agent tools.",
"reviewed": true,
"commands": [
"aitable +base-get",
"aitable +base-list",
"aitable +base-search",
"aitable +chart-get",
"aitable +chart-widgets-example",
"aitable +dashboard-config-example",
"aitable +dashboard-get",
"aitable +field-get",
"aitable +find-record",
"aitable +form-field-list",
"aitable +form-list",
"aitable +form-share-get",
"aitable +list-tables",
"aitable +record-history-list",
"aitable +record-query",
"aitable +record-query-empty",
"aitable +record-share-links",
"aitable +record-share-url",
"aitable +resolve-base",
"aitable +resolve-table",
"aitable +role-list",
"aitable +section-list-empty",
"aitable +section-list-nodes",
"aitable +table-get",
"aitable +template-search",
"aitable +view-get",
"aitable +view-get-frozen-cols",
"aitable +view-get-lock",
"aitable +view-get-row-height",
"attendance +check-record",
"attendance +check-result",
"attendance +get-adjustment-rule",
"attendance +get-approve-template",
"attendance +get-checkin-record",
"attendance +get-leave-records",
"attendance +get-overtime-rule",
"attendance +get-schedule",
"attendance +get-self-setting",
"attendance +get-summary",
"attendance +list-approve",
"attendance +list-leave-types",
"attendance +my-attendance",
"attendance +query-report-data",
"attendance +search-adjustment-rule",
"attendance +search-class",
"attendance +search-group",
"attendance +search-overtime-rule",
"attendance +this-month",
"calendar +agenda",
"calendar +attendee-list",
"calendar +book",
"calendar +book-list",
"calendar +book-search",
"calendar +cancel-event",
"calendar +conflicts",
"calendar +free",
"calendar +free-slots",
"calendar +freebusy",
"calendar +invite",
"calendar +my-free",
"calendar +next-event",
"calendar +reschedule",
"calendar +room-groups",
"calendar +room-search",
"calendar +suggest-time",
"calendar +today",
"calendar +tomorrow",
"calendar +week",
"chat +at-me",
"chat +bot-find",
"chat +bot-search",
"chat +broadcast",
"chat +category-create",
"chat +category-delete",
"chat +category-list",
"chat +category-rename",
"chat +chat-bots",
"chat +chat-dismiss",
"chat +chat-invite-url",
"chat +chat-list-all",
"chat +chat-list-join-requests",
"chat +chat-list-mine",
"chat +chat-mute",
"chat +chat-role-add",
"chat +chat-role-list",
"chat +chat-role-query-user",
"chat +chat-role-set-user",
"chat +chat-role-update",
"chat +chat-search",
"chat +chat-set-admin",
"chat +chat-set-history",
"chat +chat-update-alias",
"chat +chat-update-nick",
"chat +conversation-clear-all-red-point",
"chat +conversation-info",
"chat +conversation-list",
"chat +conversation-list-top",
"chat +dm",
"chat +group-members",
"chat +messages-list-direct",
"chat +messages-list-pin",
"chat +messages-list-unread-conversations",
"chat +messages-mget",
"chat +messages-query-send-status",
"chat +messages-read-status",
"chat +messages-send-by-webhook",
"chat +messages-update-card",
"chat +my-groups",
"chat +send-to-group",
"chat +unread-chats",
"contact +by-mobile",
"contact +dept-members",
"contact +list-dept-members",
"contact +list-followings",
"contact +list-role-members",
"contact +list-roles",
"contact +list-sub-depts",
"contact +lookup",
"contact +me",
"contact +org",
"contact +resolve-dept",
"contact +search-mobile",
"contact +search-user",
"contact +team",
"devapp +create",
"devapp +delete",
"devapp +disable",
"devapp +enable",
"devapp +event-list",
"devapp +get",
"devapp +list",
"devapp +member-add",
"devapp +member-list",
"devapp +member-remove",
"devapp +permission-list",
"devapp +robot-get",
"devapp +update",
"devapp +version-check-approval",
"devapp +version-get",
"devapp +version-list",
"devapp +version-status",
"devapp +webapp-config",
"devapp +webapp-get",
"ding +list",
"ding +recall-personal",
"ding +receiver-status",
"ding +send-personal",
"doc +comment-create",
"doc +comment-list",
"doc +comment-reply",
"doc +copy",
"doc +doc-append",
"doc +export-get",
"doc +export-submit",
"doc +find-doc",
"doc +list",
"doc +move",
"doc +search",
"doc +share-doc",
"doc +template-list",
"doc +template-search",
"doc +version-list",
"doc +version-revert",
"doc +version-save",
"drive +copy",
"drive +find-file",
"drive +info",
"drive +move",
"drive +recent",
"drive +search",
"drive +search-docs",
"mail +contact-list",
"mail +find-mail-user",
"mail +folder-list",
"mail +recent-mail",
"mail +search-mail",
"mail +tag-list",
"mail +template-list",
"mail +thread-list",
"mail +unread-mail",
"mail +user-search",
"minutes +detail",
"minutes +list-all",
"minutes +list-mine",
"minutes +list-shared",
"minutes +record-start",
"minutes +replace-batch",
"oa +list-cc",
"oa +list-executed",
"oa +list-forms",
"oa +list-pending",
"oa +list-submitted",
"oa +my-initiated",
"oa +search-forms",
"report +inbox-list",
"report +outbox-list",
"sheet +list-sheets",
"sheet +read",
"todo +assign",
"todo +assign-multi",
"todo +created-todos",
"todo +get",
"todo +get-my-tasks",
"todo +list-attachment",
"todo +list-comment",
"todo +list-sub",
"todo +overdue",
"todo +remind",
"todo +todo-done",
"wiki +space-search"
]
},
{
"id": "agoal-out-of-surface",
"reason": "The Agoal product remains executable for compatibility but is outside the currently reviewed open-source Agent command surface.",
+1 -1
View File
@@ -34,7 +34,7 @@ var embeddedSchemaCommandRegistrySchemaJSON []byte
var (
commandRegistryProductIDPattern = regexp.MustCompile(`^[A-Za-z0-9][A-Za-z0-9_-]*$`)
commandRegistryCanonicalPattern = regexp.MustCompile(`^[A-Za-z0-9][A-Za-z0-9_-]*\.[A-Za-z0-9][A-Za-z0-9_.-]*$`)
commandRegistryCLIPathToken = regexp.MustCompile(`^[A-Za-z0-9][A-Za-z0-9._:-]*$`)
commandRegistryCLIPathToken = regexp.MustCompile(`^(?:[A-Za-z0-9][A-Za-z0-9._:-]*|\+[A-Za-z0-9][A-Za-z0-9._:-]*)$`)
)
type schemaCommandRegistrySnapshot struct {
+845
View File
@@ -488,6 +488,122 @@
{
"canonical_path": "aitable.workflow_update",
"cli_path": "aitable workflow update"
},
{
"canonical_path": "aitable.shortcut_base_list",
"cli_path": "aitable +base-list"
},
{
"canonical_path": "aitable.shortcut_base_search",
"cli_path": "aitable +base-search"
},
{
"canonical_path": "aitable.shortcut_base_get",
"cli_path": "aitable +base-get"
},
{
"canonical_path": "aitable.shortcut_table_get",
"cli_path": "aitable +table-get"
},
{
"canonical_path": "aitable.shortcut_field_get",
"cli_path": "aitable +field-get"
},
{
"canonical_path": "aitable.shortcut_record_query",
"cli_path": "aitable +record-query"
},
{
"canonical_path": "aitable.shortcut_record_query_empty",
"cli_path": "aitable +record-query-empty"
},
{
"canonical_path": "aitable.shortcut_record_history_list",
"cli_path": "aitable +record-history-list"
},
{
"canonical_path": "aitable.shortcut_record_share_url",
"cli_path": "aitable +record-share-url"
},
{
"canonical_path": "aitable.shortcut_template_search",
"cli_path": "aitable +template-search"
},
{
"canonical_path": "aitable.shortcut_view_get",
"cli_path": "aitable +view-get"
},
{
"canonical_path": "aitable.shortcut_view_get_lock",
"cli_path": "aitable +view-get-lock"
},
{
"canonical_path": "aitable.shortcut_view_get_frozen_cols",
"cli_path": "aitable +view-get-frozen-cols"
},
{
"canonical_path": "aitable.shortcut_view_get_row_height",
"cli_path": "aitable +view-get-row-height"
},
{
"canonical_path": "aitable.shortcut_form_list",
"cli_path": "aitable +form-list"
},
{
"canonical_path": "aitable.shortcut_form_field_list",
"cli_path": "aitable +form-field-list"
},
{
"canonical_path": "aitable.shortcut_form_share_get",
"cli_path": "aitable +form-share-get"
},
{
"canonical_path": "aitable.shortcut_dashboard_config_example",
"cli_path": "aitable +dashboard-config-example"
},
{
"canonical_path": "aitable.shortcut_dashboard_get",
"cli_path": "aitable +dashboard-get"
},
{
"canonical_path": "aitable.shortcut_chart_widgets_example",
"cli_path": "aitable +chart-widgets-example"
},
{
"canonical_path": "aitable.shortcut_chart_get",
"cli_path": "aitable +chart-get"
},
{
"canonical_path": "aitable.shortcut_role_list",
"cli_path": "aitable +role-list"
},
{
"canonical_path": "aitable.shortcut_section_list_empty",
"cli_path": "aitable +section-list-empty"
},
{
"canonical_path": "aitable.shortcut_section_list_nodes",
"cli_path": "aitable +section-list-nodes"
},
{
"canonical_path": "aitable.shortcut_find_record",
"cli_path": "aitable +find-record"
},
{
"canonical_path": "aitable.shortcut_list_tables",
"cli_path": "aitable +list-tables"
},
{
"canonical_path": "aitable.shortcut_record_share_links",
"cli_path": "aitable +record-share-links"
},
{
"canonical_path": "aitable.shortcut_resolve_base",
"cli_path": "aitable +resolve-base"
},
{
"canonical_path": "aitable.shortcut_resolve_table",
"cli_path": "aitable +resolve-table"
}
]
},
@@ -645,6 +761,82 @@
{
"canonical_path": "attendance.vacation_update_type",
"cli_path": "attendance vacation update-type"
},
{
"canonical_path": "attendance.shortcut_check_result",
"cli_path": "attendance +check-result"
},
{
"canonical_path": "attendance.shortcut_check_record",
"cli_path": "attendance +check-record"
},
{
"canonical_path": "attendance.shortcut_list_approve",
"cli_path": "attendance +list-approve"
},
{
"canonical_path": "attendance.shortcut_get_approve_template",
"cli_path": "attendance +get-approve-template"
},
{
"canonical_path": "attendance.shortcut_get_schedule",
"cli_path": "attendance +get-schedule"
},
{
"canonical_path": "attendance.shortcut_search_class",
"cli_path": "attendance +search-class"
},
{
"canonical_path": "attendance.shortcut_get_adjustment_rule",
"cli_path": "attendance +get-adjustment-rule"
},
{
"canonical_path": "attendance.shortcut_search_adjustment_rule",
"cli_path": "attendance +search-adjustment-rule"
},
{
"canonical_path": "attendance.shortcut_get_overtime_rule",
"cli_path": "attendance +get-overtime-rule"
},
{
"canonical_path": "attendance.shortcut_search_overtime_rule",
"cli_path": "attendance +search-overtime-rule"
},
{
"canonical_path": "attendance.shortcut_search_group",
"cli_path": "attendance +search-group"
},
{
"canonical_path": "attendance.shortcut_get_summary",
"cli_path": "attendance +get-summary"
},
{
"canonical_path": "attendance.shortcut_get_self_setting",
"cli_path": "attendance +get-self-setting"
},
{
"canonical_path": "attendance.shortcut_query_report_data",
"cli_path": "attendance +query-report-data"
},
{
"canonical_path": "attendance.shortcut_list_leave_types",
"cli_path": "attendance +list-leave-types"
},
{
"canonical_path": "attendance.shortcut_get_leave_records",
"cli_path": "attendance +get-leave-records"
},
{
"canonical_path": "attendance.shortcut_get_checkin_record",
"cli_path": "attendance +get-checkin-record"
},
{
"canonical_path": "attendance.shortcut_my_attendance",
"cli_path": "attendance +my-attendance"
},
{
"canonical_path": "attendance.shortcut_this_month",
"cli_path": "attendance +this-month"
}
]
},
@@ -747,6 +939,86 @@
{
"canonical_path": "calendar.query_available_meeting_room",
"cli_path": "calendar room search"
},
{
"canonical_path": "calendar.shortcut_agenda",
"cli_path": "calendar +agenda"
},
{
"canonical_path": "calendar.shortcut_attendee_list",
"cli_path": "calendar +attendee-list"
},
{
"canonical_path": "calendar.shortcut_room_search",
"cli_path": "calendar +room-search"
},
{
"canonical_path": "calendar.shortcut_room_groups",
"cli_path": "calendar +room-groups"
},
{
"canonical_path": "calendar.shortcut_freebusy",
"cli_path": "calendar +freebusy"
},
{
"canonical_path": "calendar.shortcut_book_list",
"cli_path": "calendar +book-list"
},
{
"canonical_path": "calendar.shortcut_book_search",
"cli_path": "calendar +book-search"
},
{
"canonical_path": "calendar.shortcut_book",
"cli_path": "calendar +book"
},
{
"canonical_path": "calendar.shortcut_cancel_event",
"cli_path": "calendar +cancel-event"
},
{
"canonical_path": "calendar.shortcut_conflicts",
"cli_path": "calendar +conflicts"
},
{
"canonical_path": "calendar.shortcut_free_slots",
"cli_path": "calendar +free-slots"
},
{
"canonical_path": "calendar.shortcut_free",
"cli_path": "calendar +free"
},
{
"canonical_path": "calendar.shortcut_invite",
"cli_path": "calendar +invite"
},
{
"canonical_path": "calendar.shortcut_my_free",
"cli_path": "calendar +my-free"
},
{
"canonical_path": "calendar.shortcut_next_event",
"cli_path": "calendar +next-event"
},
{
"canonical_path": "calendar.shortcut_reschedule",
"cli_path": "calendar +reschedule"
},
{
"canonical_path": "calendar.shortcut_suggest_time",
"cli_path": "calendar +suggest-time"
},
{
"canonical_path": "calendar.shortcut_today",
"cli_path": "calendar +today"
},
{
"canonical_path": "calendar.shortcut_tomorrow",
"cli_path": "calendar +tomorrow"
},
{
"canonical_path": "calendar.shortcut_week",
"cli_path": "calendar +week"
}
]
},
@@ -1064,6 +1336,174 @@
{
"canonical_path": "chat.upgrade_group_to_external",
"cli_path": "chat group upgrade-to-external"
},
{
"canonical_path": "chat.shortcut_bot_search",
"cli_path": "chat +bot-search"
},
{
"canonical_path": "chat.shortcut_bot_find",
"cli_path": "chat +bot-find"
},
{
"canonical_path": "chat.shortcut_conversation_info",
"cli_path": "chat +conversation-info"
},
{
"canonical_path": "chat.shortcut_conversation_clear_all_red_point",
"cli_path": "chat +conversation-clear-all-red-point"
},
{
"canonical_path": "chat.shortcut_conversation_list",
"cli_path": "chat +conversation-list"
},
{
"canonical_path": "chat.shortcut_conversation_list_top",
"cli_path": "chat +conversation-list-top"
},
{
"canonical_path": "chat.shortcut_category_list",
"cli_path": "chat +category-list"
},
{
"canonical_path": "chat.shortcut_category_create",
"cli_path": "chat +category-create"
},
{
"canonical_path": "chat.shortcut_category_delete",
"cli_path": "chat +category-delete"
},
{
"canonical_path": "chat.shortcut_category_rename",
"cli_path": "chat +category-rename"
},
{
"canonical_path": "chat.shortcut_chat_search",
"cli_path": "chat +chat-search"
},
{
"canonical_path": "chat.shortcut_chat_invite_url",
"cli_path": "chat +chat-invite-url"
},
{
"canonical_path": "chat.shortcut_chat_dismiss",
"cli_path": "chat +chat-dismiss"
},
{
"canonical_path": "chat.shortcut_chat_set_history",
"cli_path": "chat +chat-set-history"
},
{
"canonical_path": "chat.shortcut_chat_update_nick",
"cli_path": "chat +chat-update-nick"
},
{
"canonical_path": "chat.shortcut_chat_update_alias",
"cli_path": "chat +chat-update-alias"
},
{
"canonical_path": "chat.shortcut_chat_list_mine",
"cli_path": "chat +chat-list-mine"
},
{
"canonical_path": "chat.shortcut_chat_list_all",
"cli_path": "chat +chat-list-all"
},
{
"canonical_path": "chat.shortcut_chat_list_join_requests",
"cli_path": "chat +chat-list-join-requests"
},
{
"canonical_path": "chat.shortcut_chat_bots",
"cli_path": "chat +chat-bots"
},
{
"canonical_path": "chat.shortcut_chat_set_admin",
"cli_path": "chat +chat-set-admin"
},
{
"canonical_path": "chat.shortcut_chat_mute",
"cli_path": "chat +chat-mute"
},
{
"canonical_path": "chat.shortcut_chat_role_list",
"cli_path": "chat +chat-role-list"
},
{
"canonical_path": "chat.shortcut_chat_role_add",
"cli_path": "chat +chat-role-add"
},
{
"canonical_path": "chat.shortcut_chat_role_update",
"cli_path": "chat +chat-role-update"
},
{
"canonical_path": "chat.shortcut_chat_role_set_user",
"cli_path": "chat +chat-role-set-user"
},
{
"canonical_path": "chat.shortcut_chat_role_query_user",
"cli_path": "chat +chat-role-query-user"
},
{
"canonical_path": "chat.shortcut_messages_send_by_webhook",
"cli_path": "chat +messages-send-by-webhook"
},
{
"canonical_path": "chat.shortcut_messages_list_direct",
"cli_path": "chat +messages-list-direct"
},
{
"canonical_path": "chat.shortcut_messages_list_unread_conversations",
"cli_path": "chat +messages-list-unread-conversations"
},
{
"canonical_path": "chat.shortcut_messages_mget",
"cli_path": "chat +messages-mget"
},
{
"canonical_path": "chat.shortcut_messages_query_send_status",
"cli_path": "chat +messages-query-send-status"
},
{
"canonical_path": "chat.shortcut_messages_read_status",
"cli_path": "chat +messages-read-status"
},
{
"canonical_path": "chat.shortcut_messages_update_card",
"cli_path": "chat +messages-update-card"
},
{
"canonical_path": "chat.shortcut_messages_list_pin",
"cli_path": "chat +messages-list-pin"
},
{
"canonical_path": "chat.shortcut_at_me",
"cli_path": "chat +at-me"
},
{
"canonical_path": "chat.shortcut_broadcast",
"cli_path": "chat +broadcast"
},
{
"canonical_path": "chat.shortcut_dm",
"cli_path": "chat +dm"
},
{
"canonical_path": "chat.shortcut_group_members",
"cli_path": "chat +group-members"
},
{
"canonical_path": "chat.shortcut_my_groups",
"cli_path": "chat +my-groups"
},
{
"canonical_path": "chat.shortcut_send_to_group",
"cli_path": "chat +send-to-group"
},
{
"canonical_path": "chat.shortcut_unread_chats",
"cli_path": "chat +unread-chats"
}
]
},
@@ -1149,6 +1589,62 @@
{
"canonical_path": "contact.self_user_profile_update",
"cli_path": "contact user update-self"
},
{
"canonical_path": "contact.shortcut_list_followings",
"cli_path": "contact +list-followings"
},
{
"canonical_path": "contact.shortcut_search_user",
"cli_path": "contact +search-user"
},
{
"canonical_path": "contact.shortcut_search_mobile",
"cli_path": "contact +search-mobile"
},
{
"canonical_path": "contact.shortcut_list_roles",
"cli_path": "contact +list-roles"
},
{
"canonical_path": "contact.shortcut_list_role_members",
"cli_path": "contact +list-role-members"
},
{
"canonical_path": "contact.shortcut_list_sub_depts",
"cli_path": "contact +list-sub-depts"
},
{
"canonical_path": "contact.shortcut_list_dept_members",
"cli_path": "contact +list-dept-members"
},
{
"canonical_path": "contact.shortcut_by_mobile",
"cli_path": "contact +by-mobile"
},
{
"canonical_path": "contact.shortcut_dept_members",
"cli_path": "contact +dept-members"
},
{
"canonical_path": "contact.shortcut_lookup",
"cli_path": "contact +lookup"
},
{
"canonical_path": "contact.shortcut_org",
"cli_path": "contact +org"
},
{
"canonical_path": "contact.shortcut_resolve_dept",
"cli_path": "contact +resolve-dept"
},
{
"canonical_path": "contact.shortcut_team",
"cli_path": "contact +team"
},
{
"canonical_path": "contact.shortcut_me",
"cli_path": "contact +me"
}
]
},
@@ -1293,6 +1789,87 @@
}
]
},
{
"id": "devapp",
"tools": [
{
"canonical_path": "devapp.shortcut_list",
"cli_path": "devapp +list"
},
{
"canonical_path": "devapp.shortcut_get",
"cli_path": "devapp +get"
},
{
"canonical_path": "devapp.shortcut_create",
"cli_path": "devapp +create"
},
{
"canonical_path": "devapp.shortcut_update",
"cli_path": "devapp +update"
},
{
"canonical_path": "devapp.shortcut_delete",
"cli_path": "devapp +delete"
},
{
"canonical_path": "devapp.shortcut_enable",
"cli_path": "devapp +enable"
},
{
"canonical_path": "devapp.shortcut_disable",
"cli_path": "devapp +disable"
},
{
"canonical_path": "devapp.shortcut_webapp_get",
"cli_path": "devapp +webapp-get"
},
{
"canonical_path": "devapp.shortcut_webapp_config",
"cli_path": "devapp +webapp-config"
},
{
"canonical_path": "devapp.shortcut_permission_list",
"cli_path": "devapp +permission-list"
},
{
"canonical_path": "devapp.shortcut_member_list",
"cli_path": "devapp +member-list"
},
{
"canonical_path": "devapp.shortcut_member_add",
"cli_path": "devapp +member-add"
},
{
"canonical_path": "devapp.shortcut_member_remove",
"cli_path": "devapp +member-remove"
},
{
"canonical_path": "devapp.shortcut_robot_get",
"cli_path": "devapp +robot-get"
},
{
"canonical_path": "devapp.shortcut_event_list",
"cli_path": "devapp +event-list"
},
{
"canonical_path": "devapp.shortcut_version_list",
"cli_path": "devapp +version-list"
},
{
"canonical_path": "devapp.shortcut_version_get",
"cli_path": "devapp +version-get"
},
{
"canonical_path": "devapp.shortcut_version_check_approval",
"cli_path": "devapp +version-check-approval"
},
{
"canonical_path": "devapp.shortcut_version_status",
"cli_path": "devapp +version-status"
}
]
},
{
"id": "devdoc",
"tools": [
@@ -1312,6 +1889,22 @@
{
"canonical_path": "ding.send_ding_message",
"cli_path": "ding message send"
},
{
"canonical_path": "ding.shortcut_list",
"cli_path": "ding +list"
},
{
"canonical_path": "ding.shortcut_receiver_status",
"cli_path": "ding +receiver-status"
},
{
"canonical_path": "ding.shortcut_send_personal",
"cli_path": "ding +send-personal"
},
{
"canonical_path": "ding.shortcut_recall_personal",
"cli_path": "ding +recall-personal"
}
]
},
@@ -1469,6 +2062,74 @@
{
"canonical_path": "doc.version_save",
"cli_path": "doc version save"
},
{
"canonical_path": "doc.shortcut_search",
"cli_path": "doc +search"
},
{
"canonical_path": "doc.shortcut_list",
"cli_path": "doc +list"
},
{
"canonical_path": "doc.shortcut_copy",
"cli_path": "doc +copy"
},
{
"canonical_path": "doc.shortcut_move",
"cli_path": "doc +move"
},
{
"canonical_path": "doc.shortcut_comment_list",
"cli_path": "doc +comment-list"
},
{
"canonical_path": "doc.shortcut_comment_create",
"cli_path": "doc +comment-create"
},
{
"canonical_path": "doc.shortcut_comment_reply",
"cli_path": "doc +comment-reply"
},
{
"canonical_path": "doc.shortcut_export_submit",
"cli_path": "doc +export-submit"
},
{
"canonical_path": "doc.shortcut_export_get",
"cli_path": "doc +export-get"
},
{
"canonical_path": "doc.shortcut_version_save",
"cli_path": "doc +version-save"
},
{
"canonical_path": "doc.shortcut_version_list",
"cli_path": "doc +version-list"
},
{
"canonical_path": "doc.shortcut_version_revert",
"cli_path": "doc +version-revert"
},
{
"canonical_path": "doc.shortcut_template_list",
"cli_path": "doc +template-list"
},
{
"canonical_path": "doc.shortcut_template_search",
"cli_path": "doc +template-search"
},
{
"canonical_path": "doc.shortcut_doc_append",
"cli_path": "doc +doc-append"
},
{
"canonical_path": "doc.shortcut_find_doc",
"cli_path": "doc +find-doc"
},
{
"canonical_path": "doc.shortcut_share_doc",
"cli_path": "doc +share-doc"
}
]
},
@@ -1574,6 +2235,34 @@
{
"canonical_path": "drive.get_upload_info",
"cli_path": "drive upload-info"
},
{
"canonical_path": "drive.shortcut_info",
"cli_path": "drive +info"
},
{
"canonical_path": "drive.shortcut_search",
"cli_path": "drive +search"
},
{
"canonical_path": "drive.shortcut_search_docs",
"cli_path": "drive +search-docs"
},
{
"canonical_path": "drive.shortcut_copy",
"cli_path": "drive +copy"
},
{
"canonical_path": "drive.shortcut_move",
"cli_path": "drive +move"
},
{
"canonical_path": "drive.shortcut_recent",
"cli_path": "drive +recent"
},
{
"canonical_path": "drive.shortcut_find_file",
"cli_path": "drive +find-file"
}
]
},
@@ -1786,6 +2475,46 @@
{
"canonical_path": "mail.search_mail_users",
"cli_path": "mail user search"
},
{
"canonical_path": "mail.shortcut_thread_list",
"cli_path": "mail +thread-list"
},
{
"canonical_path": "mail.shortcut_folder_list",
"cli_path": "mail +folder-list"
},
{
"canonical_path": "mail.shortcut_tag_list",
"cli_path": "mail +tag-list"
},
{
"canonical_path": "mail.shortcut_user_search",
"cli_path": "mail +user-search"
},
{
"canonical_path": "mail.shortcut_template_list",
"cli_path": "mail +template-list"
},
{
"canonical_path": "mail.shortcut_contact_list",
"cli_path": "mail +contact-list"
},
{
"canonical_path": "mail.shortcut_find_mail_user",
"cli_path": "mail +find-mail-user"
},
{
"canonical_path": "mail.shortcut_recent_mail",
"cli_path": "mail +recent-mail"
},
{
"canonical_path": "mail.shortcut_search_mail",
"cli_path": "mail +search-mail"
},
{
"canonical_path": "mail.shortcut_unread_mail",
"cli_path": "mail +unread-mail"
}
]
},
@@ -1945,6 +2674,30 @@
{
"canonical_path": "minutes.create_upload_session",
"cli_path": "minutes upload create"
},
{
"canonical_path": "minutes.shortcut_list_mine",
"cli_path": "minutes +list-mine"
},
{
"canonical_path": "minutes.shortcut_list_shared",
"cli_path": "minutes +list-shared"
},
{
"canonical_path": "minutes.shortcut_list_all",
"cli_path": "minutes +list-all"
},
{
"canonical_path": "minutes.shortcut_record_start",
"cli_path": "minutes +record-start"
},
{
"canonical_path": "minutes.shortcut_detail",
"cli_path": "minutes +detail"
},
{
"canonical_path": "minutes.shortcut_replace_batch",
"cli_path": "minutes +replace-batch"
}
]
},
@@ -2010,6 +2763,34 @@
{
"canonical_path": "oa.list_pending_tasks",
"cli_path": "oa approval tasks"
},
{
"canonical_path": "oa.shortcut_list_pending",
"cli_path": "oa +list-pending"
},
{
"canonical_path": "oa.shortcut_list_forms",
"cli_path": "oa +list-forms"
},
{
"canonical_path": "oa.shortcut_search_forms",
"cli_path": "oa +search-forms"
},
{
"canonical_path": "oa.shortcut_list_executed",
"cli_path": "oa +list-executed"
},
{
"canonical_path": "oa.shortcut_list_submitted",
"cli_path": "oa +list-submitted"
},
{
"canonical_path": "oa.shortcut_list_cc",
"cli_path": "oa +list-cc"
},
{
"canonical_path": "oa.shortcut_my_initiated",
"cli_path": "oa +my-initiated"
}
]
},
@@ -2075,6 +2856,14 @@
{
"canonical_path": "report.get_available_report_templates",
"cli_path": "report template list"
},
{
"canonical_path": "report.shortcut_inbox_list",
"cli_path": "report +inbox-list"
},
{
"canonical_path": "report.shortcut_outbox_list",
"cli_path": "report +outbox-list"
}
]
},
@@ -2402,6 +3191,14 @@
{
"canonical_path": "sheet.write_image",
"cli_path": "sheet write-image"
},
{
"canonical_path": "sheet.shortcut_list_sheets",
"cli_path": "sheet +list-sheets"
},
{
"canonical_path": "sheet.shortcut_read",
"cli_path": "sheet +read"
}
]
},
@@ -2499,6 +3296,50 @@
{
"canonical_path": "todo.update_todo_tag",
"cli_path": "todo tag update"
},
{
"canonical_path": "todo.shortcut_assign",
"cli_path": "todo +assign"
},
{
"canonical_path": "todo.shortcut_assign_multi",
"cli_path": "todo +assign-multi"
},
{
"canonical_path": "todo.shortcut_created_todos",
"cli_path": "todo +created-todos"
},
{
"canonical_path": "todo.shortcut_overdue",
"cli_path": "todo +overdue"
},
{
"canonical_path": "todo.shortcut_remind",
"cli_path": "todo +remind"
},
{
"canonical_path": "todo.shortcut_todo_done",
"cli_path": "todo +todo-done"
},
{
"canonical_path": "todo.shortcut_get_my_tasks",
"cli_path": "todo +get-my-tasks"
},
{
"canonical_path": "todo.shortcut_list_sub",
"cli_path": "todo +list-sub"
},
{
"canonical_path": "todo.shortcut_get",
"cli_path": "todo +get"
},
{
"canonical_path": "todo.shortcut_list_attachment",
"cli_path": "todo +list-attachment"
},
{
"canonical_path": "todo.shortcut_list_comment",
"cli_path": "todo +list-comment"
}
]
},
@@ -2564,6 +3405,10 @@
{
"canonical_path": "wiki.search_wikiSpaces",
"cli_path": "wiki space search"
},
{
"canonical_path": "wiki.shortcut_space_search",
"cli_path": "wiki +space-search"
}
]
}
@@ -36,8 +36,8 @@
},
"cliPath": {
"type": "string",
"pattern": "^(?!dws(?:\\s|$))(?!.*(?:^|\\s)--)(?!.*[*?\\[\\]])[A-Za-z0-9][A-Za-z0-9._:-]*(?: [A-Za-z0-9][A-Za-z0-9._:-]*)*$",
"description": "Exact Cobra command path without the leading 'dws', flags, wildcard characters, or surrounding/repeated whitespace."
"pattern": "^(?!dws(?:\\s|$))(?!.*(?:^|\\s)--)(?!.*[*?\\[\\]])(?:[A-Za-z0-9][A-Za-z0-9._:-]*|\\+[A-Za-z0-9][A-Za-z0-9._:-]*)(?: (?:[A-Za-z0-9][A-Za-z0-9._:-]*|\\+[A-Za-z0-9][A-Za-z0-9._:-]*))*$",
"description": "Exact Cobra command path without the leading 'dws', flags, wildcard characters, or surrounding/repeated whitespace. A token may use the reviewed '+' shortcut prefix."
},
"product": {
"type": "object",
@@ -94,6 +94,14 @@ func TestDecodeCommandRegistryEnforcesReviewedSourceConstraints(t *testing.T) {
if got := registry.ByCanonical["sample.run"].Visibility; got != SchemaVisibilityPublic {
t.Fatalf("default visibility = %q, want public", got)
}
shortcut, err := decodeCommandRegistry([]byte(wrap(`[{"id":"sample","tools":[{"canonical_path":"sample.shortcut_run","cli_path":"sample +run"}]}]`)))
if err != nil {
t.Fatalf("decode shortcut registry path: %v", err)
}
if got := shortcut.ByCanonical["sample.shortcut_run"].PrimaryCLIPath; got != "sample +run" {
t.Fatalf("shortcut primary path = %q, want sample +run", got)
}
}
func TestCommandRegistryHashCoversEveryStableCommandField(t *testing.T) {
+62
View File
@@ -16,6 +16,7 @@
"chat": "metadata/chat.json",
"contact": "metadata/contact.json",
"dev": "metadata/dev.json",
"devapp": "metadata/devapp.json",
"devdoc": "metadata/devdoc.json",
"ding": "metadata/ding.json",
"doc": "metadata/doc.json",
@@ -43,6 +44,7 @@
"chat": "selection/chat.json",
"contact": "selection/contact.json",
"dev": "selection/dev.json",
"devapp": "selection/devapp.json",
"devdoc": "selection/devdoc.json",
"ding": "selection/ding.json",
"doc": "selection/doc.json",
@@ -656,6 +658,16 @@
"target": "event consume",
"reason": "已审查的带 event_key 参数的 Skill 引用,固定映射到当前公开 event consume leaf"
},
"event consume user_im_message_receive_o2o_all -f ndjson": {
"status": "alias",
"target": "event consume",
"reason": "已审查的全量单聊事件 Skill 引用,固定映射到当前公开 event consume leaf"
},
"event consume user_im_message_receive_group_all -f ndjson": {
"status": "alias",
"target": "event consume",
"reason": "已审查的全量群消息事件 Skill 引用,固定映射到当前公开 event consume leaf"
},
"event consume user_im_message_read_o2o": {
"status": "alias",
"target": "event consume",
@@ -686,6 +698,26 @@
"target": "event consume",
"reason": "已审查的带 event_key 参数的 Skill 引用,固定映射到当前公开 event consume leaf"
},
"event consume user_im_group_updated": {
"status": "alias",
"target": "event consume",
"reason": "已审查的群标题变更事件 Skill 引用,固定映射到当前公开 event consume leaf"
},
"event consume user_im_group_member_added": {
"status": "alias",
"target": "event consume",
"reason": "已审查的群成员加入事件 Skill 引用,固定映射到当前公开 event consume leaf"
},
"event consume user_im_group_member_exited": {
"status": "alias",
"target": "event consume",
"reason": "已审查的群成员退出事件 Skill 引用,固定映射到当前公开 event consume leaf"
},
"event consume user_im_group_disbanded": {
"status": "alias",
"target": "event consume",
"reason": "已审查的群解散事件 Skill 引用,固定映射到当前公开 event consume leaf"
},
"event schema user_im_message_receive_at": {
"status": "alias",
"target": "event schema",
@@ -706,6 +738,16 @@
"target": "event schema",
"reason": "已审查的带 event_key 参数的 Skill 引用,固定映射到当前公开 event schema leaf"
},
"event schema user_im_message_receive_o2o_all": {
"status": "alias",
"target": "event schema",
"reason": "已审查的带 event_key 参数的 Skill 引用,固定映射到当前公开 event schema leaf"
},
"event schema user_im_message_receive_group_all": {
"status": "alias",
"target": "event schema",
"reason": "已审查的带 event_key 参数的 Skill 引用,固定映射到当前公开 event schema leaf"
},
"event schema user_im_message_read_o2o": {
"status": "alias",
"target": "event schema",
@@ -736,6 +778,26 @@
"target": "event schema",
"reason": "已审查的带 event_key 参数的 Skill 引用,固定映射到当前公开 event schema leaf"
},
"event schema user_im_group_updated": {
"status": "alias",
"target": "event schema",
"reason": "已审查的带 event_key 参数的 Skill 引用,固定映射到当前公开 event schema leaf"
},
"event schema user_im_group_member_added": {
"status": "alias",
"target": "event schema",
"reason": "已审查的带 event_key 参数的 Skill 引用,固定映射到当前公开 event schema leaf"
},
"event schema user_im_group_member_exited": {
"status": "alias",
"target": "event schema",
"reason": "已审查的带 event_key 参数的 Skill 引用,固定映射到当前公开 event schema leaf"
},
"event schema user_im_group_disbanded": {
"status": "alias",
"target": "event schema",
"reason": "已审查的带 event_key 参数的 Skill 引用,固定映射到当前公开 event schema leaf"
},
"mail aisearch person": {
"status": "alias",
"target": "aisearch person",
@@ -1056,6 +1056,383 @@
"review_reason": "The command maps --base-id, --workflow-id and a decoded complete workflow-dsl/v1 --dsl object to aitable/update_workflow. This is full replacement rather than a partial patch; locale remains optional.",
"cli_path": "aitable workflow update",
"runtime_gate": "none"
},
"aitable.shortcut_base_list": {
"effect": "read",
"risk": "low",
"confirmation": "not_required",
"idempotency": "idempotent",
"interface_mode": "composite",
"availability": "available",
"interface_reason": "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
"reviewed": true,
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity.",
"cli_path": "aitable +base-list",
"runtime_gate": "none"
},
"aitable.shortcut_base_search": {
"effect": "read",
"risk": "low",
"confirmation": "not_required",
"idempotency": "idempotent",
"interface_mode": "composite",
"availability": "available",
"interface_reason": "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
"reviewed": true,
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity.",
"cli_path": "aitable +base-search",
"runtime_gate": "none"
},
"aitable.shortcut_base_get": {
"effect": "read",
"risk": "low",
"confirmation": "not_required",
"idempotency": "idempotent",
"interface_mode": "composite",
"availability": "available",
"interface_reason": "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
"reviewed": true,
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity.",
"cli_path": "aitable +base-get",
"runtime_gate": "none"
},
"aitable.shortcut_table_get": {
"effect": "read",
"risk": "low",
"confirmation": "not_required",
"idempotency": "idempotent",
"interface_mode": "composite",
"availability": "available",
"interface_reason": "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
"reviewed": true,
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity.",
"cli_path": "aitable +table-get",
"runtime_gate": "none"
},
"aitable.shortcut_field_get": {
"effect": "read",
"risk": "low",
"confirmation": "not_required",
"idempotency": "idempotent",
"interface_mode": "composite",
"availability": "available",
"interface_reason": "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
"reviewed": true,
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity.",
"cli_path": "aitable +field-get",
"runtime_gate": "none"
},
"aitable.shortcut_record_query": {
"effect": "read",
"risk": "low",
"confirmation": "not_required",
"idempotency": "idempotent",
"interface_mode": "composite",
"availability": "available",
"interface_reason": "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
"reviewed": true,
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity.",
"cli_path": "aitable +record-query",
"runtime_gate": "none"
},
"aitable.shortcut_record_query_empty": {
"effect": "read",
"risk": "low",
"confirmation": "not_required",
"idempotency": "idempotent",
"interface_mode": "composite",
"availability": "available",
"interface_reason": "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
"reviewed": true,
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity.",
"cli_path": "aitable +record-query-empty",
"runtime_gate": "none"
},
"aitable.shortcut_record_history_list": {
"effect": "read",
"risk": "low",
"confirmation": "not_required",
"idempotency": "idempotent",
"interface_mode": "composite",
"availability": "available",
"interface_reason": "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
"reviewed": true,
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity.",
"cli_path": "aitable +record-history-list",
"runtime_gate": "none"
},
"aitable.shortcut_record_share_url": {
"effect": "read",
"risk": "low",
"confirmation": "not_required",
"idempotency": "idempotent",
"interface_mode": "composite",
"availability": "available",
"interface_reason": "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
"reviewed": true,
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity.",
"cli_path": "aitable +record-share-url",
"runtime_gate": "none"
},
"aitable.shortcut_template_search": {
"effect": "read",
"risk": "low",
"confirmation": "not_required",
"idempotency": "idempotent",
"interface_mode": "composite",
"availability": "available",
"interface_reason": "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
"reviewed": true,
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity.",
"cli_path": "aitable +template-search",
"runtime_gate": "none"
},
"aitable.shortcut_view_get": {
"effect": "read",
"risk": "low",
"confirmation": "not_required",
"idempotency": "idempotent",
"interface_mode": "composite",
"availability": "available",
"interface_reason": "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
"reviewed": true,
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity.",
"cli_path": "aitable +view-get",
"runtime_gate": "none"
},
"aitable.shortcut_view_get_lock": {
"effect": "read",
"risk": "low",
"confirmation": "not_required",
"idempotency": "idempotent",
"interface_mode": "composite",
"availability": "available",
"interface_reason": "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
"reviewed": true,
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity.",
"cli_path": "aitable +view-get-lock",
"runtime_gate": "none"
},
"aitable.shortcut_view_get_frozen_cols": {
"effect": "read",
"risk": "low",
"confirmation": "not_required",
"idempotency": "idempotent",
"interface_mode": "composite",
"availability": "available",
"interface_reason": "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
"reviewed": true,
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity.",
"cli_path": "aitable +view-get-frozen-cols",
"runtime_gate": "none"
},
"aitable.shortcut_view_get_row_height": {
"effect": "read",
"risk": "low",
"confirmation": "not_required",
"idempotency": "idempotent",
"interface_mode": "composite",
"availability": "available",
"interface_reason": "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
"reviewed": true,
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity.",
"cli_path": "aitable +view-get-row-height",
"runtime_gate": "none"
},
"aitable.shortcut_form_list": {
"effect": "read",
"risk": "low",
"confirmation": "not_required",
"idempotency": "idempotent",
"interface_mode": "composite",
"availability": "available",
"interface_reason": "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
"reviewed": true,
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity.",
"cli_path": "aitable +form-list",
"runtime_gate": "none"
},
"aitable.shortcut_form_field_list": {
"effect": "read",
"risk": "low",
"confirmation": "not_required",
"idempotency": "idempotent",
"interface_mode": "composite",
"availability": "available",
"interface_reason": "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
"reviewed": true,
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity.",
"cli_path": "aitable +form-field-list",
"runtime_gate": "none"
},
"aitable.shortcut_form_share_get": {
"effect": "read",
"risk": "low",
"confirmation": "not_required",
"idempotency": "idempotent",
"interface_mode": "composite",
"availability": "available",
"interface_reason": "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
"reviewed": true,
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity.",
"cli_path": "aitable +form-share-get",
"runtime_gate": "none"
},
"aitable.shortcut_dashboard_config_example": {
"effect": "read",
"risk": "low",
"confirmation": "not_required",
"idempotency": "idempotent",
"interface_mode": "composite",
"availability": "available",
"interface_reason": "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
"reviewed": true,
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity.",
"cli_path": "aitable +dashboard-config-example",
"runtime_gate": "none"
},
"aitable.shortcut_dashboard_get": {
"effect": "read",
"risk": "low",
"confirmation": "not_required",
"idempotency": "idempotent",
"interface_mode": "composite",
"availability": "available",
"interface_reason": "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
"reviewed": true,
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity.",
"cli_path": "aitable +dashboard-get",
"runtime_gate": "none"
},
"aitable.shortcut_chart_widgets_example": {
"effect": "read",
"risk": "low",
"confirmation": "not_required",
"idempotency": "idempotent",
"interface_mode": "composite",
"availability": "available",
"interface_reason": "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
"reviewed": true,
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity.",
"cli_path": "aitable +chart-widgets-example",
"runtime_gate": "none"
},
"aitable.shortcut_chart_get": {
"effect": "read",
"risk": "low",
"confirmation": "not_required",
"idempotency": "idempotent",
"interface_mode": "composite",
"availability": "available",
"interface_reason": "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
"reviewed": true,
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity.",
"cli_path": "aitable +chart-get",
"runtime_gate": "none"
},
"aitable.shortcut_role_list": {
"effect": "read",
"risk": "low",
"confirmation": "not_required",
"idempotency": "idempotent",
"interface_mode": "composite",
"availability": "available",
"interface_reason": "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
"reviewed": true,
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity.",
"cli_path": "aitable +role-list",
"runtime_gate": "none"
},
"aitable.shortcut_section_list_empty": {
"effect": "read",
"risk": "low",
"confirmation": "not_required",
"idempotency": "idempotent",
"interface_mode": "composite",
"availability": "available",
"interface_reason": "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
"reviewed": true,
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity.",
"cli_path": "aitable +section-list-empty",
"runtime_gate": "none"
},
"aitable.shortcut_section_list_nodes": {
"effect": "read",
"risk": "low",
"confirmation": "not_required",
"idempotency": "idempotent",
"interface_mode": "composite",
"availability": "available",
"interface_reason": "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
"reviewed": true,
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity.",
"cli_path": "aitable +section-list-nodes",
"runtime_gate": "none"
},
"aitable.shortcut_find_record": {
"effect": "read",
"risk": "low",
"confirmation": "not_required",
"idempotency": "idempotent",
"interface_mode": "composite",
"availability": "available",
"interface_reason": "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
"reviewed": true,
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity.",
"cli_path": "aitable +find-record",
"runtime_gate": "none"
},
"aitable.shortcut_list_tables": {
"effect": "read",
"risk": "low",
"confirmation": "not_required",
"idempotency": "idempotent",
"interface_mode": "composite",
"availability": "available",
"interface_reason": "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
"reviewed": true,
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity.",
"cli_path": "aitable +list-tables",
"runtime_gate": "none"
},
"aitable.shortcut_record_share_links": {
"effect": "read",
"risk": "low",
"confirmation": "not_required",
"idempotency": "idempotent",
"interface_mode": "composite",
"availability": "available",
"interface_reason": "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
"reviewed": true,
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity.",
"cli_path": "aitable +record-share-links",
"runtime_gate": "none"
},
"aitable.shortcut_resolve_base": {
"effect": "read",
"risk": "low",
"confirmation": "not_required",
"idempotency": "idempotent",
"interface_mode": "composite",
"availability": "available",
"interface_reason": "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
"reviewed": true,
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity.",
"cli_path": "aitable +resolve-base",
"runtime_gate": "none"
},
"aitable.shortcut_resolve_table": {
"effect": "read",
"risk": "low",
"confirmation": "not_required",
"idempotency": "idempotent",
"interface_mode": "composite",
"availability": "available",
"interface_reason": "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
"reviewed": true,
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity.",
"cli_path": "aitable +resolve-table",
"runtime_gate": "none"
}
}
}
@@ -428,6 +428,253 @@
"interface_reason": "Reviewed unpinned remote adapter: this executable CLI wrapper calls a remote helper that is absent from the pinned MCP metadata snapshot; no single pinned semantically equivalent interface_ref can represent the command.",
"reviewed": true,
"runtime_gate": "typed_yes"
},
"attendance.shortcut_check_result": {
"effect": "read",
"risk": "low",
"confirmation": "not_required",
"idempotency": "idempotent",
"interface_mode": "composite",
"availability": "available",
"interface_reason": "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
"reviewed": true,
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity.",
"cli_path": "attendance +check-result",
"runtime_gate": "none"
},
"attendance.shortcut_check_record": {
"effect": "read",
"risk": "low",
"confirmation": "not_required",
"idempotency": "idempotent",
"interface_mode": "composite",
"availability": "available",
"interface_reason": "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
"reviewed": true,
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity.",
"cli_path": "attendance +check-record",
"runtime_gate": "none"
},
"attendance.shortcut_list_approve": {
"effect": "read",
"risk": "low",
"confirmation": "not_required",
"idempotency": "idempotent",
"interface_mode": "composite",
"availability": "available",
"interface_reason": "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
"reviewed": true,
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity.",
"cli_path": "attendance +list-approve",
"runtime_gate": "none"
},
"attendance.shortcut_get_approve_template": {
"effect": "read",
"risk": "low",
"confirmation": "not_required",
"idempotency": "idempotent",
"interface_mode": "composite",
"availability": "available",
"interface_reason": "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
"reviewed": true,
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity.",
"cli_path": "attendance +get-approve-template",
"runtime_gate": "none"
},
"attendance.shortcut_get_schedule": {
"effect": "read",
"risk": "low",
"confirmation": "not_required",
"idempotency": "idempotent",
"interface_mode": "composite",
"availability": "available",
"interface_reason": "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
"reviewed": true,
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity.",
"cli_path": "attendance +get-schedule",
"runtime_gate": "none"
},
"attendance.shortcut_search_class": {
"effect": "read",
"risk": "low",
"confirmation": "not_required",
"idempotency": "idempotent",
"interface_mode": "composite",
"availability": "available",
"interface_reason": "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
"reviewed": true,
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity.",
"cli_path": "attendance +search-class",
"runtime_gate": "none"
},
"attendance.shortcut_get_adjustment_rule": {
"effect": "read",
"risk": "low",
"confirmation": "not_required",
"idempotency": "idempotent",
"interface_mode": "composite",
"availability": "available",
"interface_reason": "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
"reviewed": true,
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity.",
"cli_path": "attendance +get-adjustment-rule",
"runtime_gate": "none"
},
"attendance.shortcut_search_adjustment_rule": {
"effect": "read",
"risk": "low",
"confirmation": "not_required",
"idempotency": "idempotent",
"interface_mode": "composite",
"availability": "available",
"interface_reason": "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
"reviewed": true,
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity.",
"cli_path": "attendance +search-adjustment-rule",
"runtime_gate": "none"
},
"attendance.shortcut_get_overtime_rule": {
"effect": "read",
"risk": "low",
"confirmation": "not_required",
"idempotency": "idempotent",
"interface_mode": "composite",
"availability": "available",
"interface_reason": "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
"reviewed": true,
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity.",
"cli_path": "attendance +get-overtime-rule",
"runtime_gate": "none"
},
"attendance.shortcut_search_overtime_rule": {
"effect": "read",
"risk": "low",
"confirmation": "not_required",
"idempotency": "idempotent",
"interface_mode": "composite",
"availability": "available",
"interface_reason": "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
"reviewed": true,
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity.",
"cli_path": "attendance +search-overtime-rule",
"runtime_gate": "none"
},
"attendance.shortcut_search_group": {
"effect": "read",
"risk": "low",
"confirmation": "not_required",
"idempotency": "idempotent",
"interface_mode": "composite",
"availability": "available",
"interface_reason": "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
"reviewed": true,
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity.",
"cli_path": "attendance +search-group",
"runtime_gate": "none"
},
"attendance.shortcut_get_summary": {
"effect": "read",
"risk": "low",
"confirmation": "not_required",
"idempotency": "idempotent",
"interface_mode": "composite",
"availability": "available",
"interface_reason": "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
"reviewed": true,
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity.",
"cli_path": "attendance +get-summary",
"runtime_gate": "none"
},
"attendance.shortcut_get_self_setting": {
"effect": "read",
"risk": "low",
"confirmation": "not_required",
"idempotency": "idempotent",
"interface_mode": "composite",
"availability": "available",
"interface_reason": "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
"reviewed": true,
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity.",
"cli_path": "attendance +get-self-setting",
"runtime_gate": "none"
},
"attendance.shortcut_query_report_data": {
"effect": "read",
"risk": "low",
"confirmation": "not_required",
"idempotency": "idempotent",
"interface_mode": "composite",
"availability": "available",
"interface_reason": "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
"reviewed": true,
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity.",
"cli_path": "attendance +query-report-data",
"runtime_gate": "none"
},
"attendance.shortcut_list_leave_types": {
"effect": "read",
"risk": "low",
"confirmation": "not_required",
"idempotency": "idempotent",
"interface_mode": "composite",
"availability": "available",
"interface_reason": "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
"reviewed": true,
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity.",
"cli_path": "attendance +list-leave-types",
"runtime_gate": "none"
},
"attendance.shortcut_get_leave_records": {
"effect": "read",
"risk": "low",
"confirmation": "not_required",
"idempotency": "idempotent",
"interface_mode": "composite",
"availability": "available",
"interface_reason": "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
"reviewed": true,
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity.",
"cli_path": "attendance +get-leave-records",
"runtime_gate": "none"
},
"attendance.shortcut_get_checkin_record": {
"effect": "read",
"risk": "low",
"confirmation": "not_required",
"idempotency": "idempotent",
"interface_mode": "composite",
"availability": "available",
"interface_reason": "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
"reviewed": true,
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity.",
"cli_path": "attendance +get-checkin-record",
"runtime_gate": "none"
},
"attendance.shortcut_my_attendance": {
"effect": "read",
"risk": "low",
"confirmation": "not_required",
"idempotency": "idempotent",
"interface_mode": "composite",
"availability": "available",
"interface_reason": "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
"reviewed": true,
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity.",
"cli_path": "attendance +my-attendance",
"runtime_gate": "none"
},
"attendance.shortcut_this_month": {
"effect": "read",
"risk": "low",
"confirmation": "not_required",
"idempotency": "idempotent",
"interface_mode": "composite",
"availability": "available",
"interface_reason": "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
"reviewed": true,
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity.",
"cli_path": "attendance +this-month",
"runtime_gate": "none"
}
}
}
@@ -119,6 +119,266 @@
"interface_mode": "mcp",
"availability": "available",
"reviewed": true
},
"calendar.shortcut_agenda": {
"effect": "read",
"risk": "low",
"confirmation": "not_required",
"idempotency": "idempotent",
"interface_mode": "composite",
"availability": "available",
"interface_reason": "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
"reviewed": true,
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity.",
"cli_path": "calendar +agenda",
"runtime_gate": "none"
},
"calendar.shortcut_attendee_list": {
"effect": "read",
"risk": "low",
"confirmation": "not_required",
"idempotency": "idempotent",
"interface_mode": "composite",
"availability": "available",
"interface_reason": "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
"reviewed": true,
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity.",
"cli_path": "calendar +attendee-list",
"runtime_gate": "none"
},
"calendar.shortcut_room_search": {
"effect": "read",
"risk": "low",
"confirmation": "not_required",
"idempotency": "idempotent",
"interface_mode": "composite",
"availability": "available",
"interface_reason": "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
"reviewed": true,
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity.",
"cli_path": "calendar +room-search",
"runtime_gate": "none"
},
"calendar.shortcut_room_groups": {
"effect": "read",
"risk": "low",
"confirmation": "not_required",
"idempotency": "idempotent",
"interface_mode": "composite",
"availability": "available",
"interface_reason": "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
"reviewed": true,
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity.",
"cli_path": "calendar +room-groups",
"runtime_gate": "none"
},
"calendar.shortcut_freebusy": {
"effect": "read",
"risk": "low",
"confirmation": "not_required",
"idempotency": "idempotent",
"interface_mode": "composite",
"availability": "available",
"interface_reason": "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
"reviewed": true,
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity.",
"cli_path": "calendar +freebusy",
"runtime_gate": "none"
},
"calendar.shortcut_book_list": {
"effect": "read",
"risk": "low",
"confirmation": "not_required",
"idempotency": "idempotent",
"interface_mode": "composite",
"availability": "available",
"interface_reason": "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
"reviewed": true,
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity.",
"cli_path": "calendar +book-list",
"runtime_gate": "none"
},
"calendar.shortcut_book_search": {
"effect": "read",
"risk": "low",
"confirmation": "not_required",
"idempotency": "idempotent",
"interface_mode": "composite",
"availability": "available",
"interface_reason": "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
"reviewed": true,
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity.",
"cli_path": "calendar +book-search",
"runtime_gate": "none"
},
"calendar.shortcut_book": {
"effect": "write",
"risk": "medium",
"confirmation": "user_required",
"idempotency": "unknown",
"interface_mode": "composite",
"availability": "available",
"interface_reason": "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
"reviewed": true,
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity.",
"cli_path": "calendar +book",
"runtime_gate": "typed_yes"
},
"calendar.shortcut_cancel_event": {
"effect": "destructive",
"risk": "high",
"confirmation": "user_required",
"idempotency": "unknown",
"interface_mode": "composite",
"availability": "available",
"interface_reason": "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
"reviewed": true,
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity.",
"cli_path": "calendar +cancel-event",
"runtime_gate": "typed_yes"
},
"calendar.shortcut_conflicts": {
"effect": "read",
"risk": "low",
"confirmation": "not_required",
"idempotency": "idempotent",
"interface_mode": "composite",
"availability": "available",
"interface_reason": "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
"reviewed": true,
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity.",
"cli_path": "calendar +conflicts",
"runtime_gate": "none"
},
"calendar.shortcut_free_slots": {
"effect": "read",
"risk": "low",
"confirmation": "not_required",
"idempotency": "idempotent",
"interface_mode": "composite",
"availability": "available",
"interface_reason": "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
"reviewed": true,
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity.",
"cli_path": "calendar +free-slots",
"runtime_gate": "none"
},
"calendar.shortcut_free": {
"effect": "read",
"risk": "low",
"confirmation": "not_required",
"idempotency": "idempotent",
"interface_mode": "composite",
"availability": "available",
"interface_reason": "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
"reviewed": true,
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity.",
"cli_path": "calendar +free",
"runtime_gate": "none"
},
"calendar.shortcut_invite": {
"effect": "write",
"risk": "medium",
"confirmation": "user_required",
"idempotency": "unknown",
"interface_mode": "composite",
"availability": "available",
"interface_reason": "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
"reviewed": true,
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity.",
"cli_path": "calendar +invite",
"runtime_gate": "typed_yes"
},
"calendar.shortcut_my_free": {
"effect": "read",
"risk": "low",
"confirmation": "not_required",
"idempotency": "idempotent",
"interface_mode": "composite",
"availability": "available",
"interface_reason": "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
"reviewed": true,
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity.",
"cli_path": "calendar +my-free",
"runtime_gate": "none"
},
"calendar.shortcut_next_event": {
"effect": "read",
"risk": "low",
"confirmation": "not_required",
"idempotency": "idempotent",
"interface_mode": "composite",
"availability": "available",
"interface_reason": "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
"reviewed": true,
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity.",
"cli_path": "calendar +next-event",
"runtime_gate": "none"
},
"calendar.shortcut_reschedule": {
"effect": "write",
"risk": "medium",
"confirmation": "user_required",
"idempotency": "unknown",
"interface_mode": "composite",
"availability": "available",
"interface_reason": "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
"reviewed": true,
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity.",
"cli_path": "calendar +reschedule",
"runtime_gate": "typed_yes"
},
"calendar.shortcut_suggest_time": {
"effect": "read",
"risk": "low",
"confirmation": "not_required",
"idempotency": "idempotent",
"interface_mode": "composite",
"availability": "available",
"interface_reason": "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
"reviewed": true,
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity.",
"cli_path": "calendar +suggest-time",
"runtime_gate": "none"
},
"calendar.shortcut_today": {
"effect": "read",
"risk": "low",
"confirmation": "not_required",
"idempotency": "idempotent",
"interface_mode": "composite",
"availability": "available",
"interface_reason": "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
"reviewed": true,
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity.",
"cli_path": "calendar +today",
"runtime_gate": "none"
},
"calendar.shortcut_tomorrow": {
"effect": "read",
"risk": "low",
"confirmation": "not_required",
"idempotency": "idempotent",
"interface_mode": "composite",
"availability": "available",
"interface_reason": "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
"reviewed": true,
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity.",
"cli_path": "calendar +tomorrow",
"runtime_gate": "none"
},
"calendar.shortcut_week": {
"effect": "read",
"risk": "low",
"confirmation": "not_required",
"idempotency": "idempotent",
"interface_mode": "composite",
"availability": "available",
"interface_reason": "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
"reviewed": true,
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity.",
"cli_path": "calendar +week",
"runtime_gate": "none"
}
}
}
@@ -800,6 +800,552 @@
"review_reason": "The reviewed handler validates the target group and optional string-valued extension object, permits caller-authoritative dry-run, and requires explicit --yes before the irreversible upgrade.",
"cli_path": "chat group upgrade-to-external",
"runtime_gate": "confirm_dangerous"
},
"chat.shortcut_bot_search": {
"effect": "read",
"risk": "low",
"confirmation": "not_required",
"idempotency": "idempotent",
"interface_mode": "composite",
"availability": "available",
"interface_reason": "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
"reviewed": true,
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity.",
"cli_path": "chat +bot-search",
"runtime_gate": "none"
},
"chat.shortcut_bot_find": {
"effect": "read",
"risk": "low",
"confirmation": "not_required",
"idempotency": "idempotent",
"interface_mode": "composite",
"availability": "available",
"interface_reason": "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
"reviewed": true,
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity.",
"cli_path": "chat +bot-find",
"runtime_gate": "none"
},
"chat.shortcut_conversation_info": {
"effect": "read",
"risk": "low",
"confirmation": "not_required",
"idempotency": "idempotent",
"interface_mode": "composite",
"availability": "available",
"interface_reason": "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
"reviewed": true,
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity.",
"cli_path": "chat +conversation-info",
"runtime_gate": "none"
},
"chat.shortcut_conversation_clear_all_red_point": {
"effect": "write",
"risk": "medium",
"confirmation": "user_required",
"idempotency": "unknown",
"interface_mode": "composite",
"availability": "available",
"interface_reason": "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
"reviewed": true,
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity.",
"cli_path": "chat +conversation-clear-all-red-point",
"runtime_gate": "typed_yes"
},
"chat.shortcut_conversation_list": {
"effect": "read",
"risk": "low",
"confirmation": "not_required",
"idempotency": "idempotent",
"interface_mode": "composite",
"availability": "available",
"interface_reason": "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
"reviewed": true,
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity.",
"cli_path": "chat +conversation-list",
"runtime_gate": "none"
},
"chat.shortcut_conversation_list_top": {
"effect": "read",
"risk": "low",
"confirmation": "not_required",
"idempotency": "idempotent",
"interface_mode": "composite",
"availability": "available",
"interface_reason": "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
"reviewed": true,
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity.",
"cli_path": "chat +conversation-list-top",
"runtime_gate": "none"
},
"chat.shortcut_category_list": {
"effect": "read",
"risk": "low",
"confirmation": "not_required",
"idempotency": "idempotent",
"interface_mode": "composite",
"availability": "available",
"interface_reason": "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
"reviewed": true,
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity.",
"cli_path": "chat +category-list",
"runtime_gate": "none"
},
"chat.shortcut_category_create": {
"effect": "write",
"risk": "medium",
"confirmation": "user_required",
"idempotency": "unknown",
"interface_mode": "composite",
"availability": "available",
"interface_reason": "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
"reviewed": true,
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity.",
"cli_path": "chat +category-create",
"runtime_gate": "typed_yes"
},
"chat.shortcut_category_delete": {
"effect": "destructive",
"risk": "high",
"confirmation": "user_required",
"idempotency": "unknown",
"interface_mode": "composite",
"availability": "available",
"interface_reason": "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
"reviewed": true,
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity.",
"cli_path": "chat +category-delete",
"runtime_gate": "typed_yes"
},
"chat.shortcut_category_rename": {
"effect": "write",
"risk": "medium",
"confirmation": "user_required",
"idempotency": "unknown",
"interface_mode": "composite",
"availability": "available",
"interface_reason": "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
"reviewed": true,
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity.",
"cli_path": "chat +category-rename",
"runtime_gate": "typed_yes"
},
"chat.shortcut_chat_search": {
"effect": "read",
"risk": "low",
"confirmation": "not_required",
"idempotency": "idempotent",
"interface_mode": "composite",
"availability": "available",
"interface_reason": "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
"reviewed": true,
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity.",
"cli_path": "chat +chat-search",
"runtime_gate": "none"
},
"chat.shortcut_chat_invite_url": {
"effect": "read",
"risk": "low",
"confirmation": "not_required",
"idempotency": "idempotent",
"interface_mode": "composite",
"availability": "available",
"interface_reason": "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
"reviewed": true,
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity.",
"cli_path": "chat +chat-invite-url",
"runtime_gate": "none"
},
"chat.shortcut_chat_dismiss": {
"effect": "destructive",
"risk": "high",
"confirmation": "user_required",
"idempotency": "unknown",
"interface_mode": "composite",
"availability": "available",
"interface_reason": "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
"reviewed": true,
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity.",
"cli_path": "chat +chat-dismiss",
"runtime_gate": "typed_yes"
},
"chat.shortcut_chat_set_history": {
"effect": "write",
"risk": "medium",
"confirmation": "user_required",
"idempotency": "unknown",
"interface_mode": "composite",
"availability": "available",
"interface_reason": "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
"reviewed": true,
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity.",
"cli_path": "chat +chat-set-history",
"runtime_gate": "typed_yes"
},
"chat.shortcut_chat_update_nick": {
"effect": "write",
"risk": "medium",
"confirmation": "user_required",
"idempotency": "unknown",
"interface_mode": "composite",
"availability": "available",
"interface_reason": "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
"reviewed": true,
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity.",
"cli_path": "chat +chat-update-nick",
"runtime_gate": "typed_yes"
},
"chat.shortcut_chat_update_alias": {
"effect": "write",
"risk": "medium",
"confirmation": "user_required",
"idempotency": "unknown",
"interface_mode": "composite",
"availability": "available",
"interface_reason": "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
"reviewed": true,
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity.",
"cli_path": "chat +chat-update-alias",
"runtime_gate": "typed_yes"
},
"chat.shortcut_chat_list_mine": {
"effect": "read",
"risk": "low",
"confirmation": "not_required",
"idempotency": "idempotent",
"interface_mode": "composite",
"availability": "available",
"interface_reason": "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
"reviewed": true,
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity.",
"cli_path": "chat +chat-list-mine",
"runtime_gate": "none"
},
"chat.shortcut_chat_list_all": {
"effect": "read",
"risk": "low",
"confirmation": "not_required",
"idempotency": "idempotent",
"interface_mode": "composite",
"availability": "available",
"interface_reason": "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
"reviewed": true,
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity.",
"cli_path": "chat +chat-list-all",
"runtime_gate": "none"
},
"chat.shortcut_chat_list_join_requests": {
"effect": "read",
"risk": "low",
"confirmation": "not_required",
"idempotency": "idempotent",
"interface_mode": "composite",
"availability": "available",
"interface_reason": "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
"reviewed": true,
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity.",
"cli_path": "chat +chat-list-join-requests",
"runtime_gate": "none"
},
"chat.shortcut_chat_bots": {
"effect": "read",
"risk": "low",
"confirmation": "not_required",
"idempotency": "idempotent",
"interface_mode": "composite",
"availability": "available",
"interface_reason": "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
"reviewed": true,
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity.",
"cli_path": "chat +chat-bots",
"runtime_gate": "none"
},
"chat.shortcut_chat_set_admin": {
"effect": "write",
"risk": "medium",
"confirmation": "user_required",
"idempotency": "unknown",
"interface_mode": "composite",
"availability": "available",
"interface_reason": "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
"reviewed": true,
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity.",
"cli_path": "chat +chat-set-admin",
"runtime_gate": "typed_yes"
},
"chat.shortcut_chat_mute": {
"effect": "write",
"risk": "medium",
"confirmation": "user_required",
"idempotency": "unknown",
"interface_mode": "composite",
"availability": "available",
"interface_reason": "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
"reviewed": true,
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity.",
"cli_path": "chat +chat-mute",
"runtime_gate": "typed_yes"
},
"chat.shortcut_chat_role_list": {
"effect": "read",
"risk": "low",
"confirmation": "not_required",
"idempotency": "idempotent",
"interface_mode": "composite",
"availability": "available",
"interface_reason": "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
"reviewed": true,
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity.",
"cli_path": "chat +chat-role-list",
"runtime_gate": "none"
},
"chat.shortcut_chat_role_add": {
"effect": "write",
"risk": "medium",
"confirmation": "user_required",
"idempotency": "unknown",
"interface_mode": "composite",
"availability": "available",
"interface_reason": "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
"reviewed": true,
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity.",
"cli_path": "chat +chat-role-add",
"runtime_gate": "typed_yes"
},
"chat.shortcut_chat_role_update": {
"effect": "write",
"risk": "medium",
"confirmation": "user_required",
"idempotency": "unknown",
"interface_mode": "composite",
"availability": "available",
"interface_reason": "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
"reviewed": true,
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity.",
"cli_path": "chat +chat-role-update",
"runtime_gate": "typed_yes"
},
"chat.shortcut_chat_role_set_user": {
"effect": "write",
"risk": "medium",
"confirmation": "user_required",
"idempotency": "unknown",
"interface_mode": "composite",
"availability": "available",
"interface_reason": "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
"reviewed": true,
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity.",
"cli_path": "chat +chat-role-set-user",
"runtime_gate": "typed_yes"
},
"chat.shortcut_chat_role_query_user": {
"effect": "read",
"risk": "low",
"confirmation": "not_required",
"idempotency": "idempotent",
"interface_mode": "composite",
"availability": "available",
"interface_reason": "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
"reviewed": true,
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity.",
"cli_path": "chat +chat-role-query-user",
"runtime_gate": "none"
},
"chat.shortcut_messages_send_by_webhook": {
"effect": "write",
"risk": "medium",
"confirmation": "user_required",
"idempotency": "unknown",
"interface_mode": "composite",
"availability": "available",
"interface_reason": "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
"reviewed": true,
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity.",
"cli_path": "chat +messages-send-by-webhook",
"runtime_gate": "typed_yes"
},
"chat.shortcut_messages_list_direct": {
"effect": "read",
"risk": "low",
"confirmation": "not_required",
"idempotency": "idempotent",
"interface_mode": "composite",
"availability": "available",
"interface_reason": "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
"reviewed": true,
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity.",
"cli_path": "chat +messages-list-direct",
"runtime_gate": "none"
},
"chat.shortcut_messages_list_unread_conversations": {
"effect": "read",
"risk": "low",
"confirmation": "not_required",
"idempotency": "idempotent",
"interface_mode": "composite",
"availability": "available",
"interface_reason": "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
"reviewed": true,
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity.",
"cli_path": "chat +messages-list-unread-conversations",
"runtime_gate": "none"
},
"chat.shortcut_messages_mget": {
"effect": "read",
"risk": "low",
"confirmation": "not_required",
"idempotency": "idempotent",
"interface_mode": "composite",
"availability": "available",
"interface_reason": "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
"reviewed": true,
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity.",
"cli_path": "chat +messages-mget",
"runtime_gate": "none"
},
"chat.shortcut_messages_query_send_status": {
"effect": "read",
"risk": "low",
"confirmation": "not_required",
"idempotency": "idempotent",
"interface_mode": "composite",
"availability": "available",
"interface_reason": "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
"reviewed": true,
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity.",
"cli_path": "chat +messages-query-send-status",
"runtime_gate": "none"
},
"chat.shortcut_messages_read_status": {
"effect": "read",
"risk": "low",
"confirmation": "not_required",
"idempotency": "idempotent",
"interface_mode": "composite",
"availability": "available",
"interface_reason": "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
"reviewed": true,
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity.",
"cli_path": "chat +messages-read-status",
"runtime_gate": "none"
},
"chat.shortcut_messages_update_card": {
"effect": "write",
"risk": "medium",
"confirmation": "user_required",
"idempotency": "unknown",
"interface_mode": "composite",
"availability": "available",
"interface_reason": "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
"reviewed": true,
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity.",
"cli_path": "chat +messages-update-card",
"runtime_gate": "typed_yes"
},
"chat.shortcut_messages_list_pin": {
"effect": "read",
"risk": "low",
"confirmation": "not_required",
"idempotency": "idempotent",
"interface_mode": "composite",
"availability": "available",
"interface_reason": "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
"reviewed": true,
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity.",
"cli_path": "chat +messages-list-pin",
"runtime_gate": "none"
},
"chat.shortcut_at_me": {
"effect": "read",
"risk": "low",
"confirmation": "not_required",
"idempotency": "idempotent",
"interface_mode": "composite",
"availability": "available",
"interface_reason": "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
"reviewed": true,
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity.",
"cli_path": "chat +at-me",
"runtime_gate": "none"
},
"chat.shortcut_broadcast": {
"effect": "write",
"risk": "medium",
"confirmation": "user_required",
"idempotency": "unknown",
"interface_mode": "composite",
"availability": "available",
"interface_reason": "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
"reviewed": true,
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity.",
"cli_path": "chat +broadcast",
"runtime_gate": "typed_yes"
},
"chat.shortcut_dm": {
"effect": "write",
"risk": "medium",
"confirmation": "user_required",
"idempotency": "unknown",
"interface_mode": "composite",
"availability": "available",
"interface_reason": "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
"reviewed": true,
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity.",
"cli_path": "chat +dm",
"runtime_gate": "typed_yes"
},
"chat.shortcut_group_members": {
"effect": "read",
"risk": "low",
"confirmation": "not_required",
"idempotency": "idempotent",
"interface_mode": "composite",
"availability": "available",
"interface_reason": "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
"reviewed": true,
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity.",
"cli_path": "chat +group-members",
"runtime_gate": "none"
},
"chat.shortcut_my_groups": {
"effect": "read",
"risk": "low",
"confirmation": "not_required",
"idempotency": "idempotent",
"interface_mode": "composite",
"availability": "available",
"interface_reason": "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
"reviewed": true,
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity.",
"cli_path": "chat +my-groups",
"runtime_gate": "none"
},
"chat.shortcut_send_to_group": {
"effect": "write",
"risk": "medium",
"confirmation": "user_required",
"idempotency": "unknown",
"interface_mode": "composite",
"availability": "available",
"interface_reason": "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
"reviewed": true,
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity.",
"cli_path": "chat +send-to-group",
"runtime_gate": "typed_yes"
},
"chat.shortcut_unread_chats": {
"effect": "read",
"risk": "low",
"confirmation": "not_required",
"idempotency": "idempotent",
"interface_mode": "composite",
"availability": "available",
"interface_reason": "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
"reviewed": true,
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity.",
"cli_path": "chat +unread-chats",
"runtime_gate": "none"
}
}
}
@@ -388,6 +388,188 @@
"review_reason": "The reviewed handler requires at least one of nickname or avatar file ID, confirms, then invokes self_user_profile_update for the current user.",
"cli_path": "contact user update-self",
"runtime_gate": "confirm_dangerous"
},
"contact.shortcut_list_followings": {
"effect": "read",
"risk": "low",
"confirmation": "not_required",
"idempotency": "idempotent",
"interface_mode": "composite",
"availability": "available",
"interface_reason": "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
"reviewed": true,
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity.",
"cli_path": "contact +list-followings",
"runtime_gate": "none"
},
"contact.shortcut_search_user": {
"effect": "read",
"risk": "low",
"confirmation": "not_required",
"idempotency": "idempotent",
"interface_mode": "composite",
"availability": "available",
"interface_reason": "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
"reviewed": true,
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity.",
"cli_path": "contact +search-user",
"runtime_gate": "none"
},
"contact.shortcut_search_mobile": {
"effect": "read",
"risk": "low",
"confirmation": "not_required",
"idempotency": "idempotent",
"interface_mode": "composite",
"availability": "available",
"interface_reason": "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
"reviewed": true,
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity.",
"cli_path": "contact +search-mobile",
"runtime_gate": "none"
},
"contact.shortcut_list_roles": {
"effect": "read",
"risk": "low",
"confirmation": "not_required",
"idempotency": "idempotent",
"interface_mode": "composite",
"availability": "available",
"interface_reason": "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
"reviewed": true,
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity.",
"cli_path": "contact +list-roles",
"runtime_gate": "none"
},
"contact.shortcut_list_role_members": {
"effect": "read",
"risk": "low",
"confirmation": "not_required",
"idempotency": "idempotent",
"interface_mode": "composite",
"availability": "available",
"interface_reason": "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
"reviewed": true,
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity.",
"cli_path": "contact +list-role-members",
"runtime_gate": "none"
},
"contact.shortcut_list_sub_depts": {
"effect": "read",
"risk": "low",
"confirmation": "not_required",
"idempotency": "idempotent",
"interface_mode": "composite",
"availability": "available",
"interface_reason": "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
"reviewed": true,
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity.",
"cli_path": "contact +list-sub-depts",
"runtime_gate": "none"
},
"contact.shortcut_list_dept_members": {
"effect": "read",
"risk": "low",
"confirmation": "not_required",
"idempotency": "idempotent",
"interface_mode": "composite",
"availability": "available",
"interface_reason": "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
"reviewed": true,
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity.",
"cli_path": "contact +list-dept-members",
"runtime_gate": "none"
},
"contact.shortcut_by_mobile": {
"effect": "read",
"risk": "low",
"confirmation": "not_required",
"idempotency": "idempotent",
"interface_mode": "composite",
"availability": "available",
"interface_reason": "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
"reviewed": true,
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity.",
"cli_path": "contact +by-mobile",
"runtime_gate": "none"
},
"contact.shortcut_dept_members": {
"effect": "read",
"risk": "low",
"confirmation": "not_required",
"idempotency": "idempotent",
"interface_mode": "composite",
"availability": "available",
"interface_reason": "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
"reviewed": true,
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity.",
"cli_path": "contact +dept-members",
"runtime_gate": "none"
},
"contact.shortcut_lookup": {
"effect": "read",
"risk": "low",
"confirmation": "not_required",
"idempotency": "idempotent",
"interface_mode": "composite",
"availability": "available",
"interface_reason": "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
"reviewed": true,
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity.",
"cli_path": "contact +lookup",
"runtime_gate": "none"
},
"contact.shortcut_org": {
"effect": "read",
"risk": "low",
"confirmation": "not_required",
"idempotency": "idempotent",
"interface_mode": "composite",
"availability": "available",
"interface_reason": "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
"reviewed": true,
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity.",
"cli_path": "contact +org",
"runtime_gate": "none"
},
"contact.shortcut_resolve_dept": {
"effect": "read",
"risk": "low",
"confirmation": "not_required",
"idempotency": "idempotent",
"interface_mode": "composite",
"availability": "available",
"interface_reason": "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
"reviewed": true,
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity.",
"cli_path": "contact +resolve-dept",
"runtime_gate": "none"
},
"contact.shortcut_team": {
"effect": "read",
"risk": "low",
"confirmation": "not_required",
"idempotency": "idempotent",
"interface_mode": "composite",
"availability": "available",
"interface_reason": "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
"reviewed": true,
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity.",
"cli_path": "contact +team",
"runtime_gate": "none"
},
"contact.shortcut_me": {
"effect": "read",
"risk": "low",
"confirmation": "not_required",
"idempotency": "idempotent",
"interface_mode": "composite",
"availability": "available",
"interface_reason": "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
"reviewed": true,
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity.",
"cli_path": "contact +me",
"runtime_gate": "none"
}
}
}
@@ -0,0 +1,259 @@
{
"version": 1,
"source": {
"kind": "explicit",
"name": "dws-tool-metadata/devapp",
"repository": "DingTalk-Real-AI/dingtalk-workspace-cli",
"channel": "open-source",
"reviewed": true
},
"tools": {
"devapp.shortcut_list": {
"effect": "read",
"risk": "low",
"confirmation": "not_required",
"idempotency": "idempotent",
"interface_mode": "composite",
"availability": "available",
"interface_reason": "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
"reviewed": true,
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity.",
"cli_path": "devapp +list",
"runtime_gate": "none"
},
"devapp.shortcut_get": {
"effect": "read",
"risk": "low",
"confirmation": "not_required",
"idempotency": "idempotent",
"interface_mode": "composite",
"availability": "available",
"interface_reason": "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
"reviewed": true,
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity.",
"cli_path": "devapp +get",
"runtime_gate": "none"
},
"devapp.shortcut_create": {
"effect": "write",
"risk": "medium",
"confirmation": "user_required",
"idempotency": "unknown",
"interface_mode": "composite",
"availability": "available",
"interface_reason": "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
"reviewed": true,
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity.",
"cli_path": "devapp +create",
"runtime_gate": "typed_yes"
},
"devapp.shortcut_update": {
"effect": "write",
"risk": "medium",
"confirmation": "user_required",
"idempotency": "unknown",
"interface_mode": "composite",
"availability": "available",
"interface_reason": "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
"reviewed": true,
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity.",
"cli_path": "devapp +update",
"runtime_gate": "typed_yes"
},
"devapp.shortcut_delete": {
"effect": "destructive",
"risk": "high",
"confirmation": "user_required",
"idempotency": "unknown",
"interface_mode": "composite",
"availability": "available",
"interface_reason": "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
"reviewed": true,
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity.",
"cli_path": "devapp +delete",
"runtime_gate": "typed_yes"
},
"devapp.shortcut_enable": {
"effect": "write",
"risk": "medium",
"confirmation": "user_required",
"idempotency": "unknown",
"interface_mode": "composite",
"availability": "available",
"interface_reason": "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
"reviewed": true,
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity.",
"cli_path": "devapp +enable",
"runtime_gate": "typed_yes"
},
"devapp.shortcut_disable": {
"effect": "destructive",
"risk": "high",
"confirmation": "user_required",
"idempotency": "unknown",
"interface_mode": "composite",
"availability": "available",
"interface_reason": "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
"reviewed": true,
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity.",
"cli_path": "devapp +disable",
"runtime_gate": "typed_yes"
},
"devapp.shortcut_webapp_get": {
"effect": "read",
"risk": "low",
"confirmation": "not_required",
"idempotency": "idempotent",
"interface_mode": "composite",
"availability": "available",
"interface_reason": "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
"reviewed": true,
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity.",
"cli_path": "devapp +webapp-get",
"runtime_gate": "none"
},
"devapp.shortcut_webapp_config": {
"effect": "write",
"risk": "medium",
"confirmation": "user_required",
"idempotency": "unknown",
"interface_mode": "composite",
"availability": "available",
"interface_reason": "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
"reviewed": true,
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity.",
"cli_path": "devapp +webapp-config",
"runtime_gate": "typed_yes"
},
"devapp.shortcut_permission_list": {
"effect": "read",
"risk": "low",
"confirmation": "not_required",
"idempotency": "idempotent",
"interface_mode": "composite",
"availability": "available",
"interface_reason": "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
"reviewed": true,
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity.",
"cli_path": "devapp +permission-list",
"runtime_gate": "none"
},
"devapp.shortcut_member_list": {
"effect": "read",
"risk": "low",
"confirmation": "not_required",
"idempotency": "idempotent",
"interface_mode": "composite",
"availability": "available",
"interface_reason": "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
"reviewed": true,
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity.",
"cli_path": "devapp +member-list",
"runtime_gate": "none"
},
"devapp.shortcut_member_add": {
"effect": "write",
"risk": "medium",
"confirmation": "user_required",
"idempotency": "unknown",
"interface_mode": "composite",
"availability": "available",
"interface_reason": "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
"reviewed": true,
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity.",
"cli_path": "devapp +member-add",
"runtime_gate": "typed_yes"
},
"devapp.shortcut_member_remove": {
"effect": "destructive",
"risk": "high",
"confirmation": "user_required",
"idempotency": "unknown",
"interface_mode": "composite",
"availability": "available",
"interface_reason": "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
"reviewed": true,
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity.",
"cli_path": "devapp +member-remove",
"runtime_gate": "typed_yes"
},
"devapp.shortcut_robot_get": {
"effect": "read",
"risk": "low",
"confirmation": "not_required",
"idempotency": "idempotent",
"interface_mode": "composite",
"availability": "available",
"interface_reason": "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
"reviewed": true,
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity.",
"cli_path": "devapp +robot-get",
"runtime_gate": "none"
},
"devapp.shortcut_event_list": {
"effect": "read",
"risk": "low",
"confirmation": "not_required",
"idempotency": "idempotent",
"interface_mode": "composite",
"availability": "available",
"interface_reason": "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
"reviewed": true,
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity.",
"cli_path": "devapp +event-list",
"runtime_gate": "none"
},
"devapp.shortcut_version_list": {
"effect": "read",
"risk": "low",
"confirmation": "not_required",
"idempotency": "idempotent",
"interface_mode": "composite",
"availability": "available",
"interface_reason": "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
"reviewed": true,
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity.",
"cli_path": "devapp +version-list",
"runtime_gate": "none"
},
"devapp.shortcut_version_get": {
"effect": "read",
"risk": "low",
"confirmation": "not_required",
"idempotency": "idempotent",
"interface_mode": "composite",
"availability": "available",
"interface_reason": "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
"reviewed": true,
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity.",
"cli_path": "devapp +version-get",
"runtime_gate": "none"
},
"devapp.shortcut_version_check_approval": {
"effect": "read",
"risk": "low",
"confirmation": "not_required",
"idempotency": "idempotent",
"interface_mode": "composite",
"availability": "available",
"interface_reason": "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
"reviewed": true,
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity.",
"cli_path": "devapp +version-check-approval",
"runtime_gate": "none"
},
"devapp.shortcut_version_status": {
"effect": "read",
"risk": "low",
"confirmation": "not_required",
"idempotency": "idempotent",
"interface_mode": "composite",
"availability": "available",
"interface_reason": "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
"reviewed": true,
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity.",
"cli_path": "devapp +version-status",
"runtime_gate": "none"
}
}
}
@@ -21,6 +21,58 @@
"interface_mode": "mcp",
"availability": "available",
"reviewed": true
},
"ding.shortcut_list": {
"effect": "read",
"risk": "low",
"confirmation": "not_required",
"idempotency": "idempotent",
"interface_mode": "composite",
"availability": "available",
"interface_reason": "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
"reviewed": true,
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity.",
"cli_path": "ding +list",
"runtime_gate": "none"
},
"ding.shortcut_receiver_status": {
"effect": "read",
"risk": "low",
"confirmation": "not_required",
"idempotency": "idempotent",
"interface_mode": "composite",
"availability": "available",
"interface_reason": "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
"reviewed": true,
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity.",
"cli_path": "ding +receiver-status",
"runtime_gate": "none"
},
"ding.shortcut_send_personal": {
"effect": "write",
"risk": "medium",
"confirmation": "user_required",
"idempotency": "unknown",
"interface_mode": "composite",
"availability": "available",
"interface_reason": "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
"reviewed": true,
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity.",
"cli_path": "ding +send-personal",
"runtime_gate": "typed_yes"
},
"ding.shortcut_recall_personal": {
"effect": "destructive",
"risk": "high",
"confirmation": "user_required",
"idempotency": "unknown",
"interface_mode": "composite",
"availability": "available",
"interface_reason": "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
"reviewed": true,
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity.",
"cli_path": "ding +recall-personal",
"runtime_gate": "typed_yes"
}
}
}
+222 -2
View File
@@ -301,8 +301,7 @@
},
"review_reason": "Preserve the reviewed user-mention conversion and normalize repeated or comma-separated group conversation IDs into mentionedOpenConversationIds.",
"cli_path": "doc comment update",
"runtime_gate": "none",
"confirmation": "not_required"
"runtime_gate": "none"
},
"doc.update_document": {
"effect": "write",
@@ -367,6 +366,227 @@
"interface_reason": "Reviewed unpinned remote adapter: this executable CLI wrapper calls a remote helper that is absent from the pinned MCP metadata snapshot; no single pinned semantically equivalent interface_ref can represent the command.",
"reviewed": true,
"review_reason": "migrated to reviewed metadata block"
},
"doc.shortcut_search": {
"effect": "read",
"risk": "low",
"confirmation": "not_required",
"idempotency": "idempotent",
"interface_mode": "composite",
"availability": "available",
"interface_reason": "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
"reviewed": true,
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity.",
"cli_path": "doc +search",
"runtime_gate": "none"
},
"doc.shortcut_list": {
"effect": "read",
"risk": "low",
"confirmation": "not_required",
"idempotency": "idempotent",
"interface_mode": "composite",
"availability": "available",
"interface_reason": "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
"reviewed": true,
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity.",
"cli_path": "doc +list",
"runtime_gate": "none"
},
"doc.shortcut_copy": {
"effect": "write",
"risk": "medium",
"confirmation": "user_required",
"idempotency": "unknown",
"interface_mode": "composite",
"availability": "available",
"interface_reason": "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
"reviewed": true,
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity.",
"cli_path": "doc +copy",
"runtime_gate": "typed_yes"
},
"doc.shortcut_move": {
"effect": "write",
"risk": "medium",
"confirmation": "user_required",
"idempotency": "unknown",
"interface_mode": "composite",
"availability": "available",
"interface_reason": "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
"reviewed": true,
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity.",
"cli_path": "doc +move",
"runtime_gate": "typed_yes"
},
"doc.shortcut_comment_list": {
"effect": "read",
"risk": "low",
"confirmation": "not_required",
"idempotency": "idempotent",
"interface_mode": "composite",
"availability": "available",
"interface_reason": "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
"reviewed": true,
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity.",
"cli_path": "doc +comment-list",
"runtime_gate": "none"
},
"doc.shortcut_comment_create": {
"effect": "write",
"risk": "medium",
"confirmation": "user_required",
"idempotency": "unknown",
"interface_mode": "composite",
"availability": "available",
"interface_reason": "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
"reviewed": true,
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity.",
"cli_path": "doc +comment-create",
"runtime_gate": "typed_yes"
},
"doc.shortcut_comment_reply": {
"effect": "write",
"risk": "medium",
"confirmation": "user_required",
"idempotency": "unknown",
"interface_mode": "composite",
"availability": "available",
"interface_reason": "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
"reviewed": true,
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity.",
"cli_path": "doc +comment-reply",
"runtime_gate": "typed_yes"
},
"doc.shortcut_export_submit": {
"effect": "read",
"risk": "low",
"confirmation": "not_required",
"idempotency": "idempotent",
"interface_mode": "composite",
"availability": "available",
"interface_reason": "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
"reviewed": true,
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity.",
"cli_path": "doc +export-submit",
"runtime_gate": "none"
},
"doc.shortcut_export_get": {
"effect": "read",
"risk": "low",
"confirmation": "not_required",
"idempotency": "idempotent",
"interface_mode": "composite",
"availability": "available",
"interface_reason": "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
"reviewed": true,
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity.",
"cli_path": "doc +export-get",
"runtime_gate": "none"
},
"doc.shortcut_version_save": {
"effect": "write",
"risk": "medium",
"confirmation": "user_required",
"idempotency": "unknown",
"interface_mode": "composite",
"availability": "available",
"interface_reason": "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
"reviewed": true,
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity.",
"cli_path": "doc +version-save",
"runtime_gate": "typed_yes"
},
"doc.shortcut_version_list": {
"effect": "read",
"risk": "low",
"confirmation": "not_required",
"idempotency": "idempotent",
"interface_mode": "composite",
"availability": "available",
"interface_reason": "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
"reviewed": true,
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity.",
"cli_path": "doc +version-list",
"runtime_gate": "none"
},
"doc.shortcut_version_revert": {
"effect": "destructive",
"risk": "high",
"confirmation": "user_required",
"idempotency": "unknown",
"interface_mode": "composite",
"availability": "available",
"interface_reason": "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
"reviewed": true,
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity.",
"cli_path": "doc +version-revert",
"runtime_gate": "typed_yes"
},
"doc.shortcut_template_list": {
"effect": "read",
"risk": "low",
"confirmation": "not_required",
"idempotency": "idempotent",
"interface_mode": "composite",
"availability": "available",
"interface_reason": "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
"reviewed": true,
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity.",
"cli_path": "doc +template-list",
"runtime_gate": "none"
},
"doc.shortcut_template_search": {
"effect": "read",
"risk": "low",
"confirmation": "not_required",
"idempotency": "idempotent",
"interface_mode": "composite",
"availability": "available",
"interface_reason": "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
"reviewed": true,
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity.",
"cli_path": "doc +template-search",
"runtime_gate": "none"
},
"doc.shortcut_doc_append": {
"effect": "write",
"risk": "medium",
"confirmation": "user_required",
"idempotency": "unknown",
"interface_mode": "composite",
"availability": "available",
"interface_reason": "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
"reviewed": true,
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity.",
"cli_path": "doc +doc-append",
"runtime_gate": "typed_yes"
},
"doc.shortcut_find_doc": {
"effect": "read",
"risk": "low",
"confirmation": "not_required",
"idempotency": "idempotent",
"interface_mode": "composite",
"availability": "available",
"interface_reason": "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
"reviewed": true,
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity.",
"cli_path": "doc +find-doc",
"runtime_gate": "none"
},
"doc.shortcut_share_doc": {
"effect": "write",
"risk": "medium",
"confirmation": "user_required",
"idempotency": "unknown",
"interface_mode": "composite",
"availability": "available",
"interface_reason": "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
"reviewed": true,
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity.",
"cli_path": "doc +share-doc",
"runtime_gate": "typed_yes"
}
}
}
+99 -1
View File
@@ -203,7 +203,14 @@
},
"interface_mode": "mcp",
"availability": "available",
"reviewed": true
"reviewed": true,
"parameters": {
"name": {
"description": "新显示名称;实际执行前读取节点类型与当前扩展名,仅对非文件夹且末尾后缀与当前扩展名一致的名称去掉一层,避免双扩展名"
}
},
"review_reason": "The reviewed CLI reads node metadata before mutation and strips only the exact current extension for non-folder nodes, avoiding both duplicate extensions and dotted-folder corruption without a suffix whitelist.",
"cli_path": "drive rename"
},
"drive.search_files": {
"interface_mode": "mcp",
@@ -228,6 +235,97 @@
"review_reason": "The reviewed helper preserves the stable command-level Schema contract for ordinary uploads; --node switches to overwrite mode and the runtime still requires explicit confirmation unless --yes is supplied.",
"cli_path": "drive upload",
"runtime_gate": "none"
},
"drive.shortcut_info": {
"effect": "read",
"risk": "low",
"confirmation": "not_required",
"idempotency": "idempotent",
"interface_mode": "composite",
"availability": "available",
"interface_reason": "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
"reviewed": true,
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity.",
"cli_path": "drive +info",
"runtime_gate": "none"
},
"drive.shortcut_search": {
"effect": "read",
"risk": "low",
"confirmation": "not_required",
"idempotency": "idempotent",
"interface_mode": "composite",
"availability": "available",
"interface_reason": "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
"reviewed": true,
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity.",
"cli_path": "drive +search",
"runtime_gate": "none"
},
"drive.shortcut_search_docs": {
"effect": "read",
"risk": "low",
"confirmation": "not_required",
"idempotency": "idempotent",
"interface_mode": "composite",
"availability": "available",
"interface_reason": "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
"reviewed": true,
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity.",
"cli_path": "drive +search-docs",
"runtime_gate": "none"
},
"drive.shortcut_copy": {
"effect": "write",
"risk": "medium",
"confirmation": "user_required",
"idempotency": "unknown",
"interface_mode": "composite",
"availability": "available",
"interface_reason": "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
"reviewed": true,
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity.",
"cli_path": "drive +copy",
"runtime_gate": "typed_yes"
},
"drive.shortcut_move": {
"effect": "write",
"risk": "medium",
"confirmation": "user_required",
"idempotency": "unknown",
"interface_mode": "composite",
"availability": "available",
"interface_reason": "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
"reviewed": true,
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity.",
"cli_path": "drive +move",
"runtime_gate": "typed_yes"
},
"drive.shortcut_recent": {
"effect": "read",
"risk": "low",
"confirmation": "not_required",
"idempotency": "idempotent",
"interface_mode": "composite",
"availability": "available",
"interface_reason": "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
"reviewed": true,
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity.",
"cli_path": "drive +recent",
"runtime_gate": "none"
},
"drive.shortcut_find_file": {
"effect": "read",
"risk": "low",
"confirmation": "not_required",
"idempotency": "idempotent",
"interface_mode": "composite",
"availability": "available",
"interface_reason": "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
"reviewed": true,
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity.",
"cli_path": "drive +find-file",
"runtime_gate": "none"
}
}
}
@@ -218,6 +218,136 @@
"cli_path": "mail template update",
"runtime_gate": "none",
"confirmation": "not_required"
},
"mail.shortcut_thread_list": {
"effect": "read",
"risk": "low",
"confirmation": "not_required",
"idempotency": "idempotent",
"interface_mode": "composite",
"availability": "available",
"interface_reason": "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
"reviewed": true,
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity.",
"cli_path": "mail +thread-list",
"runtime_gate": "none"
},
"mail.shortcut_folder_list": {
"effect": "read",
"risk": "low",
"confirmation": "not_required",
"idempotency": "idempotent",
"interface_mode": "composite",
"availability": "available",
"interface_reason": "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
"reviewed": true,
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity.",
"cli_path": "mail +folder-list",
"runtime_gate": "none"
},
"mail.shortcut_tag_list": {
"effect": "read",
"risk": "low",
"confirmation": "not_required",
"idempotency": "idempotent",
"interface_mode": "composite",
"availability": "available",
"interface_reason": "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
"reviewed": true,
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity.",
"cli_path": "mail +tag-list",
"runtime_gate": "none"
},
"mail.shortcut_user_search": {
"effect": "read",
"risk": "low",
"confirmation": "not_required",
"idempotency": "idempotent",
"interface_mode": "composite",
"availability": "available",
"interface_reason": "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
"reviewed": true,
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity.",
"cli_path": "mail +user-search",
"runtime_gate": "none"
},
"mail.shortcut_template_list": {
"effect": "read",
"risk": "low",
"confirmation": "not_required",
"idempotency": "idempotent",
"interface_mode": "composite",
"availability": "available",
"interface_reason": "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
"reviewed": true,
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity.",
"cli_path": "mail +template-list",
"runtime_gate": "none"
},
"mail.shortcut_contact_list": {
"effect": "read",
"risk": "low",
"confirmation": "not_required",
"idempotency": "idempotent",
"interface_mode": "composite",
"availability": "available",
"interface_reason": "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
"reviewed": true,
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity.",
"cli_path": "mail +contact-list",
"runtime_gate": "none"
},
"mail.shortcut_find_mail_user": {
"effect": "read",
"risk": "low",
"confirmation": "not_required",
"idempotency": "idempotent",
"interface_mode": "composite",
"availability": "available",
"interface_reason": "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
"reviewed": true,
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity.",
"cli_path": "mail +find-mail-user",
"runtime_gate": "none"
},
"mail.shortcut_recent_mail": {
"effect": "read",
"risk": "low",
"confirmation": "not_required",
"idempotency": "idempotent",
"interface_mode": "composite",
"availability": "available",
"interface_reason": "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
"reviewed": true,
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity.",
"cli_path": "mail +recent-mail",
"runtime_gate": "none"
},
"mail.shortcut_search_mail": {
"effect": "read",
"risk": "low",
"confirmation": "not_required",
"idempotency": "idempotent",
"interface_mode": "composite",
"availability": "available",
"interface_reason": "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
"reviewed": true,
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity.",
"cli_path": "mail +search-mail",
"runtime_gate": "none"
},
"mail.shortcut_unread_mail": {
"effect": "read",
"risk": "low",
"confirmation": "not_required",
"idempotency": "idempotent",
"interface_mode": "composite",
"availability": "available",
"interface_reason": "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
"reviewed": true,
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity.",
"cli_path": "mail +unread-mail",
"runtime_gate": "none"
}
}
}
@@ -194,6 +194,89 @@
"interface_mode": "mcp",
"availability": "available",
"reviewed": true
},
"minutes.shortcut_list_mine": {
"effect": "read",
"risk": "low",
"confirmation": "not_required",
"idempotency": "idempotent",
"interface_mode": "composite",
"availability": "available",
"interface_reason": "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
"reviewed": true,
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity.",
"cli_path": "minutes +list-mine",
"runtime_gate": "none"
},
"minutes.shortcut_list_shared": {
"effect": "read",
"risk": "low",
"confirmation": "not_required",
"idempotency": "idempotent",
"interface_mode": "composite",
"availability": "available",
"interface_reason": "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
"reviewed": true,
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity.",
"cli_path": "minutes +list-shared",
"runtime_gate": "none"
},
"minutes.shortcut_list_all": {
"effect": "read",
"risk": "low",
"confirmation": "not_required",
"idempotency": "idempotent",
"interface_mode": "composite",
"availability": "available",
"interface_reason": "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
"reviewed": true,
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity.",
"cli_path": "minutes +list-all",
"runtime_gate": "none"
},
"minutes.shortcut_record_start": {
"effect": "write",
"risk": "medium",
"confirmation": "user_required",
"idempotency": "unknown",
"interface_mode": "composite",
"availability": "available",
"interface_reason": "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
"reviewed": true,
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity.",
"cli_path": "minutes +record-start",
"runtime_gate": "typed_yes"
},
"minutes.shortcut_detail": {
"effect": "read",
"risk": "low",
"confirmation": "not_required",
"idempotency": "idempotent",
"interface_mode": "composite",
"availability": "available",
"interface_reason": "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
"reviewed": true,
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity.",
"cli_path": "minutes +detail",
"runtime_gate": "none"
},
"minutes.shortcut_replace_batch": {
"effect": "write",
"risk": "medium",
"confirmation": "user_required",
"idempotency": "unknown",
"interface_mode": "composite",
"availability": "available",
"interface_reason": "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
"reviewed": true,
"parameters": {
"pair": {
"description": "替换规则,格式 \"原文=>替换\",可重复传多组(必填);每组原文不能为空且不能重复"
}
},
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate, composite interface, and the custom --pair format/uniqueness validation without inventing a direct MCP identity.",
"cli_path": "minutes +replace-batch",
"runtime_gate": "typed_yes"
}
}
}
@@ -90,6 +90,97 @@
"availability": "available",
"reviewed": true,
"runtime_gate": "confirm_delete"
},
"oa.shortcut_list_pending": {
"effect": "read",
"risk": "low",
"confirmation": "not_required",
"idempotency": "idempotent",
"interface_mode": "composite",
"availability": "available",
"interface_reason": "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
"reviewed": true,
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity.",
"cli_path": "oa +list-pending",
"runtime_gate": "none"
},
"oa.shortcut_list_forms": {
"effect": "read",
"risk": "low",
"confirmation": "not_required",
"idempotency": "idempotent",
"interface_mode": "composite",
"availability": "available",
"interface_reason": "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
"reviewed": true,
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity.",
"cli_path": "oa +list-forms",
"runtime_gate": "none"
},
"oa.shortcut_search_forms": {
"effect": "read",
"risk": "low",
"confirmation": "not_required",
"idempotency": "idempotent",
"interface_mode": "composite",
"availability": "available",
"interface_reason": "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
"reviewed": true,
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity.",
"cli_path": "oa +search-forms",
"runtime_gate": "none"
},
"oa.shortcut_list_executed": {
"effect": "read",
"risk": "low",
"confirmation": "not_required",
"idempotency": "idempotent",
"interface_mode": "composite",
"availability": "available",
"interface_reason": "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
"reviewed": true,
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity.",
"cli_path": "oa +list-executed",
"runtime_gate": "none"
},
"oa.shortcut_list_submitted": {
"effect": "read",
"risk": "low",
"confirmation": "not_required",
"idempotency": "idempotent",
"interface_mode": "composite",
"availability": "available",
"interface_reason": "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
"reviewed": true,
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity.",
"cli_path": "oa +list-submitted",
"runtime_gate": "none"
},
"oa.shortcut_list_cc": {
"effect": "read",
"risk": "low",
"confirmation": "not_required",
"idempotency": "idempotent",
"interface_mode": "composite",
"availability": "available",
"interface_reason": "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
"reviewed": true,
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity.",
"cli_path": "oa +list-cc",
"runtime_gate": "none"
},
"oa.shortcut_my_initiated": {
"effect": "read",
"risk": "low",
"confirmation": "not_required",
"idempotency": "idempotent",
"interface_mode": "composite",
"availability": "available",
"interface_reason": "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
"reviewed": true,
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity.",
"cli_path": "oa +my-initiated",
"runtime_gate": "none"
}
}
}
@@ -51,6 +51,32 @@
"interface_mode": "mcp",
"availability": "available",
"reviewed": true
},
"report.shortcut_inbox_list": {
"effect": "read",
"risk": "low",
"confirmation": "not_required",
"idempotency": "idempotent",
"interface_mode": "composite",
"availability": "available",
"interface_reason": "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
"reviewed": true,
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity.",
"cli_path": "report +inbox-list",
"runtime_gate": "none"
},
"report.shortcut_outbox_list": {
"effect": "read",
"risk": "low",
"confirmation": "not_required",
"idempotency": "idempotent",
"interface_mode": "composite",
"availability": "available",
"interface_reason": "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
"reviewed": true,
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity.",
"cli_path": "report +outbox-list",
"runtime_gate": "none"
}
}
}
@@ -708,6 +708,32 @@
"interface_mode": "mcp",
"availability": "available",
"reviewed": true
},
"sheet.shortcut_list_sheets": {
"effect": "read",
"risk": "low",
"confirmation": "not_required",
"idempotency": "idempotent",
"interface_mode": "composite",
"availability": "available",
"interface_reason": "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
"reviewed": true,
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity.",
"cli_path": "sheet +list-sheets",
"runtime_gate": "none"
},
"sheet.shortcut_read": {
"effect": "read",
"risk": "low",
"confirmation": "not_required",
"idempotency": "idempotent",
"interface_mode": "composite",
"availability": "available",
"interface_reason": "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
"reviewed": true,
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity.",
"cli_path": "sheet +read",
"runtime_gate": "none"
}
}
}
+151 -1
View File
@@ -126,7 +126,14 @@
"todo.reset_todo_reminder": {
"interface_mode": "mcp",
"availability": "available",
"reviewed": true
"reviewed": true,
"parameters": {
"reminder-rules": {
"description": "提醒规则 JSON 数组;不传表示清除,显式传值必须为对象数组,且每条按 baseTime 提供整数 dueDateOffset 或 ISO8601 reminderTimeStamp"
}
},
"review_reason": "The reviewed CLI rejects malformed or structurally invalid non-empty reminder rules before the reset mutation, while preserving omission and [] as explicit clear forms.",
"cli_path": "todo task reset-reminder"
},
"todo.update_todo_done_status": {
"interface_mode": "mcp",
@@ -233,6 +240,149 @@
"review_reason": "The reviewed handler requires a non-null JSON array of tag update records and invokes update_todo_tag without a runtime confirmation gate.",
"cli_path": "todo tag update",
"runtime_gate": "none"
},
"todo.shortcut_assign": {
"effect": "write",
"risk": "medium",
"confirmation": "user_required",
"idempotency": "unknown",
"interface_mode": "composite",
"availability": "available",
"interface_reason": "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
"reviewed": true,
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity.",
"cli_path": "todo +assign",
"runtime_gate": "typed_yes"
},
"todo.shortcut_assign_multi": {
"effect": "write",
"risk": "medium",
"confirmation": "user_required",
"idempotency": "unknown",
"interface_mode": "composite",
"availability": "available",
"interface_reason": "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
"reviewed": true,
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity.",
"cli_path": "todo +assign-multi",
"runtime_gate": "typed_yes"
},
"todo.shortcut_created_todos": {
"effect": "read",
"risk": "low",
"confirmation": "not_required",
"idempotency": "idempotent",
"interface_mode": "composite",
"availability": "available",
"interface_reason": "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
"reviewed": true,
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity.",
"cli_path": "todo +created-todos",
"runtime_gate": "none"
},
"todo.shortcut_overdue": {
"effect": "read",
"risk": "low",
"confirmation": "not_required",
"idempotency": "idempotent",
"interface_mode": "composite",
"availability": "available",
"interface_reason": "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
"reviewed": true,
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity.",
"cli_path": "todo +overdue",
"runtime_gate": "none"
},
"todo.shortcut_remind": {
"effect": "write",
"risk": "medium",
"confirmation": "user_required",
"idempotency": "unknown",
"interface_mode": "composite",
"availability": "available",
"interface_reason": "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
"reviewed": true,
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity.",
"cli_path": "todo +remind",
"runtime_gate": "typed_yes"
},
"todo.shortcut_todo_done": {
"effect": "write",
"risk": "medium",
"confirmation": "user_required",
"idempotency": "unknown",
"interface_mode": "composite",
"availability": "available",
"interface_reason": "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
"reviewed": true,
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity.",
"cli_path": "todo +todo-done",
"runtime_gate": "typed_yes"
},
"todo.shortcut_get_my_tasks": {
"effect": "read",
"risk": "low",
"confirmation": "not_required",
"idempotency": "idempotent",
"interface_mode": "composite",
"availability": "available",
"interface_reason": "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
"reviewed": true,
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity.",
"cli_path": "todo +get-my-tasks",
"runtime_gate": "none"
},
"todo.shortcut_list_sub": {
"effect": "read",
"risk": "low",
"confirmation": "not_required",
"idempotency": "idempotent",
"interface_mode": "composite",
"availability": "available",
"interface_reason": "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
"reviewed": true,
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity.",
"cli_path": "todo +list-sub",
"runtime_gate": "none"
},
"todo.shortcut_get": {
"effect": "read",
"risk": "low",
"confirmation": "not_required",
"idempotency": "idempotent",
"interface_mode": "composite",
"availability": "available",
"interface_reason": "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
"reviewed": true,
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity.",
"cli_path": "todo +get",
"runtime_gate": "none"
},
"todo.shortcut_list_attachment": {
"effect": "read",
"risk": "low",
"confirmation": "not_required",
"idempotency": "idempotent",
"interface_mode": "composite",
"availability": "available",
"interface_reason": "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
"reviewed": true,
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity.",
"cli_path": "todo +list-attachment",
"runtime_gate": "none"
},
"todo.shortcut_list_comment": {
"effect": "read",
"risk": "low",
"confirmation": "not_required",
"idempotency": "idempotent",
"interface_mode": "composite",
"availability": "available",
"interface_reason": "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
"reviewed": true,
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity.",
"cli_path": "todo +list-comment",
"runtime_gate": "none"
}
}
}
@@ -119,6 +119,19 @@
"interface_mode": "mcp",
"availability": "available",
"reviewed": true
},
"wiki.shortcut_space_search": {
"effect": "read",
"risk": "low",
"confirmation": "not_required",
"idempotency": "idempotent",
"interface_mode": "composite",
"availability": "available",
"interface_reason": "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
"reviewed": true,
"review_reason": "Reviewed against the built-in Shortcut registry and mounted Cobra command: publishes the executable risk gate and composite interface without inventing a direct MCP identity.",
"cli_path": "wiki +space-search",
"runtime_gate": "none"
}
}
}
@@ -601,6 +601,16 @@
"target": "event consume",
"reason": "已审查的带 event_key 参数的 Skill 引用,固定映射到当前公开 event consume leaf"
},
"event consume user_im_message_receive_o2o_all -f ndjson": {
"status": "alias",
"target": "event consume",
"reason": "已审查的全量单聊事件 Skill 引用,固定映射到当前公开 event consume leaf"
},
"event consume user_im_message_receive_group_all -f ndjson": {
"status": "alias",
"target": "event consume",
"reason": "已审查的全量群消息事件 Skill 引用,固定映射到当前公开 event consume leaf"
},
"event consume user_im_message_read_o2o": {
"status": "alias",
"target": "event consume",
@@ -631,6 +641,26 @@
"target": "event consume",
"reason": "已审查的带 event_key 参数的 Skill 引用,固定映射到当前公开 event consume leaf"
},
"event consume user_im_group_updated": {
"status": "alias",
"target": "event consume",
"reason": "已审查的群标题变更事件 Skill 引用,固定映射到当前公开 event consume leaf"
},
"event consume user_im_group_member_added": {
"status": "alias",
"target": "event consume",
"reason": "已审查的群成员加入事件 Skill 引用,固定映射到当前公开 event consume leaf"
},
"event consume user_im_group_member_exited": {
"status": "alias",
"target": "event consume",
"reason": "已审查的群成员退出事件 Skill 引用,固定映射到当前公开 event consume leaf"
},
"event consume user_im_group_disbanded": {
"status": "alias",
"target": "event consume",
"reason": "已审查的群解散事件 Skill 引用,固定映射到当前公开 event consume leaf"
},
"event schema user_im_message_receive_at": {
"status": "alias",
"target": "event schema",
@@ -651,6 +681,16 @@
"target": "event schema",
"reason": "已审查的带 event_key 参数的 Skill 引用,固定映射到当前公开 event schema leaf"
},
"event schema user_im_message_receive_o2o_all": {
"status": "alias",
"target": "event schema",
"reason": "已审查的带 event_key 参数的 Skill 引用,固定映射到当前公开 event schema leaf"
},
"event schema user_im_message_receive_group_all": {
"status": "alias",
"target": "event schema",
"reason": "已审查的带 event_key 参数的 Skill 引用,固定映射到当前公开 event schema leaf"
},
"event schema user_im_message_read_o2o": {
"status": "alias",
"target": "event schema",
@@ -681,6 +721,26 @@
"target": "event schema",
"reason": "已审查的带 event_key 参数的 Skill 引用,固定映射到当前公开 event schema leaf"
},
"event schema user_im_group_updated": {
"status": "alias",
"target": "event schema",
"reason": "已审查的带 event_key 参数的 Skill 引用,固定映射到当前公开 event schema leaf"
},
"event schema user_im_group_member_added": {
"status": "alias",
"target": "event schema",
"reason": "已审查的带 event_key 参数的 Skill 引用,固定映射到当前公开 event schema leaf"
},
"event schema user_im_group_member_exited": {
"status": "alias",
"target": "event schema",
"reason": "已审查的带 event_key 参数的 Skill 引用,固定映射到当前公开 event schema leaf"
},
"event schema user_im_group_disbanded": {
"status": "alias",
"target": "event schema",
"reason": "已审查的带 event_key 参数的 Skill 引用,固定映射到当前公开 event schema leaf"
},
"mail aisearch person": {
"status": "alias",
"target": "aisearch person",
@@ -7,7 +7,7 @@
"channel": "open-source"
},
"coverage": {
"source_tools": 603,
"source_tools": 813,
"matched_tools": 71
},
"tools": {
@@ -2328,6 +2328,561 @@
"cobra-help:dws aitable workflow update --help",
"skills/mono/references/products/aitable/aitable-workflow.md"
]
},
"aitable.shortcut_base_list": {
"agent_summary": "获取当前用户可访问的 AI 表格 Base 列表(最近访问,支持游标分页)",
"use_when": [
"当你不知道具体 baseId、想先浏览自己最近用过或可访问的 AI 表格清单以便定位目标时使用;支持游标分页,返回 Base 列表及其 baseId。"
],
"avoid_when": [
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
],
"examples": [
"dws aitable +base-list",
"dws aitable +base-list --limit 5 --cursor NEXT"
],
"reviewed": true,
"review_reason": "Agent-authored and reviewed from the built-in Shortcut intent, executable Cobra path, and declared outcome; it affects selection only and does not alter execution or safety facts.",
"source_refs": [
"internal/cli/schema_command_registry.json#aitable.shortcut_base_list",
"cobra-help:dws aitable +base-list",
"ShortcutRegistry:aitable +base-list"
]
},
"aitable.shortcut_base_search": {
"agent_summary": "按名称关键词搜索 AI 表格 Base",
"use_when": [
"当你知道某个 AI 表格的名字或部分关键词、想直接定位到它并拿到 baseId 时使用;输入名称关键词,返回匹配的 Base 列表。"
],
"avoid_when": [
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
],
"examples": [
"dws aitable +base-search --query \"项目管理\""
],
"reviewed": true,
"review_reason": "Agent-authored and reviewed from the built-in Shortcut intent, executable Cobra path, and declared outcome; it affects selection only and does not alter execution or safety facts.",
"source_refs": [
"internal/cli/schema_command_registry.json#aitable.shortcut_base_search",
"cobra-help:dws aitable +base-search",
"ShortcutRegistry:aitable +base-search"
]
},
"aitable.shortcut_base_get": {
"agent_summary": "获取指定 Base 的目录信息(tables / dashboards summary)",
"use_when": [
"当你已有 baseId、需要了解这个表格里有哪些数据表和仪表盘(拿到 tableId/dashboardId)以便进一步操作时使用;返回 Base 的目录结构概要。"
],
"avoid_when": [
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
],
"examples": [
"dws aitable +base-get --base-id BASE_ID"
],
"reviewed": true,
"review_reason": "Agent-authored and reviewed from the built-in Shortcut intent, executable Cobra path, and declared outcome; it affects selection only and does not alter execution or safety facts.",
"source_refs": [
"internal/cli/schema_command_registry.json#aitable.shortcut_base_get",
"cobra-help:dws aitable +base-get",
"ShortcutRegistry:aitable +base-get"
]
},
"aitable.shortcut_table_get": {
"agent_summary": "批量获取指定数据表的表级信息、字段目录与视图目录",
"use_when": [
"当你已进入某个 Base、需要了解其中某些数据表有哪些字段(拿 fieldId)、有哪些视图(拿 viewId)以便读写数据时使用;批量返回表信息、字段目录和视图目录。"
],
"avoid_when": [
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
],
"examples": [
"dws aitable +table-get --base-id BASE_ID",
"dws aitable +table-get --base-id B --table-ids tbl1,tbl2"
],
"reviewed": true,
"review_reason": "Agent-authored and reviewed from the built-in Shortcut intent, executable Cobra path, and declared outcome; it affects selection only and does not alter execution or safety facts.",
"source_refs": [
"internal/cli/schema_command_registry.json#aitable.shortcut_table_get",
"cobra-help:dws aitable +table-get",
"ShortcutRegistry:aitable +table-get"
]
},
"aitable.shortcut_field_get": {
"agent_summary": "批量获取字段详情(含类型相关完整配置)",
"use_when": [
"当你需要查看字段的完整类型配置(如单选选项、关联表设置、AI 配置)以便正确写入数据或改配置时使用;批量返回字段详情。"
],
"avoid_when": [
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
],
"examples": [
"dws aitable +field-get --base-id B --table-id T"
],
"reviewed": true,
"review_reason": "Agent-authored and reviewed from the built-in Shortcut intent, executable Cobra path, and declared outcome; it affects selection only and does not alter execution or safety facts.",
"source_refs": [
"internal/cli/schema_command_registry.json#aitable.shortcut_field_get",
"cobra-help:dws aitable +field-get",
"ShortcutRegistry:aitable +field-get"
]
},
"aitable.shortcut_record_query": {
"agent_summary": "查询表格记录(按 ID 取 / 条件筛选 / 关键词 / 分页)",
"use_when": [
"当你要读取表格里的行数据——按 recordId 精确取、按结构化条件筛选、按关键词全文搜索或分页遍历时使用;返回匹配记录及其单元格值。"
],
"avoid_when": [
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
],
"examples": [
"dws aitable +record-query --base-id B --table-id T --query \"关键词\" --limit 50"
],
"reviewed": true,
"review_reason": "Agent-authored and reviewed from the built-in Shortcut intent, executable Cobra path, and declared outcome; it affects selection only and does not alter execution or safety facts.",
"source_refs": [
"internal/cli/schema_command_registry.json#aitable.shortcut_record_query",
"cobra-help:dws aitable +record-query",
"ShortcutRegistry:aitable +record-query"
]
},
"aitable.shortcut_record_query_empty": {
"agent_summary": "扫描并过滤出完全没填用户字段的空行",
"use_when": [
"当你想清理表格、需要先找出那些所有用户字段都为空的空行时使用;扫描并返回空行列表,支持分页预算。"
],
"avoid_when": [
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
],
"examples": [
"dws aitable +record-query-empty --base-id B --table-id T"
],
"reviewed": true,
"review_reason": "Agent-authored and reviewed from the built-in Shortcut intent, executable Cobra path, and declared outcome; it affects selection only and does not alter execution or safety facts.",
"source_refs": [
"internal/cli/schema_command_registry.json#aitable.shortcut_record_query_empty",
"cobra-help:dws aitable +record-query-empty",
"ShortcutRegistry:aitable +record-query-empty"
]
},
"aitable.shortcut_record_history_list": {
"agent_summary": "按 recordId 查询单条记录的变更历史",
"use_when": [
"当你要追溯某条记录曾被谁在何时改过哪些字段时使用;按 recordId 分页返回该行的变更历史。"
],
"avoid_when": [
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
],
"examples": [
"dws aitable +record-history-list --base-id B --table-id T --record-id R"
],
"reviewed": true,
"review_reason": "Agent-authored and reviewed from the built-in Shortcut intent, executable Cobra path, and declared outcome; it affects selection only and does not alter execution or safety facts.",
"source_refs": [
"internal/cli/schema_command_registry.json#aitable.shortcut_record_history_list",
"cobra-help:dws aitable +record-history-list",
"ShortcutRegistry:aitable +record-history-list"
]
},
"aitable.shortcut_record_share_url": {
"agent_summary": "按 recordId 批量获取记录分享链接,单次最多 20 条",
"use_when": [
"当你要把某几条记录以链接形式分享给他人(可带视图上下文)时使用;按 recordId 批量返回分享链接,单次最多 20 条。"
],
"avoid_when": [
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
],
"examples": [
"dws aitable +record-share-url --base-id B --table-id T --record-ids rec1,rec2"
],
"reviewed": true,
"review_reason": "Agent-authored and reviewed from the built-in Shortcut intent, executable Cobra path, and declared outcome; it affects selection only and does not alter execution or safety facts.",
"source_refs": [
"internal/cli/schema_command_registry.json#aitable.shortcut_record_share_url",
"cobra-help:dws aitable +record-share-url",
"ShortcutRegistry:aitable +record-share-url"
]
},
"aitable.shortcut_template_search": {
"agent_summary": "按名称关键词搜索 AI 表格模板",
"use_when": [
"当你要新建表格并想套用现成模板、需要先按关键词找模板(不传关键词则返回热门)时使用;返回模板列表及其模板 ID。"
],
"avoid_when": [
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
],
"examples": [
"dws aitable +template-search --query \"项目管理\""
],
"reviewed": true,
"review_reason": "Agent-authored and reviewed from the built-in Shortcut intent, executable Cobra path, and declared outcome; it affects selection only and does not alter execution or safety facts.",
"source_refs": [
"internal/cli/schema_command_registry.json#aitable.shortcut_template_search",
"cobra-help:dws aitable +template-search",
"ShortcutRegistry:aitable +template-search"
]
},
"aitable.shortcut_view_get": {
"agent_summary": "获取视图完整信息(列顺序、筛选、排序、分组等)",
"use_when": [
"当你要了解某个视图当前的列顺序、筛选、排序、分组等完整配置以便复用或修改时使用;批量返回视图详情。"
],
"avoid_when": [
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
],
"examples": [
"dws aitable +view-get --base-id B --table-id T"
],
"reviewed": true,
"review_reason": "Agent-authored and reviewed from the built-in Shortcut intent, executable Cobra path, and declared outcome; it affects selection only and does not alter execution or safety facts.",
"source_refs": [
"internal/cli/schema_command_registry.json#aitable.shortcut_view_get",
"cobra-help:dws aitable +view-get",
"ShortcutRegistry:aitable +view-get"
]
},
"aitable.shortcut_view_get_lock": {
"agent_summary": "获取视图锁定状态",
"use_when": [
"当你想确认某视图是否已被锁定(以防他人误改其配置)时使用;返回该视图的锁定状态。"
],
"avoid_when": [
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
],
"examples": [
"dws aitable +view-get-lock --base-id B --table-id T --view-id V"
],
"reviewed": true,
"review_reason": "Agent-authored and reviewed from the built-in Shortcut intent, executable Cobra path, and declared outcome; it affects selection only and does not alter execution or safety facts.",
"source_refs": [
"internal/cli/schema_command_registry.json#aitable.shortcut_view_get_lock",
"cobra-help:dws aitable +view-get-lock",
"ShortcutRegistry:aitable +view-get-lock"
]
},
"aitable.shortcut_view_get_frozen_cols": {
"agent_summary": "获取视图当前冻结的左侧列数",
"use_when": [
"当你想知道某视图当前冻结了左侧几列时使用;返回冻结列数。"
],
"avoid_when": [
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
],
"examples": [
"dws aitable +view-get-frozen-cols --base-id B --table-id T --view-id V"
],
"reviewed": true,
"review_reason": "Agent-authored and reviewed from the built-in Shortcut intent, executable Cobra path, and declared outcome; it affects selection only and does not alter execution or safety facts.",
"source_refs": [
"internal/cli/schema_command_registry.json#aitable.shortcut_view_get_frozen_cols",
"cobra-help:dws aitable +view-get-frozen-cols",
"ShortcutRegistry:aitable +view-get-frozen-cols"
]
},
"aitable.shortcut_view_get_row_height": {
"agent_summary": "获取视图单元格行高(像素)",
"use_when": [
"当你想知道某视图当前的行高档位时使用;返回单元格行高的像素值。"
],
"avoid_when": [
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
],
"examples": [
"dws aitable +view-get-row-height --base-id B --table-id T --view-id V"
],
"reviewed": true,
"review_reason": "Agent-authored and reviewed from the built-in Shortcut intent, executable Cobra path, and declared outcome; it affects selection only and does not alter execution or safety facts.",
"source_refs": [
"internal/cli/schema_command_registry.json#aitable.shortcut_view_get_row_height",
"cobra-help:dws aitable +view-get-row-height",
"ShortcutRegistry:aitable +view-get-row-height"
]
},
"aitable.shortcut_form_list": {
"agent_summary": "列出指定数据表下的所有表单视图",
"use_when": [
"当你要查看某数据表下已有哪些收集表单时使用;返回该表的全部表单视图列表。"
],
"avoid_when": [
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
],
"examples": [
"dws aitable +form-list --base-id B --table-id T"
],
"reviewed": true,
"review_reason": "Agent-authored and reviewed from the built-in Shortcut intent, executable Cobra path, and declared outcome; it affects selection only and does not alter execution or safety facts.",
"source_refs": [
"internal/cli/schema_command_registry.json#aitable.shortcut_form_list",
"cobra-help:dws aitable +form-list",
"ShortcutRegistry:aitable +form-list"
]
},
"aitable.shortcut_form_field_list": {
"agent_summary": "列出表单视图当前可见的字段及其配置",
"use_when": [
"当你要查看某表单当前放出了哪些字段供填写及其是否必填等配置时使用;返回表单可见字段列表。"
],
"avoid_when": [
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
],
"examples": [
"dws aitable +form-field-list --base-id B --table-id T --view-id V"
],
"reviewed": true,
"review_reason": "Agent-authored and reviewed from the built-in Shortcut intent, executable Cobra path, and declared outcome; it affects selection only and does not alter execution or safety facts.",
"source_refs": [
"internal/cli/schema_command_registry.json#aitable.shortcut_form_field_list",
"cobra-help:dws aitable +form-field-list",
"ShortcutRegistry:aitable +form-field-list"
]
},
"aitable.shortcut_form_share_get": {
"agent_summary": "读取视图当前的分享表单配置",
"use_when": [
"当你要查看某视图的表单分享是否已开启及其分享配置时使用;返回当前的分享表单配置。"
],
"avoid_when": [
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
],
"examples": [
"dws aitable +form-share-get --base-id B --table-id T --view-id V"
],
"reviewed": true,
"review_reason": "Agent-authored and reviewed from the built-in Shortcut intent, executable Cobra path, and declared outcome; it affects selection only and does not alter execution or safety facts.",
"source_refs": [
"internal/cli/schema_command_registry.json#aitable.shortcut_form_share_get",
"cobra-help:dws aitable +form-share-get",
"ShortcutRegistry:aitable +form-share-get"
]
},
"aitable.shortcut_dashboard_config_example": {
"agent_summary": "获取 dashboard config 的结构示例",
"use_when": [
"当你准备创建或更新仪表盘、需要先了解 dashboard config 的字段结构长什么样时使用;返回一份配置结构示例供参考。"
],
"avoid_when": [
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
],
"examples": [
"dws aitable +dashboard-config-example"
],
"reviewed": true,
"review_reason": "Agent-authored and reviewed from the built-in Shortcut intent, executable Cobra path, and declared outcome; it affects selection only and does not alter execution or safety facts.",
"source_refs": [
"internal/cli/schema_command_registry.json#aitable.shortcut_dashboard_config_example",
"cobra-help:dws aitable +dashboard-config-example",
"ShortcutRegistry:aitable +dashboard-config-example"
]
},
"aitable.shortcut_dashboard_get": {
"agent_summary": "获取指定 dashboard 的详细信息(含 charts summary)",
"use_when": [
"当你要查看某仪表盘的配置详情及它包含哪些图表(拿 chartId)时使用;返回 dashboard 信息与 charts 概要。"
],
"avoid_when": [
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
],
"examples": [
"dws aitable +dashboard-get --base-id B --dashboard-id D"
],
"reviewed": true,
"review_reason": "Agent-authored and reviewed from the built-in Shortcut intent, executable Cobra path, and declared outcome; it affects selection only and does not alter execution or safety facts.",
"source_refs": [
"internal/cli/schema_command_registry.json#aitable.shortcut_dashboard_get",
"cobra-help:dws aitable +dashboard-get",
"ShortcutRegistry:aitable +dashboard-get"
]
},
"aitable.shortcut_chart_widgets_example": {
"agent_summary": "获取所有图表类型的 widget config 示例",
"use_when": [
"当你准备创建或修改图表、需要先参考各类图表 widget config 的示例结构时使用;返回所有图表类型的配置示例。"
],
"avoid_when": [
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
],
"examples": [
"dws aitable +chart-widgets-example"
],
"reviewed": true,
"review_reason": "Agent-authored and reviewed from the built-in Shortcut intent, executable Cobra path, and declared outcome; it affects selection only and does not alter execution or safety facts.",
"source_refs": [
"internal/cli/schema_command_registry.json#aitable.shortcut_chart_widgets_example",
"cobra-help:dws aitable +chart-widgets-example",
"ShortcutRegistry:aitable +chart-widgets-example"
]
},
"aitable.shortcut_chart_get": {
"agent_summary": "获取指定 chart 的详细信息",
"use_when": [
"当你要查看某个图表的配置详情(统计维度、样式等)时使用;返回指定 chart 的详细信息。"
],
"avoid_when": [
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
],
"examples": [
"dws aitable +chart-get --base-id B --dashboard-id D --chart-id C"
],
"reviewed": true,
"review_reason": "Agent-authored and reviewed from the built-in Shortcut intent, executable Cobra path, and declared outcome; it affects selection only and does not alter execution or safety facts.",
"source_refs": [
"internal/cli/schema_command_registry.json#aitable.shortcut_chart_get",
"cobra-help:dws aitable +chart-get",
"ShortcutRegistry:aitable +chart-get"
]
},
"aitable.shortcut_role_list": {
"agent_summary": "列出指定 Base 下的全部角色",
"use_when": [
"当你要查看某 Base 下配置了哪些角色(拿 roleId)时使用;返回全部角色列表。"
],
"avoid_when": [
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
],
"examples": [
"dws aitable +role-list --base-id B"
],
"reviewed": true,
"review_reason": "Agent-authored and reviewed from the built-in Shortcut intent, executable Cobra path, and declared outcome; it affects selection only and does not alter execution or safety facts.",
"source_refs": [
"internal/cli/schema_command_registry.json#aitable.shortcut_role_list",
"cobra-help:dws aitable +role-list",
"ShortcutRegistry:aitable +role-list"
]
},
"aitable.shortcut_section_list_empty": {
"agent_summary": "列出指定 Base 下所有没有子节点的空文件夹",
"use_when": [
"当你想清理 Base、需要先找出所有没有任何子节点的空文件夹时使用;返回空文件夹列表。"
],
"avoid_when": [
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
],
"examples": [
"dws aitable +section-list-empty --base-id B"
],
"reviewed": true,
"review_reason": "Agent-authored and reviewed from the built-in Shortcut intent, executable Cobra path, and declared outcome; it affects selection only and does not alter execution or safety facts.",
"source_refs": [
"internal/cli/schema_command_registry.json#aitable.shortcut_section_list_empty",
"cobra-help:dws aitable +section-list-empty",
"ShortcutRegistry:aitable +section-list-empty"
]
},
"aitable.shortcut_section_list_nodes": {
"agent_summary": "列出指定 Base 当前版本下的全部 nsheet 节点",
"use_when": [
"当你要总览某 Base 当前版本下的全部节点(表、仪表盘、文件夹等)目录结构时使用;返回所有 nsheet 节点。"
],
"avoid_when": [
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
],
"examples": [
"dws aitable +section-list-nodes --base-id B"
],
"reviewed": true,
"review_reason": "Agent-authored and reviewed from the built-in Shortcut intent, executable Cobra path, and declared outcome; it affects selection only and does not alter execution or safety facts.",
"source_refs": [
"internal/cli/schema_command_registry.json#aitable.shortcut_section_list_nodes",
"cobra-help:dws aitable +section-list-nodes",
"ShortcutRegistry:aitable +section-list-nodes"
]
},
"aitable.shortcut_find_record": {
"agent_summary": "在指定多维表里按关键词查记录(只读)",
"use_when": [
"当你已经知道某个多维表的 baseId 和 tableId、想按一个关键词快速找出匹配的行记录,却不想手写结构化过滤条件时使用;内部直接调用 query_records,把 --query 作为全文关键词(keyword)在该表里检索并打印匹配记录。不传 --query 时则返回该表的前若干条记录。这是只读操作,不会修改任何数据。"
],
"avoid_when": [
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
],
"examples": [
"dws aitable +find-record --base B --table T",
"dws aitable +find-record --base B --table T --query 张三"
],
"reviewed": true,
"review_reason": "Agent-authored and reviewed from the built-in Shortcut intent, executable Cobra path, and declared outcome; it affects selection only and does not alter execution or safety facts.",
"source_refs": [
"internal/cli/schema_command_registry.json#aitable.shortcut_find_record",
"cobra-help:dws aitable +find-record",
"ShortcutRegistry:aitable +find-record"
]
},
"aitable.shortcut_list_tables": {
"agent_summary": "列出某个多维表(base)里的所有数据表(只读,投影 tableId/tableName)",
"use_when": [
"当你已经知道某个多维表(base)的 baseId、想一步看清这个 base 下都有哪些数据表(table)、拿到它们的 tableId 和 tableName 以便后续查记录或改结构,却不想手动翻 base get 的完整目录时使用;内部直接调用 get_tables,只传 baseId(不带 tableIds,因此返回该 base 下的全部数据表),再在本地把每张表投影成 tableId、tableName 两个关键字段打印出来。这是纯只读操作,只做列举与本地投影,不会创建、修改或删除任何表。"
],
"avoid_when": [
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
],
"examples": [
"dws aitable +list-tables --base B"
],
"reviewed": true,
"review_reason": "Agent-authored and reviewed from the built-in Shortcut intent, executable Cobra path, and declared outcome; it affects selection only and does not alter execution or safety facts.",
"source_refs": [
"internal/cli/schema_command_registry.json#aitable.shortcut_list_tables",
"cobra-help:dws aitable +list-tables",
"ShortcutRegistry:aitable +list-tables"
]
},
"aitable.shortcut_record_share_links": {
"agent_summary": "批量(可 >20 条)获取多维表记录分享链接:去重+分片+合并",
"use_when": [
"当你要一次性拿到很多条多维表记录的分享链接、数量可能超过底层工具单次 20 条上限时使用;内部先对 --record-ids 去重(保持顺序),再按每批 ≤20 条切片,逐批调用 get_record_share_url(在 aitable-helper 服务上),最后把各批返回的 {recordId, shareUrl} 合并成一个列表;某一批失败会记录错误但不影响其余批。这是只读操作,只生成/获取分享链接、不修改记录。可选 --view-id 生成带视图上下文的链接。"
],
"avoid_when": [
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
],
"examples": [
"dws aitable +record-share-links --base B --table T --record-ids rec1,rec2,rec3",
"dws aitable +record-share-links --base B --table T --record-ids rec1 --view-id viw_VIP"
],
"reviewed": true,
"review_reason": "Agent-authored and reviewed from the built-in Shortcut intent, executable Cobra path, and declared outcome; it affects selection only and does not alter execution or safety facts.",
"source_refs": [
"internal/cli/schema_command_registry.json#aitable.shortcut_record_share_links",
"cobra-help:dws aitable +record-share-links",
"ShortcutRegistry:aitable +record-share-links"
]
},
"aitable.shortcut_resolve_base": {
"agent_summary": "按名称搜索多维表 Base 并解析出唯一 baseId(只读)",
"use_when": [
"当你只知道某个多维表 Base 的名称(或名称里的关键词)、想把它解析成可直接用于后续工具的 baseId 时使用;内部按 --name 关键词调用 search_bases 搜索 Base,再在本地投影出每个候选的 baseId 和 name。如果只命中一个 Base 就直接返回它的 baseId;如果命中多个则列出全部候选让你消歧,绝不替你瞎猜;如果一个都没命中则提示未找到。这是纯只读操作,只做搜索与本地投影,不会修改任何 Base。"
],
"avoid_when": [
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
],
"examples": [
"dws aitable +resolve-base --name 项目管理"
],
"reviewed": true,
"review_reason": "Agent-authored and reviewed from the built-in Shortcut intent, executable Cobra path, and declared outcome; it affects selection only and does not alter execution or safety facts.",
"source_refs": [
"internal/cli/schema_command_registry.json#aitable.shortcut_resolve_base",
"cobra-help:dws aitable +resolve-base",
"ShortcutRegistry:aitable +resolve-base"
]
},
"aitable.shortcut_resolve_table": {
"agent_summary": "在某个多维表 Base 内按名称解析出唯一的数据表 tableId(只读)",
"use_when": [
"当你已经知道某个多维表 Base 的 baseId、又只记得里面某张数据表(table)的名称或名称关键词、想把它解析成可直接用于后续工具的 tableId 时使用;内部先用 get_tables(只传 baseId)列出该 Base 下的全部数据表,再在本地把每张表投影成 tableId、name,并按 --name 关键词做大小写不敏感的包含匹配来筛选候选。如果只命中一张表就直接返回它的 tableId;如果命中多张则列出全部候选让你消歧,绝不替你瞎猜;如果一张都没命中则提示未找到。这是纯只读操作,只做列举、本地匹配与投影,不会创建、修改或删除任何数据表。"
],
"avoid_when": [
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
],
"examples": [
"dws aitable +resolve-table --base B --name 任务"
],
"reviewed": true,
"review_reason": "Agent-authored and reviewed from the built-in Shortcut intent, executable Cobra path, and declared outcome; it affects selection only and does not alter execution or safety facts.",
"source_refs": [
"internal/cli/schema_command_registry.json#aitable.shortcut_resolve_table",
"cobra-help:dws aitable +resolve-table",
"ShortcutRegistry:aitable +resolve-table"
]
}
},
"products": {
@@ -886,6 +886,367 @@
"skills/mono/references/products/attendance-vacation.md",
"live-dws-schema:attendance.vacation_update_type"
]
},
"attendance.shortcut_check_result": {
"agent_summary": "查询用户打卡结果(迟到/早退/缺卡等)",
"use_when": [
"当你需要批量统计一批员工在某段时间的打卡结果(正常/迟到/早退/缺卡等判定结论)时使用,例如做月度考勤汇总或异常人员排查;输入多个 userId(最多 100 人)和不超过 1 个月的日期区间,返回每人每天的打卡结果状态,支持分页。"
],
"avoid_when": [
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
],
"examples": [
"dws attendance +check-result --users userId1,userId2 --start 2026-04-01 --end 2026-04-30 --limit 50"
],
"reviewed": true,
"review_reason": "Agent-authored and reviewed from the built-in Shortcut intent, executable Cobra path, and declared outcome; it affects selection only and does not alter execution or safety facts.",
"source_refs": [
"internal/cli/schema_command_registry.json#attendance.shortcut_check_result",
"cobra-help:dws attendance +check-result",
"ShortcutRegistry:attendance +check-result"
]
},
"attendance.shortcut_check_record": {
"agent_summary": "查询用户打卡流水(打卡时间/地点/定位方式)",
"use_when": [
"当你要查看员工每一次实际打卡的原始记录(具体打卡时刻、打卡地点、定位/Wifi/蓝牙等方式)而不是判定结论时使用,例如核实某人是否在指定地点打卡;输入 userId 列表和不超过 1 个月的日期区间,返回逐条打卡流水。与 +check-result 的区别是这里返回明细流水而非迟到早退结论。"
],
"avoid_when": [
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
],
"examples": [
"dws attendance +check-record --users userId1 --start 2026-04-01 --end 2026-04-30"
],
"reviewed": true,
"review_reason": "Agent-authored and reviewed from the built-in Shortcut intent, executable Cobra path, and declared outcome; it affects selection only and does not alter execution or safety facts.",
"source_refs": [
"internal/cli/schema_command_registry.json#attendance.shortcut_check_record",
"cobra-help:dws attendance +check-record",
"ShortcutRegistry:attendance +check-record"
]
},
"attendance.shortcut_list_approve": {
"agent_summary": "查询用户考勤审批单(补卡/加班/请假/出差外出)",
"use_when": [
"当你想查看某些员工已提交的考勤类审批单(加班、请假、出差外出、补卡)时使用,例如核对某人这段时间请了几次假或有没有补卡审批;输入 userId 列表、审批类型(overtime/加班、leave/请假、trip/出差外出、patch/补卡)和日期区间,返回匹配的审批单记录。"
],
"avoid_when": [
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
],
"examples": [
"dws attendance +list-approve --users userId1 --types overtime,leave --start 2026-04-01 --end 2026-04-30"
],
"reviewed": true,
"review_reason": "Agent-authored and reviewed from the built-in Shortcut intent, executable Cobra path, and declared outcome; it affects selection only and does not alter execution or safety facts.",
"source_refs": [
"internal/cli/schema_command_registry.json#attendance.shortcut_list_approve",
"cobra-help:dws attendance +list-approve",
"ShortcutRegistry:attendance +list-approve"
]
},
"attendance.shortcut_get_approve_template": {
"agent_summary": "查询补卡/请假/加班/外出/出差审批提交链接",
"use_when": [
"当用户想自己发起一条考勤审批(补卡、请假、加班、外出、出差)、需要拿到对应审批表单的提交入口链接时使用;输入审批类型(如 leave/请假),返回可直接打开填写并提交的审批链接。本命令只返回链接、不代替用户提交审批。"
],
"avoid_when": [
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
],
"examples": [
"dws attendance +get-approve-template --type leave"
],
"reviewed": true,
"review_reason": "Agent-authored and reviewed from the built-in Shortcut intent, executable Cobra path, and declared outcome; it affects selection only and does not alter execution or safety facts.",
"source_refs": [
"internal/cli/schema_command_registry.json#attendance.shortcut_get_approve_template",
"cobra-help:dws attendance +get-approve-template",
"ShortcutRegistry:attendance +get-approve-template"
]
},
"attendance.shortcut_get_schedule": {
"agent_summary": "获取指定用户一段时间内的排班记录",
"use_when": [
"当你要查看排班制考勤组下员工在某段时间的排班记录(含排班 id、班次、是否休息)时使用,尤其是需要拿到排班 id 用于后续 BOSS 改签打卡(+boss-check)的场景;输入 userId 列表和起止时间,返回逐日排班明细。"
],
"avoid_when": [
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
],
"examples": [
"dws attendance +get-schedule --users user001,user002 --start 2026-04-01 --end 2026-04-30"
],
"reviewed": true,
"review_reason": "Agent-authored and reviewed from the built-in Shortcut intent, executable Cobra path, and declared outcome; it affects selection only and does not alter execution or safety facts.",
"source_refs": [
"internal/cli/schema_command_registry.json#attendance.shortcut_get_schedule",
"cobra-help:dws attendance +get-schedule",
"ShortcutRegistry:attendance +get-schedule"
]
},
"attendance.shortcut_search_class": {
"agent_summary": "查询当前用户可管理的班次详情列表",
"use_when": [
"当你要浏览或按名称查找当前用户能管理的班次、以便拿到班次 ID 用于建组、排班或改班次时使用;可选按班次名关键字模糊搜索、按 ALL/我负责的过滤并分页,返回班次列表及详情。要看某个具体班次的完整配置用 +get-class。"
],
"avoid_when": [
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
],
"examples": [
"dws attendance +search-class --query \"早班\" --filter-type MINE_OWN"
],
"reviewed": true,
"review_reason": "Agent-authored and reviewed from the built-in Shortcut intent, executable Cobra path, and declared outcome; it affects selection only and does not alter execution or safety facts.",
"source_refs": [
"internal/cli/schema_command_registry.json#attendance.shortcut_search_class",
"cobra-help:dws attendance +search-class",
"ShortcutRegistry:attendance +search-class"
]
},
"attendance.shortcut_get_adjustment_rule": {
"agent_summary": "根据补卡规则主键 ID 查询补卡规则详情",
"use_when": [
"当你已知某条补卡规则的主键 ID、想查看它的具体规则内容(每月可补卡次数、时限、适用范围等)时使用;输入 adjustmentId,返回该补卡规则的完整详情。不知道 ID 时先用 +search-adjustment-rule 列出。"
],
"avoid_when": [
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
],
"examples": [
"dws attendance +get-adjustment-rule --adjustment-id 12345"
],
"reviewed": true,
"review_reason": "Agent-authored and reviewed from the built-in Shortcut intent, executable Cobra path, and declared outcome; it affects selection only and does not alter execution or safety facts.",
"source_refs": [
"internal/cli/schema_command_registry.json#attendance.shortcut_get_adjustment_rule",
"cobra-help:dws attendance +get-adjustment-rule",
"ShortcutRegistry:attendance +get-adjustment-rule"
]
},
"attendance.shortcut_search_adjustment_rule": {
"agent_summary": "查询当前用户可管理的补卡规则列表",
"use_when": [
"当你要浏览或按名称查找当前用户可管理的补卡规则、以便拿到规则 ID 做进一步查看时使用;可选按规则名关键字模糊搜索并分页,返回补卡规则列表。要看某条规则的完整内容用 +get-adjustment-rule。"
],
"avoid_when": [
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
],
"examples": [
"dws attendance +search-adjustment-rule --query \"标准\" --page 1 --limit 50"
],
"reviewed": true,
"review_reason": "Agent-authored and reviewed from the built-in Shortcut intent, executable Cobra path, and declared outcome; it affects selection only and does not alter execution or safety facts.",
"source_refs": [
"internal/cli/schema_command_registry.json#attendance.shortcut_search_adjustment_rule",
"cobra-help:dws attendance +search-adjustment-rule",
"ShortcutRegistry:attendance +search-adjustment-rule"
]
},
"attendance.shortcut_get_overtime_rule": {
"agent_summary": "根据加班规则主键 ID 查询加班规则详情",
"use_when": [
"当你已知某条加班规则的主键 ID、想查看它的具体内容(工作日/休息日/节假日加班计算方式、适用范围等)时使用;输入 overtimeId,返回该加班规则的完整详情。不知道 ID 时先用 +search-overtime-rule 列出。"
],
"avoid_when": [
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
],
"examples": [
"dws attendance +get-overtime-rule --overtime-id 12345"
],
"reviewed": true,
"review_reason": "Agent-authored and reviewed from the built-in Shortcut intent, executable Cobra path, and declared outcome; it affects selection only and does not alter execution or safety facts.",
"source_refs": [
"internal/cli/schema_command_registry.json#attendance.shortcut_get_overtime_rule",
"cobra-help:dws attendance +get-overtime-rule",
"ShortcutRegistry:attendance +get-overtime-rule"
]
},
"attendance.shortcut_search_overtime_rule": {
"agent_summary": "查询当前用户可管理的加班规则列表",
"use_when": [
"当你要浏览或按名称查找当前用户可管理的加班规则、以便拿到规则 ID 做进一步查看时使用;可选按规则名关键字模糊搜索并分页,返回加班规则列表。要看某条规则的完整内容用 +get-overtime-rule。"
],
"avoid_when": [
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
],
"examples": [
"dws attendance +search-overtime-rule --query \"节假日\" --page 1 --limit 50"
],
"reviewed": true,
"review_reason": "Agent-authored and reviewed from the built-in Shortcut intent, executable Cobra path, and declared outcome; it affects selection only and does not alter execution or safety facts.",
"source_refs": [
"internal/cli/schema_command_registry.json#attendance.shortcut_search_overtime_rule",
"cobra-help:dws attendance +search-overtime-rule",
"ShortcutRegistry:attendance +search-overtime-rule"
]
},
"attendance.shortcut_search_group": {
"agent_summary": "查询当前用户可管理的考勤组列表",
"use_when": [
"当你要浏览或按名称查找当前用户可管理的考勤组、以便拿到考勤组 ID 用于查看详情、改成员或改配置时使用;可选按名称关键字、考勤组类型(固定班制/排班制/自由工时)过滤,并可选带出定位/Wifi/蓝牙信息,分页返回考勤组简要列表。"
],
"avoid_when": [
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
],
"examples": [
"dws attendance +search-group --query \"研发\" --type FIXED --limit 50"
],
"reviewed": true,
"review_reason": "Agent-authored and reviewed from the built-in Shortcut intent, executable Cobra path, and declared outcome; it affects selection only and does not alter execution or safety facts.",
"source_refs": [
"internal/cli/schema_command_registry.json#attendance.shortcut_search_group",
"cobra-help:dws attendance +search-group",
"ShortcutRegistry:attendance +search-group"
]
},
"attendance.shortcut_get_summary": {
"agent_summary": "查询某个人的考勤统计摘要(周/月)",
"use_when": [
"当你想快速了解某个人一周或一月的考勤汇总(出勤天数、迟到早退次数、加班、请假时长等统计口径)而不是逐天明细时使用;输入 userId、所在周期内的任意日期和统计类型(week/month),返回该周期的考勤统计摘要。"
],
"avoid_when": [
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
],
"examples": [
"dws attendance +get-summary --user USER_ID --date 2026-03-12 --stats-type week"
],
"reviewed": true,
"review_reason": "Agent-authored and reviewed from the built-in Shortcut intent, executable Cobra path, and declared outcome; it affects selection only and does not alter execution or safety facts.",
"source_refs": [
"internal/cli/schema_command_registry.json#attendance.shortcut_get_summary",
"cobra-help:dws attendance +get-summary",
"ShortcutRegistry:attendance +get-summary"
]
},
"attendance.shortcut_get_self_setting": {
"agent_summary": "查询个人规则设置(打卡提醒/极速打卡/缺卡提醒等)",
"use_when": [
"当你想查看某个用户在指定场景下的个人考勤规则开关配置(如打卡提醒、极速打卡、缺卡提醒、考勤结果通知等是否开启)时使用;输入设置场景 settingScene 和用户 userId,返回该用户对应场景的个人设置。查企业全局设置用 +get-global-setting。"
],
"avoid_when": [
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
],
"examples": [
"dws attendance +get-self-setting --setting-scene checkRemind --user USER_ID"
],
"reviewed": true,
"review_reason": "Agent-authored and reviewed from the built-in Shortcut intent, executable Cobra path, and declared outcome; it affects selection only and does not alter execution or safety facts.",
"source_refs": [
"internal/cli/schema_command_registry.json#attendance.shortcut_get_self_setting",
"cobra-help:dws attendance +get-self-setting",
"ShortcutRegistry:attendance +get-self-setting"
]
},
"attendance.shortcut_query_report_data": {
"agent_summary": "根据字段查询考勤报表数据(仅管理员)",
"use_when": [
"当你要按指定考勤字段导出/查询一批员工在某时间段的报表数值(如各人的出勤天数、迟到次数、加班时长)时使用,常用于做考勤汇总或核算;输入 userId 列表(最多 20 人)、字段 ID 列表(来自 +list-report-columns)和不超过 32 天的时间区间,返回对应字段的数据。仅管理员可用。"
],
"avoid_when": [
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
],
"examples": [
"dws attendance +query-report-data --users userId1,userId2 --columns 1001,1002 --start \"2026-03-01 00:00:00\" --end \"2026-03-31 23:59:59\""
],
"reviewed": true,
"review_reason": "Agent-authored and reviewed from the built-in Shortcut intent, executable Cobra path, and declared outcome; it affects selection only and does not alter execution or safety facts.",
"source_refs": [
"internal/cli/schema_command_registry.json#attendance.shortcut_query_report_data",
"cobra-help:dws attendance +query-report-data",
"ShortcutRegistry:attendance +query-report-data"
]
},
"attendance.shortcut_list_leave_types": {
"agent_summary": "查询当前用户可用的假期规则列表",
"use_when": [
"当你想知道企业有哪些假期类型(年假、事假、病假等)及其对应的假期编码 code、单位、是否带薪时使用,通常是查余额或改假期规则前先拿到 leaveCode;无需参数,返回当前用户可用的假期规则列表。"
],
"avoid_when": [
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
],
"examples": [
"dws attendance +list-leave-types"
],
"reviewed": true,
"review_reason": "Agent-authored and reviewed from the built-in Shortcut intent, executable Cobra path, and declared outcome; it affects selection only and does not alter execution or safety facts.",
"source_refs": [
"internal/cli/schema_command_registry.json#attendance.shortcut_list_leave_types",
"cobra-help:dws attendance +list-leave-types",
"ShortcutRegistry:attendance +list-leave-types"
]
},
"attendance.shortcut_get_leave_records": {
"agent_summary": "查询指定员工的假期余额变更记录",
"use_when": [
"当你想追溯某个员工假期额度的变动流水(何时发放、扣减、因请假消耗多少)以核对余额来龙去脉时使用;输入单个 userId、日期区间,可选假期 code(不传查所有假期),返回该员工在该时间段的余额变更记录。只想看当前余额用 +get-leave-balance。"
],
"avoid_when": [
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
],
"examples": [
"dws attendance +get-leave-records --user USER_ID --leave-code a1b2c3d4-e5f6-7890-abcd-ef1234567890 --start 2026-04-01 --end 2026-04-22"
],
"reviewed": true,
"review_reason": "Agent-authored and reviewed from the built-in Shortcut intent, executable Cobra path, and declared outcome; it affects selection only and does not alter execution or safety facts.",
"source_refs": [
"internal/cli/schema_command_registry.json#attendance.shortcut_get_leave_records",
"cobra-help:dws attendance +get-leave-records",
"ShortcutRegistry:attendance +get-leave-records"
]
},
"attendance.shortcut_get_checkin_record": {
"agent_summary": "查询指定员工一段时间内的签到记录",
"use_when": [
"当你要查看员工的外勤/移动办公签到记录(signin/checkin,区别于考勤打卡)时使用,例如核实业务员的拜访签到轨迹;需提供操作者企业 ID 与员工 ID、目标员工 ID 列表(最多 100 人)以及不超过 7 天的时间区间,返回这些人的签到明细。"
],
"avoid_when": [
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
],
"examples": [
"dws attendance +get-checkin-record --operator-corp-id dingXXX --operator-staff-id op001 --staff-ids user001,user002 --start \"2026-04-01 00:00:00\" --end \"2026-04-07 00:00:00\""
],
"reviewed": true,
"review_reason": "Agent-authored and reviewed from the built-in Shortcut intent, executable Cobra path, and declared outcome; it affects selection only and does not alter execution or safety facts.",
"source_refs": [
"internal/cli/schema_command_registry.json#attendance.shortcut_get_checkin_record",
"cobra-help:dws attendance +get-checkin-record",
"ShortcutRegistry:attendance +get-checkin-record"
]
},
"attendance.shortcut_my_attendance": {
"agent_summary": "查我今天的考勤打卡记录(打卡流水,自动解析当前用户)",
"use_when": [
"当你想快速看自己今天的打卡流水(几点上下班打卡、打卡地址/定位方式)、又不想先查自己的 userId 再手动填写今天的起止时间时使用;内部先取当前登录用户的 userId,再按本地时区算出今天 00:00 到次日 00:00 的时间窗,最后查询你今天的打卡流水记录。只读操作,不会修改任何考勤数据;今天若还没有任何打卡则返回空结果。"
],
"avoid_when": [
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
],
"examples": [
"dws attendance +my-attendance"
],
"reviewed": true,
"review_reason": "Agent-authored and reviewed from the built-in Shortcut intent, executable Cobra path, and declared outcome; it affects selection only and does not alter execution or safety facts.",
"source_refs": [
"internal/cli/schema_command_registry.json#attendance.shortcut_my_attendance",
"cobra-help:dws attendance +my-attendance",
"ShortcutRegistry:attendance +my-attendance"
]
},
"attendance.shortcut_this_month": {
"agent_summary": "查我本月的考勤打卡记录(打卡流水,自动解析当前用户)",
"use_when": [
"当你想快速看自己本月的打卡流水(几点上下班打卡、打卡地址/定位方式)、又不想先查自己的 userId 再手动填写本月的起止时间时使用;内部先取当前登录用户的 userId,再按本地时区算出本月 1 号 00:00 到下月 1 号 00:00 的时间窗,最后查询你本月的打卡流水记录。只读操作,不会修改任何考勤数据;本月若还没有任何打卡则返回空结果。"
],
"avoid_when": [
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
],
"examples": [
"dws attendance +this-month"
],
"reviewed": true,
"review_reason": "Agent-authored and reviewed from the built-in Shortcut intent, executable Cobra path, and declared outcome; it affects selection only and does not alter execution or safety facts.",
"source_refs": [
"internal/cli/schema_command_registry.json#attendance.shortcut_this_month",
"cobra-help:dws attendance +this-month",
"ShortcutRegistry:attendance +this-month"
]
}
},
"products": {
@@ -437,6 +437,393 @@
"skills/mono/references/products/calendar.md",
"live-dws-schema:calendar.update_calendar_event"
]
},
"calendar.shortcut_agenda": {
"agent_summary": "查询日程列表(不传时间默认查询今天)",
"use_when": [
"当你想了解某人(默认自己)在某段时间内的日程安排、看看今天/本周有哪些会时使用;可传 --start/--end 圈定时间范围、--calendar-id 指定日历,返回该区间内的日程列表(含日程 ID,可配合 +get 看详情)。"
],
"avoid_when": [
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
],
"examples": [
"dws calendar +agenda",
"dws calendar +agenda --start \"2026-03-10T00:00:00+08:00\" --end \"2026-03-31T23:59:59+08:00\" --limit 50"
],
"reviewed": true,
"review_reason": "Agent-authored and reviewed from the built-in Shortcut intent, executable Cobra path, and declared outcome; it affects selection only and does not alter execution or safety facts.",
"source_refs": [
"internal/cli/schema_command_registry.json#calendar.shortcut_agenda",
"cobra-help:dws calendar +agenda",
"ShortcutRegistry:calendar +agenda"
]
},
"calendar.shortcut_attendee_list": {
"agent_summary": "查看日程参会人",
"use_when": [
"当你想知道某个日程都有谁参加、各人的出席响应状态时使用;输入 --event 日程 ID,返回参会人列表(userId 及其接受/拒绝等状态)。"
],
"avoid_when": [
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
],
"examples": [
"dws calendar +attendee-list --event EVENT_ID"
],
"reviewed": true,
"review_reason": "Agent-authored and reviewed from the built-in Shortcut intent, executable Cobra path, and declared outcome; it affects selection only and does not alter execution or safety facts.",
"source_refs": [
"internal/cli/schema_command_registry.json#calendar.shortcut_attendee_list",
"cobra-help:dws calendar +attendee-list",
"ShortcutRegistry:calendar +attendee-list"
]
},
"calendar.shortcut_room_search": {
"agent_summary": "按名称模糊搜索会议室(不检查可用性)",
"use_when": [
"当你只知道会议室名字、想拿到它的 roomId 以便后续预定时使用;输入 --room-name 名称关键词(建议只填核心专名,去掉“会议室”等后缀),返回名称匹配的会议室列表。它只按名字找、不判断该时段是否空闲,查可用性请用 +room-find。"
],
"avoid_when": [
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
],
"examples": [
"dws calendar +room-search --room-name 永澄亭"
],
"reviewed": true,
"review_reason": "Agent-authored and reviewed from the built-in Shortcut intent, executable Cobra path, and declared outcome; it affects selection only and does not alter execution or safety facts.",
"source_refs": [
"internal/cli/schema_command_registry.json#calendar.shortcut_room_search",
"cobra-help:dws calendar +room-search",
"ShortcutRegistry:calendar +room-search"
]
},
"calendar.shortcut_room_groups": {
"agent_summary": "会议室分组列表",
"use_when": [
"当你想按楼层/园区等分组浏览会议室、或需要拿到 groupId 以便在 +room-find 里按分组过滤时使用;返回会议室分组列表,支持 --limit/--page 分页。"
],
"avoid_when": [
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
],
"examples": [
"dws calendar +room-groups"
],
"reviewed": true,
"review_reason": "Agent-authored and reviewed from the built-in Shortcut intent, executable Cobra path, and declared outcome; it affects selection only and does not alter execution or safety facts.",
"source_refs": [
"internal/cli/schema_command_registry.json#calendar.shortcut_room_groups",
"cobra-help:dws calendar +room-groups",
"ShortcutRegistry:calendar +room-groups"
]
},
"calendar.shortcut_freebusy": {
"agent_summary": "查询用户 / 会议室闲忙状态(--users 与 --rooms 至少其一)",
"use_when": [
"当你要在约会前确认某些人或会议室在指定时间段是否有空、避免冲突时使用;传入 --start/--end 时间段并至少给出 --users 或 --rooms 其一,返回各对象在该区间的忙/闲时段。只看忙闲结果、不看具体日程内容,需要系统给出建议时段可用 +suggestion。"
],
"avoid_when": [
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
],
"examples": [
"dws calendar +freebusy --users userId1,userId2 --start \"2026-03-10T14:00:00+08:00\" --end \"2026-03-10T18:00:00+08:00\""
],
"reviewed": true,
"review_reason": "Agent-authored and reviewed from the built-in Shortcut intent, executable Cobra path, and declared outcome; it affects selection only and does not alter execution or safety facts.",
"source_refs": [
"internal/cli/schema_command_registry.json#calendar.shortcut_freebusy",
"cobra-help:dws calendar +freebusy",
"ShortcutRegistry:calendar +freebusy"
]
},
"calendar.shortcut_book_list": {
"agent_summary": "查询用户的日历本列表",
"use_when": [
"当你想知道自己有哪些日历本(主日历、项目日历、订阅日历等)、或需要拿到某个日历的 calendarId 以便在 +agenda/+create 中指定时使用;无需参数,返回全部日历本列表。"
],
"avoid_when": [
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
],
"examples": [
"dws calendar +book-list"
],
"reviewed": true,
"review_reason": "Agent-authored and reviewed from the built-in Shortcut intent, executable Cobra path, and declared outcome; it affects selection only and does not alter execution or safety facts.",
"source_refs": [
"internal/cli/schema_command_registry.json#calendar.shortcut_book_list",
"cobra-help:dws calendar +book-list",
"ShortcutRegistry:calendar +book-list"
]
},
"calendar.shortcut_book_search": {
"agent_summary": "按名称模糊搜索日历本",
"use_when": [
"当你只记得日历本名字的一部分、想据此找到对应的 calendarId 时使用;输入 --query 名称关键词,返回名称匹配的日历本列表,便于后续在其它命令里指定 --calendar-id。"
],
"avoid_when": [
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
],
"examples": [
"dws calendar +book-search --query \"项目\""
],
"reviewed": true,
"review_reason": "Agent-authored and reviewed from the built-in Shortcut intent, executable Cobra path, and declared outcome; it affects selection only and does not alter execution or safety facts.",
"source_refs": [
"internal/cli/schema_command_registry.json#calendar.shortcut_book_search",
"cobra-help:dws calendar +book-search",
"ShortcutRegistry:calendar +book-search"
]
},
"calendar.shortcut_book": {
"agent_summary": "创建日程,并可按姓名邀请参会人(自动解析 userId,失败自动回滚删除日程)",
"use_when": [
"当你想快速排一个会/日程、并顺手把几位同事按姓名拉进来时使用;内部先建日程拿到 eventId,再把每个姓名解析成唯一 userId 批量加为参会人。如果加参会人失败,会自动删除刚建好的日程回滚,避免留下一个没人的空日程。会真实创建日程并发出参会邀请。"
],
"avoid_when": [
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
],
"examples": [
"dws calendar +book --title \"周会\" --start \"2026-03-10T14:00:00+08:00\" --end \"2026-03-10T15:00:00+08:00\"",
"dws calendar +book --title \"Q1 复盘会\" --start \"2026-03-10T14:00:00+08:00\" --end \"2026-03-10T15:00:00+08:00\" --with 张三,李四"
],
"reviewed": true,
"review_reason": "Agent-authored and reviewed from the built-in Shortcut intent, executable Cobra path, and declared outcome; it affects selection only and does not alter execution or safety facts.",
"source_refs": [
"internal/cli/schema_command_registry.json#calendar.shortcut_book",
"cobra-help:dws calendar +book",
"ShortcutRegistry:calendar +book"
]
},
"calendar.shortcut_cancel_event": {
"agent_summary": "取消(删除)一个已有日程(删除前先确认它真实存在)",
"use_when": [
"当你想取消/删除一个已经存在的日程时使用;内部先用 eventId 拉一次日程详情确认它真实存在并回显标题,再执行删除,避免因 eventId 写错而误删别的日程。如果 eventId 查不到会直接报错,不会盲目删除。这是高危写操作,会真实删除该日程,框架会二次确认。"
],
"avoid_when": [
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
],
"examples": [
"dws calendar +cancel-event --event EVENT_ID"
],
"reviewed": true,
"review_reason": "Agent-authored and reviewed from the built-in Shortcut intent, executable Cobra path, and declared outcome; it affects selection only and does not alter execution or safety facts.",
"source_refs": [
"internal/cli/schema_command_registry.json#calendar.shortcut_cancel_event",
"cobra-help:dws calendar +cancel-event",
"ShortcutRegistry:calendar +cancel-event"
]
},
"calendar.shortcut_conflicts": {
"agent_summary": "检测我某天日程的时间冲突(重叠/双重预订,默认今天)",
"use_when": [
"当你想快速知道『我今天(或某天)的日程有没有时间冲突、撞车的会议』时使用;内部自动算出目标日期的时间范围(默认今天,可用 --in-days 指定几天后),列出当天全部日程,再在本地两两比对开始/结束时间,找出所有时间段重叠的日程对并报告。只读操作,不修改任何日程;没有冲突时明确告诉你「无冲突」。"
],
"avoid_when": [
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
],
"examples": [
"dws calendar +conflicts",
"dws calendar +conflicts --in-days 1"
],
"reviewed": true,
"review_reason": "Agent-authored and reviewed from the built-in Shortcut intent, executable Cobra path, and declared outcome; it affects selection only and does not alter execution or safety facts.",
"source_refs": [
"internal/cli/schema_command_registry.json#calendar.shortcut_conflicts",
"cobra-help:dws calendar +conflicts",
"ShortcutRegistry:calendar +conflicts"
]
},
"calendar.shortcut_free_slots": {
"agent_summary": "找我某天工作时段内的空闲时间段(默认今天 09:00-18:00)",
"use_when": [
"当你想知道『我今天(或某天)还有哪些时间是空的、可以安排会议/事情』时使用;内部列出目标日期(默认今天,--in-days 指定几天后)的全部日程,合并忙碌时段,再在工作时间范围内(默认 09:00-18:00,可用 --from/--to 指定起止小时)算出所有空闲窗口并给出每段的起止与时长。只读操作,不修改任何日程;用于快速定位可预约的空档。"
],
"avoid_when": [
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
],
"examples": [
"dws calendar +free-slots",
"dws calendar +free-slots --in-days 1 --from 9 --to 20"
],
"reviewed": true,
"review_reason": "Agent-authored and reviewed from the built-in Shortcut intent, executable Cobra path, and declared outcome; it affects selection only and does not alter execution or safety facts.",
"source_refs": [
"internal/cli/schema_command_registry.json#calendar.shortcut_free_slots",
"cobra-help:dws calendar +free-slots",
"ShortcutRegistry:calendar +free-slots"
]
},
"calendar.shortcut_free": {
"agent_summary": "按姓名查询某人在指定时间段内的忙闲状态(自动解析 userId)",
"use_when": [
"当你只知道对方姓名、想知道 TA 在某段时间内是空闲还是被日程占用(比如约会前先看看有没有空)而不想先手动查 userId 时使用;内部先按姓名搜通讯录解析出唯一 userId,姓名匹配到多人时会列出候选让你区分,再按时间范围查询忙闲。只读,不产生任何日程变更。"
],
"avoid_when": [
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
],
"examples": [
"dws calendar +free --who 张三 --start \"2026-03-10T14:00:00+08:00\" --end \"2026-03-10T18:00:00+08:00\""
],
"reviewed": true,
"review_reason": "Agent-authored and reviewed from the built-in Shortcut intent, executable Cobra path, and declared outcome; it affects selection only and does not alter execution or safety facts.",
"source_refs": [
"internal/cli/schema_command_registry.json#calendar.shortcut_free",
"cobra-help:dws calendar +free",
"ShortcutRegistry:calendar +free"
]
},
"calendar.shortcut_invite": {
"agent_summary": "按姓名把参会人加入已有日程(自动解析 userId 后批量添加)",
"use_when": [
"当你已经有一个日程(知道 eventId),想按姓名把几位同事拉进来当参会人时使用;内部先把 --with 里每个姓名解析成唯一 userId,再一次性把他们全部加到 --event 指定的日程里。会真实修改日程并发出参会邀请。"
],
"avoid_when": [
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
],
"examples": [
"dws calendar +invite --event EVENT_ID --with 张三",
"dws calendar +invite --event EVENT_ID --with 张三,李四"
],
"reviewed": true,
"review_reason": "Agent-authored and reviewed from the built-in Shortcut intent, executable Cobra path, and declared outcome; it affects selection only and does not alter execution or safety facts.",
"source_refs": [
"internal/cli/schema_command_registry.json#calendar.shortcut_invite",
"cobra-help:dws calendar +invite",
"ShortcutRegistry:calendar +invite"
]
},
"calendar.shortcut_my_free": {
"agent_summary": "查我自己在某时间段的忙闲(默认今天,无需输入姓名)",
"use_when": [
"当你(或 AI agent)想知道『我自己什么时候有空/忙』、用于安排会议或回复邀约时使用;不用像 +free 那样传别人的姓名——内部自动解析当前用户的 userId,再查其忙闲时段。默认查今天(本地时区 00:00 到次日 00:00),也可用 --start/--end 指定 ISO8601 时间范围。只读操作,只查忙闲、不创建或修改任何日程;返回按时间排列的忙碌时段,空则表示这段时间全空。"
],
"avoid_when": [
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
],
"examples": [
"dws calendar +my-free",
"dws calendar +my-free --start 2026-07-10T09:00:00+08:00 --end 2026-07-10T18:00:00+08:00"
],
"reviewed": true,
"review_reason": "Agent-authored and reviewed from the built-in Shortcut intent, executable Cobra path, and declared outcome; it affects selection only and does not alter execution or safety facts.",
"source_refs": [
"internal/cli/schema_command_registry.json#calendar.shortcut_my_free",
"cobra-help:dws calendar +my-free",
"ShortcutRegistry:calendar +my-free"
]
},
"calendar.shortcut_next_event": {
"agent_summary": "查看接下来最近的一个日程(默认扫描未来 7 天)",
"use_when": [
"当你只想知道『我下一个日程是什么、什么时候开始』、而不想翻一整份日程列表时使用;内部以当前时间为起点、往后 7 天为范围,拉取主日历下的日程,按开始时间升序挑出最近的那一个并打印摘要(标题、开始/结束时间、地点)。若这 7 天内没有任何日程,会明确提示『近 7 天无日程』。只读,不做任何修改。"
],
"avoid_when": [
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
],
"examples": [
"dws calendar +next-event"
],
"reviewed": true,
"review_reason": "Agent-authored and reviewed from the built-in Shortcut intent, executable Cobra path, and declared outcome; it affects selection only and does not alter execution or safety facts.",
"source_refs": [
"internal/cli/schema_command_registry.json#calendar.shortcut_next_event",
"cobra-help:dws calendar +next-event",
"ShortcutRegistry:calendar +next-event"
]
},
"calendar.shortcut_reschedule": {
"agent_summary": "改一个已有日程的时间(只动开始/结束时间,其他字段不变)",
"use_when": [
"当你想把一个已经存在的日程改到新的时间段、又不想动标题/描述/参会人等其他内容时使用;内部先用 eventId 拉一次日程详情确认它真实存在,再只更新开始和结束时间。如果 eventId 查不到会直接报错,不会误改别的日程。会真实修改该日程的时间。"
],
"avoid_when": [
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
],
"examples": [
"dws calendar +reschedule --event EVENT_ID --start \"2026-03-10T15:00:00+08:00\" --end \"2026-03-10T16:00:00+08:00\""
],
"reviewed": true,
"review_reason": "Agent-authored and reviewed from the built-in Shortcut intent, executable Cobra path, and declared outcome; it affects selection only and does not alter execution or safety facts.",
"source_refs": [
"internal/cli/schema_command_registry.json#calendar.shortcut_reschedule",
"cobra-help:dws calendar +reschedule",
"ShortcutRegistry:calendar +reschedule"
]
},
"calendar.shortcut_suggest_time": {
"agent_summary": "按姓名解析多位参与者,推荐大家都有空的可开会时间段(自动解析 userId)",
"use_when": [
"当你想为几个人凑一个大家都空闲的开会时间、但只知道他们的姓名而不想逐个手动查 userId 时使用;内部会把 --with 里的姓名逐个搜通讯录解析成唯一 userId(任何一个没匹配到或匹配到多人都会明确报出来,绝不瞎猜),再基于所有参与者的忙闲,在给定时间范围内推荐若干可用时段。只读,不创建任何日程。"
],
"avoid_when": [
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
],
"examples": [
"dws calendar +suggest-time --with 张三,李四 --start \"2026-03-10T09:00:00+08:00\" --end \"2026-03-10T18:00:00+08:00\"",
"dws calendar +suggest-time --with 张三,李四,王五 --start \"2026-03-10T09:00:00+08:00\" --end \"2026-03-10T18:00:00+08:00\" --duration 30"
],
"reviewed": true,
"review_reason": "Agent-authored and reviewed from the built-in Shortcut intent, executable Cobra path, and declared outcome; it affects selection only and does not alter execution or safety facts.",
"source_refs": [
"internal/cli/schema_command_registry.json#calendar.shortcut_suggest_time",
"cobra-help:dws calendar +suggest-time",
"ShortcutRegistry:calendar +suggest-time"
]
},
"calendar.shortcut_today": {
"agent_summary": "列出我今天的日程(自动计算今天的起止时间,无需手动填时间范围)",
"use_when": [
"当你想快速看看『我今天有哪些日程/会议安排』时使用;内部用本地时区自动把时间范围算成今天 00:00 到次日 00:00,转成毫秒时间戳,查询主日历(primary)下今天的全部日程。只读,不会创建或修改任何日程。"
],
"avoid_when": [
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
],
"examples": [
"dws calendar +today"
],
"reviewed": true,
"review_reason": "Agent-authored and reviewed from the built-in Shortcut intent, executable Cobra path, and declared outcome; it affects selection only and does not alter execution or safety facts.",
"source_refs": [
"internal/cli/schema_command_registry.json#calendar.shortcut_today",
"cobra-help:dws calendar +today",
"ShortcutRegistry:calendar +today"
]
},
"calendar.shortcut_tomorrow": {
"agent_summary": "列出我明天的日程(自动计算明天的起止时间,无需手动填时间范围)",
"use_when": [
"当你想快速看看『我明天有哪些日程/会议安排』、提前准备时使用;内部用本地时区自动把时间范围算成明天 00:00 到后天 00:00,转成毫秒时间戳,查询主日历(primary)下明天的全部日程,并投影出标题、开始时间、结束时间、地点、eventId。只读,不会创建或修改任何日程。"
],
"avoid_when": [
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
],
"examples": [
"dws calendar +tomorrow"
],
"reviewed": true,
"review_reason": "Agent-authored and reviewed from the built-in Shortcut intent, executable Cobra path, and declared outcome; it affects selection only and does not alter execution or safety facts.",
"source_refs": [
"internal/cli/schema_command_registry.json#calendar.shortcut_tomorrow",
"cobra-help:dws calendar +tomorrow",
"ShortcutRegistry:calendar +tomorrow"
]
},
"calendar.shortcut_week": {
"agent_summary": "列出我本周的日程(自动按周一为周首计算本周起止时间,无需手动填时间范围)",
"use_when": [
"当你想快速看看『我本周有哪些日程/会议安排』时使用;内部用本地时区、以周一为一周开始,自动把时间范围算成本周一 00:00 到下周一 00:00,转成毫秒时间戳,查询主日历(primary)下本周的全部日程,并投影出标题、开始时间、结束时间、eventId。只读,不会创建或修改任何日程。"
],
"avoid_when": [
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
],
"examples": [
"dws calendar +week"
],
"reviewed": true,
"review_reason": "Agent-authored and reviewed from the built-in Shortcut intent, executable Cobra path, and declared outcome; it affects selection only and does not alter execution or safety facts.",
"source_refs": [
"internal/cli/schema_command_registry.json#calendar.shortcut_week",
"cobra-help:dws calendar +week",
"ShortcutRegistry:calendar +week"
]
}
},
"products": {
@@ -1595,6 +1595,807 @@
"Wukong-parity:3306c3307",
"live-dws-schema:chat.upgrade_group_to_external#FAILED"
]
},
"chat.shortcut_bot_search": {
"agent_summary": "搜索当前用户自己创建的机器人",
"use_when": [
"当你要管理或复用自己创建的机器人(比如查到其 robotCode 以便让它进群或发消息)时使用;按机器人名称模糊搜索,只返回当前用户名下创建的机器人列表。"
],
"avoid_when": [
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
],
"examples": [
"dws chat +bot-search --page 1 --name \"日报\""
],
"reviewed": true,
"review_reason": "Agent-authored and reviewed from the built-in Shortcut intent, executable Cobra path, and declared outcome; it affects selection only and does not alter execution or safety facts.",
"source_refs": [
"internal/cli/schema_command_registry.json#chat.shortcut_bot_search",
"cobra-help:dws chat +bot-search",
"ShortcutRegistry:chat +bot-search"
]
},
"chat.shortcut_bot_find": {
"agent_summary": "搜索全部可用机器人(含他人/官方,返回 openDingTalkId 可发单聊)",
"use_when": [
"当你想找到平台上任意可用机器人(含他人创建或官方助手,例如某个日报/审批机器人)以便与其发起单聊时使用;输入关键词,返回含 openDingTalkId 的机器人列表。"
],
"avoid_when": [
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
],
"examples": [
"dws chat +bot-find --query \"日报\""
],
"reviewed": true,
"review_reason": "Agent-authored and reviewed from the built-in Shortcut intent, executable Cobra path, and declared outcome; it affects selection only and does not alter execution or safety facts.",
"source_refs": [
"internal/cli/schema_command_registry.json#chat.shortcut_bot_find",
"cobra-help:dws chat +bot-find",
"ShortcutRegistry:chat +bot-find"
]
},
"chat.shortcut_conversation_info": {
"agent_summary": "获取会话信息(群聊传 --group,单聊传 --open-dingtalk-id)",
"use_when": [
"当你已有群 openConversationId 或单聊对方 openDingTalkId、需要查看该会话的名称/类型/成员数等基础信息时使用;只读,群聊传 --group、单聊传 --open-dingtalk-id 二选一。"
],
"avoid_when": [
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
],
"examples": [
"dws chat +conversation-info --group <openConversationId>"
],
"reviewed": true,
"review_reason": "Agent-authored and reviewed from the built-in Shortcut intent, executable Cobra path, and declared outcome; it affects selection only and does not alter execution or safety facts.",
"source_refs": [
"internal/cli/schema_command_registry.json#chat.shortcut_conversation_info",
"cobra-help:dws chat +conversation-info",
"ShortcutRegistry:chat +conversation-info"
]
},
"chat.shortcut_conversation_clear_all_red_point": {
"agent_summary": "清除所有会话红点(全部已读)",
"use_when": [
"当你想一键把全部会话标记为已读、清空所有红点时使用;会实际清除当前用户所有会话的红点,无需任何参数。"
],
"avoid_when": [
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
],
"examples": [
"dws chat +conversation-clear-all-red-point"
],
"reviewed": true,
"review_reason": "Agent-authored and reviewed from the built-in Shortcut intent, executable Cobra path, and declared outcome; it affects selection only and does not alter execution or safety facts.",
"source_refs": [
"internal/cli/schema_command_registry.json#chat.shortcut_conversation_clear_all_red_point",
"cobra-help:dws chat +conversation-clear-all-red-point",
"ShortcutRegistry:chat +conversation-clear-all-red-point"
]
},
"chat.shortcut_conversation_list": {
"agent_summary": "分页获取当前用户的全部会话列表(单聊+群聊)",
"use_when": [
"当你想遍历当前用户的所有会话(单聊+群聊)做统计、清理或批量处理时使用;只读分页返回,可用 --exclude-muted 排除已免打扰会话。"
],
"avoid_when": [
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
],
"examples": [
"dws chat +conversation-list --limit 50"
],
"reviewed": true,
"review_reason": "Agent-authored and reviewed from the built-in Shortcut intent, executable Cobra path, and declared outcome; it affects selection only and does not alter execution or safety facts.",
"source_refs": [
"internal/cli/schema_command_registry.json#chat.shortcut_conversation_list",
"cobra-help:dws chat +conversation-list",
"ShortcutRegistry:chat +conversation-list"
]
},
"chat.shortcut_conversation_list_top": {
"agent_summary": "拉取置顶会话列表",
"use_when": [
"当你只想查看被置顶的那些会话时使用;只读分页返回置顶会话列表,可用 --exclude-muted 排除已免打扰会话。"
],
"avoid_when": [
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
],
"examples": [
"dws chat +conversation-list-top --limit 1000"
],
"reviewed": true,
"review_reason": "Agent-authored and reviewed from the built-in Shortcut intent, executable Cobra path, and declared outcome; it affects selection only and does not alter execution or safety facts.",
"source_refs": [
"internal/cli/schema_command_registry.json#chat.shortcut_conversation_list_top",
"cobra-help:dws chat +conversation-list-top",
"ShortcutRegistry:chat +conversation-list-top"
]
},
"chat.shortcut_category_list": {
"agent_summary": "获取用户自定义会话分组",
"use_when": [
"当你想查看当前用户自建了哪些会话分组(如'工作群''项目群')时使用;只读返回分组列表及其 categoryId,供后续按分组拉会话或增删。"
],
"avoid_when": [
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
],
"examples": [
"dws chat +category-list"
],
"reviewed": true,
"review_reason": "Agent-authored and reviewed from the built-in Shortcut intent, executable Cobra path, and declared outcome; it affects selection only and does not alter execution or safety facts.",
"source_refs": [
"internal/cli/schema_command_registry.json#chat.shortcut_category_list",
"cobra-help:dws chat +category-list",
"ShortcutRegistry:chat +category-list"
]
},
"chat.shortcut_category_create": {
"agent_summary": "创建用户自定义会话分组",
"use_when": [
"当你想新建一个会话分组来归类会话时使用;会实际创建分组并返回其 ID,需传分组名称 --title。"
],
"avoid_when": [
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
],
"examples": [
"dws chat +category-create --title \"工作群\""
],
"reviewed": true,
"review_reason": "Agent-authored and reviewed from the built-in Shortcut intent, executable Cobra path, and declared outcome; it affects selection only and does not alter execution or safety facts.",
"source_refs": [
"internal/cli/schema_command_registry.json#chat.shortcut_category_create",
"cobra-help:dws chat +category-create",
"ShortcutRegistry:chat +category-create"
]
},
"chat.shortcut_category_delete": {
"agent_summary": "删除用户自定义会话分组",
"use_when": [
"当你想删除某个自定义会话分组时使用;会实际删除分组(不影响其中会话本身),不可逆,需传 categoryId。"
],
"avoid_when": [
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
],
"examples": [
"dws chat +category-delete --category-id <分组ID>"
],
"reviewed": true,
"review_reason": "Agent-authored and reviewed from the built-in Shortcut intent, executable Cobra path, and declared outcome; it affects selection only and does not alter execution or safety facts.",
"source_refs": [
"internal/cli/schema_command_registry.json#chat.shortcut_category_delete",
"cobra-help:dws chat +category-delete",
"ShortcutRegistry:chat +category-delete"
]
},
"chat.shortcut_category_rename": {
"agent_summary": "更新用户自定义会话分组的名称",
"use_when": [
"当你想重命名已有的自定义会话分组时使用;会实际更新分组名称,需传 categoryId 和新名称 --title。"
],
"avoid_when": [
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
],
"examples": [
"dws chat +category-rename --category-id <分组ID> --title \"新名称\""
],
"reviewed": true,
"review_reason": "Agent-authored and reviewed from the built-in Shortcut intent, executable Cobra path, and declared outcome; it affects selection only and does not alter execution or safety facts.",
"source_refs": [
"internal/cli/schema_command_registry.json#chat.shortcut_category_rename",
"cobra-help:dws chat +category-rename",
"ShortcutRegistry:chat +category-rename"
]
},
"chat.shortcut_chat_search": {
"agent_summary": "按关键词搜索群聊",
"use_when": [
"当你只记得群名称关键词、需要拿到群 openConversationId 以便发消息或管理该群时使用;按群名模糊搜索,只读分页返回匹配的群列表。"
],
"avoid_when": [
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
],
"examples": [
"dws chat +chat-search --query \"项目冲刺\""
],
"reviewed": true,
"review_reason": "Agent-authored and reviewed from the built-in Shortcut intent, executable Cobra path, and declared outcome; it affects selection only and does not alter execution or safety facts.",
"source_refs": [
"internal/cli/schema_command_registry.json#chat.shortcut_chat_search",
"cobra-help:dws chat +chat-search",
"ShortcutRegistry:chat +chat-search"
]
},
"chat.shortcut_chat_invite_url": {
"agent_summary": "获取群邀请链接",
"use_when": [
"当你想拿到一条群邀请链接分享给别人加群时使用;只读生成链接,需传群 openConversationId,可用 --expires-seconds 设置有效期(0 表示永久)。"
],
"avoid_when": [
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
],
"examples": [
"dws chat +chat-invite-url --group <openConversationId>"
],
"reviewed": true,
"review_reason": "Agent-authored and reviewed from the built-in Shortcut intent, executable Cobra path, and declared outcome; it affects selection only and does not alter execution or safety facts.",
"source_refs": [
"internal/cli/schema_command_registry.json#chat.shortcut_chat_invite_url",
"cobra-help:dws chat +chat-invite-url",
"ShortcutRegistry:chat +chat-invite-url"
]
},
"chat.shortcut_chat_dismiss": {
"agent_summary": "解散群聊(不可逆,需群主权限)",
"use_when": [
"当你要彻底解散一个群时使用;会实际销毁群聊,不可逆且需群主权限,仅需传群 openConversationId,操作前务必确认。"
],
"avoid_when": [
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
],
"examples": [
"dws chat +chat-dismiss --group <openConversationId>"
],
"reviewed": true,
"review_reason": "Agent-authored and reviewed from the built-in Shortcut intent, executable Cobra path, and declared outcome; it affects selection only and does not alter execution or safety facts.",
"source_refs": [
"internal/cli/schema_command_registry.json#chat.shortcut_chat_dismiss",
"cobra-help:dws chat +chat-dismiss",
"ShortcutRegistry:chat +chat-dismiss"
]
},
"chat.shortcut_chat_set_history": {
"agent_summary": "设置新成员入群可查看历史消息范围",
"use_when": [
"当你想控制新成员入群后能看到多少历史消息时使用;会实际修改群配置,需传群 openConversationId 和范围(FORBIDDEN 不可见 / RECENT_100 最近100条 / ALL 全部)。"
],
"avoid_when": [
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
],
"examples": [
"dws chat +chat-set-history --group <openConversationId> --option RECENT_100"
],
"reviewed": true,
"review_reason": "Agent-authored and reviewed from the built-in Shortcut intent, executable Cobra path, and declared outcome; it affects selection only and does not alter execution or safety facts.",
"source_refs": [
"internal/cli/schema_command_registry.json#chat.shortcut_chat_set_history",
"cobra-help:dws chat +chat-set-history",
"ShortcutRegistry:chat +chat-set-history"
]
},
"chat.shortcut_chat_update_nick": {
"agent_summary": "设置当前用户在群内的群昵称",
"use_when": [
"当你想设置当前用户在某个群里显示的群昵称时使用;会实际更新本人在该群的昵称,需传群 openConversationId 和昵称。"
],
"avoid_when": [
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
],
"examples": [
"dws chat +chat-update-nick --group <openConversationId> --nick \"我的群昵称\""
],
"reviewed": true,
"review_reason": "Agent-authored and reviewed from the built-in Shortcut intent, executable Cobra path, and declared outcome; it affects selection only and does not alter execution or safety facts.",
"source_refs": [
"internal/cli/schema_command_registry.json#chat.shortcut_chat_update_nick",
"cobra-help:dws chat +chat-update-nick",
"ShortcutRegistry:chat +chat-update-nick"
]
},
"chat.shortcut_chat_update_alias": {
"agent_summary": "设置群备注(仅自己可见)",
"use_when": [
"当你想给某个群设置仅自己可见的备注名以便区分同名群时使用;会实际保存本人对该群的备注,需传群 openConversationId 和备注标题。"
],
"avoid_when": [
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
],
"examples": [
"dws chat +chat-update-alias --group <openConversationId> --alias-title \"项目A群\""
],
"reviewed": true,
"review_reason": "Agent-authored and reviewed from the built-in Shortcut intent, executable Cobra path, and declared outcome; it affects selection only and does not alter execution or safety facts.",
"source_refs": [
"internal/cli/schema_command_registry.json#chat.shortcut_chat_update_alias",
"cobra-help:dws chat +chat-update-alias",
"ShortcutRegistry:chat +chat-update-alias"
]
},
"chat.shortcut_chat_list_mine": {
"agent_summary": "拉取我创建/管理的群",
"use_when": [
"当你想查看自己作为群主或管理员在管理哪些群时使用;只读分页返回,可用 --role OWNER/ADMIN 按角色过滤。"
],
"avoid_when": [
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
],
"examples": [
"dws chat +chat-list-mine --role OWNER"
],
"reviewed": true,
"review_reason": "Agent-authored and reviewed from the built-in Shortcut intent, executable Cobra path, and declared outcome; it affects selection only and does not alter execution or safety facts.",
"source_refs": [
"internal/cli/schema_command_registry.json#chat.shortcut_chat_list_mine",
"cobra-help:dws chat +chat-list-mine",
"ShortcutRegistry:chat +chat-list-mine"
]
},
"chat.shortcut_chat_list_all": {
"agent_summary": "分页拉取我加入的所有群列表",
"use_when": [
"当你想遍历当前用户加入的所有群做统计或批量操作时使用;只读分页返回全部已加入的群列表。"
],
"avoid_when": [
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
],
"examples": [
"dws chat +chat-list-all --limit 50"
],
"reviewed": true,
"review_reason": "Agent-authored and reviewed from the built-in Shortcut intent, executable Cobra path, and declared outcome; it affects selection only and does not alter execution or safety facts.",
"source_refs": [
"internal/cli/schema_command_registry.json#chat.shortcut_chat_list_all",
"cobra-help:dws chat +chat-list-all",
"ShortcutRegistry:chat +chat-list-all"
]
},
"chat.shortcut_chat_list_join_requests": {
"agent_summary": "分页拉取入群验证记录",
"use_when": [
"当你作为群主/管理员想查看待处理的入群申请时使用;只读分页返回入群验证记录(含 recordId、申请人与邀请人 ID),供后续用 chat-audit-join 审批。"
],
"avoid_when": [
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
],
"examples": [
"dws chat +chat-list-join-requests --limit 30"
],
"reviewed": true,
"review_reason": "Agent-authored and reviewed from the built-in Shortcut intent, executable Cobra path, and declared outcome; it affects selection only and does not alter execution or safety facts.",
"source_refs": [
"internal/cli/schema_command_registry.json#chat.shortcut_chat_list_join_requests",
"cobra-help:dws chat +chat-list-join-requests",
"ShortcutRegistry:chat +chat-list-join-requests"
]
},
"chat.shortcut_chat_bots": {
"agent_summary": "查看群内所有机器人",
"use_when": [
"当你想查看某个群里已添加了哪些机器人时使用;需传群 openConversationId,只读返回群内机器人列表(含 openBotId,供后续移除)。"
],
"avoid_when": [
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
],
"examples": [
"dws chat +chat-bots --group <openConversationId>"
],
"reviewed": true,
"review_reason": "Agent-authored and reviewed from the built-in Shortcut intent, executable Cobra path, and declared outcome; it affects selection only and does not alter execution or safety facts.",
"source_refs": [
"internal/cli/schema_command_registry.json#chat.shortcut_chat_bots",
"cobra-help:dws chat +chat-bots",
"ShortcutRegistry:chat +chat-bots"
]
},
"chat.shortcut_chat_set_admin": {
"agent_summary": "设置 / 取消群管理员",
"use_when": [
"当你想把某些成员设为或取消群管理员时使用;会实际变更成员角色,需传群 openConversationId 和成员 userId/openDingTalkId 列表,加 --off 取消管理员。"
],
"avoid_when": [
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
],
"examples": [
"dws chat +chat-set-admin --group <openConversationId> --users userId1,userId2"
],
"reviewed": true,
"review_reason": "Agent-authored and reviewed from the built-in Shortcut intent, executable Cobra path, and declared outcome; it affects selection only and does not alter execution or safety facts.",
"source_refs": [
"internal/cli/schema_command_registry.json#chat.shortcut_chat_set_admin",
"cobra-help:dws chat +chat-set-admin",
"ShortcutRegistry:chat +chat-set-admin"
]
},
"chat.shortcut_chat_mute": {
"agent_summary": "全员禁言 / 取消全员禁言",
"use_when": [
"当你想对整个群开启或取消全员禁言时使用;会实际切换群的全员禁言状态,需传群 openConversationId,加 --off 取消禁言。"
],
"avoid_when": [
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
],
"examples": [
"dws chat +chat-mute --group <openConversationId>"
],
"reviewed": true,
"review_reason": "Agent-authored and reviewed from the built-in Shortcut intent, executable Cobra path, and declared outcome; it affects selection only and does not alter execution or safety facts.",
"source_refs": [
"internal/cli/schema_command_registry.json#chat.shortcut_chat_mute",
"cobra-help:dws chat +chat-mute",
"ShortcutRegistry:chat +chat-mute"
]
},
"chat.shortcut_chat_role_list": {
"agent_summary": "拉取会话的群身份列表",
"use_when": [
"当你想查看某群自定义的群身份(如'班长''值日')都有哪些时使用;需传群 openConversationId,只读返回群身份列表及 openRoleId。"
],
"avoid_when": [
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
],
"examples": [
"dws chat +chat-role-list --group <openConversationId>"
],
"reviewed": true,
"review_reason": "Agent-authored and reviewed from the built-in Shortcut intent, executable Cobra path, and declared outcome; it affects selection only and does not alter execution or safety facts.",
"source_refs": [
"internal/cli/schema_command_registry.json#chat.shortcut_chat_role_list",
"cobra-help:dws chat +chat-role-list",
"ShortcutRegistry:chat +chat-role-list"
]
},
"chat.shortcut_chat_role_add": {
"agent_summary": "添加群身份",
"use_when": [
"当你想在群里新增一个自定义群身份/头衔时使用;会实际创建群身份,需传群 openConversationId 和身份名称。"
],
"avoid_when": [
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
],
"examples": [
"dws chat +chat-role-add --group <openConversationId> --name \"管理员\""
],
"reviewed": true,
"review_reason": "Agent-authored and reviewed from the built-in Shortcut intent, executable Cobra path, and declared outcome; it affects selection only and does not alter execution or safety facts.",
"source_refs": [
"internal/cli/schema_command_registry.json#chat.shortcut_chat_role_add",
"cobra-help:dws chat +chat-role-add",
"ShortcutRegistry:chat +chat-role-add"
]
},
"chat.shortcut_chat_role_update": {
"agent_summary": "更新群身份名称",
"use_when": [
"当你想重命名已有的群身份时使用;会实际更新身份名称,需传群 openConversationId、身份 openRoleId 和新名称。"
],
"avoid_when": [
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
],
"examples": [
"dws chat +chat-role-update --group <openConversationId> --role-id <openRoleId> --name \"新名称\""
],
"reviewed": true,
"review_reason": "Agent-authored and reviewed from the built-in Shortcut intent, executable Cobra path, and declared outcome; it affects selection only and does not alter execution or safety facts.",
"source_refs": [
"internal/cli/schema_command_registry.json#chat.shortcut_chat_role_update",
"cobra-help:dws chat +chat-role-update",
"ShortcutRegistry:chat +chat-role-update"
]
},
"chat.shortcut_chat_role_set_user": {
"agent_summary": "设置用户的群身份(覆盖该用户的全部群身份)",
"use_when": [
"当你想为某成员整体设定其在群内的身份时使用;会实际改写该用户的群身份集合(覆盖其原有全部身份),需传群、用户和 openRoleId 列表(传空则清除全部)。"
],
"avoid_when": [
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
],
"examples": [
"dws chat +chat-role-set-user --group <openConversationId> --user <userId> --role-ids roleId1,roleId2"
],
"reviewed": true,
"review_reason": "Agent-authored and reviewed from the built-in Shortcut intent, executable Cobra path, and declared outcome; it affects selection only and does not alter execution or safety facts.",
"source_refs": [
"internal/cli/schema_command_registry.json#chat.shortcut_chat_role_set_user",
"cobra-help:dws chat +chat-role-set-user",
"ShortcutRegistry:chat +chat-role-set-user"
]
},
"chat.shortcut_chat_role_query_user": {
"agent_summary": "查询群成员的群身份",
"use_when": [
"当你想查看某个群成员当前拥有哪些群身份时使用;只读,需传群 openConversationId 和用户 userId 或 openDingTalkId。"
],
"avoid_when": [
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
],
"examples": [
"dws chat +chat-role-query-user --group <openConversationId> --user <userId>"
],
"reviewed": true,
"review_reason": "Agent-authored and reviewed from the built-in Shortcut intent, executable Cobra path, and declared outcome; it affects selection only and does not alter execution or safety facts.",
"source_refs": [
"internal/cli/schema_command_registry.json#chat.shortcut_chat_role_query_user",
"cobra-help:dws chat +chat-role-query-user",
"ShortcutRegistry:chat +chat-role-query-user"
]
},
"chat.shortcut_messages_send_by_webhook": {
"agent_summary": "自定义机器人 Webhook 发送群消息",
"use_when": [
"当你只有自定义机器人的 Webhook token、想往其所在群推送消息时使用;会实际通过 Webhook 发群消息,需传 token、标题、正文,可 @手机号/userId 或 @所有人。"
],
"avoid_when": [
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
],
"examples": [
"dws chat +messages-send-by-webhook --token <token> --title \"告警\" --text \"CPU 超 90%\" --at-all"
],
"reviewed": true,
"review_reason": "Agent-authored and reviewed from the built-in Shortcut intent, executable Cobra path, and declared outcome; it affects selection only and does not alter execution or safety facts.",
"source_refs": [
"internal/cli/schema_command_registry.json#chat.shortcut_messages_send_by_webhook",
"cobra-help:dws chat +messages-send-by-webhook",
"ShortcutRegistry:chat +messages-send-by-webhook"
]
},
"chat.shortcut_messages_list_direct": {
"agent_summary": "拉取单聊会话消息",
"use_when": [
"当你想按时间拉取与某人单聊的历史消息时使用;只读,需传对方 userId 或 openDingTalkId 及起始时间,--forward 控制翻页方向。"
],
"avoid_when": [
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
],
"examples": [
"dws chat +messages-list-direct --user <userId> --time \"2025-03-01 00:00:00\""
],
"reviewed": true,
"review_reason": "Agent-authored and reviewed from the built-in Shortcut intent, executable Cobra path, and declared outcome; it affects selection only and does not alter execution or safety facts.",
"source_refs": [
"internal/cli/schema_command_registry.json#chat.shortcut_messages_list_direct",
"cobra-help:dws chat +messages-list-direct",
"ShortcutRegistry:chat +messages-list-direct"
]
},
"chat.shortcut_messages_list_unread_conversations": {
"agent_summary": "获取有未读消息的会话列表",
"use_when": [
"当你想快速定位哪些会话还有未读消息时使用;只读返回有未读的会话列表,可用 --exclude-muted 排除已免打扰会话。"
],
"avoid_when": [
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
],
"examples": [
"dws chat +messages-list-unread-conversations --count 20"
],
"reviewed": true,
"review_reason": "Agent-authored and reviewed from the built-in Shortcut intent, executable Cobra path, and declared outcome; it affects selection only and does not alter execution or safety facts.",
"source_refs": [
"internal/cli/schema_command_registry.json#chat.shortcut_messages_list_unread_conversations",
"cobra-help:dws chat +messages-list-unread-conversations",
"ShortcutRegistry:chat +messages-list-unread-conversations"
]
},
"chat.shortcut_messages_mget": {
"agent_summary": "根据消息 ID 批量查询消息(最多 50 条)",
"use_when": [
"当你已有一批消息 openMsgId、需要批量取回它们的详细内容时使用;只读,一次最多查询 50 条。"
],
"avoid_when": [
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
],
"examples": [
"dws chat +messages-mget --msg-ids msgId1,msgId2"
],
"reviewed": true,
"review_reason": "Agent-authored and reviewed from the built-in Shortcut intent, executable Cobra path, and declared outcome; it affects selection only and does not alter execution or safety facts.",
"source_refs": [
"internal/cli/schema_command_registry.json#chat.shortcut_messages_mget",
"cobra-help:dws chat +messages-mget",
"ShortcutRegistry:chat +messages-mget"
]
},
"chat.shortcut_messages_query_send_status": {
"agent_summary": "查询消息发送状态",
"use_when": [
"当你发消息后拿到 openTaskId、想确认这条消息是否发送成功时使用;只读返回发送状态,需传 --open-task-id。"
],
"avoid_when": [
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
],
"examples": [
"dws chat +messages-query-send-status --open-task-id <openTaskId>"
],
"reviewed": true,
"review_reason": "Agent-authored and reviewed from the built-in Shortcut intent, executable Cobra path, and declared outcome; it affects selection only and does not alter execution or safety facts.",
"source_refs": [
"internal/cli/schema_command_registry.json#chat.shortcut_messages_query_send_status",
"cobra-help:dws chat +messages-query-send-status",
"ShortcutRegistry:chat +messages-query-send-status"
]
},
"chat.shortcut_messages_read_status": {
"agent_summary": "查询消息的已读/未读状态",
"use_when": [
"当你想知道自己发出的某条消息有哪些人已读/未读时使用;只读,需传会话 openConversationId 和该消息 openMessageId,可指定目标成员列表。"
],
"avoid_when": [
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
],
"examples": [
"dws chat +messages-read-status --conversation-id <openConversationId> --message-id <openMessageId>"
],
"reviewed": true,
"review_reason": "Agent-authored and reviewed from the built-in Shortcut intent, executable Cobra path, and declared outcome; it affects selection only and does not alter execution or safety facts.",
"source_refs": [
"internal/cli/schema_command_registry.json#chat.shortcut_messages_read_status",
"cobra-help:dws chat +messages-read-status",
"ShortcutRegistry:chat +messages-read-status"
]
},
"chat.shortcut_messages_update_card": {
"agent_summary": "流式更新卡片内容(最后一次 --flow-status 应为 3)",
"use_when": [
"当你要向已发送的流式卡片持续追加/更新内容时使用;会实际更新卡片,需传 send-card 返回的 bizId、新内容及 flowStatus(最后一次应为 3 表示完成)。"
],
"avoid_when": [
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
],
"examples": [
"dws chat +messages-update-card --biz-id <bizId> --content \"内容\" --flow-status 3"
],
"reviewed": true,
"review_reason": "Agent-authored and reviewed from the built-in Shortcut intent, executable Cobra path, and declared outcome; it affects selection only and does not alter execution or safety facts.",
"source_refs": [
"internal/cli/schema_command_registry.json#chat.shortcut_messages_update_card",
"cobra-help:dws chat +messages-update-card",
"ShortcutRegistry:chat +messages-update-card"
]
},
"chat.shortcut_messages_list_pin": {
"agent_summary": "拉取会话中钉住的消息列表",
"use_when": [
"当你想查看某会话里当前钉住的消息有哪些时使用;只读分页返回,需传会话 openConversationId。"
],
"avoid_when": [
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
],
"examples": [
"dws chat +messages-list-pin --open-conversation-id <openConversationId>"
],
"reviewed": true,
"review_reason": "Agent-authored and reviewed from the built-in Shortcut intent, executable Cobra path, and declared outcome; it affects selection only and does not alter execution or safety facts.",
"source_refs": [
"internal/cli/schema_command_registry.json#chat.shortcut_messages_list_pin",
"cobra-help:dws chat +messages-list-pin",
"ShortcutRegistry:chat +messages-list-pin"
]
},
"chat.shortcut_at_me": {
"agent_summary": "查最近 @我 的消息(自动算时间窗,投影发送人/时间/内容/会话)",
"use_when": [
"当你想快速看回最近谁在群里或单聊里 @了你、但不想手动把起止时间换算成毫秒、也不想记 list-mentions 的一堆参数时使用;内部按本地时区算出「最近 N 天」(默认 7 天,可用 --days 调整回溯天数)的时间窗,搜索这段时间内 @我 的消息,再在本地把每条消息投影成发送人、时间、内容、所在会话四个关键字段。这是纯只读操作,只做搜索与本地投影,不会发送、撤回或标记任何消息。"
],
"avoid_when": [
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
],
"examples": [
"dws chat +at-me",
"dws chat +at-me --days 3"
],
"reviewed": true,
"review_reason": "Agent-authored and reviewed from the built-in Shortcut intent, executable Cobra path, and declared outcome; it affects selection only and does not alter execution or safety facts.",
"source_refs": [
"internal/cli/schema_command_registry.json#chat.shortcut_at_me",
"cobra-help:dws chat +at-me",
"ShortcutRegistry:chat +at-me"
]
},
"chat.shortcut_broadcast": {
"agent_summary": "按姓名逐一给多个人群发同一条单聊消息(自动解析 userId、逐个发送)",
"use_when": [
"当你想把同一条通知一次性单聊发给多位同事、但只知道他们的姓名不想逐个查 userId 时使用;内部把姓名列表逐个解析成唯一用户后,用 openDingTalkId 对每个人单独发一条单聊消息,并汇总成功/失败人数。某个姓名匹配不到人或匹配到多人时,会跳过该人并在结尾报出,不影响其他人收到消息。会真实发出多条消息。"
],
"avoid_when": [
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
],
"examples": [
"dws chat +broadcast --to \"张三,李四,王五\" --text \"今晚 8 点上线,请留意\""
],
"reviewed": true,
"review_reason": "Agent-authored and reviewed from the built-in Shortcut intent, executable Cobra path, and declared outcome; it affects selection only and does not alter execution or safety facts.",
"source_refs": [
"internal/cli/schema_command_registry.json#chat.shortcut_broadcast",
"cobra-help:dws chat +broadcast",
"ShortcutRegistry:chat +broadcast"
]
},
"chat.shortcut_dm": {
"agent_summary": "按姓名直接给某人发单聊消息(自动解析 userId)",
"use_when": [
"当你只知道对方姓名、想直接发一条单聊消息而不想先查 userId 时使用;内部先按姓名搜通讯录解析出唯一用户,并用其 openDingTalkId 发送,姓名匹配到多人时会列出候选让你区分。会真实发出消息。"
],
"avoid_when": [
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
],
"examples": [
"dws chat +dm --to 张三 --text \"周报发我一下\""
],
"reviewed": true,
"review_reason": "Agent-authored and reviewed from the built-in Shortcut intent, executable Cobra path, and declared outcome; it affects selection only and does not alter execution or safety facts.",
"source_refs": [
"internal/cli/schema_command_registry.json#chat.shortcut_dm",
"cobra-help:dws chat +dm",
"ShortcutRegistry:chat +dm"
]
},
"chat.shortcut_group_members": {
"agent_summary": "按群名列出群成员(自动搜群解析 openConversationId)",
"use_when": [
"当你只知道群的名字、想看看这个群里有哪些成员,而不想先手动查群 ID 时使用;内部先按群名搜索群聊解析出唯一 openConversationId,再拉取该群的成员列表。群名匹配到多个群时会列出候选让你区分、绝不自行假定。只读,不改动任何数据。"
],
"avoid_when": [
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
],
"examples": [
"dws chat +group-members --group 项目冲刺"
],
"reviewed": true,
"review_reason": "Agent-authored and reviewed from the built-in Shortcut intent, executable Cobra path, and declared outcome; it affects selection only and does not alter execution or safety facts.",
"source_refs": [
"internal/cli/schema_command_registry.json#chat.shortcut_group_members",
"cobra-help:dws chat +group-members",
"ShortcutRegistry:chat +group-members"
]
},
"chat.shortcut_my_groups": {
"agent_summary": "列出我加入的群,可按类型过滤并投影关键字段",
"use_when": [
"当你想快速看一眼自己都加入了哪些群、以及每个群的会话ID、名称、群主和人数,而不想翻分页或盯着原始返回时使用;内部分页拉取你加入的群列表,把每个群防御式地投影成 会话id / 名称 / 群主 / 人数 / 类型 等关键字段,输出成干净的结果。可选 --type 在本地按群类型过滤(底层接口本身不带类型参数,故为客户端过滤)。这是只读操作,不会改动任何群或成员关系。"
],
"avoid_when": [
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
],
"examples": [
"dws chat +my-groups",
"dws chat +my-groups --type group"
],
"reviewed": true,
"review_reason": "Agent-authored and reviewed from the built-in Shortcut intent, executable Cobra path, and declared outcome; it affects selection only and does not alter execution or safety facts.",
"source_refs": [
"internal/cli/schema_command_registry.json#chat.shortcut_my_groups",
"cobra-help:dws chat +my-groups",
"ShortcutRegistry:chat +my-groups"
]
},
"chat.shortcut_send_to_group": {
"agent_summary": "按群名直接给群发消息(自动搜群解析 openConversationId)",
"use_when": [
"当你只知道群的名字、想直接往这个群里发一条消息而不想先手动查群 ID 时使用;内部先按群名搜索群聊解析出唯一 openConversationId 再发送,群名匹配到多个群时会列出候选让你区分、绝不自行假定。会真实发出群消息。"
],
"avoid_when": [
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
],
"examples": [
"dws chat +send-to-group --group 项目冲刺 --text \"今天 5 点前提交进度\""
],
"reviewed": true,
"review_reason": "Agent-authored and reviewed from the built-in Shortcut intent, executable Cobra path, and declared outcome; it affects selection only and does not alter execution or safety facts.",
"source_refs": [
"internal/cli/schema_command_registry.json#chat.shortcut_send_to_group",
"cobra-help:dws chat +send-to-group",
"ShortcutRegistry:chat +send-to-group"
]
},
"chat.shortcut_unread_chats": {
"agent_summary": "列出我有未读消息的会话(投影会话名/未读数/会话ID)",
"use_when": [
"当你想快速看清自己当前有哪些会话还有未读消息、方便逐个处理时使用;内部调用未读会话列表接口,可用 --count 控制返回的会话条数(不传则用服务端默认值),用 --exclude-muted 排除你已设置免打扰的会话;再在本地把每个会话投影成会话名、未读数和会话 ID 三个关键字段。这是纯只读操作,只做列表与本地投影,不会把任何会话标记为已读或未读;若没有未读会话则返回空列表。"
],
"avoid_when": [
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
],
"examples": [
"dws chat +unread-chats",
"dws chat +unread-chats --count 20"
],
"reviewed": true,
"review_reason": "Agent-authored and reviewed from the built-in Shortcut intent, executable Cobra path, and declared outcome; it affects selection only and does not alter execution or safety facts.",
"source_refs": [
"internal/cli/schema_command_registry.json#chat.shortcut_unread_chats",
"cobra-help:dws chat +unread-chats",
"ShortcutRegistry:chat +unread-chats"
]
}
},
"products": {
@@ -431,6 +431,272 @@
"Wukong-parity:3306c3307",
"live-dws-schema:contact.self_user_profile_update#FAILED"
]
},
"contact.shortcut_list_followings": {
"agent_summary": "获取当前用户的特别关注列表",
"use_when": [
"当你想查看本人在通讯录里「特别关注」的联系人名单(例如常打交道的同事、上级)时使用;无需输入,返回关注对象的用户列表,可用于快速定位这些人的 userId 再发消息或排日程。"
],
"avoid_when": [
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
],
"examples": [
"dws contact +list-followings"
],
"reviewed": true,
"review_reason": "Agent-authored and reviewed from the built-in Shortcut intent, executable Cobra path, and declared outcome; it affects selection only and does not alter execution or safety facts.",
"source_refs": [
"internal/cli/schema_command_registry.json#contact.shortcut_list_followings",
"cobra-help:dws contact +list-followings",
"ShortcutRegistry:contact +list-followings"
]
},
"contact.shortcut_search_user": {
"agent_summary": "按关键词搜索通讯录用户",
"use_when": [
"当你只知道某人的姓名、花名或部分名字,需要把它解析成 userId 及部门等信息以便后续发消息、排日程或指派任务时使用;输入搜索关键词(--query),返回匹配的用户列表。"
],
"avoid_when": [
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
],
"examples": [
"dws contact +search-user --query \"张三\""
],
"reviewed": true,
"review_reason": "Agent-authored and reviewed from the built-in Shortcut intent, executable Cobra path, and declared outcome; it affects selection only and does not alter execution or safety facts.",
"source_refs": [
"internal/cli/schema_command_registry.json#contact.shortcut_search_user",
"cobra-help:dws contact +search-user",
"ShortcutRegistry:contact +search-user"
]
},
"contact.shortcut_search_mobile": {
"agent_summary": "按手机号搜索通讯录用户",
"use_when": [
"当你手里只有某人的手机号、需要反查出对应的通讯录用户和 userId 时使用;输入手机号(--mobile),返回该手机号所属的用户信息,适合从电话或名片信息定位到具体员工。"
],
"avoid_when": [
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
],
"examples": [
"dws contact +search-mobile --mobile 13800138000"
],
"reviewed": true,
"review_reason": "Agent-authored and reviewed from the built-in Shortcut intent, executable Cobra path, and declared outcome; it affects selection only and does not alter execution or safety facts.",
"source_refs": [
"internal/cli/schema_command_registry.json#contact.shortcut_search_mobile",
"cobra-help:dws contact +search-mobile",
"ShortcutRegistry:contact +search-mobile"
]
},
"contact.shortcut_list_roles": {
"agent_summary": "获取企业所有角色(标签)列表",
"use_when": [
"当你想总览企业里都有哪些角色/员工标签(如「管理员」「财务」「销售」)及其角色 ID 时使用;无需输入,返回全量角色列表,常用于按角色圈定人群前先摸清有哪些角色可选。"
],
"avoid_when": [
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
],
"examples": [
"dws contact +list-roles"
],
"reviewed": true,
"review_reason": "Agent-authored and reviewed from the built-in Shortcut intent, executable Cobra path, and declared outcome; it affects selection only and does not alter execution or safety facts.",
"source_refs": [
"internal/cli/schema_command_registry.json#contact.shortcut_list_roles",
"cobra-help:dws contact +list-roles",
"ShortcutRegistry:contact +list-roles"
]
},
"contact.shortcut_list_role_members": {
"agent_summary": "查询角色下的成员列表",
"use_when": [
"当你已知某个角色 ID、想列出该角色(标签)下的全部成员以便群发通知或统计人群时使用;输入角色 ID(--id),返回该角色下的用户列表,通常先用 +search-role 拿到角色 ID 再调用。"
],
"avoid_when": [
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
],
"examples": [
"dws contact +list-role-members --id 12345"
],
"reviewed": true,
"review_reason": "Agent-authored and reviewed from the built-in Shortcut intent, executable Cobra path, and declared outcome; it affects selection only and does not alter execution or safety facts.",
"source_refs": [
"internal/cli/schema_command_registry.json#contact.shortcut_list_role_members",
"cobra-help:dws contact +list-role-members",
"ShortcutRegistry:contact +list-role-members"
]
},
"contact.shortcut_list_sub_depts": {
"agent_summary": "查看指定部门的子部门",
"use_when": [
"当你想逐层浏览组织架构、查看某个部门下一级的子部门时使用;输入父部门 ID(--dept,根部门为 1),返回其直属子部门列表,可用于自顶向下遍历部门树。"
],
"avoid_when": [
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
],
"examples": [
"dws contact +list-sub-depts --dept 1"
],
"reviewed": true,
"review_reason": "Agent-authored and reviewed from the built-in Shortcut intent, executable Cobra path, and declared outcome; it affects selection only and does not alter execution or safety facts.",
"source_refs": [
"internal/cli/schema_command_registry.json#contact.shortcut_list_sub_depts",
"cobra-help:dws contact +list-sub-depts",
"ShortcutRegistry:contact +list-sub-depts"
]
},
"contact.shortcut_list_dept_members": {
"agent_summary": "查看部门成员(仅本部门,不含下级)",
"use_when": [
"当你想列出一个或多个部门本级的员工(不含下级子部门)以便群发通知、统计或指派任务时使用;输入部门 ID 列表(--depts,逗号分隔),返回这些部门下的成员,如需含下级需自行遍历子部门。"
],
"avoid_when": [
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
],
"examples": [
"dws contact +list-dept-members --depts 12345,67890"
],
"reviewed": true,
"review_reason": "Agent-authored and reviewed from the built-in Shortcut intent, executable Cobra path, and declared outcome; it affects selection only and does not alter execution or safety facts.",
"source_refs": [
"internal/cli/schema_command_registry.json#contact.shortcut_list_dept_members",
"cobra-help:dws contact +list-dept-members",
"ShortcutRegistry:contact +list-dept-members"
]
},
"contact.shortcut_by_mobile": {
"agent_summary": "按手机号查询某人的完整资料(自动解析 userId 后取详情)",
"use_when": [
"当你只知道对方手机号、想一步拿到其完整资料(部门、职位、联系方式、是否管理员等)而不想先按手机号搜出 userId 再单独查详情时使用;内部先用手机号在通讯录里查出对应的 userId,若没有人绑定该手机号会明确报错,再用该 userId 取完整详情。这是纯只读操作,不会修改任何数据。"
],
"avoid_when": [
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
],
"examples": [
"dws contact +by-mobile --mobile 13800138000"
],
"reviewed": true,
"review_reason": "Agent-authored and reviewed from the built-in Shortcut intent, executable Cobra path, and declared outcome; it affects selection only and does not alter execution or safety facts.",
"source_refs": [
"internal/cli/schema_command_registry.json#contact.shortcut_by_mobile",
"cobra-help:dws contact +by-mobile",
"ShortcutRegistry:contact +by-mobile"
]
},
"contact.shortcut_dept_members": {
"agent_summary": "按部门名列出部门成员(自动解析 deptId)",
"use_when": [
"当你只知道某个部门的名称、想知道该部门里都有哪些成员时使用;内部先按部门名搜索出唯一的 deptId(匹配到 0 个或多个时不猜测,而是列出候选部门名与 deptId 让你选),再打印该部门的直接成员列表(仅本部门,不递归下级部门)。只读,不做任何修改。"
],
"avoid_when": [
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
],
"examples": [
"dws contact +dept-members --dept 技术部"
],
"reviewed": true,
"review_reason": "Agent-authored and reviewed from the built-in Shortcut intent, executable Cobra path, and declared outcome; it affects selection only and does not alter execution or safety facts.",
"source_refs": [
"internal/cli/schema_command_registry.json#contact.shortcut_dept_members",
"cobra-help:dws contact +dept-members",
"ShortcutRegistry:contact +dept-members"
]
},
"contact.shortcut_lookup": {
"agent_summary": "按姓名查询某人的完整资料(自动解析 userId 后取详情)",
"use_when": [
"当你只知道对方姓名、想一步拿到其完整资料(部门、职位、联系方式等)而不想先搜 userId 再查详情时使用;内部先按姓名搜通讯录解析出唯一 userId,再取详情,姓名匹配到多人时会列出候选让你区分。只读操作。"
],
"avoid_when": [
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
],
"examples": [
"dws contact +lookup --name 张三"
],
"reviewed": true,
"review_reason": "Agent-authored and reviewed from the built-in Shortcut intent, executable Cobra path, and declared outcome; it affects selection only and does not alter execution or safety facts.",
"source_refs": [
"internal/cli/schema_command_registry.json#contact.shortcut_lookup",
"cobra-help:dws contact +lookup",
"ShortcutRegistry:contact +lookup"
]
},
"contact.shortcut_org": {
"agent_summary": "按姓名查某人所在部门的详情(自动解析 userId 与 deptId)",
"use_when": [
"当你只知道某位同事的姓名、想知道 TA 所在部门(部门ID、名称、人数)时使用;内部先按姓名解析出唯一 userId,再取 TA 的组织信息拿到主部门 deptId,最后打印该部门的详情。只读,不做任何修改。"
],
"avoid_when": [
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
],
"examples": [
"dws contact +org --name 张三"
],
"reviewed": true,
"review_reason": "Agent-authored and reviewed from the built-in Shortcut intent, executable Cobra path, and declared outcome; it affects selection only and does not alter execution or safety facts.",
"source_refs": [
"internal/cli/schema_command_registry.json#contact.shortcut_org",
"cobra-help:dws contact +org",
"ShortcutRegistry:contact +org"
]
},
"contact.shortcut_resolve_dept": {
"agent_summary": "按名称搜索部门并解析出唯一 deptId(只读)",
"use_when": [
"当你只知道某个部门的名称(或名称里的关键词)、想把它解析成可直接用于后续工具的 deptId 时使用;内部按 --name 关键词调用 search_dept_by_keyword 搜索部门,再在本地投影出每个候选的 deptId 和 name。如果只命中一个部门就直接返回它的 deptId;如果命中多个则列出全部候选让你消歧,绝不替你瞎猜;如果一个都没命中则提示未找到。这是纯只读操作,只做搜索与本地投影,不会修改任何部门,也不会列出部门成员。"
],
"avoid_when": [
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
],
"examples": [
"dws contact +resolve-dept --name 技术部"
],
"reviewed": true,
"review_reason": "Agent-authored and reviewed from the built-in Shortcut intent, executable Cobra path, and declared outcome; it affects selection only and does not alter execution or safety facts.",
"source_refs": [
"internal/cli/schema_command_registry.json#contact.shortcut_resolve_dept",
"cobra-help:dws contact +resolve-dept",
"ShortcutRegistry:contact +resolve-dept"
]
},
"contact.shortcut_team": {
"agent_summary": "按姓名列出某人所在部门的成员(自动解析 userId 与 deptId)",
"use_when": [
"当你只知道某位同事的姓名、想知道 TA 所在部门里都有哪些成员时使用;内部先按姓名解析出唯一 userId,再取 TA 的组织信息拿到主部门 deptId,最后打印该部门的直接成员列表(仅本部门,不递归下级部门)。只读,不做任何修改。"
],
"avoid_when": [
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
],
"examples": [
"dws contact +team --name 张三"
],
"reviewed": true,
"review_reason": "Agent-authored and reviewed from the built-in Shortcut intent, executable Cobra path, and declared outcome; it affects selection only and does not alter execution or safety facts.",
"source_refs": [
"internal/cli/schema_command_registry.json#contact.shortcut_team",
"cobra-help:dws contact +team",
"ShortcutRegistry:contact +team"
]
},
"contact.shortcut_me": {
"agent_summary": "查看我自己的通讯录资料(姓名/userId/手机/部门/组织,干净投影)",
"use_when": [
"当你(或 AI agent)需要知道「我是谁」——我自己的 userId、姓名、所在部门、组织、手机号,用于后续按名解析他人前先确定自己身份、或填充发起人信息时使用;内部调用零参数的 get_current_user_profile(永远是「我」,无需传姓名),再把冗长的原始资料投影成 {name,userId,mobile,dept,org,email} 几个关键字段。这是纯只读操作,不修改任何资料。"
],
"avoid_when": [
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
],
"examples": [
"dws contact +me"
],
"reviewed": true,
"review_reason": "Agent-authored and reviewed from the built-in Shortcut intent, executable Cobra path, and declared outcome; it affects selection only and does not alter execution or safety facts.",
"source_refs": [
"internal/cli/schema_command_registry.json#contact.shortcut_me",
"cobra-help:dws contact +me",
"ShortcutRegistry:contact +me"
]
}
},
"products": {
@@ -0,0 +1,391 @@
{
"version": 1,
"source": {
"kind": "explicit",
"name": "dws-agent-selection/devapp",
"repository": "DingTalk-Real-AI/dingtalk-workspace-cli",
"channel": "open-source",
"reviewed": true
},
"tools": {
"devapp.shortcut_list": {
"agent_summary": "查询开放平台企业内部应用列表",
"use_when": [
"当你要在开发者后台盘点或定位某个企业内部应用(例如按应用名、appKey、创建人或机器人名搜索,拿到其 unifiedAppId 以便后续查看详情、配置或发布)时使用;支持关键词过滤、排序和分页,返回应用列表。"
],
"avoid_when": [
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
],
"examples": [
"dws devapp +list"
],
"reviewed": true,
"review_reason": "Agent-authored and reviewed from the built-in Shortcut intent, executable Cobra path, and declared outcome; it affects selection only and does not alter execution or safety facts.",
"source_refs": [
"internal/cli/schema_command_registry.json#devapp.shortcut_list",
"cobra-help:dws devapp +list",
"ShortcutRegistry:devapp +list"
]
},
"devapp.shortcut_get": {
"agent_summary": "查询开放平台企业内部应用详情",
"use_when": [
"当你已知某应用的 unifiedAppId、需要查看它的完整配置信息(如名称、描述、图标、能力开关等)以便核对现状或作为修改前的依据时使用;输入 unifiedAppId,返回单个应用的详情。"
],
"avoid_when": [
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
],
"examples": [
"dws devapp +get --unified-app-id <UNIFIED_APP_ID>"
],
"reviewed": true,
"review_reason": "Agent-authored and reviewed from the built-in Shortcut intent, executable Cobra path, and declared outcome; it affects selection only and does not alter execution or safety facts.",
"source_refs": [
"internal/cli/schema_command_registry.json#devapp.shortcut_get",
"cobra-help:dws devapp +get",
"ShortcutRegistry:devapp +get"
]
},
"devapp.shortcut_create": {
"agent_summary": "创建开放平台企业内部应用",
"use_when": [
"当你要在开放平台从零新建一个企业内部应用(H5/机器人等的载体)时使用;传入应用名称、可选描述与图标 mediaId,会实际创建出一个新应用并返回其 unifiedAppId 供后续配置。"
],
"avoid_when": [
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
],
"examples": [
"dws devapp +create --name <NAME>"
],
"reviewed": true,
"review_reason": "Agent-authored and reviewed from the built-in Shortcut intent, executable Cobra path, and declared outcome; it affects selection only and does not alter execution or safety facts.",
"source_refs": [
"internal/cli/schema_command_registry.json#devapp.shortcut_create",
"cobra-help:dws devapp +create",
"ShortcutRegistry:devapp +create"
]
},
"devapp.shortcut_update": {
"agent_summary": "修改开放平台企业内部应用基础信息",
"use_when": [
"当你要改动一个已存在应用的基础信息(更名、改描述或换图标)时使用;指定 unifiedAppId 及要更新的字段,会实际写回并覆盖对应的应用基础资料。"
],
"avoid_when": [
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
],
"examples": [
"dws devapp +update --unified-app-id <UNIFIED_APP_ID>"
],
"reviewed": true,
"review_reason": "Agent-authored and reviewed from the built-in Shortcut intent, executable Cobra path, and declared outcome; it affects selection only and does not alter execution or safety facts.",
"source_refs": [
"internal/cli/schema_command_registry.json#devapp.shortcut_update",
"cobra-help:dws devapp +update",
"ShortcutRegistry:devapp +update"
]
},
"devapp.shortcut_delete": {
"agent_summary": "删除开放平台企业内部应用(不可逆)",
"use_when": [
"当你确认要彻底废弃某个企业内部应用时使用;传入 unifiedAppId 会真实且不可逆地删除该应用及其配置,执行前务必确认无误。"
],
"avoid_when": [
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
],
"examples": [
"dws devapp +delete --unified-app-id <UNIFIED_APP_ID>"
],
"reviewed": true,
"review_reason": "Agent-authored and reviewed from the built-in Shortcut intent, executable Cobra path, and declared outcome; it affects selection only and does not alter execution or safety facts.",
"source_refs": [
"internal/cli/schema_command_registry.json#devapp.shortcut_delete",
"cobra-help:dws devapp +delete",
"ShortcutRegistry:devapp +delete"
]
},
"devapp.shortcut_enable": {
"agent_summary": "启用开放平台企业内部应用",
"use_when": [
"当某个应用处于停用状态、你要让它重新生效可用时使用;传入 unifiedAppId 会实际将应用状态切换为启用。"
],
"avoid_when": [
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
],
"examples": [
"dws devapp +enable --unified-app-id <UNIFIED_APP_ID>"
],
"reviewed": true,
"review_reason": "Agent-authored and reviewed from the built-in Shortcut intent, executable Cobra path, and declared outcome; it affects selection only and does not alter execution or safety facts.",
"source_refs": [
"internal/cli/schema_command_registry.json#devapp.shortcut_enable",
"cobra-help:dws devapp +enable",
"ShortcutRegistry:devapp +enable"
]
},
"devapp.shortcut_disable": {
"agent_summary": "停用开放平台企业内部应用",
"use_when": [
"当你要临时下线某个应用、让它对用户不可用又不删除时使用;传入 unifiedAppId 会实际将应用状态切换为停用,可日后再启用恢复。"
],
"avoid_when": [
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
],
"examples": [
"dws devapp +disable --unified-app-id <UNIFIED_APP_ID>"
],
"reviewed": true,
"review_reason": "Agent-authored and reviewed from the built-in Shortcut intent, executable Cobra path, and declared outcome; it affects selection only and does not alter execution or safety facts.",
"source_refs": [
"internal/cli/schema_command_registry.json#devapp.shortcut_disable",
"cobra-help:dws devapp +disable",
"ShortcutRegistry:devapp +disable"
]
},
"devapp.shortcut_webapp_get": {
"agent_summary": "查询网页应用配置",
"use_when": [
"当你要查看某应用的网页(H5)能力现状,如移动端/PC 首页地址、管理后台地址等,以便核对或作为改配置前的参考时使用;输入 unifiedAppId,返回当前网页应用配置。"
],
"avoid_when": [
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
],
"examples": [
"dws devapp +webapp-get --unified-app-id <UNIFIED_APP_ID>"
],
"reviewed": true,
"review_reason": "Agent-authored and reviewed from the built-in Shortcut intent, executable Cobra path, and declared outcome; it affects selection only and does not alter execution or safety facts.",
"source_refs": [
"internal/cli/schema_command_registry.json#devapp.shortcut_webapp_get",
"cobra-help:dws devapp +webapp-get",
"ShortcutRegistry:devapp +webapp-get"
]
},
"devapp.shortcut_webapp_config": {
"agent_summary": "配置网页应用能力",
"use_when": [
"当你要为应用开通或调整网页(H5)入口,如设置移动端/PC 端首页 URL、管理后台地址或页面类型时使用;指定 unifiedAppId 及相应地址,会实际写入该应用的网页应用配置。"
],
"avoid_when": [
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
],
"examples": [
"dws devapp +webapp-config --unified-app-id <UNIFIED_APP_ID>"
],
"reviewed": true,
"review_reason": "Agent-authored and reviewed from the built-in Shortcut intent, executable Cobra path, and declared outcome; it affects selection only and does not alter execution or safety facts.",
"source_refs": [
"internal/cli/schema_command_registry.json#devapp.shortcut_webapp_config",
"cobra-help:dws devapp +webapp-config",
"ShortcutRegistry:devapp +webapp-config"
]
},
"devapp.shortcut_permission_list": {
"agent_summary": "查询开放平台应用权限列表",
"use_when": [
"当你要查看某应用已申请/可申请的 API 权限点及其授权状态(用于排查接口报权限错、或确认某 scopeValue 是否已开通)时使用;可按关键词、scopeValue、授权状态等过滤,返回权限点列表。"
],
"avoid_when": [
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
],
"examples": [
"dws devapp +permission-list --unified-app-id <UNIFIED_APP_ID>"
],
"reviewed": true,
"review_reason": "Agent-authored and reviewed from the built-in Shortcut intent, executable Cobra path, and declared outcome; it affects selection only and does not alter execution or safety facts.",
"source_refs": [
"internal/cli/schema_command_registry.json#devapp.shortcut_permission_list",
"cobra-help:dws devapp +permission-list",
"ShortcutRegistry:devapp +permission-list"
]
},
"devapp.shortcut_member_list": {
"agent_summary": "查询开放平台应用成员",
"use_when": [
"当你要查看某应用有哪些成员及其角色(如谁是开发者/管理员),用于核对协作人员或权限归属时使用;输入 unifiedAppId,返回成员列表。"
],
"avoid_when": [
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
],
"examples": [
"dws devapp +member-list --unified-app-id <UNIFIED_APP_ID>"
],
"reviewed": true,
"review_reason": "Agent-authored and reviewed from the built-in Shortcut intent, executable Cobra path, and declared outcome; it affects selection only and does not alter execution or safety facts.",
"source_refs": [
"internal/cli/schema_command_registry.json#devapp.shortcut_member_list",
"cobra-help:dws devapp +member-list",
"ShortcutRegistry:devapp +member-list"
]
},
"devapp.shortcut_member_add": {
"agent_summary": "添加开放平台应用成员",
"use_when": [
"当你要给某应用增加协作人员(如把某人加为开发者)时使用;传入 unifiedAppId、userId 列表和成员类型(如 DEVELOPER),会实际把这些人加入应用成员并赋予对应角色。"
],
"avoid_when": [
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
],
"examples": [
"dws devapp +member-add --unified-app-id <UNIFIED_APP_ID> --user-ids <VALUES> --member-type <MEMBER_TYPE>"
],
"reviewed": true,
"review_reason": "Agent-authored and reviewed from the built-in Shortcut intent, executable Cobra path, and declared outcome; it affects selection only and does not alter execution or safety facts.",
"source_refs": [
"internal/cli/schema_command_registry.json#devapp.shortcut_member_add",
"cobra-help:dws devapp +member-add",
"ShortcutRegistry:devapp +member-add"
]
},
"devapp.shortcut_member_remove": {
"agent_summary": "移除开放平台应用成员",
"use_when": [
"当某人离职或不再参与、你要取消其对应用的访问/协作权限时使用;传入 unifiedAppId、userId 列表和成员类型,会实际把这些人从应用成员中移除。"
],
"avoid_when": [
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
],
"examples": [
"dws devapp +member-remove --unified-app-id <UNIFIED_APP_ID> --user-ids <VALUES> --member-type <MEMBER_TYPE>"
],
"reviewed": true,
"review_reason": "Agent-authored and reviewed from the built-in Shortcut intent, executable Cobra path, and declared outcome; it affects selection only and does not alter execution or safety facts.",
"source_refs": [
"internal/cli/schema_command_registry.json#devapp.shortcut_member_remove",
"cobra-help:dws devapp +member-remove",
"ShortcutRegistry:devapp +member-remove"
]
},
"devapp.shortcut_robot_get": {
"agent_summary": "查询现有应用的机器人配置",
"use_when": [
"当你要查看某应用已有的机器人配置(名称、回调地址、模式 HTTPS/STREAM/AISKILL、技能等)以核对现状或作为改配置前的依据时使用;输入 unifiedAppId,返回当前机器人配置。"
],
"avoid_when": [
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
],
"examples": [
"dws devapp +robot-get --unified-app-id <UNIFIED_APP_ID>"
],
"reviewed": true,
"review_reason": "Agent-authored and reviewed from the built-in Shortcut intent, executable Cobra path, and declared outcome; it affects selection only and does not alter execution or safety facts.",
"source_refs": [
"internal/cli/schema_command_registry.json#devapp.shortcut_robot_get",
"cobra-help:dws devapp +robot-get",
"ShortcutRegistry:devapp +robot-get"
]
},
"devapp.shortcut_event_list": {
"agent_summary": "查询应用已订阅的事件列表",
"use_when": [
"当你要确认某应用当前订阅了哪些事件回调(用于排查漏收事件、或退订前先查事件码)时使用;输入 unifiedAppId,可按事件码/名称关键词过滤并分页,返回已订阅事件列表。"
],
"avoid_when": [
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
],
"examples": [
"dws devapp +event-list --unified-app-id <UNIFIED_APP_ID>"
],
"reviewed": true,
"review_reason": "Agent-authored and reviewed from the built-in Shortcut intent, executable Cobra path, and declared outcome; it affects selection only and does not alter execution or safety facts.",
"source_refs": [
"internal/cli/schema_command_registry.json#devapp.shortcut_event_list",
"cobra-help:dws devapp +event-list",
"ShortcutRegistry:devapp +event-list"
]
},
"devapp.shortcut_version_list": {
"agent_summary": "分页查询应用版本列表",
"use_when": [
"当你要查看某应用的历史版本(找某个 versionId、看各版本发布状态或回顾迭代记录)时使用;输入 unifiedAppId 并分页,返回版本列表。"
],
"avoid_when": [
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
],
"examples": [
"dws devapp +version-list --unified-app-id <UNIFIED_APP_ID>"
],
"reviewed": true,
"review_reason": "Agent-authored and reviewed from the built-in Shortcut intent, executable Cobra path, and declared outcome; it affects selection only and does not alter execution or safety facts.",
"source_refs": [
"internal/cli/schema_command_registry.json#devapp.shortcut_version_list",
"cobra-help:dws devapp +version-list",
"ShortcutRegistry:devapp +version-list"
]
},
"devapp.shortcut_version_get": {
"agent_summary": "查询指定版本详情",
"use_when": [
"当你已知某个 versionId、要查看该版本的具体内容(版本号、描述、包含的配置等)以核对发布内容时使用;输入 unifiedAppId 和 versionId,返回单个版本的详情。"
],
"avoid_when": [
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
],
"examples": [
"dws devapp +version-get --unified-app-id <UNIFIED_APP_ID> --version-id <VERSION_ID>"
],
"reviewed": true,
"review_reason": "Agent-authored and reviewed from the built-in Shortcut intent, executable Cobra path, and declared outcome; it affects selection only and does not alter execution or safety facts.",
"source_refs": [
"internal/cli/schema_command_registry.json#devapp.shortcut_version_get",
"cobra-help:dws devapp +version-get",
"ShortcutRegistry:devapp +version-get"
]
},
"devapp.shortcut_version_check_approval": {
"agent_summary": "预检版本发布是否需要审批(不实际发布)",
"use_when": [
"当你在正式发布某版本前想先确认它是否会触发审批、是否含高敏权限等发布前置要求时使用;传入 unifiedAppId 和 versionId,仅做预检返回审批要求,不会真正发布。"
],
"avoid_when": [
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
],
"examples": [
"dws devapp +version-check-approval --unified-app-id <UNIFIED_APP_ID> --version-id <VERSION_ID>"
],
"reviewed": true,
"review_reason": "Agent-authored and reviewed from the built-in Shortcut intent, executable Cobra path, and declared outcome; it affects selection only and does not alter execution or safety facts.",
"source_refs": [
"internal/cli/schema_command_registry.json#devapp.shortcut_version_check_approval",
"cobra-help:dws devapp +version-check-approval",
"ShortcutRegistry:devapp +version-check-approval"
]
},
"devapp.shortcut_version_status": {
"agent_summary": "查询版本发布/审批状态",
"use_when": [
"当你已提交发布、想跟进某版本当前处于什么阶段(审批中、已发布、被驳回等)时使用;输入 unifiedAppId 和 versionId,返回该版本的发布/审批状态。"
],
"avoid_when": [
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
],
"examples": [
"dws devapp +version-status --unified-app-id <UNIFIED_APP_ID> --version-id <VERSION_ID>"
],
"reviewed": true,
"review_reason": "Agent-authored and reviewed from the built-in Shortcut intent, executable Cobra path, and declared outcome; it affects selection only and does not alter execution or safety facts.",
"source_refs": [
"internal/cli/schema_command_registry.json#devapp.shortcut_version_status",
"cobra-help:dws devapp +version-status",
"ShortcutRegistry:devapp +version-status"
]
}
},
"products": {
"devapp": {
"agent_summary": "管理钉钉开放平台企业内部应用、成员、权限、机器人、事件与版本",
"use_when": [
"请求涉及企业内部应用的查询、创建、配置、成员权限、机器人、事件订阅或版本管理"
],
"avoid_when": [
"开放平台接口文档搜索使用 devdoc;普通钉钉业务数据使用对应产品命令"
],
"reviewed": true,
"review_reason": "Agent-authored and reviewed from the public devapp Shortcut surface; product routing does not alter command identity, parameters, or runtime safety.",
"source_refs": [
"internal/cli/schema_command_registry.json",
"internal/shortcut/devapp",
"CommandRegistry:product=devapp"
]
}
}
}
@@ -58,6 +58,82 @@
"skills/mono/references/products/ding.md",
"live-dws-schema:ding.send_ding_message"
]
},
"ding.shortcut_list": {
"agent_summary": "查询 DING 消息列表",
"use_when": [
"当你想查看当前身份收到或发出的 DING 消息、回顾有哪些强提醒或获取某条 DING 的 openDingId 以便后续查已读或撤回时使用;可选按类型过滤并用 cursor 翻页,只读不产生副作用。"
],
"avoid_when": [
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
],
"examples": [
"dws ding +list"
],
"reviewed": true,
"review_reason": "Agent-authored and reviewed from the built-in Shortcut intent, executable Cobra path, and declared outcome; it affects selection only and does not alter execution or safety facts.",
"source_refs": [
"internal/cli/schema_command_registry.json#ding.shortcut_list",
"cobra-help:dws ding +list",
"ShortcutRegistry:ding +list"
]
},
"ding.shortcut_receiver_status": {
"agent_summary": "查询 DING 消息接收人已读状态",
"use_when": [
"当你发出一条 DING 后想确认每位接收人是否已读、追踪谁还没看到以便催办时使用;需提供该 DING 的 openDingId,返回各接收人的已读/未读状态,只读操作。"
],
"avoid_when": [
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
],
"examples": [
"dws ding +receiver-status --ding-id <DING_ID>"
],
"reviewed": true,
"review_reason": "Agent-authored and reviewed from the built-in Shortcut intent, executable Cobra path, and declared outcome; it affects selection only and does not alter execution or safety facts.",
"source_refs": [
"internal/cli/schema_command_registry.json#ding.shortcut_receiver_status",
"cobra-help:dws ding +receiver-status",
"ShortcutRegistry:ding +receiver-status"
]
},
"ding.shortcut_send_personal": {
"agent_summary": "以本人身份发送 DING 给指定人",
"use_when": [
"当你想以自己(而非机器人)的身份直接给某些同事发 DING 强提醒,让对方看到是本人发起时使用;需提供接收人的 openDingTalkId 列表和内容,可选提醒方式与幂等 uuid,会真实向这些人发出 DING。"
],
"avoid_when": [
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
],
"examples": [
"dws ding +send-personal --users <VALUES> --content <CONTENT>"
],
"reviewed": true,
"review_reason": "Agent-authored and reviewed from the built-in Shortcut intent, executable Cobra path, and declared outcome; it affects selection only and does not alter execution or safety facts.",
"source_refs": [
"internal/cli/schema_command_registry.json#ding.shortcut_send_personal",
"cobra-help:dws ding +send-personal",
"ShortcutRegistry:ding +send-personal"
]
},
"ding.shortcut_recall_personal": {
"agent_summary": "撤回本人发起的 DING",
"use_when": [
"当你以本人身份发出的某条 DING 发错人或内容有误、想收回时使用(对应 send-personal/send-by-message 发出的 DING);需提供该 DING 的 openDingId,会真实撤回它,接收人将不再看到该提醒。"
],
"avoid_when": [
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
],
"examples": [
"dws ding +recall-personal --id <ID>"
],
"reviewed": true,
"review_reason": "Agent-authored and reviewed from the built-in Shortcut intent, executable Cobra path, and declared outcome; it affects selection only and does not alter execution or safety facts.",
"source_refs": [
"internal/cli/schema_command_registry.json#ding.shortcut_recall_personal",
"cobra-help:dws ding +recall-personal",
"ShortcutRegistry:ding +recall-personal"
]
}
},
"products": {
+333 -3
View File
@@ -328,7 +328,8 @@
],
"avoid_when": [
"已确认是 adoc 且只要正文改用 dws doc read",
"只要目录列表改用 dws drive list / wiki node list"
"只要目录列表改用 dws drive list / wiki node list",
"需要可靠文件大小 fileSize 时改用 dws drive info;文档元信息接口可能不返回大小"
],
"examples": [
"dws doc info --node <DOC_ID> --format json",
@@ -581,11 +582,12 @@
"doc.rename_document": {
"agent_summary": "兼容入口:重命名文档或文件;文件重命名能力已迁移到 drive。",
"use_when": [
"用户要改文档/文件在列表与链接中展示的名称时"
"用户要改在线文档在列表与链接中展示的名称时"
],
"avoid_when": [
"改正文标题/章节标题改用 doc block update",
"不要用 update 或重新 create 来改名"
"不要用 update 或重新 create 来改名",
"文件或文件夹重命名优先用 dws drive rename,由该命令读取真实节点类型和当前扩展名"
],
"examples": [
"dws doc rename --node <DOC_ID> --name \"新名称\" --format json"
@@ -896,6 +898,334 @@
"structured-hint:internal/cli/schema_hints/products/doc.json",
"live-mcp:dws schema doc.save_doc_version"
]
},
"doc.shortcut_search": {
"agent_summary": "按关键词搜索有权限的文档 (不传则返回最近访问)",
"use_when": [
"当你只记得文档的标题或主题词、需要先定位到某篇钉钉文档拿到它的 nodeId/URL 以便后续阅读或编辑时使用;可按关键词、扩展名、创建/访问时间、创建者等条件过滤,不传关键词则返回最近访问的文档,返回匹配的文档列表。"
],
"avoid_when": [
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
],
"examples": [
"dws doc +search --query \"会议纪要\"",
"dws doc +search --extensions pdf,docx"
],
"reviewed": true,
"review_reason": "Agent-authored and reviewed from the built-in Shortcut intent, executable Cobra path, and declared outcome; it affects selection only and does not alter execution or safety facts.",
"source_refs": [
"internal/cli/schema_command_registry.json#doc.shortcut_search",
"cobra-help:dws doc +search",
"ShortcutRegistry:doc +search"
]
},
"doc.shortcut_list": {
"agent_summary": "列出文件夹或知识库下的直接子节点",
"use_when": [
"当你已知某个文档文件夹或知识库的 ID、想浏览它下面直接包含的文档与子文件夹(不递归深层)以便逐层导航时使用;输入 folder 或 workspace,返回该层级的子节点列表。"
],
"avoid_when": [
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
],
"examples": [
"dws doc +list --folder DOC_FOLDER_NODE_ID",
"dws doc +list --workspace WS_ID --limit 20"
],
"reviewed": true,
"review_reason": "Agent-authored and reviewed from the built-in Shortcut intent, executable Cobra path, and declared outcome; it affects selection only and does not alter execution or safety facts.",
"source_refs": [
"internal/cli/schema_command_registry.json#doc.shortcut_list",
"cobra-help:dws doc +list",
"ShortcutRegistry:doc +list"
]
},
"doc.shortcut_copy": {
"agent_summary": "复制文档/文件到指定文件夹或知识库",
"use_when": [
"当你想保留原件、在另一个文件夹或知识库里生成一份文档/文件副本(例如以某篇文档为模板另存)时使用;输入源 node 与目标 folder/workspace,会实际创建一个副本。"
],
"avoid_when": [
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
],
"examples": [
"dws doc +copy --node DOC_ID --folder TARGET_FOLDER_NODE_ID"
],
"reviewed": true,
"review_reason": "Agent-authored and reviewed from the built-in Shortcut intent, executable Cobra path, and declared outcome; it affects selection only and does not alter execution or safety facts.",
"source_refs": [
"internal/cli/schema_command_registry.json#doc.shortcut_copy",
"cobra-help:dws doc +copy",
"ShortcutRegistry:doc +copy"
]
},
"doc.shortcut_move": {
"agent_summary": "移动文档/文件到指定文件夹或知识库",
"use_when": [
"当你要整理文档归属、把某篇文档/文件从当前位置挪到另一个文件夹或知识库(原位置不再保留)时使用;输入 node 与目标 folder/workspace,会实际改变文件的存放位置。"
],
"avoid_when": [
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
],
"examples": [
"dws doc +move --node DOC_ID --folder TARGET_FOLDER_NODE_ID"
],
"reviewed": true,
"review_reason": "Agent-authored and reviewed from the built-in Shortcut intent, executable Cobra path, and declared outcome; it affects selection only and does not alter execution or safety facts.",
"source_refs": [
"internal/cli/schema_command_registry.json#doc.shortcut_move",
"cobra-help:dws doc +move",
"ShortcutRegistry:doc +move"
]
},
"doc.shortcut_comment_list": {
"agent_summary": "查询文档评论列表",
"use_when": [
"当你想查看某篇文档上已有的评论、了解有哪些反馈或待处理意见(可按全文/划词、已解决/未解决过滤)时使用;输入 node,返回评论列表及其 commentKey 以便后续回复。"
],
"avoid_when": [
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
],
"examples": [
"dws doc +comment-list --node DOC_ID",
"dws doc +comment-list --node DOC_ID --type inline --resolve-status unresolved"
],
"reviewed": true,
"review_reason": "Agent-authored and reviewed from the built-in Shortcut intent, executable Cobra path, and declared outcome; it affects selection only and does not alter execution or safety facts.",
"source_refs": [
"internal/cli/schema_command_registry.json#doc.shortcut_comment_list",
"cobra-help:dws doc +comment-list",
"ShortcutRegistry:doc +comment-list"
]
},
"doc.shortcut_comment_create": {
"agent_summary": "在文档上创建一条评论",
"use_when": [
"当你想对整篇文档留一条全文评论、给出反馈或 @ 相关同事时使用;输入 node 与评论 content(可带 mention),会实际在文档上发布一条新评论。"
],
"avoid_when": [
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
],
"examples": [
"dws doc +comment-create --node DOC_ID --content \"这里需要修改\""
],
"reviewed": true,
"review_reason": "Agent-authored and reviewed from the built-in Shortcut intent, executable Cobra path, and declared outcome; it affects selection only and does not alter execution or safety facts.",
"source_refs": [
"internal/cli/schema_command_registry.json#doc.shortcut_comment_create",
"cobra-help:dws doc +comment-create",
"ShortcutRegistry:doc +comment-create"
]
},
"doc.shortcut_comment_reply": {
"agent_summary": "回复文档中的一条评论",
"use_when": [
"当你要针对某条已有评论进行回复、参与讨论或用表情贴图回应时使用;先从评论列表拿到 comment-key,再输入 node、comment-key 与 content(--emoji 则作为表情回复),会实际发布一条回复。"
],
"avoid_when": [
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
],
"examples": [
"dws doc +comment-reply --node DOC_ID --comment-key COMMENT_KEY --content \"同意\""
],
"reviewed": true,
"review_reason": "Agent-authored and reviewed from the built-in Shortcut intent, executable Cobra path, and declared outcome; it affects selection only and does not alter execution or safety facts.",
"source_refs": [
"internal/cli/schema_command_registry.json#doc.shortcut_comment_reply",
"cobra-help:dws doc +comment-reply",
"ShortcutRegistry:doc +comment-reply"
]
},
"doc.shortcut_export_submit": {
"agent_summary": "提交在线文档导出任务 (docx/markdown/pdf),返回 jobId",
"use_when": [
"当你想把在线文档导出成 docx/markdown/pdf 文件(例如离线保存或外发)时使用;这是异步任务的第一步,输入 node 与 export-format 提交导出,返回 jobId,随后用 +export-get 轮询结果。"
],
"avoid_when": [
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
],
"examples": [
"dws doc +export-submit --node DOC_ID --export-format markdown"
],
"reviewed": true,
"review_reason": "Agent-authored and reviewed from the built-in Shortcut intent, executable Cobra path, and declared outcome; it affects selection only and does not alter execution or safety facts.",
"source_refs": [
"internal/cli/schema_command_registry.json#doc.shortcut_export_submit",
"cobra-help:dws doc +export-submit",
"ShortcutRegistry:doc +export-submit"
]
},
"doc.shortcut_export_get": {
"agent_summary": "根据 jobId 查询文档导出任务结果",
"use_when": [
"当你已用 +export-submit 提交了导出任务、想查询它是否完成并拿到导出文件的下载链接时使用;输入上一步返回的 job-id,返回任务状态与结果。"
],
"avoid_when": [
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
],
"examples": [
"dws doc +export-get --job-id JOB_ID"
],
"reviewed": true,
"review_reason": "Agent-authored and reviewed from the built-in Shortcut intent, executable Cobra path, and declared outcome; it affects selection only and does not alter execution or safety facts.",
"source_refs": [
"internal/cli/schema_command_registry.json#doc.shortcut_export_get",
"cobra-help:dws doc +export-get",
"ShortcutRegistry:doc +export-get"
]
},
"doc.shortcut_version_save": {
"agent_summary": "手动保存文档版本快照",
"use_when": [
"当你在做重大改动前后、想手动打一个可回滚的版本存档点时使用;输入 node,会实际为该文档保存一个当前内容的历史版本快照。"
],
"avoid_when": [
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
],
"examples": [
"dws doc +version-save --node DOC_ID"
],
"reviewed": true,
"review_reason": "Agent-authored and reviewed from the built-in Shortcut intent, executable Cobra path, and declared outcome; it affects selection only and does not alter execution or safety facts.",
"source_refs": [
"internal/cli/schema_command_registry.json#doc.shortcut_version_save",
"cobra-help:dws doc +version-save",
"ShortcutRegistry:doc +version-save"
]
},
"doc.shortcut_version_list": {
"agent_summary": "查看文档历史版本列表",
"use_when": [
"当你想查看某篇文档有哪些历史版本、以便挑一个版本号用于回滚时使用;输入 node,返回历史版本列表及其版本号。"
],
"avoid_when": [
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
],
"examples": [
"dws doc +version-list --node DOC_ID"
],
"reviewed": true,
"review_reason": "Agent-authored and reviewed from the built-in Shortcut intent, executable Cobra path, and declared outcome; it affects selection only and does not alter execution or safety facts.",
"source_refs": [
"internal/cli/schema_command_registry.json#doc.shortcut_version_list",
"cobra-help:dws doc +version-list",
"ShortcutRegistry:doc +version-list"
]
},
"doc.shortcut_version_revert": {
"agent_summary": "回滚文档到指定历史版本",
"use_when": [
"当文档被误改、你想把它整体恢复到某个历史版本时使用;先用 +version-list 找到目标版本号,再输入 node 与 version,会实际把文档内容覆盖回该版本,属于高风险写操作,需谨慎确认。"
],
"avoid_when": [
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
],
"examples": [
"dws doc +version-revert --node DOC_ID --version 3"
],
"reviewed": true,
"review_reason": "Agent-authored and reviewed from the built-in Shortcut intent, executable Cobra path, and declared outcome; it affects selection only and does not alter execution or safety facts.",
"source_refs": [
"internal/cli/schema_command_registry.json#doc.shortcut_version_revert",
"cobra-help:dws doc +version-revert",
"ShortcutRegistry:doc +version-revert"
]
},
"doc.shortcut_template_list": {
"agent_summary": "获取文档模板列表",
"use_when": [
"当你想基于模板新建文档、需要先浏览可用的模板(自己的 MY 或公共 PUBLIC)并拿到 templateId 时使用;返回模板列表,随后可配合 +template-apply 套用。"
],
"avoid_when": [
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
],
"examples": [
"dws doc +template-list --source PUBLIC"
],
"reviewed": true,
"review_reason": "Agent-authored and reviewed from the built-in Shortcut intent, executable Cobra path, and declared outcome; it affects selection only and does not alter execution or safety facts.",
"source_refs": [
"internal/cli/schema_command_registry.json#doc.shortcut_template_list",
"cobra-help:dws doc +template-list",
"ShortcutRegistry:doc +template-list"
]
},
"doc.shortcut_template_search": {
"agent_summary": "根据关键词搜索文档模板",
"use_when": [
"当模板较多、你想按关键词(如“周报”“合同”)快速找到合适的模板并拿到 templateId 时使用;输入 query,返回匹配的模板列表,随后可配合 +template-apply 套用。"
],
"avoid_when": [
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
],
"examples": [
"dws doc +template-search --query \"周报\""
],
"reviewed": true,
"review_reason": "Agent-authored and reviewed from the built-in Shortcut intent, executable Cobra path, and declared outcome; it affects selection only and does not alter execution or safety facts.",
"source_refs": [
"internal/cli/schema_command_registry.json#doc.shortcut_template_search",
"cobra-help:dws doc +template-search",
"ShortcutRegistry:doc +template-search"
]
},
"doc.shortcut_doc_append": {
"agent_summary": "在文档末尾追加一段文本(安全追加,不改动原有内容)",
"use_when": [
"当你只想往一篇钉钉文档的最后面补一段文字、又不想动原有内容时使用;内部用文档更新的“追加(append)”模式,把你给的文本安全地拼到文档末尾,不需要你先去查文档块列表、算末尾位置或手工拼块结构。会真实写入文档内容。"
],
"avoid_when": [
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
],
"examples": [
"dws doc +doc-append --doc DOC_ID --text \"补充说明:本方案已评审通过。\"",
"dws doc +doc-append --doc \"https://alidocs.dingtalk.com/i/nodes/<DOC_UUID>\" --text \"追加一行备注\""
],
"reviewed": true,
"review_reason": "Agent-authored and reviewed from the built-in Shortcut intent, executable Cobra path, and declared outcome; it affects selection only and does not alter execution or safety facts.",
"source_refs": [
"internal/cli/schema_command_registry.json#doc.shortcut_doc_append",
"cobra-help:dws doc +doc-append",
"ShortcutRegistry:doc +doc-append"
]
},
"doc.shortcut_find_doc": {
"agent_summary": "按关键词搜索云文档并投影关键字段(只读)",
"use_when": [
"当你只记得云文档标题或内容里的某个关键词,想快速按关键词找到匹配的文档、拿到它的标题、URL、类型和 token 以便后续查看或编辑,却不想拿到一大坨原始字段时使用;内部调用云文档的 search_documents 工具,把 --query 作为搜索关键词(keyword),可选地用 --limit 限制返回条数(pageSize),再在本地把每条命中结果精简为「标题、URL、类型、token」四个字段后打印。这是纯只读操作,只做搜索与本地投影,不会创建、修改或删除任何文档;未命中时提示「没搜到文档」。"
],
"avoid_when": [
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
],
"examples": [
"dws doc +find-doc --query 季度汇报",
"dws doc +find-doc --query 合同 --limit 10"
],
"reviewed": true,
"review_reason": "Agent-authored and reviewed from the built-in Shortcut intent, executable Cobra path, and declared outcome; it affects selection only and does not alter execution or safety facts.",
"source_refs": [
"internal/cli/schema_command_registry.json#doc.shortcut_find_doc",
"cobra-help:dws doc +find-doc",
"ShortcutRegistry:doc +find-doc"
]
},
"doc.shortcut_share_doc": {
"agent_summary": "按姓名把文档链接私信发给某人(自动解析 userId)",
"use_when": [
"当你手上已经有一个文档链接、想直接私信发给某个人而不必先查 userId 时使用;内部先按姓名搜通讯录解析出唯一用户,再用 openDingTalkId 把链接拼成一条 Markdown 消息发出去,姓名匹配到多人时会列出候选让你区分。只发链接、不读取或改动文档本身,会真实发出消息。"
],
"avoid_when": [
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
],
"examples": [
"dws doc +share-doc --to 张三 --url https://docs.dingtalk.com/xxx --note \"帮忙过一下\""
],
"reviewed": true,
"review_reason": "Agent-authored and reviewed from the built-in Shortcut intent, executable Cobra path, and declared outcome; it affects selection only and does not alter execution or safety facts.",
"source_refs": [
"internal/cli/schema_command_registry.json#doc.shortcut_share_doc",
"cobra-help:dws doc +share-doc",
"ShortcutRegistry:doc +share-doc"
]
}
},
"products": {
+140 -3
View File
@@ -545,13 +545,14 @@
]
},
"drive.rename_document": {
"agent_summary": "修改文档空间中文档或文件的名称",
"agent_summary": "安全重命名文档空间中的文档、文件或文件夹,并按节点真实扩展名避免双后缀",
"use_when": [
"用户要重命名钉盘/文档空间中的文件或文档显示名称时"
"用户要重命名钉盘/文档空间中的文件、文档或文件夹时;实际执行会读取节点类型和当前扩展名,仅去掉完全匹配的一层后缀"
],
"avoid_when": [
"要改正文里的标题/章节 H1 改用 dws doc block update,不要用 rename",
"只要复制或移动改用 copy/move"
"只要复制或移动改用 copy/move",
"dry-run 不读取节点元数据,输出名称尚未做基于当前扩展名的规范化"
],
"examples": [
"dws drive rename --node <ID> --name \"新名称\" --format json"
@@ -621,6 +622,142 @@
"structured-hint:internal/cli/schema_hints/products/drive.json",
"live-mcp:none (Cobra+Skill; composite/unpinned)"
]
},
"drive.shortcut_info": {
"agent_summary": "获取钉盘文件/文件夹元数据",
"use_when": [
"当你已经知道某个节点的 dentryUuid、想查看它的详细信息(名称、大小、类型、创建/修改时间、所属空间等)而不是仅列表概览时使用;输入 node(节点 ID),返回该单个文件或文件夹的完整元数据。"
],
"avoid_when": [
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
],
"examples": [
"dws drive +info --node <dentryUuid>"
],
"reviewed": true,
"review_reason": "Agent-authored and reviewed from the built-in Shortcut intent, executable Cobra path, and declared outcome; it affects selection only and does not alter execution or safety facts.",
"source_refs": [
"internal/cli/schema_command_registry.json#drive.shortcut_info",
"cobra-help:dws drive +info",
"ShortcutRegistry:drive +info"
]
},
"drive.shortcut_search": {
"agent_summary": "搜索钉盘文件",
"use_when": [
"当你只记得文件名或内容关键词、不知道它在哪个目录时用它全局检索钉盘文件;输入 query,可按文件类型、扩展名、创建者、创建/修改时间范围过滤,返回匹配文件及其 ID,便于再做下载或整理。"
],
"avoid_when": [
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
],
"examples": [
"dws drive +search --query \"季度汇报\"",
"dws drive +search --query \"合同\" --target file --extensions pdf,docx"
],
"reviewed": true,
"review_reason": "Agent-authored and reviewed from the built-in Shortcut intent, executable Cobra path, and declared outcome; it affects selection only and does not alter execution or safety facts.",
"source_refs": [
"internal/cli/schema_command_registry.json#drive.shortcut_search",
"cobra-help:dws drive +search",
"ShortcutRegistry:drive +search"
]
},
"drive.shortcut_search_docs": {
"agent_summary": "搜索文档空间文档",
"use_when": [
"当你只记得文档标题或关键词、想在文档空间/知识库中检索在线文档(区别于 +search 检索钉盘文件)时使用;输入 query 关键词,返回匹配的文档及其节点信息。"
],
"avoid_when": [
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
],
"examples": [
"dws drive +search-docs --query \"季度汇报\""
],
"reviewed": true,
"review_reason": "Agent-authored and reviewed from the built-in Shortcut intent, executable Cobra path, and declared outcome; it affects selection only and does not alter execution or safety facts.",
"source_refs": [
"internal/cli/schema_command_registry.json#drive.shortcut_search_docs",
"cobra-help:dws drive +search-docs",
"ShortcutRegistry:drive +search-docs"
]
},
"drive.shortcut_copy": {
"agent_summary": "复制文件/文档到指定位置",
"use_when": [
"当你想保留原件、把某个文件/文档拷贝一份到指定文件夹或知识库时使用;输入源节点 node 及目标 folder/workspace,会实际生成一个副本,原文件位置不变。"
],
"avoid_when": [
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
],
"examples": [
"dws drive +copy --node <nodeId> --folder <targetFolderId>"
],
"reviewed": true,
"review_reason": "Agent-authored and reviewed from the built-in Shortcut intent, executable Cobra path, and declared outcome; it affects selection only and does not alter execution or safety facts.",
"source_refs": [
"internal/cli/schema_command_registry.json#drive.shortcut_copy",
"cobra-help:dws drive +copy",
"ShortcutRegistry:drive +copy"
]
},
"drive.shortcut_move": {
"agent_summary": "移动文件/文档到指定位置",
"use_when": [
"当你要把某个文件/文档从当前位置转移到另一个文件夹或知识库(整理归档、调整目录结构)时使用;输入源节点 node 及目标 folder/workspace,会实际改变文件所在位置,原位置不再保留该文件。"
],
"avoid_when": [
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
],
"examples": [
"dws drive +move --node <nodeId> --folder <targetFolderId>"
],
"reviewed": true,
"review_reason": "Agent-authored and reviewed from the built-in Shortcut intent, executable Cobra path, and declared outcome; it affects selection only and does not alter execution or safety facts.",
"source_refs": [
"internal/cli/schema_command_registry.json#drive.shortcut_move",
"cobra-help:dws drive +move",
"ShortcutRegistry:drive +move"
]
},
"drive.shortcut_recent": {
"agent_summary": "获取最近访问/编辑的文档列表",
"use_when": [
"当你想快速找回「我最近看过/改过的那个文档」而不记得它放在哪时使用;可按操作类型(最近访问/最近编辑)和创建人(全部/我创建/他人创建)过滤,返回近期文档列表及其节点信息。"
],
"avoid_when": [
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
],
"examples": [
"dws drive +recent",
"dws drive +recent --operate-type 1 --creator-type 1"
],
"reviewed": true,
"review_reason": "Agent-authored and reviewed from the built-in Shortcut intent, executable Cobra path, and declared outcome; it affects selection only and does not alter execution or safety facts.",
"source_refs": [
"internal/cli/schema_command_registry.json#drive.shortcut_recent",
"cobra-help:dws drive +recent",
"ShortcutRegistry:drive +recent"
]
},
"drive.shortcut_find_file": {
"agent_summary": "按名称关键词搜索钉盘文件并投影关键字段(只读)",
"use_when": [
"当你只记得钉盘文件的名字(或其中一部分),想快速按文件名关键词找到它、拿到它的 dentryId 以便后续下载/查看,却不想手动翻目录或写复杂过滤条件时使用;内部调用钉盘的 search_files 工具,把 --query 作为文件名关键词(keyword) 并限定搜索范围为钉盘文件(searchTarget=file),再在本地把每条命中结果精简为「文件名、类型、dentryId、大小」四个字段后打印。这是纯只读操作,只做搜索与本地投影,不会创建、移动或删除任何文件;未命中时返回空列表。"
],
"avoid_when": [
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
],
"examples": [
"dws drive +find-file --query 季度汇报",
"dws drive +find-file --query 合同"
],
"reviewed": true,
"review_reason": "Agent-authored and reviewed from the built-in Shortcut intent, executable Cobra path, and declared outcome; it affects selection only and does not alter execution or safety facts.",
"source_refs": [
"internal/cli/schema_command_registry.json#drive.shortcut_find_file",
"cobra-help:dws drive +find-file",
"ShortcutRegistry:drive +find-file"
]
}
},
"products": {
@@ -9,11 +9,14 @@
},
"tools": {
"event.consume": {
"agent_summary": "订阅并持续消费指定个人事件;Agent 使用 --flatten 输出顶层业务 NDJSON",
"agent_summary": "订阅并持续消费一个或多个兼容的个人事件;Agent 使用 --flatten 输出顶层业务 NDJSON",
"use_when": [
"需要实时监听 @我、指定单聊、指定群或指定发送人的后续消息事件",
"用户明确要求监听当前身份的所有单聊或所有群消息",
"需要监听指定单聊或群聊中的消息已读、撤回或表情回应事件",
"监听机器人、外部联系人等以 openDingtalkId 标识的单聊目标"
"需要监听指定群的标题变更、成员进退群或群解散事件",
"监听机器人、外部联系人等以 openDingtalkId 标识的单聊目标",
"同一目标、同一过滤条件需要同时监听多个兼容事件"
],
"avoid_when": [
"只查历史聊天记录时用 chat 查询命令",
@@ -21,7 +24,7 @@
],
"examples": [
"dws event consume user_im_message_receive_user --open-dingtalk-id open-example --flatten --max-events 1 --format ndjson",
"dws event consume user_im_message_reaction_group --group cid-example --flatten --max-events 1 --format ndjson"
"dws event consume user_im_message_receive_o2o user_im_message_read_o2o --user test-user-001 --flatten --max-events 2 --format ndjson"
],
"reviewed": true,
"review_reason": "人工依据实时 dws schema(或 Skill/Cobra/pinned MCP 对照)决策化选型文案与门禁;不改写命令身份与参数契约;示例不含 --yes。",
@@ -59,7 +62,7 @@
"event.schema": {
"agent_summary": "查询指定个人事件码的输出字段结构;Agent 应查询 --flatten 模式",
"use_when": [
"已知任一公开个人消息 event_key,消费前需要理解扁平输出字段"
"已知任一公开个人 IM event_key,消费前需要理解输出字段或保守 payload 契约"
],
"avoid_when": [
"查询 CLI 命令参数契约时用顶层 dws schema",
@@ -125,9 +128,9 @@
},
"products": {
"event": {
"agent_summary": "订阅/消费个人消息接收、已读、撤回和表情回应事件,并管理订阅生命周期",
"agent_summary": "订阅/消费个人消息、动作与群生命周期事件,并管理订阅生命周期",
"use_when": [
"需要实时监听个人消息接收、已读、撤回或表情回应事件,或管理个人事件订阅生命周期"
"需要实时监听个人消息接收、全量消息、已读、撤回、表情回应或群生命周期事件,或管理个人事件订阅生命周期"
],
"avoid_when": [
"查历史聊天或主动发消息分别用 chat 查询/发送命令"
@@ -638,6 +638,201 @@
"skills/mono/SKILL.md",
"skills/mono/references/products/mail.md"
]
},
"mail.shortcut_thread_list": {
"agent_summary": "列出指定邮箱文件夹下的邮件会话(thread)",
"use_when": [
"当你想按会话(同一往来主题的邮件串)而非单封邮件来浏览某个文件夹时使用;传入邮箱和文件夹 ID,可按时间范围和升降序筛选,返回会话列表及其 conversationId,供 +thread 查看详情。"
],
"avoid_when": [
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
],
"examples": [
"dws mail +thread-list --email user@company.com --folder 104 --limit 20"
],
"reviewed": true,
"review_reason": "Agent-authored and reviewed from the built-in Shortcut intent, executable Cobra path, and declared outcome; it affects selection only and does not alter execution or safety facts.",
"source_refs": [
"internal/cli/schema_command_registry.json#mail.shortcut_thread_list",
"cobra-help:dws mail +thread-list",
"ShortcutRegistry:mail +thread-list"
]
},
"mail.shortcut_folder_list": {
"agent_summary": "列出顶层文件夹或指定父文件夹下的子文件夹",
"use_when": [
"当你需要了解某个邮箱有哪些文件夹、或要取得文件夹 ID 以便移动邮件、按文件夹列信/建规则时使用;传入邮箱(可选父文件夹 ID 查子级),返回文件夹列表及其 ID。"
],
"avoid_when": [
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
],
"examples": [
"dws mail +folder-list --email user@company.com"
],
"reviewed": true,
"review_reason": "Agent-authored and reviewed from the built-in Shortcut intent, executable Cobra path, and declared outcome; it affects selection only and does not alter execution or safety facts.",
"source_refs": [
"internal/cli/schema_command_registry.json#mail.shortcut_folder_list",
"cobra-help:dws mail +folder-list",
"ShortcutRegistry:mail +folder-list"
]
},
"mail.shortcut_tag_list": {
"agent_summary": "列出指定邮箱下的所有邮件标签",
"use_when": [
"当你要查看邮箱里有哪些标签、或需要取得标签 ID 以便给邮件/会话加标签时使用;传入邮箱地址,返回全部邮件标签及其 ID。"
],
"avoid_when": [
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
],
"examples": [
"dws mail +tag-list --email user@company.com"
],
"reviewed": true,
"review_reason": "Agent-authored and reviewed from the built-in Shortcut intent, executable Cobra path, and declared outcome; it affects selection only and does not alter execution or safety facts.",
"source_refs": [
"internal/cli/schema_command_registry.json#mail.shortcut_tag_list",
"cobra-help:dws mail +tag-list",
"ShortcutRegistry:mail +tag-list"
]
},
"mail.shortcut_user_search": {
"agent_summary": "按关键词或工号搜索邮箱用户(仅企业邮箱)",
"use_when": [
"当你只知道同事的姓名或工号、需要查出其企业邮箱地址以便发信或添加联系人时使用;提供关键词或工号(至少其一),返回匹配的企业邮箱用户列表。"
],
"avoid_when": [
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
],
"examples": [
"dws mail +user-search --keyword \"张三\"",
"dws mail +user-search --email user@company.com --employee-no \"E123456\""
],
"reviewed": true,
"review_reason": "Agent-authored and reviewed from the built-in Shortcut intent, executable Cobra path, and declared outcome; it affects selection only and does not alter execution or safety facts.",
"source_refs": [
"internal/cli/schema_command_registry.json#mail.shortcut_user_search",
"cobra-help:dws mail +user-search",
"ShortcutRegistry:mail +user-search"
]
},
"mail.shortcut_template_list": {
"agent_summary": "列出指定邮箱的所有邮件模板",
"use_when": [
"当你想查看某个邮箱下已有哪些邮件模板、或需要取得模板 ID 以便查看详情或更新时使用;传入邮箱和每页数量,返回模板列表,支持分页。"
],
"avoid_when": [
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
],
"examples": [
"dws mail +template-list --email user@company.com --limit 20"
],
"reviewed": true,
"review_reason": "Agent-authored and reviewed from the built-in Shortcut intent, executable Cobra path, and declared outcome; it affects selection only and does not alter execution or safety facts.",
"source_refs": [
"internal/cli/schema_command_registry.json#mail.shortcut_template_list",
"cobra-help:dws mail +template-list",
"ShortcutRegistry:mail +template-list"
]
},
"mail.shortcut_contact_list": {
"agent_summary": "列出指定邮箱的所有邮件联系人",
"use_when": [
"当你想查看某邮箱通讯录里有哪些联系人、或需要取得联系人 ID 以便更新或删除时使用;传入邮箱和每页数量,返回联系人列表,支持分页。"
],
"avoid_when": [
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
],
"examples": [
"dws mail +contact-list --email user@company.com --limit 20"
],
"reviewed": true,
"review_reason": "Agent-authored and reviewed from the built-in Shortcut intent, executable Cobra path, and declared outcome; it affects selection only and does not alter execution or safety facts.",
"source_refs": [
"internal/cli/schema_command_registry.json#mail.shortcut_contact_list",
"cobra-help:dws mail +contact-list",
"ShortcutRegistry:mail +contact-list"
]
},
"mail.shortcut_find_mail_user": {
"agent_summary": "按关键词搜索邮箱联系人并投影列表(姓名/昵称/邮箱/工号等)",
"use_when": [
"当你只知道某人的姓名、花名或邮箱片段,想在企业邮箱通讯录里按关键词把匹配的邮箱用户找出来、并只看一份精简清单(姓名、昵称、邮箱地址、工号、职位、工作地)而不想拿到一大坨原始字段时使用;内部按 --query 关键词调用邮箱用户搜索,再在本地把每个匹配用户投影成整洁记录打印出来,可配合 --format/--jq/--fields。这是纯只读操作,只做搜索与本地投影,不会修改任何数据;注意仅企业邮箱可用(个人邮箱如 xxx@dingtalk.com 会因无权限报错);若没有命中则提示「没搜到邮箱联系人」。"
],
"avoid_when": [
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
],
"examples": [
"dws mail +find-mail-user --query \"张三\"",
"dws mail +find-mail-user --query alice --limit 10"
],
"reviewed": true,
"review_reason": "Agent-authored and reviewed from the built-in Shortcut intent, executable Cobra path, and declared outcome; it affects selection only and does not alter execution or safety facts.",
"source_refs": [
"internal/cli/schema_command_registry.json#mail.shortcut_find_mail_user",
"cobra-help:dws mail +find-mail-user",
"ShortcutRegistry:mail +find-mail-user"
]
},
"mail.shortcut_recent_mail": {
"agent_summary": "列出收件箱近期邮件会话并投影列表(主题/发件人/时间/threadId)",
"use_when": [
"当你想快速看一眼自己邮箱里近期的邮件会话(收件箱线程/conversation),只需要一份精简清单(主题、发件人、最后修改时间、会话 threadId)、而不想翻完整正文或原始字段时使用;内部先确定要看的邮箱地址——你可以用 --email 指定,不指定时自动取你绑定的第一个邮箱——再解析要看的文件夹——你可以用 --folder 指定文件夹 ID,不指定时自动定位收件箱——然后列出该文件夹下的近期会话,最后在本地把每条会话投影成 {subject, from, date, threadId} 打印出来,可配合 --format/--jq/--fields。这是纯只读操作,只做列举与本地投影,不会修改、发送或删除任何邮件;若最近没有邮件则提示「最近没有邮件」。"
],
"avoid_when": [
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
],
"examples": [
"dws mail +recent-mail",
"dws mail +recent-mail --limit 30"
],
"reviewed": true,
"review_reason": "Agent-authored and reviewed from the built-in Shortcut intent, executable Cobra path, and declared outcome; it affects selection only and does not alter execution or safety facts.",
"source_refs": [
"internal/cli/schema_command_registry.json#mail.shortcut_recent_mail",
"cobra-help:dws mail +recent-mail",
"ShortcutRegistry:mail +recent-mail"
]
},
"mail.shortcut_search_mail": {
"agent_summary": "按 KQL 关键词搜索邮件并投影列表(主题/发件人/时间/messageId)",
"use_when": [
"当你想按关键词(KQL 表达式,如 subject:周报、from:alice、hasAttachments:true、folderId:2 等)快速搜自己的邮件、并只看一份精简清单(主题、发件人、时间、邮件 messageId)而不想翻完整正文时使用;内部先确定要搜的邮箱地址——你可以用 --email 指定,不指定时自动取你绑定的第一个邮箱——再执行邮件搜索,最后在本地把每封邮件投影成 {subject, from, date, messageId} 打印出来,可配合 --format/--jq/--fields。这是纯只读操作,只做搜索与本地投影,不会修改、发送或删除任何邮件;若没有命中则返回空列表。"
],
"avoid_when": [
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
],
"examples": [
"dws mail +search-mail --query \"subject:周报\"",
"dws mail +search-mail --query \"from:alice AND date>2025-06-01T00:00:00Z\""
],
"reviewed": true,
"review_reason": "Agent-authored and reviewed from the built-in Shortcut intent, executable Cobra path, and declared outcome; it affects selection only and does not alter execution or safety facts.",
"source_refs": [
"internal/cli/schema_command_registry.json#mail.shortcut_search_mail",
"cobra-help:dws mail +search-mail",
"ShortcutRegistry:mail +search-mail"
]
},
"mail.shortcut_unread_mail": {
"agent_summary": "列出未读邮件并投影列表(主题/发件人/时间/messageId)",
"use_when": [
"当你想快速看自己邮箱里有哪些未读邮件、并只看一份精简清单(主题、发件人、时间、邮件 messageId)而不想翻完整正文时使用;内部先确定要查的邮箱地址——你可以用 --email 指定,不指定时自动取你绑定的第一个邮箱——再用 KQL 过滤条件 isRead:false 搜索未读邮件,最后在本地把每封邮件投影成 {subject, from, date, messageId} 打印出来,可配合 --format/--jq/--fields。这是纯只读操作,只做搜索与本地投影,不会把邮件标记为已读,也不会修改、发送或删除任何邮件;若没有未读邮件则返回空列表。"
],
"avoid_when": [
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
],
"examples": [
"dws mail +unread-mail",
"dws mail +unread-mail --email user@company.com"
],
"reviewed": true,
"review_reason": "Agent-authored and reviewed from the built-in Shortcut intent, executable Cobra path, and declared outcome; it affects selection only and does not alter execution or safety facts.",
"source_refs": [
"internal/cli/schema_command_registry.json#mail.shortcut_unread_mail",
"cobra-help:dws mail +unread-mail",
"ShortcutRegistry:mail +unread-mail"
]
}
},
"products": {
@@ -688,6 +688,122 @@
"skills/mono/references/best_practices/07-minutes.md",
"live-dws-schema:minutes.update_minutes_title"
]
},
"minutes.shortcut_list_mine": {
"agent_summary": "查询我创建的听记列表",
"use_when": [
"当你想找回自己发起或录制的某次听记(会议纪要),却只记得大概的标题关键字时使用;可按关键字筛选并分页,返回自己创建的听记列表及其 taskUuid,便于后续查看摘要、转写或待办。"
],
"avoid_when": [
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
],
"examples": [
"dws minutes +list-mine --query \"周会\" --limit 10"
],
"reviewed": true,
"review_reason": "Agent-authored and reviewed from the built-in Shortcut intent, executable Cobra path, and declared outcome; it affects selection only and does not alter execution or safety facts.",
"source_refs": [
"internal/cli/schema_command_registry.json#minutes.shortcut_list_mine",
"cobra-help:dws minutes +list-mine",
"ShortcutRegistry:minutes +list-mine"
]
},
"minutes.shortcut_list_shared": {
"agent_summary": "查询他人共享给我的听记列表",
"use_when": [
"当你要找同事分享给你的会议听记、想快速定位别人共享过来的纪要时使用;可按关键字筛选并分页,返回他人共享给你的听记列表及 taskUuid。"
],
"avoid_when": [
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
],
"examples": [
"dws minutes +list-shared --limit 20"
],
"reviewed": true,
"review_reason": "Agent-authored and reviewed from the built-in Shortcut intent, executable Cobra path, and declared outcome; it affects selection only and does not alter execution or safety facts.",
"source_refs": [
"internal/cli/schema_command_registry.json#minutes.shortcut_list_shared",
"cobra-help:dws minutes +list-shared",
"ShortcutRegistry:minutes +list-shared"
]
},
"minutes.shortcut_list_all": {
"agent_summary": "查询我有权限访问的所有听记列表",
"use_when": [
"当你不确定某条听记是自己创建还是别人共享、想在所有可访问的听记中一次性检索时使用;合并「我创建的」和「共享给我的」,按关键字筛选并分页返回全部有权限的听记及 taskUuid。"
],
"avoid_when": [
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
],
"examples": [
"dws minutes +list-all --query \"周会\" --limit 20"
],
"reviewed": true,
"review_reason": "Agent-authored and reviewed from the built-in Shortcut intent, executable Cobra path, and declared outcome; it affects selection only and does not alter execution or safety facts.",
"source_refs": [
"internal/cli/schema_command_registry.json#minutes.shortcut_list_all",
"cobra-help:dws minutes +list-all",
"ShortcutRegistry:minutes +list-all"
]
},
"minutes.shortcut_record_start": {
"agent_summary": "发起听记(开始录音)",
"use_when": [
"当你要开始一场实时会议/通话的 AI 听记、立刻启动录音并生成一条新听记任务时使用;可选传入 AI 助理会话 ID,会真实发起录音,返回新建听记的 taskUuid 供后续暂停/恢复/结束。"
],
"avoid_when": [
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
],
"examples": [
"dws minutes +record-start --session-id <sessionId>"
],
"reviewed": true,
"review_reason": "Agent-authored and reviewed from the built-in Shortcut intent, executable Cobra path, and declared outcome; it affects selection only and does not alter execution or safety facts.",
"source_refs": [
"internal/cli/schema_command_registry.json#minutes.shortcut_record_start",
"cobra-help:dws minutes +record-start",
"ShortcutRegistry:minutes +record-start"
]
},
"minutes.shortcut_detail": {
"agent_summary": "一条命令聚合取一条妙记(听记)的多项产物(基础信息/摘要/关键词/逐字稿/待办)",
"use_when": [
"当你已经有某条听记的 taskUuid,想在一次操作里同时拿到它的基础信息、AI 摘要、关键词、逐字稿和待办,而不想分别敲 4~5 个子命令再自己拼时使用;内部按 --artifacts 选择要拉的产物(默认全部:basic/summary/keywords/transcript/todos),逐个调用对应的原子工具并聚合成一个结果,某一项失败不会中断整体(会以错误字符串记录在该项下)。这是纯只读操作,不会修改听记;--direction 仅影响逐字稿排序(0=正序默认,1=倒序)。"
],
"avoid_when": [
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
],
"examples": [
"dws minutes +detail --id <taskUuid>",
"dws minutes +detail --id <taskUuid> --artifacts summary,todos"
],
"reviewed": true,
"review_reason": "Agent-authored and reviewed from the built-in Shortcut intent, executable Cobra path, and declared outcome; it affects selection only and does not alter execution or safety facts.",
"source_refs": [
"internal/cli/schema_command_registry.json#minutes.shortcut_detail",
"cobra-help:dws minutes +detail",
"ShortcutRegistry:minutes +detail"
]
},
"minutes.shortcut_replace_batch": {
"agent_summary": "对一条妙记(听记)批量执行多组文字替换(原文=>替换)",
"use_when": [
"当你要在同一条听记里一次性纠正多个词(如把多个错识别的人名/术语统一替换),而底层工具一次只能替换一组时使用;内部按多个 --pair \"原文=>替换\" 逐组调用替换工具,先在本地校验去重(同一个「原文」不能出现两次,避免两条规则互相打架),再逐组应用并聚合每组的成功/失败结果,某一组失败不会中断其余组。这是写操作,会实际修改听记文字内容,请确认 taskUuid 与替换规则无误。"
],
"avoid_when": [
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
],
"examples": [
"dws minutes +replace-batch --id <taskUuid> --pair \"张三=>张三丰\"",
"dws minutes +replace-batch --id <taskUuid> --pair \"Q2=>第二季度\" --pair \"PM=>产品经理\""
],
"reviewed": true,
"review_reason": "Agent-authored and reviewed from the built-in Shortcut intent, executable Cobra path, and declared outcome; it affects selection only and does not alter execution or safety facts.",
"source_refs": [
"internal/cli/schema_command_registry.json#minutes.shortcut_replace_batch",
"cobra-help:dws minutes +replace-batch",
"ShortcutRegistry:minutes +replace-batch"
]
}
},
"products": {
+134
View File
@@ -359,6 +359,140 @@
"structured-hint:internal/cli/schema_hints/imported/wukong.json#oa.revoke_processInstance",
"live-dws-schema:oa.revoke_processInstance"
]
},
"oa.shortcut_list_pending": {
"agent_summary": "查询待我处理的审批(时间范围为 epoch 毫秒)",
"use_when": [
"当你想知道自己当前有哪些审批还没处理、需要清理审批待办或按时间段/关键字盘点待审批单时使用;传入起止时间(epoch 毫秒,可选关键字与分页),返回待我审批的实例列表,是后续 +get 查详情、+approve/+reject 处理的入口。"
],
"avoid_when": [
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
],
"examples": [
"dws oa +list-pending --start 1741536000000 --end 1741622399000 --query 报销"
],
"reviewed": true,
"review_reason": "Agent-authored and reviewed from the built-in Shortcut intent, executable Cobra path, and declared outcome; it affects selection only and does not alter execution or safety facts.",
"source_refs": [
"internal/cli/schema_command_registry.json#oa.shortcut_list_pending",
"cobra-help:dws oa +list-pending",
"ShortcutRegistry:oa +list-pending"
]
},
"oa.shortcut_list_forms": {
"agent_summary": "获取当前用户可见的审批表单列表",
"use_when": [
"当你想浏览自己有权限发起哪些审批模板、或需要为 +list-initiated 等操作枚举 processCode 时使用;无需关键字,按游标分页返回当前用户可见的全部审批表单,适合不确定表单名称时先整体看一遍。"
],
"avoid_when": [
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
],
"examples": [
"dws oa +list-forms --cursor 0 --limit 100"
],
"reviewed": true,
"review_reason": "Agent-authored and reviewed from the built-in Shortcut intent, executable Cobra path, and declared outcome; it affects selection only and does not alter execution or safety facts.",
"source_refs": [
"internal/cli/schema_command_registry.json#oa.shortcut_list_forms",
"cobra-help:dws oa +list-forms",
"ShortcutRegistry:oa +list-forms"
]
},
"oa.shortcut_search_forms": {
"agent_summary": "按关键字模糊搜索当前用户可见的审批表单",
"use_when": [
"当你已知想找的审批大致名称(如「报销」「请假」)、想快速定位对应表单及其 processCode 时使用,比 +list-forms 全量列举更高效;传入关键字,返回名称或 processCode 匹配的表单,供后续 +list-initiated 按模板查询。"
],
"avoid_when": [
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
],
"examples": [
"dws oa +search-forms --query 报销"
],
"reviewed": true,
"review_reason": "Agent-authored and reviewed from the built-in Shortcut intent, executable Cobra path, and declared outcome; it affects selection only and does not alter execution or safety facts.",
"source_refs": [
"internal/cli/schema_command_registry.json#oa.shortcut_search_forms",
"cobra-help:dws oa +search-forms",
"ShortcutRegistry:oa +search-forms"
]
},
"oa.shortcut_list_executed": {
"agent_summary": "获取当前用户已经处理过的审批单列表",
"use_when": [
"当你想回顾自己历史上审批过(已同意/拒绝等)的单子、做复盘或查找某条已办审批时使用,区别于 +list-pending 的待办;按页码/关键字分页返回我已处理的审批单。"
],
"avoid_when": [
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
],
"examples": [
"dws oa +list-executed --limit 20 --page 1 --query 报销"
],
"reviewed": true,
"review_reason": "Agent-authored and reviewed from the built-in Shortcut intent, executable Cobra path, and declared outcome; it affects selection only and does not alter execution or safety facts.",
"source_refs": [
"internal/cli/schema_command_registry.json#oa.shortcut_list_executed",
"cobra-help:dws oa +list-executed",
"ShortcutRegistry:oa +list-executed"
]
},
"oa.shortcut_list_submitted": {
"agent_summary": "获取当前用户已发起的审批单列表",
"use_when": [
"当你想查看自己提交发起的审批单及其审批进度(如某笔报销/请假审到哪一步)时使用;按页码/关键字分页返回我发起的审批单,可据此决定是否 +revoke 撤销或催办。"
],
"avoid_when": [
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
],
"examples": [
"dws oa +list-submitted --limit 20 --page 1 --query 报销"
],
"reviewed": true,
"review_reason": "Agent-authored and reviewed from the built-in Shortcut intent, executable Cobra path, and declared outcome; it affects selection only and does not alter execution or safety facts.",
"source_refs": [
"internal/cli/schema_command_registry.json#oa.shortcut_list_submitted",
"cobra-help:dws oa +list-submitted",
"ShortcutRegistry:oa +list-submitted"
]
},
"oa.shortcut_list_cc": {
"agent_summary": "获取抄送当前用户的审批单列表",
"use_when": [
"当你想查看抄送给自己、需要知悉但无需审批的单子时使用;按页码/关键字分页返回抄送我的审批单列表,适合了解与自己相关但不用自己动手处理的审批动态。"
],
"avoid_when": [
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
],
"examples": [
"dws oa +list-cc --limit 20 --page 1 --query 报销"
],
"reviewed": true,
"review_reason": "Agent-authored and reviewed from the built-in Shortcut intent, executable Cobra path, and declared outcome; it affects selection only and does not alter execution or safety facts.",
"source_refs": [
"internal/cli/schema_command_registry.json#oa.shortcut_list_cc",
"cobra-help:dws oa +list-cc",
"ShortcutRegistry:oa +list-cc"
]
},
"oa.shortcut_my_initiated": {
"agent_summary": "列出我发起(提交)的审批单据",
"use_when": [
"当你想快速看清自己发起(提交)过哪些 OA 审批单据、方便跟进它们的进展时使用;内部直接拉取当前用户已发起的审批实例列表(等价于 dws oa approval list-submitted),再在本地把每条单据投影成标题、单号(businessId)、状态和审批实例 ID(processInstanceId) 四个关键字段。可用 --query 按关键字过滤、--page/--limit 翻页(默认第 1 页、每页 20 条)。这是纯只读操作,只做列表与本地投影,不会同意、拒绝、撤销或修改任何审批单据;若没有发起过审批则返回空列表。"
],
"avoid_when": [
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
],
"examples": [
"dws oa +my-initiated",
"dws oa +my-initiated --query 报销"
],
"reviewed": true,
"review_reason": "Agent-authored and reviewed from the built-in Shortcut intent, executable Cobra path, and declared outcome; it affects selection only and does not alter execution or safety facts.",
"source_refs": [
"internal/cli/schema_command_registry.json#oa.shortcut_my_initiated",
"cobra-help:dws oa +my-initiated",
"ShortcutRegistry:oa +my-initiated"
]
}
},
"products": {
@@ -172,6 +172,45 @@
"structured-hint:internal/cli/schema_hints/imported/wukong.json#report.get_template_details_by_name",
"live-dws-schema:report.get_template_details_by_name"
]
},
"report.shortcut_inbox_list": {
"agent_summary": "列出我收到的日报(按时间范围分页)",
"use_when": [
"当你要查看下属或同事发给自己的日报周报、想在某个时间段内浏览或审阅收到的汇报时使用;输入起止时间(ISO-8601),可按发送人 staffId 过滤,分页返回收到的日报列表及其 reportId,供后续 +entry-get 读正文。"
],
"avoid_when": [
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
],
"examples": [
"dws report +inbox-list --start \"2026-03-10T00:00:00+08:00\" --end \"2026-03-10T23:59:59+08:00\" --cursor 0 --size 20"
],
"reviewed": true,
"review_reason": "Agent-authored and reviewed from the built-in Shortcut intent, executable Cobra path, and declared outcome; it affects selection only and does not alter execution or safety facts.",
"source_refs": [
"internal/cli/schema_command_registry.json#report.shortcut_inbox_list",
"cobra-help:dws report +inbox-list",
"ShortcutRegistry:report +inbox-list"
]
},
"report.shortcut_outbox_list": {
"agent_summary": "列出我发出的日报(可选时间/模版名过滤)",
"use_when": [
"当你要回顾自己写过、提交过的日报周报,比如确认某天是否已交、找回历史汇报内容或统计提交情况时使用;可按创建/修改时间范围和模版名过滤,分页返回自己发出的日报列表及 reportId,供后续 +entry-get 查看正文。"
],
"avoid_when": [
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
],
"examples": [
"dws report +outbox-list --cursor 0 --size 20",
"dws report +outbox-list --cursor 0 --size 20 --template-name \"日报\""
],
"reviewed": true,
"review_reason": "Agent-authored and reviewed from the built-in Shortcut intent, executable Cobra path, and declared outcome; it affects selection only and does not alter execution or safety facts.",
"source_refs": [
"internal/cli/schema_command_registry.json#report.shortcut_outbox_list",
"cobra-help:dws report +outbox-list",
"ShortcutRegistry:report +outbox-list"
]
}
},
"products": {
@@ -1591,6 +1591,44 @@
"skills/mono/references/products/sheet.md",
"dws-schema-live:sheet.write_image"
]
},
"sheet.shortcut_list_sheets": {
"agent_summary": "获取表格文档中全部工作表列表",
"use_when": [
"当你拿到一个表格文档、想先了解它里面有哪些工作表(sheet)以及各自的 sheetId 时使用,通常作为读写具体数据前的第一步;传入表格文档 ID 或 URL,返回工作表清单。"
],
"avoid_when": [
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
],
"examples": [
"dws sheet +list-sheets --node NODE_ID"
],
"reviewed": true,
"review_reason": "Agent-authored and reviewed from the built-in Shortcut intent, executable Cobra path, and declared outcome; it affects selection only and does not alter execution or safety facts.",
"source_refs": [
"internal/cli/schema_command_registry.json#sheet.shortcut_list_sheets",
"cobra-help:dws sheet +list-sheets",
"ShortcutRegistry:sheet +list-sheets"
]
},
"sheet.shortcut_read": {
"agent_summary": "读取工作表指定范围的结构化单元格数据",
"use_when": [
"当你需要按单元格逐格获取数据(含类型、公式或格式化值等结构化信息)以便程序处理时使用;传入表格与可选范围(A1 表示法,不传则全部),可指定取格式化值/原始值/公式,返回结构化单元格数组。若只想要纯文本可改用 +csv-get。"
],
"avoid_when": [
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
],
"examples": [
"dws sheet +read --node NODE_ID --sheet-id SHEET_ID --range \"A1:D10\""
],
"reviewed": true,
"review_reason": "Agent-authored and reviewed from the built-in Shortcut intent, executable Cobra path, and declared outcome; it affects selection only and does not alter execution or safety facts.",
"source_refs": [
"internal/cli/schema_command_registry.json#sheet.shortcut_read",
"cobra-help:dws sheet +read",
"ShortcutRegistry:sheet +read"
]
}
},
"products": {
+220 -7
View File
@@ -103,13 +103,14 @@
]
},
"todo.add_todo_reminder": {
"agent_summary": "添加待办提醒",
"agent_summary": "写入一条待办提醒规则(上游不支持规则读回)",
"use_when": [
"已知 taskId,需要按截止时间偏移或自定义时间戳添加提醒时(dueTime 模式要求待办已有截止时间)"
"已知 taskId,需要按截止时间偏移或自定义时间戳添加提醒时(dueTime 模式要求待办已有截止时间);成功响应仅作为写入回执"
],
"avoid_when": [
"要重置/清除提醒规则时改用 dws todo task reset-reminder",
"提醒时间与目标待办未确认时不要添加"
"提醒时间与目标待办未确认时不要添加",
"业务要求写后读取并核验 reminderRules 时不要声称可验证;当前 task get/list 均不返回提醒规则"
],
"examples": [
"dws todo task add-reminder --task-id <taskId> --base-time dueTime --due-date-offset -30",
@@ -224,7 +225,8 @@
],
"avoid_when": [
"需要按条件查多个待办时改用 dws todo task list",
"需要修改待办时改用 update/done/delete 等写命令"
"需要修改待办时改用 update/done/delete 等写命令",
"需要读取或验证提醒规则时不要使用;当前详情接口不返回 reminderRules"
],
"examples": [
"dws todo task get --task-id <taskId>",
@@ -356,13 +358,14 @@
]
},
"todo.reset_todo_reminder": {
"agent_summary": "重置待办提醒",
"agent_summary": "整体替换或清除待办提醒规则(上游不支持规则读回)",
"use_when": [
"需要清除或整体替换待办提醒规则时(可不传 reminder-rules 以清除)"
"需要清除或整体替换待办提醒规则时(不传 reminder-rules 或传 [] 可清除);非空规则会在远端调用前严格校验,成功响应仅作为写入回执"
],
"avoid_when": [
"只需追加一条提醒时改用 dws todo task add-reminder",
"新规则与目标待办未确认时不要重置"
"新规则与目标待办未确认时不要重置",
"业务要求写后读取并核验 reminderRules 时不要声称可验证;当前 task get/list 均不返回提醒规则"
],
"examples": [
"dws todo task reset-reminder --task-id <taskId>",
@@ -525,6 +528,216 @@
"Wukong-parity:3306c3307",
"live-dws-schema:todo.update_todo_tag#FAILED"
]
},
"todo.shortcut_assign": {
"agent_summary": "按姓名给某人创建并指派一条待办(自动解析 userId)",
"use_when": [
"当你想把一件事指派给某位同事、但只知道对方姓名不想先查 userId 时使用;内部先按姓名解析出唯一 userId,再创建待办并把 TA 设为执行人。会真实创建待办。"
],
"avoid_when": [
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
],
"examples": [
"dws todo +assign --to 张三 --task \"整理周报\""
],
"reviewed": true,
"review_reason": "Agent-authored and reviewed from the built-in Shortcut intent, executable Cobra path, and declared outcome; it affects selection only and does not alter execution or safety facts.",
"source_refs": [
"internal/cli/schema_command_registry.json#todo.shortcut_assign",
"cobra-help:dws todo +assign",
"ShortcutRegistry:todo +assign"
]
},
"todo.shortcut_assign_multi": {
"agent_summary": "把一条待办按姓名一次性指派给多个人(自动把每个姓名解析成 userId)",
"use_when": [
"当你想把同一条待办同时指派给好几个同事、但手上只有他们的姓名而不是 userId 时使用;内部会把 --to 里的每个姓名逐个解析成唯一 userId,只要有任何一个姓名查不到或者重名有歧义,就把这些问题一次性汇总报错、并且完全不创建待办(不会建出只指派了一半人的残缺待办)。全部姓名都解析成功后,才用这些 userId 一次性创建这条待办并指派给所有人。会真实创建一条新的待办。"
],
"avoid_when": [
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
],
"examples": [
"dws todo +assign-multi --to \"张三,李四\" --task \"周五前提交排期\""
],
"reviewed": true,
"review_reason": "Agent-authored and reviewed from the built-in Shortcut intent, executable Cobra path, and declared outcome; it affects selection only and does not alter execution or safety facts.",
"source_refs": [
"internal/cli/schema_command_registry.json#todo.shortcut_assign_multi",
"cobra-help:dws todo +assign-multi",
"ShortcutRegistry:todo +assign-multi"
]
},
"todo.shortcut_created_todos": {
"agent_summary": "列出我创建的待办(我作为创建人 creator 发起的待办,而非分配给我执行的)",
"use_when": [
"当你想快速看清『哪些待办是我自己创建/发起的』,而不是别人指派给我执行的待办时使用;内部拉取你当前组织下角色为创建人(creator)的待办列表(roleTypes=[\"creator\"],creator 是待办列表支持的角色枚举之一),再在本地把每条待办投影成标题(subject)、任务 ID(taskId) 和截止时间(dueTime) 打印出来。这是纯只读操作,只做列表与投影,不会创建或修改任何待办;若没有你创建的待办则返回空列表。"
],
"avoid_when": [
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
],
"examples": [
"dws todo +created-todos"
],
"reviewed": true,
"review_reason": "Agent-authored and reviewed from the built-in Shortcut intent, executable Cobra path, and declared outcome; it affects selection only and does not alter execution or safety facts.",
"source_refs": [
"internal/cli/schema_command_registry.json#todo.shortcut_created_todos",
"cobra-help:dws todo +created-todos",
"ShortcutRegistry:todo +created-todos"
]
},
"todo.shortcut_overdue": {
"agent_summary": "列出我已过期未完成的待办",
"use_when": [
"当你想快速看清自己有哪些待办已经过了截止时间却还没做完、方便优先处理时使用;内部先拉取你当前组织下作为执行人(executor)的待办列表,再在本地按「有截止时间(dueTime) 且早于当前时刻 且尚未完成」的条件筛选,最后只打印这些逾期待办的标题(subject)、截止时间(dueTime) 和任务 ID(taskId)。这是纯只读操作,只做列表与本地过滤,不会修改或完成任何待办;若没有逾期待办则返回空列表。"
],
"avoid_when": [
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
],
"examples": [
"dws todo +overdue"
],
"reviewed": true,
"review_reason": "Agent-authored and reviewed from the built-in Shortcut intent, executable Cobra path, and declared outcome; it affects selection only and does not alter execution or safety facts.",
"source_refs": [
"internal/cli/schema_command_registry.json#todo.shortcut_overdue",
"cobra-help:dws todo +overdue",
"ShortcutRegistry:todo +overdue"
]
},
"todo.shortcut_remind": {
"agent_summary": "给自己创建一条带可选截止时间的待办",
"use_when": [
"当你想给自己记一件事、并(可选)设一个截止时间,又不想先查自己的 userId 时使用;内部先解析当前登录用户的 userId,再显式设置 executorIds,--at 只会按 ISO8601 写入截止时间 dueTime,不会创建独立提醒规则。会真实创建待办。"
],
"avoid_when": [
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令",
"用户需要独立提醒规则时改用 dws todo task add-reminder;不要把 --at 解释成提醒时间"
],
"examples": [
"dws todo +remind --task \"交周报\" --at 2026-03-10T18:00:00+08:00"
],
"reviewed": true,
"review_reason": "Agent-authored and reviewed from the built-in Shortcut intent, executable Cobra path, and declared outcome; it affects selection only and does not alter execution or safety facts.",
"source_refs": [
"internal/cli/schema_command_registry.json#todo.shortcut_remind",
"cobra-help:dws todo +remind",
"ShortcutRegistry:todo +remind"
]
},
"todo.shortcut_todo_done": {
"agent_summary": "按标题关键词把我的某条待办标记完成(自动定位 taskId)",
"use_when": [
"当你只记得某条待办的标题关键词、想直接把它标记完成,却不想先翻列表复制 taskId 时使用;内部先拉取你当前组织下作为执行人的待办列表,按标题(subject)包含关键词匹配:没匹配到会提示「没找到匹配待办」,匹配到多条会列出候选(标题+taskId)让你写得更精确,唯一命中时才把它标记为已完成。这会真实修改待办完成状态。"
],
"avoid_when": [
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
],
"examples": [
"dws todo +todo-done --task 周报"
],
"reviewed": true,
"review_reason": "Agent-authored and reviewed from the built-in Shortcut intent, executable Cobra path, and declared outcome; it affects selection only and does not alter execution or safety facts.",
"source_refs": [
"internal/cli/schema_command_registry.json#todo.shortcut_todo_done",
"cobra-help:dws todo +todo-done",
"ShortcutRegistry:todo +todo-done"
]
},
"todo.shortcut_get_my_tasks": {
"agent_summary": "查询当前组织下我的待办列表",
"use_when": [
"当你想查看自己在当前组织下的待办清单、盘点未完成事项或按条件筛选任务时使用;可按完成状态、优先级、角色(创建者/执行者/参与者)和截止时间范围过滤并分页,返回匹配的待办列表。"
],
"avoid_when": [
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
],
"examples": [
"dws todo +get-my-tasks --status false --priority 40,30"
],
"reviewed": true,
"review_reason": "Agent-authored and reviewed from the built-in Shortcut intent, executable Cobra path, and declared outcome; it affects selection only and does not alter execution or safety facts.",
"source_refs": [
"internal/cli/schema_command_registry.json#todo.shortcut_get_my_tasks",
"cobra-help:dws todo +get-my-tasks",
"ShortcutRegistry:todo +get-my-tasks"
]
},
"todo.shortcut_list_sub": {
"agent_summary": "查询子待办列表",
"use_when": [
"当你已知某个待办任务 ID、想了解它被拆解出的所有子任务时使用;输入父任务 ID,返回其下的子待办列表。"
],
"avoid_when": [
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
],
"examples": [
"dws todo +list-sub --task-id <taskId>"
],
"reviewed": true,
"review_reason": "Agent-authored and reviewed from the built-in Shortcut intent, executable Cobra path, and declared outcome; it affects selection only and does not alter execution or safety facts.",
"source_refs": [
"internal/cli/schema_command_registry.json#todo.shortcut_list_sub",
"cobra-help:dws todo +list-sub",
"ShortcutRegistry:todo +list-sub"
]
},
"todo.shortcut_get": {
"agent_summary": "查询待办详情",
"use_when": [
"当你已知某条待办的任务 ID、想查看它的完整信息(标题、执行者、截止时间、优先级、状态等)时使用;输入任务 ID,返回该待办的详细内容。"
],
"avoid_when": [
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
],
"examples": [
"dws todo +get --task-id <taskId>"
],
"reviewed": true,
"review_reason": "Agent-authored and reviewed from the built-in Shortcut intent, executable Cobra path, and declared outcome; it affects selection only and does not alter execution or safety facts.",
"source_refs": [
"internal/cli/schema_command_registry.json#todo.shortcut_get",
"cobra-help:dws todo +get",
"ShortcutRegistry:todo +get"
]
},
"todo.shortcut_list_attachment": {
"agent_summary": "查询待办任务的附件列表",
"use_when": [
"当你想查看某条待办上挂了哪些附件、或需要拿到附件 ID 以便后续删除时使用;输入任务 ID,返回该待办的附件列表。"
],
"avoid_when": [
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
],
"examples": [
"dws todo +list-attachment --task-id <taskId>"
],
"reviewed": true,
"review_reason": "Agent-authored and reviewed from the built-in Shortcut intent, executable Cobra path, and declared outcome; it affects selection only and does not alter execution or safety facts.",
"source_refs": [
"internal/cli/schema_command_registry.json#todo.shortcut_list_attachment",
"cobra-help:dws todo +list-attachment",
"ShortcutRegistry:todo +list-attachment"
]
},
"todo.shortcut_list_comment": {
"agent_summary": "查询待办评论列表",
"use_when": [
"当你想查看某条待办下的讨论记录、了解协作沟通历史或获取评论 ID 以便删除时使用;输入任务 ID 并可分页,返回该待办的评论列表。"
],
"avoid_when": [
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
],
"examples": [
"dws todo +list-comment --task-id <taskId>"
],
"reviewed": true,
"review_reason": "Agent-authored and reviewed from the built-in Shortcut intent, executable Cobra path, and declared outcome; it affects selection only and does not alter execution or safety facts.",
"source_refs": [
"internal/cli/schema_command_registry.json#todo.shortcut_list_comment",
"cobra-help:dws todo +list-comment",
"ShortcutRegistry:todo +list-comment"
]
}
},
"products": {
@@ -354,6 +354,25 @@
"structured-hint:internal/cli/schema_hints/products/wiki.json",
"live-mcp:dws schema wiki.update_member"
]
},
"wiki.shortcut_space_search": {
"agent_summary": "搜索知识库",
"use_when": [
"当你只记得知识库名称的部分关键词、想快速按名称定位某个知识库时使用;输入关键词返回匹配的知识库列表,比逐页 +space-list 更快找到目标 workspaceId。"
],
"avoid_when": [
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
],
"examples": [
"dws wiki +space-search --query \"产品文档\""
],
"reviewed": true,
"review_reason": "Agent-authored and reviewed from the built-in Shortcut intent, executable Cobra path, and declared outcome; it affects selection only and does not alter execution or safety facts.",
"source_refs": [
"internal/cli/schema_command_registry.json#wiki.shortcut_space_search",
"cobra-help:dws wiki +space-search",
"ShortcutRegistry:wiki +space-search"
]
}
},
"products": {
+126
View File
@@ -10,6 +10,8 @@ import (
"net"
"os"
"path/filepath"
"strings"
"sync/atomic"
"testing"
"time"
@@ -448,6 +450,45 @@ func TestCrossPlatformCoverageHandleConnectionProtocolEdges(t *testing.T) {
t.Fatal(err)
}
})
run(newDaemon(), func(c net.Conn) {
w, r := transport.NewWriter(c), transport.NewReader(c)
_ = w.WriteJSON(transport.Hello{Type: transport.FrameTypeHello, Role: transport.HelloRoleConsumerStop})
_ = w.WriteJSON(transport.StatusReq{Type: transport.FrameTypeStatusReq})
var resp transport.ConsumerStopResp
if err := r.ReadJSON(&resp); err != nil {
t.Fatal(err)
}
if resp.Type != transport.FrameTypeConsumerStopResp || !strings.Contains(resp.Error, "unexpected") {
t.Fatalf("consumer stop protocol error = %#v", resp)
}
})
run(newDaemon(), func(c net.Conn) {
w, r := transport.NewWriter(c), transport.NewReader(c)
_ = w.WriteJSON(transport.Hello{Type: transport.FrameTypeHello, Role: transport.HelloRoleConsumerStop})
_, _ = c.Write([]byte("{\n"))
var resp transport.ConsumerStopResp
if err := r.ReadJSON(&resp); err != nil {
t.Fatal(err)
}
if resp.Type != transport.FrameTypeConsumerStopResp || resp.Error != "malformed consumer stop request" {
t.Fatalf("malformed consumer stop response = %#v", resp)
}
})
run(newDaemon(), func(c net.Conn) {
w, r := transport.NewWriter(c), transport.NewReader(c)
_ = w.WriteJSON(transport.Hello{Type: transport.FrameTypeHello, Role: transport.HelloRoleConsumerStop})
_ = w.WriteJSON(transport.ConsumerStopReq{
Type: transport.FrameTypeConsumerStopReq,
SubscribeIDs: []string{"", "missing", "missing"},
})
var resp transport.ConsumerStopResp
if err := r.ReadJSON(&resp); err != nil {
t.Fatal(err)
}
if len(resp.NotFound) != 1 || resp.NotFound[0] != "missing" {
t.Fatalf("consumer stop response = %#v", resp)
}
})
run(newDaemon(), func(c net.Conn) {
w, r := transport.NewWriter(c), transport.NewReader(c)
_ = w.WriteJSON(transport.Hello{Type: transport.FrameTypeHello, Filter: "["})
@@ -495,6 +536,91 @@ func TestCrossPlatformCoverageHandleConnectionProtocolEdges(t *testing.T) {
})
}
func TestCrossPlatformCoverageHandleConnectionPrioritizesQueuedConsumerStop(t *testing.T) {
d := &daemon{
cfg: Config{ClientID: "client", Edition: "open", IdleTimeout: time.Second},
log: slog.New(slog.NewTextHandler(io.Discard, nil)),
hub: NewHub(2),
started: time.Now(),
idleStop: make(chan struct{}),
}
server, client := net.Pipe()
countedServer := &countingCloseConn{Conn: server}
done := make(chan struct{})
go func() {
d.handleConnection(context.Background(), countedServer)
close(done)
}()
w, r := transport.NewWriter(client), transport.NewReader(client)
if err := w.WriteJSON(transport.Hello{
Type: transport.FrameTypeHello,
SubscribeID: "sub-priority",
}); err != nil {
t.Fatal(err)
}
deadline := time.Now().Add(time.Second)
for d.hub.Len() != 1 {
if time.Now().After(deadline) {
t.Fatal("consumer was not registered")
}
time.Sleep(time.Millisecond)
}
if stopped := d.hub.StopConsumers([]string{"sub-priority"}, "priority-stop"); len(stopped) != 1 {
t.Fatalf("stopped = %#v", stopped)
}
var ack transport.HelloAck
if err := r.ReadJSON(&ack); err != nil {
t.Fatal(err)
}
var bye transport.Bye
if err := r.ReadJSON(&bye); err != nil {
t.Fatal(err)
}
if bye.Reason != "priority-stop" {
t.Fatalf("bye = %#v", bye)
}
_ = client.Close()
select {
case <-done:
case <-time.After(time.Second):
t.Fatal("connection handler did not stop")
}
if got := countedServer.closeCount.Load(); got != 1 {
t.Fatalf("underlying connection close count = %d, want 1", got)
}
}
func TestCrossPlatformCoverageEnsureCloseOnceReusesWrapper(t *testing.T) {
server, client := net.Pipe()
defer client.Close()
counted := &countingCloseConn{Conn: server}
wrapped := ensureCloseOnce(counted)
if got := ensureCloseOnce(wrapped); got != wrapped {
t.Fatal("ensureCloseOnce wrapped an already managed connection")
}
if err := wrapped.Close(); err != nil {
t.Fatal(err)
}
if err := wrapped.Close(); err != nil {
t.Fatal(err)
}
if got := counted.closeCount.Load(); got != 1 {
t.Fatalf("underlying connection close count = %d, want 1", got)
}
}
type countingCloseConn struct {
net.Conn
closeCount atomic.Int32
}
func (c *countingCloseConn) Close() error {
c.closeCount.Add(1)
return c.Conn.Close()
}
type queryConn struct{}
func (*queryConn) Read([]byte) (int, error) { return 0, io.EOF }
+97 -5
View File
@@ -22,6 +22,8 @@ import (
"net"
"os"
"path/filepath"
"sort"
"strings"
"sync"
"sync/atomic"
"time"
@@ -274,6 +276,28 @@ type daemon struct {
idleStop chan struct{}
}
// closeOnceConn makes every connection close path idempotent. A live consumer
// can be closed by its writer, the connection handler, or daemon shutdown.
type closeOnceConn struct {
net.Conn
once sync.Once
err error
}
func (c *closeOnceConn) Close() error {
c.once.Do(func() {
c.err = c.Conn.Close()
})
return c.err
}
func ensureCloseOnce(conn net.Conn) net.Conn {
if _, ok := conn.(*closeOnceConn); ok {
return conn
}
return &closeOnceConn{Conn: conn}
}
// acceptLoop drives the IPC accept goroutine. Each accepted connection is
// passed to handleConnection in its own goroutine; the accept loop returns
// when the listener Close()s (typically during shutdown).
@@ -292,6 +316,7 @@ func (d *daemon) acceptLoop(ctx context.Context) {
d.log.Warn("bus: accept error", "err", err)
continue
}
conn = ensureCloseOnce(conn)
// Track the connection before publishing the handler goroutine. Once
// acceptLoop returns, shutdown can therefore close every accepted
// connection before waiting for handlers to drain.
@@ -308,9 +333,10 @@ func (d *daemon) acceptLoop(ctx context.Context) {
// Hello → register with Hub → spawn writer goroutine → read until EOF/Bye.
// Always Unregisters and Closes on exit (plan invariant #5).
func (d *daemon) handleConnection(ctx context.Context, conn net.Conn) {
conn = ensureCloseOnce(conn)
defer func() {
d.conns.Delete(conn)
conn.Close()
_ = conn.Close()
}()
r := transport.NewReader(conn)
@@ -341,6 +367,10 @@ func (d *daemon) handleConnection(ctx context.Context, conn net.Conn) {
go d.triggerShutdown("stop_request")
return
}
if hello.Role == transport.HelloRoleConsumerStop {
d.handleConsumerStopRPC(w, r)
return
}
// Regular consumer registration
c, err := d.hub.Register(hello)
@@ -370,10 +400,28 @@ func (d *daemon) handleConnection(ctx context.Context, conn net.Conn) {
writerDone := make(chan struct{})
go func() {
defer close(writerDone)
for frame := range c.SendCh {
if err := w.WriteJSON(frame); err != nil {
// Wire error: peer dead. Returning here will let the
// reader goroutine notice EOF and Unregister.
for {
// Give an already-queued targeted stop priority over buffered
// events. The second select still handles a stop that arrives
// between this check and the blocking wait.
select {
case reason := <-c.StopCh:
_ = w.WriteJSON(transport.Bye{Type: transport.FrameTypeBye, Reason: reason})
_ = conn.Close()
return
default:
}
select {
case frame, ok := <-c.SendCh:
if !ok {
return
}
if err := w.WriteJSON(frame); err != nil {
return
}
case reason := <-c.StopCh:
_ = w.WriteJSON(transport.Bye{Type: transport.FrameTypeBye, Reason: reason})
_ = conn.Close()
return
}
}
@@ -406,6 +454,50 @@ func (d *daemon) handleConnection(ctx context.Context, conn net.Conn) {
_ = ctx // for future use (writer ctx-cancel propagation)
}
func (d *daemon) handleConsumerStopRPC(w *transport.Writer, r *transport.Reader) {
var req transport.ConsumerStopReq
if err := r.ReadJSON(&req); err != nil {
d.log.Debug("bus: malformed consumer stop request", "err", err)
_ = w.WriteJSON(transport.ConsumerStopResp{
Type: transport.FrameTypeConsumerStopResp,
Error: "malformed consumer stop request",
})
return
}
if req.Type != transport.FrameTypeConsumerStopReq {
_ = w.WriteJSON(transport.ConsumerStopResp{
Type: transport.FrameTypeConsumerStopResp,
Error: fmt.Sprintf("unexpected consumer stop request type %q", req.Type),
})
return
}
stopped := d.hub.StopConsumers(req.SubscribeIDs, transport.ByeReasonSubscriptionStopped)
stoppedSet := make(map[string]struct{}, len(stopped))
for _, id := range stopped {
stoppedSet[id] = struct{}{}
}
notFoundSet := make(map[string]struct{})
for _, id := range req.SubscribeIDs {
id = strings.TrimSpace(id)
if id == "" {
continue
}
if _, ok := stoppedSet[id]; !ok {
notFoundSet[id] = struct{}{}
}
}
notFound := make([]string, 0, len(notFoundSet))
for id := range notFoundSet {
notFound = append(notFound, id)
}
sort.Strings(notFound)
_ = w.WriteJSON(transport.ConsumerStopResp{
Type: transport.FrameTypeConsumerStopResp,
Stopped: stopped,
NotFound: notFound,
})
}
// handleStatusRPC services a single status_req and returns. The connection
// is closed by the caller's defer.
func (d *daemon) handleStatusRPC(w *transport.Writer, r *transport.Reader) {
+116
View File
@@ -17,6 +17,7 @@ import (
"context"
"errors"
"io"
"net"
"os"
"path/filepath"
"runtime"
@@ -218,6 +219,121 @@ func TestDaemon_ConsumerReceivesEvents(t *testing.T) {
<-runDone
}
func TestDaemon_ConsumerStopClosesOnlyMatchingSubscription(t *testing.T) {
skipOnWindows(t, "uses Unix socket dial")
workDir := shortTempDir(t)
sockPath := filepath.Join(workDir, "bus.sock")
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
runDone := make(chan error, 1)
go func() {
runDone <- Run(ctx, Config{
WorkDir: workDir,
IPCEndpoint: sockPath,
ClientID: "ding_test",
Edition: "open",
Source: &fakeSource{},
})
}()
defer func() { cancel(); <-runDone }()
waitForFile(t, sockPath, 2*time.Second)
connect := func(subscribeID string) (net.Conn, *transport.Reader) {
t.Helper()
conn, err := transport.Dial(sockPath)
if err != nil {
t.Fatalf("dial consumer: %v", err)
}
w := transport.NewWriter(conn)
r := transport.NewReader(conn)
if err := w.WriteJSON(transport.Hello{
Type: transport.FrameTypeHello,
ConsumerPID: os.Getpid(),
SubscribeID: subscribeID,
}); err != nil {
t.Fatalf("write hello: %v", err)
}
var ack transport.HelloAck
if err := r.ReadJSON(&ack); err != nil || ack.Type != transport.FrameTypeHelloAck {
t.Fatalf("read hello ack: %#v, %v", ack, err)
}
return conn, r
}
aConn, aReader := connect("sub-a")
defer aConn.Close()
bConn, _ := connect("sub-b")
defer bConn.Close()
ctl, err := transport.Dial(sockPath)
if err != nil {
t.Fatal(err)
}
ctlW := transport.NewWriter(ctl)
ctlR := transport.NewReader(ctl)
if err := ctlW.WriteJSON(transport.Hello{
Type: transport.FrameTypeHello,
Role: transport.HelloRoleConsumerStop,
}); err != nil {
t.Fatal(err)
}
if err := ctlW.WriteJSON(transport.ConsumerStopReq{
Type: transport.FrameTypeConsumerStopReq,
SubscribeIDs: []string{"sub-a", "missing"},
}); err != nil {
t.Fatal(err)
}
var resp transport.ConsumerStopResp
if err := ctlR.ReadJSON(&resp); err != nil {
t.Fatal(err)
}
_ = ctl.Close()
if len(resp.Stopped) != 1 || resp.Stopped[0] != "sub-a" || len(resp.NotFound) != 1 || resp.NotFound[0] != "missing" {
t.Fatalf("consumer stop response = %#v", resp)
}
var bye transport.Bye
if err := aReader.ReadJSON(&bye); err != nil {
t.Fatalf("read targeted bye: %v", err)
}
if bye.Type != transport.FrameTypeBye || bye.Reason != transport.ByeReasonSubscriptionStopped {
t.Fatalf("targeted bye = %#v", bye)
}
deadline := time.Now().Add(time.Second)
for {
status := queryDaemonStatus(t, sockPath)
if len(status.Consumers) == 1 && status.Consumers[0].SubscribeID == "sub-b" {
break
}
if time.Now().After(deadline) {
t.Fatalf("consumers after targeted stop = %#v", status.Consumers)
}
time.Sleep(10 * time.Millisecond)
}
}
func queryDaemonStatus(t *testing.T, endpoint string) transport.StatusResp {
t.Helper()
conn, err := transport.Dial(endpoint)
if err != nil {
t.Fatalf("dial status: %v", err)
}
defer conn.Close()
w := transport.NewWriter(conn)
r := transport.NewReader(conn)
if err := w.WriteJSON(transport.Hello{Type: transport.FrameTypeHello, Role: transport.HelloRoleStatus}); err != nil {
t.Fatal(err)
}
if err := w.WriteJSON(transport.StatusReq{Type: transport.FrameTypeStatusReq}); err != nil {
t.Fatal(err)
}
var status transport.StatusResp
if err := r.ReadJSON(&status); err != nil {
t.Fatal(err)
}
return status
}
func TestDaemon_LockBusyOnSecondRun(t *testing.T) {
skipOnWindows(t, "uses Unix socket / flock semantics")
workDir := shortTempDir(t)
+49 -1
View File
@@ -41,7 +41,8 @@ type Consumer struct {
Filter string // raw regex from Hello (for status display)
SubscribeID string // optional personal subscription label and local isolation key
SubscribedAt time.Time
SendCh chan any // bus → consume frames (Event/SourceState/Heartbeat/Bye)
SendCh chan any // bus → consume frames (Event/SourceState/Heartbeat/Bye)
StopCh chan string // targeted local stop reason; consumed by daemon writer
matcher consumerMatcher
sendMu sync.Mutex // serialises Deliver/Broadcast with SendCh close
closed bool // guarded by sendMu
@@ -192,12 +193,59 @@ func (h *Hub) Register(hello transport.Hello) (*Consumer, error) {
SubscribeID: strings.TrimSpace(hello.SubscribeID),
SubscribedAt: time.Now().UTC(),
SendCh: make(chan any, h.bufferSize),
StopCh: make(chan string, 1),
matcher: m,
}
h.consumers[c.ID] = c
return c, nil
}
// StopConsumers requests a graceful close for every consumer whose exact
// SubscribeID appears in subscribeIDs. The daemon writer owns the wire, so
// this method signals StopCh instead of writing or closing SendCh directly.
func (h *Hub) StopConsumers(subscribeIDs []string, reason string) []string {
targets := make(map[string]struct{}, len(subscribeIDs))
for _, id := range subscribeIDs {
if id = strings.TrimSpace(id); id != "" {
targets[id] = struct{}{}
}
}
if len(targets) == 0 {
return nil
}
if strings.TrimSpace(reason) == "" {
reason = transport.ByeReasonSubscriptionStopped
}
matched := make(map[string]struct{}, len(targets))
stopTargets := make([]*Consumer, 0, len(targets))
h.mu.RLock()
for _, c := range h.consumers {
id := strings.TrimSpace(c.SubscribeID)
if _, ok := targets[id]; !ok {
continue
}
matched[id] = struct{}{}
stopTargets = append(stopTargets, c)
}
h.mu.RUnlock()
for _, target := range stopTargets {
select {
case target.StopCh <- reason:
default:
// A stop is already queued for this consumer.
}
}
out := make([]string, 0, len(matched))
for id := range matched {
out = append(out, id)
}
sort.Strings(out)
return out
}
// Unregister removes a consumer by ID and closes its sendCh. Idempotent —
// calling twice or on an unknown ID is a no-op. closeSend shares the same
// per-consumer lock as Deliver/Broadcast, so a stale Hub snapshot cannot send
+80
View File
@@ -15,6 +15,7 @@ package bus
import (
"errors"
"fmt"
"sync"
"testing"
"time"
@@ -290,6 +291,85 @@ func TestHub_UnregisterIdempotent(t *testing.T) {
h.Unregister(9999) // unknown ID
}
func TestHub_StopConsumersTargetsExactSubscribeID(t *testing.T) {
h := NewHub(10)
if stopped := h.StopConsumers([]string{"", " "}, "ignored"); stopped != nil {
t.Fatalf("empty targets stopped = %#v", stopped)
}
a, err := h.Register(transport.Hello{SubscribeID: "sub-a"})
if err != nil {
t.Fatal(err)
}
b, err := h.Register(transport.Hello{SubscribeID: "sub-b"})
if err != nil {
t.Fatal(err)
}
stopped := h.StopConsumers([]string{" sub-a ", "sub-a", "missing"}, "")
if len(stopped) != 1 || stopped[0] != "sub-a" {
t.Fatalf("stopped = %#v, want [sub-a]", stopped)
}
select {
case reason := <-a.StopCh:
if reason != transport.ByeReasonSubscriptionStopped {
t.Fatalf("reason = %q", reason)
}
case <-time.After(time.Second):
t.Fatal("sub-a stop was not signalled")
}
select {
case reason := <-b.StopCh:
t.Fatalf("sub-b was stopped: %q", reason)
case <-time.After(50 * time.Millisecond):
}
}
func TestHub_StopConsumersCoalescesQueuedStop(t *testing.T) {
h := NewHub(10)
c, err := h.Register(transport.Hello{SubscribeID: "sub-a"})
if err != nil {
t.Fatal(err)
}
if got := h.StopConsumers([]string{"sub-a"}, "first"); len(got) != 1 {
t.Fatalf("first stop = %#v", got)
}
if got := h.StopConsumers([]string{"sub-a"}, "second"); len(got) != 1 {
t.Fatalf("second stop = %#v", got)
}
if reason := <-c.StopCh; reason != "first" {
t.Fatalf("queued reason = %q, want first", reason)
}
select {
case reason := <-c.StopCh:
t.Fatalf("duplicate stop queued: %q", reason)
default:
}
}
func TestHub_ConcurrentStopConsumersRegisterUnregister(t *testing.T) {
h := NewHub(4)
const workers = 32
var wg sync.WaitGroup
for i := 0; i < workers; i++ {
wg.Add(1)
go func(i int) {
defer wg.Done()
subscribeID := fmt.Sprintf("sub-%d", i)
c, err := h.Register(transport.Hello{SubscribeID: subscribeID})
if err != nil {
t.Errorf("register: %v", err)
return
}
h.StopConsumers([]string{subscribeID}, "concurrent-stop")
h.Unregister(c.ID)
}(i)
}
wg.Wait()
if got := h.Len(); got != 0 {
t.Fatalf("remaining consumers = %d", got)
}
}
func TestHub_ConcurrentDeliverBroadcastUnregister(t *testing.T) {
for iteration := 0; iteration < 200; iteration++ {
h := NewHub(4)
+78
View File
@@ -0,0 +1,78 @@
// Copyright 2026 Alibaba Group
// Licensed under the Apache License, Version 2.0 (the "License");
package busctl
import (
"errors"
"fmt"
"os"
"strings"
"time"
"github.com/DingTalk-Real-AI/dingtalk-workspace-cli/internal/event/transport"
)
// ErrConsumerStopUnsupported lets callers fall back to the legacy process
// signal when an already-running bus predates the targeted stop protocol.
var ErrConsumerStopUnsupported = errors.New("busctl: targeted consumer stop is unsupported")
var consumerStopDial = transport.Dial
// StopConsumers asks a running bus to close only consumers matching the
// supplied personal subscription IDs.
func StopConsumers(endpoint string, subscribeIDs []string) (transport.ConsumerStopResp, error) {
var empty transport.ConsumerStopResp
if strings.TrimSpace(endpoint) == "" {
return empty, errors.New("busctl: consumer stop endpoint is required")
}
ids := make([]string, 0, len(subscribeIDs))
seen := map[string]struct{}{}
for _, id := range subscribeIDs {
id = strings.TrimSpace(id)
if id == "" {
continue
}
if _, ok := seen[id]; ok {
continue
}
seen[id] = struct{}{}
ids = append(ids, id)
}
if len(ids) == 0 {
return empty, errors.New("busctl: at least one subscribe_id is required")
}
conn, err := consumerStopDial(endpoint)
if err != nil {
return empty, fmt.Errorf("busctl: dial bus for consumer stop: %w", err)
}
defer conn.Close()
_ = conn.SetDeadline(time.Now().Add(DefaultStatusRPCTimeout))
w := transport.NewWriter(conn)
r := transport.NewReader(conn)
if err := w.WriteJSON(transport.Hello{
Type: transport.FrameTypeHello,
ConsumerPID: os.Getpid(),
Role: transport.HelloRoleConsumerStop,
}); err != nil {
return empty, fmt.Errorf("busctl: write consumer stop hello: %w", err)
}
if err := w.WriteJSON(transport.ConsumerStopReq{
Type: transport.FrameTypeConsumerStopReq,
SubscribeIDs: ids,
}); err != nil {
return empty, fmt.Errorf("busctl: write consumer stop request: %w", err)
}
var resp transport.ConsumerStopResp
if err := r.ReadJSON(&resp); err != nil {
return empty, fmt.Errorf("busctl: read consumer stop response: %w", err)
}
if resp.Type != transport.FrameTypeConsumerStopResp {
return empty, fmt.Errorf("%w: unexpected response type %q", ErrConsumerStopUnsupported, resp.Type)
}
if strings.TrimSpace(resp.Error) != "" {
return empty, fmt.Errorf("busctl: consumer stop request rejected: %s", resp.Error)
}
return resp, nil
}
+161
View File
@@ -0,0 +1,161 @@
// Copyright 2026 Alibaba Group
// Licensed under the Apache License, Version 2.0 (the "License");
package busctl
import (
"errors"
"net"
"reflect"
"strings"
"testing"
"github.com/DingTalk-Real-AI/dingtalk-workspace-cli/internal/event/transport"
)
func TestStopConsumersRoundTrip(t *testing.T) {
client, server := net.Pipe()
oldDial := consumerStopDial
consumerStopDial = func(string) (net.Conn, error) { return client, nil }
t.Cleanup(func() {
consumerStopDial = oldDial
_ = server.Close()
})
requestCh := make(chan transport.ConsumerStopReq, 1)
errCh := make(chan error, 1)
go func() {
r := transport.NewReader(server)
w := transport.NewWriter(server)
var hello transport.Hello
if err := r.ReadJSON(&hello); err != nil {
errCh <- err
return
}
if hello.Role != transport.HelloRoleConsumerStop {
errCh <- errors.New("unexpected hello role")
return
}
var req transport.ConsumerStopReq
if err := r.ReadJSON(&req); err != nil {
errCh <- err
return
}
requestCh <- req
errCh <- w.WriteJSON(transport.ConsumerStopResp{
Type: transport.FrameTypeConsumerStopResp,
Stopped: []string{"sub-a"},
NotFound: []string{"sub-b"},
})
}()
resp, err := StopConsumers("pipe", []string{" sub-a ", "sub-a", "", "sub-b"})
if err != nil {
t.Fatalf("StopConsumers() error = %v", err)
}
if !reflect.DeepEqual(resp.Stopped, []string{"sub-a"}) || !reflect.DeepEqual(resp.NotFound, []string{"sub-b"}) {
t.Fatalf("response = %#v", resp)
}
if err := <-errCh; err != nil {
t.Fatalf("server error = %v", err)
}
req := <-requestCh
if req.Type != transport.FrameTypeConsumerStopReq || !reflect.DeepEqual(req.SubscribeIDs, []string{"sub-a", "sub-b"}) {
t.Fatalf("request = %#v", req)
}
}
func TestStopConsumersDetectsLegacyBus(t *testing.T) {
client, server := net.Pipe()
oldDial := consumerStopDial
consumerStopDial = func(string) (net.Conn, error) { return client, nil }
t.Cleanup(func() {
consumerStopDial = oldDial
_ = server.Close()
})
go func() {
r := transport.NewReader(server)
w := transport.NewWriter(server)
var hello transport.Hello
_ = r.ReadJSON(&hello)
var req transport.ConsumerStopReq
_ = r.ReadJSON(&req)
_ = w.WriteJSON(transport.HelloAck{Type: transport.FrameTypeHelloAck, BusPID: 1})
}()
_, err := StopConsumers("pipe", []string{"sub-a"})
if !errors.Is(err, ErrConsumerStopUnsupported) {
t.Fatalf("StopConsumers() error = %v, want ErrConsumerStopUnsupported", err)
}
}
func TestStopConsumersReturnsServerProtocolError(t *testing.T) {
client, server := net.Pipe()
oldDial := consumerStopDial
consumerStopDial = func(string) (net.Conn, error) { return client, nil }
t.Cleanup(func() {
consumerStopDial = oldDial
_ = server.Close()
})
go func() {
r := transport.NewReader(server)
w := transport.NewWriter(server)
var hello transport.Hello
_ = r.ReadJSON(&hello)
var req transport.ConsumerStopReq
_ = r.ReadJSON(&req)
_ = w.WriteJSON(transport.ConsumerStopResp{
Type: transport.FrameTypeConsumerStopResp,
Error: "malformed consumer stop request",
})
}()
_, err := StopConsumers("pipe", []string{"sub-a"})
if err == nil || !strings.Contains(err.Error(), "malformed consumer stop request") {
t.Fatalf("StopConsumers() error = %v", err)
}
}
func TestStopConsumersValidatesInputAndDialErrors(t *testing.T) {
if _, err := StopConsumers("", []string{"sub-a"}); err == nil {
t.Fatal("empty endpoint succeeded")
}
if _, err := StopConsumers("pipe", []string{"", " "}); err == nil {
t.Fatal("empty subscriptions succeeded")
}
wantErr := errors.New("dial")
oldDial := consumerStopDial
consumerStopDial = func(string) (net.Conn, error) { return nil, wantErr }
t.Cleanup(func() { consumerStopDial = oldDial })
if _, err := StopConsumers("pipe", []string{"sub-a"}); !errors.Is(err, wantErr) {
t.Fatalf("dial error = %v", err)
}
}
func TestCrossPlatformCoverageStopConsumersProtocolErrors(t *testing.T) {
oldDial := consumerStopDial
t.Cleanup(func() { consumerStopDial = oldDial })
for _, test := range []struct {
name string
failAt int
want string
}{
{name: "hello write", failAt: 1, want: "write consumer stop hello"},
{name: "request write", failAt: 2, want: "write consumer stop request"},
{name: "response read", failAt: 0, want: "read consumer stop response"},
} {
t.Run(test.name, func(t *testing.T) {
consumerStopDial = func(string) (net.Conn, error) {
return &queryErrorConn{failAt: test.failAt}, nil
}
_, err := StopConsumers("pipe", []string{"sub-a"})
if !errors.Is(err, errBusctlInjected) || !strings.Contains(err.Error(), test.want) {
t.Fatalf("error = %v, want %q", err, test.want)
}
})
}
}
+314
View File
@@ -0,0 +1,314 @@
// Copyright 2026 Alibaba Group
// Licensed under the Apache License, Version 2.0 (the "License");
package consume
import (
"context"
"encoding/json"
"errors"
"fmt"
"io"
"net"
"os"
"sort"
"strings"
"time"
"github.com/DingTalk-Real-AI/dingtalk-workspace-cli/internal/event/busctl"
"github.com/DingTalk-Real-AI/dingtalk-workspace-cli/internal/event/transport"
)
// ConsumerSpec describes one logical subscription within a multi-event
// foreground process. Each spec gets an independent IPC connection so the bus
// can continue enforcing the exact event_type + subscribe_id pair.
type ConsumerSpec struct {
EventKey string
EventTypes []string
Filter string
SubscribeID string
ReadySubscribeID string
}
type manySession struct {
spec ConsumerSpec
conn net.Conn
w *transport.Writer
r *transport.Reader
ack transport.HelloAck
}
type manyFrame struct {
index int
raw []byte
err error
}
var runManyIsCtxCancelled = isCtxCancelled
// RunMany consumes multiple independently isolated subscriptions in one
// process. It shares one formatter/sink pipeline and one command lifecycle,
// while retaining one bus IPC connection per subscription.
func RunMany(ctx context.Context, cfg Config, specs []ConsumerSpec) error {
if cfg.WorkDir == "" || cfg.IPCEndpoint == "" || cfg.ClientID == "" {
return errors.New("consume: WorkDir, IPCEndpoint, and ClientID are required")
}
if len(specs) < 2 {
return errors.New("consume: RunMany requires at least two consumers")
}
if err := validateConsumerSpecs(specs); err != nil {
return err
}
if cfg.Stdout == nil {
cfg.Stdout = os.Stdout
}
if cfg.Stderr == nil {
cfg.Stderr = os.Stderr
}
if cfg.Quiet {
cfg.Stderr = io.Discard
}
if cfg.Format == "" {
cfg.Format = FormatNDJSON
}
if cfg.DryRun {
PrintDryRunMany(cfg.Stderr, cfg, specs)
return nil
}
parentCtx := ctx
var timeoutCtx context.Context
if cfg.Duration > 0 {
var cancel context.CancelFunc
timeoutCtx, cancel = context.WithTimeout(ctx, cfg.Duration)
defer cancel()
ctx = timeoutCtx
}
runCtx, cancelRun := context.WithCancel(ctx)
defer cancelRun()
ctx = runCtx
pipeline, err := BuildPipeline(
cfg.Format,
cfg.OutputDir,
cfg.Routes,
cfg.Stdout,
WithProjector(cfg.Projector),
WithProjectionWarnings(cfg.Stderr),
)
if err != nil {
return fmt.Errorf("consume: build pipeline: %w", err)
}
defer pipeline.Close()
sessions := make([]*manySession, 0, len(specs))
closeSessions := func() {
for _, session := range sessions {
_ = session.conn.Close()
}
}
defer closeSessions()
for _, spec := range specs {
conn, err := discoverBus(busctl.DiscoverConfig{
WorkDir: cfg.WorkDir,
IPCEndpoint: cfg.IPCEndpoint,
ClientID: cfg.ClientID,
SpawnExtraArgs: cfg.SpawnExtraArgs,
})
if err != nil {
return fmt.Errorf("consume: discover bus for %s: %w", spec.EventKey, err)
}
session := &manySession{
spec: spec,
conn: conn,
w: transport.NewWriter(conn),
r: transport.NewReader(conn),
}
sessions = append(sessions, session)
closeOnContext(ctx, session.conn)
if err := session.w.WriteJSON(transport.Hello{
Type: transport.FrameTypeHello,
ConsumerPID: os.Getpid(),
EventTypes: spec.EventTypes,
Filter: spec.Filter,
SubscribeID: spec.SubscribeID,
Compact: cfg.Compact,
}); err != nil {
return fmt.Errorf("consume: write hello for %s: %w", spec.EventKey, err)
}
if err := session.r.ReadJSON(&session.ack); err != nil {
return fmt.Errorf("consume: read hello_ack for %s: %w", spec.EventKey, err)
}
if session.ack.Type != transport.FrameTypeHelloAck {
return fmt.Errorf("consume: unexpected first frame type %q for %s", session.ack.Type, spec.EventKey)
}
if len(sessions) > 1 && session.ack.BusPID != sessions[0].ack.BusPID {
return fmt.Errorf("consume: consumers connected to different bus processes (%d and %d)", sessions[0].ack.BusPID, session.ack.BusPID)
}
}
if !cfg.Quiet {
for _, session := range sessions {
fmt.Fprintf(cfg.Stderr, "[event] subscription event_key=%s subscribe_id=%s\n",
session.spec.EventKey, session.spec.ReadySubscribeID)
}
fmt.Fprintf(cfg.Stderr, "[event] ready event_count=%d bus_pid=%d\n", len(sessions), sessions[0].ack.BusPID)
fmt.Fprintf(cfg.Stderr, "[event] bus source=%s state=%s idle_timeout=%ds\n",
sessions[0].ack.StateSource, sessions[0].ack.SourceState, sessions[0].ack.IdleTimeoutSecs)
}
if cfg.Stdin != nil {
go watchStdinEOF(runCtx, cfg.Stdin, cfg.Stderr, cancelRun)
}
frames := make(chan manyFrame, len(sessions)*2)
active := make(map[int]struct{}, len(sessions))
for index, session := range sessions {
active[index] = struct{}{}
go readManySession(ctx, index, session, frames)
}
received := 0
start := time.Now()
reason := ""
defer func() {
if !cfg.Quiet && reason != "" {
fmt.Fprintf(cfg.Stderr, "[event] exited — received %d event(s) in %s (reason: %s)\n",
received, time.Since(start).Round(time.Millisecond), reason)
}
}()
classifyCancel := func() string {
if timeoutCtx != nil && errors.Is(timeoutCtx.Err(), context.DeadlineExceeded) && parentCtx.Err() == nil {
return "timeout"
}
return "signal"
}
for len(active) > 0 {
select {
case <-ctx.Done():
reason = classifyCancel()
return nil
case frame := <-frames:
if frame.err != nil {
if runManyIsCtxCancelled(ctx) {
reason = classifyCancel()
return nil
}
return fmt.Errorf("consume: read frame for %s: %w", sessions[frame.index].spec.EventKey, frame.err)
}
typ, err := transport.PeekType(frame.raw)
if err != nil {
continue
}
switch typ {
case transport.FrameTypeEvent:
var ev transport.Event
if err := json.Unmarshal(frame.raw, &ev); err != nil {
continue
}
if err := pipeline.Deliver(ev); err != nil {
if errors.Is(err, ErrPipeClosed) {
sendManyBye(sessions, active, "client_done")
reason = "signal"
return nil
}
return fmt.Errorf("consume: deliver event: %w", err)
}
received++
if cfg.MaxEvents > 0 && received >= cfg.MaxEvents {
sendManyBye(sessions, active, "client_done")
reason = "limit"
return nil
}
case transport.FrameTypeBye:
var bye transport.Bye
_ = json.Unmarshal(frame.raw, &bye)
if bye.Reason == transport.ByeReasonSubscriptionStopped {
delete(active, frame.index)
_ = sessions[frame.index].conn.Close()
if !cfg.Quiet {
fmt.Fprintf(cfg.Stderr, "[event] subscription stopped event_key=%s subscribe_id=%s remaining=%d\n",
sessions[frame.index].spec.EventKey,
sessions[frame.index].spec.ReadySubscribeID,
len(active))
}
continue
}
if !cfg.Quiet {
fmt.Fprintf(cfg.Stderr, "[event] bus closing: %s\n", bye.Reason)
}
reason = "bus_shutdown"
return nil
case transport.FrameTypeSourceState:
if !cfg.Quiet && frame.index == firstActiveSession(active) {
var state transport.SourceState
_ = json.Unmarshal(frame.raw, &state)
fmt.Fprintf(cfg.Stderr, "source state: %s (source=%s, attempt=%d)\n", state.State, state.StateSource, state.Attempt)
}
case transport.FrameTypeHeartbeat:
// silent
}
}
}
reason = "subscriptions_stopped"
return nil
}
func validateConsumerSpecs(specs []ConsumerSpec) error {
seen := map[string]struct{}{}
for _, spec := range specs {
if strings.TrimSpace(spec.EventKey) == "" {
return errors.New("consume: consumer event_key is required")
}
id := strings.TrimSpace(spec.SubscribeID)
if id == "" {
return fmt.Errorf("consume: subscribe_id is required for %s", spec.EventKey)
}
if _, ok := seen[id]; ok {
return fmt.Errorf("consume: duplicate subscribe_id %q", id)
}
seen[id] = struct{}{}
}
return nil
}
func readManySession(ctx context.Context, index int, session *manySession, out chan<- manyFrame) {
for {
raw, err := session.r.Read()
frame := manyFrame{index: index, raw: raw, err: err}
select {
case out <- frame:
case <-ctx.Done():
return
}
if err != nil {
return
}
if typ, _ := transport.PeekType(raw); typ == transport.FrameTypeBye {
return
}
}
}
func sendManyBye(sessions []*manySession, active map[int]struct{}, reason string) {
indices := make([]int, 0, len(active))
for index := range active {
indices = append(indices, index)
}
sort.Ints(indices)
for _, index := range indices {
_ = sessions[index].w.WriteJSON(transport.Bye{Type: transport.FrameTypeBye, Reason: reason})
}
}
func firstActiveSession(active map[int]struct{}) int {
first := -1
for index := range active {
if first == -1 || index < first {
first = index
}
}
return first
}
+535
View File
@@ -0,0 +1,535 @@
// Copyright 2026 Alibaba Group
// Licensed under the Apache License, Version 2.0 (the "License");
package consume
import (
"bytes"
"context"
"encoding/json"
"errors"
"io"
"net"
"strings"
"sync"
"testing"
"time"
"github.com/DingTalk-Real-AI/dingtalk-workspace-cli/internal/event/busctl"
"github.com/DingTalk-Real-AI/dingtalk-workspace-cli/internal/event/transport"
)
type synchronizedBuffer struct {
mu sync.Mutex
b bytes.Buffer
}
func (b *synchronizedBuffer) Write(p []byte) (int, error) {
b.mu.Lock()
defer b.mu.Unlock()
return b.b.Write(p)
}
func (b *synchronizedBuffer) String() string {
b.mu.Lock()
defer b.mu.Unlock()
return b.b.String()
}
type manyFakeBus struct {
client net.Conn
server net.Conn
hello chan transport.Hello
acked chan struct{}
ackGate <-chan struct{}
ack transport.HelloAck
send chan any
}
func newManyFakeBus(busPID int, ackGate <-chan struct{}) *manyFakeBus {
client, server := net.Pipe()
f := &manyFakeBus{
client: client,
server: server,
hello: make(chan transport.Hello, 1),
acked: make(chan struct{}),
ackGate: ackGate,
ack: transport.HelloAck{
Type: transport.FrameTypeHelloAck,
BusPID: busPID,
SourceState: "connected",
StateSource: "inferred",
IdleTimeoutSecs: 300,
},
send: make(chan any, 8),
}
go f.serve()
return f
}
func (f *manyFakeBus) serve() {
r := transport.NewReader(f.server)
w := transport.NewWriter(f.server)
var hello transport.Hello
if err := r.ReadJSON(&hello); err != nil {
return
}
f.hello <- hello
if f.ackGate != nil {
select {
case <-f.ackGate:
case <-time.After(5 * time.Second):
return
}
}
if err := w.WriteJSON(f.ack); err != nil {
return
}
close(f.acked)
go func() {
for {
if _, err := r.Read(); err != nil {
return
}
}
}()
for frame := range f.send {
if err := w.WriteJSON(frame); err != nil {
return
}
}
}
func installManyDiscover(t *testing.T, buses ...*manyFakeBus) {
t.Helper()
oldDiscover := discoverBus
index := 0
discoverBus = func(busctl.DiscoverConfig) (net.Conn, error) {
if index >= len(buses) {
return nil, errors.New("unexpected discover call")
}
conn := buses[index].client
index++
return conn, nil
}
t.Cleanup(func() {
discoverBus = oldDiscover
for _, bus := range buses {
_ = bus.client.Close()
_ = bus.server.Close()
close(bus.send)
}
})
}
func manyTestConfig(stdout, stderr io.Writer) Config {
return Config{
WorkDir: "workdir",
IPCEndpoint: "endpoint",
ClientID: "client",
Stdout: stdout,
Stderr: stderr,
Format: FormatNDJSON,
}
}
func manyTestSpecs() []ConsumerSpec {
return []ConsumerSpec{
{EventKey: "event-a", EventTypes: []string{"event-a"}, SubscribeID: "sub-a", ReadySubscribeID: "sub-a"},
{EventKey: "event-b", EventTypes: []string{"event-b"}, SubscribeID: "sub-b", ReadySubscribeID: "sub-b"},
}
}
func waitForBuffer(t *testing.T, b *synchronizedBuffer, want string) {
t.Helper()
deadline := time.Now().Add(2 * time.Second)
for time.Now().Before(deadline) {
if strings.Contains(b.String(), want) {
return
}
time.Sleep(5 * time.Millisecond)
}
t.Fatalf("buffer did not contain %q:\n%s", want, b.String())
}
func TestRunManyWaitsForAllConsumersAndStopsOneAtATime(t *testing.T) {
secondAck := make(chan struct{})
busA := newManyFakeBus(101, nil)
busB := newManyFakeBus(101, secondAck)
installManyDiscover(t, busA, busB)
var stdout, stderr synchronizedBuffer
done := make(chan error, 1)
go func() {
done <- RunMany(context.Background(), manyTestConfig(&stdout, &stderr), manyTestSpecs())
}()
helloA := <-busA.hello
helloB := <-busB.hello
if helloA.SubscribeID != "sub-a" || strings.Join(helloA.EventTypes, ",") != "event-a" {
t.Fatalf("first hello = %#v", helloA)
}
if helloB.SubscribeID != "sub-b" || strings.Join(helloB.EventTypes, ",") != "event-b" {
t.Fatalf("second hello = %#v", helloB)
}
select {
case err := <-done:
t.Fatalf("RunMany exited before all acknowledgements: %v", err)
default:
}
if strings.Contains(stderr.String(), "[event] ready") || strings.Contains(stderr.String(), "[event] subscription") {
t.Fatalf("ready output appeared before all acknowledgements:\n%s", stderr.String())
}
close(secondAck)
waitForBuffer(t, &stderr, "[event] ready event_count=2 bus_pid=101")
if !strings.Contains(stderr.String(), "event_key=event-a subscribe_id=sub-a") ||
!strings.Contains(stderr.String(), "event_key=event-b subscribe_id=sub-b") {
t.Fatalf("subscription markers missing:\n%s", stderr.String())
}
busA.send <- transport.Event{Type: transport.FrameTypeEvent, EventID: "event-1", EventType: "event-a", SubscribeID: "sub-a", Data: `{}`}
waitForBuffer(t, &stdout, `"event_id":"event-1"`)
busA.send <- transport.Bye{Type: transport.FrameTypeBye, Reason: transport.ByeReasonSubscriptionStopped}
waitForBuffer(t, &stderr, "subscribe_id=sub-a remaining=1")
busB.send <- transport.Event{Type: transport.FrameTypeEvent, EventID: "event-2", EventType: "event-b", SubscribeID: "sub-b", Data: `{}`}
waitForBuffer(t, &stdout, `"event_id":"event-2"`)
busB.send <- transport.Bye{Type: transport.FrameTypeBye, Reason: transport.ByeReasonSubscriptionStopped}
select {
case err := <-done:
if err != nil {
t.Fatalf("RunMany() error = %v", err)
}
case <-time.After(2 * time.Second):
t.Fatal("RunMany did not exit after the last subscription stopped")
}
if !strings.Contains(stderr.String(), "subscribe_id=sub-b remaining=0") {
t.Fatalf("last stop marker missing:\n%s", stderr.String())
}
lines := strings.Split(strings.TrimSpace(stdout.String()), "\n")
if len(lines) != 2 {
t.Fatalf("stdout lines = %d:\n%s", len(lines), stdout.String())
}
for i, line := range lines {
var event transport.Event
if err := json.Unmarshal([]byte(line), &event); err != nil {
t.Fatalf("line %d: %v", i, err)
}
}
}
func TestRunManyMaxEventsIsSharedAcrossConsumers(t *testing.T) {
busA := newManyFakeBus(202, nil)
busB := newManyFakeBus(202, nil)
installManyDiscover(t, busA, busB)
var stdout, stderr synchronizedBuffer
cfg := manyTestConfig(&stdout, &stderr)
cfg.MaxEvents = 1
done := make(chan error, 1)
go func() { done <- RunMany(context.Background(), cfg, manyTestSpecs()) }()
<-busA.hello
<-busB.hello
<-busA.acked
<-busB.acked
busB.send <- transport.Event{Type: transport.FrameTypeEvent, EventID: "only", EventType: "event-b", SubscribeID: "sub-b", Data: `{}`}
select {
case err := <-done:
if err != nil {
t.Fatalf("RunMany() error = %v", err)
}
case <-time.After(2 * time.Second):
t.Fatal("RunMany did not stop at the shared max-events limit")
}
if got := strings.Count(strings.TrimSpace(stdout.String()), "\n") + 1; got != 1 {
t.Fatalf("output event count = %d", got)
}
}
func TestRunManyRejectsInvalidSpecsAndDifferentBuses(t *testing.T) {
cfg := manyTestConfig(io.Discard, io.Discard)
for _, test := range []struct {
name string
specs []ConsumerSpec
}{
{name: "one", specs: []ConsumerSpec{{EventKey: "a", SubscribeID: "sub-a"}}},
{name: "empty event", specs: []ConsumerSpec{{SubscribeID: "sub-a"}, {EventKey: "b", SubscribeID: "sub-b"}}},
{name: "empty subscription", specs: []ConsumerSpec{{EventKey: "a"}, {EventKey: "b", SubscribeID: "sub-b"}}},
{name: "duplicate subscription", specs: []ConsumerSpec{{EventKey: "a", SubscribeID: "sub"}, {EventKey: "b", SubscribeID: "sub"}}},
} {
t.Run(test.name, func(t *testing.T) {
if err := RunMany(context.Background(), cfg, test.specs); err == nil {
t.Fatal("RunMany succeeded")
}
})
}
busA := newManyFakeBus(1, nil)
busB := newManyFakeBus(2, nil)
installManyDiscover(t, busA, busB)
if err := RunMany(context.Background(), cfg, manyTestSpecs()); err == nil || !strings.Contains(err.Error(), "different bus processes") {
t.Fatalf("different bus error = %v", err)
}
}
func TestRunManyContextCancellationInterruptsHandshake(t *testing.T) {
neverAck := make(chan struct{})
busA := newManyFakeBus(1, neverAck)
busB := newManyFakeBus(1, nil)
installManyDiscover(t, busA, busB)
ctx, cancel := context.WithCancel(context.Background())
done := make(chan error, 1)
go func() { done <- RunMany(ctx, manyTestConfig(io.Discard, io.Discard), manyTestSpecs()) }()
<-busA.hello
cancel()
select {
case <-done:
case <-time.After(time.Second):
t.Fatal("RunMany handshake did not unblock on context cancellation")
}
close(neverAck)
}
func TestCrossPlatformCoverageRunManySetupAndHandshakeEdges(t *testing.T) {
specs := manyTestSpecs()
if err := RunMany(context.Background(), Config{}, specs); err == nil {
t.Fatal("missing required config should fail")
}
cfg := Config{
WorkDir: "workdir", IPCEndpoint: "endpoint", ClientID: "client",
Quiet: true, DryRun: true,
}
if err := RunMany(context.Background(), cfg, specs); err != nil {
t.Fatalf("defaulted quiet dry run: %v", err)
}
cfg = manyTestConfig(io.Discard, io.Discard)
cfg.Format = Format("invalid")
if err := RunMany(context.Background(), cfg, specs); err == nil {
t.Fatal("invalid format should fail before discovery")
}
oldDiscover := discoverBus
t.Cleanup(func() { discoverBus = oldDiscover })
wantErr := errors.New("synthetic discover failure")
discoverBus = func(busctl.DiscoverConfig) (net.Conn, error) { return nil, wantErr }
if err := RunMany(context.Background(), manyTestConfig(io.Discard, io.Discard), specs); !errors.Is(err, wantErr) {
t.Fatalf("discover error = %v", err)
}
discoverBus = func(busctl.DiscoverConfig) (net.Conn, error) {
return &faultConn{writeErr: wantErr}, nil
}
if err := RunMany(context.Background(), manyTestConfig(io.Discard, io.Discard), specs); !errors.Is(err, wantErr) {
t.Fatalf("hello write error = %v", err)
}
discoverBus = func(busctl.DiscoverConfig) (net.Conn, error) {
return &faultConn{readErr: io.EOF}, nil
}
if err := RunMany(context.Background(), manyTestConfig(io.Discard, io.Discard), specs); err == nil || !strings.Contains(err.Error(), "hello_ack") {
t.Fatalf("hello ack read error = %v", err)
}
wrongAck := append(mustJSONFrame(t, transport.HelloAck{Type: transport.FrameTypeHeartbeat}), '\n')
discoverBus = func(busctl.DiscoverConfig) (net.Conn, error) {
return &faultConn{readData: append([]byte(nil), wrongAck...), readErr: io.EOF}, nil
}
if err := RunMany(context.Background(), manyTestConfig(io.Discard, io.Discard), specs); err == nil || !strings.Contains(err.Error(), "unexpected first frame") {
t.Fatalf("unexpected ack error = %v", err)
}
}
func TestRunManyDurationAndStdinEOFStopTheWholeGroup(t *testing.T) {
t.Run("duration", func(t *testing.T) {
busA := newManyFakeBus(301, nil)
busB := newManyFakeBus(301, nil)
installManyDiscover(t, busA, busB)
cfg := manyTestConfig(io.Discard, io.Discard)
cfg.Duration = 15 * time.Millisecond
cfg.Quiet = true
if err := RunMany(context.Background(), cfg, manyTestSpecs()); err != nil {
t.Fatalf("duration run: %v", err)
}
})
t.Run("stdin EOF", func(t *testing.T) {
busA := newManyFakeBus(302, nil)
busB := newManyFakeBus(302, nil)
installManyDiscover(t, busA, busB)
var stderr synchronizedBuffer
cfg := manyTestConfig(io.Discard, &stderr)
cfg.Stdin = strings.NewReader("")
if err := RunMany(context.Background(), cfg, manyTestSpecs()); err != nil {
t.Fatalf("stdin EOF run: %v", err)
}
if !strings.Contains(stderr.String(), "reason: signal") {
t.Fatalf("stdin exit marker missing:\n%s", stderr.String())
}
})
}
func TestRunManySourceStateAndBusShutdown(t *testing.T) {
busA := newManyFakeBus(401, nil)
busB := newManyFakeBus(401, nil)
installManyDiscover(t, busA, busB)
var stderr synchronizedBuffer
done := make(chan error, 1)
go func() {
done <- RunMany(context.Background(), manyTestConfig(io.Discard, &stderr), manyTestSpecs())
}()
<-busA.hello
<-busB.hello
<-busA.acked
<-busB.acked
busA.send <- transport.SourceState{Type: transport.FrameTypeSourceState, State: "reconnecting", StateSource: "hook", Attempt: 2}
waitForBuffer(t, &stderr, "source state: reconnecting")
busB.send <- transport.Heartbeat{Type: transport.FrameTypeHeartbeat}
busB.send <- transport.Bye{Type: transport.FrameTypeBye, Reason: "shutdown"}
select {
case err := <-done:
if err != nil {
t.Fatalf("bus shutdown run: %v", err)
}
case <-time.After(2 * time.Second):
t.Fatal("RunMany did not exit on bus shutdown")
}
if !strings.Contains(stderr.String(), "bus closing: shutdown") {
t.Fatalf("bus shutdown marker missing:\n%s", stderr.String())
}
if got := firstActiveSession(map[int]struct{}{3: {}, 1: {}, 2: {}}); got != 1 {
t.Fatalf("firstActiveSession() = %d", got)
}
}
func TestRunManyOutputAndReadFailures(t *testing.T) {
t.Run("broken output pipe is graceful", func(t *testing.T) {
busA := newManyFakeBus(501, nil)
busB := newManyFakeBus(501, nil)
installManyDiscover(t, busA, busB)
cfg := manyTestConfig(errorWriter{err: testBrokenPipeError()}, io.Discard)
done := make(chan error, 1)
go func() { done <- RunMany(context.Background(), cfg, manyTestSpecs()) }()
<-busA.hello
<-busB.hello
<-busA.acked
<-busB.acked
busA.send <- transport.Event{Type: transport.FrameTypeEvent, EventID: "pipe", EventType: "event-a", SubscribeID: "sub-a", Data: `{}`}
select {
case err := <-done:
if err != nil {
t.Fatalf("broken pipe run: %v", err)
}
case <-time.After(2 * time.Second):
t.Fatal("RunMany did not exit on broken output pipe")
}
})
t.Run("ordinary output error fails group", func(t *testing.T) {
busA := newManyFakeBus(502, nil)
busB := newManyFakeBus(502, nil)
installManyDiscover(t, busA, busB)
wantErr := errors.New("output failed")
cfg := manyTestConfig(errorWriter{err: wantErr}, io.Discard)
done := make(chan error, 1)
go func() { done <- RunMany(context.Background(), cfg, manyTestSpecs()) }()
<-busA.hello
<-busB.hello
<-busA.acked
<-busB.acked
busB.send <- transport.Event{Type: transport.FrameTypeEvent, EventID: "error", EventType: "event-b", SubscribeID: "sub-b", Data: `{}`}
select {
case err := <-done:
if !errors.Is(err, wantErr) {
t.Fatalf("output error = %v", err)
}
case <-time.After(2 * time.Second):
t.Fatal("RunMany did not exit on output failure")
}
})
t.Run("unexpected peer close fails group", func(t *testing.T) {
ack := mustJSONFrame(t, transport.HelloAck{Type: transport.FrameTypeHelloAck, BusPID: 503})
oldDiscover := discoverBus
discoverBus = pipeDiscover(t, [][]byte{ack}, true)
t.Cleanup(func() { discoverBus = oldDiscover })
if err := RunMany(context.Background(), manyTestConfig(io.Discard, io.Discard), manyTestSpecs()); err == nil || !strings.Contains(err.Error(), "read frame") {
t.Fatalf("peer close error = %v", err)
}
})
}
func TestCrossPlatformCoverageRunManyIgnoresMalformedFrames(t *testing.T) {
busA := newManyFakeBus(601, nil)
busB := newManyFakeBus(601, nil)
installManyDiscover(t, busA, busB)
var stdout synchronizedBuffer
cfg := manyTestConfig(&stdout, io.Discard)
cfg.MaxEvents = 1
done := make(chan error, 1)
go func() { done <- RunMany(context.Background(), cfg, manyTestSpecs()) }()
<-busA.hello
<-busB.hello
<-busA.acked
<-busB.acked
busA.send <- "not-an-object"
busA.send <- map[string]any{"type": "event", "seq": "not-a-number"}
busA.send <- transport.Event{
Type: transport.FrameTypeEvent, EventID: "valid",
EventType: "event-a", SubscribeID: "sub-a", Data: `{}`,
}
select {
case err := <-done:
if err != nil {
t.Fatalf("RunMany() error = %v", err)
}
case <-time.After(2 * time.Second):
t.Fatal("RunMany did not stop after the valid event")
}
if !strings.Contains(stdout.String(), `"event_id":"valid"`) {
t.Fatalf("stdout = %q", stdout.String())
}
}
func TestCrossPlatformCoverageRunManyTreatsCancelledReadAsGraceful(t *testing.T) {
busA := newManyFakeBus(602, nil)
busB := newManyFakeBus(602, nil)
installManyDiscover(t, busA, busB)
oldCancelled := runManyIsCtxCancelled
runManyIsCtxCancelled = func(context.Context) bool { return true }
t.Cleanup(func() { runManyIsCtxCancelled = oldCancelled })
done := make(chan error, 1)
go func() {
done <- RunMany(context.Background(), manyTestConfig(io.Discard, io.Discard), manyTestSpecs())
}()
<-busA.hello
<-busB.hello
<-busA.acked
<-busB.acked
_ = busA.server.Close()
select {
case err := <-done:
if err != nil {
t.Fatalf("cancelled read error = %v", err)
}
case <-time.After(2 * time.Second):
t.Fatal("RunMany did not stop after the cancelled read")
}
}
func TestPrintDryRunManyDisplaysPendingSubscription(t *testing.T) {
var output bytes.Buffer
PrintDryRunMany(&output, manyTestConfig(io.Discard, io.Discard), []ConsumerSpec{
{EventKey: "event-a", EventTypes: []string{"event-a"}},
})
if !strings.Contains(output.String(), "subscribe_id=(pending)") {
t.Fatalf("dry-run output = %q", output.String())
}
}
+22
View File
@@ -150,3 +150,25 @@ func PrintDryRun(w io.Writer, cfg Config) {
fmt.Fprintf(w, " foreground : %v\n", cfg.Foreground)
fmt.Fprintf(w, " force : %v\n", cfg.Force)
}
// PrintDryRunMany renders the shared consume configuration plus every local
// logical consumer without opening the bus.
func PrintDryRunMany(w io.Writer, cfg Config, specs []ConsumerSpec) {
preview := cfg
preview.EventTypes = nil
for _, spec := range specs {
preview.EventTypes = append(preview.EventTypes, spec.EventTypes...)
}
PrintDryRun(w, preview)
for i, spec := range specs {
fmt.Fprintf(w, " consumer[%d] : event_key=%s subscribe_id=%s event_types=%s\n",
i, spec.EventKey, displayDryRunValue(spec.SubscribeID), strings.Join(spec.EventTypes, ","))
}
}
func displayDryRunValue(value string) string {
if value = strings.TrimSpace(value); value != "" {
return value
}
return "(pending)"
}
+6
View File
@@ -125,9 +125,15 @@ func TestClientCreateRuleBasedSubscriptionsUsesDocumentedRuleParam(t *testing.T)
{"reaction_o2o/openDingtalkId", EventReactionO2O, RuleOptions{OpenDingTalkID: "open-user-1"}, map[string]any{"targetUid": "open-user-1", "targetUidType": "openDingtalkId"}},
{"receive_user/staffId", EventFromUser, RuleOptions{UserID: "staff-1"}, map[string]any{"targetUid": "staff-1", "targetUidType": "staffId"}},
{"receive_user/openDingtalkId", EventFromUser, RuleOptions{OpenDingTalkID: "open-user-1"}, map[string]any{"targetUid": "open-user-1", "targetUidType": "openDingtalkId"}},
{"receive_o2o_all", EventAllSingleChat, RuleOptions{}, map[string]any{}},
{"receive_group_all", EventAllGroupChat, RuleOptions{}, map[string]any{}},
{"read_group", EventReadGroup, RuleOptions{GroupID: "cid-1"}, map[string]any{"openConversationId": "cid-1"}},
{"recall_group", EventRecallGroup, RuleOptions{GroupID: "cid-1"}, map[string]any{"openConversationId": "cid-1"}},
{"reaction_group", EventReactionGroup, RuleOptions{GroupID: "cid-1"}, map[string]any{"openConversationId": "cid-1"}},
{"group_updated", EventGroupUpdated, RuleOptions{GroupID: "cid-1"}, map[string]any{"openConversationId": "cid-1"}},
{"group_member_added", EventGroupMemberAdded, RuleOptions{GroupID: "cid-1"}, map[string]any{"openConversationId": "cid-1"}},
{"group_member_exited", EventGroupMemberExited, RuleOptions{GroupID: "cid-1"}, map[string]any{"openConversationId": "cid-1"}},
{"group_disbanded", EventGroupDisbanded, RuleOptions{GroupID: "cid-1"}, map[string]any{"openConversationId": "cid-1"}},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
+249 -26
View File
@@ -27,17 +27,31 @@ import (
// message receive events. Schema output is generated from these tags so the
// documented fields cannot drift from the values written by consume.
type MessageEventOutput struct {
Type string `json:"type" description:"事件类型,固定为当前 event_key"`
EventID string `json:"event_id" description:"事件 ID,可用于去重"`
Timestamp int64 `json:"timestamp" description:"事件发生时间戳" format:"timestamp_ms"`
SubscribeID string `json:"subscribe_id" description:"订阅 ID"`
MessageID string `json:"message_id" description:"开放消息 ID" format:"open_message_id"`
ConversationID string `json:"conversation_id" description:"会话 ID" format:"open_conversation_id"`
Sender string `json:"sender" description:"发送人展示名"`
SenderOpenDingTalkID string `json:"sender_open_dingtalk_id" description:"发送人开放 ID" format:"open_dingtalk_id"`
Content string `json:"content" description:"消息正文"`
CreateTime string `json:"create_time" description:"消息创建时间"`
EventTime int64 `json:"event_time" description:"消息事件时间戳" format:"timestamp_ms"`
Type string `json:"type" description:"事件类型,固定为当前 event_key"`
EventID string `json:"event_id" description:"事件 ID,可用于去重"`
Timestamp int64 `json:"timestamp" description:"事件发生时间戳" format:"timestamp_ms"`
SubscribeID string `json:"subscribe_id" description:"订阅 ID"`
MessageID string `json:"message_id" description:"开放消息 ID" format:"open_message_id"`
ConversationID string `json:"conversation_id" description:"会话 ID" format:"open_conversation_id"`
Sender string `json:"sender" description:"发送人展示名"`
SenderOpenDingTalkID string `json:"sender_open_dingtalk_id" description:"发送人开放 ID" format:"open_dingtalk_id"`
Content string `json:"content" description:"消息正文"`
CreateTime string `json:"create_time" description:"消息创建时间"`
EventTime int64 `json:"event_time" description:"消息事件时间戳" format:"timestamp_ms"`
QuotedMessage *MessageEventContext `json:"quoted_message,omitempty" description:"引用回复所引用的原消息;非引用回复时不输出"`
ForwardMessages []MessageEventContext `json:"forward_messages,omitempty" description:"合并转发包含的原消息列表;非合并转发时不输出"`
}
// MessageEventContext preserves the business context nested under a quoted
// reply or merged-forward message. Keep these fields structured instead of
// parsing the localized outer content summary.
type MessageEventContext struct {
MessageID string `json:"message_id" description:"内部消息的开放消息 ID" format:"open_message_id"`
ConversationID string `json:"conversation_id" description:"内部消息原来所在的会话 ID" format:"open_conversation_id"`
Sender string `json:"sender" description:"内部消息发送人展示名;服务端未提供时可能为空或为 null 字符串"`
SenderOpenDingTalkID string `json:"sender_open_dingtalk_id" description:"内部消息发送人开放 ID;服务端未提供时为空" format:"open_dingtalk_id"`
Content string `json:"content" description:"内部消息正文;媒体消息可能包含 mediaId 等下载定位信息"`
CreateTime string `json:"create_time" description:"内部消息创建时间"`
}
type ReadEventOutput struct {
@@ -95,6 +109,35 @@ type baseEventOutput struct {
SubscribeID string `json:"subscribe_id" description:"订阅 ID"`
}
// GroupLifecycleEventOutput is intentionally conservative until stable group
// event payload samples are available. Payload keeps unknown business fields
// while transport identity and routing metadata remain available only in raw
// output.
type GroupLifecycleEventOutput struct {
Type string `json:"type" description:"事件类型,固定为当前 event_key"`
EventID string `json:"event_id" description:"事件 ID,可用于去重"`
Timestamp int64 `json:"timestamp" description:"事件发生时间戳" format:"timestamp_ms"`
SubscribeID string `json:"subscribe_id" description:"订阅 ID"`
Payload map[string]any `json:"payload" description:"群生命周期事件业务数据,字段以服务端实际推送为准" additional_properties:"true"`
}
type GroupMemberEventOutput struct {
Type string `json:"type" description:"事件类型,固定为当前 event_key"`
EventID string `json:"event_id" description:"事件 ID,可用于去重"`
Timestamp int64 `json:"timestamp" description:"事件发生时间戳" format:"timestamp_ms"`
SubscribeID string `json:"subscribe_id" description:"订阅 ID"`
ConversationID string `json:"conversation_id" description:"发生成员变更的群会话 ID" format:"open_conversation_id"`
Operator string `json:"operator" description:"执行成员变更操作的用户展示名,系统操作或成员自行退出时可能为空"`
OperatorOpenDingTalkID string `json:"operator_open_dingtalk_id" description:"执行成员变更操作的用户开放 ID,系统操作或成员自行退出时可能为空" format:"open_dingtalk_id"`
Members []GroupMemberEventMember `json:"members" description:"本次加入或退出的成员列表"`
EventTime int64 `json:"event_time" description:"群成员变更事件时间戳" format:"timestamp_ms"`
}
type GroupMemberEventMember struct {
Nick string `json:"nick" description:"成员展示名"`
OpenDingTalkID string `json:"open_dingtalk_id" description:"成员开放 ID" format:"open_dingtalk_id"`
}
type personalEventData struct {
EventID string `json:"eventId"`
EventKey string `json:"eventKey"`
@@ -106,15 +149,26 @@ type personalEventData struct {
type personalMessagePayload struct {
EventTime int64 `json:"event_time"`
Body struct {
CreateTime string `json:"createTime"`
Sender string `json:"sender"`
OpenMessageID string `json:"openMessageId"`
SenderOpenDingTalkID string `json:"senderOpenDingTalkId"`
OpenConversationID string `json:"openConversationId"`
Content string `json:"content"`
CreateTime string `json:"createTime"`
Sender string `json:"sender"`
OpenMessageID string `json:"openMessageId"`
SenderOpenDingTalkID string `json:"senderOpenDingTalkId"`
OpenConversationID string `json:"openConversationId"`
Content string `json:"content"`
QuotedMessage *personalMessageContext `json:"quotedMessage"`
ForwardMessages []personalMessageContext `json:"forwardMessages"`
} `json:"body"`
}
type personalMessageContext struct {
CreateTime string `json:"createTime"`
Sender string `json:"sender"`
OpenMessageID string `json:"openMessageId"`
SenderOpenDingTalkID string `json:"senderOpenDingTalkId"`
OpenConversationID string `json:"openConversationId"`
Content string `json:"content"`
}
type personalReadPayload struct {
EventTime int64 `json:"event_time"`
Body struct {
@@ -159,6 +213,23 @@ type personalReactionBody struct {
SenderOpenDingTalkID string `json:"senderOpenDingTalkId"`
}
type personalGroupMemberPayload struct {
EventTime int64 `json:"event_time"`
Body personalGroupMemberBody `json:"body"`
}
type personalGroupMemberBody struct {
ConversationID string `json:"openConversationId"`
Operator string `json:"operNick"`
OperatorOpenDingTalkID string `json:"-"`
Members []personalGroupMemberRecord `json:"members"`
}
type personalGroupMemberRecord struct {
Nick string `json:"nick"`
OpenDingTalkID string `json:"openDingTalkId"`
}
func (b *personalReactionBody) UnmarshalJSON(data []byte) error {
// encoding/json otherwise falls back to case-insensitive field matching.
// Read this protocol field from a map so only operOpenDingtalkId is accepted.
@@ -182,6 +253,29 @@ func (b *personalReactionBody) UnmarshalJSON(data []byte) error {
return nil
}
func (b *personalGroupMemberBody) UnmarshalJSON(data []byte) error {
// Keep the protocol spelling strict: encoding/json would otherwise accept
// operOpenDingTalkId through case-insensitive fallback matching.
var fields map[string]json.RawMessage
if err := json.Unmarshal(data, &fields); err != nil {
return err
}
type bodyAlias personalGroupMemberBody
var decoded bodyAlias
if err := json.Unmarshal(data, &decoded); err != nil {
return err
}
if raw, ok := fields["operOpenDingtalkId"]; ok {
if err := json.Unmarshal(raw, &decoded.OperatorOpenDingTalkID); err != nil {
return fmt.Errorf("decode operOpenDingtalkId: %w", err)
}
}
*b = personalGroupMemberBody(decoded)
return nil
}
// ProjectOutput converts the transport envelope into the stable personal
// event output. On malformed Data it returns the original envelope together
// with an error; the formatter logs the warning and still emits that envelope.
@@ -204,6 +298,18 @@ func ProjectOutput(ev transport.Event) (any, error) {
if err := decodeRequiredPayload(data.Payload, &payload); err != nil {
return ev, fmt.Errorf("decode personal message payload: %w", err)
}
var quotedMessage *MessageEventContext
if payload.Body.QuotedMessage != nil {
projected := projectMessageEventContext(*payload.Body.QuotedMessage)
quotedMessage = &projected
}
var forwardMessages []MessageEventContext
if len(payload.Body.ForwardMessages) > 0 {
forwardMessages = make([]MessageEventContext, 0, len(payload.Body.ForwardMessages))
for _, message := range payload.Body.ForwardMessages {
forwardMessages = append(forwardMessages, projectMessageEventContext(message))
}
}
return MessageEventOutput{
Type: eventType,
EventID: eventID,
@@ -216,6 +322,8 @@ func ProjectOutput(ev transport.Event) (any, error) {
Content: payload.Body.Content,
CreateTime: payload.Body.CreateTime,
EventTime: payload.EventTime,
QuotedMessage: quotedMessage,
ForwardMessages: forwardMessages,
}, nil
}
@@ -232,11 +340,57 @@ func ProjectOutput(ev transport.Event) (any, error) {
return projectRecallEvent(ev, base, data.Payload)
case isReactionEvent(eventType):
return projectReactionEvent(ev, base, data.Payload)
case isGroupMemberEvent(eventType):
return projectGroupMemberEvent(ev, base, data.Payload)
case isGroupLifecycleEvent(eventType):
payload, err := decodeGroupLifecyclePayload(data.Payload)
if err != nil {
return ev, fmt.Errorf("decode personal group lifecycle payload: %w", err)
}
return GroupLifecycleEventOutput{
Type: base.Type,
EventID: base.EventID,
Timestamp: base.Timestamp,
SubscribeID: base.SubscribeID,
Payload: payload,
}, nil
default:
return ev, fmt.Errorf("unsupported personal event type %q", eventType)
}
}
func projectMessageEventContext(message personalMessageContext) MessageEventContext {
return MessageEventContext{
MessageID: message.OpenMessageID,
ConversationID: message.OpenConversationID,
Sender: message.Sender,
SenderOpenDingTalkID: message.SenderOpenDingTalkID,
Content: message.Content,
CreateTime: message.CreateTime,
}
}
func decodeGroupLifecyclePayload(raw json.RawMessage) (map[string]any, error) {
trimmed := bytes.TrimSpace(raw)
if len(trimmed) == 0 || bytes.Equal(trimmed, []byte("null")) {
return nil, fmt.Errorf("payload is missing")
}
var payload map[string]any
if err := json.Unmarshal(trimmed, &payload); err != nil {
return nil, err
}
if len(payload) == 0 {
return nil, fmt.Errorf("payload is empty")
}
for key := range payload {
switch strings.ToLower(key) {
case "uid", "corpid", "clientid", "filtersubid", "bizid", "orgid", "sourceid":
delete(payload, key)
}
}
return payload, nil
}
func projectReadEvent(ev transport.Event, base baseEventOutput, raw json.RawMessage) (any, error) {
var payload personalReadPayload
if err := decodeRequiredPayload(raw, &payload); err != nil {
@@ -303,6 +457,38 @@ func projectReactionEvent(ev transport.Event, base baseEventOutput, raw json.Raw
}, nil
}
func projectGroupMemberEvent(ev transport.Event, base baseEventOutput, raw json.RawMessage) (any, error) {
var payload personalGroupMemberPayload
if err := decodeRequiredPayload(raw, &payload); err != nil {
return ev, fmt.Errorf("decode personal group member payload: %w", err)
}
if strings.TrimSpace(payload.Body.ConversationID) == "" {
return ev, fmt.Errorf("decode personal group member payload: openConversationId is required")
}
if len(payload.Body.Members) == 0 {
return ev, fmt.Errorf("decode personal group member payload: members is required")
}
members := make([]GroupMemberEventMember, 0, len(payload.Body.Members))
for _, member := range payload.Body.Members {
members = append(members, GroupMemberEventMember{
Nick: member.Nick,
OpenDingTalkID: member.OpenDingTalkID,
})
}
return GroupMemberEventOutput{
Type: base.Type,
EventID: base.EventID,
Timestamp: base.Timestamp,
SubscribeID: base.SubscribeID,
ConversationID: payload.Body.ConversationID,
Operator: payload.Body.Operator,
OperatorOpenDingTalkID: payload.Body.OperatorOpenDingTalkID,
Members: members,
EventTime: payload.EventTime,
}, nil
}
func decodeRequiredPayload(raw json.RawMessage, target any) error {
trimmed := bytes.TrimSpace(raw)
if len(trimmed) == 0 || bytes.Equal(trimmed, []byte("null")) {
@@ -364,16 +550,26 @@ func decodePersonalEventData(raw string) (personalEventData, error) {
func outputSchema(eventKey string) map[string]any {
outputType := outputTypeForEvent(eventKey)
properties := make(map[string]any, outputType.NumField())
for i := 0; i < outputType.NumField(); i++ {
field := outputType.Field(i)
schema := schemaForStruct(outputType)
properties := schema["properties"].(map[string]any)
if property, ok := properties["type"].(map[string]any); ok {
property["enum"] = []string{eventKey}
}
return schema
}
func schemaForStruct(t reflect.Type) map[string]any {
for t.Kind() == reflect.Pointer {
t = t.Elem()
}
properties := make(map[string]any, t.NumField())
for i := 0; i < t.NumField(); i++ {
field := t.Field(i)
name := strings.Split(field.Tag.Get("json"), ",")[0]
if name == "" || name == "-" {
continue
}
property := map[string]any{
"type": schemaType(field.Type),
}
property := schemaForType(field.Type)
if description := field.Tag.Get("description"); description != "" {
property["description"] = description
}
@@ -383,9 +579,6 @@ func outputSchema(eventKey string) map[string]any {
if field.Tag.Get("additional_properties") == "true" {
property["additionalProperties"] = true
}
if name == "type" {
property["enum"] = []string{eventKey}
}
properties[name] = property
}
return map[string]any{
@@ -394,6 +587,23 @@ func outputSchema(eventKey string) map[string]any {
}
}
func schemaForType(t reflect.Type) map[string]any {
for t.Kind() == reflect.Pointer {
t = t.Elem()
}
switch t.Kind() {
case reflect.Struct:
return schemaForStruct(t)
case reflect.Slice, reflect.Array:
return map[string]any{
"type": "array",
"items": schemaForType(t.Elem()),
}
default:
return map[string]any{"type": schemaType(t)}
}
}
func transportEnvelopeSchema(eventKey string) map[string]any {
eventType := reflect.TypeOf(transport.Event{})
properties := make(map[string]any, eventType.NumField())
@@ -437,6 +647,10 @@ func outputTypeForEvent(eventKey string) reflect.Type {
return reflect.TypeOf(RecallEventOutput{})
case isReactionEvent(eventKey):
return reflect.TypeOf(ReactionEventOutput{})
case isGroupMemberEvent(eventKey):
return reflect.TypeOf(GroupMemberEventOutput{})
case isGroupLifecycleEvent(eventKey):
return reflect.TypeOf(GroupLifecycleEventOutput{})
default:
return reflect.TypeOf(baseEventOutput{})
}
@@ -454,6 +668,15 @@ func isReactionEvent(eventKey string) bool {
return eventKey == EventReactionO2O || eventKey == EventReactionGroup
}
func isGroupMemberEvent(eventKey string) bool {
return eventKey == EventGroupMemberAdded || eventKey == EventGroupMemberExited
}
func isGroupLifecycleEvent(eventKey string) bool {
return eventKey == EventGroupUpdated ||
eventKey == EventGroupDisbanded
}
func schemaType(t reflect.Type) string {
switch t.Kind() {
case reflect.String:
+423 -1
View File
@@ -124,8 +124,34 @@ func personalReactionData(eventKey, messageID, conversationID string) string {
}`, eventKey, conversationID, messageID)
}
func personalGroupMemberData(eventKey string) string {
return fmt.Sprintf(`{
"eventId":"group-member-event",
"eventKey":%q,
"occurredAtMs":1784782513647,
"subId":"group-member-sub",
"payload":{
"uid":100001,
"clientId":"internal-client",
"corpid":"internal-corp",
"bizid":"internal-biz",
"filterSubId":"internal-filter",
"body":{
"operNick":"测试用户甲",
"members":[
{"nick":"测试用户乙","openDingTalkId":"member-open-id-1"},
{"nick":"测试用户丙","openDingTalkId":"member-open-id-2"}
],
"operOpenDingtalkId":"operator-open-id",
"openConversationId":"cid-group-1"
},
"event_time":1784782513502
}
}`, eventKey)
}
func TestCrossPlatformCoverageProjectOutputMessageEvents(t *testing.T) {
for _, eventKey := range []string{EventMention, EventSingleChat, EventInChat, EventFromUser} {
for _, eventKey := range []string{EventMention, EventSingleChat, EventInChat, EventFromUser, EventAllSingleChat, EventAllGroupChat} {
t.Run(eventKey, func(t *testing.T) {
projected, err := ProjectOutput(transport.Event{
Type: transport.FrameTypeEvent,
@@ -158,6 +184,367 @@ func TestCrossPlatformCoverageProjectOutputMessageEvents(t *testing.T) {
if !reflect.DeepEqual(got, want) {
t.Fatalf("ProjectOutput() = %#v, want %#v", got, want)
}
encoded, err := json.Marshal(got)
if err != nil {
t.Fatal(err)
}
for _, absent := range []string{"quoted_message", "forward_messages"} {
if strings.Contains(string(encoded), `"`+absent+`"`) {
t.Fatalf("ordinary message output contains optional field %q: %s", absent, encoded)
}
}
})
}
}
func TestProjectOutputPreservesQuotedMessageContext(t *testing.T) {
data := `{
"eventId":"quoted-event",
"eventKey":"user_im_message_receive_group",
"occurredAtMs":1784792292580,
"subId":"quoted-sub",
"payload":{
"body":{
"createTime":"2026-07-23 15:38:11",
"sender":"郑御白",
"openMessageId":"outer-message",
"senderOpenDingTalkId":"outer-sender-open-id",
"openConversationId":"target-conversation",
"content":"引用回复",
"quotedMessage":{
"createTime":"2026-07-23 15:35:03",
"sender":"null",
"openMessageId":"quoted-message",
"senderOpenDingTalkId":"quoted-sender-open-id",
"openConversationId":"source-conversation",
"content":"被引用的原消息"
}
},
"event_time":1784792291637
}
}`
projected, err := ProjectOutput(transport.Event{
EventType: EventInChat,
SubscribeID: "outer-sub",
Data: data,
})
if err != nil {
t.Fatalf("ProjectOutput() error = %v", err)
}
got := projected.(MessageEventOutput)
want := &MessageEventContext{
MessageID: "quoted-message",
ConversationID: "source-conversation",
Sender: "null",
SenderOpenDingTalkID: "quoted-sender-open-id",
Content: "被引用的原消息",
CreateTime: "2026-07-23 15:35:03",
}
if !reflect.DeepEqual(got.QuotedMessage, want) {
t.Fatalf("quoted_message = %#v, want %#v", got.QuotedMessage, want)
}
if got.ForwardMessages != nil {
t.Fatalf("forward_messages = %#v, want nil", got.ForwardMessages)
}
}
func TestProjectOutputPreservesMergedForwardContextAndMediaLocator(t *testing.T) {
data := `{
"eventId":"forward-event",
"eventKey":"user_im_message_receive_group",
"occurredAtMs":1784861030151,
"subId":"forward-sub",
"payload":{
"body":{
"createTime":"2026-07-24 10:43:49",
"sender":"郑御白",
"openMessageId":"outer-forward-message",
"senderOpenDingTalkId":"outer-sender-open-id",
"openConversationId":"target-conversation",
"content":"Chat history between two users\nUser A:[Image]\nUser A:Forwarded chat record",
"forwardMessages":[
{
"createTime":"2026-07-24 10:33:31",
"sender":"null",
"openMessageId":"image-message",
"senderOpenDingTalkId":"image-sender-open-id",
"openConversationId":"source-conversation",
"content":"[图片消息](mediaId=media-1) 注意:如需下载使用dws chat message download-media命令下载"
},
{
"createTime":"2026-07-24 10:34:46",
"sender":"null",
"openMessageId":"text-message",
"openConversationId":"source-conversation",
"content":"转发聊天记录"
}
]
},
"event_time":1784861029265
}
}`
projected, err := ProjectOutput(transport.Event{
EventType: EventInChat,
SubscribeID: "outer-sub",
Data: data,
})
if err != nil {
t.Fatalf("ProjectOutput() error = %v", err)
}
got := projected.(MessageEventOutput)
want := []MessageEventContext{
{
MessageID: "image-message",
ConversationID: "source-conversation",
Sender: "null",
SenderOpenDingTalkID: "image-sender-open-id",
Content: "[图片消息](mediaId=media-1) 注意:如需下载使用dws chat message download-media命令下载",
CreateTime: "2026-07-24 10:33:31",
},
{
MessageID: "text-message",
ConversationID: "source-conversation",
Sender: "null",
Content: "转发聊天记录",
CreateTime: "2026-07-24 10:34:46",
},
}
if !reflect.DeepEqual(got.ForwardMessages, want) {
t.Fatalf("forward_messages = %#v, want %#v", got.ForwardMessages, want)
}
if got.QuotedMessage != nil {
t.Fatalf("quoted_message = %#v, want nil", got.QuotedMessage)
}
encoded, err := json.Marshal(got)
if err != nil {
t.Fatal(err)
}
for _, wantFragment := range []string{
`"forward_messages"`,
`"message_id":"image-message"`,
`"conversation_id":"source-conversation"`,
`mediaId=media-1`,
} {
if !strings.Contains(string(encoded), wantFragment) {
t.Fatalf("flattened merged-forward output missing %q: %s", wantFragment, encoded)
}
}
for _, localizedDetection := range []string{"群聊的聊天记录", "的聊天记录"} {
if strings.Contains(got.Content, localizedDetection) {
t.Fatalf("test fixture should not require localized title %q for projection", localizedDetection)
}
}
}
func TestCrossPlatformCoverageProjectOutputGroupLifecycleEvents(t *testing.T) {
for _, eventKey := range []string{EventGroupUpdated, EventGroupDisbanded} {
t.Run(eventKey, func(t *testing.T) {
data := fmt.Sprintf(`{
"eventId":"group-event",
"eventKey":%q,
"occurredAtMs":1784009000000,
"subId":"data-sub",
"payload":{
"uid":100001,
"CORPID":"internal-corp",
"clientId":"internal-client",
"filterSubId":"internal-filter",
"bizid":"internal-biz",
"orgId":100002,
"sourceId":"open",
"event_time":1784008999000,
"body":{
"openConversationId":"cid-group-1",
"title":"测试群新标题",
"operator":{"uid":"business-user-1"}
}
}
}`, eventKey)
projected, err := ProjectOutput(transport.Event{
EventID: "outer-event",
EventType: eventKey,
SubscribeID: "outer-sub",
Data: data,
})
if err != nil {
t.Fatalf("ProjectOutput() error = %v", err)
}
got, ok := projected.(GroupLifecycleEventOutput)
if !ok {
t.Fatalf("ProjectOutput() type = %T", projected)
}
if got.Type != eventKey || got.EventID != "group-event" || got.Timestamp != 1784009000000 || got.SubscribeID != "outer-sub" {
t.Fatalf("common fields = %#v", got)
}
for _, internal := range []string{"uid", "CORPID", "clientId", "filterSubId", "bizid", "orgId", "sourceId"} {
if _, ok := got.Payload[internal]; ok {
t.Fatalf("payload retained internal field %q: %#v", internal, got.Payload)
}
}
body, ok := got.Payload["body"].(map[string]any)
if !ok || body["title"] != "测试群新标题" {
t.Fatalf("payload body = %#v", got.Payload["body"])
}
operator := body["operator"].(map[string]any)
if operator["uid"] != "business-user-1" {
t.Fatalf("nested business uid was removed: %#v", operator)
}
})
}
}
func TestCrossPlatformCoverageProjectOutputGroupMemberEvents(t *testing.T) {
for _, eventKey := range []string{EventGroupMemberAdded, EventGroupMemberExited} {
t.Run(eventKey, func(t *testing.T) {
projected, err := ProjectOutput(transport.Event{
EventID: "outer-event",
EventBornTime: 11,
EventType: eventKey,
SubscribeID: "outer-sub",
Data: personalGroupMemberData(eventKey),
})
if err != nil {
t.Fatalf("ProjectOutput() error = %v", err)
}
want := GroupMemberEventOutput{
Type: eventKey,
EventID: "group-member-event",
Timestamp: 1784782513647,
SubscribeID: "outer-sub",
ConversationID: "cid-group-1",
Operator: "测试用户甲",
OperatorOpenDingTalkID: "operator-open-id",
Members: []GroupMemberEventMember{
{Nick: "测试用户乙", OpenDingTalkID: "member-open-id-1"},
{Nick: "测试用户丙", OpenDingTalkID: "member-open-id-2"},
},
EventTime: 1784782513502,
}
if !reflect.DeepEqual(projected, want) {
t.Fatalf("ProjectOutput() = %#v, want %#v", projected, want)
}
assertNoInternalActionFields(t, projected)
})
}
}
func TestCrossPlatformCoverageProjectOutputGroupMemberAllowsMissingOperator(t *testing.T) {
data := strings.ReplaceAll(personalGroupMemberData(EventGroupMemberExited), `"operNick":"测试用户甲",`, "")
data = strings.ReplaceAll(data, `"operOpenDingtalkId":"operator-open-id",`, "")
projected, err := ProjectOutput(transport.Event{EventType: EventGroupMemberExited, Data: data})
if err != nil {
t.Fatalf("ProjectOutput() error = %v", err)
}
got := projected.(GroupMemberEventOutput)
if got.Operator != "" || got.OperatorOpenDingTalkID != "" {
t.Fatalf("operator fields = %q/%q, want empty", got.Operator, got.OperatorOpenDingTalkID)
}
if len(got.Members) != 2 {
t.Fatalf("members = %#v, want two members", got.Members)
}
}
func TestCrossPlatformCoverageProjectOutputGroupMemberRejectsLegacyOperatorOpenIDSpellings(t *testing.T) {
for _, legacyField := range []string{"operOpenDingtlkId", "operOpenDingTalkId"} {
t.Run(legacyField, func(t *testing.T) {
data := strings.Replace(personalGroupMemberData(EventGroupMemberAdded), "operOpenDingtalkId", legacyField, 1)
projected, err := ProjectOutput(transport.Event{EventType: EventGroupMemberAdded, Data: data})
if err != nil {
t.Fatalf("ProjectOutput() error = %v", err)
}
got := projected.(GroupMemberEventOutput)
if got.OperatorOpenDingTalkID != "" {
t.Fatalf("operator_open_dingtalk_id = %q, want empty for legacy field %s", got.OperatorOpenDingTalkID, legacyField)
}
if got.Members[0].OpenDingTalkID != "member-open-id-1" {
t.Fatalf("members[0].open_dingtalk_id = %q, want protocol openDingTalkId value", got.Members[0].OpenDingTalkID)
}
})
}
}
func TestCrossPlatformCoverageGroupMemberBodyUnmarshalErrors(t *testing.T) {
var malformed personalGroupMemberBody
if err := malformed.UnmarshalJSON([]byte(`{`)); err == nil {
t.Fatal("UnmarshalJSON() error = nil, want malformed object error")
}
tests := []struct {
name string
data string
}{
{name: "invalid members", data: `{"members":"invalid"}`},
{name: "invalid operator open id", data: `{"operOpenDingtalkId":{"unexpected":true}}`},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
var body personalGroupMemberBody
if err := json.Unmarshal([]byte(tt.data), &body); err == nil {
t.Fatal("json.Unmarshal() error = nil, want protocol error")
}
})
}
}
func TestCrossPlatformCoverageProjectOutputRejectsInvalidGroupMemberPayloads(t *testing.T) {
tests := []struct {
name string
data string
}{
{
name: "missing conversation",
data: `{"eventKey":"user_im_group_member_added","payload":{"body":{"members":[{"nick":"测试用户甲","openDingTalkId":"member-1"}]},"event_time":1}}`,
},
{
name: "empty conversation",
data: `{"eventKey":"user_im_group_member_added","payload":{"body":{"openConversationId":" ","members":[{"nick":"测试用户甲","openDingTalkId":"member-1"}]},"event_time":1}}`,
},
{
name: "missing members",
data: `{"eventKey":"user_im_group_member_added","payload":{"body":{"openConversationId":"cid-1"},"event_time":1}}`,
},
{
name: "empty members",
data: `{"eventKey":"user_im_group_member_added","payload":{"body":{"openConversationId":"cid-1","members":[]},"event_time":1}}`,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
ev := transport.Event{EventID: "outer-event", EventType: EventGroupMemberAdded, Data: tt.data}
projected, err := ProjectOutput(ev)
if err == nil {
t.Fatal("ProjectOutput() error = nil, want group member validation error")
}
if got, ok := projected.(transport.Event); !ok || !reflect.DeepEqual(got, ev) {
t.Fatalf("ProjectOutput() fallback = %#v, want %#v", projected, ev)
}
})
}
}
func TestCrossPlatformCoverageProjectOutputRejectsInvalidGroupLifecyclePayloads(t *testing.T) {
tests := []struct {
name string
data string
}{
{name: "missing", data: `{"eventKey":"user_im_group_updated"}`},
{name: "null", data: `{"eventKey":"user_im_group_updated","payload":null}`},
{name: "empty object", data: `{"eventKey":"user_im_group_updated","payload":{}}`},
{name: "array", data: `{"eventKey":"user_im_group_updated","payload":[]}`},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
ev := transport.Event{EventID: "outer-event", EventType: EventGroupUpdated, Data: tt.data}
projected, err := ProjectOutput(ev)
if err == nil {
t.Fatal("ProjectOutput() error = nil, want payload validation error")
}
if got, ok := projected.(transport.Event); !ok || !reflect.DeepEqual(got, ev) {
t.Fatalf("ProjectOutput() fallback = %#v, want %#v", projected, ev)
}
})
}
}
@@ -333,12 +720,16 @@ func TestCrossPlatformCoverageProjectOutputRejectsEmptyPayloads(t *testing.T) {
EventSingleChat,
EventInChat,
EventFromUser,
EventAllSingleChat,
EventAllGroupChat,
EventReadO2O,
EventReadGroup,
EventRecallO2O,
EventRecallGroup,
EventReactionO2O,
EventReactionGroup,
EventGroupMemberAdded,
EventGroupMemberExited,
}
payloads := []struct {
name string
@@ -384,3 +775,34 @@ func TestCrossPlatformCoverageProjectOutputMalformedDataReturnsRawEnvelope(t *te
t.Fatalf("ProjectOutput() fallback = %#v", projected)
}
}
func TestCrossPlatformCoverageSchemaReflectionSupportsNestedArraysAndPointers(t *testing.T) {
type nested struct {
Value string `json:"value" description:"nested value" format:"nested_id"`
Hidden string `json:"-"`
}
type fixture struct {
Items []*nested `json:"items" description:"nested items"`
Meta map[string]any `json:"meta" additional_properties:"true"`
}
schema := schemaForStruct(reflect.TypeOf((*fixture)(nil)))
properties := schema["properties"].(map[string]any)
if len(properties) != 2 {
t.Fatalf("schema properties = %#v, want items and meta", properties)
}
items := properties["items"].(map[string]any)
itemSchema := items["items"].(map[string]any)
itemProperties := itemSchema["properties"].(map[string]any)
if items["type"] != "array" || itemSchema["type"] != "object" || len(itemProperties) != 1 {
t.Fatalf("nested items schema = %#v", items)
}
value := itemProperties["value"].(map[string]any)
if value["type"] != "string" || value["description"] != "nested value" || value["format"] != "nested_id" {
t.Fatalf("nested value schema = %#v", value)
}
meta := properties["meta"].(map[string]any)
if meta["type"] != "object" || meta["additionalProperties"] != true {
t.Fatalf("meta schema = %#v", meta)
}
}
+91 -12
View File
@@ -23,16 +23,22 @@ import (
)
const (
EventMention = "user_im_message_receive_at"
EventSingleChat = "user_im_message_receive_o2o"
EventInChat = "user_im_message_receive_group"
EventFromUser = "user_im_message_receive_user"
EventReadO2O = "user_im_message_read_o2o"
EventReadGroup = "user_im_message_read_group"
EventRecallO2O = "user_im_message_recall_o2o"
EventRecallGroup = "user_im_message_recall_group"
EventReactionO2O = "user_im_message_reaction_o2o"
EventReactionGroup = "user_im_message_reaction_group"
EventMention = "user_im_message_receive_at"
EventSingleChat = "user_im_message_receive_o2o"
EventInChat = "user_im_message_receive_group"
EventFromUser = "user_im_message_receive_user"
EventAllSingleChat = "user_im_message_receive_o2o_all"
EventAllGroupChat = "user_im_message_receive_group_all"
EventReadO2O = "user_im_message_read_o2o"
EventReadGroup = "user_im_message_read_group"
EventRecallO2O = "user_im_message_recall_o2o"
EventRecallGroup = "user_im_message_recall_group"
EventReactionO2O = "user_im_message_reaction_o2o"
EventReactionGroup = "user_im_message_reaction_group"
EventGroupUpdated = "user_im_group_updated"
EventGroupMemberAdded = "user_im_group_member_added"
EventGroupMemberExited = "user_im_group_member_exited"
EventGroupDisbanded = "user_im_group_disbanded"
)
const (
@@ -132,6 +138,28 @@ var definitions = []Definition{
Auth: map[string]any{"identity": "user"},
Public: true,
},
{
EventKey: EventAllSingleChat,
DisplayName: "全部单聊消息",
Description: "当前用户收到的所有单聊消息",
Category: "im",
RuleType: "all",
Status: StatusEnabled,
RequiredParams: nil,
Auth: map[string]any{"identity": "user"},
Public: true,
},
{
EventKey: EventAllGroupChat,
DisplayName: "全部群消息",
Description: "当前用户收到的所有群聊消息",
Category: "im",
RuleType: "all",
Status: StatusEnabled,
RequiredParams: nil,
Auth: map[string]any{"identity": "user"},
Public: true,
},
{
EventKey: EventReadO2O,
DisplayName: "指定单聊消息已读",
@@ -201,6 +229,50 @@ var definitions = []Definition{
Auth: map[string]any{"identity": "user"},
Public: true,
},
{
EventKey: EventGroupUpdated,
DisplayName: "群标题变更",
Description: "指定群聊的标题发生变更",
Category: "im",
RuleType: "group",
Status: StatusEnabled,
RequiredParams: []string{"group"},
Auth: map[string]any{"identity": "user"},
Public: true,
},
{
EventKey: EventGroupMemberAdded,
DisplayName: "群成员加入",
Description: "指定群聊有成员加入",
Category: "im",
RuleType: "group",
Status: StatusEnabled,
RequiredParams: []string{"group"},
Auth: map[string]any{"identity": "user"},
Public: true,
},
{
EventKey: EventGroupMemberExited,
DisplayName: "群成员退出",
Description: "指定群聊有成员退出",
Category: "im",
RuleType: "group",
Status: StatusEnabled,
RequiredParams: []string{"group"},
Auth: map[string]any{"identity": "user"},
Public: true,
},
{
EventKey: EventGroupDisbanded,
DisplayName: "群解散",
Description: "指定群聊被解散",
Category: "im",
RuleType: "group",
Status: StatusEnabled,
RequiredParams: []string{"group"},
Auth: map[string]any{"identity": "user"},
Public: true,
},
}
func targetUIDConstraints() *ParameterConstraints {
@@ -332,7 +404,7 @@ func BuildRuleParam(eventKey string, opts RuleOptions) (ruleType string, rulePar
openDingTalkID := strings.TrimSpace(opts.OpenDingTalkID)
groupID := strings.TrimSpace(opts.GroupID)
switch def.RuleType {
case "at":
case "at", "all":
if userID != "" {
return "", nil, fmt.Errorf("--user is not supported for %s", eventKey)
}
@@ -490,13 +562,20 @@ func normalizeFilterAliases(v any) any {
func isMessageReceiveEvent(eventKey string) bool {
switch eventKey {
case EventMention, EventSingleChat, EventInChat, EventFromUser:
case EventMention, EventSingleChat, EventInChat, EventFromUser, EventAllSingleChat, EventAllGroupChat:
return true
default:
return false
}
}
// SupportsMessageFilter reports whether --query/--filter-json describe a
// stable message payload for this event. Action and group lifecycle events do
// not expose the same filter fields.
func SupportsMessageFilter(eventKey string) bool {
return isMessageReceiveEvent(eventKey)
}
func IsSchemaPending(err error) bool {
var pending *SchemaPendingError
return errors.As(err, &pending)
+180 -4
View File
@@ -34,12 +34,18 @@ func TestCatalogEnabledEvents(t *testing.T) {
EventSingleChat,
EventInChat,
EventFromUser,
EventAllSingleChat,
EventAllGroupChat,
EventReadO2O,
EventReadGroup,
EventRecallO2O,
EventRecallGroup,
EventReactionO2O,
EventReactionGroup,
EventGroupUpdated,
EventGroupMemberAdded,
EventGroupMemberExited,
EventGroupDisbanded,
}
if !reflect.DeepEqual(keys, want) {
t.Fatalf("keys = %#v, want %#v", keys, want)
@@ -96,7 +102,24 @@ func TestDefinitionJSONHidesInternalSchemaIDs(t *testing.T) {
}
func TestSchemaDocumentsDefaultToTransportEnvelope(t *testing.T) {
for _, eventKey := range []string{EventMention, EventSingleChat, EventInChat, EventFromUser} {
for _, eventKey := range []string{
EventMention,
EventSingleChat,
EventInChat,
EventFromUser,
EventAllSingleChat,
EventAllGroupChat,
EventReadO2O,
EventReadGroup,
EventRecallO2O,
EventRecallGroup,
EventReactionO2O,
EventReactionGroup,
EventGroupUpdated,
EventGroupMemberAdded,
EventGroupMemberExited,
EventGroupDisbanded,
} {
t.Run(eventKey, func(t *testing.T) {
def, ok := Lookup(eventKey)
if !ok {
@@ -189,7 +212,7 @@ func TestSchemaDocumentsDefaultToTransportEnvelope(t *testing.T) {
}
func TestFlattenedSchemaDocumentsUseMessageDTO(t *testing.T) {
for _, eventKey := range []string{EventMention, EventSingleChat, EventInChat, EventFromUser} {
for _, eventKey := range []string{EventMention, EventSingleChat, EventInChat, EventFromUser, EventAllSingleChat, EventAllGroupChat} {
t.Run(eventKey, func(t *testing.T) {
def, ok := Lookup(eventKey)
if !ok {
@@ -206,7 +229,7 @@ func TestFlattenedSchemaDocumentsUseMessageDTO(t *testing.T) {
wantProperties := []string{
"type", "event_id", "timestamp", "subscribe_id", "message_id",
"conversation_id", "sender", "sender_open_dingtalk_id", "content",
"create_time", "event_time",
"create_time", "event_time", "quoted_message", "forward_messages",
}
if len(props) != len(wantProperties) {
t.Fatalf("schema.properties = %#v, want exactly %d DTO fields", props, len(wantProperties))
@@ -221,6 +244,22 @@ func TestFlattenedSchemaDocumentsUseMessageDTO(t *testing.T) {
t.Fatalf("flattened schema exposed transport field %q", transportField)
}
}
quoted := props["quoted_message"].(map[string]any)
if quoted["type"] != "object" {
t.Fatalf("quoted_message schema = %#v, want object", quoted)
}
quotedProperties, ok := quoted["properties"].(map[string]any)
if !ok || len(quotedProperties) != 6 {
t.Fatalf("quoted_message properties = %#v, want six context fields", quoted["properties"])
}
forward := props["forward_messages"].(map[string]any)
if forward["type"] != "array" {
t.Fatalf("forward_messages schema = %#v, want array", forward)
}
items, ok := forward["items"].(map[string]any)
if !ok || items["type"] != "object" {
t.Fatalf("forward_messages items = %#v, want object", forward["items"])
}
})
}
}
@@ -269,6 +308,15 @@ func TestTargetUIDEventSchemasRequireUserOrOpenDingTalkID(t *testing.T) {
if group.Constraints != nil {
t.Fatalf("group constraints = %#v, want none", group.Constraints)
}
for _, eventKey := range []string{EventAllSingleChat, EventAllGroupChat} {
def, _ := Lookup(eventKey)
if len(def.RequiredParams) != 0 {
t.Fatalf("%s required_params = %#v, want none", eventKey, def.RequiredParams)
}
if def.Constraints != nil {
t.Fatalf("%s constraints = %#v, want none", eventKey, def.Constraints)
}
}
}
func TestDefinitionCopiesDoNotMutateRegistryConstraints(t *testing.T) {
@@ -353,6 +401,86 @@ func TestActionSchemaDocumentsMatchOutputDTOs(t *testing.T) {
}
}
func TestGroupLifecycleSchemaDocumentsUseConservativePayload(t *testing.T) {
wantProperties := []string{"type", "event_id", "timestamp", "subscribe_id", "payload"}
for _, eventKey := range []string{EventGroupUpdated, EventGroupDisbanded} {
t.Run(eventKey, func(t *testing.T) {
def, ok := Lookup(eventKey)
if !ok {
t.Fatalf("Lookup(%q) failed", eventKey)
}
if want := []string{"group"}; !reflect.DeepEqual(def.RequiredParams, want) {
t.Fatalf("required_params = %#v, want %#v", def.RequiredParams, want)
}
doc := BuildSchemaDocumentForMode(def, true)
props, ok := doc.Schema["properties"].(map[string]any)
if !ok {
t.Fatalf("schema.properties = %#v", doc.Schema["properties"])
}
if len(props) != len(wantProperties) {
t.Fatalf("schema.properties = %#v, want exactly %d fields", props, len(wantProperties))
}
for _, name := range wantProperties {
if _, ok := props[name].(map[string]any); !ok {
t.Fatalf("schema.properties.%s = %#v, want object", name, props[name])
}
}
payload := props["payload"].(map[string]any)
if payload["type"] != "object" || payload["additionalProperties"] != true {
t.Fatalf("schema.properties.payload = %#v, want open object", payload)
}
})
}
}
func TestGroupMemberSchemaDocumentsMatchOutputDTO(t *testing.T) {
wantProperties := []string{
"type", "event_id", "timestamp", "subscribe_id", "conversation_id",
"operator", "operator_open_dingtalk_id", "members", "event_time",
}
for _, eventKey := range []string{EventGroupMemberAdded, EventGroupMemberExited} {
t.Run(eventKey, func(t *testing.T) {
def, ok := Lookup(eventKey)
if !ok {
t.Fatalf("Lookup(%q) failed", eventKey)
}
if want := []string{"group"}; !reflect.DeepEqual(def.RequiredParams, want) {
t.Fatalf("required_params = %#v, want %#v", def.RequiredParams, want)
}
doc := BuildSchemaDocumentForMode(def, true)
props, ok := doc.Schema["properties"].(map[string]any)
if !ok || len(props) != len(wantProperties) {
t.Fatalf("schema.properties = %#v, want exactly %d fields", doc.Schema["properties"], len(wantProperties))
}
for _, name := range wantProperties {
if _, ok := props[name].(map[string]any); !ok {
t.Fatalf("schema.properties.%s = %#v, want object", name, props[name])
}
}
members := props["members"].(map[string]any)
if members["type"] != "array" {
t.Fatalf("schema.properties.members = %#v, want array", members)
}
items, ok := members["items"].(map[string]any)
if !ok || items["type"] != "object" {
t.Fatalf("schema.properties.members.items = %#v, want object", members["items"])
}
memberProps, ok := items["properties"].(map[string]any)
if !ok || len(memberProps) != 2 {
t.Fatalf("schema.properties.members.items.properties = %#v", items["properties"])
}
for _, name := range []string{"nick", "open_dingtalk_id"} {
if _, ok := memberProps[name].(map[string]any); !ok {
t.Fatalf("member schema missing %q: %#v", name, memberProps)
}
}
if _, ok := props["payload"]; ok {
t.Fatalf("group member schema exposed generic payload: %#v", props)
}
})
}
}
func TestBuildRuleParamMention(t *testing.T) {
rule, param, err := BuildRuleParam(EventMention, RuleOptions{})
if err != nil {
@@ -366,6 +494,29 @@ func TestBuildRuleParamMention(t *testing.T) {
}
}
func TestBuildRuleParamAllEvents(t *testing.T) {
for _, eventKey := range []string{EventAllSingleChat, EventAllGroupChat} {
t.Run(eventKey, func(t *testing.T) {
rule, param, err := BuildRuleParam(eventKey, RuleOptions{})
if err != nil {
t.Fatalf("BuildRuleParam() error = %v", err)
}
if rule != "all" || len(param) != 0 {
t.Fatalf("rule = %q, param = %#v; want all and empty map", rule, param)
}
for name, opts := range map[string]RuleOptions{
"user": {UserID: "staff-1"},
"open-dingtalk-id": {OpenDingTalkID: "open-user-1"},
"group": {GroupID: "cid-1"},
} {
if _, _, err := BuildRuleParam(eventKey, opts); err == nil || !strings.Contains(err.Error(), "--"+name+" is not supported for "+eventKey) {
t.Fatalf("%s error = %v, want unsupported flag", name, err)
}
}
})
}
}
func TestBuildRuleParamSingleChatRequiresPeer(t *testing.T) {
_, _, err := BuildRuleParam(EventSingleChat, RuleOptions{})
if err == nil || !strings.Contains(err.Error(), "one of --user or --open-dingtalk-id is required") {
@@ -469,7 +620,7 @@ func TestBuildRuleParamActionEvents(t *testing.T) {
})
}
for _, eventKey := range []string{EventReadGroup, EventRecallGroup, EventReactionGroup} {
for _, eventKey := range []string{EventReadGroup, EventRecallGroup, EventReactionGroup, EventGroupUpdated, EventGroupMemberAdded, EventGroupMemberExited, EventGroupDisbanded} {
t.Run(eventKey, func(t *testing.T) {
if _, _, err := BuildRuleParam(eventKey, RuleOptions{}); err == nil || !strings.Contains(err.Error(), "--group is required for "+eventKey) {
t.Fatalf("missing group error = %v", err)
@@ -533,6 +684,31 @@ func TestBuildFilterQueryAndJSON(t *testing.T) {
}
}
func TestSupportsMessageFilter(t *testing.T) {
for _, eventKey := range []string{
EventMention,
EventSingleChat,
EventInChat,
EventFromUser,
EventAllSingleChat,
EventAllGroupChat,
} {
if !SupportsMessageFilter(eventKey) {
t.Fatalf("SupportsMessageFilter(%q) = false, want true", eventKey)
}
}
for _, eventKey := range []string{
EventReadO2O,
EventReactionGroup,
EventGroupUpdated,
"unknown_event",
} {
if SupportsMessageFilter(eventKey) {
t.Fatalf("SupportsMessageFilter(%q) = true, want false", eventKey)
}
}
}
func TestIdempotencyKeyUsesLocalIdentityKey(t *testing.T) {
left := Identity{LocalSubject: "refresh:left", ClientID: "client-1", SourceID: "open"}
right := Identity{LocalSubject: "refresh:right", ClientID: "client-1", SourceID: "open"}
+46
View File
@@ -691,6 +691,52 @@ func TestPersonalSourceSenderEventPassesNormalBusFilter(t *testing.T) {
}
}
func TestPersonalSourceNewIMEventsPassNormalBusFilter(t *testing.T) {
for _, eventKey := range []string{
"user_im_message_receive_o2o_all",
"user_im_message_receive_group_all",
"user_im_group_updated",
"user_im_group_member_added",
"user_im_group_member_exited",
"user_im_group_disbanded",
} {
t.Run(eventKey, func(t *testing.T) {
subscribeID := "sub-" + eventKey
src := personalSourceForRawEventTests()
raw := src.rawEventFromDataFrame(&payload.DataFrame{
Headers: payload.DataFrameHeader{
"EVENT_TYPE": eventKey,
"SUB_ID": subscribeID,
},
Data: fmt.Sprintf(`{"eventKey":%q,"subId":%q,"payload":{"body":{"content":"test"}}}`, eventKey, subscribeID),
})
h := bus.NewHub(10)
consumer, err := h.Register(transport.Hello{
EventTypes: []string{eventKey},
SubscribeID: subscribeID,
})
if err != nil {
t.Fatal(err)
}
h.Deliver(raw)
select {
case frame := <-consumer.SendCh:
eventFrame, ok := frame.(transport.Event)
if !ok {
t.Fatalf("frame = %T, want transport.Event", frame)
}
if eventFrame.EventType != eventKey || eventFrame.SubscribeID != subscribeID {
t.Fatalf("event = %#v", eventFrame)
}
case <-time.After(time.Second):
t.Fatal("timed out waiting for filtered event")
}
})
}
}
func personalSourceForRawEventTests() *PersonalSource {
return &PersonalSource{cfg: PersonalConfig{
SourceID: "fallback_source",
+35 -11
View File
@@ -26,14 +26,16 @@ import (
type FrameType string
const (
FrameTypeHello FrameType = "hello" // consume → bus
FrameTypeHelloAck FrameType = "hello_ack" // bus → consume
FrameTypeEvent FrameType = "event" // bus → consume
FrameTypeHeartbeat FrameType = "heartbeat" // bidirectional
FrameTypeSourceState FrameType = "source_state" // bus → consume
FrameTypeBye FrameType = "bye" // bidirectional
FrameTypeStatusReq FrameType = "status_req" // consume/ad-hoc → bus
FrameTypeStatusResp FrameType = "status_resp" // bus → consume/ad-hoc
FrameTypeHello FrameType = "hello" // consume → bus
FrameTypeHelloAck FrameType = "hello_ack" // bus → consume
FrameTypeEvent FrameType = "event" // bus → consume
FrameTypeHeartbeat FrameType = "heartbeat" // bidirectional
FrameTypeSourceState FrameType = "source_state" // bus → consume
FrameTypeBye FrameType = "bye" // bidirectional
FrameTypeStatusReq FrameType = "status_req" // consume/ad-hoc → bus
FrameTypeStatusResp FrameType = "status_resp" // bus → consume/ad-hoc
FrameTypeConsumerStopReq FrameType = "consumer_stop_req" // ad-hoc → bus
FrameTypeConsumerStopResp FrameType = "consumer_stop_resp" // bus → ad-hoc
)
// Hello is the first frame a consumer sends after dialing the bus. The bus
@@ -56,9 +58,10 @@ type Hello struct {
type HelloRole string
const (
HelloRoleConsumer HelloRole = "" // default
HelloRoleStatus HelloRole = "status" // event list / event status
HelloRoleStop HelloRole = "stop" // event stop (graceful trigger)
HelloRoleConsumer HelloRole = "" // default
HelloRoleStatus HelloRole = "status" // event list / event status
HelloRoleStop HelloRole = "stop" // event stop (graceful trigger)
HelloRoleConsumerStop HelloRole = "consumer_stop" // stop selected consumers only
)
// HelloAck is the bus's reply on accepted Hello. SourceState/StateSource
@@ -117,11 +120,32 @@ type SourceState struct {
// "shutdown" — bus SIGTERM/SIGINT
// "idle_timeout" — bus IdleTimeout fired with no consumers
// "stop_request" — bus received explicit Stop RPC
// "subscription_stopped" — one consumer was removed by subscribe_id
type Bye struct {
Type FrameType `json:"type"`
Reason string `json:"reason"`
}
const ByeReasonSubscriptionStopped = "subscription_stopped"
// ConsumerStopReq asks the bus to close consumers whose exact personal
// subscription IDs match. It is an additive local IPC control operation;
// the remote Stream connection remains alive while other consumers exist.
type ConsumerStopReq struct {
Type FrameType `json:"type"`
SubscribeIDs []string `json:"subscribe_ids"`
}
// ConsumerStopResp reports which requested subscriptions had a live local
// consumer. Missing IDs are not errors because a server subscription can be
// cancelled after its foreground consumer has already exited.
type ConsumerStopResp struct {
Type FrameType `json:"type"`
Stopped []string `json:"stopped,omitempty"`
NotFound []string `json:"not_found,omitempty"`
Error string `json:"error,omitempty"`
}
// StatusReq is an empty JSON frame ad-hoc tooling sends after Hello to
// request a full StatusResp. Bus replies with one StatusResp then closes
// the connection.
+47 -11
View File
@@ -28,14 +28,16 @@ func TestFrameType_StableWireValues(t *testing.T) {
// change. The test value list is duplicated here on purpose so a
// reviewer renaming a constant is forced to also update the test.
wants := map[FrameType]string{
FrameTypeHello: "hello",
FrameTypeHelloAck: "hello_ack",
FrameTypeEvent: "event",
FrameTypeHeartbeat: "heartbeat",
FrameTypeSourceState: "source_state",
FrameTypeBye: "bye",
FrameTypeStatusReq: "status_req",
FrameTypeStatusResp: "status_resp",
FrameTypeHello: "hello",
FrameTypeHelloAck: "hello_ack",
FrameTypeEvent: "event",
FrameTypeHeartbeat: "heartbeat",
FrameTypeSourceState: "source_state",
FrameTypeBye: "bye",
FrameTypeStatusReq: "status_req",
FrameTypeStatusResp: "status_resp",
FrameTypeConsumerStopReq: "consumer_stop_req",
FrameTypeConsumerStopResp: "consumer_stop_resp",
}
for ft, want := range wants {
if string(ft) != want {
@@ -46,9 +48,10 @@ func TestFrameType_StableWireValues(t *testing.T) {
func TestHelloRole_StableWireValues(t *testing.T) {
wants := map[HelloRole]string{
HelloRoleConsumer: "",
HelloRoleStatus: "status",
HelloRoleStop: "stop",
HelloRoleConsumer: "",
HelloRoleStatus: "status",
HelloRoleStop: "stop",
HelloRoleConsumerStop: "consumer_stop",
}
for r, want := range wants {
if string(r) != want {
@@ -57,6 +60,39 @@ func TestHelloRole_StableWireValues(t *testing.T) {
}
}
func TestConsumerStopFrames_Roundtrip(t *testing.T) {
inReq := ConsumerStopReq{
Type: FrameTypeConsumerStopReq,
SubscribeIDs: []string{"sub-a", "sub-b"},
}
var outReq ConsumerStopReq
roundTrip(t, inReq, &outReq)
if outReq.Type != FrameTypeConsumerStopReq || strings.Join(outReq.SubscribeIDs, ",") != "sub-a,sub-b" {
t.Fatalf("request roundtrip = %#v", outReq)
}
inResp := ConsumerStopResp{
Type: FrameTypeConsumerStopResp,
Stopped: []string{"sub-a"},
NotFound: []string{"sub-b"},
}
var outResp ConsumerStopResp
roundTrip(t, inResp, &outResp)
if outResp.Type != FrameTypeConsumerStopResp || strings.Join(outResp.Stopped, ",") != "sub-a" || strings.Join(outResp.NotFound, ",") != "sub-b" {
t.Fatalf("response roundtrip = %#v", outResp)
}
inError := ConsumerStopResp{
Type: FrameTypeConsumerStopResp,
Error: "malformed consumer stop request",
}
var outError ConsumerStopResp
roundTrip(t, inError, &outError)
if outError.Type != FrameTypeConsumerStopResp || outError.Error != inError.Error {
t.Fatalf("error response roundtrip = %#v", outError)
}
}
func roundTrip(t *testing.T, in any, dst any) {
t.Helper()
b, err := json.Marshal(in)
+285
View File
@@ -0,0 +1,285 @@
// Copyright 2026 Alibaba Group
// Licensed under the Apache License, Version 2.0 (the "License");
package helpers
import (
"bytes"
"context"
"encoding/json"
"errors"
"io"
"strings"
"testing"
"github.com/DingTalk-Real-AI/dingtalk-workspace-cli/pkg/edition"
"github.com/spf13/cobra"
)
type contractDefectCaller struct {
dryRun bool
calls []guardedMutationCall
responses map[string]string
errors map[string]error
}
func (c *contractDefectCaller) CallTool(_ context.Context, productID, toolName string, args map[string]any) (*edition.ToolResult, error) {
c.calls = append(c.calls, guardedMutationCall{productID: productID, toolName: toolName, args: args})
key := productID + "/" + toolName
if err := c.errors[key]; err != nil {
return nil, err
}
text := c.responses[key]
if text == "" {
text = `{}`
}
return &edition.ToolResult{Content: []edition.ContentBlock{{Type: "text", Text: text}}}, nil
}
func (*contractDefectCaller) Format() string { return "json" }
func (c *contractDefectCaller) DryRun() bool { return c.dryRun }
func (*contractDefectCaller) Fields() string { return "" }
func (*contractDefectCaller) JQ() string { return "" }
func executeContractDefectCommand(t *testing.T, caller *contractDefectCaller, build func() *cobra.Command, args ...string) (string, error) {
t.Helper()
previousDeps := deps
t.Cleanup(func() { deps = previousDeps })
InitDeps(caller)
var stdout bytes.Buffer
deps.Out.w = &stdout
deps.Out.errW = io.Discard
root := build()
if root.PersistentFlags().Lookup("yes") == nil {
root.PersistentFlags().Bool("yes", false, "confirm high-risk operation")
}
if root.PersistentFlags().Lookup("dry-run") == nil {
root.PersistentFlags().Bool("dry-run", false, "preview without executing")
}
root.SilenceErrors = true
root.SilenceUsage = true
root.SetArgs(args)
err := root.Execute()
return stdout.String(), err
}
func TestApprovalRevokeDryRunSkipsConfirmationAndEmitsPreview(t *testing.T) {
caller := &contractDefectCaller{dryRun: true}
output, err := executeContractDefectCommand(t, caller, newOaCommand,
"approval", "revoke", "--instance-id", "instance-dry-run", "--dry-run")
if err != nil {
t.Fatalf("approval revoke dry-run returned error: %v", err)
}
if len(caller.calls) != 0 {
t.Fatalf("dry-run tool calls = %#v, want none", caller.calls)
}
if !strings.Contains(output, `"tool": "revoke_processInstance"`) ||
!strings.Contains(output, `"processInstanceId": "instance-dry-run"`) {
t.Fatalf("dry-run output = %q, want revoke preview", output)
}
}
func TestDocVersionRevertDryRunSkipsRemotePreflightAndEmitsPreview(t *testing.T) {
caller := &contractDefectCaller{dryRun: true}
output, err := executeContractDefectCommand(t, caller, newDocCommand,
"version", "revert", "--node", "node-dry-run", "--version", "7", "--dry-run")
if err != nil {
t.Fatalf("doc version revert dry-run returned error: %v", err)
}
if len(caller.calls) != 0 {
t.Fatalf("dry-run tool calls = %#v, want none", caller.calls)
}
if !strings.Contains(output, `"tool": "revert_doc_version"`) ||
!strings.Contains(output, `"version": 7`) {
t.Fatalf("dry-run output = %q, want version revert preview", output)
}
}
func TestDocRenamePreservesCallerProvidedDisplayName(t *testing.T) {
renameCmd, remaining, err := newDocCommand().Find([]string{"rename"})
if err != nil || len(remaining) != 0 {
t.Fatalf("find doc rename: command=%v remaining=%v err=%v", renameCmd, remaining, err)
}
nameFlag := renameCmd.Flags().Lookup("name")
if nameFlag == nil {
t.Fatal("doc rename --name flag is missing")
}
if !strings.Contains(nameFlag.Usage, "原样传给服务端") ||
!strings.Contains(nameFlag.Usage, "drive rename") ||
strings.Contains(nameFlag.Usage, "自动去掉") {
t.Fatalf("doc rename --name usage = %q, want verbatim forwarding and drive rename routing", nameFlag.Usage)
}
caller := &contractDefectCaller{}
if _, err = executeContractDefectCommand(t, caller, newDocCommand,
"rename", "--node", "node-1", "--name", "release.v2"); err != nil {
t.Fatalf("doc rename returned error: %v", err)
}
if len(caller.calls) != 1 {
t.Fatalf("tool calls = %#v, want one rename call", caller.calls)
}
if got := caller.calls[0].args["newName"]; got != "release.v2" {
t.Fatalf("newName = %#v, want release.v2", got)
}
}
func TestDriveRenameUsesNodeTypeAndCurrentExtension(t *testing.T) {
tests := []struct {
name string
metadata string
nodeID string
wantFileID string
requested string
wantNewName string
}{
{
name: "file strips matching extension outside old whitelist",
metadata: `{"result":{"type":"file","extension":"heic"}}`,
nodeID: "https://alidocs.dingtalk.com/i/nodes/node-1",
wantFileID: "node-1",
requested: "photo.HEIC",
wantNewName: "photo",
},
{
name: "folder preserves dotted display name",
metadata: `{"result":{"type":"folder","extension":"v2"}}`,
requested: "release.v2",
wantNewName: "release.v2",
},
{
name: "different current extension is preserved",
metadata: `{"result":{"nodeType":"file","fileExtension":"txt"}}`,
requested: "report.final",
wantNewName: "report.final",
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
caller := &contractDefectCaller{responses: map[string]string{
"drive/get_file_info": test.metadata,
}}
nodeID := test.nodeID
if nodeID == "" {
nodeID = "node-1"
}
if _, err := executeContractDefectCommand(t, caller, newDriveCommand,
"rename", "--node", nodeID, "--name", test.requested); err != nil {
t.Fatalf("drive rename returned error: %v", err)
}
if len(caller.calls) != 2 {
t.Fatalf("tool calls = %#v, want metadata read plus rename", caller.calls)
}
if caller.calls[0].productID != "drive" || caller.calls[0].toolName != "get_file_info" {
t.Fatalf("first call = %#v, want drive/get_file_info", caller.calls[0])
}
wantFileID := test.wantFileID
if wantFileID == "" {
wantFileID = nodeID
}
if got := caller.calls[0].args["fileId"]; got != wantFileID {
t.Fatalf("metadata fileId = %#v, want %q", got, wantFileID)
}
if caller.calls[1].productID != "doc" || caller.calls[1].toolName != "rename_document" {
t.Fatalf("second call = %#v, want doc/rename_document", caller.calls[1])
}
if got := caller.calls[1].args["newName"]; got != test.wantNewName {
t.Fatalf("newName = %#v, want %q", got, test.wantNewName)
}
})
}
}
func TestDriveRenameMetadataFailuresDoNotMutate(t *testing.T) {
tests := []struct {
name string
responses map[string]string
errors map[string]error
}{
{
name: "metadata call error",
errors: map[string]error{"drive/get_file_info": errors.New("metadata unavailable")},
},
{
name: "invalid metadata JSON",
responses: map[string]string{"drive/get_file_info": `{`},
},
{
name: "metadata result missing",
responses: map[string]string{"drive/get_file_info": `{}`},
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
caller := &contractDefectCaller{responses: test.responses, errors: test.errors}
_, err := executeContractDefectCommand(t, caller, newDriveCommand,
"rename", "--node", "node-1", "--name", "report.txt")
if err == nil {
t.Fatal("drive rename returned nil error")
}
if len(caller.calls) != 1 || caller.calls[0].toolName != "get_file_info" {
t.Fatalf("tool calls = %#v, want metadata read only", caller.calls)
}
})
}
}
func TestDriveRenameDryRunDefersMetadataNormalization(t *testing.T) {
caller := &contractDefectCaller{dryRun: true}
output, err := executeContractDefectCommand(t, caller, newDriveCommand,
"rename", "--node", "node-1", "--name", "photo.heic", "--dry-run")
if err != nil {
t.Fatalf("drive rename dry-run returned error: %v", err)
}
if len(caller.calls) != 0 {
t.Fatalf("dry-run tool calls = %#v, want none", caller.calls)
}
if !strings.Contains(output, `"newName": "photo.heic"`) {
t.Fatalf("dry-run output = %q, want unmodified input name", output)
}
}
func TestDriveRenameBaseNameConservativeEdges(t *testing.T) {
tests := []struct {
name string
nodeType string
extension string
want string
}{
{name: ".txt", nodeType: "file", extension: "txt", want: ".txt"},
{name: "report.txt", nodeType: "directory", extension: "txt", want: "report.txt"},
{name: "report.txt", nodeType: "dir", extension: "txt", want: "report.txt"},
{name: " report.txt ", nodeType: "file", extension: "", want: "report.txt"},
{name: "report.txt", nodeType: "file", extension: ".txt", want: "report"},
}
for _, test := range tests {
if got := driveRenameBaseName(test.name, test.nodeType, test.extension); got != test.want {
t.Fatalf("driveRenameBaseName(%q, %q, %q) = %q, want %q",
test.name, test.nodeType, test.extension, got, test.want)
}
}
}
func TestDriveInfoRestoresFileSizeWhenDocMetadataIsNull(t *testing.T) {
caller := &contractDefectCaller{responses: map[string]string{
"drive/get_file_info": `{"result":{"fileId":"node-1","extension":"adoc","fileSize":93682}}`,
"doc/get_document_info": `{"result":{"title":"Doc","fileSize":null}}`,
}}
output, err := executeContractDefectCommand(t, caller, newDriveCommand,
"info", "--node", "node-1")
if err != nil {
t.Fatalf("drive info returned error: %v", err)
}
var payload struct {
Result struct {
FileSize int64 `json:"fileSize"`
} `json:"result"`
}
if err := json.Unmarshal([]byte(output), &payload); err != nil {
t.Fatalf("decode drive info output %q: %v", output, err)
}
if payload.Result.FileSize != 93682 {
t.Fatalf("fileSize = %d, want 93682", payload.Result.FileSize)
}
}

Some files were not shown because too many files have changed in this diff Show More