352 Commits
Author SHA1 Message Date
修雨 2ba1dcdda4 docs(changelog): add 1.0.26 release notes (#271)
Cover the 5 PRs merged since v1.0.25:
- #259 -f ndjson / -f csv + real-traffic preferredListKeys extension
- #250 chat send --title required for direct messages (pre-existing
  Unreleased entry preserved verbatim)
- #242 Windows PAT URL truncation fix via rundll32 opener
- #268 axls preflight on dws doc download
- #267 DWS_DISABLE_KEYCHAIN fallback for macOS sandbox

Also document the resolution of #240 (dws doc comment *
PARAM_ERROR - 未找到指定工具): fix is in the market metadata
(`serverOverride: doc-comment` on the four comment toolOverrides)
rather than in CLI code, so existing users need to run
`dws cache refresh` once. Verified post-refresh that dry-run
resolves to the doc-comment MCP server endpoint and real calls
return normal business responses instead of the PARAM_ERROR.
v1.0.26
2026-05-12 16:40:34 +08:00
修雨 1637ae16c7 fix(keychain): add DWS_DISABLE_KEYCHAIN fallback for macOS sandbox (#214) (#267)
In sandboxed macOS runtimes (e.g. Codex App), `security` / Keychain APIs
are blocked, so `keyring.Get`/`Set` for the DEK fails on every token
read/write. Add an opt-in env var that switches the macOS implementation
to the same file-based DEK scheme already used on Linux. Default
behavior is unchanged.

- Extract shared `fileDEK(service)` into `file_dek.go` (darwin || linux)
- Linux `getDEK` now delegates to `fileDEK`
- Darwin `getDEK` short-circuits to `fileDEK` when DWS_DISABLE_KEYCHAIN=1
- Document the tradeoff in reference.md (DEK and ciphertext co-located)
- Add darwin-only tests covering fallback path + overwrite
2026-05-12 14:57:00 +08:00
xuanandshangguanxuan.sgx eee19d7347 fix(pat): preserve auth link on Windows browser open (#242)
* fix(pat): preserve auth link on Windows browser open

* fix(pat): expose copy-safe authorization URL

* test(pat): preserve extra authorization route params

---------

Co-authored-by: shangguanxuan.sgx <shangguanxuan.sgx@alibaba-inc.com>
2026-05-12 14:55:59 +08:00
xuanandshangguanxuan.sgx 19f7b59ffb fix(doc): preflight axls download (#268)
Co-authored-by: shangguanxuan.sgx <shangguanxuan.sgx@alibaba-inc.com>
2026-05-12 11:48:31 +08:00
修雨 c4952d0207 feat(output): add -f ndjson and -f csv (#252) (#259)
* feat(output): add -f ndjson (implemented) and scaffold -f csv (#252)

`larksuite/cli` exposes --format ndjson / csv; dws only had json/table/raw/
pretty. This adds both as recognised global formats:

- ndjson: fully implemented (internal/output/ndjson.go) — top-level arrays
  and well-known list wrappers ({items|results|data|records|...}) emit one
  compact JSON document per line; anything else degrades to a single line.
  Streaming-friendly counterpart to `-f json`.
- csv: scaffolded (internal/output/csv.go) — writeCSV currently returns a
  clear "not implemented (#252)" error rather than silently degrading to
  JSON. A TODO block spells out the planned implementation (reuse the table
  renderer's extractRowsFromMap/rowsFromSlice flattening + encoding/csv).

Wired through Write() and normalizeFormat(); --format help string now lists
ndjson (csv to be added when writeCSV lands). Tests cover ndjson rendering
(array / wrapped-list / scalar) and pin the csv not-implemented contract.

Skeleton PR for #252 — ndjson is shippable as-is; csv is left for a
follow-up commit on this branch.

* feat(output): implement -f csv (#252)

Completes the CSV half of the format work. writeCSV mirrors the shape
decisions `-f table` already makes (reuses normalizePayload /
unwrapPrimaryObject / extractRowsFromMap / rowsFromSlice / formatValue) so
columns and value flattening are consistent between the two formats:

- a list of objects (bare or wrapped under items/results/data/records/...) →
  header row + one row per element; union of keys sorted; missing values are
  empty cells; nested objects/arrays render as compact JSON in the cell;
  sibling metadata of the list (total, hasMore, ...) is dropped.
- a single object → two-column key,value CSV (keys sorted).
- a non-uniform list / scalar → single-column `value` CSV.
- empty / nil → empty document.

encoding/csv.Writer handles RFC-4180 quoting (commas, quotes, newlines);
cells go through formatValue (also strips terminal control sequences, same
as the table renderer). `--fields` projection composes for free since
WriteFiltered applies SelectFields before Write.

--format help now lists csv; FormatCSV doc comment dropped the WIP marker;
the not-implemented test is replaced with real coverage (list with
comma/CJK/nested-array-as-JSON, wrapped-list-with-metadata, single object,
scalar) plus a --fields composition test.

* feat(output): broadcast list metadata as trailing columns in -f csv (#252)

Per review preference: instead of dropping the list's sibling metadata
(total, hasMore, ...) when rendering {records:[...], total:N} as CSV, append
each meta key as a trailing column repeated on every row, so a CSV consumer
never silently loses it (CSV has no "footer table" the way the table
renderer does). Meta keys colliding with a data column are skipped; an empty
list still emits the header (data + meta) plus one row of empty data cells
carrying the meta values. New broadcastMeta helper; doc comment + tests
updated (incl. an empty-list-with-metadata case).

* feat(output): recognise real DingTalk envelope keys in -f csv/ndjson/table

之前 -f csv 和 -f ndjson 的 list 检测白名单只认 items/results/data/list/
records/tools/servers/products,但真实钉钉响应用的是 result(单数直接数组
或一层包裹)/documents/emailAccounts/todoCards/events/messages,导致大
部分 list 命令的 csv 输出退化成 key,value 二列、ndjson 退化成整包一行。

修复点:

1. preferredListKeys 扩展加上真实 envelope key(result/documents/
   emailAccounts/todoCards/events/messages),并把它升级为 csv/table/
   ndjson 共享的"单一事实源"——filter.go 的 findDataList 不再维护自己
   的本地副本,直接复用这个列表。
2. extractRowsFromMap 改为委托 findDataList,自动获得"一层深度"的
   wrapper 支持({result: {todoCards: [...]}} 这种 envelope 现在能识
   别)。meta 合并:outer + inner 双层 sibling 拉通,outer 同名 key
   优先(避免 inner 把外层 success/total 等覆盖掉)。
3. writeTableish 和 writeCSV 调整 unwrapPrimaryObject 和
   extractRowsFromMap 的优先级——先试 list 检测,没命中再走 unwrap,
   避免 {result: {todoCards: [...]}} 被 unwrap 剥掉外层后直接走
   key,value 分支。
4. findDataList 允许"空数组+preferred key" 命中,保留原来"空 list +
   metadata 仍渲染为表格 + meta 广播一行"的行为。

新增 TestTabularDetectsRealDingTalkEnvelopes:四个真实 envelope 形态
(contact user search 的 result 直接数组、doc search 的 documents 顶层、
mail mailbox list 的 emailAccounts、todo task list 的 result.todoCards
一层深度)验证 ndjson 行数和 csv 表头都符合预期。

真接口回归(已登录态跑过):
- dws contact user search -f csv  → name/userId 列正常出表
- dws todo task list -f ndjson    → 20 行一条任务,可 jq -r .subject 直接管
- dws doc search -f csv           → 10 行 + nextPageToken 等 meta 广播尾列
- dws schema -f csv               → 无回归

Closes part of #252 follow-up.
2026-05-11 22:15:59 +08:00
修雨 ecf2684f58 docs(skills): add sheet product reference rewritten against dws schema (#266)
The `sheet` (在线电子表格) product registers **34 envelope tools** that
have been live for a while, but `skills/references/products/sheet.md`
was never added, and `skills/SKILL.md` 产品总览 didn't list `sheet`.
Agents had no per-command reference and would skip the product during
intent routing. This PR closes the gap with a doc **written against
the actual envelope state**, not copied from a downstream draft.

Process (different from prior #264, which was withdrawn for citing
phantom commands):

1. `dws schema | jq '.products[] | select(.id=="sheet")'` to enumerate
   the 34 real tools, with `required` and `flag_overlay` per tool.
2. Wrote sheet.md grouped by function: worksheet / range / dimension /
   merge / find-replace / filter-view (named views) / filter (sheet-
   level) / image / export. Each section lists tools with their
   canonical_path and cli_name as-they-actually-exist.
3. Documented v1.0.25 reality on naming: about a third of `sheet`
   tools still expose snake_case cli_names (`copy_sheet`,
   `submit_export_job`, `set_filter_criteria`, etc.) pending the
   `CLIAliases` (#246) rollout. Mixed style is called out at the top.
4. Documented the export reality: v1.0.25 envelope exposes only the
   atomic `submit_export_job` + `query_export_job`. There is **no
   consolidated `dws sheet export`** — Pipeline (#247) is the future
   plumbing for that. Doc walks through the two-step manual flow.
5. Verified before commit: every `dws sheet ...` reference in sheet.md
   maps to one of the 34 envelope cli paths. Zero phantom commands.
   Zero cross-repo `../url-patterns.md` style relative links.

Verification command:

  python3 verify.py  # set-diff sheet.md refs against `dws schema sheet`
  # → md covers 34/34 envelope cli paths; the only "extras" are
  #   `dws sheet export` and `dws sheet filter-view` mentioned
  #   purely as disambiguation/group prefix references.

Files:

- skills/references/products/sheet.md (new, 304 lines) — compact
  but complete: 命令命名风格说明 + 8 functional groups + common
  usage examples + 易混淆点 + 危险操作 + 何时不要用 sheet +
  权威参考命令.
- skills/SKILL.md — adds `sheet` row to 产品总览 table, adds an
  intent-routing line (在线电子表格/axls/工作表/单元格读写/合并
  单元格/筛选视图/导出 xlsx → `sheet`), extends frontmatter
  `description` to include 在线电子表格 (axls).
- CHANGELOG.md — extends v1.0.25 `### Added` with an entry that
  honestly describes both the 34 tools shipping and the v1.0.25
  caveats (mixed cli_name style + no consolidated export).
- README.md / README_zh.md — adds a Sheet row (34 cmds) to "Key
  Services" with full subcommand inventory; updates the total to
  "197 commands across 15 products" (was 163 / 14). `wiki` is
  intentionally untouched — it ships separately in PR #265.

Scope note: this PR intentionally does NOT bundle `wiki` —
PR #265 ships `wiki` independently because the prior combined
attempt #264 made review harder. Splitting keeps each PR
verifiable as a unit.
2026-05-11 16:54:07 +08:00
修雨 9e9b898dd2 docs(skills): add wiki product reference + register in SKILL.md/README (#265)
The `wiki` (知识库) product registers 7 envelope tools — `wiki.create_wikiSpace`,
`wiki.get_wikiSpace`, `wiki.list_wikiSpaces`, `wiki.search_wikiSpaces`, plus
`wiki.add_member` / `list_member` / `update_member` — surfacing as
`dws wiki space {create,get,list,search}` and `dws wiki member {add,list,update}`.
They've been registered for a while, but `skills/references/products/wiki.md`
was never added and `skills/SKILL.md` 产品总览 didn't list `wiki`, so agents
had no per-command reference to read and would skip it during intent routing.

Verified before commit: every `dws wiki ...` reference inside wiki.md
matches a `cli_name` from `dws schema` output (7/7).

Files:

- skills/references/products/wiki.md (new, 177 lines) — full command
  reference: space create / get / list / search + member add / list /
  update. Style matches existing `chat.md` / `aitable.md`. No cross-repo
  links (verified: 0 external relative refs).
- skills/SKILL.md — adds `wiki` row to 产品总览 table, adds an
  intent-routing line ("知识库 / wiki / 团队空间 / 知识库成员管理" → `wiki`),
  extends frontmatter `description` to include 知识库.
- CHANGELOG.md — v1.0.25 ### Added gains an entry explaining that the
  wiki envelope tools were already registered but the skill reference
  hadn't shipped; this release closes that doc gap.
- README.md / README_zh.md — adds a "Wiki" / "知识库" row to "Key Services"
  (7 cmds, subcommand groups `space` `member`), updates the total to
  "170 commands across 15 products" (was 163 / 14), removes `wiki` from
  the "Coming soon" callouts.

Scope note: this PR intentionally does NOT touch `sheet` — the prior
PR #264 was withdrawn after envelope verification showed several
sheet commands (`dws sheet export`, `media-upload`, `filter-view
set-criteria` / `clear-criteria`, `range get`) referenced in the
downstream draft don't actually exist in the v1.0.25 envelope. A
separate sheet PR will follow after a full rewrite against
`dws schema sheet`.
2026-05-11 16:21:53 +08:00
修雨andClaude Opus 4.7 17f692e7f1 fix(chat): require --title for direct messages, fix misleading help (#250)
* fix(chat): require --title for direct messages, fix misleading help

`dws chat message send --user <id> --text ...` (and the --open-dingtalk-id
variant) failed at the API layer with the cryptic "发群服务窗会话消息失败"
when --title was omitted, because send_direct_message_as_user requires a
title at the business layer. The CLI advertised the opposite: the --title
flag description said "可选" (optional) and one help example sent a direct
message without it.

- Validate --title up front for --user / --open-dingtalk-id sends:
  "--title is required for direct messages (--user / --open-dingtalk-id)".
  Group messages are unchanged (title stays optional there).
- Fix the long help, the --title flag description, the help examples, and
  skills/references/products/chat.md to say title is required for direct
  messages, optional for group messages.
- Tests: add --title to the direct-message routing cases that now require
  it, and add rejection cases for direct sends without --title.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* docs(changelog): add Unreleased entry for #250 chat send --title fix

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-11 16:21:41 +08:00
修雨 574d9aa2f7 docs(changelog): backfill 1.0.24 + add 1.0.25 release notes (#262)
- 1.0.24 (tag already pushed at 9e15115 on 2026-05-09): #248 embedded
  self-upgrade guard, #238 auth login help realignment, #261 release
  workflow_dispatch fallback.
- 1.0.25 (next release): #246 CLIAliases tool override + json_parse_strict
  transform, #247 Pipeline tool override + executor for multi-step
  workflows (submit → poll → download).
v1.0.25
2026-05-11 13:56:12 +08:00
修雨 1aaaef0274 feat(schema): add CLIAliases tool override + json_parse_strict transform (#246)
Two generic CLI envelope schema enhancements that close gaps surfaced by
the cli_to_mcp test suite without resorting to product-specific helpers:

1. CLIToolOverride.CLIAliases (registry.go + dynamic_commands.go)
   - Lets a single MCP tool register additional cobra command aliases via
     envelope JSON (e.g. `range read` accepts `range get`, `member list`
     accepts `member ls`). Plumbs through the existing Route.Aliases ->
     cobra.Command.Aliases path; conflicts with siblings are silently
     skipped by cobra.

2. json_parse_strict transform (transform.go)
   - Strict JSON variant of json_parse that does NOT fall back to YAML.
     Use when the upstream tool requires a structured array/object value
     and silently coercing malformed input to a scalar string would mask
     a real user error (observed: filter-view --criteria 'NOT_VALID_JSON'
     was being accepted and quietly creating an empty-criteria view).
2026-05-11 10:56:19 +08:00
修雨 00c037b5be feat(schema): add Pipeline tool override + executor for multi-step workflows (#247)
A generic envelope schema feature that lets a single CLI command orchestrate
an ordered sequence of MCP tool calls plus optional HTTP-download sinks,
declared entirely in the envelope JSON. The motivating use case is the
"submit-job + poll-status + download-result" pattern (e.g. sheet export),
which previously required hardcoded helper commands per product.

## Schema additions

- `CLIToolOverride.Pipeline []PipelineStep` — when non-empty, dispatch
  ignores the parent map key (no single "primary tool") and walks the
  steps in order. CLI surface (CLIName / Group / Flags) still applies.
- `PipelineStep` struct — supports two step types:
  - `type:"call"` (default) invokes Tool with templated Args. Optional
    PollUntilField/Value turn it into a polling loop with configurable
    PollIntervalSec / PollTimeoutSec.
  - `type:"download"` resolves DownloadURLField, fetches via HTTP GET,
    and writes the body to the path supplied by OutputFlag's value
    (with directory-path filename inference).
- `CLIFlagOverride.PipelineLocal bool` — marks a flag as CLI-side only;
  CollectBindings skips it so the value never reaches MCP params, but
  the pipeline executor reads it via extractFlagValuesByAlias.

## Template language

Two prefixes supported in PipelineStep.Args / DownloadURLField:

  $flag.<name>           — value of the user's CLI flag whose alias is <name>
  $step.<idx>.<dotPath>  — field from a prior step's response (idx 0-based)
  literal                — passed through

dotPath walks nested map[string]any so "$step.1.content.downloadUrl"
resolves through wrapped MCP envelopes correctly.

## Stdout contract

The download step always emits machine-parseable plain-text lines
("jobId: <id>\\n", "downloadUrl: <url>\\n") in addition to the standard
JSON output, so shell pipelines and regex-based test suites can extract
key values without parsing JSON. Structured callers continue to consume
the output.WriteCommandPayload JSON envelope.

## Why this is schema enhancement, not product hardcode

- The executor is product-agnostic: any envelope can declare a Pipeline
  and benefit (sheet export today; doc/drive/aitable async patterns
  tomorrow).
- The template language is the only product-coupling, and it lives in
  the envelope JSON — not in Go code.
- No sheet/wiki/aitable-specific code in dingtalk-workspace-cli.

Files: 1 new + 3 modified (~250 LOC of executor + ~30 LOC of schema
plumbing). Existing tests pass; new pipeline tests TBA in a follow-up
once the schema fields are in upstream.
2026-05-11 10:56:10 +08:00
修雨 9e15115ad4 ci(release): add workflow_dispatch trigger as fallback (#261)
GitHub occasionally drops tag push events; this adds a manual trigger
so we can re-run the release job against any tag ref without having to
delete and re-push the tag.
v1.0.24
2026-05-11 09:53:23 +08:00
xianfeng wangandgithub-actions[bot] 25bf3d12f2 feat(upgrade): block self-upgrade in embedded distributions (#248)
* chore: update coverage badge [skip ci]

* chore: update coverage badge [skip ci]

* chore: update coverage badge [skip ci]

* chore: update coverage badge [skip ci]

* chore: update coverage badge [skip ci]

* 1.0.19 changelog

* upgrade not in embed

* remove comment

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-05-09 20:27:20 +08:00
修雨 f78cc5c846 docs(auth): correct login help to reflect actual default + SSH guidance (#226) (#238)
* docs(auth): correct login help to reflect actual default + SSH guidance (#226)

The --help long description for `dws auth login` claimed the default mode was
"OAuth 设备流 (默认)", but the actual default starts a 127.0.0.1 loopback
listener (oauth_provider.go:131-136) and only switches to device flow when
--device is passed. SSH-into-headless-Linux users following the docs hit a
dead end because the local browser cannot reach 127.0.0.1 on the remote box.

Rewrite the description so each method is named after its real flag:
  - OAuth Loopback 流 (默认)
  - OAuth 设备流 (--device)
  - 直接提供 Token (--token)

Add an explicit warning callout and a `--device` example for SSH/headless
environments. Also realign two flagErrorWithSuggestions strings in root.go
that propagated the same misconception ("默认使用设备流" → loopback default,
SSH 用户加 --device).

No behavioural change.

* docs(auth): drop emoji from login help warning
2026-05-09 09:25:57 +08:00
修雨 72fe795f3f docs(changelog): add 1.0.23 release notes (#241)
Backfill 1.0.23 entry covering PR #237 (HTTP_PROXY/HTTPS_PROXY support
restored across the three custom http.Transport instances). Format
follows the 1.0.22 section: narrative + Fixed + Tests.
2026-05-08 20:41:11 +08:00
修雨 0e892c7d75 fix: honour HTTP_PROXY/HTTPS_PROXY in custom HTTP transports (#236) (#237)
The three custom http.Transport instances built by the CLI
(`internal/transport/client.go` MCP transport, `internal/apiclient/client.go`
DingTalk OpenAPI client, `internal/app/legacy.go` IPv4-forcing registry
client) all set DialContext / TLSClientConfig / timeouts but omit the Proxy
field. Per Go's net/http contract, a non-nil Transport without an explicit
Proxy means "no proxy" — env vars are silently ignored, breaking sandboxed
or air-gapped deployments that route outbound through HTTP_PROXY /
HTTPS_PROXY.

Set Proxy: http.ProxyFromEnvironment on all three. Adds a regression test
per package that pointer-compares the Transport's Proxy func against
http.ProxyFromEnvironment (avoids flakiness from Go's envProxyOnce
memoisation when running alongside tests that read proxy env early).
v1.0.23
2026-05-08 16:07:14 +08:00
修雨 8995bf65d6 docs(changelog): backfill 1.0.20 / 1.0.21 / 1.0.22 release notes (#231) 2026-05-07 21:52:09 +08:00
ybc❤️zyrand猷诺 91af2bc3b8 fix(install): add .hermes/skills to AGENT_DIRS (#188) (#221)
* fix(install): add .hermes/skills to AGENT_DIRS (#188)

dws 安装后未自动复制 skill 到 .hermes/skills/。在 4 份硬编码的
AGENT_DIRS 清单(build/npm/install.js、scripts/install.sh、
scripts/install.ps1、scripts/install-skills.sh)末尾追加
.hermes/skills,与现有'父目录守卫'逻辑天然兼容:

- 已安装 Hermes 的用户:自动获得 ~/.hermes/skills/dws
- 未安装 Hermes 的用户:父目录不存在则跳过,零副作用

Closes #188

* fix(install): cover remaining 4 AGENT_DIRS sources for .hermes/skills (review feedback)

Address review feedback from #221: the AGENT_DIRS list actually has 8 sources
in the repo, not 4. Without this commit, dws upgrade users and Homebrew users
would still NOT get .hermes/skills/dws populated even after the previous
4-script fix landed.

Fixes (functional):
- internal/upgrade/paths.go (knownSkillDirs):
    Append .hermes/skills so 'dws upgrade' refreshes ~/.hermes/skills/dws/
    for users who have Hermes installed.
- build/homebrew.rb.tmpl (post_install targets):
    Append .hermes/skills/dws so brew users also get the skill copied to
    ~/.hermes/skills/dws on post_install.

Fixes (test / smoke coverage):
- test/scripts/package_script_test.go (expectedPackagedSkillTargets):
    Append .hermes/skills/dws so future regressions (e.g. someone removes
    .hermes from install.js) are caught by CI.
- scripts/release/verify-package-managers.sh (HOME_AGENT_PARENTS / HOME_SKILL_TARGETS):
    Append .hermes to both lists so the release-time npm/brew smoke test
    actually asserts ~/.hermes/skills/dws/SKILL.md exists.

Documentation:
- internal/upgrade/paths.go: expanded the 'Kept in sync with' comment from
    a single file reference (build/npm/install.js) to all 7 in-sync sources,
    so future maintainers don't have to rediscover this list.

Verification:
- go vet ./internal/upgrade/... ./test/scripts/...   PASS
- go build ./...                                     PASS
- sh -n scripts/install.sh                           PASS
- sh -n scripts/install-skills.sh                    PASS
- sh -n scripts/release/verify-package-managers.sh   PASS
- node --check build/npm/install.js                  PASS

---------

Co-authored-by: 猷诺 <bicheng.ybc@alibaba-inc.com>
v1.0.22
2026-05-07 11:07:27 +08:00
ybc❤️zyrand猷诺 e2e8b3bf52 fix(attendance): summary 命令新增 --stats-type flag,修复 C0002 报错 (#228)
* fix(attendance): add --stats-type flag to summary to fix C0002 error

The MCP tool get_attendance_summary requires statsType in QueryUserAttendVO
at the server-side business layer (DingTalk schema marks it required:[] but
service rejects with C0002 'statistics type error' when omitted).

Changes:
- internal/helpers/attendance.go: add --stats-type flag (week/month);
  conditionally write statsType into QueryUserAttendVO when provided;
  update Long description and flag help to mark --stats-type as required
- test/cli_compat/attendance_test.go: add execSummaryDryRun helper and
  3 new tests verifying statsType is correctly threaded through VO
- skills/references/products/attendance.md: document --stats-type flag
  with warning about server-side mandatory enforcement

Verification:
- go build ./...                                                  PASS
- go vet ./internal/helpers/... ./test/cli_compat/...              PASS
- go test -run TestAttendanceSummary_should_pass_stats_type_*      PASS (3/3)
- go test -run TestAttendanceSummary_should_not_pass_stats_type_*  PASS
- Real CLI: dws attendance summary --stats-type month              success:true
  (C0002 fully gone; full attendance data returned)

Closes #227

* fix(attendance): enforce --stats-type as required at CLI layer (review feedback)

Address review feedback from #228:

1. Fail-fast validation: --stats-type is now required at the CLI layer
   instead of being conditionally written into VO. Previously the doc
   said 'required' but RunE silently let omission through, causing the
   same C0002 the fix was supposed to prevent.

2. Enum validation: only 'week' or 'month' are accepted; any other value
   is rejected at the CLI layer instead of being forwarded to the server.

3. Cosmetic: split the single-line Long description into multiple lines
   for readability.

Test changes:
- Replaced TestAttendanceSummary_should_not_pass_stats_type_when_omitted
  with two semantically-correct tests:
    * TestAttendanceSummary_should_error_when_stats_type_missing
    * TestAttendanceSummary_should_error_when_stats_type_invalid
- Updated other TestAttendanceSummary_* tests to pass --stats-type=month
  to match the new fail-fast contract.
- Renamed should_pass_only_user_flag to should_pass_user_id_through_vo
  (the old name no longer makes sense now that --stats-type is mandatory).

Skill doc:
- skills/references/products/attendance.md: clarified that the CLI now
  rejects missing/invalid --stats-type at the client side (won't even
  reach the server).

Verification:
- go build ./...                                    PASS
- go vet ./internal/helpers/... ./test/cli_compat/  PASS
- go test -run 'TestAttendanceSummary_should_(pass_stats_type|error_when_stats_type)' -v
                                                    PASS (4/4)
- dws attendance summary --help                     new layout shown

---------

Co-authored-by: 猷诺 <bicheng.ybc@alibaba-inc.com>
2026-05-07 10:50:24 +08:00
ybc❤️zyrand猷诺 a652b90fd4 fix: resolve cross-product tool routing collision (issue #219) (#220)
When two MCP servers register tools with the same name (e.g. both drive
and doc have 'create_folder'), the tool-level endpoint map uses
last-writer-wins, causing invocations to route to the wrong server.

Fix: swap Priority 1 (product-level) and Priority 2 (tool-level) in
directRuntimeEndpoint so that when the caller already knows the
productID, the product endpoint is authoritative. Tool-level lookup
remains as a fallback for cases where productID is empty.

This fixes 'dws drive mkdir' and 'dws drive download' being silently
routed to the doc MCP server instead of the drive MCP server.

Closes #219

Co-authored-by: 猷诺 <bicheng.ybc@alibaba-inc.com>
v1.0.21
2026-05-05 16:21:51 +08:00
修雨 7a868ddf39 docs(help): document --contents.key=field_name and mark required flags (#106 #107) (#217)
- chat message send-by-bot --robot-code/--title/--text: append (必填) marker
  (RunE already enforces these as required; help text was missing the tag,
  causing downstream MCP wrappers to generate schemas with required fields
  missing).
- report create --contents: description appended "key must exactly equal the
  template field_name (look it up via report template detail --name <template>)";
  Long now warns about the API's SYSTEM_ERROR on key mismatch; Examples
  rewritten as a two-step pipeline (template detail → create).

Closes #106
Closes #107
v1.0.20
2026-05-04 17:57:23 +08:00
ybc❤️zyrand猷诺 89d7c5f11b docs: clarify message list pagination to prevent nextCursor misuse (fixes #195) (#218)
* docs: clarify message list pagination to prevent nextCursor misuse (fixes #195)

- Add detailed pagination walkthrough section for 'message list' in chat.md
- Add comparison table distinguishing 'message list' (--time with createTime)
  from 'message list-all' (--cursor with nextCursor)
- Add warning about common error: using nextCursor as --time value
  causes infinite loop returning same page

* docs: fix --time required label and add --forward misuse warning (fixes #195)

---------

Co-authored-by: 猷诺 <bicheng.ybc@alibaba-inc.com>
2026-05-04 16:38:09 +08:00
ybc❤️zyrand猷诺 efb61cae02 fix: restore unconditional MCP re-fetch in login to prevent stale clientId errors (#213)
PR #184 merge conflict resolution inadvertently introduced a conditional
branch that preserved stale clientID from previous login sessions instead
of always re-fetching from MCP server. This caused exchangeCode() to use
direct DingTalk API mode (which requires clientSecret) instead of MCP
proxy mode, resulting in 'clientId或者clientSecret错误' errors.

This commit restores the original PR #157 logic: both DeviceFlowProvider
and OAuthProvider unconditionally call resetCredentialState() followed by
FetchClientIDFromMCP() + SetClientIDFromMCP(), ensuring exchangeCode()
always uses the MCP proxy path regardless of prior login state.

Root cause: the conditional branch treated any non-empty clientID (from
runtimeClientID or app.json) as a user-provided --client-id flag value,
skipping MCP re-fetch and leaving clientIDFromMCP=false after reset.

Affected scenarios:
- OAuth login → device flow login
- Any login → --force login
- New terminal with existing app.json → device flow login

Fixes regression introduced in PR #184 (commit 46192d6).
Restores fix from PR #157 (commit ad33a46).

Co-authored-by: 猷诺 <bicheng.ybc@alibaba-inc.com>
2026-05-04 09:26:25 +08:00
ybc❤️zyrand猷诺 fb88c6ace9 docs(todo): 增强待办命令帮助文本,补充字段语义说明 (#205)
基于实际 API 返回验证,所有描述均经过 dws 命令实测确认。

- todo task get: 新增 Long 说明
  · creatorId/executorIds/participantIds/modifierId 是待办内部短数字 ID
    (如 6380165826),非通讯录 userId (如 035551044606950179),已实测确认
  · 提示 creatorInfo/executorInfos/participantInfos 包含 name 属性可获取姓名
  · bizTag/source 返回 teambition 是引擎实现标识,已实测确认
  · tenantId 非 corpId,补充 tenantType 维度说明
- todo task list: 新增 Long 说明
  · 覆盖范围: 仅返回执行者维度待办,不含仅参与/仅创建的待办
  · 仅限个人待办,不含 OA 审批流/Teambition 项目任务
  · 自动分页说明
- todo task create: --executors flag 描述增强
  · 明确此处 userId 是通讯录 userId 并提示查询方式

Fixes #105

Co-authored-by: 猷诺 <bicheng.ybc@alibaba-inc.com>
2026-05-04 09:26:12 +08:00
ybc❤️zyrand猷诺 5258959a14 docs: update contact search examples from --keyword to --query (#209)
The --keyword flag was renamed to --query for contact user search,
contact dept search, and devdoc article search (as noted in CHANGELOG).
This commit updates all documentation references for the contact
commands. devdoc will be addressed in a follow-up PR.

Refs #105

Co-authored-by: 猷诺 <bicheng.ybc@alibaba-inc.com>
2026-04-30 16:32:34 +08:00
xianfeng wangandgithub-actions[bot] c515fc1001 1.0.19 CHANGELOG (#204)
* chore: update coverage badge [skip ci]

* chore: update coverage badge [skip ci]

* chore: update coverage badge [skip ci]

* chore: update coverage badge [skip ci]

* chore: update coverage badge [skip ci]

* 1.0.19 changelog

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-04-30 14:36:58 +08:00
xianfeng wangandgithub-actions[bot] 7692048cf4 fix(discovery): surface edition supplement/fallback servers in runtime catalog loader (#197)
* chore: update coverage badge [skip ci]

* chore: update coverage badge [skip ci]

* chore: update coverage badge [skip ci]

* fix conference discovery

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
v1.0.19
2026-04-30 09:44:25 +08:00
ybc❤️zyrand猷诺 426810a776 fix(todo): correct tool name from query_todo_detail to get_todo_detail (#202)
The todo task get command was calling query_todo_detail which returns
empty results. Changed to get_todo_detail to match the tool name
defined in discovery.json, restoring correct behavior.

Co-authored-by: 猷诺 <bicheng.ybc@alibaba-inc.com>
2026-04-29 22:59:37 +08:00
xuanandshangguanxuan.sgx f253841cf7 docs(changelog): note PAT core changes (#193)
* docs(changelog): note PAT core changes

* docs(changelog): rewrite v1.0.18 release notes

---------

Co-authored-by: shangguanxuan.sgx <shangguanxuan.sgx@alibaba-inc.com>
2026-04-28 23:00:48 +08:00
xuanandshangguanxuan.sgx da2522c8ca feat(pat)!: A-core loop for host-owned PAT (#142)
* feat(pat): A-core minimum viable loop for host-owned PAT

Enables third-party agents to integrate PAT via the smallest possible
contract surface. The flow a host needs to ship end-to-end:

  1. Export DINGTALK_DWS_AGENTCODE in the spawned shell.
  2. Run any `dws ...` business command.
  3. On PAT hit, CLI exits with code 4 and writes a single-line stderr
     JSON following docs/pat/contract.md §2.
  4. Host parses the JSON and invokes `dws pat chmod <scope>...
     --agentCode ... --grant-type ...` to grant.
  5. Host replays the original command.

This PR intentionally ships ONLY the chmod path plus the host-owned
switch; the async flow (apply / status / scopes + authRequestId
registry + PAT_SCOPE_AUTH_REQUIRED active-request branch) lands in a
follow-up stacked PR (A-ext).

Scope of A-core:
- Host-owned PAT trigger: auth.HostOwnsPATFlow() keyed exclusively on
  DINGTALK_DWS_AGENTCODE (DINGTALK_AGENT / DWS_CHANNEL / claw-type do
  NOT participate).
- claw-type: hard-wired to "openClaw" in pkg/edition/default.go
  MergeHeaders hook, matching historical main behavior and decoupled
  from DINGTALK_AGENT.
- dws pat chmod: factory-built (newChmodCommand) so the PAT subcommand
  tree has no shared package-level caller.
- stderr JSON classifier: internal/errors/pat.go covers PAT_NO_PERMISSION
  / PAT_LOW_RISK / PAT_MEDIUM_RISK / PAT_HIGH_RISK / PAT_SCOPE_*
  selectors and fills data.hostControl for host consumption.
- Env contract: DINGTALK_SESSION_ID / DINGTALK_TRACE_ID /
  DINGTALK_MESSAGE_ID carry HTTP trace headers (Chain B);
  DWS_SESSION_ID (+ REWIND_SESSION_ID alias) is the lone fallback for
  `dws pat chmod --session-id` (Chain A). The two chains are
  independent; aliases do not cross-pollinate.
- Docs: docs/pat/{README,contract,host-integration}.md + refreshed
  docs/architecture.md "PAT Architecture" chapter + docs/reference.md
  PAT section. error-catalog.md folded into contract.md §6.
- Tests: host-owned signal, stderr contract, classifier, chmod factory,
  retry / poll loop for the CLI-owned chmod path.

Compatibility: no breaking changes on main. All non-PAT commands are
untouched.

Follow-up PRs (in order):
- codex/pat-ext  : dws pat apply / status / scopes + AsyncRegistry.
- codex/pat-oss-refactor (aka PR B) : oauth_helpers / secure_store
  refactors split out per code review.

Made-with: Cursor

* fix(pat): unify host-owned stderr contract

* revert: drop PAT docs and changelog from pat core PR

* fix(pat): pin behavior auth endpoint

* fix(pat): recognize legacy tool miss

* fix(pat): fallback on gateway diagnostics

* fix(pat): treat authorization uri as opaque

* fix(pat): accept result envelope in device-flow polling

* feat(pat): add browser policy and poll compatibility

* fix(pat): keep CLI and host PAT contracts separate

* test(pat): isolate opaque uri retry env

* fix(pat): harden PAT result routing

* fix(pat): harden host-owned flow and runtime fallback

* fix(pat): preserve chmod failure on empty grant result

* refactor(pat): drop dead doc anchors, dedupe stderr injection helpers

Address two reviewer findings on top of the PAT-core series.

Fix:
- chmod: resolveSessionIDFromEnv silently selects DWS_SESSION_ID; drop
  the slog.Warn that emitted both raw session ids into stderr /
  ~/.dws/logs.
- Remove every docs/pat/contract.md / error-catalog.md /
  host-integration.md anchor from comments, help, and tests; those
  files were never added by this branch. Comments are now self-contained
  or point to docs/reference.md where applicable.

Refactor:
- internal/errors/pat.go: extract lookupCodeIn so getPATErrorCode and
  getDWSGatewayErrorCode share one traversal; have ClassifyPatAuthCheck
  delegate to getPATErrorCode instead of repeating the enum walk.
- internal/errors/pat.go + internal/app/pat_auth_retry.go: extract
  ApplyHostMutations as the single injection point for data.hostControl
  + data.openBrowser; cleanPATJSON and enrichPATErrorForHostControl now
  share it so the two stderr-JSON write paths cannot drift.
- internal/app/pat_auth_retry.go: drop buildPATScopeHostJSON and
  buildHostControlState dead 1-line wrappers.
- internal/pat/chmod.go: drop unused patApply/Status/Scopes constants;
  derive legacyToolArgs from toolArgs by clone + scopes->scope rename so
  the two payloads stay in lock-step on every other field.

Net 15 files, +176 / -193. Verification:

  env -u DINGTALK_DWS_AGENTCODE go test \
    ./internal/errors ./internal/auth ./internal/pat ./test/unit
  env -u DINGTALK_DWS_AGENTCODE go test ./internal/app \
    -run 'Test(IsPat|ExtractPat|PrintPat|PollPat|HandlePat|RetryWithPat|EnrichPAT|BuildPAT|DirectRuntime|ResolveIdentityHeaders)'
  env -u DINGTALK_DWS_AGENTCODE go test -race \
    ./internal/pat ./internal/errors ./test/unit
  env -u DINGTALK_DWS_AGENTCODE go test -race ./internal/app \
    -run 'Test(HandlePat|RetryWithPat|EnrichPAT|BuildPAT|ResolveIdentityHeaders|DirectRuntime)'
  go vet ./... && go build ./...

All green.

Made-with: Cursor

---------

Co-authored-by: shangguanxuan.sgx <shangguanxuan.sgx@alibaba-inc.com>
v1.0.18
2026-04-28 21:17:23 +08:00
43715e28b0 docs: fix skill reference docs to align with actual CLI commands (#186)
* chore: update coverage badge [skip ci]

* chore: update coverage badge [skip ci]

* docs: fix skill reference docs to align with actual CLI commands

- simple.md: fix OA command names (pending→list-pending, initiated→list-initiated)
- simple.md: remove non-existent devdoc article search-error command
- simple.md: update intent guide and context table to use correct command names
- workbench.md: mark as Draft since workbench commands are not yet available in CLI runtime

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: wxianfeng <wang.fl1429@gmail.com>
Co-authored-by: 猷诺 <bicheng.ybc@alibaba-inc.com>
2026-04-28 11:18:30 +08:00
修雨 d11aa0f030 fix(chat,cmdutil): restore group @-mentions and explicit members list subcommand (#180)
* fix(chat,cmdutil): restore group @-mentions and members list subcommand

Two CLI regressions surfaced after PR #170 promoted hardcoded helpers over
the discovery envelope:

1. issue #177 — `dws chat message send --group ... --at-users ...` failed
   with `unknown flag: --at-users` on v1.0.16. The hardcoded helper that
   replaced the v1.0.15 envelope leaf only declared --group / --user /
   --open-dingtalk-id / --text / --title and silently dropped the envelope's
   --at-users / --at-all / --at-mobiles flags, so any group @-mention call
   bounced at cobra's flag parser before ever reaching the MCP tool.

   Add the three flags back to `newChatMessageSendCommand` and forward
   atUserIds / isAtAll / atMobiles to `send_message_as_user` in the
   `--group` branch; reject the flags loudly outside `--group` to avoid
   silently swallowing user intent in single-chat mode (single-chat tools
   have no @-mention semantics).

2. issue #164 — `dws chat group members --id <openConversationId>` returned
   `unknown flag: --id`. The helper had `members` as a "group with bare
   RunE + --id/--cursor flags" while the envelope publishes `members` as a
   leaf for `get_group_members`; the merge layer's shape-mismatch branch
   treats envelope as authority and silently drops the entire helper
   subtree (list / add / remove / add-bot all become unreachable).

   Restructure helper-side `members` into a pure group container and
   promote list to its own explicit subcommand:

     dws chat group members list --id <openconversation_id>

   And extend `pkg/cmdutil/MergeHardcodedLeaves` so the existing
   OverridePriority annotation also applies to the leaf↔group shape
   mismatch — when the helper group carries strictly higher priority it
   replaces the dynamic leaf, mirroring the leaf↔leaf override semantics
   that PR #170 already established. Without this the helper's `list`
   subcommand stays unreachable and the user-visible bug persists.

Tests:

- `internal/helpers/chat_test.go`: forwards-at-mentions (group + each
  flag variant), rejects-at-mentions-outside-group (single-chat
  validation), members-list-subcommand (structure + execution).
- `internal/app/legacy_test.go`: pickCommands integration test that
  reproduces the #164 shape mismatch end-to-end and asserts the helper
  subtree survives.
- `pkg/cmdutil/leaf_merge_test.go`: positive + boundary (equal priority)
  cases for the new leaf↔group promotion path.

Verified end-to-end against existing chat helper tests, app pickCommands
tests, cmdutil merge tests, cobracmd priority tests, compat dynamic
command tests — all green.

Skill docs (`skills/references/products/chat.md`) updated to teach the
new `members list` path with a migration note, and to surface the
restored `--at-mobiles` flag alongside `--at-all` / `--at-users`.

Closes #177
Closes #164

* fix(chat): resolve merge conflict in command Long text

Combine --title-required wording from #174 with at-mention
documentation introduced in this PR. CI build was failing on
unresolved <<<<<<< / >>>>>>> markers left in chat.go.
2026-04-27 22:56:46 +08:00
fantiu 41396426d0 Merge pull request #184 from fantiu/feat-rawapi
feat(api): add raw DingTalk OpenAPI command with dual-version token support
2026-04-27 21:14:15 +08:00
fantiu 6946929e0b feat(api): add raw DingTalk OpenAPI command with dual-version token support 2026-04-27 20:54:57 +08:00
xuanandshangguanxuan.sgx 6756ead38a fix: preserve PAT authorization uri and accept result poll envelope (#182)
* fix(pat): treat authorization uri as opaque

* fix(pat): accept result envelope in device-flow polling

* docs(pat): clarify empty-uri guard intent

* fix(pat): guard poll debug output and lock envelope priority

---------

Co-authored-by: shangguanxuan.sgx <shangguanxuan.sgx@alibaba-inc.com>
2026-04-27 20:04:01 +08:00
fantiu 4c44a2b632 Merge branch 'main' into feat-rawapi 2026-04-27 20:03:40 +08:00
修雨 c0468754a4 docs(changelog,readme): backfill v1.0.17 with mail product surface (#181)
CHANGELOG v1.0.17:
- Mail (#167) is a runtime product, not just a skill doc — verified by
  building v1.0.17 from source and inspecting `dws --help`. Lists the
  four leaf commands (mailbox list, message search/get/send) and the
  KQL pagination support.
- Plugin overlay-first registration (#179), VisibleProducts union
  refactor (#179), buildStdioCommands shared-helper refactor (#179),
  cache-poisoning guard (#179), --title docs clarification (#174).

README / README_zh:
- Add Mail row to Key Services table (4 commands, mailbox/message
  subgroups).
- Bump total: 159 commands across 13 products → 163 commands across
  14 products.
- Remove `mail` from "Coming soon" (now shipped).
- Drop the hardcoded "159" count from the command-index callout to
  avoid future skew until command-index.md is regenerated.
2026-04-27 17:21:45 +08:00
fantiu e06b1d68ff feat: raw API update README. 2026-04-27 17:08:32 +08:00
修雨 b101d5b12b docs(skills,chat): clarify --title is required for chat message send (#174)
The backend MCP tools `send_direct_message_as_user` and
`send_message_as_user` both require `title` as a required field.
However, the skill documentation and --help Long text described
--title as "optional", causing AI models to omit it and trigger
a vague `business error: success=false`.

Update descriptions in:
- skills/references/products/chat.md
- internal/helpers/chat.go (command Long text)

Closes #173
v1.0.17
2026-04-27 16:49:05 +08:00
xianfeng wangandgithub-actions[bot] acc5dc8a3c feat(plugins): register stdio plugin commands from CLI overlay before discovery (#179)
* chore: update coverage badge [skip ci]

* chore: update coverage badge [skip ci]

* plugin in help

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-04-27 16:20:35 +08:00
fantiu 592749c986 feat: The token retrieval interface is now unified to one. 2026-04-27 15:11:12 +08:00
fantiu 9b6174e03e feat(api): add raw DingTalk OpenAPI command with dual-version token support 2026-04-27 10:29:34 +08:00
github-actions[bot] f73fa08e49 chore: update coverage badge [skip ci] 2026-04-27 01:49:47 +00:00
fantiu bb5e5a40d9 Merge branch 'DingTalk-Real-AI:main' into main 2026-04-27 09:47:46 +08:00
ybc❤️zyrand猷诺 e3b0c9b1ed feat: add mail product reference doc and register in SKILL.md (#167)
- Add skills/references/products/mail.md with 4 subcommands:
  mailbox list, message search (KQL), message get, message send
- Register mail in SKILL.md: product table, intent routing, description
- All params verified against dws schema and --mock execution

Co-authored-by: 猷诺 <bicheng.ybc@alibaba-inc.com>
2026-04-27 09:35:49 +08:00
修雨 cc1f0a47e3 docs(changelog): backfill v1.0.16 (and v1.0.14 docs-only re-tag) (#171)
* docs(changelog): backfill v1.0.16 (and v1.0.14 docs-only re-tag)

v1.0.16 ships discovery service abstraction + schema v3 (#156),
open-edition helper grafting (#169), chat message send destination
routing (#170), and device flow defensive credential reset (#157).
The v1.0.16 GitHub release notes are refreshed in lock-step with
this CHANGELOG entry (previously they were the auto-generated
4-line commit list).

v1.0.14 also gets a one-line entry noting it is a docs-only re-tag
of v1.0.13 (#153 backfilled v1.0.13 release notes after the binary
was already published; no functional change).

* docs(changelog): reorder v1.0.14 to date-descending position

v1.0.14 (2026-04-22) was sitting above v1.0.15 (2026-04-23). Move it between v1.0.15 and v1.0.13 so the file reads strictly date-descending.

* docs(changelog): trim v1.0.16 entry to match prior granularity

Drop nested sub-bullets (discovery internals / schema v3 enumeration / device-flow root cause) and the standalone Tests section; keep one bullet per PR. Aligns with v1.0.4 / v1.0.7 / v1.0.8 single-paragraph + 3-5 bullets style. 47 lines → 7 net (-40).
2026-04-27 09:35:11 +08:00
Ari b0c9160ed5 fix(chat,cmdutil): route chat message send by destination flag (#170)
* fix(app): graft hardcoded helpers into dynamic tree instead of dropping

pickCommands previously dropped an entire helper subtree whenever a
top-level product name collided with the dynamic overlay. The intent was
to keep the discovery envelope as runtime authority for leaves it
declared, but the side effect was that every helper-only sibling
disappeared too — commands like `chat message send-by-bot`,
`chat message recall-by-bot`, `chat message send-by-webhook`, and
`chat group members add-bot` had silently vanished from the open
edition since the pickCommands introduction, making PR #161's
send-by-bot routing fix unreachable for open-source users.

Switch to cmdutil.MergeHardcodedLeaves for same-named products: the
dynamic side still wins every leaf conflict, and helper-only subtrees
are grafted into the dynamic tree. Envelopes remain authoritative for
leaves they declare; helpers once again fill the gaps they don't.

Verified on a freshly built open-edition binary:
  dws chat message send-by-bot --robot-code ... --users ... --text ...
returns a real processQueryKey and routes through canonical_product=bot
via helper_override, matching the v1.0.15 baseline.

* fix(chat,cmdutil): route chat message send by destination flag

`dws chat message send --user <userId>` failed with `不合法的参数` because
the envelope-generated dynamic command maps every destination to the
group-only tool `send_message_as_user`. Only `--group` was viable on the
open edition; single-chat (`--user`, `--open-dingtalk-id`) has never
worked end-to-end.

Fix is in two parts:

1. Add a hardcoded `chat message send` helper that dispatches by
   destination flag — `--group` → `send_message_as_user`, `--user` /
   `--open-dingtalk-id` → `send_direct_message_as_user`. This mirrors the
   closed-source wukong overlay's chatMessageSendCmd so the open edition
   lines up with the rest of the product.

2. Let hardcoded leaves opt into overriding the dynamic envelope in
   `cmdutil.MergeHardcodedLeaves` when they carry a strictly higher
   OverridePriority. The default remains "envelope is authority"; the
   opt-in exists for the narrow case where the envelope exposes a single
   dispatch path but the hardcoded leaf needs richer flag-based routing
   (as here). The new send helper uses preferLegacyLeaf (priority 100)
   so it wins against the dynamic leaf.

OverridePriority/SetOverridePriority move to pkg/cmdutil as the new
source of truth; internal/cobracmd/priority.go now delegates so the
annotation key stays single-sourced across the merge layer and helpers.

Verified end-to-end against a freshly built open-edition binary:
  send --user 034766 --text …  → send_direct_message_as_user → success
  send --group <cid> --text …  → send_message_as_user          → success
v1.0.16
2026-04-24 20:41:30 +08:00
Ari 0b544a86b7 fix(app): graft hardcoded helpers into dynamic tree instead of dropping (#169)
pickCommands previously dropped an entire helper subtree whenever a
top-level product name collided with the dynamic overlay. The intent was
to keep the discovery envelope as runtime authority for leaves it
declared, but the side effect was that every helper-only sibling
disappeared too — commands like `chat message send-by-bot`,
`chat message recall-by-bot`, `chat message send-by-webhook`, and
`chat group members add-bot` had silently vanished from the open
edition since the pickCommands introduction, making PR #161's
send-by-bot routing fix unreachable for open-source users.

Switch to cmdutil.MergeHardcodedLeaves for same-named products: the
dynamic side still wins every leaf conflict, and helper-only subtrees
are grafted into the dynamic tree. Envelopes remain authoritative for
leaves they declare; helpers once again fill the gaps they don't.

Verified on a freshly built open-edition binary:
  dws chat message send-by-bot --robot-code ... --users ... --text ...
returns a real processQueryKey and routes through canonical_product=bot
via helper_override, matching the v1.0.15 baseline.
2026-04-24 20:32:05 +08:00
fantiu d71031d097 feat(api): add raw DingTalk OpenAPI command 2026-04-24 17:14:10 +08:00
fantiu 46192d68ba feat(api):client logic fixed. 2026-04-24 17:04:09 +08:00