sync wukong hardcoded command additions
sync wukong hardcoded command additions Co-authored-by: 修雨 <47820304+PeterGuy326@users.noreply.github.com>
This commit is contained in:
61 files changed
+3954
-83
No files matched your search
@@ -71,9 +71,9 @@ The installer ships skills in one of two layouts. CLI commands (`dws aitable ...
|
|||||||
| Mode | What gets installed | Best for |
|
| Mode | What gets installed | Best for |
|
||||||
|------|----------------------|----------|
|
|------|----------------------|----------|
|
||||||
| **mono** (stable, default) | One `dws` skill covering all products | Cross-product workflows; single entry point |
|
| **mono** (stable, default) | One `dws` skill covering all products | Cross-product workflows; single entry point |
|
||||||
| **multi** 🧪 **EXPERIMENTAL** | 18 per-product skills (`dingtalk-aitable`, `dingtalk-calendar`, `dingtalk-chat`, ...) | Single-product tasks; smaller context per call |
|
| **multi** 🧪 **EXPERIMENTAL** | 22 per-product skills (`dingtalk-aitable`, `dingtalk-calendar`, `dingtalk-chat`, ...) | Single-product tasks; smaller context per call |
|
||||||
|
|
||||||
> 🧪 **`multi` is currently EXPERIMENTAL / preview.** 18 product-scoped skills all pass the dispatch verifier, but interface, naming and cross-skill references may change in future releases. For production / shared environments, prefer `mono`. File issues if you hit problems.
|
> 🧪 **`multi` is currently EXPERIMENTAL / preview.** 22 product-scoped skills all pass the dispatch verifier, but interface, naming and cross-skill references may change in future releases. For production / shared environments, prefer `mono`. File issues if you hit problems.
|
||||||
|
|
||||||
How to pick:
|
How to pick:
|
||||||
|
|
||||||
@@ -331,7 +331,7 @@ dws aitable record query --base-id BASE_ID --table-id TABLE_ID --limit 10
|
|||||||
The repo ships a complete Agent Skill system under `skills/`, now organized into two layouts:
|
The repo ships a complete Agent Skill system under `skills/`, now organized into two layouts:
|
||||||
|
|
||||||
- `skills/mono/` — single-skill layout (one `SKILL.md` + `references/products/`), recommended default.
|
- `skills/mono/` — single-skill layout (one `SKILL.md` + `references/products/`), recommended default.
|
||||||
- `skills/multi/` — per-product skills (`dingtalk-aitable/`, `dingtalk-calendar/`, `dingtalk-chat/`, ... 20 products in total), each with its own `SKILL.md`. 🧪 **EXPERIMENTAL / preview — see banner in each multi `SKILL.md` for caveats.**
|
- `skills/multi/` — per-product skills (`dingtalk-aitable/`, `dingtalk-calendar/`, `dingtalk-chat/`, ... 22 products in total), each with its own `SKILL.md`. 🧪 **EXPERIMENTAL / preview — see banner in each multi `SKILL.md` for caveats.**
|
||||||
|
|
||||||
After installing, AI tools like Claude Code / Cursor can operate DingTalk directly through natural language:
|
After installing, AI tools like Claude Code / Cursor can operate DingTalk directly through natural language:
|
||||||
|
|
||||||
|
|||||||
+3
-3
@@ -71,9 +71,9 @@ irm https://raw.githubusercontent.com/DingTalk-Real-AI/dingtalk-workspace-cli/ma
|
|||||||
| 模式 | 安装内容 | 适合场景 |
|
| 模式 | 安装内容 | 适合场景 |
|
||||||
|------|----------|----------|
|
|------|----------|----------|
|
||||||
| **mono**(稳定,默认) | 一个 `dws` skill,覆盖全部产品 | 跨产品组合操作;单一入口召唤 |
|
| **mono**(稳定,默认) | 一个 `dws` skill,覆盖全部产品 | 跨产品组合操作;单一入口召唤 |
|
||||||
| **multi** 🧪 **试验版 / Preview** | 20 个独立产品 skill(`dingtalk-aitable` / `dingtalk-calendar` / `dingtalk-chat` ...) | 单产品任务;每次召唤上下文更小 |
|
| **multi** 🧪 **试验版 / Preview** | 22 个独立产品 skill(`dingtalk-aitable` / `dingtalk-calendar` / `dingtalk-chat` ...) | 单产品任务;每次召唤上下文更小 |
|
||||||
|
|
||||||
> 🧪 **multi 模式当前为 EXPERIMENTAL(试验版 / Preview)**。20 个独立 skill 全部通过 dispatch verifier,但接口、命名、跨 skill 引用后续可能调整。生产 / 共享环境建议优先用 `mono`。问题请提 issue 反馈。
|
> 🧪 **multi 模式当前为 EXPERIMENTAL(试验版 / Preview)**。22 个独立 skill 全部通过 dispatch verifier,但接口、命名、跨 skill 引用后续可能调整。生产 / 共享环境建议优先用 `mono`。问题请提 issue 反馈。
|
||||||
|
|
||||||
怎么选:
|
怎么选:
|
||||||
|
|
||||||
@@ -328,7 +328,7 @@ dws aitable record query --base-id BASE_ID --table-id TABLE_ID --limit 10
|
|||||||
仓库内置完整的 Agent Skill 体系(`skills/` 目录),目前重组为两套布局:
|
仓库内置完整的 Agent Skill 体系(`skills/` 目录),目前重组为两套布局:
|
||||||
|
|
||||||
- `skills/mono/` — 单 skill 布局(一个 `SKILL.md` + `references/products/`),默认推荐。
|
- `skills/mono/` — 单 skill 布局(一个 `SKILL.md` + `references/products/`),默认推荐。
|
||||||
- `skills/multi/` — 每个产品一个独立 skill(`dingtalk-aitable/` / `dingtalk-calendar/` / `dingtalk-chat/` ... 共 18 个),每个 skill 自带 `SKILL.md`。🧪 **试验版 / Preview — 各 multi `SKILL.md` 头部有详细注意事项。**
|
- `skills/multi/` — 每个产品一个独立 skill(`dingtalk-aitable/` / `dingtalk-calendar/` / `dingtalk-chat/` ... 共 22 个),每个 skill 自带 `SKILL.md`。🧪 **试验版 / Preview — 各 multi `SKILL.md` 头部有详细注意事项。**
|
||||||
|
|
||||||
安装之后,Claude Code / Cursor 等 AI 工具就能通过自然语言直接操作钉钉:
|
安装之后,Claude Code / Cursor 等 AI 工具就能通过自然语言直接操作钉钉:
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,16 @@
|
|||||||
|
# cli_to_mcp smoke tests
|
||||||
|
|
||||||
|
This directory contains lightweight command-to-tool contract tests for hardcoded
|
||||||
|
DWS commands synced from `dws-wukong`.
|
||||||
|
|
||||||
|
The tests do not call live DingTalk APIs. They exercise command help, validation,
|
||||||
|
and `--dry-run` output so command paths and MCP argument mappings stay stable.
|
||||||
|
|
||||||
|
Run with an already built binary:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
DWS_BIN=/path/to/dws pytest auto-test/cli_to_mcp/testcases
|
||||||
|
```
|
||||||
|
|
||||||
|
If `DWS_BIN` is not set, the runner falls back to `go run ./cmd` from the repo
|
||||||
|
root.
|
||||||
@@ -0,0 +1,193 @@
|
|||||||
|
from test_utils import combined_output, dry_run_args
|
||||||
|
|
||||||
|
|
||||||
|
def assert_ok(result):
|
||||||
|
output = combined_output(result)
|
||||||
|
assert result.returncode == 0, output
|
||||||
|
return output
|
||||||
|
|
||||||
|
|
||||||
|
def test_agoal_strategy_and_contract_cli_to_mcp(dws):
|
||||||
|
output = assert_ok(
|
||||||
|
dws.run_raw(
|
||||||
|
"agoal",
|
||||||
|
"strategy",
|
||||||
|
"list",
|
||||||
|
"--scope-type",
|
||||||
|
"PERSONAL",
|
||||||
|
"--scope-id",
|
||||||
|
"user123",
|
||||||
|
"--request-id",
|
||||||
|
"req-1",
|
||||||
|
"--dry-run",
|
||||||
|
)
|
||||||
|
)
|
||||||
|
assert "list_strategy_decodings" in output
|
||||||
|
assert dry_run_args(output) == {
|
||||||
|
"scopeType": "PERSONAL",
|
||||||
|
"openId": "user123",
|
||||||
|
"requestId": "req-1",
|
||||||
|
}
|
||||||
|
|
||||||
|
output = assert_ok(
|
||||||
|
dws.run_raw(
|
||||||
|
"agoal",
|
||||||
|
"strategy",
|
||||||
|
"update",
|
||||||
|
"--profile-id",
|
||||||
|
"profile123",
|
||||||
|
"--content",
|
||||||
|
'[{"id":"e1","title":{"title":"new"}}]',
|
||||||
|
"--dry-run",
|
||||||
|
)
|
||||||
|
)
|
||||||
|
assert "update_strategy_decoding" in output
|
||||||
|
assert dry_run_args(output) == {
|
||||||
|
"profileId": "profile123",
|
||||||
|
"content": [{"id": "e1", "title": {"title": "new"}}],
|
||||||
|
}
|
||||||
|
|
||||||
|
output = assert_ok(dws.run_raw("agoal", "contract", "fields", "--dry-run"))
|
||||||
|
assert "list_op_contract_fields" in output
|
||||||
|
assert dry_run_args(output) == {}
|
||||||
|
|
||||||
|
output = assert_ok(
|
||||||
|
dws.run_raw(
|
||||||
|
"agoal",
|
||||||
|
"contract",
|
||||||
|
"update",
|
||||||
|
"--contract-id",
|
||||||
|
"contract123",
|
||||||
|
"--dimensions",
|
||||||
|
'[{"id":"dim1","title":"metric"}]',
|
||||||
|
"--audit-config",
|
||||||
|
'{"needAudit":true}',
|
||||||
|
"--objective-template",
|
||||||
|
'{"id":"tpl1"}',
|
||||||
|
"--dry-run",
|
||||||
|
)
|
||||||
|
)
|
||||||
|
assert "update_op_contract" in output
|
||||||
|
assert dry_run_args(output) == {
|
||||||
|
"contractId": "contract123",
|
||||||
|
"dimensions": [{"id": "dim1", "title": "metric"}],
|
||||||
|
"auditConfig": '{"needAudit":true}',
|
||||||
|
"objectiveTemplate": '{"id":"tpl1"}',
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def test_agoal_scorecard_user_report_template_cli_to_mcp(dws):
|
||||||
|
output = assert_ok(
|
||||||
|
dws.run_raw(
|
||||||
|
"agoal",
|
||||||
|
"scorecard",
|
||||||
|
"detail",
|
||||||
|
"--selected-time",
|
||||||
|
"2026-01-01T00:00:00+08:00",
|
||||||
|
"--dept-id",
|
||||||
|
"dept123",
|
||||||
|
"--dry-run",
|
||||||
|
)
|
||||||
|
)
|
||||||
|
assert "get_score_card_detail" in output
|
||||||
|
args = dry_run_args(output)
|
||||||
|
assert args["deptId"] == "dept123"
|
||||||
|
assert args["selectedTime"] == 1767196800000
|
||||||
|
|
||||||
|
output = assert_ok(
|
||||||
|
dws.run_raw(
|
||||||
|
"agoal",
|
||||||
|
"scorecard",
|
||||||
|
"update",
|
||||||
|
"--dept-id",
|
||||||
|
"dept123",
|
||||||
|
"--selected-time",
|
||||||
|
"2026-01-01",
|
||||||
|
"--id",
|
||||||
|
"sc123",
|
||||||
|
"--tracking-period-type",
|
||||||
|
"MONTHLY",
|
||||||
|
"--content",
|
||||||
|
'[{"id":"dim1","items":[]}]',
|
||||||
|
"--dry-run",
|
||||||
|
)
|
||||||
|
)
|
||||||
|
assert "update_score_card" in output
|
||||||
|
args = dry_run_args(output)
|
||||||
|
assert args["selectedTime"] == 1767196800000
|
||||||
|
assert args["content"] == [{"id": "dim1", "items": []}]
|
||||||
|
|
||||||
|
output = assert_ok(
|
||||||
|
dws.run_raw(
|
||||||
|
"agoal",
|
||||||
|
"user",
|
||||||
|
"objectives",
|
||||||
|
"--user-id",
|
||||||
|
"user123",
|
||||||
|
"--rule-id",
|
||||||
|
"rule123",
|
||||||
|
"--period-ids",
|
||||||
|
"p1,p2",
|
||||||
|
"--dry-run",
|
||||||
|
)
|
||||||
|
)
|
||||||
|
assert "list_user_objectives" in output
|
||||||
|
assert dry_run_args(output) == {
|
||||||
|
"dingUserId": "user123",
|
||||||
|
"objectiveRuleId": "rule123",
|
||||||
|
"periodIds": ["p1", "p2"],
|
||||||
|
}
|
||||||
|
|
||||||
|
output = assert_ok(
|
||||||
|
dws.run_raw(
|
||||||
|
"agoal",
|
||||||
|
"report",
|
||||||
|
"submit-detail",
|
||||||
|
"--template-id",
|
||||||
|
"tpl123",
|
||||||
|
"--submit-state",
|
||||||
|
"LATE",
|
||||||
|
"--query-date",
|
||||||
|
"2026-06-18T00:00:00+08:00",
|
||||||
|
"--page",
|
||||||
|
"1",
|
||||||
|
"--page-size",
|
||||||
|
"20",
|
||||||
|
"--keyword",
|
||||||
|
"alice",
|
||||||
|
"--dry-run",
|
||||||
|
)
|
||||||
|
)
|
||||||
|
assert "get_submit_detail" in output
|
||||||
|
assert dry_run_args(output) == {
|
||||||
|
"templateId": "tpl123",
|
||||||
|
"submitState": "LATE",
|
||||||
|
"queryDate": "2026-06-18",
|
||||||
|
"page": 1,
|
||||||
|
"pageSize": 20,
|
||||||
|
"keyword": "alice",
|
||||||
|
}
|
||||||
|
|
||||||
|
output = assert_ok(
|
||||||
|
dws.run_raw(
|
||||||
|
"agoal",
|
||||||
|
"obj-template",
|
||||||
|
"create-or-update",
|
||||||
|
"--title",
|
||||||
|
"tpl",
|
||||||
|
"--dimensions",
|
||||||
|
'[{"title":"dim"}]',
|
||||||
|
"--objective-weight",
|
||||||
|
"--dimension-weight",
|
||||||
|
"--compute-by-weight",
|
||||||
|
"--dry-run",
|
||||||
|
)
|
||||||
|
)
|
||||||
|
assert "create_or_update_obj_template" in output
|
||||||
|
assert dry_run_args(output) == {
|
||||||
|
"title": "tpl",
|
||||||
|
"dimensions": '[{"title":"dim"}]',
|
||||||
|
"objectiveWeight": True,
|
||||||
|
"dimensionWeight": True,
|
||||||
|
"computeByWeight": True,
|
||||||
|
}
|
||||||
@@ -0,0 +1,163 @@
|
|||||||
|
from test_utils import combined_output, dry_run_args
|
||||||
|
|
||||||
|
|
||||||
|
def assert_ok(result):
|
||||||
|
output = combined_output(result)
|
||||||
|
assert result.returncode == 0, output
|
||||||
|
return output
|
||||||
|
|
||||||
|
|
||||||
|
def test_group_notice_cli_to_mcp(dws):
|
||||||
|
output = assert_ok(
|
||||||
|
dws.run_raw(
|
||||||
|
"chat",
|
||||||
|
"group",
|
||||||
|
"notice",
|
||||||
|
"create",
|
||||||
|
"--group",
|
||||||
|
"cid123",
|
||||||
|
"--content",
|
||||||
|
"maintenance tonight",
|
||||||
|
"--sticky",
|
||||||
|
"--send-ding",
|
||||||
|
"--dry-run",
|
||||||
|
)
|
||||||
|
)
|
||||||
|
assert "create_group_notice" in output
|
||||||
|
assert dry_run_args(output) == {
|
||||||
|
"openConversationId": "cid123",
|
||||||
|
"content": "maintenance tonight",
|
||||||
|
"sticky": True,
|
||||||
|
"sendDing": True,
|
||||||
|
}
|
||||||
|
|
||||||
|
output = assert_ok(
|
||||||
|
dws.run_raw(
|
||||||
|
"chat",
|
||||||
|
"group",
|
||||||
|
"notice",
|
||||||
|
"edit",
|
||||||
|
"--group",
|
||||||
|
"cid123",
|
||||||
|
"--notice-id",
|
||||||
|
"notice123",
|
||||||
|
"--content",
|
||||||
|
"updated",
|
||||||
|
"--dry-run",
|
||||||
|
)
|
||||||
|
)
|
||||||
|
assert "edit_group_notice" in output
|
||||||
|
assert dry_run_args(output) == {
|
||||||
|
"openConversationId": "cid123",
|
||||||
|
"dataId": "notice123",
|
||||||
|
"content": "updated",
|
||||||
|
}
|
||||||
|
|
||||||
|
output = assert_ok(
|
||||||
|
dws.run_raw(
|
||||||
|
"chat",
|
||||||
|
"group",
|
||||||
|
"notice",
|
||||||
|
"get",
|
||||||
|
"--group",
|
||||||
|
"cid123",
|
||||||
|
"--notice-id",
|
||||||
|
"notice123",
|
||||||
|
"--dry-run",
|
||||||
|
)
|
||||||
|
)
|
||||||
|
assert "get_group_notice" in output
|
||||||
|
assert dry_run_args(output) == {
|
||||||
|
"openConversationId": "cid123",
|
||||||
|
"dataId": "notice123",
|
||||||
|
}
|
||||||
|
|
||||||
|
output = assert_ok(
|
||||||
|
dws.run_raw(
|
||||||
|
"chat",
|
||||||
|
"group",
|
||||||
|
"notice",
|
||||||
|
"list",
|
||||||
|
"--group",
|
||||||
|
"cid123",
|
||||||
|
"--limit",
|
||||||
|
"20",
|
||||||
|
"--cursor",
|
||||||
|
"next",
|
||||||
|
"--scheduled",
|
||||||
|
"--dry-run",
|
||||||
|
)
|
||||||
|
)
|
||||||
|
assert "list_group_notices" in output
|
||||||
|
assert dry_run_args(output) == {
|
||||||
|
"openConversationId": "cid123",
|
||||||
|
"limit": 20,
|
||||||
|
"cursor": "next",
|
||||||
|
"scheduled": True,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def test_chat_misc_new_commands_cli_to_mcp(dws):
|
||||||
|
output = assert_ok(
|
||||||
|
dws.run_raw(
|
||||||
|
"chat",
|
||||||
|
"group",
|
||||||
|
"share-invite",
|
||||||
|
"--source",
|
||||||
|
"sourceCid",
|
||||||
|
"--target",
|
||||||
|
"targetCid",
|
||||||
|
"--expires-seconds",
|
||||||
|
"3600",
|
||||||
|
"--uuid",
|
||||||
|
"uuid-1",
|
||||||
|
"--dry-run",
|
||||||
|
)
|
||||||
|
)
|
||||||
|
assert "share_group_invite_url" in output
|
||||||
|
assert dry_run_args(output) == {
|
||||||
|
"sourceOpenConversationId": "sourceCid",
|
||||||
|
"targetOpenConversationId": "targetCid",
|
||||||
|
"expiresSeconds": 3600,
|
||||||
|
"uuid": "uuid-1",
|
||||||
|
}
|
||||||
|
|
||||||
|
output = assert_ok(
|
||||||
|
dws.run_raw("chat", "text", "translate", "--query", "hello", "--to", "zh_CN", "--dry-run")
|
||||||
|
)
|
||||||
|
assert "translate" in output
|
||||||
|
assert dry_run_args(output) == {"query": "hello", "to": "zh_CN"}
|
||||||
|
|
||||||
|
output = assert_ok(
|
||||||
|
dws.run_raw(
|
||||||
|
"chat",
|
||||||
|
"category",
|
||||||
|
"create-smart",
|
||||||
|
"--name",
|
||||||
|
"priority",
|
||||||
|
"--keywords",
|
||||||
|
"alpha,beta",
|
||||||
|
"--members",
|
||||||
|
"uid1,uid2",
|
||||||
|
"--dry-run",
|
||||||
|
)
|
||||||
|
)
|
||||||
|
assert "create_smart_conv_category" in output
|
||||||
|
assert dry_run_args(output) == {
|
||||||
|
"title": "priority",
|
||||||
|
"keywords": ["alpha", "beta"],
|
||||||
|
"memberOpenDingTalkIds": ["uid1", "uid2"],
|
||||||
|
}
|
||||||
|
|
||||||
|
output = assert_ok(
|
||||||
|
dws.run_raw(
|
||||||
|
"chat",
|
||||||
|
"message",
|
||||||
|
"list-emotion-replies",
|
||||||
|
"--msg-ids",
|
||||||
|
"msg1,msg2",
|
||||||
|
"--dry-run",
|
||||||
|
)
|
||||||
|
)
|
||||||
|
assert "list_message_emotion_replies" in output
|
||||||
|
assert dry_run_args(output) == {"openMessageIds": ["msg1", "msg2"]}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
import pytest
|
||||||
|
|
||||||
|
from test_utils import DWSRunner
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture(scope="session")
|
||||||
|
def dws():
|
||||||
|
return DWSRunner()
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
from test_utils import combined_output
|
||||||
|
|
||||||
|
|
||||||
|
def assert_ok(result):
|
||||||
|
output = combined_output(result)
|
||||||
|
assert result.returncode == 0, output
|
||||||
|
return output
|
||||||
|
|
||||||
|
|
||||||
|
def test_doc_import_help_and_validation(dws, tmp_path):
|
||||||
|
output = assert_ok(dws.run_raw("doc", "import", "--help"))
|
||||||
|
assert "dws doc import" in output
|
||||||
|
assert "--file string" in output
|
||||||
|
assert "--workspace string" in output
|
||||||
|
assert "--name string" in output
|
||||||
|
|
||||||
|
result = dws.run_raw("doc", "import", "--file", str(tmp_path / "missing.md"), "--dry-run")
|
||||||
|
output = combined_output(result)
|
||||||
|
assert result.returncode != 0
|
||||||
|
assert "cannot read file" in output
|
||||||
|
|
||||||
|
bad = tmp_path / "bad.exe"
|
||||||
|
bad.write_text("bad", encoding="utf-8")
|
||||||
|
result = dws.run_raw("doc", "import", "--file", str(bad), "--dry-run")
|
||||||
|
output = combined_output(result)
|
||||||
|
assert result.returncode != 0
|
||||||
|
assert "unsupported file format" in output
|
||||||
|
|
||||||
|
|
||||||
|
def test_doc_import_dry_run(dws, tmp_path):
|
||||||
|
source = tmp_path / "sample.md"
|
||||||
|
source.write_text("# Sample\n\nhello\n", encoding="utf-8")
|
||||||
|
|
||||||
|
output = assert_ok(
|
||||||
|
dws.run_raw(
|
||||||
|
"doc",
|
||||||
|
"import",
|
||||||
|
"--file",
|
||||||
|
str(source),
|
||||||
|
"--name",
|
||||||
|
"Imported Sample",
|
||||||
|
"--workspace",
|
||||||
|
"workspace123",
|
||||||
|
"--dry-run",
|
||||||
|
)
|
||||||
|
)
|
||||||
|
assert "Imported Sample" in output
|
||||||
|
assert "sample.md" in output
|
||||||
|
assert "md" in output
|
||||||
|
|
||||||
|
|
||||||
|
def test_doc_import_get_dry_run(dws):
|
||||||
|
output = assert_ok(dws.run_raw("doc", "import", "get", "--task-id", "task123", "--dry-run"))
|
||||||
|
assert "task123" in output
|
||||||
@@ -0,0 +1,143 @@
|
|||||||
|
import os
|
||||||
|
|
||||||
|
from test_utils import combined_output, dry_run_args
|
||||||
|
|
||||||
|
|
||||||
|
def mail_email() -> str:
|
||||||
|
return os.environ.get("DINGTALK_MAIL_EMAIL", "user@example.com")
|
||||||
|
|
||||||
|
|
||||||
|
def assert_ok(result):
|
||||||
|
output = combined_output(result)
|
||||||
|
assert result.returncode == 0, output
|
||||||
|
return output
|
||||||
|
|
||||||
|
|
||||||
|
def assert_fails(result, expected: str):
|
||||||
|
output = combined_output(result)
|
||||||
|
assert result.returncode != 0, output
|
||||||
|
assert expected in output
|
||||||
|
|
||||||
|
|
||||||
|
def test_mailbox_profile_cli_to_mcp(dws):
|
||||||
|
output = assert_ok(dws.run_raw("mail", "mailbox", "profile", "--help"))
|
||||||
|
assert "dws mail mailbox profile" in output
|
||||||
|
assert "--email string" in output
|
||||||
|
|
||||||
|
assert_fails(dws.run_raw("mail", "mailbox", "profile"), "email")
|
||||||
|
|
||||||
|
output = assert_ok(
|
||||||
|
dws.run_raw("mail", "mailbox", "profile", "--email", mail_email(), "--dry-run")
|
||||||
|
)
|
||||||
|
assert "get_mailbox_profile" in output
|
||||||
|
assert dry_run_args(output) == {"email": mail_email()}
|
||||||
|
|
||||||
|
|
||||||
|
def test_message_batch_get_cli_to_mcp(dws):
|
||||||
|
output = assert_ok(dws.run_raw("mail", "message", "batch-get", "--help"))
|
||||||
|
assert "dws mail message batch-get" in output
|
||||||
|
assert "--email string" in output
|
||||||
|
assert "--ids string" in output
|
||||||
|
|
||||||
|
assert_fails(
|
||||||
|
dws.run_raw("mail", "message", "batch-get", "--email", mail_email()),
|
||||||
|
"ids",
|
||||||
|
)
|
||||||
|
|
||||||
|
too_many_ids = ",".join(f"msg_{i:02d}" for i in range(21))
|
||||||
|
assert_fails(
|
||||||
|
dws.run_raw(
|
||||||
|
"mail",
|
||||||
|
"message",
|
||||||
|
"batch-get",
|
||||||
|
"--email",
|
||||||
|
mail_email(),
|
||||||
|
"--ids",
|
||||||
|
too_many_ids,
|
||||||
|
"--dry-run",
|
||||||
|
),
|
||||||
|
"20",
|
||||||
|
)
|
||||||
|
|
||||||
|
output = assert_ok(
|
||||||
|
dws.run_raw(
|
||||||
|
"mail",
|
||||||
|
"message",
|
||||||
|
"batch-get",
|
||||||
|
"--email",
|
||||||
|
mail_email(),
|
||||||
|
"--ids",
|
||||||
|
"msg_001,msg_002",
|
||||||
|
"--dry-run",
|
||||||
|
)
|
||||||
|
)
|
||||||
|
assert "get_email_by_message_id" in output
|
||||||
|
assert "msg_001" in output
|
||||||
|
assert "msg_002" in output
|
||||||
|
|
||||||
|
|
||||||
|
def test_sent_message_recall_cli_to_mcp(dws):
|
||||||
|
output = assert_ok(dws.run_raw("mail", "sent-message", "recall", "--help"))
|
||||||
|
assert "dws mail sent-message recall" in output
|
||||||
|
assert "--subject string" in output
|
||||||
|
assert "--yes" in output
|
||||||
|
|
||||||
|
assert_fails(
|
||||||
|
dws.run_raw(
|
||||||
|
"mail",
|
||||||
|
"sent-message",
|
||||||
|
"recall",
|
||||||
|
"--email",
|
||||||
|
mail_email(),
|
||||||
|
"--id",
|
||||||
|
"msg_001",
|
||||||
|
"--subject",
|
||||||
|
"subject",
|
||||||
|
),
|
||||||
|
"--yes",
|
||||||
|
)
|
||||||
|
|
||||||
|
output = assert_ok(
|
||||||
|
dws.run_raw(
|
||||||
|
"mail",
|
||||||
|
"sent-message",
|
||||||
|
"recall",
|
||||||
|
"--email",
|
||||||
|
mail_email(),
|
||||||
|
"--id",
|
||||||
|
"msg_001",
|
||||||
|
"--subject",
|
||||||
|
"subject",
|
||||||
|
"--yes",
|
||||||
|
"--dry-run",
|
||||||
|
)
|
||||||
|
)
|
||||||
|
assert "recall_sent_message" in output
|
||||||
|
assert dry_run_args(output) == {
|
||||||
|
"email": mail_email(),
|
||||||
|
"id": "msg_001",
|
||||||
|
"subject": "subject",
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def test_sent_message_recall_detail_cli_to_mcp(dws):
|
||||||
|
output = assert_ok(dws.run_raw("mail", "sent-message", "recall-detail", "--help"))
|
||||||
|
assert "dws mail sent-message recall-detail" in output
|
||||||
|
assert "--email string" in output
|
||||||
|
assert "--id string" in output
|
||||||
|
assert "FINISHED" in output
|
||||||
|
|
||||||
|
output = assert_ok(
|
||||||
|
dws.run_raw(
|
||||||
|
"mail",
|
||||||
|
"sent-message",
|
||||||
|
"recall-detail",
|
||||||
|
"--email",
|
||||||
|
mail_email(),
|
||||||
|
"--id",
|
||||||
|
"task_001",
|
||||||
|
"--dry-run",
|
||||||
|
)
|
||||||
|
)
|
||||||
|
assert "get_recall_detail" in output
|
||||||
|
assert dry_run_args(output) == {"email": mail_email(), "id": "task_001"}
|
||||||
@@ -0,0 +1,133 @@
|
|||||||
|
import os
|
||||||
|
|
||||||
|
from test_utils import combined_output, dry_run_args
|
||||||
|
|
||||||
|
|
||||||
|
def mail_email() -> str:
|
||||||
|
return os.environ.get("DINGTALK_MAIL_EMAIL", "user@example.com")
|
||||||
|
|
||||||
|
|
||||||
|
def assert_ok(result):
|
||||||
|
output = combined_output(result)
|
||||||
|
assert result.returncode == 0, output
|
||||||
|
return output
|
||||||
|
|
||||||
|
|
||||||
|
def test_auto_reply_update_cli_to_mcp(dws):
|
||||||
|
output = assert_ok(dws.run_raw("mail", "auto-reply", "update", "--help"))
|
||||||
|
for flag in ("--email string", "--enabled string", "--start string", "--end string", "--scope string", "--content string"):
|
||||||
|
assert flag in output
|
||||||
|
|
||||||
|
output = assert_ok(
|
||||||
|
dws.run_raw(
|
||||||
|
"mail",
|
||||||
|
"auto-reply",
|
||||||
|
"update",
|
||||||
|
"--email",
|
||||||
|
mail_email(),
|
||||||
|
"--enabled",
|
||||||
|
"true",
|
||||||
|
"--start",
|
||||||
|
"2026/07/01 09:00:00 +0800",
|
||||||
|
"--end",
|
||||||
|
"2026/07/07 18:00:00 +0800",
|
||||||
|
"--scope",
|
||||||
|
"all",
|
||||||
|
"--content",
|
||||||
|
"out of office",
|
||||||
|
"--dry-run",
|
||||||
|
)
|
||||||
|
)
|
||||||
|
assert "update_auto_reply" in output
|
||||||
|
assert dry_run_args(output) == {
|
||||||
|
"email": mail_email(),
|
||||||
|
"enabled": True,
|
||||||
|
"startTime": "2026/07/01 09:00:00 +0800",
|
||||||
|
"endTime": "2026/07/07 18:00:00 +0800",
|
||||||
|
"scope": "all",
|
||||||
|
"content": "out of office",
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def test_allow_list_cli_to_mcp(dws):
|
||||||
|
output = assert_ok(dws.run_raw("mail", "allow-list", "list", "--email", mail_email(), "--dry-run"))
|
||||||
|
assert "list_mailbox_allowlist" in output
|
||||||
|
assert dry_run_args(output) == {"email": mail_email()}
|
||||||
|
|
||||||
|
output = assert_ok(
|
||||||
|
dws.run_raw(
|
||||||
|
"mail",
|
||||||
|
"allow-list",
|
||||||
|
"add",
|
||||||
|
"--email",
|
||||||
|
mail_email(),
|
||||||
|
"--entries",
|
||||||
|
"partner@example.com,@example.org",
|
||||||
|
"--dry-run",
|
||||||
|
)
|
||||||
|
)
|
||||||
|
assert "add_mailbox_allowlist" in output
|
||||||
|
assert dry_run_args(output) == {
|
||||||
|
"email": mail_email(),
|
||||||
|
"entries": ["partner@example.com", "@example.org"],
|
||||||
|
}
|
||||||
|
|
||||||
|
output = assert_ok(
|
||||||
|
dws.run_raw(
|
||||||
|
"mail",
|
||||||
|
"allow-list",
|
||||||
|
"remove",
|
||||||
|
"--email",
|
||||||
|
mail_email(),
|
||||||
|
"--entries",
|
||||||
|
"partner@example.com",
|
||||||
|
"--dry-run",
|
||||||
|
)
|
||||||
|
)
|
||||||
|
assert "remove_mailbox_allowlist" in output
|
||||||
|
assert dry_run_args(output) == {
|
||||||
|
"email": mail_email(),
|
||||||
|
"entries": ["partner@example.com"],
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def test_block_list_cli_to_mcp(dws):
|
||||||
|
output = assert_ok(dws.run_raw("mail", "block-list", "list", "--email", mail_email(), "--dry-run"))
|
||||||
|
assert "list_mailbox_blocklist" in output
|
||||||
|
assert dry_run_args(output) == {"email": mail_email()}
|
||||||
|
|
||||||
|
output = assert_ok(
|
||||||
|
dws.run_raw(
|
||||||
|
"mail",
|
||||||
|
"block-list",
|
||||||
|
"add",
|
||||||
|
"--email",
|
||||||
|
mail_email(),
|
||||||
|
"--entries",
|
||||||
|
"spam@example.com,@junk.example",
|
||||||
|
"--dry-run",
|
||||||
|
)
|
||||||
|
)
|
||||||
|
assert "add_mailbox_blocklist" in output
|
||||||
|
assert dry_run_args(output) == {
|
||||||
|
"email": mail_email(),
|
||||||
|
"entries": ["spam@example.com", "@junk.example"],
|
||||||
|
}
|
||||||
|
|
||||||
|
output = assert_ok(
|
||||||
|
dws.run_raw(
|
||||||
|
"mail",
|
||||||
|
"block-list",
|
||||||
|
"remove",
|
||||||
|
"--email",
|
||||||
|
mail_email(),
|
||||||
|
"--entries",
|
||||||
|
"spam@example.com",
|
||||||
|
"--dry-run",
|
||||||
|
)
|
||||||
|
)
|
||||||
|
assert "remove_mailbox_blocklist" in output
|
||||||
|
assert dry_run_args(output) == {
|
||||||
|
"email": mail_email(),
|
||||||
|
"entries": ["spam@example.com"],
|
||||||
|
}
|
||||||
@@ -0,0 +1,80 @@
|
|||||||
|
from test_utils import combined_output, dry_run_args
|
||||||
|
|
||||||
|
|
||||||
|
def assert_ok(result):
|
||||||
|
output = combined_output(result)
|
||||||
|
assert result.returncode == 0, output
|
||||||
|
return output
|
||||||
|
|
||||||
|
|
||||||
|
def test_group_dimension_cli_to_mcp(dws):
|
||||||
|
output = assert_ok(dws.run_raw("sheet", "group-dimension", "--help"))
|
||||||
|
assert "dws sheet group-dimension" in output
|
||||||
|
assert "--group-state string" in output
|
||||||
|
|
||||||
|
output = assert_ok(
|
||||||
|
dws.run_raw(
|
||||||
|
"sheet",
|
||||||
|
"group-dimension",
|
||||||
|
"--node",
|
||||||
|
"node123",
|
||||||
|
"--sheet-id",
|
||||||
|
"Sheet1",
|
||||||
|
"--range",
|
||||||
|
"3:7",
|
||||||
|
"--group-state",
|
||||||
|
"fold",
|
||||||
|
"--dry-run",
|
||||||
|
)
|
||||||
|
)
|
||||||
|
assert "group_dimension" in output
|
||||||
|
assert dry_run_args(output) == {
|
||||||
|
"nodeId": "node123",
|
||||||
|
"sheetId": "Sheet1",
|
||||||
|
"range": "3:7",
|
||||||
|
"groupState": "fold",
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def test_ungroup_dimension_cli_to_mcp(dws):
|
||||||
|
output = assert_ok(dws.run_raw("sheet", "ungroup-dimension", "--help"))
|
||||||
|
assert "dws sheet ungroup-dimension" in output
|
||||||
|
|
||||||
|
output = assert_ok(
|
||||||
|
dws.run_raw(
|
||||||
|
"sheet",
|
||||||
|
"ungroup-dimension",
|
||||||
|
"--node",
|
||||||
|
"node123",
|
||||||
|
"--sheet-id",
|
||||||
|
"Sheet1",
|
||||||
|
"--range",
|
||||||
|
"C:F",
|
||||||
|
"--dry-run",
|
||||||
|
)
|
||||||
|
)
|
||||||
|
assert "ungroup_dimension" in output
|
||||||
|
assert dry_run_args(output) == {
|
||||||
|
"nodeId": "node123",
|
||||||
|
"sheetId": "Sheet1",
|
||||||
|
"range": "C:F",
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def test_group_dimension_rejects_invalid_state(dws):
|
||||||
|
result = dws.run_raw(
|
||||||
|
"sheet",
|
||||||
|
"group-dimension",
|
||||||
|
"--node",
|
||||||
|
"node123",
|
||||||
|
"--sheet-id",
|
||||||
|
"Sheet1",
|
||||||
|
"--range",
|
||||||
|
"3:7",
|
||||||
|
"--group-state",
|
||||||
|
"invalid",
|
||||||
|
"--dry-run",
|
||||||
|
)
|
||||||
|
output = combined_output(result)
|
||||||
|
assert result.returncode != 0
|
||||||
|
assert "group-state" in output
|
||||||
@@ -0,0 +1,58 @@
|
|||||||
|
import json
|
||||||
|
import os
|
||||||
|
import re
|
||||||
|
import shlex
|
||||||
|
import subprocess
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
|
||||||
|
def repo_root(start_file: str) -> Path:
|
||||||
|
current = Path(start_file).resolve()
|
||||||
|
for parent in [current, *current.parents]:
|
||||||
|
if (parent / "go.mod").exists():
|
||||||
|
return parent
|
||||||
|
raise RuntimeError(f"cannot locate repo root from {start_file}")
|
||||||
|
|
||||||
|
|
||||||
|
def resolve_dws_cmd(start_file: str) -> list[str]:
|
||||||
|
root = repo_root(start_file)
|
||||||
|
if env_bin := os.environ.get("DWS_BIN"):
|
||||||
|
return shlex.split(env_bin)
|
||||||
|
|
||||||
|
for rel in ("dws", "build/dws", "bin/dws", "dingtalk-workspace-cli"):
|
||||||
|
candidate = root / rel
|
||||||
|
if candidate.exists() and os.access(candidate, os.X_OK):
|
||||||
|
return [str(candidate)]
|
||||||
|
|
||||||
|
return ["go", "run", "./cmd"]
|
||||||
|
|
||||||
|
|
||||||
|
def combined_output(result: subprocess.CompletedProcess) -> str:
|
||||||
|
return (result.stdout or "") + (result.stderr or "")
|
||||||
|
|
||||||
|
|
||||||
|
def dry_run_args(output: str) -> dict:
|
||||||
|
match = re.search(r"Arguments:\s*(\{.*\})", output, re.S)
|
||||||
|
assert match, f"dry-run output does not contain Arguments JSON: {output}"
|
||||||
|
return json.loads(match.group(1))
|
||||||
|
|
||||||
|
|
||||||
|
class DWSRunner:
|
||||||
|
def __init__(self):
|
||||||
|
self.root = repo_root(__file__)
|
||||||
|
self.cmd = resolve_dws_cmd(__file__)
|
||||||
|
|
||||||
|
def run_raw(self, *args: str, timeout: int = 45) -> subprocess.CompletedProcess:
|
||||||
|
return subprocess.run(
|
||||||
|
[*self.cmd, *args],
|
||||||
|
cwd=self.root,
|
||||||
|
text=True,
|
||||||
|
capture_output=True,
|
||||||
|
timeout=timeout,
|
||||||
|
)
|
||||||
|
|
||||||
|
def run(self, *args: str, timeout: int = 45):
|
||||||
|
result = self.run_raw(*args, timeout=timeout)
|
||||||
|
output = combined_output(result)
|
||||||
|
assert result.returncode == 0, output
|
||||||
|
return json.loads(result.stdout)
|
||||||
@@ -64,7 +64,7 @@ skill 源默认取二进制内嵌的版本(升级二进制即升级 skill)
|
|||||||
dws skill setup --mode mono --yes # 非交互装 mono
|
dws skill setup --mode mono --yes # 非交互装 mono
|
||||||
dws skill setup --mode multi --target claude # multi 全装到 ~/.claude/skills/
|
dws skill setup --mode multi --target claude # multi 全装到 ~/.claude/skills/
|
||||||
dws skill setup --mode multi -s aitable -s calendar # 只装 aitable + calendar
|
dws skill setup --mode multi -s aitable -s calendar # 只装 aitable + calendar
|
||||||
dws skill setup --mode multi -x live -x devdoc # 装其余 18 个,剔除 2 个
|
dws skill setup --mode multi -x live -x devdoc # 装其余 20 个,剔除 2 个
|
||||||
dws skill setup --source /path/to/repo # 显式指定 skill 源`,
|
dws skill setup --source /path/to/repo # 显式指定 skill 源`,
|
||||||
DisableAutoGenTag: true,
|
DisableAutoGenTag: true,
|
||||||
RunE: runSkillSetup,
|
RunE: runSkillSetup,
|
||||||
@@ -506,7 +506,7 @@ func confirmSkillSetup(out io.Writer, mode, src string, dests []string, multiSki
|
|||||||
if mode == skillSetupModeMulti {
|
if mode == skillSetupModeMulti {
|
||||||
fmt.Fprintln(out, "\n🧪 ─────────────────────────────────────────────────────────────")
|
fmt.Fprintln(out, "\n🧪 ─────────────────────────────────────────────────────────────")
|
||||||
fmt.Fprintln(out, " multi 模式当前为 EXPERIMENTAL(试验版 / Preview)")
|
fmt.Fprintln(out, " multi 模式当前为 EXPERIMENTAL(试验版 / Preview)")
|
||||||
fmt.Fprintln(out, " · 20 个 dingtalk-* 子 skill 跑过 verifier,可用但未达 stable")
|
fmt.Fprintln(out, " · 22 个 dingtalk-* 子 skill 跑过 verifier,可用但未达 stable")
|
||||||
fmt.Fprintln(out, " · 跨 skill 引用、bundle 命名、目录布局后续可能调整")
|
fmt.Fprintln(out, " · 跨 skill 引用、bundle 命名、目录布局后续可能调整")
|
||||||
fmt.Fprintln(out, " · 不建议在生产 / 共享环境直接落地;问题请提 issue 反馈")
|
fmt.Fprintln(out, " · 不建议在生产 / 共享环境直接落地;问题请提 issue 反馈")
|
||||||
fmt.Fprintln(out, " 稳定版请用 --mode mono")
|
fmt.Fprintln(out, " 稳定版请用 --mode mono")
|
||||||
|
|||||||
@@ -0,0 +1,603 @@
|
|||||||
|
package helpers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ──────────────────────────────────────────────────────────
|
||||||
|
// dws agoal — Agoal 管理
|
||||||
|
// ──────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
func newAgoalCommand() *cobra.Command {
|
||||||
|
root := &cobra.Command{
|
||||||
|
Use: "agoal",
|
||||||
|
Short: "Agoal 管理",
|
||||||
|
Long: `管理钉钉 Agoal:战略解码、经营合约、计分卡、用户目标、周月报。
|
||||||
|
|
||||||
|
命令结构:
|
||||||
|
dws agoal strategy list 获取战略解码列表
|
||||||
|
dws agoal strategy detail 获取战略解码详情
|
||||||
|
dws agoal strategy update 更新战略解码
|
||||||
|
dws agoal contract list 获取经营合约列表
|
||||||
|
dws agoal contract fields 获取经营合约字段列表
|
||||||
|
dws agoal contract detail 获取经营合约详情
|
||||||
|
dws agoal contract update 更新经营合约
|
||||||
|
dws agoal scorecard detail 获取计分卡详情
|
||||||
|
dws agoal scorecard entity-detail 获取计分卡实体详情
|
||||||
|
dws agoal scorecard update 更新计分卡
|
||||||
|
dws agoal user rules 获取用户规则
|
||||||
|
dws agoal user objectives 查询用户目标列表
|
||||||
|
dws agoal report list-statistics 获取周月报数据跟催列表
|
||||||
|
dws agoal report submit-detail 获取周月报规则提交详情
|
||||||
|
dws agoal obj-template list 获取目标模板列表
|
||||||
|
dws agoal obj-template create-or-update 新增或更新目标模板`,
|
||||||
|
RunE: groupRunE,
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── strategy: 战略解码管理 ──────────────────────────────────
|
||||||
|
|
||||||
|
strategyCmd := &cobra.Command{Use: "strategy", Short: "战略解码管理", RunE: groupRunE}
|
||||||
|
|
||||||
|
strategyListCmd := &cobra.Command{
|
||||||
|
Use: "list",
|
||||||
|
Short: "获取战略解码列表",
|
||||||
|
Long: `按部门或个人维度查询战略解码列表。
|
||||||
|
scopeType 支持:
|
||||||
|
DEPT — 按部门查询
|
||||||
|
PERSONAL — 按个人查询
|
||||||
|
|
||||||
|
--scope-id 为对应维度的钉钉部门 id 或用户 id。`,
|
||||||
|
Example: ` dws agoal strategy list --scope-type PERSONAL --scope-id USER_ID
|
||||||
|
dws agoal strategy list --scope-type DEPT --scope-id DEPT_ID`,
|
||||||
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
|
if err := validateRequiredFlags(cmd, "scope-type", "scope-id"); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
toolArgs := map[string]any{
|
||||||
|
"scopeType": mustGetFlag(cmd, "scope-type"),
|
||||||
|
"openId": mustGetFlag(cmd, "scope-id"),
|
||||||
|
}
|
||||||
|
if v, _ := cmd.Flags().GetString("request-id"); v != "" {
|
||||||
|
toolArgs["requestId"] = v
|
||||||
|
}
|
||||||
|
return callMCPTool("list_strategy_decodings", toolArgs)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
strategyListCmd.Flags().String("scope-type", "", "解码范围类型: DEPT/PERSONAL (必填)")
|
||||||
|
strategyListCmd.Flags().String("scope-id", "", "scope-type 对应的钉钉部门 id 或用户 id (必填)")
|
||||||
|
strategyListCmd.Flags().String("request-id", "", "requestId (可选)")
|
||||||
|
|
||||||
|
strategyDetailCmd := &cobra.Command{
|
||||||
|
Use: "detail",
|
||||||
|
Short: "获取战略解码详情",
|
||||||
|
Long: `根据战略解码 id (profileId) 获取战略解码的详细信息。`,
|
||||||
|
Example: ` dws agoal strategy detail --profile-id PROFILE_ID`,
|
||||||
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
|
if err := validateRequiredFlags(cmd, "profile-id"); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
toolArgs := map[string]any{
|
||||||
|
"profileId": mustGetFlag(cmd, "profile-id"),
|
||||||
|
}
|
||||||
|
if v, _ := cmd.Flags().GetString("request-id"); v != "" {
|
||||||
|
toolArgs["requestId"] = v
|
||||||
|
}
|
||||||
|
return callMCPTool("get_strategy_decoding_detail", toolArgs)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
strategyDetailCmd.Flags().String("profile-id", "", "战略解码 id (必填)")
|
||||||
|
strategyDetailCmd.Flags().String("request-id", "", "requestId (可选)")
|
||||||
|
|
||||||
|
strategyUpdateCmd := &cobra.Command{
|
||||||
|
Use: "update",
|
||||||
|
Short: "更新战略解码",
|
||||||
|
Long: `谨慎操作,基于查询接口返回的老数据进行修改,本接口是覆盖逻辑,会根据战略解码id进行对应的修改。
|
||||||
|
--content 为 JSON 数组,每个实体对象包含:
|
||||||
|
id — 实体 id
|
||||||
|
title — 标题对象 {"title":"标题文本"}
|
||||||
|
linkEntityId — 所属的实体 ID(查询接口中此字段有值一定要传进来)
|
||||||
|
entityType — 类型: OGSM_OBJECTIVE/目的、OGSM_GOAL/目标、OGSM_STRATEGY/策略、OGSM_MEASUREMENT/衡量标准、OGSM_TACTICS/行动方案
|
||||||
|
status — 状态: NORMAL/正常、PRE_PUBLISH_THEN_UPDATE/预发布更新、PRE_PUBLISH_THEN_CREATE/预发布新增、PRE_PUBLISH_THEN_DELETE/预发布删除
|
||||||
|
supporters — 承接人数组 [{type, dingId, staffId}],type: USER/个人、DEPARTMENT/部门,staffId 类型为 USER 时必填
|
||||||
|
indicators — 关键指标 id 字符串数组,如 ["id1","id2"]
|
||||||
|
linkSources — 资源关联数组 [{id, linkType, linkId, source, objectId, keyResultId}]
|
||||||
|
linkType: project/项目、task/任务、campaign/战役空间、product/产品空间、doc/文档、standard/其它
|
||||||
|
source: teambition
|
||||||
|
executors — 人员 dingId 字符串数组,如 ["dingId1","dingId2"]
|
||||||
|
teams — 部门 dingId 字符串数组,如 ["dingId1","dingId2"]`,
|
||||||
|
Example: ` dws agoal strategy update --profile-id PROFILE_ID --content '[{"id":"entity1","title":{"title":"新目标"},"entityType":"OGSM_OBJECTIVE","status":"NORMAL","executors":["dingId1"],"teams":["deptDingId1"]}]'`,
|
||||||
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
|
if err := validateRequiredFlags(cmd, "profile-id", "content"); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
var contentArr []any
|
||||||
|
if err := json.Unmarshal([]byte(mustGetFlag(cmd, "content")), &contentArr); err != nil {
|
||||||
|
return fmt.Errorf("--content must be a valid JSON array: %w", err)
|
||||||
|
}
|
||||||
|
toolArgs := map[string]any{
|
||||||
|
"profileId": mustGetFlag(cmd, "profile-id"),
|
||||||
|
"content": contentArr,
|
||||||
|
}
|
||||||
|
if v, _ := cmd.Flags().GetString("request-id"); v != "" {
|
||||||
|
toolArgs["requestId"] = v
|
||||||
|
}
|
||||||
|
return callMCPTool("update_strategy_decoding", toolArgs)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
strategyUpdateCmd.Flags().String("profile-id", "", "战略解码 id (必填)")
|
||||||
|
strategyUpdateCmd.Flags().String("content", "", "实体列表 JSON 数组 (必填)")
|
||||||
|
strategyUpdateCmd.Flags().String("request-id", "", "requestId (可选)")
|
||||||
|
|
||||||
|
strategyCmd.AddCommand(strategyListCmd, strategyDetailCmd, strategyUpdateCmd)
|
||||||
|
|
||||||
|
// ── contract: 经营合约管理 ──────────────────────────────────
|
||||||
|
|
||||||
|
contractCmd := &cobra.Command{Use: "contract", Short: "经营合约管理", RunE: groupRunE}
|
||||||
|
|
||||||
|
contractListCmd := &cobra.Command{
|
||||||
|
Use: "list",
|
||||||
|
Short: "获取经营合约列表",
|
||||||
|
Long: `按部门或个人维度查询经营合约列表。
|
||||||
|
scopeType 支持:
|
||||||
|
DEPT — 按部门查询
|
||||||
|
PERSONAL — 按个人查询
|
||||||
|
|
||||||
|
--scope-id 为通讯录里的部门 id 或用户 id。`,
|
||||||
|
Example: ` dws agoal contract list --scope-type PERSONAL --scope-id USER_ID
|
||||||
|
dws agoal contract list --scope-type DEPT --scope-id DEPT_ID`,
|
||||||
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
|
if err := validateRequiredFlags(cmd, "scope-type", "scope-id"); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
toolArgs := map[string]any{
|
||||||
|
"scopeType": mustGetFlag(cmd, "scope-type"),
|
||||||
|
"openId": mustGetFlag(cmd, "scope-id"),
|
||||||
|
}
|
||||||
|
if v, _ := cmd.Flags().GetString("request-id"); v != "" {
|
||||||
|
toolArgs["requestId"] = v
|
||||||
|
}
|
||||||
|
return callMCPTool("list_op_contracts", toolArgs)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
contractListCmd.Flags().String("scope-type", "", "合约范围类型: DEPT/PERSONAL (必填)")
|
||||||
|
contractListCmd.Flags().String("scope-id", "", "scope-type 对应的钉钉部门 id 或用户 id (必填)")
|
||||||
|
contractListCmd.Flags().String("request-id", "", "requestId (可选)")
|
||||||
|
|
||||||
|
contractFieldsCmd := &cobra.Command{
|
||||||
|
Use: "fields",
|
||||||
|
Short: "获取经营合约字段列表",
|
||||||
|
Long: `获取指定组织下经营合约的字段配置信息。`,
|
||||||
|
Example: ` dws agoal contract fields`,
|
||||||
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
|
toolArgs := map[string]any{}
|
||||||
|
if v, _ := cmd.Flags().GetString("request-id"); v != "" {
|
||||||
|
toolArgs["requestId"] = v
|
||||||
|
}
|
||||||
|
return callMCPTool("list_op_contract_fields", toolArgs)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
contractFieldsCmd.Flags().String("request-id", "", "requestId (可选)")
|
||||||
|
|
||||||
|
contractDetailCmd := &cobra.Command{
|
||||||
|
Use: "detail",
|
||||||
|
Short: "获取经营合约详情",
|
||||||
|
Long: `根据经营合约 id 获取经营合约的详细信息。`,
|
||||||
|
Example: ` dws agoal contract detail --contract-id CONTRACT_ID`,
|
||||||
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
|
if err := validateRequiredFlags(cmd, "contract-id"); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
toolArgs := map[string]any{
|
||||||
|
"contractId": mustGetFlag(cmd, "contract-id"),
|
||||||
|
}
|
||||||
|
if v, _ := cmd.Flags().GetString("request-id"); v != "" {
|
||||||
|
toolArgs["requestId"] = v
|
||||||
|
}
|
||||||
|
return callMCPTool("get_op_contract_detail", toolArgs)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
contractDetailCmd.Flags().String("contract-id", "", "经营合约 id (必填)")
|
||||||
|
contractDetailCmd.Flags().String("request-id", "", "requestId (可选)")
|
||||||
|
|
||||||
|
contractUpdateCmd := &cobra.Command{
|
||||||
|
Use: "update",
|
||||||
|
Short: "更新经营合约",
|
||||||
|
Long: `谨慎操作,一定要基于查询接口返回的老数据进行修改,然后给本接口使用,因为本接口是覆盖逻辑,会使用传入的数据去覆盖已存在的数据。
|
||||||
|
--dimensions 为 JSON 数组,每个维度对象包含:
|
||||||
|
id — 维度 id
|
||||||
|
title — 维度名称
|
||||||
|
description — 维度描述
|
||||||
|
weight — 维度权重
|
||||||
|
objectives — 目标列表
|
||||||
|
dimensionConfig — 维度配置
|
||||||
|
children — 子维度列表
|
||||||
|
|
||||||
|
可选参数:
|
||||||
|
--audit-config — 审批配置 JSON,如 {"needAudit":true,"processTemplateId":"TPL_ID"}
|
||||||
|
--objective-template — 合约模板 JSON,如 {"id":"TPL_ID","title":"模板名称"}`,
|
||||||
|
Example: ` dws agoal contract update --contract-id CONTRACT_ID \
|
||||||
|
--dimensions '[{"id":"dim1","title":"业绩","weight":60,"objectives":[{"id":"obj1"}]}]'`,
|
||||||
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
|
if err := validateRequiredFlags(cmd, "contract-id", "dimensions"); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
var dimensionsArr []any
|
||||||
|
if err := json.Unmarshal([]byte(mustGetFlag(cmd, "dimensions")), &dimensionsArr); err != nil {
|
||||||
|
return fmt.Errorf("--dimensions must be a valid JSON array: %w", err)
|
||||||
|
}
|
||||||
|
toolArgs := map[string]any{
|
||||||
|
"contractId": mustGetFlag(cmd, "contract-id"),
|
||||||
|
"dimensions": dimensionsArr,
|
||||||
|
}
|
||||||
|
if v, _ := cmd.Flags().GetString("request-id"); v != "" {
|
||||||
|
toolArgs["requestId"] = v
|
||||||
|
}
|
||||||
|
if v, _ := cmd.Flags().GetString("audit-config"); v != "" {
|
||||||
|
toolArgs["auditConfig"] = v
|
||||||
|
}
|
||||||
|
if v, _ := cmd.Flags().GetString("objective-template"); v != "" {
|
||||||
|
toolArgs["objectiveTemplate"] = v
|
||||||
|
}
|
||||||
|
return callMCPTool("update_op_contract", toolArgs)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
contractUpdateCmd.Flags().String("contract-id", "", "经营合约 id (必填)")
|
||||||
|
contractUpdateCmd.Flags().String("request-id", "", "requestId (可选)")
|
||||||
|
contractUpdateCmd.Flags().String("audit-config", "", "审批配置 JSON (可选)")
|
||||||
|
contractUpdateCmd.Flags().String("objective-template", "", "合约模板 JSON (可选)")
|
||||||
|
contractUpdateCmd.Flags().String("dimensions", "", "维度内容列表 JSON 数组 (必填)")
|
||||||
|
|
||||||
|
contractCmd.AddCommand(contractListCmd, contractFieldsCmd, contractDetailCmd, contractUpdateCmd)
|
||||||
|
|
||||||
|
// ── scorecard: 计分卡管理 ───────────────────────────────────
|
||||||
|
|
||||||
|
scorecardCmd := &cobra.Command{Use: "scorecard", Short: "计分卡管理", RunE: groupRunE}
|
||||||
|
|
||||||
|
scorecardDetailCmd := &cobra.Command{
|
||||||
|
Use: "detail",
|
||||||
|
Short: "获取计分卡详情",
|
||||||
|
Long: `根据部门 id 和时间获取计分卡详情。
|
||||||
|
--selected-time 接受 ISO-8601 字符串,传入对应周期起始时刻:
|
||||||
|
2026年 → "2026-01-01T00:00:00+08:00"
|
||||||
|
2026年5月 → "2026-05-01T00:00:00+08:00"`,
|
||||||
|
Example: ` dws agoal scorecard detail --selected-time "2026-01-01T00:00:00+08:00" --dept-id DEPT_ID`,
|
||||||
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
|
if err := validateRequiredFlags(cmd, "selected-time", "dept-id"); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
selectedTimeMs, err := parseISO8601ToMillis(mustGetFlag(cmd, "selected-time"))
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("--selected-time: %w", err)
|
||||||
|
}
|
||||||
|
toolArgs := map[string]any{
|
||||||
|
"selectedTime": selectedTimeMs,
|
||||||
|
"deptId": mustGetFlag(cmd, "dept-id"),
|
||||||
|
}
|
||||||
|
if v, _ := cmd.Flags().GetString("request-id"); v != "" {
|
||||||
|
toolArgs["requestId"] = v
|
||||||
|
}
|
||||||
|
return callMCPTool("get_score_card_detail", toolArgs)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
scorecardDetailCmd.Flags().String("selected-time", "", "ISO-8601 时间字符串,如 \"2026-01-01T00:00:00+08:00\" (必填)")
|
||||||
|
scorecardDetailCmd.Flags().String("dept-id", "", "部门 id (必填)")
|
||||||
|
scorecardDetailCmd.Flags().String("request-id", "", "requestId (可选)")
|
||||||
|
|
||||||
|
scorecardEntityDetailCmd := &cobra.Command{
|
||||||
|
Use: "entity-detail",
|
||||||
|
Short: "获取计分卡实体详情",
|
||||||
|
Long: `根据计分卡 id 和实体 id 获取计分卡实体的详细信息。`,
|
||||||
|
Example: ` dws agoal scorecard entity-detail --sc-id SC_ID --entity-id ENTITY_ID`,
|
||||||
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
|
if err := validateRequiredFlags(cmd, "sc-id", "entity-id"); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
toolArgs := map[string]any{
|
||||||
|
"scId": mustGetFlag(cmd, "sc-id"),
|
||||||
|
"entityId": mustGetFlag(cmd, "entity-id"),
|
||||||
|
}
|
||||||
|
if v, _ := cmd.Flags().GetString("request-id"); v != "" {
|
||||||
|
toolArgs["requestId"] = v
|
||||||
|
}
|
||||||
|
return callMCPTool("get_score_card_entity_detail", toolArgs)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
scorecardEntityDetailCmd.Flags().String("sc-id", "", "计分卡 id (必填)")
|
||||||
|
scorecardEntityDetailCmd.Flags().String("entity-id", "", "计分卡实体 id (必填)")
|
||||||
|
scorecardEntityDetailCmd.Flags().String("request-id", "", "requestId (可选)")
|
||||||
|
|
||||||
|
scorecardUpdateCmd := &cobra.Command{
|
||||||
|
Use: "update",
|
||||||
|
Short: "更新计分卡",
|
||||||
|
Long: `更新指定计分卡的内容。
|
||||||
|
--content 为 JSON 数组,每个维度对象包含:
|
||||||
|
id — 维度 id
|
||||||
|
title — 维度名称
|
||||||
|
items — 指标或关键事项列表,每项包含:
|
||||||
|
id — 实体 id
|
||||||
|
title — 名称
|
||||||
|
reference — 参考信息
|
||||||
|
start — 起始值
|
||||||
|
target — 目标值
|
||||||
|
executors — 负责人列表,每项包含 openId`,
|
||||||
|
Example: ` dws agoal scorecard update --dept-id DEPT_ID --selected-time "2025-01-01T00:00:00+08:00" --id SC_ID --tracking-period-type MONTHLY --content '[{"id":"dim1","title":"业绩","items":[{"id":"item1","title":"收入","target":"100"}]}]'`,
|
||||||
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
|
if err := validateRequiredFlags(cmd, "dept-id", "selected-time", "id", "tracking-period-type", "content"); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
selectedTimeMs, err := parseISO8601ToMillis(mustGetFlag(cmd, "selected-time"))
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("--selected-time: %w", err)
|
||||||
|
}
|
||||||
|
var contentArr []any
|
||||||
|
if err := json.Unmarshal([]byte(mustGetFlag(cmd, "content")), &contentArr); err != nil {
|
||||||
|
return fmt.Errorf("--content must be a valid JSON array: %w", err)
|
||||||
|
}
|
||||||
|
toolArgs := map[string]any{
|
||||||
|
"deptId": mustGetFlag(cmd, "dept-id"),
|
||||||
|
"selectedTime": selectedTimeMs,
|
||||||
|
"id": mustGetFlag(cmd, "id"),
|
||||||
|
"trackingPeriodType": mustGetFlag(cmd, "tracking-period-type"),
|
||||||
|
"content": contentArr,
|
||||||
|
}
|
||||||
|
if v, _ := cmd.Flags().GetString("request-id"); v != "" {
|
||||||
|
toolArgs["requestId"] = v
|
||||||
|
}
|
||||||
|
return callMCPTool("update_score_card", toolArgs)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
scorecardUpdateCmd.Flags().String("dept-id", "", "部门 id (必填)")
|
||||||
|
scorecardUpdateCmd.Flags().String("selected-time", "", "ISO-8601 时间字符串,如 \"2026-01-01T00:00:00+08:00\" (必填)")
|
||||||
|
scorecardUpdateCmd.Flags().String("id", "", "计分卡 id (必填)")
|
||||||
|
scorecardUpdateCmd.Flags().String("tracking-period-type", "", "跟踪周期类型: MONTHLY/月度追踪、QUARTERLY/季度追踪 (必填)")
|
||||||
|
scorecardUpdateCmd.Flags().String("content", "", "内容 JSON 数组 (必填)")
|
||||||
|
scorecardUpdateCmd.Flags().String("request-id", "", "requestId (可选)")
|
||||||
|
|
||||||
|
scorecardCmd.AddCommand(scorecardDetailCmd, scorecardEntityDetailCmd, scorecardUpdateCmd)
|
||||||
|
|
||||||
|
// ── user: 用户目标管理 ──────────────────────────────────────
|
||||||
|
|
||||||
|
userCmd := &cobra.Command{Use: "user", Short: "用户目标管理", RunE: groupRunE}
|
||||||
|
|
||||||
|
userRulesCmd := &cobra.Command{
|
||||||
|
Use: "rules",
|
||||||
|
Short: "获取用户的规则周期列表",
|
||||||
|
Long: `获取用户的规则周期列表。不传 dingUserId 时默认取操作人自己的规则。`,
|
||||||
|
Example: ` dws agoal user rules
|
||||||
|
dws agoal user rules --user-id USER_ID`,
|
||||||
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
|
toolArgs := map[string]any{}
|
||||||
|
if v, _ := cmd.Flags().GetString("user-id"); v != "" {
|
||||||
|
toolArgs["dingUserId"] = v
|
||||||
|
}
|
||||||
|
if v, _ := cmd.Flags().GetString("request-id"); v != "" {
|
||||||
|
toolArgs["requestId"] = v
|
||||||
|
}
|
||||||
|
return callMCPTool("get_user_rules", toolArgs)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
userRulesCmd.Flags().String("user-id", "", "要查询的人员钉钉 id (可选,默认取操作人)")
|
||||||
|
userRulesCmd.Flags().String("request-id", "", "requestId (可选)")
|
||||||
|
|
||||||
|
userObjectivesCmd := &cobra.Command{
|
||||||
|
Use: "objectives",
|
||||||
|
Short: "查询用户目标列表",
|
||||||
|
Long: `根据用户、规则 id 和周期列表查询用户的目标。`,
|
||||||
|
Example: ` dws agoal user objectives --user-id USER_ID --rule-id RULE_ID --period-ids "period1,period2"`,
|
||||||
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
|
if err := validateRequiredFlags(cmd, "user-id", "rule-id", "period-ids"); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
toolArgs := map[string]any{
|
||||||
|
"dingUserId": mustGetFlag(cmd, "user-id"),
|
||||||
|
"objectiveRuleId": mustGetFlag(cmd, "rule-id"),
|
||||||
|
"periodIds": parseCSVValues(mustGetFlag(cmd, "period-ids")),
|
||||||
|
}
|
||||||
|
if v, _ := cmd.Flags().GetString("request-id"); v != "" {
|
||||||
|
toolArgs["requestId"] = v
|
||||||
|
}
|
||||||
|
return callMCPTool("list_user_objectives", toolArgs)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
userObjectivesCmd.Flags().String("user-id", "", "要查询的人员钉钉 id (必填)")
|
||||||
|
userObjectivesCmd.Flags().String("rule-id", "", "规则 id (必填)")
|
||||||
|
userObjectivesCmd.Flags().String("period-ids", "", "周期 id 列表,逗号分隔 (必填)")
|
||||||
|
userObjectivesCmd.Flags().String("request-id", "", "requestId (可选)")
|
||||||
|
|
||||||
|
userCmd.AddCommand(userRulesCmd, userObjectivesCmd)
|
||||||
|
|
||||||
|
// ── report: 周月报管理 ──────────────────────────────────────
|
||||||
|
|
||||||
|
reportCmd := &cobra.Command{Use: "report", Short: "周月报管理", RunE: groupRunE}
|
||||||
|
|
||||||
|
reportListStatisticsCmd := &cobra.Command{
|
||||||
|
Use: "list-statistics",
|
||||||
|
Short: "获取周月报数据跟催列表",
|
||||||
|
Long: `获取周月报规则的人员提交情况列表,包含按时提交、迟交、未提交的人员数量统计。`,
|
||||||
|
Example: ` dws agoal report list-statistics
|
||||||
|
dws agoal report list-statistics --keyword "周报规则"`,
|
||||||
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
|
toolArgs := map[string]any{}
|
||||||
|
if v, _ := cmd.Flags().GetString("request-id"); v != "" {
|
||||||
|
toolArgs["requestId"] = v
|
||||||
|
}
|
||||||
|
if v, _ := cmd.Flags().GetString("keyword"); v != "" {
|
||||||
|
toolArgs["keyword"] = v
|
||||||
|
}
|
||||||
|
return callMCPTool("list_report_statistics", toolArgs)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
reportListStatisticsCmd.Flags().String("request-id", "", "requestId (可选)")
|
||||||
|
reportListStatisticsCmd.Flags().String("keyword", "", "搜索规则名称 (可选)")
|
||||||
|
|
||||||
|
reportSubmitDetailCmd := &cobra.Command{
|
||||||
|
Use: "submit-detail",
|
||||||
|
Short: "获取周月报规则提交详情",
|
||||||
|
Long: `获取周月报规则提交详情,包含按时提交、迟交、未提交中的具体人员以及提交的时间、周报id等。
|
||||||
|
--submit-state 支持:
|
||||||
|
ON_TIME — 按时提交
|
||||||
|
LATE — 迟交
|
||||||
|
NOT_SUBMITTED — 未提交
|
||||||
|
--query-date 接受 ISO-8601 字符串,如 "2026-06-18T00:00:00+08:00"`,
|
||||||
|
Example: ` dws agoal report submit-detail --template-id TPL_ID --submit-state ON_TIME
|
||||||
|
dws agoal report submit-detail --template-id TPL_ID --submit-state LATE --query-date "2026-06-18T00:00:00+08:00" --page 1 --page-size 20`,
|
||||||
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
|
if err := validateRequiredFlags(cmd, "template-id", "submit-state"); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
toolArgs := map[string]any{
|
||||||
|
"templateId": mustGetFlag(cmd, "template-id"),
|
||||||
|
"submitState": mustGetFlag(cmd, "submit-state"),
|
||||||
|
}
|
||||||
|
if v, _ := cmd.Flags().GetString("request-id"); v != "" {
|
||||||
|
toolArgs["requestId"] = v
|
||||||
|
}
|
||||||
|
if v, _ := cmd.Flags().GetString("query-date"); v != "" {
|
||||||
|
queryDateMs, err := parseISO8601ToMillis(v)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("--query-date: %w", err)
|
||||||
|
}
|
||||||
|
toolArgs["queryDate"] = time.UnixMilli(queryDateMs).In(shanghaiLocation()).Format("2006-01-02")
|
||||||
|
}
|
||||||
|
if v, _ := cmd.Flags().GetInt("page"); v != 0 {
|
||||||
|
toolArgs["page"] = v
|
||||||
|
}
|
||||||
|
if v, _ := cmd.Flags().GetInt("page-size"); v != 0 {
|
||||||
|
toolArgs["pageSize"] = v
|
||||||
|
}
|
||||||
|
if v, _ := cmd.Flags().GetString("keyword"); v != "" {
|
||||||
|
toolArgs["keyword"] = v
|
||||||
|
}
|
||||||
|
return callMCPTool("get_submit_detail", toolArgs)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
reportSubmitDetailCmd.Flags().String("template-id", "", "规则模板 id (必填)")
|
||||||
|
reportSubmitDetailCmd.Flags().String("submit-state", "", "提交状态: ON_TIME(按时提交)/LATE(迟交)/NOT_SUBMITTED(未提交) (必填)")
|
||||||
|
reportSubmitDetailCmd.Flags().String("request-id", "", "requestId (可选)")
|
||||||
|
reportSubmitDetailCmd.Flags().String("query-date", "", "查询日期,ISO-8601 格式(如 \"2026-06-18T00:00:00+08:00\"),默认为当天 (可选)")
|
||||||
|
reportSubmitDetailCmd.Flags().Int("page", 0, "分页参数,默认为 1 (可选)")
|
||||||
|
reportSubmitDetailCmd.Flags().Int("page-size", 0, "分页参数,默认为 10 (可选)")
|
||||||
|
reportSubmitDetailCmd.Flags().String("keyword", "", "搜索员工名称 (可选)")
|
||||||
|
|
||||||
|
reportCmd.AddCommand(reportListStatisticsCmd, reportSubmitDetailCmd)
|
||||||
|
|
||||||
|
// ── template: 目标模板管理 ──────────────────────────────────
|
||||||
|
|
||||||
|
objTemplateCmd := &cobra.Command{Use: "obj-template", Short: "目标模板管理", RunE: groupRunE}
|
||||||
|
|
||||||
|
objTemplateListCmd := &cobra.Command{
|
||||||
|
Use: "list",
|
||||||
|
Short: "获取目标模板列表",
|
||||||
|
Long: `获取目标模板列表,支持关键词搜索和分页。`,
|
||||||
|
Example: ` dws agoal obj-template list
|
||||||
|
dws agoal obj-template list --keyword "业绩" --page 1 --page-size 10`,
|
||||||
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
|
toolArgs := map[string]any{}
|
||||||
|
if v, _ := cmd.Flags().GetString("request-id"); v != "" {
|
||||||
|
toolArgs["requestId"] = v
|
||||||
|
}
|
||||||
|
if v, _ := cmd.Flags().GetInt("page"); v != 0 {
|
||||||
|
toolArgs["page"] = v
|
||||||
|
}
|
||||||
|
if v, _ := cmd.Flags().GetInt("page-size"); v != 0 {
|
||||||
|
toolArgs["pageSize"] = v
|
||||||
|
}
|
||||||
|
if v, _ := cmd.Flags().GetString("keyword"); v != "" {
|
||||||
|
toolArgs["keyword"] = v
|
||||||
|
}
|
||||||
|
return callMCPTool("list_obj_template", toolArgs)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
objTemplateListCmd.Flags().String("request-id", "", "requestId (可选)")
|
||||||
|
objTemplateListCmd.Flags().Int("page", 0, "页码,默认 1 (可选)")
|
||||||
|
objTemplateListCmd.Flags().Int("page-size", 0, "每页数量,默认 10 (可选)")
|
||||||
|
objTemplateListCmd.Flags().String("keyword", "", "搜索关键词 (可选)")
|
||||||
|
|
||||||
|
objTemplateCreateOrUpdateCmd := &cobra.Command{
|
||||||
|
Use: "create-or-update",
|
||||||
|
Short: "新增或更新目标模板",
|
||||||
|
Long: `新增或更新目标模板。覆盖逻辑,更新时一定要基于查询接口返回的老数据进行修改,新增时建议先参考已存在的模板数据。
|
||||||
|
--dimensions 为必填参数,JSON 字符串,包含目标维度、维度配置、目标内容。
|
||||||
|
--objective-weight 启用目标权重
|
||||||
|
--dimension-weight 启用维度权重
|
||||||
|
--compute-by-weight 维度是否参与计算`,
|
||||||
|
Example: ` dws agoal obj-template create-or-update --title "业绩模板" --dimensions '[{"title":"维度1","weight":100}]'
|
||||||
|
dws agoal obj-template create-or-update --template-id TPL_ID --dimensions '[...基于老数据修改...]'`,
|
||||||
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
|
if err := validateRequiredFlags(cmd, "dimensions"); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
toolArgs := map[string]any{}
|
||||||
|
if v, _ := cmd.Flags().GetString("request-id"); v != "" {
|
||||||
|
toolArgs["requestId"] = v
|
||||||
|
}
|
||||||
|
if v, _ := cmd.Flags().GetString("template-id"); v != "" {
|
||||||
|
toolArgs["templateId"] = v
|
||||||
|
}
|
||||||
|
if v, _ := cmd.Flags().GetString("title"); v != "" {
|
||||||
|
toolArgs["title"] = v
|
||||||
|
}
|
||||||
|
if cmd.Flags().Changed("objective-weight") {
|
||||||
|
v, _ := cmd.Flags().GetBool("objective-weight")
|
||||||
|
toolArgs["objectiveWeight"] = v
|
||||||
|
}
|
||||||
|
if cmd.Flags().Changed("dimension-weight") {
|
||||||
|
v, _ := cmd.Flags().GetBool("dimension-weight")
|
||||||
|
toolArgs["dimensionWeight"] = v
|
||||||
|
}
|
||||||
|
if cmd.Flags().Changed("compute-by-weight") {
|
||||||
|
v, _ := cmd.Flags().GetBool("compute-by-weight")
|
||||||
|
toolArgs["computeByWeight"] = v
|
||||||
|
}
|
||||||
|
toolArgs["dimensions"] = mustGetFlag(cmd, "dimensions")
|
||||||
|
return callMCPTool("create_or_update_obj_template", toolArgs)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
objTemplateCreateOrUpdateCmd.Flags().String("request-id", "", "requestId (可选)")
|
||||||
|
objTemplateCreateOrUpdateCmd.Flags().String("template-id", "", "模板 id (更新时必填)")
|
||||||
|
objTemplateCreateOrUpdateCmd.Flags().String("title", "", "模板标题 (新增时必填)")
|
||||||
|
objTemplateCreateOrUpdateCmd.Flags().Bool("objective-weight", false, "是否启用目标权重")
|
||||||
|
objTemplateCreateOrUpdateCmd.Flags().Bool("dimension-weight", false, "是否启用维度权重")
|
||||||
|
objTemplateCreateOrUpdateCmd.Flags().Bool("compute-by-weight", false, "维度是否参与计算")
|
||||||
|
objTemplateCreateOrUpdateCmd.Flags().String("dimensions", "", "模板关联的维度 JSON 字符串 (必填,更新时基于老数据修改,新增时建议参考已有模板)")
|
||||||
|
|
||||||
|
objTemplateCmd.AddCommand(objTemplateListCmd, objTemplateCreateOrUpdateCmd)
|
||||||
|
|
||||||
|
root.AddCommand(strategyCmd, contractCmd, scorecardCmd, userCmd, reportCmd, objTemplateCmd)
|
||||||
|
|
||||||
|
return root
|
||||||
|
}
|
||||||
|
|
||||||
|
// parseISO8601ToMillis 将 ISO-8601 时间字符串解析为毫秒时间戳。
|
||||||
|
// 支持格式:RFC3339(含时区)、无时区(默认 Asia/Shanghai)、仅日期。
|
||||||
|
func parseISO8601ToMillis(value string) (int64, error) {
|
||||||
|
if t, err := time.Parse(time.RFC3339, value); err == nil {
|
||||||
|
return t.UnixMilli(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
shanghaiLoc := shanghaiLocation()
|
||||||
|
|
||||||
|
if t, err := time.ParseInLocation("2006-01-02T15:04:05", value, shanghaiLoc); err == nil {
|
||||||
|
return t.UnixMilli(), nil
|
||||||
|
}
|
||||||
|
if t, err := time.ParseInLocation("2006-01-02 15:04:05", value, shanghaiLoc); err == nil {
|
||||||
|
return t.UnixMilli(), nil
|
||||||
|
}
|
||||||
|
if t, err := time.ParseInLocation("2006-01-02", value, shanghaiLoc); err == nil {
|
||||||
|
return t.UnixMilli(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0, fmt.Errorf("invalid ISO-8601 time format %q, expected e.g. \"2026-01-01T00:00:00+08:00\"", value)
|
||||||
|
}
|
||||||
|
|
||||||
|
func shanghaiLocation() *time.Location {
|
||||||
|
loc, err := time.LoadLocation("Asia/Shanghai")
|
||||||
|
if err != nil {
|
||||||
|
return time.FixedZone("CST", 8*3600)
|
||||||
|
}
|
||||||
|
return loc
|
||||||
|
}
|
||||||
+268
-3
@@ -4741,12 +4741,277 @@ status 可选值:
|
|||||||
chatGroupMembersListByIdsCmd.Flags().String("users", "", "成员 openDingTalkId 列表,逗号分隔 (必填)")
|
chatGroupMembersListByIdsCmd.Flags().String("users", "", "成员 openDingTalkId 列表,逗号分隔 (必填)")
|
||||||
_ = chatGroupMembersListByIdsCmd.MarkFlagRequired("users")
|
_ = chatGroupMembersListByIdsCmd.MarkFlagRequired("users")
|
||||||
|
|
||||||
chatGroupCmd.AddCommand(chatGroupBotsCmd, chatGroupDismissCmd, chatGroupSetHistoryCmd, chatGroupListMyGroupsCmd, chatGroupUpdateNickCmd, chatGroupUpdateAliasCmd, chatGroupListAllCmd, chatGroupListJoinValidationsCmd, chatGroupAuditJoinValidationCmd)
|
// ── group notice: 群公告管理 ────────────────────────────────
|
||||||
|
chatGroupNoticeCmd := &cobra.Command{Use: "notice", Short: "群公告管理", RunE: groupRunE}
|
||||||
|
|
||||||
|
chatGroupNoticeCreateCmd := &cobra.Command{
|
||||||
|
Use: "create",
|
||||||
|
Short: "发布群公告",
|
||||||
|
Long: `在指定群聊中发布群公告,正文为 Markdown 格式。
|
||||||
|
支持标题、加粗、斜体、删除线、行内代码、链接、代码块、有序/无序/任务列表、表格、引用、分割线、图片、段落、换行。
|
||||||
|
定时发布:传 --run-at 指定执行时间点,ISO-8601 格式(建议带时区偏移,不带时按北京时区处理)。`,
|
||||||
|
Example: ` dws chat group notice create --group <openConversationId> --content "今晚 22 点系统维护,请提前保存工作内容"
|
||||||
|
dws chat group notice create --group <openConversationId> --content "# 重要通知\n请大家查收" --sticky --send-ding
|
||||||
|
dws chat group notice create --group <openConversationId> --content "明早九点例会" --run-at "2026-07-03T09:00:00+08:00"
|
||||||
|
# 查询群 ID: dws chat search --query "群名"`,
|
||||||
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
|
if err := validateRequiredFlags(cmd, "group", "content"); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
toolArgs := map[string]any{
|
||||||
|
"openConversationId": mustGetFlag(cmd, "group"),
|
||||||
|
"content": mustGetFlag(cmd, "content"),
|
||||||
|
}
|
||||||
|
if v, _ := cmd.Flags().GetBool("sticky"); v {
|
||||||
|
toolArgs["sticky"] = true
|
||||||
|
}
|
||||||
|
if v, _ := cmd.Flags().GetBool("send-ding"); v {
|
||||||
|
toolArgs["sendDing"] = true
|
||||||
|
}
|
||||||
|
if v, _ := cmd.Flags().GetString("run-at"); v != "" {
|
||||||
|
toolArgs["scheduled"] = true
|
||||||
|
toolArgs["runAtText"] = v
|
||||||
|
}
|
||||||
|
return callMCPToolOnServer("im", "create_group_notice", toolArgs)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
chatGroupNoticeCreateCmd.Flags().String("group", "", "群聊 openConversationId (必填)")
|
||||||
|
_ = chatGroupNoticeCreateCmd.MarkFlagRequired("group")
|
||||||
|
chatGroupNoticeCreateCmd.Flags().String("content", "", "公告正文,Markdown 格式 (必填)")
|
||||||
|
_ = chatGroupNoticeCreateCmd.MarkFlagRequired("content")
|
||||||
|
chatGroupNoticeCreateCmd.Flags().Bool("sticky", false, "是否吊顶置顶(默认 false)")
|
||||||
|
chatGroupNoticeCreateCmd.Flags().Bool("send-ding", false, "是否发 DING 提醒(默认 false)")
|
||||||
|
chatGroupNoticeCreateCmd.Flags().String("run-at", "", "定时发布时间 ISO-8601(如 2026-07-03T09:00:00+08:00,传入则定时发布)")
|
||||||
|
|
||||||
|
chatGroupNoticeEditCmd := &cobra.Command{
|
||||||
|
Use: "edit",
|
||||||
|
Short: "修改群公告",
|
||||||
|
Long: `修改指定群聊中的群公告,正文为 Markdown 格式,会整体替换原公告内容。`,
|
||||||
|
Example: ` dws chat group notice edit --group <openConversationId> --notice-id <dataId> --content "更新后的公告内容"
|
||||||
|
dws chat group notice edit --group <openConversationId> --notice-id <dataId> --content "更新后的公告内容" --sticky --send-ding
|
||||||
|
# 查询公告 ID: dws chat group notice list --group <openConversationId>`,
|
||||||
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
|
if err := validateRequiredFlags(cmd, "group", "notice-id", "content"); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
toolArgs := map[string]any{
|
||||||
|
"openConversationId": mustGetFlag(cmd, "group"),
|
||||||
|
"dataId": mustGetFlag(cmd, "notice-id"),
|
||||||
|
"content": mustGetFlag(cmd, "content"),
|
||||||
|
}
|
||||||
|
if v, _ := cmd.Flags().GetBool("sticky"); v {
|
||||||
|
toolArgs["sticky"] = true
|
||||||
|
}
|
||||||
|
if v, _ := cmd.Flags().GetBool("send-ding"); v {
|
||||||
|
toolArgs["sendDing"] = true
|
||||||
|
}
|
||||||
|
return callMCPToolOnServer("im", "edit_group_notice", toolArgs)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
chatGroupNoticeEditCmd.Flags().String("group", "", "群聊 openConversationId (必填)")
|
||||||
|
_ = chatGroupNoticeEditCmd.MarkFlagRequired("group")
|
||||||
|
chatGroupNoticeEditCmd.Flags().String("notice-id", "", "群公告 dataId (必填)")
|
||||||
|
_ = chatGroupNoticeEditCmd.MarkFlagRequired("notice-id")
|
||||||
|
chatGroupNoticeEditCmd.Flags().String("content", "", "公告新正文,Markdown 格式 (必填)")
|
||||||
|
_ = chatGroupNoticeEditCmd.MarkFlagRequired("content")
|
||||||
|
chatGroupNoticeEditCmd.Flags().Bool("sticky", false, "是否吊顶置顶(不传按 false 处理)")
|
||||||
|
chatGroupNoticeEditCmd.Flags().Bool("send-ding", false, "是否发 DING 提醒(默认 false)")
|
||||||
|
|
||||||
|
chatGroupNoticeGetCmd := &cobra.Command{
|
||||||
|
Use: "get",
|
||||||
|
Short: "查看群公告详情",
|
||||||
|
Long: `查看指定群公告的详情,包含正文摘要、吊顶状态、发布者、已读人数、点赞/评论数等信息。`,
|
||||||
|
Example: ` dws chat group notice get --group <openConversationId> --notice-id <dataId>
|
||||||
|
# 查询公告 ID: dws chat group notice list --group <openConversationId>`,
|
||||||
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
|
if err := validateRequiredFlags(cmd, "group", "notice-id"); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return callMCPToolOnServer("im", "get_group_notice", map[string]any{
|
||||||
|
"openConversationId": mustGetFlag(cmd, "group"),
|
||||||
|
"dataId": mustGetFlag(cmd, "notice-id"),
|
||||||
|
})
|
||||||
|
},
|
||||||
|
}
|
||||||
|
chatGroupNoticeGetCmd.Flags().String("group", "", "群聊 openConversationId (必填)")
|
||||||
|
_ = chatGroupNoticeGetCmd.MarkFlagRequired("group")
|
||||||
|
chatGroupNoticeGetCmd.Flags().String("notice-id", "", "群公告 dataId (必填)")
|
||||||
|
_ = chatGroupNoticeGetCmd.MarkFlagRequired("notice-id")
|
||||||
|
|
||||||
|
chatGroupNoticeListCmd := &cobra.Command{
|
||||||
|
Use: "list",
|
||||||
|
Short: "查看群公告列表",
|
||||||
|
Long: `分页查看指定群聊的群公告列表。默认查询已发布公告,传 --scheduled 查询定时公告列表。
|
||||||
|
支持游标分页,hasMore=true 时用返回的 nextPageCursor 作为下次 --cursor。`,
|
||||||
|
Example: ` dws chat group notice list --group <openConversationId>
|
||||||
|
dws chat group notice list --group <openConversationId> --limit 20 --cursor <nextPageCursor>
|
||||||
|
dws chat group notice list --group <openConversationId> --scheduled
|
||||||
|
# 查询群 ID: dws chat search --query "群名"`,
|
||||||
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
|
if err := validateRequiredFlags(cmd, "group"); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
toolArgs := map[string]any{
|
||||||
|
"openConversationId": mustGetFlag(cmd, "group"),
|
||||||
|
}
|
||||||
|
if v, _ := cmd.Flags().GetInt("limit"); v > 0 {
|
||||||
|
toolArgs["limit"] = v
|
||||||
|
}
|
||||||
|
if v, _ := cmd.Flags().GetString("cursor"); v != "" {
|
||||||
|
toolArgs["cursor"] = v
|
||||||
|
}
|
||||||
|
if v, _ := cmd.Flags().GetBool("scheduled"); v {
|
||||||
|
toolArgs["scheduled"] = true
|
||||||
|
}
|
||||||
|
return callMCPToolOnServer("im", "list_group_notices", toolArgs)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
chatGroupNoticeListCmd.Flags().String("group", "", "群聊 openConversationId (必填)")
|
||||||
|
_ = chatGroupNoticeListCmd.MarkFlagRequired("group")
|
||||||
|
chatGroupNoticeListCmd.Flags().Int("limit", 10, "每页返回数量(默认 10,最大 100)")
|
||||||
|
chatGroupNoticeListCmd.Flags().String("cursor", "", "分页游标(首次不传,翻页传返回的 nextPageCursor)")
|
||||||
|
chatGroupNoticeListCmd.Flags().Bool("scheduled", false, "是否查询定时公告列表(默认 false,查询已发布公告)")
|
||||||
|
chatGroupNoticeCmd.AddCommand(chatGroupNoticeCreateCmd, chatGroupNoticeEditCmd, chatGroupNoticeGetCmd, chatGroupNoticeListCmd)
|
||||||
|
|
||||||
|
chatGroupShareInviteCmd := &cobra.Command{
|
||||||
|
Use: "share-invite",
|
||||||
|
Short: "分享群聊链接到会话",
|
||||||
|
Long: `将指定群的邀请链接分享到另一个会话或单聊用户。--target 和 --receiver 二选一:--target 指定目标会话,--receiver 指定单聊用户。`,
|
||||||
|
Example: ` dws chat group share-invite --source <被分享群openConversationId> --target <目标会话openConversationId>
|
||||||
|
dws chat group share-invite --source <被分享群openConversationId> --receiver <接收者openDingTalkId>
|
||||||
|
dws chat group share-invite --source <openConversationId> --target <openConversationId> --expires-seconds 86400
|
||||||
|
# 查询群 ID: dws chat search --query "群名"`,
|
||||||
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
|
if err := validateRequiredFlags(cmd, "source"); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
target, _ := cmd.Flags().GetString("target")
|
||||||
|
receiver, _ := cmd.Flags().GetString("receiver")
|
||||||
|
if target == "" && receiver == "" {
|
||||||
|
return fmt.Errorf("--target or --receiver is required")
|
||||||
|
}
|
||||||
|
if target != "" && receiver != "" {
|
||||||
|
return fmt.Errorf("--target and --receiver are mutually exclusive")
|
||||||
|
}
|
||||||
|
toolArgs := map[string]any{
|
||||||
|
"sourceOpenConversationId": mustGetFlag(cmd, "source"),
|
||||||
|
}
|
||||||
|
if target != "" {
|
||||||
|
toolArgs["targetOpenConversationId"] = target
|
||||||
|
}
|
||||||
|
if receiver != "" {
|
||||||
|
toolArgs["receiverOpenDingTalkId"] = receiver
|
||||||
|
}
|
||||||
|
if v, _ := cmd.Flags().GetInt64("expires-seconds"); v > 0 || cmd.Flags().Changed("expires-seconds") {
|
||||||
|
toolArgs["expiresSeconds"] = v
|
||||||
|
}
|
||||||
|
if v, _ := cmd.Flags().GetString("uuid"); v != "" {
|
||||||
|
toolArgs["uuid"] = v
|
||||||
|
}
|
||||||
|
return callMCPToolOnServer("im", "share_group_invite_url", toolArgs)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
chatGroupShareInviteCmd.Flags().String("source", "", "被分享群的 openConversationId (必填)")
|
||||||
|
_ = chatGroupShareInviteCmd.MarkFlagRequired("source")
|
||||||
|
chatGroupShareInviteCmd.Flags().String("target", "", "接收分享消息的会话 openConversationId(与 --receiver 二选一)")
|
||||||
|
chatGroupShareInviteCmd.Flags().String("receiver", "", "接收分享消息的单聊用户 openDingTalkId(与 --target 二选一)")
|
||||||
|
chatGroupShareInviteCmd.Flags().Int64("expires-seconds", 0, "链接有效期(秒),0 表示永久有效,不传使用服务端默认值")
|
||||||
|
chatGroupShareInviteCmd.Flags().String("uuid", "", "消息幂等键(可选)")
|
||||||
|
|
||||||
|
chatCategoryCreateSmartCmd := &cobra.Command{
|
||||||
|
Use: "create-smart",
|
||||||
|
Short: "创建智能会话分组",
|
||||||
|
Long: `创建智能会话分组,可指定群名称关键词和群内成员 openDingTalkId 作为匹配规则。`,
|
||||||
|
Example: ` dws chat category create-smart --name "工作群"
|
||||||
|
dws chat category create-smart --name "项目组" --keywords "项目,开发"
|
||||||
|
dws chat category create-smart --name "团队群" --members openDingTalkId1,openDingTalkId2
|
||||||
|
dws chat category create-smart --name "重点群" --keywords "重点" --members openDingTalkId1`,
|
||||||
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
|
if err := validateRequiredFlags(cmd, "name"); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
toolArgs := map[string]any{
|
||||||
|
"title": mustGetFlag(cmd, "name"),
|
||||||
|
}
|
||||||
|
if v, _ := cmd.Flags().GetString("keywords"); v != "" {
|
||||||
|
toolArgs["keywords"] = parseCSVValues(v)
|
||||||
|
}
|
||||||
|
if v, _ := cmd.Flags().GetString("members"); v != "" {
|
||||||
|
toolArgs["memberOpenDingTalkIds"] = parseCSVValues(v)
|
||||||
|
}
|
||||||
|
return callMCPToolOnServer("im", "create_smart_conv_category", toolArgs)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
chatCategoryCreateSmartCmd.Flags().String("name", "", "分组名称 (必填)")
|
||||||
|
_ = chatCategoryCreateSmartCmd.MarkFlagRequired("name")
|
||||||
|
chatCategoryCreateSmartCmd.Flags().String("keywords", "", "群名称关键词列表,逗号分隔(可选)")
|
||||||
|
chatCategoryCreateSmartCmd.Flags().String("members", "", "群内成员 openDingTalkId 列表,逗号分隔(可选)")
|
||||||
|
|
||||||
|
chatMessageListEmotionRepliesCmd := &cobra.Command{
|
||||||
|
Use: "list-emotion-replies",
|
||||||
|
Short: "批量拉取消息的表情回复和文字回复",
|
||||||
|
Example: ` dws chat message list-emotion-replies --msg-ids msgId1,msgId2,msgId3
|
||||||
|
# 消息 ID 可通过 dws chat message list 获取`,
|
||||||
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
|
if err := validateRequiredFlags(cmd, "msg-ids"); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
msgIds := parseCSVValues(mustGetFlag(cmd, "msg-ids"))
|
||||||
|
return callMCPToolOnServer("im", "list_message_emotion_replies", map[string]any{
|
||||||
|
"openMessageIds": msgIds,
|
||||||
|
})
|
||||||
|
},
|
||||||
|
}
|
||||||
|
chatMessageListEmotionRepliesCmd.Flags().String("msg-ids", "", "消息 ID 列表,逗号分隔 (必填)")
|
||||||
|
_ = chatMessageListEmotionRepliesCmd.MarkFlagRequired("msg-ids")
|
||||||
|
|
||||||
|
supportedTranslateLanguages := map[string]bool{
|
||||||
|
"en_US": true, "zh_CN": true, "zh_TW": true, "zh_HK": true,
|
||||||
|
"ja_JP": true, "ko_KR": true, "vi_VN": true, "th_TH": true,
|
||||||
|
"id_ID": true, "ms_MY": true, "es_419": true, "fr_FR": true,
|
||||||
|
"pt_BR": true, "tr_TR": true, "ru_RU": true, "de_DE": true,
|
||||||
|
"hi_IN": true, "hu_HU": true, "pl_PL": true, "sv_SE": true,
|
||||||
|
"fi_FI": true, "cs_CZ": true, "ar_SA": true, "tl_PH": true,
|
||||||
|
"he_IL": true, "nl_NL": true, "lo_LA": true, "it_IT": true,
|
||||||
|
}
|
||||||
|
chatTextCmd := &cobra.Command{Use: "text", Short: "文本内容处理", RunE: groupRunE}
|
||||||
|
chatTextTranslateCmd := &cobra.Command{
|
||||||
|
Use: "translate",
|
||||||
|
Short: "翻译文本内容",
|
||||||
|
Long: `将指定文本翻译成目标语言。
|
||||||
|
支持的目标语言代码: en_US, zh_CN, zh_TW, zh_HK, ja_JP, ko_KR, vi_VN, th_TH,
|
||||||
|
id_ID, ms_MY, es_419, fr_FR, pt_BR, tr_TR, ru_RU, de_DE, hi_IN, hu_HU,
|
||||||
|
pl_PL, sv_SE, fi_FI, cs_CZ, ar_SA, tl_PH, he_IL, nl_NL, lo_LA, it_IT`,
|
||||||
|
Example: ` dws chat text translate --query "你好世界" --to en_US
|
||||||
|
dws chat text translate --query "Hello World" --to zh_CN
|
||||||
|
dws chat text translate --query "Bonjour" --to ja_JP`,
|
||||||
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
|
if err := validateRequiredFlags(cmd, "query", "to"); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
toLang := mustGetFlag(cmd, "to")
|
||||||
|
if !supportedTranslateLanguages[toLang] {
|
||||||
|
return fmt.Errorf("unsupported target language: %s", toLang)
|
||||||
|
}
|
||||||
|
return callMCPToolOnServer("im", "translate", map[string]any{
|
||||||
|
"query": mustGetFlag(cmd, "query"),
|
||||||
|
"to": toLang,
|
||||||
|
})
|
||||||
|
},
|
||||||
|
}
|
||||||
|
chatTextTranslateCmd.Flags().String("query", "", "待翻译的文本内容 (必填)")
|
||||||
|
_ = chatTextTranslateCmd.MarkFlagRequired("query")
|
||||||
|
chatTextTranslateCmd.Flags().String("to", "en_US", "目标语言代码 (必填,默认 en_US)")
|
||||||
|
_ = chatTextTranslateCmd.MarkFlagRequired("to")
|
||||||
|
chatTextCmd.AddCommand(chatTextTranslateCmd)
|
||||||
|
|
||||||
|
chatGroupCmd.AddCommand(chatGroupBotsCmd, chatGroupDismissCmd, chatGroupSetHistoryCmd, chatGroupListMyGroupsCmd, chatGroupUpdateNickCmd, chatGroupUpdateAliasCmd, chatGroupListAllCmd, chatGroupListJoinValidationsCmd, chatGroupAuditJoinValidationCmd, chatGroupNoticeCmd, chatGroupShareInviteCmd)
|
||||||
chatGroupMembersCmd.AddCommand(chatGroupMembersRemoveBotCmd, chatGroupMembersListByIdsCmd)
|
chatGroupMembersCmd.AddCommand(chatGroupMembersRemoveBotCmd, chatGroupMembersListByIdsCmd)
|
||||||
chatBotCmd.AddCommand(chatBotFindCmd)
|
chatBotCmd.AddCommand(chatBotFindCmd)
|
||||||
chatMessageCmd.AddCommand(chatMessageListDirectCmd, chatMessageSearchCommonCmd, chatMessageCombineForwardCmd, chatMessageForwardTopicCmd, chatMessageSetPinCmd, chatMessageUnsetPinCmd, chatMessageListPinCmd, chatMessageSetTopMsgCmd, chatMessageUnsetTopMsgCmd)
|
chatCategoryCmd.AddCommand(chatCategoryCreateSmartCmd)
|
||||||
|
chatMessageCmd.AddCommand(chatMessageListDirectCmd, chatMessageSearchCommonCmd, chatMessageCombineForwardCmd, chatMessageForwardTopicCmd, chatMessageSetPinCmd, chatMessageUnsetPinCmd, chatMessageListPinCmd, chatMessageSetTopMsgCmd, chatMessageUnsetTopMsgCmd, chatMessageListEmotionRepliesCmd)
|
||||||
|
|
||||||
root.AddCommand(chatChmodCmd, chatDataAuthCmd, chatGroupCmd, chatSearchCmd, chatSearchCommonCmd, chatMessageCmd, chatFileCmd, newChatMediaGroup(), chatBotCmd, chatMessageListTopConversationsCmd, chatConversationInfoCmd, chatCategoryCmd, chatGroupRoleCmd, chatMuteCmd, chatSetTopCmd, chatGroupMuteCmd, chatGroupMuteMemberCmd, chatHideCmd, chatMuteAtAllCmd, chatMuteRedEnvelopeCmd, chatMarkUnreadCmd, chatClearRedPointCmd, chatClearAllRedPointCmd, chatListAllConversationsCmd, chatClearMessagesCmd, chatMarkReadCmd)
|
root.AddCommand(chatChmodCmd, chatDataAuthCmd, chatGroupCmd, chatSearchCmd, chatSearchCommonCmd, chatMessageCmd, chatFileCmd, newChatMediaGroup(), chatBotCmd, chatMessageListTopConversationsCmd, chatConversationInfoCmd, chatCategoryCmd, chatGroupRoleCmd, chatMuteCmd, chatSetTopCmd, chatGroupMuteCmd, chatGroupMuteMemberCmd, chatHideCmd, chatMuteAtAllCmd, chatMuteRedEnvelopeCmd, chatMarkUnreadCmd, chatClearRedPointCmd, chatClearAllRedPointCmd, chatListAllConversationsCmd, chatClearMessagesCmd, chatMarkReadCmd, chatTextCmd)
|
||||||
|
|
||||||
// hint: dws chat send → dws chat message send
|
// hint: dws chat send → dws chat message send
|
||||||
root.AddCommand(hintSubCmd("send", "use: dws chat message send"))
|
root.AddCommand(hintSubCmd("send", "use: dws chat message send"))
|
||||||
|
|||||||
+289
-3
@@ -787,7 +787,7 @@ func newDocCommand() *cobra.Command {
|
|||||||
root := &cobra.Command{
|
root := &cobra.Command{
|
||||||
Use: "doc",
|
Use: "doc",
|
||||||
Short: "钉钉文档管理",
|
Short: "钉钉文档管理",
|
||||||
Long: `管理钉钉文档:浏览、读写、块级编辑、导出、模板管理。
|
Long: `管理钉钉文档:浏览、读写、块级编辑、导出、导入、模板管理。
|
||||||
|
|
||||||
命令结构:
|
命令结构:
|
||||||
dws doc info 获取文档元信息
|
dws doc info 获取文档元信息
|
||||||
@@ -798,6 +798,8 @@ func newDocCommand() *cobra.Command {
|
|||||||
dws doc comment [list|create|delete] 文档评论管理
|
dws doc comment [list|create|delete] 文档评论管理
|
||||||
dws doc export 导出在线文档 (支持 docx / markdown / pdf,自动完成提交→轮询→下载)
|
dws doc export 导出在线文档 (支持 docx / markdown / pdf,自动完成提交→轮询→下载)
|
||||||
dws doc export get 查询导出任务结果 (手动兜底)
|
dws doc export get 查询导出任务结果 (手动兜底)
|
||||||
|
dws doc import 导入本地文件为在线文档 (支持 docx / xlsx / md 等)
|
||||||
|
dws doc import get 查询导入任务结果 (手动兜底)
|
||||||
dws doc template list 获取文档模板列表
|
dws doc template list 获取文档模板列表
|
||||||
dws doc template search 搜索文档模板
|
dws doc template search 搜索文档模板
|
||||||
dws doc template apply 应用文档模板创建新文档
|
dws doc template apply 应用文档模板创建新文档
|
||||||
@@ -2446,6 +2448,233 @@ CLI 内部自动完成全部流程:
|
|||||||
|
|
||||||
exportCmd.AddCommand(exportGetCmd)
|
exportCmd.AddCommand(exportGetCmd)
|
||||||
|
|
||||||
|
// ── import: 文件导入为在线文档(一体化:上传→转换→轮询)──────────────
|
||||||
|
importCmd := &cobra.Command{
|
||||||
|
Use: "import",
|
||||||
|
Short: "导入本地文件为在线文档 (支持 docx / xlsx / md 等)",
|
||||||
|
Long: `将本地文件导入为钉钉在线文档。
|
||||||
|
|
||||||
|
支持的文件格式 (按扩展名):
|
||||||
|
docx, doc → 文字文档
|
||||||
|
xlsx, xls → 电子表格
|
||||||
|
md, txt → 文字文档
|
||||||
|
xmind, mark → 脑图
|
||||||
|
|
||||||
|
文件大小限制: 20MB
|
||||||
|
|
||||||
|
CLI 内部自动完成全部流程:
|
||||||
|
1. 创建导入会话(获取 OSS 上传凭证)
|
||||||
|
2. 上传文件到 OSS
|
||||||
|
3. 确认导入(触发格式转换)
|
||||||
|
4. 渐进式退避轮询等待完成(最多约 5 分钟)
|
||||||
|
|
||||||
|
如果轮询超时仍未完成,会输出 taskId 供后续手动查询:
|
||||||
|
dws doc import get --task-id <taskId>`,
|
||||||
|
Example: ` # 导入 Word 文档
|
||||||
|
dws doc import --file ./report.docx
|
||||||
|
|
||||||
|
# 导入到指定文件夹
|
||||||
|
dws doc import --file ./notes.md --folder <FOLDER_ID>
|
||||||
|
|
||||||
|
# 导入到知识库根目录
|
||||||
|
dws doc import --file ./data.xlsx --workspace <WORKSPACE_ID>
|
||||||
|
|
||||||
|
# 自定义导入后的文档名称
|
||||||
|
dws doc import --file ./draft.md --name "项目周报"`,
|
||||||
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
|
filePath := mustGetFlag(cmd, "file")
|
||||||
|
if filePath == "" && len(args) > 0 {
|
||||||
|
filePath = args[0]
|
||||||
|
}
|
||||||
|
if filePath == "" {
|
||||||
|
return fmt.Errorf("flag --file is required (or pass file path as argument)")
|
||||||
|
}
|
||||||
|
|
||||||
|
fi, err := os.Stat(filePath)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("cannot read file %s: %w", filePath, err)
|
||||||
|
}
|
||||||
|
if fi.IsDir() {
|
||||||
|
return fmt.Errorf("%s is a directory, not a file", filePath)
|
||||||
|
}
|
||||||
|
|
||||||
|
const maxFileSize = 20 * 1024 * 1024
|
||||||
|
fileSize := fi.Size()
|
||||||
|
if fileSize > maxFileSize {
|
||||||
|
return fmt.Errorf("file size %d bytes exceeds 20MB limit", fileSize)
|
||||||
|
}
|
||||||
|
if fileSize == 0 {
|
||||||
|
return fmt.Errorf("file is empty: %s", filePath)
|
||||||
|
}
|
||||||
|
|
||||||
|
ext := strings.TrimPrefix(strings.ToLower(filepath.Ext(filePath)), ".")
|
||||||
|
supportedFormats := map[string]bool{
|
||||||
|
"docx": true, "doc": true, "xlsx": true, "xls": true,
|
||||||
|
"md": true, "txt": true, "xmind": true, "mark": true,
|
||||||
|
}
|
||||||
|
if !supportedFormats[ext] {
|
||||||
|
return fmt.Errorf("unsupported file format %q, supported: docx, doc, xlsx, xls, md, txt, xmind, mark", ext)
|
||||||
|
}
|
||||||
|
|
||||||
|
fileName := filepath.Base(filePath)
|
||||||
|
name, _ := cmd.Flags().GetString("name")
|
||||||
|
if name == "" {
|
||||||
|
name = strings.TrimSuffix(fileName, filepath.Ext(fileName))
|
||||||
|
}
|
||||||
|
folder := flagOrFallback(cmd, "folder", "folder-id")
|
||||||
|
workspace := flagOrFallback(cmd, "workspace", "workspace-id")
|
||||||
|
|
||||||
|
if deps.Caller.DryRun() {
|
||||||
|
deps.Out.PrintKeyValue("操作", "导入本地文件为在线文档")
|
||||||
|
deps.Out.PrintKeyValue("文件", filePath)
|
||||||
|
deps.Out.PrintKeyValue("名称", name)
|
||||||
|
deps.Out.PrintKeyValue("格式", ext)
|
||||||
|
deps.Out.PrintKeyValue("大小", fmt.Sprintf("%d bytes", fileSize))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx := context.Background()
|
||||||
|
|
||||||
|
deps.Out.PrintInfo("[1/4] 创建导入会话...")
|
||||||
|
sessionArgs := map[string]any{
|
||||||
|
"fileName": name,
|
||||||
|
"suffix": ext,
|
||||||
|
"fileSize": fileSize,
|
||||||
|
}
|
||||||
|
if folder != "" {
|
||||||
|
sessionArgs["targetFolderId"] = folder
|
||||||
|
}
|
||||||
|
if workspace != "" {
|
||||||
|
sessionArgs["workspaceId"] = workspace
|
||||||
|
}
|
||||||
|
|
||||||
|
sessionText, err := callMCPToolReturnText(ctx, "create_import_session", sessionArgs)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("创建导入会话失败: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var sessionResult map[string]any
|
||||||
|
if err := json.Unmarshal([]byte(sessionText), &sessionResult); err != nil {
|
||||||
|
return fmt.Errorf("解析导入会话响应失败: %w", err)
|
||||||
|
}
|
||||||
|
sessionID, _ := sessionResult["sessionId"].(string)
|
||||||
|
uploadURL, _ := sessionResult["uploadUrl"].(string)
|
||||||
|
if sessionID == "" || uploadURL == "" {
|
||||||
|
deps.Out.PrintRaw(sessionText)
|
||||||
|
return fmt.Errorf("创建导入会话成功但缺少 sessionId 或 uploadUrl")
|
||||||
|
}
|
||||||
|
deps.Out.PrintInfo(fmt.Sprintf(" 会话已创建,sessionId: %s", sessionID))
|
||||||
|
|
||||||
|
deps.Out.PrintInfo("[2/4] 上传文件...")
|
||||||
|
if err := httpPutFile(ctx, uploadURL, nil, filePath, fileSize); err != nil {
|
||||||
|
return fmt.Errorf("文件上传失败 (sessionId=%s): %w", sessionID, err)
|
||||||
|
}
|
||||||
|
deps.Out.PrintInfo(" 文件上传完成")
|
||||||
|
|
||||||
|
deps.Out.PrintInfo("[3/4] 确认导入,启动格式转换...")
|
||||||
|
confirmText, err := callMCPToolReturnText(ctx, "confirm_import", map[string]any{
|
||||||
|
"sessionId": sessionID,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("确认导入失败 (sessionId=%s): %w", sessionID, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var confirmResult map[string]any
|
||||||
|
if err := json.Unmarshal([]byte(confirmText), &confirmResult); err != nil {
|
||||||
|
return fmt.Errorf("解析确认导入响应失败: %w", err)
|
||||||
|
}
|
||||||
|
taskID, _ := confirmResult["taskId"].(string)
|
||||||
|
if taskID == "" {
|
||||||
|
deps.Out.PrintRaw(confirmText)
|
||||||
|
return fmt.Errorf("确认导入成功但未返回 taskId")
|
||||||
|
}
|
||||||
|
deps.Out.PrintInfo(fmt.Sprintf(" 转换任务已提交,taskId: %s", taskID))
|
||||||
|
|
||||||
|
deps.Out.PrintInfo("[4/4] 等待格式转换完成...")
|
||||||
|
importResult, err := pollDocImportTask(ctx, taskID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
documentURL, _ := importResult["documentUrl"].(string)
|
||||||
|
documentName, _ := importResult["documentName"].(string)
|
||||||
|
documentType, _ := importResult["documentType"].(string)
|
||||||
|
|
||||||
|
deps.Out.PrintInfo(fmt.Sprintf("导入完成: %s", documentURL))
|
||||||
|
deps.Out.PrintJSON(map[string]any{
|
||||||
|
"success": true,
|
||||||
|
"taskId": taskID,
|
||||||
|
"documentUrl": documentURL,
|
||||||
|
"documentName": documentName,
|
||||||
|
"documentType": documentType,
|
||||||
|
})
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
}
|
||||||
|
importCmd.Flags().String("file", "", "本地文件路径 (必填)")
|
||||||
|
importCmd.Flags().String("folder", "", "目标文件夹 ID 或 URL (可选,与 --workspace 至少传一个)")
|
||||||
|
importCmd.Flags().String("workspace", "", "目标知识库 ID 或 URL (可选,与 --folder 至少传一个)")
|
||||||
|
importCmd.Flags().StringP("name", "n", "", "导入后文档名称 (可选,默认取文件名)")
|
||||||
|
importCmd.Flags().String("folder-id", "", "")
|
||||||
|
_ = importCmd.Flags().MarkHidden("folder-id")
|
||||||
|
importCmd.Flags().String("workspace-id", "", "")
|
||||||
|
_ = importCmd.Flags().MarkHidden("workspace-id")
|
||||||
|
|
||||||
|
importGetCmd := &cobra.Command{
|
||||||
|
Use: "get",
|
||||||
|
Short: "查询导入任务结果(手动兜底)",
|
||||||
|
Long: `根据 taskId 查询文档导入任务的执行结果。
|
||||||
|
通常不需要手动调用,dws doc import 会自动完成轮询。
|
||||||
|
仅在导入命令超时或中断后,用于手动查询任务状态。
|
||||||
|
|
||||||
|
任务状态:
|
||||||
|
processing 转换中
|
||||||
|
completed 导入成功,返回 documentUrl
|
||||||
|
failed 导入失败`,
|
||||||
|
Example: ` dws doc import get --task-id <TASK_ID>`,
|
||||||
|
RunE: func(cmd *cobra.Command, _ []string) error {
|
||||||
|
taskID := mustGetFlag(cmd, "task-id")
|
||||||
|
if taskID == "" {
|
||||||
|
return fmt.Errorf("flag --task-id is required")
|
||||||
|
}
|
||||||
|
|
||||||
|
if deps.Caller.DryRun() {
|
||||||
|
deps.Out.PrintKeyValue("操作", "查询导入任务结果")
|
||||||
|
deps.Out.PrintKeyValue("任务ID", taskID)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx := context.Background()
|
||||||
|
text, err := callMCPToolReturnText(ctx, "query_import_task", map[string]any{"taskId": taskID})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
var result map[string]any
|
||||||
|
if err := json.Unmarshal([]byte(text), &result); err != nil {
|
||||||
|
deps.Out.PrintRaw(text)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
status, _ := result["status"].(string)
|
||||||
|
message, _ := result["message"].(string)
|
||||||
|
|
||||||
|
switch strings.ToLower(status) {
|
||||||
|
case "completed", "processing":
|
||||||
|
deps.Out.PrintJSON(result)
|
||||||
|
return nil
|
||||||
|
default:
|
||||||
|
deps.Out.PrintJSON(result)
|
||||||
|
if message != "" {
|
||||||
|
return fmt.Errorf("导入任务失败 (status=%s): %s", status, message)
|
||||||
|
}
|
||||||
|
return fmt.Errorf("导入任务失败 (status=%s)", status)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
importGetCmd.Flags().String("task-id", "", "导入任务 ID (必填)")
|
||||||
|
importCmd.AddCommand(importGetCmd)
|
||||||
|
|
||||||
// ── doc version 子命令组 ──
|
// ── doc version 子命令组 ──
|
||||||
versionCmd := &cobra.Command{
|
versionCmd := &cobra.Command{
|
||||||
Use: "version",
|
Use: "version",
|
||||||
@@ -2658,7 +2887,7 @@ CLI 内部自动完成全部流程:
|
|||||||
// will skip flags that already exist.
|
// will skip flags that already exist.
|
||||||
for _, cmd := range []*cobra.Command{
|
for _, cmd := range []*cobra.Command{
|
||||||
searchCmd, listCmd, createCmd, updateCmd, uploadCmd, downloadCmd,
|
searchCmd, listCmd, createCmd, updateCmd, uploadCmd, downloadCmd,
|
||||||
copyCmd, moveCmd, renameCmd, deleteCmd, exportCmd,
|
copyCmd, moveCmd, renameCmd, deleteCmd, exportCmd, importCmd,
|
||||||
} {
|
} {
|
||||||
RegisterCrossProductAliases(cmd)
|
RegisterCrossProductAliases(cmd)
|
||||||
}
|
}
|
||||||
@@ -2700,7 +2929,7 @@ CLI 内部自动完成全部流程:
|
|||||||
folderCmd.Hidden = true
|
folderCmd.Hidden = true
|
||||||
permissionCmd.Hidden = true
|
permissionCmd.Hidden = true
|
||||||
|
|
||||||
root.AddCommand(searchCmd, listCmd, infoCmd, readCmd, createCmd, updateCmd, uploadCmd, downloadCmd, copyCmd, moveCmd, renameCmd, deleteCmd, fileCmd, folderCmd, blockCmd, commentCmd, mediaCmd, permissionCmd, exportCmd, versionCmd, templateCmd)
|
root.AddCommand(searchCmd, listCmd, infoCmd, readCmd, createCmd, updateCmd, uploadCmd, downloadCmd, copyCmd, moveCmd, renameCmd, deleteCmd, fileCmd, folderCmd, blockCmd, commentCmd, mediaCmd, permissionCmd, exportCmd, importCmd, versionCmd, templateCmd)
|
||||||
|
|
||||||
return root
|
return root
|
||||||
}
|
}
|
||||||
@@ -2947,6 +3176,63 @@ func pollDocExportJob(ctx context.Context, jobID string) (downloadURL string, er
|
|||||||
return "", fmt.Errorf("导出任务超时:已轮询 %d 次仍在处理中 (jobId=%s),请稍后使用 dws doc export get --job-id %s 手动查询", maxPolls, jobID, jobID)
|
return "", fmt.Errorf("导出任务超时:已轮询 %d 次仍在处理中 (jobId=%s),请稍后使用 dws doc export get --job-id %s 手动查询", maxPolls, jobID, jobID)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// pollDocImportTask polls the import task status with progressive backoff.
|
||||||
|
func pollDocImportTask(ctx context.Context, taskID string) (map[string]any, error) {
|
||||||
|
const maxPolls = 30
|
||||||
|
|
||||||
|
pollInterval := func(attempt int) time.Duration {
|
||||||
|
switch {
|
||||||
|
case attempt <= 5:
|
||||||
|
return 2 * time.Second
|
||||||
|
case attempt <= 10:
|
||||||
|
return 5 * time.Second
|
||||||
|
case attempt <= 20:
|
||||||
|
return 10 * time.Second
|
||||||
|
default:
|
||||||
|
return 15 * time.Second
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for attempt := 1; attempt <= maxPolls; attempt++ {
|
||||||
|
interval := pollInterval(attempt)
|
||||||
|
deps.Out.PrintInfo(fmt.Sprintf(" 第 %d/%d 次查询,等待 %v ...", attempt, maxPolls, interval))
|
||||||
|
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
return nil, fmt.Errorf("导入轮询被取消 (taskId=%s): %w", taskID, ctx.Err())
|
||||||
|
case <-time.After(interval):
|
||||||
|
}
|
||||||
|
|
||||||
|
text, queryErr := callMCPToolReturnText(ctx, "query_import_task", map[string]any{"taskId": taskID})
|
||||||
|
if queryErr != nil {
|
||||||
|
return nil, fmt.Errorf("查询导入任务失败 (taskId=%s): %w", taskID, queryErr)
|
||||||
|
}
|
||||||
|
|
||||||
|
var result map[string]any
|
||||||
|
if parseErr := json.Unmarshal([]byte(text), &result); parseErr != nil {
|
||||||
|
return nil, fmt.Errorf("解析查询结果失败 (taskId=%s): %w", taskID, parseErr)
|
||||||
|
}
|
||||||
|
|
||||||
|
status, _ := result["status"].(string)
|
||||||
|
switch strings.ToLower(status) {
|
||||||
|
case "completed":
|
||||||
|
return result, nil
|
||||||
|
case "processing":
|
||||||
|
continue
|
||||||
|
case "failed":
|
||||||
|
message, _ := result["message"].(string)
|
||||||
|
if message != "" {
|
||||||
|
return nil, fmt.Errorf("导入任务失败 (taskId=%s): %s", taskID, message)
|
||||||
|
}
|
||||||
|
return nil, fmt.Errorf("导入任务失败 (taskId=%s)", taskID)
|
||||||
|
default:
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, fmt.Errorf("导入任务超时:已轮询 %d 次仍在处理中 (taskId=%s),请稍后使用 dws doc import get --task-id %s 手动查询", maxPolls, taskID, taskID)
|
||||||
|
}
|
||||||
|
|
||||||
// stripDuplicateTitle removes the leading H1 heading from markdown content
|
// stripDuplicateTitle removes the leading H1 heading from markdown content
|
||||||
// when it matches the document name (set via --name). This prevents the title
|
// when it matches the document name (set via --name). This prevents the title
|
||||||
// from appearing twice: once as document metadata and once in the body.
|
// from appearing twice: once as document metadata and once in the body.
|
||||||
|
|||||||
+350
-46
@@ -114,7 +114,38 @@ func newMailCommand() *cobra.Command {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
mailboxCmd.AddCommand(mailboxListCmd)
|
mailboxProfileCmd := &cobra.Command{
|
||||||
|
Use: "profile",
|
||||||
|
Short: "获取用户邮箱信息",
|
||||||
|
Long: `根据邮箱地址获取用户的邮箱详细信息,包含容量、别名等。
|
||||||
|
|
||||||
|
返回字段:
|
||||||
|
email 邮箱地址
|
||||||
|
emailAliases 邮件地址别名列表
|
||||||
|
name 用户名
|
||||||
|
nickname 用户昵称
|
||||||
|
displayName 用户显示名
|
||||||
|
mboxSize 邮箱容量(字节)
|
||||||
|
mboxSizeUsed 已使用的邮箱容量(字节)
|
||||||
|
createdTime 创建时间
|
||||||
|
modifiedTime 修改时间
|
||||||
|
|
||||||
|
错误说明:
|
||||||
|
domain.notFound 该用户的邮箱不是由钉钉邮箱托管,无法完成操作`,
|
||||||
|
Example: ` dws mail mailbox profile --email user@company.com`,
|
||||||
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
|
if err := validateRequiredFlags(cmd, "email"); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return callMCPTool("get_mailbox_profile", map[string]any{
|
||||||
|
"email": mustGetFlag(cmd, "email"),
|
||||||
|
})
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
mailboxProfileCmd.Flags().String("email", "", "用户的邮箱地址 (必填)")
|
||||||
|
|
||||||
|
mailboxCmd.AddCommand(mailboxListCmd, mailboxProfileCmd)
|
||||||
|
|
||||||
messageCmd := &cobra.Command{Use: "message", Short: "邮件管理", RunE: groupRunE}
|
messageCmd := &cobra.Command{Use: "message", Short: "邮件管理", RunE: groupRunE}
|
||||||
|
|
||||||
@@ -1138,6 +1169,64 @@ internetMessageId 来源:message send / draft send / message reply / message r
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
messageBatchGetCmd := &cobra.Command{
|
||||||
|
Use: "batch-get",
|
||||||
|
Short: "批量获取邮件详情",
|
||||||
|
Long: `根据邮件 ID 列表批量获取邮件完整内容,包含正文。
|
||||||
|
|
||||||
|
限制说明:
|
||||||
|
- 单次最多 20 个邮件 ID
|
||||||
|
- CLI 会逐个调用 get_email_by_message_id 并返回聚合结果
|
||||||
|
|
||||||
|
错误说明:
|
||||||
|
domain.notFound 该用户的邮箱不是由钉钉邮箱托管,无法完成操作`,
|
||||||
|
Example: ` dws mail message batch-get --email user@company.com --ids <id1>,<id2>
|
||||||
|
dws mail message batch-get --email user@company.com --ids <id1>,<id2>,<id3>`,
|
||||||
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
|
if err := validateRequiredFlags(cmd, "email", "ids"); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
email := mustGetFlag(cmd, "email")
|
||||||
|
ids := parseRecipients(mustGetFlag(cmd, "ids"))
|
||||||
|
if len(ids) == 0 {
|
||||||
|
return fmt.Errorf("--ids 至少需要 1 个邮件 ID")
|
||||||
|
}
|
||||||
|
if len(ids) > 20 {
|
||||||
|
return fmt.Errorf("--ids 单次最多支持 20 个邮件 ID,当前: %d", len(ids))
|
||||||
|
}
|
||||||
|
if deps.Caller.DryRun() {
|
||||||
|
fmt.Printf("[DRY-RUN] Preview only, not executed:\n")
|
||||||
|
fmt.Printf("Operation: batch-get (loop get_email_by_message_id x%d)\n", len(ids))
|
||||||
|
fmt.Printf("Arguments:\n")
|
||||||
|
fmt.Printf(" email: %s\n", email)
|
||||||
|
fmt.Printf(" ids: %v\n", ids)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
items := make([]map[string]any, 0, len(ids))
|
||||||
|
for _, id := range ids {
|
||||||
|
text, err := callMCPToolReturnText(context.Background(), "get_email_by_message_id", map[string]any{
|
||||||
|
"email": email,
|
||||||
|
"messageId": id,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("获取邮件详情失败 id=%s: %w", id, err)
|
||||||
|
}
|
||||||
|
var payload any
|
||||||
|
if err := json.Unmarshal([]byte(text), &payload); err != nil {
|
||||||
|
payload = text
|
||||||
|
}
|
||||||
|
items = append(items, map[string]any{
|
||||||
|
"id": id,
|
||||||
|
"result": payload,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return deps.Out.PrintJSON(map[string]any{
|
||||||
|
"email": email,
|
||||||
|
"messages": items,
|
||||||
|
})
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
draftCreateCmd := &cobra.Command{
|
draftCreateCmd := &cobra.Command{
|
||||||
Use: "create",
|
Use: "create",
|
||||||
Short: "创建草稿",
|
Short: "创建草稿",
|
||||||
@@ -1407,12 +1496,88 @@ internetMessageId 来源:message send / draft send / message reply / message r
|
|||||||
messageBatchModifyCmd.Flags().String("action", "", "操作类型: markRead/markUnread/addTags/removeTags (必填)")
|
messageBatchModifyCmd.Flags().String("action", "", "操作类型: markRead/markUnread/addTags/removeTags (必填)")
|
||||||
messageBatchModifyCmd.Flags().String("tags", "", "标签 ID 列表,逗号分隔 (action 为 addTags/removeTags 时必填)")
|
messageBatchModifyCmd.Flags().String("tags", "", "标签 ID 列表,逗号分隔 (action 为 addTags/removeTags 时必填)")
|
||||||
|
|
||||||
|
messageBatchGetCmd.Flags().String("email", "", "邮件所属邮箱地址 (必填)")
|
||||||
|
messageBatchGetCmd.Flags().String("ids", "", "要获取的邮件 ID 列表,逗号分隔,最多 20 个 (必填)")
|
||||||
|
|
||||||
messageVerifyCmd.Flags().String("email", "", "邮件所属邮箱地址 (必填)")
|
messageVerifyCmd.Flags().String("email", "", "邮件所属邮箱地址 (必填)")
|
||||||
messageVerifyCmd.Flags().String("internet-message-id", "", "邮件的 internetMessageId (必填),取自发送类命令返回值")
|
messageVerifyCmd.Flags().String("internet-message-id", "", "邮件的 internetMessageId (必填),取自发送类命令返回值")
|
||||||
|
|
||||||
messageCmd.AddCommand(messageListCmd, messageSearchCmd, messageGetCmd, messageSendCmd,
|
messageCmd.AddCommand(messageListCmd, messageSearchCmd, messageGetCmd, messageSendCmd,
|
||||||
messageReplyCmd, messageReplyAllCmd, messageForwardCmd,
|
messageReplyCmd, messageReplyAllCmd, messageForwardCmd,
|
||||||
messageBatchMoveCmd, messageBatchDeleteCmd, messageBatchModifyCmd, messageVerifyCmd)
|
messageBatchMoveCmd, messageBatchDeleteCmd, messageBatchModifyCmd, messageBatchGetCmd, messageVerifyCmd)
|
||||||
|
|
||||||
|
sentMessageCmd := &cobra.Command{Use: "sent-message", Short: "已发送邮件管理", RunE: groupRunE}
|
||||||
|
|
||||||
|
sentMessageRecallCmd := &cobra.Command{
|
||||||
|
Use: "recall",
|
||||||
|
Short: "[危险] 撤回已发送的邮件",
|
||||||
|
Long: `撤回已发送的邮件。仅支持撤回同组织内未读邮件。
|
||||||
|
|
||||||
|
返回字段:
|
||||||
|
id 撤回任务 ID(可用于 recall-detail 查询进度)
|
||||||
|
success 接口调用是否成功
|
||||||
|
errorCode 错误码(仅失败时存在)
|
||||||
|
errorMsg 错误信息(仅失败时存在)
|
||||||
|
|
||||||
|
错误说明:
|
||||||
|
domain.notFound 该用户的邮箱不是由钉钉邮箱托管,无法完成操作`,
|
||||||
|
Example: ` dws mail sent-message recall --email user@company.com --id <mailId> --subject "邮件主题" --yes`,
|
||||||
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
|
if err := validateRequiredFlags(cmd, "email", "id", "subject"); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
yes, _ := cmd.Flags().GetBool("yes")
|
||||||
|
if !yes && !deps.Caller.DryRun() {
|
||||||
|
return fmt.Errorf("此操作为危险操作,需要传入 --yes 确认执行")
|
||||||
|
}
|
||||||
|
return callMCPTool("recall_sent_message", map[string]any{
|
||||||
|
"email": mustGetFlag(cmd, "email"),
|
||||||
|
"id": mustGetFlag(cmd, "id"),
|
||||||
|
"subject": mustGetFlag(cmd, "subject"),
|
||||||
|
})
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
sentMessageRecallDetailCmd := &cobra.Command{
|
||||||
|
Use: "recall-detail",
|
||||||
|
Short: "查询邮件撤回进度",
|
||||||
|
Long: `根据撤回任务 ID 查询邮件撤回的详细进度。
|
||||||
|
|
||||||
|
撤回任务 ID 来源:sent-message recall 命令返回值中的 id 字段。
|
||||||
|
|
||||||
|
返回字段:
|
||||||
|
id 撤回任务 ID
|
||||||
|
status 任务状态: UNINITED/SUBMITTED/RUNNING/FINISHED/CANCELED/FAILED
|
||||||
|
createdTime 创建时间
|
||||||
|
updatedTime 更新时间
|
||||||
|
totalCount 总邮件数
|
||||||
|
succeededCount 成功撤回数
|
||||||
|
failedCount 撤回失败数
|
||||||
|
details 每封邮件的撤回结果
|
||||||
|
|
||||||
|
错误说明:
|
||||||
|
domain.notFound 该用户的邮箱不是由钉钉邮箱托管,无法完成操作`,
|
||||||
|
Example: ` dws mail sent-message recall-detail --email user@company.com --id <recallTaskId>`,
|
||||||
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
|
if err := validateRequiredFlags(cmd, "email", "id"); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return callMCPTool("get_recall_detail", map[string]any{
|
||||||
|
"email": mustGetFlag(cmd, "email"),
|
||||||
|
"id": mustGetFlag(cmd, "id"),
|
||||||
|
})
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
sentMessageRecallCmd.Flags().String("email", "", "发件人邮箱地址 (必填)")
|
||||||
|
sentMessageRecallCmd.Flags().String("id", "", "要撤回的邮件 ID (必填)")
|
||||||
|
sentMessageRecallCmd.Flags().String("subject", "", "邮件主题 (必填)")
|
||||||
|
sentMessageRecallCmd.Flags().Bool("yes", false, "跳过确认提示,直接执行")
|
||||||
|
|
||||||
|
sentMessageRecallDetailCmd.Flags().String("email", "", "用户的邮箱地址 (必填)")
|
||||||
|
sentMessageRecallDetailCmd.Flags().String("id", "", "撤回任务 ID (必填),由 recall 命令返回")
|
||||||
|
|
||||||
|
sentMessageCmd.AddCommand(sentMessageRecallCmd, sentMessageRecallDetailCmd)
|
||||||
|
|
||||||
draftCreateCmd.Flags().String("from", "", "发件人邮箱 (必填)")
|
draftCreateCmd.Flags().String("from", "", "发件人邮箱 (必填)")
|
||||||
draftCreateCmd.Flags().String("sender", "", "--from 的别名")
|
draftCreateCmd.Flags().String("sender", "", "--from 的别名")
|
||||||
@@ -1913,52 +2078,55 @@ user 对象字段:
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: auto-reply update 暂时注释,待服务端修复后恢复
|
autoReplyUpdateCmd := &cobra.Command{
|
||||||
// autoReplyUpdateCmd := &cobra.Command{
|
Use: "update",
|
||||||
// Use: "update",
|
Short: "更新/设置用户的自动回复配置",
|
||||||
// Short: "更新/设置用户的自动回复配置",
|
Long: `更新或设置用户的邮件自动回复配置。所有参数均为必填。
|
||||||
// Long: `更新或设置用户的邮件自动回复配置。所有参数均为必填。
|
|
||||||
//
|
建议工作流:先通过 auto-reply get 获取当前配置,再传入需要修改的字段值。
|
||||||
// 建议工作流:先通过 auto-reply get 获取当前配置,再传入需要修改的字段值。
|
|
||||||
//
|
时间格式示例:2026/06/25 16:00:00 +0800
|
||||||
// 时间格式示例:2026/06/25 16:30:00 +0800
|
|
||||||
//
|
参数说明(全部必填):
|
||||||
// 参数说明(全部必填):
|
--enabled 是否启用自动回复 (true/false)
|
||||||
// --enabled 是否启用自动回复 (true/false)
|
--start 自动回复开始时间
|
||||||
// --startTime 自动回复开始时间
|
--end 自动回复结束时间
|
||||||
// --endTime 自动回复结束时间
|
--scope 回复范围: "contact"(仅联系人) 或 "all"(所有人)
|
||||||
// --scope 回复范围: "contact"(仅联系人) 或 "all"(所有人)
|
--content 自动回复内容
|
||||||
// --content 自动回复内容
|
|
||||||
//
|
错误说明:
|
||||||
// 错误说明:
|
domain.notFound 该用户的邮箱不是由钉钉邮箱托管,无法完成操作`,
|
||||||
// domain.notFound 该用户的邮箱不是由钉钉邮箱托管,无法完成操作`,
|
Example: ` dws mail auto-reply update --email user@company.com --enabled true --start "2026/07/01 09:00:00 +0800" --end "2026/07/07 18:00:00 +0800" --scope all --content "出差中,请稍后联系"
|
||||||
// Example: ` dws mail auto-reply update --email user@company.com --enabled true --startTime "2026/07/01 09:00:00 +0800" --endTime "2026/07/07 18:00:00 +0800" --scope all --content "出差中,请稍后联系"
|
dws mail auto-reply update --email user@company.com --enabled false --start "2026/07/01 09:00:00 +0800" --end "2026/07/07 18:00:00 +0800" --scope all --content "已关闭自动回复"`,
|
||||||
// dws mail auto-reply update --email user@company.com --enabled false --startTime "" --endTime "" --scope all --content ""`,
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
// RunE: func(cmd *cobra.Command, args []string) error {
|
if err := validateRequiredFlags(cmd, "email", "enabled", "start", "end", "scope", "content"); err != nil {
|
||||||
// if err := validateRequiredFlags(cmd, "email", "enabled", "startTime", "endTime", "scope", "content"); err != nil {
|
return err
|
||||||
// return err
|
}
|
||||||
// }
|
enabledRaw := strings.ToLower(strings.TrimSpace(mustGetFlag(cmd, "enabled")))
|
||||||
// toolArgs := map[string]any{
|
if enabledRaw != "true" && enabledRaw != "false" {
|
||||||
// "email": mustGetFlag(cmd, "email"),
|
return fmt.Errorf("--enabled 必须为 true 或 false")
|
||||||
// "enabled": mustGetFlag(cmd, "enabled") == "true",
|
}
|
||||||
// "startTime": mustGetFlag(cmd, "startTime"),
|
toolArgs := map[string]any{
|
||||||
// "endTime": mustGetFlag(cmd, "endTime"),
|
"email": mustGetFlag(cmd, "email"),
|
||||||
// "scope": mustGetFlag(cmd, "scope"),
|
"enabled": enabledRaw == "true",
|
||||||
// "content": mustGetFlag(cmd, "content"),
|
"startTime": mustGetFlag(cmd, "start"),
|
||||||
// }
|
"endTime": mustGetFlag(cmd, "end"),
|
||||||
// return callMCPTool("update_auto_reply", toolArgs)
|
"scope": mustGetFlag(cmd, "scope"),
|
||||||
// },
|
"content": mustGetFlag(cmd, "content"),
|
||||||
// }
|
}
|
||||||
|
return callMCPTool("update_auto_reply", toolArgs)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
autoReplyGetCmd.Flags().String("email", "", "用户的邮箱地址 (必填)")
|
autoReplyGetCmd.Flags().String("email", "", "用户的邮箱地址 (必填)")
|
||||||
// autoReplyUpdateCmd.Flags().String("email", "", "用户的邮箱地址 (必填)")
|
autoReplyUpdateCmd.Flags().String("email", "", "用户的邮箱地址 (必填)")
|
||||||
// autoReplyUpdateCmd.Flags().String("enabled", "", "是否启用自动回复: true/false (必填)")
|
autoReplyUpdateCmd.Flags().String("enabled", "", "是否启用自动回复: true/false (必填)")
|
||||||
// autoReplyUpdateCmd.Flags().String("startTime", "", "自动回复开始时间 (必填),格式: 2026/06/25 16:30:00 +0800")
|
autoReplyUpdateCmd.Flags().String("start", "", "自动回复开始时间,格式: YYYY/MM/DD HH:MM:SS +ZZZZ (必填)")
|
||||||
// autoReplyUpdateCmd.Flags().String("endTime", "", "自动回复结束时间 (必填),格式: 2026/06/25 16:30:00 +0800")
|
autoReplyUpdateCmd.Flags().String("end", "", "自动回复结束时间,格式: YYYY/MM/DD HH:MM:SS +ZZZZ (必填)")
|
||||||
// autoReplyUpdateCmd.Flags().String("scope", "", "回复范围: contact(仅联系人)/all(所有人) (必填)")
|
autoReplyUpdateCmd.Flags().String("scope", "", "回复范围: contact(仅联系人)/all(所有人) (必填)")
|
||||||
// autoReplyUpdateCmd.Flags().String("content", "", "自动回复内容 (必填)")
|
autoReplyUpdateCmd.Flags().String("content", "", "自动回复内容 (必填)")
|
||||||
|
|
||||||
autoReplyCmd.AddCommand(autoReplyGetCmd) // , autoReplyUpdateCmd)
|
autoReplyCmd.AddCommand(autoReplyGetCmd, autoReplyUpdateCmd)
|
||||||
|
|
||||||
// ── rule 收信规则 ────────────────────────────────────
|
// ── rule 收信规则 ────────────────────────────────────
|
||||||
ruleCmd := &cobra.Command{Use: "rule", Short: "收信规则管理", RunE: groupRunE}
|
ruleCmd := &cobra.Command{Use: "rule", Short: "收信规则管理", RunE: groupRunE}
|
||||||
@@ -2145,7 +2313,143 @@ object 与 operation 合法组合:
|
|||||||
|
|
||||||
ruleCmd.AddCommand(ruleListCmd, ruleCreateCmd, ruleUpdateCmd, ruleDeleteCmd, ruleAdjustCmd)
|
ruleCmd.AddCommand(ruleListCmd, ruleCreateCmd, ruleUpdateCmd, ruleDeleteCmd, ruleAdjustCmd)
|
||||||
|
|
||||||
root.AddCommand(mailboxCmd, messageCmd, draftCmd, threadCmd, folderCmd, tagCmd, userCmd, attachmentCmd, templateCmd, contactCmd, autoReplyCmd, ruleCmd)
|
// ── allow-list 个人收信白名单 ────────────────────────────────
|
||||||
|
allowListCmd := &cobra.Command{Use: "allow-list", Short: "个人收信白名单管理", RunE: groupRunE}
|
||||||
|
|
||||||
|
allowListListCmd := &cobra.Command{
|
||||||
|
Use: "list",
|
||||||
|
Short: "列出个人收信白名单",
|
||||||
|
Long: `列出当前用户的个人收信白名单地址列表。
|
||||||
|
|
||||||
|
返回字段:
|
||||||
|
entries 白名单地址列表
|
||||||
|
success 接口调用是否成功
|
||||||
|
errorCode 错误码(仅失败时存在)
|
||||||
|
errorMsg 错误信息(仅失败时存在)`,
|
||||||
|
Example: ` dws mail allow-list list --email user@company.com`,
|
||||||
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
|
if err := validateRequiredFlags(cmd, "email"); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return callMCPTool("list_mailbox_allowlist", map[string]any{
|
||||||
|
"email": mustGetFlag(cmd, "email"),
|
||||||
|
})
|
||||||
|
},
|
||||||
|
}
|
||||||
|
allowListAddCmd := &cobra.Command{
|
||||||
|
Use: "add",
|
||||||
|
Short: "添加个人收信白名单",
|
||||||
|
Long: `向个人收信白名单中添加邮件地址或域名。
|
||||||
|
|
||||||
|
条目格式:
|
||||||
|
- 邮件地址:123@domain.com
|
||||||
|
- 域名:@domain.com(域名前需加 @ 符号)`,
|
||||||
|
Example: ` dws mail allow-list add --email user@company.com --entries a@b.com,@spam.com`,
|
||||||
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
|
if err := validateRequiredFlags(cmd, "email", "entries"); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return callMCPTool("add_mailbox_allowlist", map[string]any{
|
||||||
|
"email": mustGetFlag(cmd, "email"),
|
||||||
|
"entries": parseRecipients(mustGetFlag(cmd, "entries")),
|
||||||
|
})
|
||||||
|
},
|
||||||
|
}
|
||||||
|
allowListRemoveCmd := &cobra.Command{
|
||||||
|
Use: "remove",
|
||||||
|
Short: "移除个人收信白名单",
|
||||||
|
Long: `从个人收信白名单中移除邮件地址或域名。
|
||||||
|
|
||||||
|
条目格式:
|
||||||
|
- 邮件地址:123@domain.com
|
||||||
|
- 域名:@domain.com(域名前需加 @ 符号)`,
|
||||||
|
Example: ` dws mail allow-list remove --email user@company.com --entries a@b.com,@spam.com`,
|
||||||
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
|
if err := validateRequiredFlags(cmd, "email", "entries"); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return callMCPTool("remove_mailbox_allowlist", map[string]any{
|
||||||
|
"email": mustGetFlag(cmd, "email"),
|
||||||
|
"entries": parseRecipients(mustGetFlag(cmd, "entries")),
|
||||||
|
})
|
||||||
|
},
|
||||||
|
}
|
||||||
|
allowListListCmd.Flags().String("email", "", "用户的邮箱地址 (必填)")
|
||||||
|
allowListAddCmd.Flags().String("email", "", "用户的邮箱地址 (必填)")
|
||||||
|
allowListAddCmd.Flags().String("entries", "", "逗号分隔的地址列表,支持邮件地址(如123@domain.com)或域名(如@domain.com)")
|
||||||
|
allowListRemoveCmd.Flags().String("email", "", "用户的邮箱地址 (必填)")
|
||||||
|
allowListRemoveCmd.Flags().String("entries", "", "逗号分隔的地址列表,支持邮件地址(如123@domain.com)或域名(如@domain.com)")
|
||||||
|
allowListCmd.AddCommand(allowListListCmd, allowListAddCmd, allowListRemoveCmd)
|
||||||
|
|
||||||
|
// ── block-list 个人收信黑名单 ────────────────────────────────
|
||||||
|
blockListCmd := &cobra.Command{Use: "block-list", Short: "个人收信黑名单管理", RunE: groupRunE}
|
||||||
|
|
||||||
|
blockListListCmd := &cobra.Command{
|
||||||
|
Use: "list",
|
||||||
|
Short: "列出个人收信黑名单",
|
||||||
|
Long: `列出当前用户的个人收信黑名单地址列表。
|
||||||
|
|
||||||
|
返回字段:
|
||||||
|
entries 黑名单地址列表
|
||||||
|
success 接口调用是否成功
|
||||||
|
errorCode 错误码(仅失败时存在)
|
||||||
|
errorMsg 错误信息(仅失败时存在)`,
|
||||||
|
Example: ` dws mail block-list list --email user@company.com`,
|
||||||
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
|
if err := validateRequiredFlags(cmd, "email"); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return callMCPTool("list_mailbox_blocklist", map[string]any{
|
||||||
|
"email": mustGetFlag(cmd, "email"),
|
||||||
|
})
|
||||||
|
},
|
||||||
|
}
|
||||||
|
blockListAddCmd := &cobra.Command{
|
||||||
|
Use: "add",
|
||||||
|
Short: "添加个人收信黑名单",
|
||||||
|
Long: `向个人收信黑名单中添加邮件地址或域名。
|
||||||
|
|
||||||
|
条目格式:
|
||||||
|
- 邮件地址:123@domain.com
|
||||||
|
- 域名:@domain.com(域名前需加 @ 符号)`,
|
||||||
|
Example: ` dws mail block-list add --email user@company.com --entries spam@bad.com,@junk.com`,
|
||||||
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
|
if err := validateRequiredFlags(cmd, "email", "entries"); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return callMCPTool("add_mailbox_blocklist", map[string]any{
|
||||||
|
"email": mustGetFlag(cmd, "email"),
|
||||||
|
"entries": parseRecipients(mustGetFlag(cmd, "entries")),
|
||||||
|
})
|
||||||
|
},
|
||||||
|
}
|
||||||
|
blockListRemoveCmd := &cobra.Command{
|
||||||
|
Use: "remove",
|
||||||
|
Short: "移除个人收信黑名单",
|
||||||
|
Long: `从个人收信黑名单中移除邮件地址或域名。
|
||||||
|
|
||||||
|
条目格式:
|
||||||
|
- 邮件地址:123@domain.com
|
||||||
|
- 域名:@domain.com(域名前需加 @ 符号)`,
|
||||||
|
Example: ` dws mail block-list remove --email user@company.com --entries spam@bad.com,@junk.com`,
|
||||||
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
|
if err := validateRequiredFlags(cmd, "email", "entries"); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return callMCPTool("remove_mailbox_blocklist", map[string]any{
|
||||||
|
"email": mustGetFlag(cmd, "email"),
|
||||||
|
"entries": parseRecipients(mustGetFlag(cmd, "entries")),
|
||||||
|
})
|
||||||
|
},
|
||||||
|
}
|
||||||
|
blockListListCmd.Flags().String("email", "", "用户的邮箱地址 (必填)")
|
||||||
|
blockListAddCmd.Flags().String("email", "", "用户的邮箱地址 (必填)")
|
||||||
|
blockListAddCmd.Flags().String("entries", "", "逗号分隔的地址列表,支持邮件地址(如123@domain.com)或域名(如@domain.com)")
|
||||||
|
blockListRemoveCmd.Flags().String("email", "", "用户的邮箱地址 (必填)")
|
||||||
|
blockListRemoveCmd.Flags().String("entries", "", "逗号分隔的地址列表,支持邮件地址(如123@domain.com)或域名(如@domain.com)")
|
||||||
|
blockListCmd.AddCommand(blockListListCmd, blockListAddCmd, blockListRemoveCmd)
|
||||||
|
|
||||||
|
root.AddCommand(mailboxCmd, messageCmd, sentMessageCmd, draftCmd, threadCmd, folderCmd, tagCmd, userCmd, attachmentCmd, templateCmd, contactCmd, autoReplyCmd, ruleCmd, allowListCmd, blockListCmd)
|
||||||
|
|
||||||
return root
|
return root
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ func init() {
|
|||||||
name string
|
name string
|
||||||
fn func() *cobra.Command
|
fn func() *cobra.Command
|
||||||
}{
|
}{
|
||||||
|
{"agoal", newAgoalCommand},
|
||||||
{"aisearch", newAisearchCommand},
|
{"aisearch", newAisearchCommand},
|
||||||
{"aitable", newAitableCommand},
|
{"aitable", newAitableCommand},
|
||||||
{"attendance", newAttendanceCommand},
|
{"attendance", newAttendanceCommand},
|
||||||
|
|||||||
@@ -48,6 +48,8 @@ func newSheetCommand() *cobra.Command {
|
|||||||
dws sheet insert-dimension 在指定位置插入行或列
|
dws sheet insert-dimension 在指定位置插入行或列
|
||||||
dws sheet delete-dimension 删除指定位置的行或列
|
dws sheet delete-dimension 删除指定位置的行或列
|
||||||
dws sheet update-dimension 更新指定范围行/列属性(显隐、行高/列宽)
|
dws sheet update-dimension 更新指定范围行/列属性(显隐、行高/列宽)
|
||||||
|
dws sheet group-dimension 对指定连续行/列创建分组
|
||||||
|
dws sheet ungroup-dimension 取消指定连续行/列分组
|
||||||
dws sheet media-upload 上传附件到表格
|
dws sheet media-upload 上传附件到表格
|
||||||
dws sheet write-image 上传图片并写入表格单元格
|
dws sheet write-image 上传图片并写入表格单元格
|
||||||
dws sheet replace 全局查找替换文本
|
dws sheet replace 全局查找替换文本
|
||||||
|
|||||||
@@ -39,6 +39,8 @@ var batchOpDispatch = map[string]batchOpMapping{
|
|||||||
"csv-put": {"set_range_from_csv", BuildCsvPutArgs},
|
"csv-put": {"set_range_from_csv", BuildCsvPutArgs},
|
||||||
"delete-float-image": {"delete_float_image", BuildDeleteFloatImageArgs},
|
"delete-float-image": {"delete_float_image", BuildDeleteFloatImageArgs},
|
||||||
"update-dimension": {"update_dimension", BuildUpdateDimensionArgs},
|
"update-dimension": {"update_dimension", BuildUpdateDimensionArgs},
|
||||||
|
"group-dimension": {"group_dimension", BuildGroupDimensionArgs},
|
||||||
|
"ungroup-dimension": {"ungroup_dimension", BuildUngroupDimensionArgs},
|
||||||
}
|
}
|
||||||
|
|
||||||
// translateBatchOp translates a batch operation from CLI format to MCP format.
|
// translateBatchOp translates a batch operation from CLI format to MCP format.
|
||||||
@@ -269,6 +271,27 @@ func BuildUpdateDimensionArgs(input map[string]any) map[string]any {
|
|||||||
return args
|
return args
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// BuildGroupDimensionArgs converts CLI flags to MCP params for group_dimension.
|
||||||
|
func BuildGroupDimensionArgs(input map[string]any) map[string]any {
|
||||||
|
groupState := batchStr(input, "group-state", "groupState")
|
||||||
|
if groupState == "" {
|
||||||
|
groupState = "expand"
|
||||||
|
}
|
||||||
|
return map[string]any{
|
||||||
|
"sheetId": batchStr(input, "sheet-id"),
|
||||||
|
"range": batchStr(input, "range"),
|
||||||
|
"groupState": groupState,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// BuildUngroupDimensionArgs converts CLI flags to MCP params for ungroup_dimension.
|
||||||
|
func BuildUngroupDimensionArgs(input map[string]any) map[string]any {
|
||||||
|
return map[string]any{
|
||||||
|
"sheetId": batchStr(input, "sheet-id"),
|
||||||
|
"range": batchStr(input, "range"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// resolveCsvContent resolves @filepath and - stdin to CSV text, matching standalone csv-put behavior.
|
// resolveCsvContent resolves @filepath and - stdin to CSV text, matching standalone csv-put behavior.
|
||||||
func resolveCsvContent(csvVal string) string {
|
func resolveCsvContent(csvVal string) string {
|
||||||
switch {
|
switch {
|
||||||
@@ -307,8 +330,12 @@ CLI 层自动翻译为 MCP toolName + 参数名,无需记忆 MCP 参数名。
|
|||||||
支持的 CLI 命令名:
|
支持的 CLI 命令名:
|
||||||
range clear / range update / merge-cells / unmerge-cells / update-dimension
|
range clear / range update / merge-cells / unmerge-cells / update-dimension
|
||||||
range fill / range copy-to / add-dimension / delete-dimension / move-dimension
|
range fill / range copy-to / add-dimension / delete-dimension / move-dimension
|
||||||
|
group-dimension / ungroup-dimension
|
||||||
set-dropdown / delete-dropdown / csv-put / delete-float-image
|
set-dropdown / delete-dropdown / csv-put / delete-float-image
|
||||||
|
|
||||||
|
注意:batch-update 中 group-dimension 适合默认展开分组;需要 --group-state fold 时请使用独立
|
||||||
|
dws sheet group-dimension 命令。
|
||||||
|
|
||||||
--operations 是 JSON 数组,每项包含:
|
--operations 是 JSON 数组,每项包含:
|
||||||
toolName CLI 命令名(如 "range clear", "range update")
|
toolName CLI 命令名(如 "range clear", "range update")
|
||||||
input 该命令的入参(不含 --node),键用 flag 名去掉 --
|
input 该命令的入参(不含 --node),键用 flag 名去掉 --
|
||||||
|
|||||||
@@ -375,6 +375,83 @@ sheetId 支持传入工作表 ID 或工作表名称,可通过 sheet list 获
|
|||||||
updateDimensionCmd.Flags().Bool("hidden", false, "是否隐藏 (true=隐藏, false=显示)")
|
updateDimensionCmd.Flags().Bool("hidden", false, "是否隐藏 (true=隐藏, false=显示)")
|
||||||
updateDimensionCmd.Flags().Int("pixel-size", 0, "行高或列宽(像素),ROWS 时为行高,COLUMNS 时为列宽")
|
updateDimensionCmd.Flags().Int("pixel-size", 0, "行高或列宽(像素),ROWS 时为行高,COLUMNS 时为列宽")
|
||||||
|
|
||||||
|
groupDimensionCmd := &cobra.Command{
|
||||||
|
Use: "group-dimension",
|
||||||
|
Short: "对指定连续行/列创建分组",
|
||||||
|
Long: `对钉钉表格指定工作表中的连续整行或整列创建分组。
|
||||||
|
|
||||||
|
--range 使用整行/整列范围:
|
||||||
|
行分组:3:7 或 3
|
||||||
|
列分组:C:F 或 C
|
||||||
|
|
||||||
|
支持在 --range 中携带工作表前缀(如 "Sheet1!3:7" / "Sheet1!C:F"),此时将忽略 --sheet-id。
|
||||||
|
创建后可通过 sheet info --include groups 回读 rowGroups / columnGroups。
|
||||||
|
|
||||||
|
--group-state 支持 expand / fold,默认 expand。`,
|
||||||
|
Example: ` # 分组第 3~7 行
|
||||||
|
dws sheet group-dimension --node NODE_ID --sheet-id SHEET_ID --range "3:7"
|
||||||
|
|
||||||
|
# 分组并折叠 C~F 列
|
||||||
|
dws sheet group-dimension --node NODE_ID --sheet-id SHEET_ID --range "C:F" --group-state fold
|
||||||
|
|
||||||
|
# 使用工作表前缀(忽略 --sheet-id)
|
||||||
|
dws sheet group-dimension --node NODE_ID --sheet-id SHEET_ID --range "Sheet1!3:7"`,
|
||||||
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
|
if err := validateRequiredFlags(cmd, "node", "sheet-id", "range"); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
groupState, _ := cmd.Flags().GetString("group-state")
|
||||||
|
switch groupState {
|
||||||
|
case "", "expand":
|
||||||
|
groupState = "expand"
|
||||||
|
case "fold":
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("--group-state 必须为 expand 或 fold,当前值: %s", groupState)
|
||||||
|
}
|
||||||
|
return callMCPTool("group_dimension", map[string]any{
|
||||||
|
"nodeId": mustGetFlag(cmd, "node"),
|
||||||
|
"sheetId": mustGetFlag(cmd, "sheet-id"),
|
||||||
|
"range": mustGetFlag(cmd, "range"),
|
||||||
|
"groupState": groupState,
|
||||||
|
})
|
||||||
|
},
|
||||||
|
}
|
||||||
|
groupDimensionCmd.Flags().String("node", "", "表格文档 ID 或 URL (必填)")
|
||||||
|
groupDimensionCmd.Flags().String("sheet-id", "", "工作表 ID 或名称 (必填)")
|
||||||
|
groupDimensionCmd.Flags().String("range", "", `整行/整列范围 (必填),如 "3:7" 或 "C:F"`)
|
||||||
|
groupDimensionCmd.Flags().String("group-state", "expand", "创建后的分组状态: expand 或 fold")
|
||||||
|
|
||||||
|
ungroupDimensionCmd := &cobra.Command{
|
||||||
|
Use: "ungroup-dimension",
|
||||||
|
Short: "取消指定连续行/列分组",
|
||||||
|
Long: `取消钉钉表格指定工作表中的连续整行或整列分组。
|
||||||
|
|
||||||
|
--range 使用整行/整列范围:
|
||||||
|
行分组:3:7 或 3
|
||||||
|
列分组:C:F 或 C
|
||||||
|
|
||||||
|
支持在 --range 中携带工作表前缀(如 "Sheet1!3:7" / "Sheet1!C:F"),此时将忽略 --sheet-id。
|
||||||
|
取消后可通过 sheet info --include groups 回读 rowGroups / columnGroups。`,
|
||||||
|
Example: ` # 取消第 3~7 行分组
|
||||||
|
dws sheet ungroup-dimension --node NODE_ID --sheet-id SHEET_ID --range "3:7"
|
||||||
|
|
||||||
|
# 取消 C~F 列分组
|
||||||
|
dws sheet ungroup-dimension --node NODE_ID --sheet-id SHEET_ID --range "C:F"`,
|
||||||
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
|
if err := validateRequiredFlags(cmd, "node", "sheet-id", "range"); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return callMCPTool("ungroup_dimension", map[string]any{
|
||||||
|
"nodeId": mustGetFlag(cmd, "node"),
|
||||||
|
"sheetId": mustGetFlag(cmd, "sheet-id"),
|
||||||
|
"range": mustGetFlag(cmd, "range"),
|
||||||
|
})
|
||||||
|
},
|
||||||
|
}
|
||||||
|
ungroupDimensionCmd.Flags().String("node", "", "表格文档 ID 或 URL (必填)")
|
||||||
|
ungroupDimensionCmd.Flags().String("sheet-id", "", "工作表 ID 或名称 (必填)")
|
||||||
|
ungroupDimensionCmd.Flags().String("range", "", `整行/整列范围 (必填),如 "3:7" 或 "C:F"`)
|
||||||
|
|
||||||
// ── dropdown ──────────────────────────────────────────────────
|
// ── dropdown ──────────────────────────────────────────────────
|
||||||
setDropdownCmd := &cobra.Command{
|
setDropdownCmd := &cobra.Command{
|
||||||
Use: "set-dropdown",
|
Use: "set-dropdown",
|
||||||
@@ -499,7 +576,7 @@ sheetId 支持传入工作表 ID 或工作表名称,可通过 sheet list 获
|
|||||||
return []*cobra.Command{
|
return []*cobra.Command{
|
||||||
insertDimensionCmd, moveDimensionCmd, addDimensionCmd,
|
insertDimensionCmd, moveDimensionCmd, addDimensionCmd,
|
||||||
mergeCellsCmd, unmergeRangeCmd,
|
mergeCellsCmd, unmergeRangeCmd,
|
||||||
deleteDimensionCmd, updateDimensionCmd,
|
deleteDimensionCmd, updateDimensionCmd, groupDimensionCmd, ungroupDimensionCmd,
|
||||||
setDropdownCmd, getDropdownCmd, deleteDropdownCmd,
|
setDropdownCmd, getDropdownCmd, deleteDropdownCmd,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,182 @@
|
|||||||
|
// Copyright 2026 Alibaba Group
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package helpers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"reflect"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
)
|
||||||
|
|
||||||
|
func requireWukongSyncCommand(t *testing.T, root *cobra.Command, path ...string) *cobra.Command {
|
||||||
|
t.Helper()
|
||||||
|
cmd, remaining, err := root.Find(path)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("%s: command not found: %v", path, err)
|
||||||
|
}
|
||||||
|
if len(remaining) != 0 {
|
||||||
|
t.Fatalf("%s: unresolved path suffix %v (resolved to %q)", path, remaining, cmd.CommandPath())
|
||||||
|
}
|
||||||
|
return cmd
|
||||||
|
}
|
||||||
|
|
||||||
|
func requireWukongSyncFlags(t *testing.T, cmd *cobra.Command, names ...string) {
|
||||||
|
t.Helper()
|
||||||
|
for _, name := range names {
|
||||||
|
if cmd.Flags().Lookup(name) == nil {
|
||||||
|
t.Fatalf("%s: missing flag --%s", cmd.CommandPath(), name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestWukongSyncMailCommands(t *testing.T) {
|
||||||
|
root := newMailCommand()
|
||||||
|
cases := []struct {
|
||||||
|
path []string
|
||||||
|
flags []string
|
||||||
|
}{
|
||||||
|
{[]string{"mailbox", "profile"}, []string{"email"}},
|
||||||
|
{[]string{"message", "batch-get"}, []string{"email", "ids"}},
|
||||||
|
{[]string{"sent-message", "recall"}, []string{"email", "id", "subject", "yes"}},
|
||||||
|
{[]string{"sent-message", "recall-detail"}, []string{"email", "id"}},
|
||||||
|
{[]string{"auto-reply", "update"}, []string{"email", "enabled", "start", "end", "scope", "content"}},
|
||||||
|
{[]string{"allow-list", "list"}, []string{"email"}},
|
||||||
|
{[]string{"allow-list", "add"}, []string{"email", "entries"}},
|
||||||
|
{[]string{"allow-list", "remove"}, []string{"email", "entries"}},
|
||||||
|
{[]string{"block-list", "list"}, []string{"email"}},
|
||||||
|
{[]string{"block-list", "add"}, []string{"email", "entries"}},
|
||||||
|
{[]string{"block-list", "remove"}, []string{"email", "entries"}},
|
||||||
|
}
|
||||||
|
for _, tc := range cases {
|
||||||
|
cmd := requireWukongSyncCommand(t, root, tc.path...)
|
||||||
|
requireWukongSyncFlags(t, cmd, tc.flags...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestWukongSyncChatCommands(t *testing.T) {
|
||||||
|
root := newChatCommand()
|
||||||
|
cases := []struct {
|
||||||
|
path []string
|
||||||
|
flags []string
|
||||||
|
}{
|
||||||
|
{[]string{"group", "notice", "create"}, []string{"group", "content", "sticky", "send-ding", "run-at"}},
|
||||||
|
{[]string{"group", "notice", "edit"}, []string{"group", "notice-id", "content", "sticky", "send-ding"}},
|
||||||
|
{[]string{"group", "notice", "get"}, []string{"group", "notice-id"}},
|
||||||
|
{[]string{"group", "notice", "list"}, []string{"group", "limit", "cursor", "scheduled"}},
|
||||||
|
{[]string{"group", "share-invite"}, []string{"source", "target", "receiver", "expires-seconds", "uuid"}},
|
||||||
|
{[]string{"text", "translate"}, []string{"query", "to"}},
|
||||||
|
{[]string{"category", "create-smart"}, []string{"name", "keywords", "members"}},
|
||||||
|
{[]string{"message", "list-emotion-replies"}, []string{"msg-ids"}},
|
||||||
|
}
|
||||||
|
for _, tc := range cases {
|
||||||
|
cmd := requireWukongSyncCommand(t, root, tc.path...)
|
||||||
|
requireWukongSyncFlags(t, cmd, tc.flags...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestWukongSyncDocCommands(t *testing.T) {
|
||||||
|
root := newDocCommand()
|
||||||
|
|
||||||
|
importCmd := requireWukongSyncCommand(t, root, "import")
|
||||||
|
requireWukongSyncFlags(t, importCmd, "file", "folder", "workspace", "name", "folder-id", "workspace-id")
|
||||||
|
|
||||||
|
importGetCmd := requireWukongSyncCommand(t, root, "import", "get")
|
||||||
|
requireWukongSyncFlags(t, importGetCmd, "task-id")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestWukongSyncSheetCommands(t *testing.T) {
|
||||||
|
root := newSheetCommand()
|
||||||
|
|
||||||
|
groupCmd := requireWukongSyncCommand(t, root, "group-dimension")
|
||||||
|
requireWukongSyncFlags(t, groupCmd, "node", "sheet-id", "range", "group-state")
|
||||||
|
|
||||||
|
ungroupCmd := requireWukongSyncCommand(t, root, "ungroup-dimension")
|
||||||
|
requireWukongSyncFlags(t, ungroupCmd, "node", "sheet-id", "range")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestWukongSyncSheetBatchDimensionGroupMapping(t *testing.T) {
|
||||||
|
group, err := translateBatchOp(map[string]any{
|
||||||
|
"toolName": "group-dimension",
|
||||||
|
"input": map[string]any{
|
||||||
|
"sheet-id": "Sheet1",
|
||||||
|
"range": "3:7",
|
||||||
|
"group-state": "fold",
|
||||||
|
},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("group-dimension mapping returned error: %v", err)
|
||||||
|
}
|
||||||
|
wantGroup := map[string]any{
|
||||||
|
"toolName": "group_dimension",
|
||||||
|
"input": map[string]any{
|
||||||
|
"sheetId": "Sheet1",
|
||||||
|
"range": "3:7",
|
||||||
|
"groupState": "fold",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
if !reflect.DeepEqual(group, wantGroup) {
|
||||||
|
t.Fatalf("group-dimension mapping mismatch:\n got: %#v\nwant: %#v", group, wantGroup)
|
||||||
|
}
|
||||||
|
|
||||||
|
ungroup, err := translateBatchOp(map[string]any{
|
||||||
|
"toolName": "ungroup-dimension",
|
||||||
|
"input": map[string]any{
|
||||||
|
"sheet-id": "Sheet1",
|
||||||
|
"range": "C:F",
|
||||||
|
},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("ungroup-dimension mapping returned error: %v", err)
|
||||||
|
}
|
||||||
|
wantUngroup := map[string]any{
|
||||||
|
"toolName": "ungroup_dimension",
|
||||||
|
"input": map[string]any{
|
||||||
|
"sheetId": "Sheet1",
|
||||||
|
"range": "C:F",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
if !reflect.DeepEqual(ungroup, wantUngroup) {
|
||||||
|
t.Fatalf("ungroup-dimension mapping mismatch:\n got: %#v\nwant: %#v", ungroup, wantUngroup)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestWukongSyncAgoalCommands(t *testing.T) {
|
||||||
|
root := newAgoalCommand()
|
||||||
|
cases := []struct {
|
||||||
|
path []string
|
||||||
|
flags []string
|
||||||
|
}{
|
||||||
|
{[]string{"strategy", "list"}, []string{"scope-type", "scope-id", "request-id"}},
|
||||||
|
{[]string{"strategy", "detail"}, []string{"profile-id", "request-id"}},
|
||||||
|
{[]string{"strategy", "update"}, []string{"profile-id", "content", "request-id"}},
|
||||||
|
{[]string{"contract", "list"}, []string{"scope-type", "scope-id", "request-id"}},
|
||||||
|
{[]string{"contract", "fields"}, []string{"request-id"}},
|
||||||
|
{[]string{"contract", "detail"}, []string{"contract-id", "request-id"}},
|
||||||
|
{[]string{"contract", "update"}, []string{"contract-id", "dimensions", "audit-config", "objective-template", "request-id"}},
|
||||||
|
{[]string{"scorecard", "detail"}, []string{"selected-time", "dept-id", "request-id"}},
|
||||||
|
{[]string{"scorecard", "entity-detail"}, []string{"sc-id", "entity-id", "request-id"}},
|
||||||
|
{[]string{"scorecard", "update"}, []string{"dept-id", "selected-time", "id", "tracking-period-type", "content", "request-id"}},
|
||||||
|
{[]string{"user", "rules"}, []string{"user-id", "request-id"}},
|
||||||
|
{[]string{"user", "objectives"}, []string{"user-id", "rule-id", "period-ids", "request-id"}},
|
||||||
|
{[]string{"report", "list-statistics"}, []string{"keyword", "request-id"}},
|
||||||
|
{[]string{"report", "submit-detail"}, []string{"template-id", "submit-state", "query-date", "page", "page-size", "keyword", "request-id"}},
|
||||||
|
{[]string{"obj-template", "list"}, []string{"keyword", "page", "page-size", "request-id"}},
|
||||||
|
{[]string{"obj-template", "create-or-update"}, []string{"template-id", "title", "objective-weight", "dimension-weight", "compute-by-weight", "dimensions", "request-id"}},
|
||||||
|
}
|
||||||
|
for _, tc := range cases {
|
||||||
|
cmd := requireWukongSyncCommand(t, root, tc.path...)
|
||||||
|
requireWukongSyncFlags(t, cmd, tc.flags...)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -5,6 +5,7 @@ package syncdata
|
|||||||
|
|
||||||
func StaticServers() []ServerInfo {
|
func StaticServers() []ServerInfo {
|
||||||
return []ServerInfo{
|
return []ServerInfo{
|
||||||
|
{ID: "agoal", Name: "Agoal", Endpoint: "https://mcp-gw.dingtalk.com/server/1db49ea94ffe74c25a7079a68b4df6629c79130fb9095d1b499f507015d847a9", Prefixes: []string{"agoal"}},
|
||||||
{ID: "aisearch", Name: "AI 搜问", Endpoint: "https://mcp-gw.dingtalk.com/server/ai-search", Prefixes: []string{"aisearch", "enterprise"}},
|
{ID: "aisearch", Name: "AI 搜问", Endpoint: "https://mcp-gw.dingtalk.com/server/ai-search", Prefixes: []string{"aisearch", "enterprise"}},
|
||||||
{ID: "aitable", Name: "AI 多维表", Endpoint: "https://mcp-gw.dingtalk.com/server/5f0d121611f14e878f7d42c3e32bf6c4a790d433066adae38c062a657c397047", Prefixes: []string{"table", "record", "field", "base", "attachment", "view", "dashboard", "chart", "export", "import"}},
|
{ID: "aitable", Name: "AI 多维表", Endpoint: "https://mcp-gw.dingtalk.com/server/5f0d121611f14e878f7d42c3e32bf6c4a790d433066adae38c062a657c397047", Prefixes: []string{"table", "record", "field", "base", "attachment", "view", "dashboard", "chart", "export", "import"}},
|
||||||
{ID: "aitable-helper", Name: "AI 多维表(辅助)", Endpoint: "https://mcp-gw.dingtalk.com/server/bb2984ee6b10c1560b4fe943ca620f646bed31f215c551a53abf040b52591a95", Prefixes: []string{"form", "share_form"}},
|
{ID: "aitable-helper", Name: "AI 多维表(辅助)", Endpoint: "https://mcp-gw.dingtalk.com/server/bb2984ee6b10c1560b4fe943ca620f646bed31f215c551a53abf040b52591a95", Prefixes: []string{"form", "share_form"}},
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ package syncdata
|
|||||||
|
|
||||||
func CmdToProduct() map[string]string {
|
func CmdToProduct() map[string]string {
|
||||||
return map[string]string{
|
return map[string]string{
|
||||||
|
"agoal": "agoal",
|
||||||
"aisearch": "aisearch",
|
"aisearch": "aisearch",
|
||||||
"aitable": "aitable",
|
"aitable": "aitable",
|
||||||
"attendance": "attendance",
|
"attendance": "attendance",
|
||||||
|
|||||||
+1
-1
@@ -260,7 +260,7 @@ resolve_skill_mode() {
|
|||||||
print_multi_mode_notice() {
|
print_multi_mode_notice() {
|
||||||
say ""
|
say ""
|
||||||
say "🧪 Skill mode: multi (EXPERIMENTAL / preview) — automatic skill install skipped."
|
say "🧪 Skill mode: multi (EXPERIMENTAL / preview) — automatic skill install skipped."
|
||||||
say " ⚠ multi is not yet stable. 20 product-scoped skills pass dispatch verifier,"
|
say " ⚠ multi is not yet stable. 22 product-scoped skills pass dispatch verifier,"
|
||||||
say " but interface, naming and cross-skill references may change in future releases."
|
say " but interface, naming and cross-skill references may change in future releases."
|
||||||
say " For production / shared environments, use mono mode (--mode mono)."
|
say " For production / shared environments, use mono mode (--mode mono)."
|
||||||
say ""
|
say ""
|
||||||
|
|||||||
@@ -50,6 +50,7 @@ cli_version: ">=1.0.15"
|
|||||||
## 意图判断决策树
|
## 意图判断决策树
|
||||||
|
|
||||||
用户提到"AI应用/创建应用/生成系统/做工具/管理后台/低代码" → `aiapp`
|
用户提到"AI应用/创建应用/生成系统/做工具/管理后台/低代码" → `aiapp`
|
||||||
|
用户提到"目标管理/Agoal/战略解码/经营合约/计分卡/目标模板/周月报提交统计" → `agoal`
|
||||||
用户提到"找人/搜人/谁负责 XX/某事项的负责人/某项目的人/团队成员/上级/下级/按工号找人/按手机号找人" → `aisearch`
|
用户提到"找人/搜人/谁负责 XX/某事项的负责人/某项目的人/团队成员/上级/下级/按工号找人/按手机号找人" → `aisearch`
|
||||||
用户提到"表格/多维表/AI表格/记录/数据/视图/图表/仪表盘" → `aitable`
|
用户提到"表格/多维表/AI表格/记录/数据/视图/图表/仪表盘" → `aitable`
|
||||||
用户提到"考勤/打卡/排班" → `attendance`
|
用户提到"考勤/打卡/排班" → `attendance`
|
||||||
|
|||||||
@@ -0,0 +1,161 @@
|
|||||||
|
# Agoal(目标管理)
|
||||||
|
|
||||||
|
## 产品说明
|
||||||
|
|
||||||
|
Agoal 是钉钉目标管理工具,支持战略解码、经营合约、计分卡、用户目标、目标模板、周月报六大模块,帮助组织将战略目标从顶层分解到个人并持续跟踪。
|
||||||
|
|
||||||
|
**CLI 前缀**: `dws agoal`
|
||||||
|
|
||||||
|
## 命令总览
|
||||||
|
|
||||||
|
### strategy (战略解码管理)
|
||||||
|
|
||||||
|
| 命令 | 用途 | 必填参数 | 备注 |
|
||||||
|
|------|------|----------|------|
|
||||||
|
| `strategy list` | 获取战略解码列表 | `--scope-type` `--scope-id` | scopeType: DEPT/PERSONAL;scope-id 为 scope-type 对应的部门 id 或用户 id |
|
||||||
|
| `strategy detail` | 获取战略解码详情 | `--profile-id` | 根据战略解码 id 查询 |
|
||||||
|
| `strategy update` | 更新战略解码 | `--profile-id` `--content` | 覆盖逻辑,必须基于查询返回的老数据修改后再传入;`--content` 为 JSON 数组 |
|
||||||
|
|
||||||
|
`strategy update` 是覆盖式更新:一定要先 `strategy detail` 获取完整数据,在原数据基础上修改后再传入。
|
||||||
|
|
||||||
|
### contract (经营合约管理)
|
||||||
|
|
||||||
|
| 命令 | 用途 | 必填参数 | 备注 |
|
||||||
|
|------|------|----------|------|
|
||||||
|
| `contract list` | 获取经营合约列表 | `--scope-type` `--scope-id` | scopeType: DEPT/PERSONAL;scope-id 为 scope-type 对应的部门 id 或用户 id |
|
||||||
|
| `contract fields` | 获取经营合约字段列表 | - | 获取组织下经营合约的字段配置 |
|
||||||
|
| `contract detail` | 获取经营合约详情 | `--contract-id` | 根据合约 id 查询 |
|
||||||
|
| `contract update` | 更新经营合约 | `--contract-id` `--dimensions` | 覆盖逻辑;可选 `--audit-config`、`--objective-template` |
|
||||||
|
|
||||||
|
`contract update` 同样是覆盖式更新:必须基于 `contract detail` 返回的数据修改后再传入。
|
||||||
|
|
||||||
|
### scorecard (计分卡管理)
|
||||||
|
|
||||||
|
| 命令 | 用途 | 必填参数 | 备注 |
|
||||||
|
|------|------|----------|------|
|
||||||
|
| `scorecard detail` | 获取计分卡详情 | `--selected-time` `--dept-id` | selectedTime 为 ISO-8601 字符串,如 `"2026-01-01T00:00:00+08:00"` |
|
||||||
|
| `scorecard entity-detail` | 获取计分卡实体详情 | `--sc-id` `--entity-id` | 根据计分卡 id 和实体 id 查询 |
|
||||||
|
| `scorecard update` | 更新计分卡 | `--dept-id` `--selected-time` `--id` `--tracking-period-type` `--content` | trackingPeriodType: MONTHLY/QUARTERLY |
|
||||||
|
|
||||||
|
### user (用户目标管理)
|
||||||
|
|
||||||
|
| 命令 | 用途 | 必填参数 | 备注 |
|
||||||
|
|------|------|----------|------|
|
||||||
|
| `user rules` | 获取用户规则周期列表 | - | 可选 `--user-id`,不传则默认取操作人自己 |
|
||||||
|
| `user objectives` | 查询用户目标列表 | `--user-id` `--rule-id` `--period-ids` | `--period-ids` 为逗号分隔的周期 id 列表 |
|
||||||
|
|
||||||
|
### report (周月报管理)
|
||||||
|
|
||||||
|
| 命令 | 用途 | 必填参数 | 备注 |
|
||||||
|
|------|------|----------|------|
|
||||||
|
| `report list-statistics` | 获取周月报数据跟催列表 | - | 返回各规则的人员提交情况统计;可选 `--keyword` |
|
||||||
|
| `report submit-detail` | 获取周月报规则提交详情 | `--template-id` `--submit-state` | submitState: ON_TIME/LATE/NOT_SUBMITTED;可选 `--query-date`、`--page`、`--page-size`、`--keyword` |
|
||||||
|
|
||||||
|
### obj-template (目标模板管理)
|
||||||
|
|
||||||
|
| 命令 | 用途 | 必填参数 | 备注 |
|
||||||
|
|------|------|----------|------|
|
||||||
|
| `obj-template list` | 获取目标模板列表 | - | 可选 `--keyword`、`--page`、`--page-size` |
|
||||||
|
| `obj-template create-or-update` | 新增或更新目标模板 | `--dimensions` | 覆盖逻辑;新增时 `--title` 必填;更新时 `--template-id` 必填 |
|
||||||
|
|
||||||
|
## 意图判断
|
||||||
|
|
||||||
|
用户说"战略解码/战略目标/OGSM":
|
||||||
|
- 查看/列表 → `strategy list`
|
||||||
|
- 详情 → `strategy detail`
|
||||||
|
- 修改/更新 → `strategy update`(先查后改)
|
||||||
|
|
||||||
|
用户说"经营合约/合约/KPI合约":
|
||||||
|
- 查看/列表 → `contract list`
|
||||||
|
- 字段配置 → `contract fields`
|
||||||
|
- 详情 → `contract detail`
|
||||||
|
- 修改/更新 → `contract update`(先查后改)
|
||||||
|
|
||||||
|
用户说"计分卡/scorecard/绩效看板":
|
||||||
|
- 查看详情 → `scorecard detail`
|
||||||
|
- 实体详情 → `scorecard entity-detail`
|
||||||
|
- 修改/更新 → `scorecard update`
|
||||||
|
|
||||||
|
用户说"目标/OKR/我的目标/个人目标":
|
||||||
|
- 规则周期 → `user rules`
|
||||||
|
- 目标列表 → `user objectives`
|
||||||
|
|
||||||
|
用户说"目标模板/模板管理":
|
||||||
|
- 查看模板列表 → `obj-template list`
|
||||||
|
- 新增模板 → `obj-template create-or-update --title "模板名称"`
|
||||||
|
- 更新模板 → `obj-template create-or-update --template-id TPL_ID`
|
||||||
|
|
||||||
|
用户说"周月报/周报统计/提交情况/跟催/迟交/未提交":
|
||||||
|
- 查看提交统计列表 → `report list-statistics`
|
||||||
|
- 查看某规则的提交详情 → `report submit-detail`
|
||||||
|
|
||||||
|
## 核心工作流
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 查看战略解码列表与详情
|
||||||
|
dws agoal strategy list --scope-type DEPT --scope-id DEPT_ID --format json
|
||||||
|
dws agoal strategy detail --profile-id PROFILE_ID --format json
|
||||||
|
|
||||||
|
# 更新战略解码:必须基于 detail 返回内容修改后传入
|
||||||
|
dws agoal strategy update --profile-id PROFILE_ID \
|
||||||
|
--content '[{"id":"entity1","title":{"title":"新目标"},"entityType":"OGSM_OBJECTIVE","status":"NORMAL","executors":["dingId1"]}]' \
|
||||||
|
--format json
|
||||||
|
|
||||||
|
# 查看经营合约列表、字段与详情
|
||||||
|
dws agoal contract list --scope-type PERSONAL --scope-id USER_ID --format json
|
||||||
|
dws agoal contract fields --format json
|
||||||
|
dws agoal contract detail --contract-id CONTRACT_ID --format json
|
||||||
|
|
||||||
|
# 更新经营合约:必须基于 detail 返回内容修改后传入
|
||||||
|
dws agoal contract update --contract-id CONTRACT_ID \
|
||||||
|
--dimensions '[{"id":"dim1","title":"维度名称","objectives":[]}]' \
|
||||||
|
--format json
|
||||||
|
|
||||||
|
# 查看计分卡
|
||||||
|
dws agoal scorecard detail --selected-time "2026-01-01T00:00:00+08:00" --dept-id DEPT_ID --format json
|
||||||
|
dws agoal scorecard entity-detail --sc-id SC_ID --entity-id ENTITY_ID --format json
|
||||||
|
|
||||||
|
# 更新计分卡
|
||||||
|
dws agoal scorecard update --dept-id DEPT_ID --selected-time "2026-01-01T00:00:00+08:00" \
|
||||||
|
--id SC_ID --tracking-period-type MONTHLY \
|
||||||
|
--content '[{"id":"dim1","title":"业绩","items":[{"id":"item1","title":"收入","target":"100"}]}]' \
|
||||||
|
--format json
|
||||||
|
|
||||||
|
# 查询用户目标
|
||||||
|
dws agoal user rules --user-id USER_ID --format json
|
||||||
|
dws agoal user objectives --user-id USER_ID --rule-id RULE_ID --period-ids "period1,period2" --format json
|
||||||
|
|
||||||
|
# 周月报提交统计与详情
|
||||||
|
dws agoal report list-statistics --format json
|
||||||
|
dws agoal report list-statistics --keyword "周报规则" --format json
|
||||||
|
dws agoal report submit-detail --template-id TPL_ID --submit-state ON_TIME --format json
|
||||||
|
dws agoal report submit-detail --template-id TPL_ID --submit-state LATE --query-date "2026-06-18T00:00:00+08:00" --page 1 --page-size 20 --format json
|
||||||
|
|
||||||
|
# 目标模板
|
||||||
|
dws agoal obj-template list --format json
|
||||||
|
dws agoal obj-template list --keyword "业绩" --format json
|
||||||
|
dws agoal obj-template create-or-update --title "业绩模板" --objective-weight --dimension-weight --dimensions '[...]' --format json
|
||||||
|
dws agoal obj-template create-or-update --template-id TPL_ID --title "业绩模板" --dimensions '[...]' --format json
|
||||||
|
```
|
||||||
|
|
||||||
|
## 上下文传递表
|
||||||
|
|
||||||
|
| 操作 | 从返回中提取 | 用于 |
|
||||||
|
|------|-------------|------|
|
||||||
|
| `strategy list` | `profileId` | `strategy detail` / `strategy update` 的 `--profile-id` |
|
||||||
|
| `strategy detail` | 完整实体数据 | `strategy update` 的 `--content`(基于此修改) |
|
||||||
|
| `contract list` | `contractId` | `contract detail` / `contract update` 的 `--contract-id` |
|
||||||
|
| `contract detail` | 完整维度数据 | `contract update` 的 `--dimensions`(基于此修改) |
|
||||||
|
| `scorecard detail` | `scId`、`entityId` | `scorecard entity-detail` / `scorecard update` 的 `--id` |
|
||||||
|
| `user rules` | `ruleId`、`periodIds` | `user objectives` 的 `--rule-id` `--period-ids` |
|
||||||
|
| `report list-statistics` | `templateId` | `report submit-detail` 的 `--template-id` |
|
||||||
|
| `obj-template list` | `templateId` | `obj-template create-or-update` 的 `--template-id`(更新时) |
|
||||||
|
|
||||||
|
## 注意事项
|
||||||
|
|
||||||
|
- 所有 update / create-or-update 命令都是覆盖逻辑:必须先用对应 detail/list 查询完整数据,在原数据基础上修改后再传入。
|
||||||
|
- 所有命令支持可选参数 `--request-id`。
|
||||||
|
- `--scope-type` 仅支持 `DEPT` 和 `PERSONAL`。
|
||||||
|
- `--selected-time` 接受 ISO-8601 字符串,如 `"2026-01-01T00:00:00+08:00"`。
|
||||||
|
- `--period-ids` 为逗号分隔字符串,如 `"period1,period2"`。
|
||||||
|
- `report submit-detail` 的 `--query-date` 接受 ISO-8601 字符串;不传则默认当天。
|
||||||
@@ -139,6 +139,26 @@ Flags:
|
|||||||
--expires-seconds int64 链接有效期(秒),0 表示永久有效,不传使用服务端默认值
|
--expires-seconds int64 链接有效期(秒),0 表示永久有效,不传使用服务端默认值
|
||||||
```
|
```
|
||||||
|
|
||||||
|
#### 分享群聊链接到会话 — 将指定群的邀请链接分享到另一个会话或单聊用户
|
||||||
|
|
||||||
|
`--target` 和 `--receiver` 二选一:`--target` 指定目标会话,`--receiver` 指定单聊用户。
|
||||||
|
|
||||||
|
```
|
||||||
|
Usage:
|
||||||
|
dws chat group share-invite [flags]
|
||||||
|
Example:
|
||||||
|
dws chat group share-invite --source <被分享群openConversationId> --target <目标会话openConversationId>
|
||||||
|
dws chat group share-invite --source <被分享群openConversationId> --receiver <接收者openDingTalkId>
|
||||||
|
dws chat group share-invite --source <openConversationId> --target <openConversationId> --expires-seconds 86400
|
||||||
|
# 查询群 ID: dws chat search --query "群名"
|
||||||
|
Flags:
|
||||||
|
--source string 被分享群的 openConversationId (必填)
|
||||||
|
--target string 接收分享消息的会话 openConversationId(与 --receiver 二选一)
|
||||||
|
--receiver string 接收分享消息的单聊用户 openDingTalkId(与 --target 二选一)
|
||||||
|
--expires-seconds int64 链接有效期(秒),0 表示永久有效,不传使用服务端默认值
|
||||||
|
--uuid string 消息幂等键(可选)
|
||||||
|
```
|
||||||
|
|
||||||
#### 退出群聊 — 当前用户退出指定群聊
|
#### 退出群聊 — 当前用户退出指定群聊
|
||||||
```
|
```
|
||||||
Usage:
|
Usage:
|
||||||
@@ -272,6 +292,65 @@ Flags:
|
|||||||
- 不传 --role 时返回群主 + 管理员的所有群
|
- 不传 --role 时返回群主 + 管理员的所有群
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### group notice (群公告管理)
|
||||||
|
|
||||||
|
#### 发布群公告 — 在指定群聊中发布群公告,支持 Markdown、定时发布
|
||||||
|
|
||||||
|
```
|
||||||
|
Usage:
|
||||||
|
dws chat group notice create [flags]
|
||||||
|
Example:
|
||||||
|
dws chat group notice create --group <openConversationId> --content "今晚 22 点系统维护,请提前保存工作内容"
|
||||||
|
dws chat group notice create --group <openConversationId> --content "# 重要通知\n请大家查收" --sticky --send-ding
|
||||||
|
dws chat group notice create --group <openConversationId> --content "明早九点例会" --run-at "2026-07-03T09:00:00+08:00"
|
||||||
|
Flags:
|
||||||
|
--group string 群聊 openConversationId (必填)
|
||||||
|
--content string 公告正文,Markdown 格式 (必填)
|
||||||
|
--sticky 是否吊顶置顶(默认 false)
|
||||||
|
--send-ding 是否发 DING 提醒(默认 false)
|
||||||
|
--run-at string 定时发布时间 ISO-8601(传入则定时发布)
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 修改群公告 — 整体替换指定群公告的内容
|
||||||
|
```
|
||||||
|
Usage:
|
||||||
|
dws chat group notice edit [flags]
|
||||||
|
Example:
|
||||||
|
dws chat group notice edit --group <openConversationId> --notice-id <dataId> --content "更新后的公告内容"
|
||||||
|
Flags:
|
||||||
|
--group string 群聊 openConversationId (必填)
|
||||||
|
--notice-id string 群公告 dataId (必填)
|
||||||
|
--content string 公告新正文,Markdown 格式 (必填)
|
||||||
|
--sticky 是否吊顶置顶
|
||||||
|
--send-ding 是否发 DING 提醒
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 查看群公告详情
|
||||||
|
```
|
||||||
|
Usage:
|
||||||
|
dws chat group notice get [flags]
|
||||||
|
Example:
|
||||||
|
dws chat group notice get --group <openConversationId> --notice-id <dataId>
|
||||||
|
Flags:
|
||||||
|
--group string 群聊 openConversationId (必填)
|
||||||
|
--notice-id string 群公告 dataId (必填)
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 查看群公告列表
|
||||||
|
```
|
||||||
|
Usage:
|
||||||
|
dws chat group notice list [flags]
|
||||||
|
Example:
|
||||||
|
dws chat group notice list --group <openConversationId>
|
||||||
|
dws chat group notice list --group <openConversationId> --limit 20 --cursor <nextPageCursor>
|
||||||
|
dws chat group notice list --group <openConversationId> --scheduled
|
||||||
|
Flags:
|
||||||
|
--group string 群聊 openConversationId (必填)
|
||||||
|
--limit int 每页返回数量(默认 10,最大 100)
|
||||||
|
--cursor string 分页游标
|
||||||
|
--scheduled 是否查询定时公告列表
|
||||||
|
```
|
||||||
|
|
||||||
### group-role (群身份管理)
|
### group-role (群身份管理)
|
||||||
|
|
||||||
#### 查看群身份列表 — 拉取指定群聊的自定义群身份列表
|
#### 查看群身份列表 — 拉取指定群聊的自定义群身份列表
|
||||||
@@ -994,6 +1073,20 @@ Flags:
|
|||||||
- 如果已有合适的表情,无需创建新的
|
- 如果已有合适的表情,无需创建新的
|
||||||
```
|
```
|
||||||
|
|
||||||
|
#### 批量拉取消息的表情回复和文字回复
|
||||||
|
|
||||||
|
根据消息 ID 列表批量查询消息的表情回复和文字回复信息。
|
||||||
|
|
||||||
|
```
|
||||||
|
Usage:
|
||||||
|
dws chat message list-emotion-replies [flags]
|
||||||
|
Example:
|
||||||
|
dws chat message list-emotion-replies --msg-ids msgId1,msgId2,msgId3
|
||||||
|
# 消息 ID 可通过 dws chat message list 获取
|
||||||
|
Flags:
|
||||||
|
--msg-ids string 消息 ID 列表,逗号分隔 (必填)
|
||||||
|
```
|
||||||
|
|
||||||
### list-top-conversations (置顶会话)
|
### list-top-conversations (置顶会话)
|
||||||
|
|
||||||
#### 拉取置顶会话列表
|
#### 拉取置顶会话列表
|
||||||
@@ -1309,6 +1402,21 @@ Flags:
|
|||||||
--title string 分组名称 (必填)
|
--title string 分组名称 (必填)
|
||||||
```
|
```
|
||||||
|
|
||||||
|
#### 创建智能会话分组 — 可指定群名称关键词和群内成员作为匹配规则
|
||||||
|
```
|
||||||
|
Usage:
|
||||||
|
dws chat category create-smart [flags]
|
||||||
|
Example:
|
||||||
|
dws chat category create-smart --name "工作群"
|
||||||
|
dws chat category create-smart --name "项目组" --keywords "项目,开发"
|
||||||
|
dws chat category create-smart --name "团队群" --members openDingTalkId1,openDingTalkId2
|
||||||
|
dws chat category create-smart --name "重点群" --keywords "重点" --members openDingTalkId1
|
||||||
|
Flags:
|
||||||
|
--name string 分组名称 (必填)
|
||||||
|
--keywords string 群名称关键词列表,逗号分隔(可选)
|
||||||
|
--members string 群内成员 openDingTalkId 列表,逗号分隔(可选)
|
||||||
|
```
|
||||||
|
|
||||||
#### 删除会话分组
|
#### 删除会话分组
|
||||||
```
|
```
|
||||||
Usage:
|
Usage:
|
||||||
@@ -1358,6 +1466,23 @@ Flags:
|
|||||||
--category-ids string 目标分组 ID 列表,逗号分隔 (必填)
|
--category-ids string 目标分组 ID 列表,逗号分隔 (必填)
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### text (文本内容处理)
|
||||||
|
|
||||||
|
#### 翻译文本内容
|
||||||
|
```
|
||||||
|
Usage:
|
||||||
|
dws chat text translate [flags]
|
||||||
|
Example:
|
||||||
|
dws chat text translate --query "你好世界" --to en_US
|
||||||
|
dws chat text translate --query "Hello World" --to zh_CN
|
||||||
|
dws chat text translate --query "Bonjour" --to ja_JP
|
||||||
|
Flags:
|
||||||
|
--query string 待翻译的文本内容 (必填)
|
||||||
|
--to string 目标语言代码 (必填)
|
||||||
|
```
|
||||||
|
|
||||||
|
支持的目标语言代码:`en_US`, `zh_CN`, `zh_TW`, `zh_HK`, `ja_JP`, `ko_KR`, `vi_VN`, `th_TH`, `id_ID`, `ms_MY`, `es_419`, `fr_FR`, `pt_BR`, `tr_TR`, `ru_RU`, `de_DE`, `hi_IN`, `hu_HU`, `pl_PL`, `sv_SE`, `fi_FI`, `cs_CZ`, `ar_SA`, `tl_PH`, `he_IL`, `nl_NL`, `lo_LA`, `it_IT`。
|
||||||
|
|
||||||
### mute (会话免打扰)
|
### mute (会话免打扰)
|
||||||
|
|
||||||
#### 会话消息免打扰 — 开启或关闭会话消息免打扰(支持单聊和群聊)
|
#### 会话消息免打扰 — 开启或关闭会话消息免打扰(支持单聊和群聊)
|
||||||
@@ -1679,6 +1804,7 @@ Flags:
|
|||||||
用户说"查看会话分组/自定义分组" → `chat category list`
|
用户说"查看会话分组/自定义分组" → `chat category list`
|
||||||
用户说"某个分组下的会话/分组会话列表" → `chat category list-conversations`
|
用户说"某个分组下的会话/分组会话列表" → `chat category list-conversations`
|
||||||
用户说"新建会话分组/创建分组" → `chat category create`
|
用户说"新建会话分组/创建分组" → `chat category create`
|
||||||
|
用户说"创建智能分组/新建智能会话分组" → `chat category create-smart`
|
||||||
用户说"删除会话分组" → `chat category delete`
|
用户说"删除会话分组" → `chat category delete`
|
||||||
用户说"重命名分组/改分组名" → `chat category rename`
|
用户说"重命名分组/改分组名" → `chat category rename`
|
||||||
用户说"把会话加入分组/会话归类到分组" → `chat category add-conv`
|
用户说"把会话加入分组/会话归类到分组" → `chat category add-conv`
|
||||||
@@ -1693,7 +1819,10 @@ Flags:
|
|||||||
用户说"查询某人的群身份/某人在群里有什么身份" → `chat group-role query-user`
|
用户说"查询某人的群身份/某人在群里有什么身份" → `chat group-role query-user`
|
||||||
用户说"转让群主/换群主/群主转让" → `chat group transfer-owner`
|
用户说"转让群主/换群主/群主转让" → `chat group transfer-owner`
|
||||||
用户说"群邀请链接/入群链接/加群链接" → `chat group invite-url`
|
用户说"群邀请链接/入群链接/加群链接" → `chat group invite-url`
|
||||||
|
用户说"分享群链接/把群分享给某人/群链接发到某群" → `chat group share-invite`
|
||||||
|
用户说"群公告/发布公告/修改公告/查看公告/定时公告" → `chat group notice create/edit/get/list`
|
||||||
用户说"批量查消息/按ID查消息/根据消息ID查" → `chat message list-by-ids`
|
用户说"批量查消息/按ID查消息/根据消息ID查" → `chat message list-by-ids`
|
||||||
|
用户说"批量查消息回复/表情回复/文字回复/消息回应列表" → `chat message list-emotion-replies`
|
||||||
用户说"emoji回应/表情回应/给消息加表情" → `chat message add-emoji`
|
用户说"emoji回应/表情回应/给消息加表情" → `chat message add-emoji`
|
||||||
用户说"取消emoji回应/移除表情回应" → `chat message remove-emoji`
|
用户说"取消emoji回应/移除表情回应" → `chat message remove-emoji`
|
||||||
用户说"文字表情回应/添加文字表情" → `chat message add-text-emotion`
|
用户说"文字表情回应/添加文字表情" → `chat message add-text-emotion`
|
||||||
@@ -1732,6 +1861,7 @@ Flags:
|
|||||||
用户说"全员禁言/群禁言/解除禁言" → `chat group-mute`
|
用户说"全员禁言/群禁言/解除禁言" → `chat group-mute`
|
||||||
用户说"禁言某人/指定成员禁言/解除某人禁言" → `chat group-mute-member`
|
用户说"禁言某人/指定成员禁言/解除某人禁言" → `chat group-mute-member`
|
||||||
用户说"设管理员/取消管理员/设置群管理员" → `chat group set-admin`
|
用户说"设管理员/取消管理员/设置群管理员" → `chat group set-admin`
|
||||||
|
用户说"翻译文本/把这段话翻译成英文/翻译成中文" → `chat text translate`
|
||||||
|
|
||||||
关键区分:
|
关键区分:
|
||||||
- `chat search` — 搜**群/会话名**返回 `openConversationId`,**不**搜消息内容;要搜消息内容请用 `chat message search-advanced`(首选)/ `chat message search` / `list-by-sender` / `list-all`,**勿混淆**
|
- `chat search` — 搜**群/会话名**返回 `openConversationId`,**不**搜消息内容;要搜消息内容请用 `chat message search-advanced`(首选)/ `chat message search` / `list-by-sender` / `list-all`,**勿混淆**
|
||||||
@@ -1756,10 +1886,12 @@ Flags:
|
|||||||
- `chat message search-advanced` — 多维度搜索消息(支持关键词、发送者、@我、@指定人、多个会话等维度组合,与 `search` 的区别:`search` 仅支持关键词且必填,`search-advanced` 所有参数均可选)
|
- `chat message search-advanced` — 多维度搜索消息(支持关键词、发送者、@我、@指定人、多个会话等维度组合,与 `search` 的区别:`search` 仅支持关键词且必填,`search-advanced` 所有参数均可选)
|
||||||
- `chat message list-by-ids` — 根据消息 ID 批量查询消息(最多 50 条)
|
- `chat message list-by-ids` — 根据消息 ID 批量查询消息(最多 50 条)
|
||||||
- `chat message add-emoji` / `remove-emoji` — 对消息添加/移除 emoji 表情回应
|
- `chat message add-emoji` / `remove-emoji` — 对消息添加/移除 emoji 表情回应
|
||||||
|
- `chat message list-emotion-replies` — 批量拉取消息的表情回复和文字回复
|
||||||
- `chat message add-text-emotion` / `remove-text-emotion` — 对消息添加/移除文字表情回应
|
- `chat message add-text-emotion` / `remove-text-emotion` — 对消息添加/移除文字表情回应
|
||||||
- `chat message create-text-emotion` — 创建文字表情模板,返回 emotionId 供 add-text-emotion 使用
|
- `chat message create-text-emotion` — 创建文字表情模板,返回 emotionId 供 add-text-emotion 使用
|
||||||
- `chat category list` — 获取用户自定义会话分组列表
|
- `chat category list` — 获取用户自定义会话分组列表
|
||||||
- `chat category list-conversations` — 拉取指定分组下的会话列表
|
- `chat category list-conversations` — 拉取指定分组下的会话列表
|
||||||
|
- `chat category create-smart` — 创建智能会话分组(可指定群名称关键词和成员作为匹配规则)
|
||||||
- `chat mute` — 开启/关闭会话消息免打扰(默认开启,--off 关闭)
|
- `chat mute` — 开启/关闭会话消息免打扰(默认开启,--off 关闭)
|
||||||
- `chat hide` — 在会话列表中隐藏会话(支持单聊/群聊,收到新消息时重新出现)
|
- `chat hide` — 在会话列表中隐藏会话(支持单聊/群聊,收到新消息时重新出现)
|
||||||
- `chat mute-at-all` — 关闭/开启 @所有人消息提醒(默认关闭,--off 恢复)
|
- `chat mute-at-all` — 关闭/开启 @所有人消息提醒(默认关闭,--off 恢复)
|
||||||
@@ -1773,6 +1905,8 @@ Flags:
|
|||||||
- `chat file upload` — 已下线;不要调用 `chat/upload_conversation_file_by_url`,本地文件发送改用 `chat message send --msg-type file --file-path`
|
- `chat file upload` — 已下线;不要调用 `chat/upload_conversation_file_by_url`,本地文件发送改用 `chat message send --msg-type file --file-path`
|
||||||
- `chat group transfer-owner` — 转让群主
|
- `chat group transfer-owner` — 转让群主
|
||||||
- `chat group invite-url` — 获取群邀请链接
|
- `chat group invite-url` — 获取群邀请链接
|
||||||
|
- `chat group share-invite` — 分享群聊链接到会话
|
||||||
|
- `chat group notice create/edit/get/list` — 群公告发布、修改、详情、列表
|
||||||
- `chat message reply` — 引用回复消息(在群聊中引用某条消息并回复文字)
|
- `chat message reply` — 引用回复消息(在群聊中引用某条消息并回复文字)
|
||||||
- `chat message forward` — 转发单条消息(将一条消息从源会话转发到目标会话)
|
- `chat message forward` — 转发单条消息(将一条消息从源会话转发到目标会话)
|
||||||
- `chat set-top` — 设置/取消会话置顶(默认置顶,--off 取消)
|
- `chat set-top` — 设置/取消会话置顶(默认置顶,--off 取消)
|
||||||
|
|||||||
@@ -131,6 +131,27 @@ Flags:
|
|||||||
--convert 是否转换为钉钉在线文档
|
--convert 是否转换为钉钉在线文档
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### 导入本地文件为在线文档
|
||||||
|
|
||||||
|
详见 [doc/doc-import.md](./doc/doc-import.md)。
|
||||||
|
|
||||||
|
```
|
||||||
|
Usage:
|
||||||
|
dws doc import [flags]
|
||||||
|
dws doc import get [flags]
|
||||||
|
Example:
|
||||||
|
dws doc import --file ./report.docx
|
||||||
|
dws doc import --file ./notes.md --folder <FOLDER_ID>
|
||||||
|
dws doc import --file ./data.xlsx --workspace <WS_ID>
|
||||||
|
dws doc import get --task-id <TASK_ID>
|
||||||
|
Flags:
|
||||||
|
--file string 本地文件路径 (必填)
|
||||||
|
--folder string 目标文件夹 ID 或 URL
|
||||||
|
--workspace string 目标知识库 ID 或 URL
|
||||||
|
--name string 导入后文档名称 (默认取文件名)
|
||||||
|
--task-id string 导入任务 ID (import get 必填)
|
||||||
|
```
|
||||||
|
|
||||||
### 下载文件到本地(已迁移到 drive)
|
### 下载文件到本地(已迁移到 drive)
|
||||||
|
|
||||||
> **弃用提示**:`dws doc download` 已迁移到 `dws drive download`(执行 `doc download` 会打印弃用警告)。下载已有文件(PDF/图片/附件等非在线文档)改用:
|
> **弃用提示**:`dws doc download` 已迁移到 `dws drive download`(执行 `doc download` 会打印弃用警告)。下载已有文件(PDF/图片/附件等非在线文档)改用:
|
||||||
@@ -618,6 +639,12 @@ Flags:
|
|||||||
- 上传 → `upload`(需本地文件路径)
|
- 上传 → `upload`(需本地文件路径)
|
||||||
- 上传并转换 → `upload --convert`
|
- 上传并转换 → `upload --convert`
|
||||||
|
|
||||||
|
用户说"导入文件/导入为在线文档/导入 Word/导入 Excel/导入 xmind/导入 Markdown/把本地文件转在线文档":
|
||||||
|
- 导入并转换为在线文档 → `doc import --file <本地路径>`
|
||||||
|
- 支持 docx/doc/xlsx/xls/md/txt/xmind/mark,文件大小不超过 20MB
|
||||||
|
- 如果用户指定知识库或文件夹,补充 `--workspace` 或 `--folder`
|
||||||
|
- 不要把本地文件内容先读出来再 `doc create/update`;应直接使用 `doc import`
|
||||||
|
|
||||||
用户说"下载/导出/下载到本地/导出文档/导出为Word/导出为docx/把文档导出来":
|
用户说"下载/导出/下载到本地/导出文档/导出为Word/导出为docx/把文档导出来":
|
||||||
- **必须先判断目标文件类型**,再决定走 `doc export` 还是 `drive download`:
|
- **必须先判断目标文件类型**,再决定走 `doc export` 还是 `drive download`:
|
||||||
- 在线文档(alidocs/adoc)→ **`doc export`**(内容级命令,格式转换后导出为 docx,未迁移)
|
- 在线文档(alidocs/adoc)→ **`doc export`**(内容级命令,格式转换后导出为 docx,未迁移)
|
||||||
@@ -768,6 +795,20 @@ dws drive download --node <NODE_ID> --output ~/downloads/
|
|||||||
# 如果是在线文档 (ALIDOC),用 doc export:
|
# 如果是在线文档 (ALIDOC),用 doc export:
|
||||||
dws doc export --node <NODE_ID> --output ~/downloads/
|
dws doc export --node <NODE_ID> --output ~/downloads/
|
||||||
|
|
||||||
|
# ── 工作流 5a: 导入本地文件为在线文档 ──
|
||||||
|
|
||||||
|
# 导入到默认位置
|
||||||
|
dws doc import --file ./report.docx --format json
|
||||||
|
|
||||||
|
# 导入到指定文件夹
|
||||||
|
dws doc import --file ./notes.md --folder <DOC_FOLDER_NODE_ID> --format json
|
||||||
|
|
||||||
|
# 导入到知识库
|
||||||
|
dws doc import --file ./data.xlsx --workspace <WS_ID> --format json
|
||||||
|
|
||||||
|
# 如果导入命令超时或中断,可用 import get 手动查询任务状态:
|
||||||
|
# dws doc import get --task-id <TASK_ID>
|
||||||
|
|
||||||
# ── 工作流 6: 上传附件并插入文档 ──
|
# ── 工作流 6: 上传附件并插入文档 ──
|
||||||
|
|
||||||
# media insert 自动完成三步流程:
|
# media insert 自动完成三步流程:
|
||||||
@@ -882,6 +923,7 @@ dws doc export --node <DOC_ID_OR_URL> --output ./exported.docx
|
|||||||
| `drive list`(原 `doc list`,已弃用) | `nodes[].nodeId` / folder 类型 `nodeId` | read / info / update / block 的 --node;folder 用作 `--folder` |
|
| `drive list`(原 `doc list`,已弃用) | `nodes[].nodeId` / folder 类型 `nodeId` | read / info / update / block 的 --node;folder 用作 `--folder` |
|
||||||
| `drive search`(原 `doc search`,已弃用) | 文档 `nodeId` / URL / `createTime` / `creatorUid` | read / info / update 的 --node;创建时间与创建者信息 |
|
| `drive search`(原 `doc search`,已弃用) | 文档 `nodeId` / URL / `createTime` / `creatorUid` | read / info / update 的 --node;创建时间与创建者信息 |
|
||||||
| `create` | `nodeId` | update / block 操作的 --node |
|
| `create` | `nodeId` | update / block 操作的 --node |
|
||||||
|
| `import` | `nodeId` / `documentUrl` / `documentName` / `documentType`;中断时提取 `taskId` | 后续 read / info / sheet 操作;中断后用 `doc import get --task-id` |
|
||||||
| `drive mkdir`(原 `doc folder create`,已弃用) | `nodeId` | create 的 --folder |
|
| `drive mkdir`(原 `doc folder create`,已弃用) | `nodeId` | create 的 --folder |
|
||||||
| `block list` | `blockId` | block insert 的 --ref-block, block update/delete 的 --block-id |
|
| `block list` | `blockId` | block insert 的 --ref-block, block update/delete 的 --block-id |
|
||||||
| `comment list` | `commentList[].commentKey` | comment reply 的 --comment-key |
|
| `comment list` | `commentList[].commentKey` | comment reply 的 --comment-key |
|
||||||
|
|||||||
@@ -0,0 +1,53 @@
|
|||||||
|
# 本地文件导入为在线文档 (doc import)
|
||||||
|
|
||||||
|
## 使用场景
|
||||||
|
|
||||||
|
用户说"导入 Word/Excel/Markdown/xmind 到钉钉文档"、"把本地文件转成在线文档"、"导入到知识库/文件夹"时,使用 `dws doc import`。
|
||||||
|
|
||||||
|
不要先读取文件内容再调用 `doc create` 或 `doc update`。`doc import` 会按文件格式走导入任务,保留更完整的原始结构。
|
||||||
|
|
||||||
|
## 命令
|
||||||
|
|
||||||
|
```bash
|
||||||
|
dws doc import --file ./report.docx --format json
|
||||||
|
dws doc import --file ./notes.md --folder <FOLDER_ID> --format json
|
||||||
|
dws doc import --file ./data.xlsx --workspace <WORKSPACE_ID> --format json
|
||||||
|
dws doc import --file ./draft.md --name "项目周报" --format json
|
||||||
|
```
|
||||||
|
|
||||||
|
```bash
|
||||||
|
dws doc import get --task-id <TASK_ID> --format json
|
||||||
|
```
|
||||||
|
|
||||||
|
## 参数
|
||||||
|
|
||||||
|
| 参数 | 说明 |
|
||||||
|
|------|------|
|
||||||
|
| `--file` | 本地文件路径,必填 |
|
||||||
|
| `--folder` | 目标文件夹 ID 或 URL,可选 |
|
||||||
|
| `--workspace` | 目标知识库 ID 或 URL,可选 |
|
||||||
|
| `--name` / `-n` | 导入后的文档名称;不传时使用文件名 |
|
||||||
|
| `--task-id` | `import get` 查询导入任务时必填 |
|
||||||
|
|
||||||
|
支持格式:docx、doc、xlsx、xls、md、txt、xmind、mark。文件大小上限 20MB。
|
||||||
|
|
||||||
|
## 工作流
|
||||||
|
|
||||||
|
1. 确认本地文件存在且格式受支持。
|
||||||
|
2. 如果用户指定目标知识库或文件夹,传 `--workspace` 或 `--folder`;未指定时导入到默认位置。
|
||||||
|
3. 执行 `dws doc import --file ... --format json`。
|
||||||
|
4. 正常情况下 CLI 会自动提交、上传并轮询导入任务。
|
||||||
|
5. 如果命令超时或中断,从输出中提取 `taskId`,再执行 `dws doc import get --task-id <TASK_ID> --format json`。
|
||||||
|
|
||||||
|
## 上下文传递
|
||||||
|
|
||||||
|
| 操作 | 从返回中提取 | 用于 |
|
||||||
|
|------|-------------|------|
|
||||||
|
| `doc import` | `nodeId` / `documentUrl` / `documentName` / `documentType` | 后续 `doc read` / `doc info` / `sheet` 操作 |
|
||||||
|
| `doc import` 中断 | `taskId` | `doc import get --task-id` 查询任务状态 |
|
||||||
|
|
||||||
|
## 注意事项
|
||||||
|
|
||||||
|
- `--folder` 和 `--workspace` 都是目标位置参数;用户明确指定文件夹时优先使用 `--folder`。
|
||||||
|
- 导入 Excel 后通常得到在线表格,后续数据读取和编辑走 `dws sheet ...`。
|
||||||
|
- 导入 Markdown 或 Word 后通常得到在线文档,后续内容读取和编辑走 `dws doc ...`。
|
||||||
@@ -5,6 +5,7 @@
|
|||||||
| 命令 | 功能简述 |
|
| 命令 | 功能简述 |
|
||||||
|------|----------|
|
|------|----------|
|
||||||
| `dws mail mailbox list` | 查询**当前用户自己**的可用邮箱列表 |
|
| `dws mail mailbox list` | 查询**当前用户自己**的可用邮箱列表 |
|
||||||
|
| `dws mail mailbox profile` | 获取用户邮箱详细信息(容量、别名等) |
|
||||||
| `dws mail message list` | 列出指定文件夹中的邮件(默认收件箱) |
|
| `dws mail message list` | 列出指定文件夹中的邮件(默认收件箱) |
|
||||||
| `dws mail message search` | 搜索邮件(KQL 语法,按主题/发件人/日期等) |
|
| `dws mail message search` | 搜索邮件(KQL 语法,按主题/发件人/日期等) |
|
||||||
| `dws mail message get` | 查看邮件完整内容(含正文) |
|
| `dws mail message get` | 查看邮件完整内容(含正文) |
|
||||||
@@ -15,7 +16,10 @@
|
|||||||
| `dws mail message batch-move` | 批量移动邮件到指定文件夹 |
|
| `dws mail message batch-move` | 批量移动邮件到指定文件夹 |
|
||||||
| `dws mail message batch-delete` | 批量删除邮件 |
|
| `dws mail message batch-delete` | 批量删除邮件 |
|
||||||
| `dws mail message batch-update` | 批量修改邮件状态(标记已读/未读/添加标签/移除标签) |
|
| `dws mail message batch-update` | 批量修改邮件状态(标记已读/未读/添加标签/移除标签) |
|
||||||
|
| `dws mail message batch-get` | 批量获取邮件详情(最多 20 封) |
|
||||||
| `dws mail message verify` | 根据 internetMessageId 查询邮件发送状态 |
|
| `dws mail message verify` | 根据 internetMessageId 查询邮件发送状态 |
|
||||||
|
| `dws mail sent-message recall` | 撤回已发送的邮件(仅支持同组织内未读邮件) |
|
||||||
|
| `dws mail sent-message recall-detail` | 查询邮件撤回进度 |
|
||||||
| `dws mail draft create` | 创建草稿(保留在草稿箱,不发送) |
|
| `dws mail draft create` | 创建草稿(保留在草稿箱,不发送) |
|
||||||
| `dws mail draft update` | 更新草稿内容(保留在草稿箱,不发送) |
|
| `dws mail draft update` | 更新草稿内容(保留在草稿箱,不发送) |
|
||||||
| `dws mail draft send` | 发送草稿箱中已有的草稿 |
|
| `dws mail draft send` | 发送草稿箱中已有的草稿 |
|
||||||
@@ -46,6 +50,13 @@
|
|||||||
| `dws mail contact update` | 更新个人邮件联系人信息 |
|
| `dws mail contact update` | 更新个人邮件联系人信息 |
|
||||||
| `dws mail contact batch-delete` | 批量删除个人邮件联系人 |
|
| `dws mail contact batch-delete` | 批量删除个人邮件联系人 |
|
||||||
| `dws mail auto-reply get` | 获取用户的自动回复配置 |
|
| `dws mail auto-reply get` | 获取用户的自动回复配置 |
|
||||||
|
| `dws mail auto-reply update` | 更新/设置用户的自动回复配置 |
|
||||||
|
| `dws mail allow-list list` | 列出个人收信白名单 |
|
||||||
|
| `dws mail allow-list add` | 添加个人收信白名单 |
|
||||||
|
| `dws mail allow-list remove` | 移除个人收信白名单 |
|
||||||
|
| `dws mail block-list list` | 列出个人收信黑名单 |
|
||||||
|
| `dws mail block-list add` | 添加个人收信黑名单 |
|
||||||
|
| `dws mail block-list remove` | 移除个人收信黑名单 |
|
||||||
| `dws mail rule list` | 列出个人收信规则 |
|
| `dws mail rule list` | 列出个人收信规则 |
|
||||||
| `dws mail rule create` | 创建个人收信规则 |
|
| `dws mail rule create` | 创建个人收信规则 |
|
||||||
| `dws mail rule update` | 更新个人收信规则 |
|
| `dws mail rule update` | 更新个人收信规则 |
|
||||||
@@ -93,6 +104,27 @@ Example:
|
|||||||
|------|------|------|
|
|------|------|------|
|
||||||
| `emailAccounts` | `List[]` | 邮箱列表,每条包含 `email`(邮箱地址)、`orgName`(所属企业,个人邮箱为 null)、`type`(`ORG`=企业邮箱 / `PERSONAL`=个人邮箱) |
|
| `emailAccounts` | `List[]` | 邮箱列表,每条包含 `email`(邮箱地址)、`orgName`(所属企业,个人邮箱为 null)、`type`(`ORG`=企业邮箱 / `PERSONAL`=个人邮箱) |
|
||||||
|
|
||||||
|
### 获取用户邮箱信息
|
||||||
|
|
||||||
|
```
|
||||||
|
Usage:
|
||||||
|
dws mail mailbox profile [flags]
|
||||||
|
Example:
|
||||||
|
dws mail mailbox profile --email user@company.com
|
||||||
|
Flags:
|
||||||
|
--email string 用户的邮箱地址 (必填)
|
||||||
|
```
|
||||||
|
|
||||||
|
**返回字段:**
|
||||||
|
|
||||||
|
| 字段 | 类型 | 说明 |
|
||||||
|
|------|------|------|
|
||||||
|
| `email` | string | 邮箱地址 |
|
||||||
|
| `emailAliases` | List[] | 邮件地址别名列表 |
|
||||||
|
| `name` / `nickname` / `displayName` | string | 用户名、昵称、显示名 |
|
||||||
|
| `mboxSize` / `mboxSizeUsed` | int64 | 邮箱容量与已用容量(字节) |
|
||||||
|
| `createdTime` / `modifiedTime` | string | 创建时间与更新时间 |
|
||||||
|
|
||||||
### 查找他人邮箱地址(通讯录查人)
|
### 查找他人邮箱地址(通讯录查人)
|
||||||
|
|
||||||
> **这不是 `mailbox list`。** 当需要获取**某人**的邮箱地址时,必须走以下三路并发查询,取最先返回有效邮箱的结果。禁止臆测邮箱地址。
|
> **这不是 `mailbox list`。** 当需要获取**某人**的邮箱地址时,必须走以下三路并发查询,取最先返回有效邮箱的结果。禁止臆测邮箱地址。
|
||||||
@@ -218,6 +250,21 @@ Flags:
|
|||||||
|
|
||||||
> **注意:** 即使邮件确有附件,`message get` 返回的 `message` 对象也**不含** `attachments`/`isRead`/`tags` 字段(实测 keys 仅上表所列这些)。要取附件请另用 `dws mail attachment list`。
|
> **注意:** 即使邮件确有附件,`message get` 返回的 `message` 对象也**不含** `attachments`/`isRead`/`tags` 字段(实测 keys 仅上表所列这些)。要取附件请另用 `dws mail attachment list`。
|
||||||
|
|
||||||
|
### 批量获取邮件详情
|
||||||
|
|
||||||
|
```
|
||||||
|
Usage:
|
||||||
|
dws mail message batch-get [flags]
|
||||||
|
Example:
|
||||||
|
dws mail message batch-get --email user@company.com --ids <id1>,<id2>
|
||||||
|
dws mail message batch-get --email user@company.com --ids <id1>,<id2>,<id3>
|
||||||
|
Flags:
|
||||||
|
--email string 邮件所属邮箱地址 (必填)
|
||||||
|
--ids string 要获取的邮件 ID 列表,逗号分隔,最多 20 个 (必填)
|
||||||
|
```
|
||||||
|
|
||||||
|
单次最多获取 20 封邮件。CLI 会逐个调用 `get_email_by_message_id`,并以 `messages` 数组聚合返回。
|
||||||
|
|
||||||
### 发送邮件
|
### 发送邮件
|
||||||
```
|
```
|
||||||
Usage:
|
Usage:
|
||||||
@@ -935,6 +982,53 @@ Flags:
|
|||||||
| `failed` | 发送失败 |
|
| `failed` | 发送失败 |
|
||||||
| `unknown` | 未知状态 |
|
| `unknown` | 未知状态 |
|
||||||
|
|
||||||
|
### [危险] 撤回已发送的邮件
|
||||||
|
|
||||||
|
```
|
||||||
|
Usage:
|
||||||
|
dws mail sent-message recall [flags]
|
||||||
|
Example:
|
||||||
|
dws mail sent-message recall --email user@company.com --id <mailId> --subject "邮件主题" --yes
|
||||||
|
Flags:
|
||||||
|
--email string 发件人邮箱地址 (必填)
|
||||||
|
--id string 要撤回的邮件 ID (必填)
|
||||||
|
--subject string 邮件主题 (必填)
|
||||||
|
--yes 跳过确认提示,直接执行 (可选)
|
||||||
|
```
|
||||||
|
|
||||||
|
> CAUTION: 此命令会撤回已发送邮件。仅支持撤回同组织内未读邮件,执行前必须确认目标邮件、主题和影响范围。
|
||||||
|
|
||||||
|
**返回字段:**
|
||||||
|
|
||||||
|
| 字段 | 类型 | 说明 |
|
||||||
|
|------|------|------|
|
||||||
|
| `id` | string | 撤回任务 ID,可用于 `recall-detail` 查询进度 |
|
||||||
|
| `success` | boolean | 接口调用是否成功 |
|
||||||
|
| `errorCode` / `errorMsg` | string | 错误码和错误信息(仅失败时存在) |
|
||||||
|
|
||||||
|
### 查询邮件撤回进度
|
||||||
|
|
||||||
|
```
|
||||||
|
Usage:
|
||||||
|
dws mail sent-message recall-detail [flags]
|
||||||
|
Example:
|
||||||
|
dws mail sent-message recall-detail --email user@company.com --id <recallTaskId>
|
||||||
|
Flags:
|
||||||
|
--email string 用户的邮箱地址 (必填)
|
||||||
|
--id string 撤回任务 ID (必填),由 recall 命令返回
|
||||||
|
```
|
||||||
|
|
||||||
|
根据撤回任务 ID 查询邮件撤回进度。任务状态包括 `UNINITED` / `SUBMITTED` / `RUNNING` / `FINISHED` / `CANCELED` / `FAILED`。
|
||||||
|
|
||||||
|
**返回字段:**
|
||||||
|
|
||||||
|
| 字段 | 类型 | 说明 |
|
||||||
|
|------|------|------|
|
||||||
|
| `id` | string | 撤回任务 ID |
|
||||||
|
| `status` | string | 任务状态 |
|
||||||
|
| `totalCount` / `succeededCount` / `failedCount` | int | 总数、成功数、失败数 |
|
||||||
|
| `details` | List[] | 每封邮件的撤回结果 |
|
||||||
|
|
||||||
### 创建草稿
|
### 创建草稿
|
||||||
```
|
```
|
||||||
Usage:
|
Usage:
|
||||||
@@ -1220,6 +1314,61 @@ Flags:
|
|||||||
| `scope` | string | 回复范围: "contact"(仅联系人) 或 "all"(所有人) |
|
| `scope` | string | 回复范围: "contact"(仅联系人) 或 "all"(所有人) |
|
||||||
| `content` | string | 自动回复内容 |
|
| `content` | string | 自动回复内容 |
|
||||||
|
|
||||||
|
### 更新自动回复配置
|
||||||
|
|
||||||
|
更新或设置用户的邮件自动回复配置。建议先通过 `auto-reply get` 获取当前配置,再传入需要修改的字段值。
|
||||||
|
|
||||||
|
```
|
||||||
|
Usage:
|
||||||
|
dws mail auto-reply update [flags]
|
||||||
|
Example:
|
||||||
|
dws mail auto-reply update --email user@company.com --enabled true \
|
||||||
|
--start "2026/07/01 09:00:00 +0800" --end "2026/07/07 18:00:00 +0800" \
|
||||||
|
--scope all --content "出差中,请稍后联系"
|
||||||
|
dws mail auto-reply update --email user@company.com --enabled false \
|
||||||
|
--start "2026/07/01 09:00:00 +0800" --end "2026/07/07 18:00:00 +0800" \
|
||||||
|
--scope all --content "已关闭自动回复"
|
||||||
|
Flags:
|
||||||
|
--email string 用户的邮箱地址 (必填)
|
||||||
|
--enabled string 是否启用自动回复: true/false (必填)
|
||||||
|
--start string 自动回复开始时间,格式: YYYY/MM/DD HH:MM:SS +ZZZZ (必填)
|
||||||
|
--end string 自动回复结束时间,格式: YYYY/MM/DD HH:MM:SS +ZZZZ (必填)
|
||||||
|
--scope string 回复范围: contact(仅联系人)/all(所有人) (必填)
|
||||||
|
--content string 自动回复内容 (必填)
|
||||||
|
```
|
||||||
|
|
||||||
|
### 个人收信白名单管理
|
||||||
|
|
||||||
|
```
|
||||||
|
Usage:
|
||||||
|
dws mail allow-list list [flags]
|
||||||
|
dws mail allow-list add [flags]
|
||||||
|
dws mail allow-list remove [flags]
|
||||||
|
Examples:
|
||||||
|
dws mail allow-list list --email user@company.com
|
||||||
|
dws mail allow-list add --email user@company.com --entries a@b.com,@example.com
|
||||||
|
dws mail allow-list remove --email user@company.com --entries a@b.com,@example.com
|
||||||
|
Flags:
|
||||||
|
--email string 用户的邮箱地址 (必填)
|
||||||
|
--entries string 逗号分隔的地址列表,支持邮件地址或 @domain.com 域名(add/remove 必填)
|
||||||
|
```
|
||||||
|
|
||||||
|
### 个人收信黑名单管理
|
||||||
|
|
||||||
|
```
|
||||||
|
Usage:
|
||||||
|
dws mail block-list list [flags]
|
||||||
|
dws mail block-list add [flags]
|
||||||
|
dws mail block-list remove [flags]
|
||||||
|
Examples:
|
||||||
|
dws mail block-list list --email user@company.com
|
||||||
|
dws mail block-list add --email user@company.com --entries spam@bad.com,@junk.com
|
||||||
|
dws mail block-list remove --email user@company.com --entries spam@bad.com,@junk.com
|
||||||
|
Flags:
|
||||||
|
--email string 用户的邮箱地址 (必填)
|
||||||
|
--entries string 逗号分隔的地址列表,支持邮件地址或 @domain.com 域名(add/remove 必填)
|
||||||
|
```
|
||||||
|
|
||||||
### 收信规则管理
|
### 收信规则管理
|
||||||
|
|
||||||
#### 列出收信规则
|
#### 列出收信规则
|
||||||
@@ -1473,9 +1622,11 @@ Flags:
|
|||||||
## 意图判断
|
## 意图判断
|
||||||
|
|
||||||
用户说"我的邮箱/邮箱地址" → `mailbox list`(**仅限查询自己的邮箱,不能查他人**)
|
用户说"我的邮箱/邮箱地址" → `mailbox list`(**仅限查询自己的邮箱,不能查他人**)
|
||||||
|
用户说"邮箱详情/邮箱容量/邮箱别名/邮箱 profile" → `mailbox profile --email <邮箱>`
|
||||||
用户说"获取/查找/得到 某人的邮箱地址" → **不是 `mailbox list`**,走三路并发查询流程(见「查找他人邮箱地址」章节)
|
用户说"获取/查找/得到 某人的邮箱地址" → **不是 `mailbox list`**,走三路并发查询流程(见「查找他人邮箱地址」章节)
|
||||||
用户说"找邮件/搜邮件/查邮件" → `message search`
|
用户说"找邮件/搜邮件/查邮件" → `message search`
|
||||||
用户说"看邮件/打开邮件/邮件内容" → 先 `message search` 获取 messageId,再 `message get`
|
用户说"看邮件/打开邮件/邮件内容" → 先 `message search` 获取 messageId,再 `message get`
|
||||||
|
用户说"批量查看邮件详情/批量打开这些邮件/按多个邮件ID取详情" → `message batch-get --ids <id1,id2,...>`(单次最多 20 个)
|
||||||
用户说"发邮件/写邮件" → 先 `mailbox list` 获取发件地址,再 `message send`
|
用户说"发邮件/写邮件" → 先 `mailbox list` 获取发件地址,再 `message send`
|
||||||
用户说“给(某人名字)发邮件” / “查询某人发给我的邮件” / “查询发给某人的邮件” / 任何涉及按人名查找邮箱的场景 →
|
用户说“给(某人名字)发邮件” / “查询某人发给我的邮件” / “查询发给某人的邮件” / 任何涉及按人名查找邮箱的场景 →
|
||||||
**第一步**:并发同时发起以下三路查询,取最先返回有效邮箱的结果;若三路均无有效邮箱,ask_human 请用户提供,禁止臆测:
|
**第一步**:并发同时发起以下三路查询,取最先返回有效邮箱的结果;若三路均无有效邮箱,ask_human 请用户提供,禁止臆测:
|
||||||
@@ -1502,6 +1653,11 @@ Flags:
|
|||||||
用户说"删除会话/把会话放入已删除/批量删除会话" → 单条用 `thread trash`,多条用 `thread batch-trash`;传入的是会话 ID,不是邮件 ID
|
用户说"删除会话/把会话放入已删除/批量删除会话" → 单条用 `thread trash`,多条用 `thread batch-trash`;传入的是会话 ID,不是邮件 ID
|
||||||
用户说"批量标记邮件已读/未读/给邮件加标签/移除邮件标签" → `message batch-update --action markRead/markUnread/addTags/removeTags`(操作对象是邮件 ID;会话级操作用 `thread update/batch-update`)
|
用户说"批量标记邮件已读/未读/给邮件加标签/移除邮件标签" → `message batch-update --action markRead/markUnread/addTags/removeTags`(操作对象是邮件 ID;会话级操作用 `thread update/batch-update`)
|
||||||
用户说"邮件发出去了吗/查邮件发送状态/确认邮件是否发送成功/邮件投递结果" → 用发送类命令返回的 `internetMessageId`,调用 `message verify` 查询 `sendStatus`
|
用户说"邮件发出去了吗/查邮件发送状态/确认邮件是否发送成功/邮件投递结果" → 用发送类命令返回的 `internetMessageId`,调用 `message verify` 查询 `sendStatus`
|
||||||
|
用户说"撤回邮件/撤回已发送邮件" → `sent-message recall --email <邮箱> --id <mailId> --subject "<主题>" --yes`(危险操作,先确认)
|
||||||
|
用户说"查询邮件撤回进度/撤回任务状态" → `sent-message recall-detail --email <邮箱> --id <recallTaskId>`
|
||||||
|
用户说"设置/更新/关闭自动回复" → `auto-reply update`(所有字段必填,先 `auto-reply get` 获取当前配置)
|
||||||
|
用户说"收信白名单/添加白名单/移除白名单" → `allow-list list/add/remove`
|
||||||
|
用户说"收信黑名单/添加黑名单/移除黑名单" → `block-list list/add/remove`
|
||||||
用户说"查看会话/获取会话/看这封邮件的会话" → 先 `message search` 或 `message get` 获取邮件中的 `conversationId`,再 `thread get`
|
用户说"查看会话/获取会话/看这封邮件的会话" → 先 `message search` 或 `message get` 获取邮件中的 `conversationId`,再 `thread get`
|
||||||
用户说"搜索/查找/联系 邮箱用户/联系人/某人的邮箱地址" → `user search`(搜索通讯录人员,不是搜邮件内容)
|
用户说"搜索/查找/联系 邮箱用户/联系人/某人的邮箱地址" → `user search`(搜索通讯录人员,不是搜邮件内容)
|
||||||
用户说"发送草稿/把草稿发出去/发这封草稿" → 先 `message search --query "folderId:5"` 找到草稿 messageId,再 `draft send`
|
用户说"发送草稿/把草稿发出去/发这封草稿" → 先 `message search --query "folderId:5"` 找到草稿 messageId,再 `draft send`
|
||||||
@@ -1618,7 +1774,9 @@ dws mail thread get --email user@company.com --id <conversationId> --select mess
|
|||||||
| 操作 | 从返回中提取 | 用于 |
|
| 操作 | 从返回中提取 | 用于 |
|
||||||
|------|-------------|------|
|
|------|-------------|------|
|
||||||
| `mailbox list` | 邮箱地址 | message search/get/send/thread get 的 --email/--from |
|
| `mailbox list` | 邮箱地址 | message search/get/send/thread get 的 --email/--from |
|
||||||
|
| `mailbox profile` | `emailAliases` / `mboxSize` / `mboxSizeUsed` | 展示邮箱别名与容量信息 |
|
||||||
| `message search` | `messageId` | message get 的 --id |
|
| `message search` | `messageId` | message get 的 --id |
|
||||||
|
| `message search` | `messageId` | message batch-get 的 --ids |
|
||||||
| `message search` | `conversationId` | thread get 的 --id |
|
| `message search` | `conversationId` | thread get 的 --id |
|
||||||
| `message search` | `messageId` | attachment list 的 --id |
|
| `message search` | `messageId` | attachment list 的 --id |
|
||||||
| `attachment list` | `attachments[].id` / `attachments[].name` | attachment download 的 --attachment-id / --name |
|
| `attachment list` | `attachments[].id` / `attachments[].name` | attachment download 的 --attachment-id / --name |
|
||||||
@@ -1633,6 +1791,8 @@ dws mail thread get --email user@company.com --id <conversationId> --select mess
|
|||||||
| `aisearch person` → `contact user get` / `contact user search` / `mail user search` | 用户邮箱 (orgAuthEmail / email) | message send 的 --to/--cc(三路并发,取先到结果) |
|
| `aisearch person` → `contact user get` / `contact user search` / `mail user search` | 用户邮箱 (orgAuthEmail / email) | message send 的 --to/--cc(三路并发,取先到结果) |
|
||||||
| `user search` | 用户邮箱 (email) | message send 的 --to/--cc |
|
| `user search` | 用户邮箱 (email) | message send 的 --to/--cc |
|
||||||
| `message send` / `draft send` / `message reply` / `message reply-all` / `message forward` | `internetMessageId` | `message verify` 的 --internet-message-id |
|
| `message send` / `draft send` / `message reply` / `message reply-all` / `message forward` | `internetMessageId` | `message verify` 的 --internet-message-id |
|
||||||
|
| `sent-message recall` | `id` | sent-message recall-detail 的 --id |
|
||||||
|
| `auto-reply get` | `enabled` / `startTime` / `endTime` / `scope` / `content` | auto-reply update 的当前配置基线 |
|
||||||
|
|
||||||
## 注意事项
|
## 注意事项
|
||||||
|
|
||||||
|
|||||||
@@ -108,6 +108,8 @@ dws sheet filter-view --help
|
|||||||
| `sheet update-dimension` | 更新行/列属性(显隐/行高/列宽) |
|
| `sheet update-dimension` | 更新行/列属性(显隐/行高/列宽) |
|
||||||
| `sheet move-dimension` | 移动行或列到指定位置 |
|
| `sheet move-dimension` | 移动行或列到指定位置 |
|
||||||
| `sheet add-dimension` | 在末尾追加空行或空列 |
|
| `sheet add-dimension` | 在末尾追加空行或空列 |
|
||||||
|
| `sheet group-dimension` | 创建行/列分组 |
|
||||||
|
| `sheet ungroup-dimension` | 取消行/列分组 |
|
||||||
| `sheet media-upload` | 上传附件到表格 |
|
| `sheet media-upload` | 上传附件到表格 |
|
||||||
| `sheet write-image` | 上传图片并写入单元格 |
|
| `sheet write-image` | 上传图片并写入单元格 |
|
||||||
| `sheet create-float-image` | 创建浮动图片 |
|
| `sheet create-float-image` | 创建浮动图片 |
|
||||||
@@ -156,7 +158,7 @@ dws sheet filter-view --help
|
|||||||
| 读取数据 | [sheet/sheet-read-data.md](./sheet/sheet-read-data.md) | range read / csv-get |
|
| 读取数据 | [sheet/sheet-read-data.md](./sheet/sheet-read-data.md) | range read / csv-get |
|
||||||
| 区域操作 | [sheet/sheet-range-operations.md](./sheet/sheet-range-operations.md) | range clear / sort / fill / copy-to / move-to |
|
| 区域操作 | [sheet/sheet-range-operations.md](./sheet/sheet-range-operations.md) | range clear / sort / fill / copy-to / move-to |
|
||||||
| 批量操作 | [sheet/sheet-batch-operations.md](./sheet/sheet-batch-operations.md) | range batch-clear / batch-update |
|
| 批量操作 | [sheet/sheet-batch-operations.md](./sheet/sheet-batch-operations.md) | range batch-clear / batch-update |
|
||||||
| 行列操作 | [sheet/sheet-dimension-operations.md](./sheet/sheet-dimension-operations.md) | insert / delete / update / move / add-dimension |
|
| 行列操作 | [sheet/sheet-dimension-operations.md](./sheet/sheet-dimension-operations.md) | insert / delete / update / move / add-dimension / group-dimension / ungroup-dimension |
|
||||||
| 样式与合并 | [sheet/sheet-style-format.md](./sheet/sheet-style-format.md) | range set-style / batch-set-style / merge-cells / unmerge-cells |
|
| 样式与合并 | [sheet/sheet-style-format.md](./sheet/sheet-style-format.md) | range set-style / batch-set-style / merge-cells / unmerge-cells |
|
||||||
| 条件格式 | [sheet/sheet-conditional-format.md](./sheet/sheet-conditional-format.md) | cond-format list / create / update / delete |
|
| 条件格式 | [sheet/sheet-conditional-format.md](./sheet/sheet-conditional-format.md) | cond-format list / create / update / delete |
|
||||||
| 浮动图表 | [sheet/sheet-chart.md](./sheet/sheet-chart.md) | chart list / create / update / delete |
|
| 浮动图表 | [sheet/sheet-chart.md](./sheet/sheet-chart.md) | chart list / create / update / delete |
|
||||||
@@ -254,6 +256,11 @@ dws sheet filter-view --help
|
|||||||
- 注意与 `append`(追加数据行)区分:`add-dimension` 追加的是空行/空列,`append` 追加的是带数据的行
|
- 注意与 `append`(追加数据行)区分:`add-dimension` 追加的是空行/空列,`append` 追加的是带数据的行
|
||||||
- 请勿用 `range update` 写空数据来模拟追加,`add-dimension` 直接扩展表格维度
|
- 请勿用 `range update` 写空数据来模拟追加,`add-dimension` 直接扩展表格维度
|
||||||
|
|
||||||
|
用户说"创建行分组/创建列分组/折叠几行/折叠几列/取消行列分组":
|
||||||
|
- 创建分组 → `group-dimension --range <整行或整列范围>`
|
||||||
|
- 取消分组 → `ungroup-dimension --range <整行或整列范围>`
|
||||||
|
- `--range` 只能传整行或整列范围,如 `3:7`、`C:F`、`Sheet1!3:7`,不要传 `A1:C5`
|
||||||
|
|
||||||
### 单元格格式
|
### 单元格格式
|
||||||
|
|
||||||
用户说"设置样式/改颜色/设背景色/加粗/居中/换行/字体颜色/字号":
|
用户说"设置样式/改颜色/设背景色/加粗/居中/换行/字体颜色/字号":
|
||||||
@@ -1099,6 +1106,38 @@ Flags:
|
|||||||
|
|
||||||
在工作表末尾追加指定数量的空行或空列。
|
在工作表末尾追加指定数量的空行或空列。
|
||||||
|
|
||||||
|
### 创建行或列分组
|
||||||
|
```
|
||||||
|
Usage:
|
||||||
|
dws sheet group-dimension [flags]
|
||||||
|
Example:
|
||||||
|
dws sheet group-dimension --node <NODE_ID> --sheet-id <SHEET_ID> --range "3:7"
|
||||||
|
dws sheet group-dimension --node <NODE_ID> --sheet-id <SHEET_ID> --range "C:F" --group-state fold
|
||||||
|
dws sheet group-dimension --node <NODE_ID> --sheet-id <SHEET_ID> --range "Sheet1!3:7"
|
||||||
|
Flags:
|
||||||
|
--node string 表格文档 ID 或 URL (必填)
|
||||||
|
--sheet-id string 工作表 ID 或名称 (必填)
|
||||||
|
--range string 整行/整列范围,如 "3:7" 或 "C:F" (必填)
|
||||||
|
--group-state string 创建后的分组状态: expand 或 fold,默认 expand
|
||||||
|
```
|
||||||
|
|
||||||
|
在工作表中为连续行或连续列创建分组。`--range` 必须是整行或整列范围,不支持普通单元格矩形范围。
|
||||||
|
|
||||||
|
### 取消行或列分组
|
||||||
|
```
|
||||||
|
Usage:
|
||||||
|
dws sheet ungroup-dimension [flags]
|
||||||
|
Example:
|
||||||
|
dws sheet ungroup-dimension --node <NODE_ID> --sheet-id <SHEET_ID> --range "3:7"
|
||||||
|
dws sheet ungroup-dimension --node <NODE_ID> --sheet-id <SHEET_ID> --range "C:F"
|
||||||
|
Flags:
|
||||||
|
--node string 表格文档 ID 或 URL (必填)
|
||||||
|
--sheet-id string 工作表 ID 或名称 (必填)
|
||||||
|
--range string 整行/整列范围,如 "3:7" 或 "C:F" (必填)
|
||||||
|
```
|
||||||
|
|
||||||
|
取消指定连续行或连续列的分组。
|
||||||
|
|
||||||
### 取消合并单元格
|
### 取消合并单元格
|
||||||
```
|
```
|
||||||
Usage:
|
Usage:
|
||||||
@@ -1903,6 +1942,8 @@ dws sheet export --node <NODE_ID> --output ./
|
|||||||
| `replace` | `replaceCount` 被替换的单元格数量 | 确认替换结果 |
|
| `replace` | `replaceCount` 被替换的单元格数量 | 确认替换结果 |
|
||||||
| `move-dimension` | `sheetId` 工作表 ID | 确认操作完成 |
|
| `move-dimension` | `sheetId` 工作表 ID | 确认操作完成 |
|
||||||
| `add-dimension` | `sheetId` 工作表 ID | 确认操作完成 |
|
| `add-dimension` | `sheetId` 工作表 ID | 确认操作完成 |
|
||||||
|
| `group-dimension` | `range` / `level` / `groupState` | 确认行列分组层级和展开状态 |
|
||||||
|
| `ungroup-dimension` | `range` / `level` | 确认行列分组已取消 |
|
||||||
| `unmerge-cells` | `sheetId` 工作表 ID | 确认操作完成 |
|
| `unmerge-cells` | `sheetId` 工作表 ID | 确认操作完成 |
|
||||||
| `set-dropdown` | `range` 实际设置范围、`optionCount` 选项数量、`enableMultiSelect` 是否多选 | 确认下拉列表设置成功 |
|
| `set-dropdown` | `range` 实际设置范围、`optionCount` 选项数量、`enableMultiSelect` 是否多选 | 确认下拉列表设置成功 |
|
||||||
| `get-dropdown` | `hasDropdown` 是否存在下拉、`dataValidations` 下拉配置列表(含 `conditionValues`、`ranges`、`options`) | 查看已有下拉配置 |
|
| `get-dropdown` | `hasDropdown` 是否存在下拉、`dataValidations` 下拉配置列表(含 `conditionValues`、`ranges`、`options`) | 查看已有下拉配置 |
|
||||||
@@ -2032,6 +2073,9 @@ dws sheet export --node <NODE_ID> --output ./
|
|||||||
- `add-dimension` vs `range update`:需要在末尾追加空行/空列时,必须使用 `add-dimension` 命令,禁止用 `range update` 写空数据来模拟追加效果
|
- `add-dimension` vs `range update`:需要在末尾追加空行/空列时,必须使用 `add-dimension` 命令,禁止用 `range update` 写空数据来模拟追加效果
|
||||||
- `add-dimension` 追加的是空行/空列,与 `append`(追加带数据的行)不同
|
- `add-dimension` 追加的是空行/空列,与 `append`(追加带数据的行)不同
|
||||||
- `add-dimension` 的 `--length` 必须为正整数(>= 1),行列均不超过 5000
|
- `add-dimension` 的 `--length` 必须为正整数(>= 1),行列均不超过 5000
|
||||||
|
- `group-dimension` / `ungroup-dimension` 的 `--range` 只接受整行或整列范围,如 `3:7`、`C:F`、`Sheet1!3:7`,不支持 `A1:C5`
|
||||||
|
- `group-dimension` 的 `--group-state` 只接受 `expand` 或 `fold`,默认 `expand`
|
||||||
|
- `batch-update` 支持 `group-dimension` / `ungroup-dimension`;需要创建后立即折叠时,优先使用独立 `group-dimension --group-state fold`
|
||||||
- `unmerge-cells` 取消指定范围内所有合并单元格,使用 A1 表示法指定范围
|
- `unmerge-cells` 取消指定范围内所有合并单元格,使用 A1 表示法指定范围
|
||||||
- `set-dropdown` 在指定范围内设置下拉列表,`--options` 为 JSON 数组,每个元素包含 `value`(必填)和 `color`(可选,`#RRGGBB` 格式)。选项值不能包含英文逗号。`--multi-select` 启用多选模式。如果目标范围已存在下拉列表,会被新配置覆盖
|
- `set-dropdown` 在指定范围内设置下拉列表,`--options` 为 JSON 数组,每个元素包含 `value`(必填)和 `color`(可选,`#RRGGBB` 格式)。选项值不能包含英文逗号。`--multi-select` 启用多选模式。如果目标范围已存在下拉列表,会被新配置覆盖
|
||||||
- `get-dropdown` 查询指定范围内的下拉列表配置,返回 `dataValidations` 数组,相同选项的单元格聚合为一组。无下拉列表时 `hasDropdown` 为 false
|
- `get-dropdown` 查询指定范围内的下拉列表配置,返回 `dataValidations` 数组,相同选项的单元格聚合为一组。无下拉列表时 `hasDropdown` 为 false
|
||||||
|
|||||||
@@ -26,6 +26,7 @@
|
|||||||
- 需要对**多个**不同区域执行 `merge-cells` / `unmerge-cells` 时(如按分组合并多列相同内容)
|
- 需要对**多个**不同区域执行 `merge-cells` / `unmerge-cells` 时(如按分组合并多列相同内容)
|
||||||
- 需要对**多个**不同区域执行 `update-dimension` 时(如统一调整多列列宽或多行行高,含 hidden + pixelSize)
|
- 需要对**多个**不同区域执行 `update-dimension` 时(如统一调整多列列宽或多行行高,含 hidden + pixelSize)
|
||||||
- 需要先插入行列再写入数据时(`add-dimension` + `range update` / `csv-put`)
|
- 需要先插入行列再写入数据时(`add-dimension` + `range update` / `csv-put`)
|
||||||
|
- 需要批量创建或取消行列分组时(`group-dimension` / `ungroup-dimension`)
|
||||||
- 需要对多个区域执行不同写入操作时(多次 `range update` + `range clear` 等组合)
|
- 需要对多个区域执行不同写入操作时(多次 `range update` + `range clear` 等组合)
|
||||||
|
|
||||||
当同一工具需要对多个区域重复调用时,**推荐**改用 `batch-update` 合并为单次请求——`batch-update` 是原子提交(要么全成功要么整批回滚);逐个调用非原子,中途失败会留下半成品。
|
当同一工具需要对多个区域重复调用时,**推荐**改用 `batch-update` 合并为单次请求——`batch-update` 是原子提交(要么全成功要么整批回滚);逐个调用非原子,中途失败会留下半成品。
|
||||||
@@ -80,6 +81,7 @@ Flags:
|
|||||||
toolName 用 CLI 命令名,input 的键用 CLI flag 名去掉 --:
|
toolName 用 CLI 命令名,input 的键用 CLI flag 名去掉 --:
|
||||||
range clear / range update / merge-cells / unmerge-cells / update-dimension
|
range clear / range update / merge-cells / unmerge-cells / update-dimension
|
||||||
range fill / range copy-to / add-dimension / delete-dimension / move-dimension
|
range fill / range copy-to / add-dimension / delete-dimension / move-dimension
|
||||||
|
group-dimension / ungroup-dimension
|
||||||
set-dropdown / delete-dropdown / csv-put / delete-float-image
|
set-dropdown / delete-dropdown / csv-put / delete-float-image
|
||||||
Notes:
|
Notes:
|
||||||
- 默认严格事务模式:任一子操作失败 → 整批回滚到初始状态
|
- 默认严格事务模式:任一子操作失败 → 整批回滚到初始状态
|
||||||
@@ -177,6 +179,8 @@ Cause: The requested resource was not found by the identifier 'NonExistentSheet'
|
|||||||
| `add-dimension` | [sheet-dimension-operations](./sheet-dimension-operations.md) |
|
| `add-dimension` | [sheet-dimension-operations](./sheet-dimension-operations.md) |
|
||||||
| `delete-dimension` | [sheet-dimension-operations](./sheet-dimension-operations.md) |
|
| `delete-dimension` | [sheet-dimension-operations](./sheet-dimension-operations.md) |
|
||||||
| `move-dimension` | [sheet-dimension-operations](./sheet-dimension-operations.md) |
|
| `move-dimension` | [sheet-dimension-operations](./sheet-dimension-operations.md) |
|
||||||
|
| `group-dimension` | [sheet-dimension-operations](./sheet-dimension-operations.md) |
|
||||||
|
| `ungroup-dimension` | [sheet-dimension-operations](./sheet-dimension-operations.md) |
|
||||||
| `set-dropdown` | [sheet-dropdown](./sheet-dropdown.md) |
|
| `set-dropdown` | [sheet-dropdown](./sheet-dropdown.md) |
|
||||||
| `delete-dropdown` | [sheet-dropdown](./sheet-dropdown.md) |
|
| `delete-dropdown` | [sheet-dropdown](./sheet-dropdown.md) |
|
||||||
| `csv-put` | [sheet-write-data](./sheet-write-data.md) |
|
| `csv-put` | [sheet-write-data](./sheet-write-data.md) |
|
||||||
@@ -230,6 +234,19 @@ Cause: The requested resource was not found by the identifier 'NonExistentSheet'
|
|||||||
]
|
]
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### 批量创建行列分组
|
||||||
|
|
||||||
|
```jsonc
|
||||||
|
[
|
||||||
|
{"toolName":"group-dimension",
|
||||||
|
"input":{"sheet-id":"...","range":"3:7","group-state":"expand"}},
|
||||||
|
{"toolName":"group-dimension",
|
||||||
|
"input":{"sheet-id":"...","range":"C:F"}},
|
||||||
|
{"toolName":"ungroup-dimension",
|
||||||
|
"input":{"sheet-id":"...","range":"10:12"}}
|
||||||
|
]
|
||||||
|
```
|
||||||
|
|
||||||
## 上下文传递
|
## 上下文传递
|
||||||
|
|
||||||
| 操作 | 从返回中提取 | 用于 |
|
| 操作 | 从返回中提取 | 用于 |
|
||||||
@@ -244,3 +261,5 @@ Cause: The requested resource was not found by the identifier 'NonExistentSheet'
|
|||||||
- ★ **需要组合多个不同写操作(清除+写入等)时,用 `batch-update`**:原子事务,任一操作失败则整批回滚,避免留下半成品
|
- ★ **需要组合多个不同写操作(清除+写入等)时,用 `batch-update`**:原子事务,任一操作失败则整批回滚,避免留下半成品
|
||||||
- ★ **批次完成后必须回读校验**:用 `range read` 或 `csv-get` 抽样回读受影响区域,至少校验 3-5 个代表性单元格(首 / 中 / 末),与预期值对照
|
- ★ **批次完成后必须回读校验**:用 `range read` 或 `csv-get` 抽样回读受影响区域,至少校验 3-5 个代表性单元格(首 / 中 / 末),与预期值对照
|
||||||
- ★ **`batch-update` 不支持嵌套**:`--operations` 中的 `toolName` 必须是原子操作,不可再嵌套 `batch-update`
|
- ★ **`batch-update` 不支持嵌套**:`--operations` 中的 `toolName` 必须是原子操作,不可再嵌套 `batch-update`
|
||||||
|
- `batch-update` 支持 `group-dimension` / `ungroup-dimension`;`input.range` 只接受整行或整列范围,如 `3:7`、`C:F`
|
||||||
|
- 需要创建后立即折叠分组时,优先使用独立 `dws sheet group-dimension --group-state fold`
|
||||||
@@ -26,6 +26,12 @@
|
|||||||
- 注意与 `append`(追加数据行)区分:`add-dimension` 追加的是空行/空列,`append` 追加的是带数据的行
|
- 注意与 `append`(追加数据行)区分:`add-dimension` 追加的是空行/空列,`append` 追加的是带数据的行
|
||||||
- 请勿用 `range update` 写空数据来模拟追加,`add-dimension` 直接扩展表格维度
|
- 请勿用 `range update` 写空数据来模拟追加,`add-dimension` 直接扩展表格维度
|
||||||
|
|
||||||
|
用户说"创建行分组/创建列分组/折叠几行/折叠几列/展开行列分组/取消行列分组":
|
||||||
|
- 创建分组 → `group-dimension --range <整行或整列范围>`
|
||||||
|
- 创建并折叠 → `group-dimension --group-state fold`
|
||||||
|
- 取消分组 → `ungroup-dimension --range <整行或整列范围>`
|
||||||
|
- `--range` 只能传整行或整列范围,如 `3:7`、`C:F`、`Sheet1!3:7`
|
||||||
|
|
||||||
**结构预检**:插入、删除、移动行列前,必须先执行 `dws sheet info --node <NODE_ID> --sheet-id <SHEET_ID> --format json` 查看 `mergedRanges`。合并区域跨过操作位置时,行列变更可能导致表头/分组标题断裂、空白或错位;需要先向用户说明影响,必要时取消合并后操作,再按原模式重新 `merge-cells`。
|
**结构预检**:插入、删除、移动行列前,必须先执行 `dws sheet info --node <NODE_ID> --sheet-id <SHEET_ID> --format json` 查看 `mergedRanges`。合并区域跨过操作位置时,行列变更可能导致表头/分组标题断裂、空白或错位;需要先向用户说明影响,必要时取消合并后操作,再按原模式重新 `merge-cells`。
|
||||||
|
|
||||||
## 命令详细参考
|
## 命令详细参考
|
||||||
@@ -172,6 +178,38 @@ Flags:
|
|||||||
|
|
||||||
在工作表末尾追加指定数量的空行或空列。
|
在工作表末尾追加指定数量的空行或空列。
|
||||||
|
|
||||||
|
### 创建行或列分组
|
||||||
|
```
|
||||||
|
Usage:
|
||||||
|
dws sheet group-dimension [flags]
|
||||||
|
Example:
|
||||||
|
dws sheet group-dimension --node <NODE_ID> --sheet-id <SHEET_ID> --range "3:7"
|
||||||
|
dws sheet group-dimension --node <NODE_ID> --sheet-id <SHEET_ID> --range "C:F" --group-state fold
|
||||||
|
dws sheet group-dimension --node <NODE_ID> --sheet-id <SHEET_ID> --range "Sheet1!3:7"
|
||||||
|
Flags:
|
||||||
|
--node string 表格文档 ID 或 URL (必填)
|
||||||
|
--sheet-id string 工作表 ID 或名称 (必填)
|
||||||
|
--range string 整行/整列范围,如 "3:7" 或 "C:F" (必填)
|
||||||
|
--group-state string 创建后的分组状态: expand 或 fold,默认 expand
|
||||||
|
```
|
||||||
|
|
||||||
|
为连续行或连续列创建分组。`--range` 必须是整行或整列范围,不支持 `A1:C5` 这类普通单元格矩形范围。
|
||||||
|
|
||||||
|
### 取消行或列分组
|
||||||
|
```
|
||||||
|
Usage:
|
||||||
|
dws sheet ungroup-dimension [flags]
|
||||||
|
Example:
|
||||||
|
dws sheet ungroup-dimension --node <NODE_ID> --sheet-id <SHEET_ID> --range "3:7"
|
||||||
|
dws sheet ungroup-dimension --node <NODE_ID> --sheet-id <SHEET_ID> --range "C:F"
|
||||||
|
Flags:
|
||||||
|
--node string 表格文档 ID 或 URL (必填)
|
||||||
|
--sheet-id string 工作表 ID 或名称 (必填)
|
||||||
|
--range string 整行/整列范围,如 "3:7" 或 "C:F" (必填)
|
||||||
|
```
|
||||||
|
|
||||||
|
取消指定连续行或连续列的分组。
|
||||||
|
|
||||||
## 核心工作流
|
## 核心工作流
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
@@ -244,6 +282,8 @@ dws sheet update-dimension --node <NODE_ID> --sheet-id <SHEET_ID> \
|
|||||||
| `update-dimension` | `a1Notation` 被更新区域范围、`hidden` 生效的显隐状态、`pixelSize` 生效的尺寸 | 确认更新结果 |
|
| `update-dimension` | `a1Notation` 被更新区域范围、`hidden` 生效的显隐状态、`pixelSize` 生效的尺寸 | 确认更新结果 |
|
||||||
| `move-dimension` | `sheetId` 工作表 ID | 确认操作完成 |
|
| `move-dimension` | `sheetId` 工作表 ID | 确认操作完成 |
|
||||||
| `add-dimension` | `sheetId` 工作表 ID | 确认操作完成 |
|
| `add-dimension` | `sheetId` 工作表 ID | 确认操作完成 |
|
||||||
|
| `group-dimension` | `range` / `level` / `groupState` | 确认行列分组层级和展开状态 |
|
||||||
|
| `ungroup-dimension` | `range` / `level` | 确认行列分组已取消 |
|
||||||
| `list` | 工作表的 `sheetId` | info / range read / range update / find 的 --sheet-id |
|
| `list` | 工作表的 `sheetId` | info / range read / range update / find 的 --sheet-id |
|
||||||
|
|
||||||
## 注意事项
|
## 注意事项
|
||||||
@@ -273,3 +313,7 @@ dws sheet update-dimension --node <NODE_ID> --sheet-id <SHEET_ID> \
|
|||||||
- `add-dimension` vs `range update`:需要在末尾追加空行/空列时,必须使用 `add-dimension` 命令,禁止用 `range update` 写空数据来模拟追加效果
|
- `add-dimension` vs `range update`:需要在末尾追加空行/空列时,必须使用 `add-dimension` 命令,禁止用 `range update` 写空数据来模拟追加效果
|
||||||
- `add-dimension` 追加的是空行/空列,与 `append`(追加带数据的行)不同
|
- `add-dimension` 追加的是空行/空列,与 `append`(追加带数据的行)不同
|
||||||
- `add-dimension` 的 `--length` 必须为正整数(>= 1),行列均不超过 5000
|
- `add-dimension` 的 `--length` 必须为正整数(>= 1),行列均不超过 5000
|
||||||
|
- `group-dimension` / `ungroup-dimension` 的 `--range` 只接受整行或整列范围,如 `3:7`、`C:F`、`Sheet1!3:7`,不支持 `A1:C5`
|
||||||
|
- `group-dimension` 的 `--group-state` 只接受 `expand` 或 `fold`,默认 `expand`
|
||||||
|
- 分组操作返回 `level` 表示当前分组层级;不要把它误认为固定深度参数
|
||||||
|
- `batch-update` 支持 `group-dimension` / `ungroup-dimension`;需要创建后立即折叠时,优先使用独立 `group-dimension --group-state fold`
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
---
|
||||||
|
name: dingtalk-agoal
|
||||||
|
description: 钉钉 Agoal 目标管理。Use when 用户说 目标管理/Agoal/战略解码/经营合约/计分卡/OKR/目标模板/周月报提交统计/跟催。Distinct from dingtalk-todo(待办任务)、dingtalk-report(日志)。命令前缀:dws agoal。
|
||||||
|
cli_version: ">=0.2.14"
|
||||||
|
metadata:
|
||||||
|
category: product
|
||||||
|
stability: experimental
|
||||||
|
requires:
|
||||||
|
bins:
|
||||||
|
- dws
|
||||||
|
---
|
||||||
|
|
||||||
|
# 钉钉 Agoal Skill
|
||||||
|
|
||||||
|
> 🧪 **EXPERIMENTAL · 试验版 / Preview** — multi 模式当前未达 stable 标准。22 个 dingtalk-* skill 全部通过 dispatch verifier,但接口、命名、跨 skill 引用后续可能调整;生产 / 共享环境请优先使用 mono 模式(`dws skill setup --mode mono`)。问题请提 issue 反馈。
|
||||||
|
|
||||||
|
> **PREREQUISITE:** Read the `dws-shared` skill first for auth, global flags, product routing, URL preflight, error codes, and safety rules. The `dws` binary must be on PATH.
|
||||||
|
|
||||||
|
<!-- SAFETY_PREAMBLE_INJECT -->
|
||||||
|
|
||||||
|
> ⚠️ **命令可用性以当前 dws 二进制为准**。服务发现已下线,本文档随内置 skill 发布;如果 `dws <cmd> --help` 不存在,说明当前版本未暴露该命令。若命令存在但调用失败,请按错误中的 endpoint 或 tool 提示确认静态端点目录和后端工具注册。实际调用前可用 `dws <cmd> --help` 或 `--dry-run` 验证。
|
||||||
|
|
||||||
|
> 命令参考:[agoal.md](references/agoal.md)。
|
||||||
|
|
||||||
|
## 意图表
|
||||||
|
|
||||||
|
| 用户说 | 命令 |
|
||||||
|
|--------|------|
|
||||||
|
| "查战略解码 / OGSM" | `dws agoal strategy list --scope-type <DEPT|PERSONAL> --scope-id <ID>` |
|
||||||
|
| "看战略解码详情" | `dws agoal strategy detail --profile-id <PROFILE_ID>` |
|
||||||
|
| "更新战略解码" | 先 detail,基于完整数据修改后 `dws agoal strategy update ...` |
|
||||||
|
| "查经营合约 / KPI合约" | `dws agoal contract list/detail/fields` |
|
||||||
|
| "更新经营合约" | 先 detail,基于完整 dimensions 修改后 `dws agoal contract update ...` |
|
||||||
|
| "查计分卡" | `dws agoal scorecard detail --selected-time <ISO> --dept-id <DEPT_ID>` |
|
||||||
|
| "我的目标 / 个人目标" | `dws agoal user rules` → `dws agoal user objectives` |
|
||||||
|
| "周月报提交统计 / 跟催 / 迟交 / 未提交" | `dws agoal report list-statistics` → `dws agoal report submit-detail` |
|
||||||
|
| "目标模板" | `dws agoal obj-template list/create-or-update` |
|
||||||
|
|
||||||
|
## 写操作硬约束
|
||||||
|
|
||||||
|
- `strategy update` / `contract update` / `scorecard update` / `obj-template create-or-update` 都是覆盖式写入。
|
||||||
|
- 必须先读取现有详情或模板列表,在原数据基础上修改;禁止只传局部字段覆盖。
|
||||||
|
- 执行前必须向用户展示目标对象、修改字段和最终命令参数摘要,并等待确认。
|
||||||
@@ -0,0 +1,77 @@
|
|||||||
|
# Agoal(目标管理)
|
||||||
|
|
||||||
|
## 产品说明
|
||||||
|
|
||||||
|
Agoal 是钉钉目标管理工具,支持战略解码、经营合约、计分卡、用户目标、目标模板、周月报六大模块,帮助组织将战略目标从顶层分解到个人并持续跟踪。
|
||||||
|
|
||||||
|
**CLI 前缀**: `dws agoal`
|
||||||
|
|
||||||
|
## 命令总览
|
||||||
|
|
||||||
|
### strategy (战略解码管理)
|
||||||
|
|
||||||
|
| 命令 | 用途 | 必填参数 | 备注 |
|
||||||
|
|------|------|----------|------|
|
||||||
|
| `strategy list` | 获取战略解码列表 | `--scope-type` `--scope-id` | scopeType: DEPT/PERSONAL;scope-id 为 scope-type 对应的部门 id 或用户 id |
|
||||||
|
| `strategy detail` | 获取战略解码详情 | `--profile-id` | 根据战略解码 id 查询 |
|
||||||
|
| `strategy update` | 更新战略解码 | `--profile-id` `--content` | 覆盖逻辑,必须基于查询返回的老数据修改后再传入;`--content` 为 JSON 数组 |
|
||||||
|
|
||||||
|
### contract (经营合约管理)
|
||||||
|
|
||||||
|
| 命令 | 用途 | 必填参数 | 备注 |
|
||||||
|
|------|------|----------|------|
|
||||||
|
| `contract list` | 获取经营合约列表 | `--scope-type` `--scope-id` | scopeType: DEPT/PERSONAL;scope-id 为 scope-type 对应的部门 id 或用户 id |
|
||||||
|
| `contract fields` | 获取经营合约字段列表 | - | 获取组织下经营合约的字段配置 |
|
||||||
|
| `contract detail` | 获取经营合约详情 | `--contract-id` | 根据合约 id 查询 |
|
||||||
|
| `contract update` | 更新经营合约 | `--contract-id` `--dimensions` | 覆盖逻辑;可选 `--audit-config`、`--objective-template` |
|
||||||
|
|
||||||
|
### scorecard (计分卡管理)
|
||||||
|
|
||||||
|
| 命令 | 用途 | 必填参数 | 备注 |
|
||||||
|
|------|------|----------|------|
|
||||||
|
| `scorecard detail` | 获取计分卡详情 | `--selected-time` `--dept-id` | selectedTime 为 ISO-8601 字符串 |
|
||||||
|
| `scorecard entity-detail` | 获取计分卡实体详情 | `--sc-id` `--entity-id` | 根据计分卡 id 和实体 id 查询 |
|
||||||
|
| `scorecard update` | 更新计分卡 | `--dept-id` `--selected-time` `--id` `--tracking-period-type` `--content` | trackingPeriodType: MONTHLY/QUARTERLY |
|
||||||
|
|
||||||
|
### user / report / obj-template
|
||||||
|
|
||||||
|
| 命令 | 用途 | 必填参数 |
|
||||||
|
|------|------|----------|
|
||||||
|
| `user rules` | 获取用户规则周期列表 | - |
|
||||||
|
| `user objectives` | 查询用户目标列表 | `--user-id` `--rule-id` `--period-ids` |
|
||||||
|
| `report list-statistics` | 获取周月报数据跟催列表 | - |
|
||||||
|
| `report submit-detail` | 获取周月报规则提交详情 | `--template-id` `--submit-state` |
|
||||||
|
| `obj-template list` | 获取目标模板列表 | - |
|
||||||
|
| `obj-template create-or-update` | 新增或更新目标模板 | `--dimensions` |
|
||||||
|
|
||||||
|
## 意图判断
|
||||||
|
|
||||||
|
- "战略解码/战略目标/OGSM" → `strategy list/detail/update`
|
||||||
|
- "经营合约/合约/KPI合约" → `contract list/fields/detail/update`
|
||||||
|
- "计分卡/scorecard/绩效看板" → `scorecard detail/entity-detail/update`
|
||||||
|
- "目标/OKR/我的目标/个人目标" → `user rules/objectives`
|
||||||
|
- "目标模板/模板管理" → `obj-template list/create-or-update`
|
||||||
|
- "周月报/周报统计/提交情况/跟催/迟交/未提交" → `report list-statistics/submit-detail`
|
||||||
|
|
||||||
|
## 常用命令
|
||||||
|
|
||||||
|
```bash
|
||||||
|
dws agoal strategy list --scope-type DEPT --scope-id DEPT_ID --format json
|
||||||
|
dws agoal strategy detail --profile-id PROFILE_ID --format json
|
||||||
|
dws agoal contract list --scope-type PERSONAL --scope-id USER_ID --format json
|
||||||
|
dws agoal contract fields --format json
|
||||||
|
dws agoal contract detail --contract-id CONTRACT_ID --format json
|
||||||
|
dws agoal scorecard detail --selected-time "2026-01-01T00:00:00+08:00" --dept-id DEPT_ID --format json
|
||||||
|
dws agoal user rules --user-id USER_ID --format json
|
||||||
|
dws agoal user objectives --user-id USER_ID --rule-id RULE_ID --period-ids "period1,period2" --format json
|
||||||
|
dws agoal report list-statistics --format json
|
||||||
|
dws agoal report submit-detail --template-id TPL_ID --submit-state ON_TIME --format json
|
||||||
|
dws agoal obj-template list --keyword "业绩" --format json
|
||||||
|
```
|
||||||
|
|
||||||
|
## 安全规则
|
||||||
|
|
||||||
|
- 所有 update / create-or-update 命令都是覆盖逻辑:必须先用对应 detail/list 查询完整数据,在原数据基础上修改后再传入。
|
||||||
|
- 写入类命令执行前必须展示变更摘要并等待用户确认。
|
||||||
|
- `--selected-time` / `--query-date` 接受 ISO-8601 字符串。
|
||||||
|
- `--period-ids` 为逗号分隔字符串。
|
||||||
@@ -12,7 +12,7 @@ metadata:
|
|||||||
|
|
||||||
# 钉钉 AI 搜问(搜人)Skill
|
# 钉钉 AI 搜问(搜人)Skill
|
||||||
|
|
||||||
> 🧪 **EXPERIMENTAL · 试验版 / Preview** — multi 模式当前未达 stable 标准。20 个 dingtalk-* skill 全部通过 dispatch verifier,但接口、命名、跨 skill 引用后续可能调整;生产 / 共享环境请优先使用 mono 模式(`dws skill setup --mode mono`)。问题请提 issue 反馈。
|
> 🧪 **EXPERIMENTAL · 试验版 / Preview** — multi 模式当前未达 stable 标准。22 个 dingtalk-* skill 全部通过 dispatch verifier,但接口、命名、跨 skill 引用后续可能调整;生产 / 共享环境请优先使用 mono 模式(`dws skill setup --mode mono`)。问题请提 issue 反馈。
|
||||||
|
|
||||||
> **PREREQUISITE:** Read the `dws-shared` skill first for auth, global flags, product routing, URL preflight, error codes, and safety rules. The `dws` binary must be on PATH.
|
> **PREREQUISITE:** Read the `dws-shared` skill first for auth, global flags, product routing, URL preflight, error codes, and safety rules. The `dws` binary must be on PATH.
|
||||||
|
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ metadata:
|
|||||||
|
|
||||||
# 钉钉 AI 表格 Skill
|
# 钉钉 AI 表格 Skill
|
||||||
|
|
||||||
> 🧪 **EXPERIMENTAL · 试验版 / Preview** — multi 模式当前未达 stable 标准。20 个 dingtalk-* skill 全部通过 dispatch verifier,但接口、命名、跨 skill 引用后续可能调整;生产 / 共享环境请优先使用 mono 模式(`dws skill setup --mode mono`)。问题请提 issue 反馈。
|
> 🧪 **EXPERIMENTAL · 试验版 / Preview** — multi 模式当前未达 stable 标准。22 个 dingtalk-* skill 全部通过 dispatch verifier,但接口、命名、跨 skill 引用后续可能调整;生产 / 共享环境请优先使用 mono 模式(`dws skill setup --mode mono`)。问题请提 issue 反馈。
|
||||||
|
|
||||||
> **PREREQUISITE:** Read the root `dws` skill first for auth, global flags, product routing, URL preflight, error codes, and safety rules. The `dws` binary must be on PATH.
|
> **PREREQUISITE:** Read the root `dws` skill first for auth, global flags, product routing, URL preflight, error codes, and safety rules. The `dws` binary must be on PATH.
|
||||||
|
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ metadata:
|
|||||||
|
|
||||||
# 钉钉考勤 Skill
|
# 钉钉考勤 Skill
|
||||||
|
|
||||||
> 🧪 **EXPERIMENTAL · 试验版 / Preview** — multi 模式当前未达 stable 标准。20 个 dingtalk-* skill 全部通过 dispatch verifier,但接口、命名、跨 skill 引用后续可能调整;生产 / 共享环境请优先使用 mono 模式(`dws skill setup --mode mono`)。问题请提 issue 反馈。
|
> 🧪 **EXPERIMENTAL · 试验版 / Preview** — multi 模式当前未达 stable 标准。22 个 dingtalk-* skill 全部通过 dispatch verifier,但接口、命名、跨 skill 引用后续可能调整;生产 / 共享环境请优先使用 mono 模式(`dws skill setup --mode mono`)。问题请提 issue 反馈。
|
||||||
|
|
||||||
> **PREREQUISITE:** Read the `dws-shared` skill first for auth, global flags, product routing, URL preflight, error codes, and safety rules. The `dws` binary must be on PATH.
|
> **PREREQUISITE:** Read the `dws-shared` skill first for auth, global flags, product routing, URL preflight, error codes, and safety rules. The `dws` binary must be on PATH.
|
||||||
|
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ metadata:
|
|||||||
|
|
||||||
# 钉钉日历 Skill
|
# 钉钉日历 Skill
|
||||||
|
|
||||||
> 🧪 **EXPERIMENTAL · 试验版 / Preview** — multi 模式当前未达 stable 标准。20 个 dingtalk-* skill 全部通过 dispatch verifier,但接口、命名、跨 skill 引用后续可能调整;生产 / 共享环境请优先使用 mono 模式(`dws skill setup --mode mono`)。问题请提 issue 反馈。
|
> 🧪 **EXPERIMENTAL · 试验版 / Preview** — multi 模式当前未达 stable 标准。22 个 dingtalk-* skill 全部通过 dispatch verifier,但接口、命名、跨 skill 引用后续可能调整;生产 / 共享环境请优先使用 mono 模式(`dws skill setup --mode mono`)。问题请提 issue 反馈。
|
||||||
|
|
||||||
> **PREREQUISITE:** Read the `dws-shared` skill first for auth, global flags, product routing, URL preflight, error codes, and safety rules. The `dws` binary must be on PATH.
|
> **PREREQUISITE:** Read the `dws-shared` skill first for auth, global flags, product routing, URL preflight, error codes, and safety rules. The `dws` binary must be on PATH.
|
||||||
|
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ metadata:
|
|||||||
|
|
||||||
# 钉钉群聊 / 消息 Skill
|
# 钉钉群聊 / 消息 Skill
|
||||||
|
|
||||||
> 🧪 **EXPERIMENTAL · 试验版 / Preview** — multi 模式当前未达 stable 标准。20 个 dingtalk-* skill 全部通过 dispatch verifier,但接口、命名、跨 skill 引用后续可能调整;生产 / 共享环境请优先使用 mono 模式(`dws skill setup --mode mono`)。问题请提 issue 反馈。
|
> 🧪 **EXPERIMENTAL · 试验版 / Preview** — multi 模式当前未达 stable 标准。22 个 dingtalk-* skill 全部通过 dispatch verifier,但接口、命名、跨 skill 引用后续可能调整;生产 / 共享环境请优先使用 mono 模式(`dws skill setup --mode mono`)。问题请提 issue 反馈。
|
||||||
|
|
||||||
> **PREREQUISITE:** Read the `dws-shared` skill first for auth, global flags, product routing, URL preflight, error codes, and safety rules. The `dws` binary must be on PATH.
|
> **PREREQUISITE:** Read the `dws-shared` skill first for auth, global flags, product routing, URL preflight, error codes, and safety rules. The `dws` binary must be on PATH.
|
||||||
|
|
||||||
|
|||||||
@@ -140,6 +140,26 @@ Flags:
|
|||||||
--expires-seconds int64 链接有效期(秒),0 表示永久有效,不传使用服务端默认值
|
--expires-seconds int64 链接有效期(秒),0 表示永久有效,不传使用服务端默认值
|
||||||
```
|
```
|
||||||
|
|
||||||
|
#### 分享群聊链接到会话 — 将指定群的邀请链接分享到另一个会话或单聊用户
|
||||||
|
|
||||||
|
`--target` 和 `--receiver` 二选一:`--target` 指定目标会话,`--receiver` 指定单聊用户。
|
||||||
|
|
||||||
|
```
|
||||||
|
Usage:
|
||||||
|
dws chat group share-invite [flags]
|
||||||
|
Example:
|
||||||
|
dws chat group share-invite --source <被分享群openConversationId> --target <目标会话openConversationId>
|
||||||
|
dws chat group share-invite --source <被分享群openConversationId> --receiver <接收者openDingTalkId>
|
||||||
|
dws chat group share-invite --source <openConversationId> --target <openConversationId> --expires-seconds 86400
|
||||||
|
# 查询群 ID: dws chat search --query "群名"
|
||||||
|
Flags:
|
||||||
|
--source string 被分享群的 openConversationId (必填)
|
||||||
|
--target string 接收分享消息的会话 openConversationId(与 --receiver 二选一)
|
||||||
|
--receiver string 接收分享消息的单聊用户 openDingTalkId(与 --target 二选一)
|
||||||
|
--expires-seconds int64 链接有效期(秒),0 表示永久有效,不传使用服务端默认值
|
||||||
|
--uuid string 消息幂等键(可选)
|
||||||
|
```
|
||||||
|
|
||||||
#### 退出群聊 — 当前用户退出指定群聊
|
#### 退出群聊 — 当前用户退出指定群聊
|
||||||
```
|
```
|
||||||
Usage:
|
Usage:
|
||||||
@@ -274,6 +294,65 @@ Flags:
|
|||||||
- 不传 --role 时返回群主 + 管理员的所有群
|
- 不传 --role 时返回群主 + 管理员的所有群
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### group notice (群公告管理)
|
||||||
|
|
||||||
|
#### 发布群公告 — 在指定群聊中发布群公告,支持 Markdown、定时发布
|
||||||
|
|
||||||
|
```
|
||||||
|
Usage:
|
||||||
|
dws chat group notice create [flags]
|
||||||
|
Example:
|
||||||
|
dws chat group notice create --group <openConversationId> --content "今晚 22 点系统维护,请提前保存工作内容"
|
||||||
|
dws chat group notice create --group <openConversationId> --content "# 重要通知\n请大家查收" --sticky --send-ding
|
||||||
|
dws chat group notice create --group <openConversationId> --content "明早九点例会" --run-at "2026-07-03T09:00:00+08:00"
|
||||||
|
Flags:
|
||||||
|
--group string 群聊 openConversationId (必填)
|
||||||
|
--content string 公告正文,Markdown 格式 (必填)
|
||||||
|
--sticky 是否吊顶置顶(默认 false)
|
||||||
|
--send-ding 是否发 DING 提醒(默认 false)
|
||||||
|
--run-at string 定时发布时间 ISO-8601(传入则定时发布)
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 修改群公告 — 整体替换指定群公告的内容
|
||||||
|
```
|
||||||
|
Usage:
|
||||||
|
dws chat group notice edit [flags]
|
||||||
|
Example:
|
||||||
|
dws chat group notice edit --group <openConversationId> --notice-id <dataId> --content "更新后的公告内容"
|
||||||
|
Flags:
|
||||||
|
--group string 群聊 openConversationId (必填)
|
||||||
|
--notice-id string 群公告 dataId (必填)
|
||||||
|
--content string 公告新正文,Markdown 格式 (必填)
|
||||||
|
--sticky 是否吊顶置顶
|
||||||
|
--send-ding 是否发 DING 提醒
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 查看群公告详情
|
||||||
|
```
|
||||||
|
Usage:
|
||||||
|
dws chat group notice get [flags]
|
||||||
|
Example:
|
||||||
|
dws chat group notice get --group <openConversationId> --notice-id <dataId>
|
||||||
|
Flags:
|
||||||
|
--group string 群聊 openConversationId (必填)
|
||||||
|
--notice-id string 群公告 dataId (必填)
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 查看群公告列表
|
||||||
|
```
|
||||||
|
Usage:
|
||||||
|
dws chat group notice list [flags]
|
||||||
|
Example:
|
||||||
|
dws chat group notice list --group <openConversationId>
|
||||||
|
dws chat group notice list --group <openConversationId> --limit 20 --cursor <nextPageCursor>
|
||||||
|
dws chat group notice list --group <openConversationId> --scheduled
|
||||||
|
Flags:
|
||||||
|
--group string 群聊 openConversationId (必填)
|
||||||
|
--limit int 每页返回数量(默认 10,最大 100)
|
||||||
|
--cursor string 分页游标
|
||||||
|
--scheduled 是否查询定时公告列表
|
||||||
|
```
|
||||||
|
|
||||||
### group-role (群身份管理)
|
### group-role (群身份管理)
|
||||||
|
|
||||||
#### 查看群身份列表 — 拉取指定群聊的自定义群身份列表
|
#### 查看群身份列表 — 拉取指定群聊的自定义群身份列表
|
||||||
@@ -959,6 +1038,20 @@ Flags:
|
|||||||
- 如果已有合适的表情,无需创建新的
|
- 如果已有合适的表情,无需创建新的
|
||||||
```
|
```
|
||||||
|
|
||||||
|
#### 批量拉取消息的表情回复和文字回复
|
||||||
|
|
||||||
|
根据消息 ID 列表批量查询消息的表情回复和文字回复信息。
|
||||||
|
|
||||||
|
```
|
||||||
|
Usage:
|
||||||
|
dws chat message list-emotion-replies [flags]
|
||||||
|
Example:
|
||||||
|
dws chat message list-emotion-replies --msg-ids msgId1,msgId2,msgId3
|
||||||
|
# 消息 ID 可通过 dws chat message list 获取
|
||||||
|
Flags:
|
||||||
|
--msg-ids string 消息 ID 列表,逗号分隔 (必填)
|
||||||
|
```
|
||||||
|
|
||||||
### list-top-conversations (置顶会话)
|
### list-top-conversations (置顶会话)
|
||||||
|
|
||||||
#### 拉取置顶会话列表
|
#### 拉取置顶会话列表
|
||||||
@@ -1274,6 +1367,21 @@ Flags:
|
|||||||
--title string 分组名称 (必填)
|
--title string 分组名称 (必填)
|
||||||
```
|
```
|
||||||
|
|
||||||
|
#### 创建智能会话分组 — 可指定群名称关键词和群内成员作为匹配规则
|
||||||
|
```
|
||||||
|
Usage:
|
||||||
|
dws chat category create-smart [flags]
|
||||||
|
Example:
|
||||||
|
dws chat category create-smart --name "工作群"
|
||||||
|
dws chat category create-smart --name "项目组" --keywords "项目,开发"
|
||||||
|
dws chat category create-smart --name "团队群" --members openDingTalkId1,openDingTalkId2
|
||||||
|
dws chat category create-smart --name "重点群" --keywords "重点" --members openDingTalkId1
|
||||||
|
Flags:
|
||||||
|
--name string 分组名称 (必填)
|
||||||
|
--keywords string 群名称关键词列表,逗号分隔(可选)
|
||||||
|
--members string 群内成员 openDingTalkId 列表,逗号分隔(可选)
|
||||||
|
```
|
||||||
|
|
||||||
#### 删除会话分组
|
#### 删除会话分组
|
||||||
```
|
```
|
||||||
Usage:
|
Usage:
|
||||||
@@ -1323,6 +1431,23 @@ Flags:
|
|||||||
--category-ids string 目标分组 ID 列表,逗号分隔 (必填)
|
--category-ids string 目标分组 ID 列表,逗号分隔 (必填)
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### text (文本内容处理)
|
||||||
|
|
||||||
|
#### 翻译文本内容
|
||||||
|
```
|
||||||
|
Usage:
|
||||||
|
dws chat text translate [flags]
|
||||||
|
Example:
|
||||||
|
dws chat text translate --query "你好世界" --to en_US
|
||||||
|
dws chat text translate --query "Hello World" --to zh_CN
|
||||||
|
dws chat text translate --query "Bonjour" --to ja_JP
|
||||||
|
Flags:
|
||||||
|
--query string 待翻译的文本内容 (必填)
|
||||||
|
--to string 目标语言代码 (必填)
|
||||||
|
```
|
||||||
|
|
||||||
|
支持的目标语言代码:`en_US`, `zh_CN`, `zh_TW`, `zh_HK`, `ja_JP`, `ko_KR`, `vi_VN`, `th_TH`, `id_ID`, `ms_MY`, `es_419`, `fr_FR`, `pt_BR`, `tr_TR`, `ru_RU`, `de_DE`, `hi_IN`, `hu_HU`, `pl_PL`, `sv_SE`, `fi_FI`, `cs_CZ`, `ar_SA`, `tl_PH`, `he_IL`, `nl_NL`, `lo_LA`, `it_IT`。
|
||||||
|
|
||||||
### mute (会话免打扰)
|
### mute (会话免打扰)
|
||||||
|
|
||||||
#### 会话消息免打扰 — 开启或关闭会话消息免打扰(支持单聊和群聊)
|
#### 会话消息免打扰 — 开启或关闭会话消息免打扰(支持单聊和群聊)
|
||||||
@@ -1644,6 +1769,7 @@ Flags:
|
|||||||
用户说"查看会话分组/自定义分组" → `chat category list`
|
用户说"查看会话分组/自定义分组" → `chat category list`
|
||||||
用户说"某个分组下的会话/分组会话列表" → `chat category list-conversations`
|
用户说"某个分组下的会话/分组会话列表" → `chat category list-conversations`
|
||||||
用户说"新建会话分组/创建分组" → `chat category create`
|
用户说"新建会话分组/创建分组" → `chat category create`
|
||||||
|
用户说"创建智能分组/新建智能会话分组" → `chat category create-smart`
|
||||||
用户说"删除会话分组" → `chat category delete`
|
用户说"删除会话分组" → `chat category delete`
|
||||||
用户说"重命名分组/改分组名" → `chat category rename`
|
用户说"重命名分组/改分组名" → `chat category rename`
|
||||||
用户说"把会话加入分组/会话归类到分组" → `chat category add-conv`
|
用户说"把会话加入分组/会话归类到分组" → `chat category add-conv`
|
||||||
@@ -1658,7 +1784,10 @@ Flags:
|
|||||||
用户说"查询某人的群身份/某人在群里有什么身份" → `chat group-role query-user`
|
用户说"查询某人的群身份/某人在群里有什么身份" → `chat group-role query-user`
|
||||||
用户说"转让群主/换群主/群主转让" → `chat group transfer-owner`
|
用户说"转让群主/换群主/群主转让" → `chat group transfer-owner`
|
||||||
用户说"群邀请链接/入群链接/加群链接" → `chat group invite-url`
|
用户说"群邀请链接/入群链接/加群链接" → `chat group invite-url`
|
||||||
|
用户说"分享群链接/把群分享给某人/群链接发到某群" → `chat group share-invite`
|
||||||
|
用户说"群公告/发布公告/修改公告/查看公告/定时公告" → `chat group notice create/edit/get/list`
|
||||||
用户说"批量查消息/按ID查消息/根据消息ID查" → `chat message list-by-ids`
|
用户说"批量查消息/按ID查消息/根据消息ID查" → `chat message list-by-ids`
|
||||||
|
用户说"批量查消息回复/表情回复/文字回复/消息回应列表" → `chat message list-emotion-replies`
|
||||||
用户说"emoji回应/表情回应/给消息加表情" → `chat message add-emoji`
|
用户说"emoji回应/表情回应/给消息加表情" → `chat message add-emoji`
|
||||||
用户说"取消emoji回应/移除表情回应" → `chat message remove-emoji`
|
用户说"取消emoji回应/移除表情回应" → `chat message remove-emoji`
|
||||||
用户说"文字表情回应/添加文字表情" → `chat message add-text-emotion`
|
用户说"文字表情回应/添加文字表情" → `chat message add-text-emotion`
|
||||||
@@ -1697,6 +1826,7 @@ Flags:
|
|||||||
用户说"全员禁言/群禁言/解除禁言" → `chat group-mute`
|
用户说"全员禁言/群禁言/解除禁言" → `chat group-mute`
|
||||||
用户说"禁言某人/指定成员禁言/解除某人禁言" → `chat group-mute-member`
|
用户说"禁言某人/指定成员禁言/解除某人禁言" → `chat group-mute-member`
|
||||||
用户说"设管理员/取消管理员/设置群管理员" → `chat group set-admin`
|
用户说"设管理员/取消管理员/设置群管理员" → `chat group set-admin`
|
||||||
|
用户说"翻译文本/把这段话翻译成英文/翻译成中文" → `chat text translate`
|
||||||
|
|
||||||
关键区分:
|
关键区分:
|
||||||
- `chat search` — 搜**群/会话名**返回 `openConversationId`,**不**搜消息内容;要搜消息内容请用 `chat message search-advanced`(首选)/ `chat message search` / `list-by-sender` / `list-all`,**勿混淆**
|
- `chat search` — 搜**群/会话名**返回 `openConversationId`,**不**搜消息内容;要搜消息内容请用 `chat message search-advanced`(首选)/ `chat message search` / `list-by-sender` / `list-all`,**勿混淆**
|
||||||
@@ -1721,13 +1851,17 @@ Flags:
|
|||||||
- `chat message search-advanced` — 多维度搜索消息(支持关键词、发送者、@我、@指定人、多个会话等维度组合,与 `search` 的区别:`search` 仅支持关键词且必填,`search-advanced` 所有参数均可选)
|
- `chat message search-advanced` — 多维度搜索消息(支持关键词、发送者、@我、@指定人、多个会话等维度组合,与 `search` 的区别:`search` 仅支持关键词且必填,`search-advanced` 所有参数均可选)
|
||||||
- `chat message list-by-ids` — 根据消息 ID 批量查询消息(最多 50 条)
|
- `chat message list-by-ids` — 根据消息 ID 批量查询消息(最多 50 条)
|
||||||
- `chat message add-emoji` / `remove-emoji` — 对消息添加/移除 emoji 表情回应
|
- `chat message add-emoji` / `remove-emoji` — 对消息添加/移除 emoji 表情回应
|
||||||
|
- `chat message list-emotion-replies` — 批量拉取消息的表情回复和文字回复
|
||||||
- `chat message add-text-emotion` / `remove-text-emotion` — 对消息添加/移除文字表情回应
|
- `chat message add-text-emotion` / `remove-text-emotion` — 对消息添加/移除文字表情回应
|
||||||
- `chat message create-text-emotion` — 创建文字表情模板,返回 emotionId 供 add-text-emotion 使用
|
- `chat message create-text-emotion` — 创建文字表情模板,返回 emotionId 供 add-text-emotion 使用
|
||||||
- `chat category list` — 获取用户自定义会话分组列表
|
- `chat category list` — 获取用户自定义会话分组列表
|
||||||
- `chat category list-conversations` — 拉取指定分组下的会话列表
|
- `chat category list-conversations` — 拉取指定分组下的会话列表
|
||||||
|
- `chat category create-smart` — 创建智能会话分组(可指定群名称关键词和成员作为匹配规则)
|
||||||
- `chat mute` — 开启/关闭会话消息免打扰(默认开启,--off 关闭)
|
- `chat mute` — 开启/关闭会话消息免打扰(默认开启,--off 关闭)
|
||||||
- `chat group transfer-owner` — 转让群主
|
- `chat group transfer-owner` — 转让群主
|
||||||
- `chat group invite-url` — 获取群邀请链接
|
- `chat group invite-url` — 获取群邀请链接
|
||||||
|
- `chat group share-invite` — 分享群聊链接到会话
|
||||||
|
- `chat group notice create/edit/get/list` — 群公告发布、修改、详情、列表
|
||||||
- `chat message reply` — 引用回复消息(在群聊中引用某条消息并回复文字)
|
- `chat message reply` — 引用回复消息(在群聊中引用某条消息并回复文字)
|
||||||
- `chat message forward` — 转发单条消息(将一条消息从源会话转发到目标会话)
|
- `chat message forward` — 转发单条消息(将一条消息从源会话转发到目标会话)
|
||||||
- `chat set-top` — 设置/取消会话置顶(默认置顶,--off 取消)
|
- `chat set-top` — 设置/取消会话置顶(默认置顶,--off 取消)
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ metadata:
|
|||||||
|
|
||||||
# 钉钉通讯录 Skill
|
# 钉钉通讯录 Skill
|
||||||
|
|
||||||
> 🧪 **EXPERIMENTAL · 试验版 / Preview** — multi 模式当前未达 stable 标准。20 个 dingtalk-* skill 全部通过 dispatch verifier,但接口、命名、跨 skill 引用后续可能调整;生产 / 共享环境请优先使用 mono 模式(`dws skill setup --mode mono`)。问题请提 issue 反馈。
|
> 🧪 **EXPERIMENTAL · 试验版 / Preview** — multi 模式当前未达 stable 标准。22 个 dingtalk-* skill 全部通过 dispatch verifier,但接口、命名、跨 skill 引用后续可能调整;生产 / 共享环境请优先使用 mono 模式(`dws skill setup --mode mono`)。问题请提 issue 反馈。
|
||||||
|
|
||||||
> **PREREQUISITE:** Read the `dws-shared` skill first for auth, global flags, product routing, URL preflight, error codes, and safety rules. The `dws` binary must be on PATH.
|
> **PREREQUISITE:** Read the `dws-shared` skill first for auth, global flags, product routing, URL preflight, error codes, and safety rules. The `dws` binary must be on PATH.
|
||||||
|
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ metadata:
|
|||||||
|
|
||||||
# 钉钉开放平台文档 Skill
|
# 钉钉开放平台文档 Skill
|
||||||
|
|
||||||
> 🧪 **EXPERIMENTAL · 试验版 / Preview** — multi 模式当前未达 stable 标准。20 个 dingtalk-* skill 全部通过 dispatch verifier,但接口、命名、跨 skill 引用后续可能调整;生产 / 共享环境请优先使用 mono 模式(`dws skill setup --mode mono`)。问题请提 issue 反馈。
|
> 🧪 **EXPERIMENTAL · 试验版 / Preview** — multi 模式当前未达 stable 标准。22 个 dingtalk-* skill 全部通过 dispatch verifier,但接口、命名、跨 skill 引用后续可能调整;生产 / 共享环境请优先使用 mono 模式(`dws skill setup --mode mono`)。问题请提 issue 反馈。
|
||||||
|
|
||||||
> **PREREQUISITE:** Read the `dws-shared` skill first for auth, global flags, product routing, URL preflight, error codes, and safety rules. The `dws` binary must be on PATH.
|
> **PREREQUISITE:** Read the `dws-shared` skill first for auth, global flags, product routing, URL preflight, error codes, and safety rules. The `dws` binary must be on PATH.
|
||||||
|
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ metadata:
|
|||||||
|
|
||||||
# 钉钉 DING 紧急消息 Skill
|
# 钉钉 DING 紧急消息 Skill
|
||||||
|
|
||||||
> 🧪 **EXPERIMENTAL · 试验版 / Preview** — multi 模式当前未达 stable 标准。20 个 dingtalk-* skill 全部通过 dispatch verifier,但接口、命名、跨 skill 引用后续可能调整;生产 / 共享环境请优先使用 mono 模式(`dws skill setup --mode mono`)。问题请提 issue 反馈。
|
> 🧪 **EXPERIMENTAL · 试验版 / Preview** — multi 模式当前未达 stable 标准。22 个 dingtalk-* skill 全部通过 dispatch verifier,但接口、命名、跨 skill 引用后续可能调整;生产 / 共享环境请优先使用 mono 模式(`dws skill setup --mode mono`)。问题请提 issue 反馈。
|
||||||
|
|
||||||
> **PREREQUISITE:** Read the `dws-shared` skill first for auth, global flags, product routing, URL preflight, error codes, and safety rules. The `dws` binary must be on PATH.
|
> **PREREQUISITE:** Read the `dws-shared` skill first for auth, global flags, product routing, URL preflight, error codes, and safety rules. The `dws` binary must be on PATH.
|
||||||
|
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ metadata:
|
|||||||
|
|
||||||
# 钉钉文档 Skill
|
# 钉钉文档 Skill
|
||||||
|
|
||||||
> 🧪 **EXPERIMENTAL · 试验版 / Preview** — multi 模式当前未达 stable 标准。20 个 dingtalk-* skill 全部通过 dispatch verifier,但接口、命名、跨 skill 引用后续可能调整;生产 / 共享环境请优先使用 mono 模式(`dws skill setup --mode mono`)。问题请提 issue 反馈。
|
> 🧪 **EXPERIMENTAL · 试验版 / Preview** — multi 模式当前未达 stable 标准。22 个 dingtalk-* skill 全部通过 dispatch verifier,但接口、命名、跨 skill 引用后续可能调整;生产 / 共享环境请优先使用 mono 模式(`dws skill setup --mode mono`)。问题请提 issue 反馈。
|
||||||
|
|
||||||
> **PREREQUISITE:** Read the `dws-shared` skill first for auth, global flags, product routing, URL preflight, error codes, and safety rules. The `dws` binary must be on PATH.
|
> **PREREQUISITE:** Read the `dws-shared` skill first for auth, global flags, product routing, URL preflight, error codes, and safety rules. The `dws` binary must be on PATH.
|
||||||
|
|
||||||
|
|||||||
@@ -131,6 +131,27 @@ Flags:
|
|||||||
--convert 是否转换为钉钉在线文档
|
--convert 是否转换为钉钉在线文档
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### 导入本地文件为在线文档
|
||||||
|
|
||||||
|
详见 [doc/doc-import.md](./doc/doc-import.md)。
|
||||||
|
|
||||||
|
```
|
||||||
|
Usage:
|
||||||
|
dws doc import [flags]
|
||||||
|
dws doc import get [flags]
|
||||||
|
Example:
|
||||||
|
dws doc import --file ./report.docx
|
||||||
|
dws doc import --file ./notes.md --folder <FOLDER_ID>
|
||||||
|
dws doc import --file ./data.xlsx --workspace <WS_ID>
|
||||||
|
dws doc import get --task-id <TASK_ID>
|
||||||
|
Flags:
|
||||||
|
--file string 本地文件路径 (必填)
|
||||||
|
--folder string 目标文件夹 ID 或 URL
|
||||||
|
--workspace string 目标知识库 ID 或 URL
|
||||||
|
--name string 导入后文档名称 (默认取文件名)
|
||||||
|
--task-id string 导入任务 ID (import get 必填)
|
||||||
|
```
|
||||||
|
|
||||||
### 下载文件到本地(已迁移到 drive)
|
### 下载文件到本地(已迁移到 drive)
|
||||||
|
|
||||||
> **弃用提示**:`dws doc download` 已迁移到 `dws drive download`(执行 `doc download` 会打印弃用警告)。下载已有文件(PDF/图片/附件等非在线文档)改用:
|
> **弃用提示**:`dws doc download` 已迁移到 `dws drive download`(执行 `doc download` 会打印弃用警告)。下载已有文件(PDF/图片/附件等非在线文档)改用:
|
||||||
@@ -618,6 +639,12 @@ Flags:
|
|||||||
- 上传 → `upload`(需本地文件路径)
|
- 上传 → `upload`(需本地文件路径)
|
||||||
- 上传并转换 → `upload --convert`
|
- 上传并转换 → `upload --convert`
|
||||||
|
|
||||||
|
用户说"导入文件/导入为在线文档/导入 Word/导入 Excel/导入 xmind/导入 Markdown/把本地文件转在线文档":
|
||||||
|
- 导入并转换为在线文档 → `doc import --file <本地路径>`
|
||||||
|
- 支持 docx/doc/xlsx/xls/md/txt/xmind/mark,文件大小不超过 20MB
|
||||||
|
- 如果用户指定知识库或文件夹,补充 `--workspace` 或 `--folder`
|
||||||
|
- 不要把本地文件内容先读出来再 `doc create/update`;应直接使用 `doc import`
|
||||||
|
|
||||||
用户说"下载/导出/下载到本地/导出文档/导出为Word/导出为docx/把文档导出来":
|
用户说"下载/导出/下载到本地/导出文档/导出为Word/导出为docx/把文档导出来":
|
||||||
- **必须先判断目标文件类型**,再决定走 `doc export` 还是 `drive download`:
|
- **必须先判断目标文件类型**,再决定走 `doc export` 还是 `drive download`:
|
||||||
- 在线文档(alidocs/adoc)→ **`doc export`**(内容级命令,格式转换后导出为 docx,未迁移)
|
- 在线文档(alidocs/adoc)→ **`doc export`**(内容级命令,格式转换后导出为 docx,未迁移)
|
||||||
@@ -768,6 +795,20 @@ dws drive download --node <NODE_ID> --output ~/downloads/
|
|||||||
# 如果是在线文档 (ALIDOC),用 doc export:
|
# 如果是在线文档 (ALIDOC),用 doc export:
|
||||||
dws doc export --node <NODE_ID> --output ~/downloads/
|
dws doc export --node <NODE_ID> --output ~/downloads/
|
||||||
|
|
||||||
|
# ── 工作流 5a: 导入本地文件为在线文档 ──
|
||||||
|
|
||||||
|
# 导入到默认位置
|
||||||
|
dws doc import --file ./report.docx --format json
|
||||||
|
|
||||||
|
# 导入到指定文件夹
|
||||||
|
dws doc import --file ./notes.md --folder <DOC_FOLDER_NODE_ID> --format json
|
||||||
|
|
||||||
|
# 导入到知识库
|
||||||
|
dws doc import --file ./data.xlsx --workspace <WS_ID> --format json
|
||||||
|
|
||||||
|
# 如果导入命令超时或中断,可用 import get 手动查询任务状态:
|
||||||
|
# dws doc import get --task-id <TASK_ID>
|
||||||
|
|
||||||
# ── 工作流 6: 上传附件并插入文档 ──
|
# ── 工作流 6: 上传附件并插入文档 ──
|
||||||
|
|
||||||
# media insert 自动完成三步流程:
|
# media insert 自动完成三步流程:
|
||||||
@@ -882,6 +923,7 @@ dws doc export --node <DOC_ID_OR_URL> --output ./exported.docx
|
|||||||
| `drive list`(原 `doc list`,已弃用) | `nodes[].nodeId` / folder 类型 `nodeId` | read / info / update / block 的 --node;folder 用作 `--folder` |
|
| `drive list`(原 `doc list`,已弃用) | `nodes[].nodeId` / folder 类型 `nodeId` | read / info / update / block 的 --node;folder 用作 `--folder` |
|
||||||
| `drive search`(原 `doc search`,已弃用) | 文档 `nodeId` / URL / `createTime` / `creatorUid` | read / info / update 的 --node;创建时间与创建者信息 |
|
| `drive search`(原 `doc search`,已弃用) | 文档 `nodeId` / URL / `createTime` / `creatorUid` | read / info / update 的 --node;创建时间与创建者信息 |
|
||||||
| `create` | `nodeId` | update / block 操作的 --node |
|
| `create` | `nodeId` | update / block 操作的 --node |
|
||||||
|
| `import` | `nodeId` / `documentUrl` / `documentName` / `documentType`;中断时提取 `taskId` | 后续 read / info / sheet 操作;中断后用 `doc import get --task-id` |
|
||||||
| `drive mkdir`(原 `doc folder create`,已弃用) | `nodeId` | create 的 --folder |
|
| `drive mkdir`(原 `doc folder create`,已弃用) | `nodeId` | create 的 --folder |
|
||||||
| `block list` | `blockId` | block insert 的 --ref-block, block update/delete 的 --block-id |
|
| `block list` | `blockId` | block insert 的 --ref-block, block update/delete 的 --block-id |
|
||||||
| `comment list` | `commentList[].commentKey` | comment reply 的 --comment-key |
|
| `comment list` | `commentList[].commentKey` | comment reply 的 --comment-key |
|
||||||
|
|||||||
@@ -0,0 +1,53 @@
|
|||||||
|
# 本地文件导入为在线文档 (doc import)
|
||||||
|
|
||||||
|
## 使用场景
|
||||||
|
|
||||||
|
用户说"导入 Word/Excel/Markdown/xmind 到钉钉文档"、"把本地文件转成在线文档"、"导入到知识库/文件夹"时,使用 `dws doc import`。
|
||||||
|
|
||||||
|
不要先读取文件内容再调用 `doc create` 或 `doc update`。`doc import` 会按文件格式走导入任务,保留更完整的原始结构。
|
||||||
|
|
||||||
|
## 命令
|
||||||
|
|
||||||
|
```bash
|
||||||
|
dws doc import --file ./report.docx --format json
|
||||||
|
dws doc import --file ./notes.md --folder <FOLDER_ID> --format json
|
||||||
|
dws doc import --file ./data.xlsx --workspace <WORKSPACE_ID> --format json
|
||||||
|
dws doc import --file ./draft.md --name "项目周报" --format json
|
||||||
|
```
|
||||||
|
|
||||||
|
```bash
|
||||||
|
dws doc import get --task-id <TASK_ID> --format json
|
||||||
|
```
|
||||||
|
|
||||||
|
## 参数
|
||||||
|
|
||||||
|
| 参数 | 说明 |
|
||||||
|
|------|------|
|
||||||
|
| `--file` | 本地文件路径,必填 |
|
||||||
|
| `--folder` | 目标文件夹 ID 或 URL,可选 |
|
||||||
|
| `--workspace` | 目标知识库 ID 或 URL,可选 |
|
||||||
|
| `--name` / `-n` | 导入后的文档名称;不传时使用文件名 |
|
||||||
|
| `--task-id` | `import get` 查询导入任务时必填 |
|
||||||
|
|
||||||
|
支持格式:docx、doc、xlsx、xls、md、txt、xmind、mark。文件大小上限 20MB。
|
||||||
|
|
||||||
|
## 工作流
|
||||||
|
|
||||||
|
1. 确认本地文件存在且格式受支持。
|
||||||
|
2. 如果用户指定目标知识库或文件夹,传 `--workspace` 或 `--folder`;未指定时导入到默认位置。
|
||||||
|
3. 执行 `dws doc import --file ... --format json`。
|
||||||
|
4. 正常情况下 CLI 会自动提交、上传并轮询导入任务。
|
||||||
|
5. 如果命令超时或中断,从输出中提取 `taskId`,再执行 `dws doc import get --task-id <TASK_ID> --format json`。
|
||||||
|
|
||||||
|
## 上下文传递
|
||||||
|
|
||||||
|
| 操作 | 从返回中提取 | 用于 |
|
||||||
|
|------|-------------|------|
|
||||||
|
| `doc import` | `nodeId` / `documentUrl` / `documentName` / `documentType` | 后续 `doc read` / `doc info` / `sheet` 操作 |
|
||||||
|
| `doc import` 中断 | `taskId` | `doc import get --task-id` 查询任务状态 |
|
||||||
|
|
||||||
|
## 注意事项
|
||||||
|
|
||||||
|
- `--folder` 和 `--workspace` 都是目标位置参数;用户明确指定文件夹时优先使用 `--folder`。
|
||||||
|
- 导入 Excel 后通常得到在线表格,后续数据读取和编辑走 `dws sheet ...`。
|
||||||
|
- 导入 Markdown 或 Word 后通常得到在线文档,后续内容读取和编辑走 `dws doc ...`。
|
||||||
@@ -12,7 +12,7 @@ metadata:
|
|||||||
|
|
||||||
# 钉盘 Skill
|
# 钉盘 Skill
|
||||||
|
|
||||||
> 🧪 **EXPERIMENTAL · 试验版 / Preview** — multi 模式当前未达 stable 标准。20 个 dingtalk-* skill 全部通过 dispatch verifier,但接口、命名、跨 skill 引用后续可能调整;生产 / 共享环境请优先使用 mono 模式(`dws skill setup --mode mono`)。问题请提 issue 反馈。
|
> 🧪 **EXPERIMENTAL · 试验版 / Preview** — multi 模式当前未达 stable 标准。22 个 dingtalk-* skill 全部通过 dispatch verifier,但接口、命名、跨 skill 引用后续可能调整;生产 / 共享环境请优先使用 mono 模式(`dws skill setup --mode mono`)。问题请提 issue 反馈。
|
||||||
|
|
||||||
> **PREREQUISITE:** Read the `dws-shared` skill first for auth, global flags, product routing, URL preflight, error codes, and safety rules. The `dws` binary must be on PATH.
|
> **PREREQUISITE:** Read the `dws-shared` skill first for auth, global flags, product routing, URL preflight, error codes, and safety rules. The `dws` binary must be on PATH.
|
||||||
|
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ metadata:
|
|||||||
|
|
||||||
# 钉钉直播 Skill
|
# 钉钉直播 Skill
|
||||||
|
|
||||||
> 🧪 **EXPERIMENTAL · 试验版 / Preview** — multi 模式当前未达 stable 标准。20 个 dingtalk-* skill 全部通过 dispatch verifier,但接口、命名、跨 skill 引用后续可能调整;生产 / 共享环境请优先使用 mono 模式(`dws skill setup --mode mono`)。问题请提 issue 反馈。
|
> 🧪 **EXPERIMENTAL · 试验版 / Preview** — multi 模式当前未达 stable 标准。22 个 dingtalk-* skill 全部通过 dispatch verifier,但接口、命名、跨 skill 引用后续可能调整;生产 / 共享环境请优先使用 mono 模式(`dws skill setup --mode mono`)。问题请提 issue 反馈。
|
||||||
|
|
||||||
> **PREREQUISITE:** Read the `dws-shared` skill first for auth, global flags, product routing, URL preflight, error codes, and safety rules. The `dws` binary must be on PATH.
|
> **PREREQUISITE:** Read the `dws-shared` skill first for auth, global flags, product routing, URL preflight, error codes, and safety rules. The `dws` binary must be on PATH.
|
||||||
|
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ metadata:
|
|||||||
|
|
||||||
# 钉钉邮箱 Skill
|
# 钉钉邮箱 Skill
|
||||||
|
|
||||||
> 🧪 **EXPERIMENTAL · 试验版 / Preview** — multi 模式当前未达 stable 标准。20 个 dingtalk-* skill 全部通过 dispatch verifier,但接口、命名、跨 skill 引用后续可能调整;生产 / 共享环境请优先使用 mono 模式(`dws skill setup --mode mono`)。问题请提 issue 反馈。
|
> 🧪 **EXPERIMENTAL · 试验版 / Preview** — multi 模式当前未达 stable 标准。22 个 dingtalk-* skill 全部通过 dispatch verifier,但接口、命名、跨 skill 引用后续可能调整;生产 / 共享环境请优先使用 mono 模式(`dws skill setup --mode mono`)。问题请提 issue 反馈。
|
||||||
|
|
||||||
> **PREREQUISITE:** Read the `dws-shared` skill first for auth, global flags, product routing, URL preflight, error codes, and safety rules. The `dws` binary must be on PATH.
|
> **PREREQUISITE:** Read the `dws-shared` skill first for auth, global flags, product routing, URL preflight, error codes, and safety rules. The `dws` binary must be on PATH.
|
||||||
|
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
| 命令 | 功能简述 |
|
| 命令 | 功能简述 |
|
||||||
|------|----------|
|
|------|----------|
|
||||||
| `dws mail mailbox list` | 查询**当前用户自己**的可用邮箱列表 |
|
| `dws mail mailbox list` | 查询**当前用户自己**的可用邮箱列表 |
|
||||||
|
| `dws mail mailbox profile` | 获取用户邮箱详细信息(容量、别名等) |
|
||||||
| `dws mail message list` | 列出指定文件夹中的邮件(默认收件箱) |
|
| `dws mail message list` | 列出指定文件夹中的邮件(默认收件箱) |
|
||||||
| `dws mail message search` | 搜索邮件(KQL 语法,按主题/发件人/日期等) |
|
| `dws mail message search` | 搜索邮件(KQL 语法,按主题/发件人/日期等) |
|
||||||
| `dws mail message get` | 查看邮件完整内容(含正文) |
|
| `dws mail message get` | 查看邮件完整内容(含正文) |
|
||||||
@@ -15,7 +16,10 @@
|
|||||||
| `dws mail message batch-move` | 批量移动邮件到指定文件夹 |
|
| `dws mail message batch-move` | 批量移动邮件到指定文件夹 |
|
||||||
| `dws mail message batch-delete` | 批量删除邮件 |
|
| `dws mail message batch-delete` | 批量删除邮件 |
|
||||||
| `dws mail message batch-update` | 批量修改邮件状态(标记已读/未读/添加标签/移除标签) |
|
| `dws mail message batch-update` | 批量修改邮件状态(标记已读/未读/添加标签/移除标签) |
|
||||||
|
| `dws mail message batch-get` | 批量获取邮件详情(最多 20 封) |
|
||||||
| `dws mail message verify` | 根据 internetMessageId 查询邮件发送状态 |
|
| `dws mail message verify` | 根据 internetMessageId 查询邮件发送状态 |
|
||||||
|
| `dws mail sent-message recall` | 撤回已发送的邮件(仅支持同组织内未读邮件) |
|
||||||
|
| `dws mail sent-message recall-detail` | 查询邮件撤回进度 |
|
||||||
| `dws mail draft create` | 创建草稿(保留在草稿箱,不发送) |
|
| `dws mail draft create` | 创建草稿(保留在草稿箱,不发送) |
|
||||||
| `dws mail draft update` | 更新草稿内容(保留在草稿箱,不发送) |
|
| `dws mail draft update` | 更新草稿内容(保留在草稿箱,不发送) |
|
||||||
| `dws mail draft send` | 发送草稿箱中已有的草稿 |
|
| `dws mail draft send` | 发送草稿箱中已有的草稿 |
|
||||||
@@ -46,6 +50,13 @@
|
|||||||
| `dws mail contact update` | 更新个人邮件联系人信息 |
|
| `dws mail contact update` | 更新个人邮件联系人信息 |
|
||||||
| `dws mail contact batch-delete` | 批量删除个人邮件联系人 |
|
| `dws mail contact batch-delete` | 批量删除个人邮件联系人 |
|
||||||
| `dws mail auto-reply get` | 获取用户的自动回复配置 |
|
| `dws mail auto-reply get` | 获取用户的自动回复配置 |
|
||||||
|
| `dws mail auto-reply update` | 更新/设置用户的自动回复配置 |
|
||||||
|
| `dws mail allow-list list` | 列出个人收信白名单 |
|
||||||
|
| `dws mail allow-list add` | 添加个人收信白名单 |
|
||||||
|
| `dws mail allow-list remove` | 移除个人收信白名单 |
|
||||||
|
| `dws mail block-list list` | 列出个人收信黑名单 |
|
||||||
|
| `dws mail block-list add` | 添加个人收信黑名单 |
|
||||||
|
| `dws mail block-list remove` | 移除个人收信黑名单 |
|
||||||
| `dws mail rule list` | 列出个人收信规则 |
|
| `dws mail rule list` | 列出个人收信规则 |
|
||||||
| `dws mail rule create` | 创建个人收信规则 |
|
| `dws mail rule create` | 创建个人收信规则 |
|
||||||
| `dws mail rule update` | 更新个人收信规则 |
|
| `dws mail rule update` | 更新个人收信规则 |
|
||||||
@@ -93,6 +104,27 @@ Example:
|
|||||||
|------|------|------|
|
|------|------|------|
|
||||||
| `emailAccounts` | `List[]` | 邮箱列表,每条包含 `email`(邮箱地址)、`orgName`(所属企业,个人邮箱为 null)、`type`(`ORG`=企业邮箱 / `PERSONAL`=个人邮箱) |
|
| `emailAccounts` | `List[]` | 邮箱列表,每条包含 `email`(邮箱地址)、`orgName`(所属企业,个人邮箱为 null)、`type`(`ORG`=企业邮箱 / `PERSONAL`=个人邮箱) |
|
||||||
|
|
||||||
|
### 获取用户邮箱信息
|
||||||
|
|
||||||
|
```
|
||||||
|
Usage:
|
||||||
|
dws mail mailbox profile [flags]
|
||||||
|
Example:
|
||||||
|
dws mail mailbox profile --email user@company.com
|
||||||
|
Flags:
|
||||||
|
--email string 用户的邮箱地址 (必填)
|
||||||
|
```
|
||||||
|
|
||||||
|
**返回字段:**
|
||||||
|
|
||||||
|
| 字段 | 类型 | 说明 |
|
||||||
|
|------|------|------|
|
||||||
|
| `email` | string | 邮箱地址 |
|
||||||
|
| `emailAliases` | List[] | 邮件地址别名列表 |
|
||||||
|
| `name` / `nickname` / `displayName` | string | 用户名、昵称、显示名 |
|
||||||
|
| `mboxSize` / `mboxSizeUsed` | int64 | 邮箱容量与已用容量(字节) |
|
||||||
|
| `createdTime` / `modifiedTime` | string | 创建时间与更新时间 |
|
||||||
|
|
||||||
### 查找他人邮箱地址(通讯录查人)
|
### 查找他人邮箱地址(通讯录查人)
|
||||||
|
|
||||||
> **这不是 `mailbox list`。** 当需要获取**某人**的邮箱地址时,必须走以下三路并发查询,取最先返回有效邮箱的结果。禁止臆测邮箱地址。
|
> **这不是 `mailbox list`。** 当需要获取**某人**的邮箱地址时,必须走以下三路并发查询,取最先返回有效邮箱的结果。禁止臆测邮箱地址。
|
||||||
@@ -218,6 +250,21 @@ Flags:
|
|||||||
|
|
||||||
> **注意:** 即使邮件确有附件,`message get` 返回的 `message` 对象也**不含** `attachments`/`isRead`/`tags` 字段(实测 keys 仅上表所列这些)。要取附件请另用 `dws mail attachment list`。
|
> **注意:** 即使邮件确有附件,`message get` 返回的 `message` 对象也**不含** `attachments`/`isRead`/`tags` 字段(实测 keys 仅上表所列这些)。要取附件请另用 `dws mail attachment list`。
|
||||||
|
|
||||||
|
### 批量获取邮件详情
|
||||||
|
|
||||||
|
```
|
||||||
|
Usage:
|
||||||
|
dws mail message batch-get [flags]
|
||||||
|
Example:
|
||||||
|
dws mail message batch-get --email user@company.com --ids <id1>,<id2>
|
||||||
|
dws mail message batch-get --email user@company.com --ids <id1>,<id2>,<id3>
|
||||||
|
Flags:
|
||||||
|
--email string 邮件所属邮箱地址 (必填)
|
||||||
|
--ids string 要获取的邮件 ID 列表,逗号分隔,最多 20 个 (必填)
|
||||||
|
```
|
||||||
|
|
||||||
|
单次最多获取 20 封邮件。CLI 会逐个调用 `get_email_by_message_id`,并以 `messages` 数组聚合返回。
|
||||||
|
|
||||||
### 发送邮件
|
### 发送邮件
|
||||||
```
|
```
|
||||||
Usage:
|
Usage:
|
||||||
@@ -935,6 +982,53 @@ Flags:
|
|||||||
| `failed` | 发送失败 |
|
| `failed` | 发送失败 |
|
||||||
| `unknown` | 未知状态 |
|
| `unknown` | 未知状态 |
|
||||||
|
|
||||||
|
### [危险] 撤回已发送的邮件
|
||||||
|
|
||||||
|
```
|
||||||
|
Usage:
|
||||||
|
dws mail sent-message recall [flags]
|
||||||
|
Example:
|
||||||
|
dws mail sent-message recall --email user@company.com --id <mailId> --subject "邮件主题" --yes
|
||||||
|
Flags:
|
||||||
|
--email string 发件人邮箱地址 (必填)
|
||||||
|
--id string 要撤回的邮件 ID (必填)
|
||||||
|
--subject string 邮件主题 (必填)
|
||||||
|
--yes 跳过确认提示,直接执行 (可选)
|
||||||
|
```
|
||||||
|
|
||||||
|
> CAUTION: 此命令会撤回已发送邮件。仅支持撤回同组织内未读邮件,执行前必须确认目标邮件、主题和影响范围。
|
||||||
|
|
||||||
|
**返回字段:**
|
||||||
|
|
||||||
|
| 字段 | 类型 | 说明 |
|
||||||
|
|------|------|------|
|
||||||
|
| `id` | string | 撤回任务 ID,可用于 `recall-detail` 查询进度 |
|
||||||
|
| `success` | boolean | 接口调用是否成功 |
|
||||||
|
| `errorCode` / `errorMsg` | string | 错误码和错误信息(仅失败时存在) |
|
||||||
|
|
||||||
|
### 查询邮件撤回进度
|
||||||
|
|
||||||
|
```
|
||||||
|
Usage:
|
||||||
|
dws mail sent-message recall-detail [flags]
|
||||||
|
Example:
|
||||||
|
dws mail sent-message recall-detail --email user@company.com --id <recallTaskId>
|
||||||
|
Flags:
|
||||||
|
--email string 用户的邮箱地址 (必填)
|
||||||
|
--id string 撤回任务 ID (必填),由 recall 命令返回
|
||||||
|
```
|
||||||
|
|
||||||
|
根据撤回任务 ID 查询邮件撤回进度。任务状态包括 `UNINITED` / `SUBMITTED` / `RUNNING` / `FINISHED` / `CANCELED` / `FAILED`。
|
||||||
|
|
||||||
|
**返回字段:**
|
||||||
|
|
||||||
|
| 字段 | 类型 | 说明 |
|
||||||
|
|------|------|------|
|
||||||
|
| `id` | string | 撤回任务 ID |
|
||||||
|
| `status` | string | 任务状态 |
|
||||||
|
| `totalCount` / `succeededCount` / `failedCount` | int | 总数、成功数、失败数 |
|
||||||
|
| `details` | List[] | 每封邮件的撤回结果 |
|
||||||
|
|
||||||
### 创建草稿
|
### 创建草稿
|
||||||
```
|
```
|
||||||
Usage:
|
Usage:
|
||||||
@@ -1220,6 +1314,61 @@ Flags:
|
|||||||
| `scope` | string | 回复范围: "contact"(仅联系人) 或 "all"(所有人) |
|
| `scope` | string | 回复范围: "contact"(仅联系人) 或 "all"(所有人) |
|
||||||
| `content` | string | 自动回复内容 |
|
| `content` | string | 自动回复内容 |
|
||||||
|
|
||||||
|
### 更新自动回复配置
|
||||||
|
|
||||||
|
更新或设置用户的邮件自动回复配置。建议先通过 `auto-reply get` 获取当前配置,再传入需要修改的字段值。
|
||||||
|
|
||||||
|
```
|
||||||
|
Usage:
|
||||||
|
dws mail auto-reply update [flags]
|
||||||
|
Example:
|
||||||
|
dws mail auto-reply update --email user@company.com --enabled true \
|
||||||
|
--start "2026/07/01 09:00:00 +0800" --end "2026/07/07 18:00:00 +0800" \
|
||||||
|
--scope all --content "出差中,请稍后联系"
|
||||||
|
dws mail auto-reply update --email user@company.com --enabled false \
|
||||||
|
--start "2026/07/01 09:00:00 +0800" --end "2026/07/07 18:00:00 +0800" \
|
||||||
|
--scope all --content "已关闭自动回复"
|
||||||
|
Flags:
|
||||||
|
--email string 用户的邮箱地址 (必填)
|
||||||
|
--enabled string 是否启用自动回复: true/false (必填)
|
||||||
|
--start string 自动回复开始时间,格式: YYYY/MM/DD HH:MM:SS +ZZZZ (必填)
|
||||||
|
--end string 自动回复结束时间,格式: YYYY/MM/DD HH:MM:SS +ZZZZ (必填)
|
||||||
|
--scope string 回复范围: contact(仅联系人)/all(所有人) (必填)
|
||||||
|
--content string 自动回复内容 (必填)
|
||||||
|
```
|
||||||
|
|
||||||
|
### 个人收信白名单管理
|
||||||
|
|
||||||
|
```
|
||||||
|
Usage:
|
||||||
|
dws mail allow-list list [flags]
|
||||||
|
dws mail allow-list add [flags]
|
||||||
|
dws mail allow-list remove [flags]
|
||||||
|
Examples:
|
||||||
|
dws mail allow-list list --email user@company.com
|
||||||
|
dws mail allow-list add --email user@company.com --entries a@b.com,@example.com
|
||||||
|
dws mail allow-list remove --email user@company.com --entries a@b.com,@example.com
|
||||||
|
Flags:
|
||||||
|
--email string 用户的邮箱地址 (必填)
|
||||||
|
--entries string 逗号分隔的地址列表,支持邮件地址或 @domain.com 域名(add/remove 必填)
|
||||||
|
```
|
||||||
|
|
||||||
|
### 个人收信黑名单管理
|
||||||
|
|
||||||
|
```
|
||||||
|
Usage:
|
||||||
|
dws mail block-list list [flags]
|
||||||
|
dws mail block-list add [flags]
|
||||||
|
dws mail block-list remove [flags]
|
||||||
|
Examples:
|
||||||
|
dws mail block-list list --email user@company.com
|
||||||
|
dws mail block-list add --email user@company.com --entries spam@bad.com,@junk.com
|
||||||
|
dws mail block-list remove --email user@company.com --entries spam@bad.com,@junk.com
|
||||||
|
Flags:
|
||||||
|
--email string 用户的邮箱地址 (必填)
|
||||||
|
--entries string 逗号分隔的地址列表,支持邮件地址或 @domain.com 域名(add/remove 必填)
|
||||||
|
```
|
||||||
|
|
||||||
### 收信规则管理
|
### 收信规则管理
|
||||||
|
|
||||||
#### 列出收信规则
|
#### 列出收信规则
|
||||||
@@ -1473,9 +1622,11 @@ Flags:
|
|||||||
## 意图判断
|
## 意图判断
|
||||||
|
|
||||||
用户说"我的邮箱/邮箱地址" → `mailbox list`(**仅限查询自己的邮箱,不能查他人**)
|
用户说"我的邮箱/邮箱地址" → `mailbox list`(**仅限查询自己的邮箱,不能查他人**)
|
||||||
|
用户说"邮箱详情/邮箱容量/邮箱别名/邮箱 profile" → `mailbox profile --email <邮箱>`
|
||||||
用户说"获取/查找/得到 某人的邮箱地址" → **不是 `mailbox list`**,走三路并发查询流程(见「查找他人邮箱地址」章节)
|
用户说"获取/查找/得到 某人的邮箱地址" → **不是 `mailbox list`**,走三路并发查询流程(见「查找他人邮箱地址」章节)
|
||||||
用户说"找邮件/搜邮件/查邮件" → `message search`
|
用户说"找邮件/搜邮件/查邮件" → `message search`
|
||||||
用户说"看邮件/打开邮件/邮件内容" → 先 `message search` 获取 messageId,再 `message get`
|
用户说"看邮件/打开邮件/邮件内容" → 先 `message search` 获取 messageId,再 `message get`
|
||||||
|
用户说"批量查看邮件详情/批量打开这些邮件/按多个邮件ID取详情" → `message batch-get --ids <id1,id2,...>`(单次最多 20 个)
|
||||||
用户说"发邮件/写邮件" → 先 `mailbox list` 获取发件地址,再 `message send`
|
用户说"发邮件/写邮件" → 先 `mailbox list` 获取发件地址,再 `message send`
|
||||||
用户说“给(某人名字)发邮件” / “查询某人发给我的邮件” / “查询发给某人的邮件” / 任何涉及按人名查找邮箱的场景 →
|
用户说“给(某人名字)发邮件” / “查询某人发给我的邮件” / “查询发给某人的邮件” / 任何涉及按人名查找邮箱的场景 →
|
||||||
**第一步**:并发同时发起以下三路查询,取最先返回有效邮箱的结果;若三路均无有效邮箱,ask_human 请用户提供,禁止臆测:
|
**第一步**:并发同时发起以下三路查询,取最先返回有效邮箱的结果;若三路均无有效邮箱,ask_human 请用户提供,禁止臆测:
|
||||||
@@ -1502,6 +1653,11 @@ Flags:
|
|||||||
用户说"删除会话/把会话放入已删除/批量删除会话" → 单条用 `thread trash`,多条用 `thread batch-trash`;传入的是会话 ID,不是邮件 ID
|
用户说"删除会话/把会话放入已删除/批量删除会话" → 单条用 `thread trash`,多条用 `thread batch-trash`;传入的是会话 ID,不是邮件 ID
|
||||||
用户说"批量标记邮件已读/未读/给邮件加标签/移除邮件标签" → `message batch-update --action markRead/markUnread/addTags/removeTags`(操作对象是邮件 ID;会话级操作用 `thread update/batch-update`)
|
用户说"批量标记邮件已读/未读/给邮件加标签/移除邮件标签" → `message batch-update --action markRead/markUnread/addTags/removeTags`(操作对象是邮件 ID;会话级操作用 `thread update/batch-update`)
|
||||||
用户说"邮件发出去了吗/查邮件发送状态/确认邮件是否发送成功/邮件投递结果" → 用发送类命令返回的 `internetMessageId`,调用 `message verify` 查询 `sendStatus`
|
用户说"邮件发出去了吗/查邮件发送状态/确认邮件是否发送成功/邮件投递结果" → 用发送类命令返回的 `internetMessageId`,调用 `message verify` 查询 `sendStatus`
|
||||||
|
用户说"撤回邮件/撤回已发送邮件" → `sent-message recall --email <邮箱> --id <mailId> --subject "<主题>" --yes`(危险操作,先确认)
|
||||||
|
用户说"查询邮件撤回进度/撤回任务状态" → `sent-message recall-detail --email <邮箱> --id <recallTaskId>`
|
||||||
|
用户说"设置/更新/关闭自动回复" → `auto-reply update`(所有字段必填,先 `auto-reply get` 获取当前配置)
|
||||||
|
用户说"收信白名单/添加白名单/移除白名单" → `allow-list list/add/remove`
|
||||||
|
用户说"收信黑名单/添加黑名单/移除黑名单" → `block-list list/add/remove`
|
||||||
用户说"查看会话/获取会话/看这封邮件的会话" → 先 `message search` 或 `message get` 获取邮件中的 `conversationId`,再 `thread get`
|
用户说"查看会话/获取会话/看这封邮件的会话" → 先 `message search` 或 `message get` 获取邮件中的 `conversationId`,再 `thread get`
|
||||||
用户说"搜索/查找/联系 邮箱用户/联系人/某人的邮箱地址" → `user search`(搜索通讯录人员,不是搜邮件内容)
|
用户说"搜索/查找/联系 邮箱用户/联系人/某人的邮箱地址" → `user search`(搜索通讯录人员,不是搜邮件内容)
|
||||||
用户说"发送草稿/把草稿发出去/发这封草稿" → 先 `message search --query "folderId:5"` 找到草稿 messageId,再 `draft send`
|
用户说"发送草稿/把草稿发出去/发这封草稿" → 先 `message search --query "folderId:5"` 找到草稿 messageId,再 `draft send`
|
||||||
@@ -1618,7 +1774,9 @@ dws mail thread get --email user@company.com --id <conversationId> --select mess
|
|||||||
| 操作 | 从返回中提取 | 用于 |
|
| 操作 | 从返回中提取 | 用于 |
|
||||||
|------|-------------|------|
|
|------|-------------|------|
|
||||||
| `mailbox list` | 邮箱地址 | message search/get/send/thread get 的 --email/--from |
|
| `mailbox list` | 邮箱地址 | message search/get/send/thread get 的 --email/--from |
|
||||||
|
| `mailbox profile` | `emailAliases` / `mboxSize` / `mboxSizeUsed` | 展示邮箱别名与容量信息 |
|
||||||
| `message search` | `messageId` | message get 的 --id |
|
| `message search` | `messageId` | message get 的 --id |
|
||||||
|
| `message search` | `messageId` | message batch-get 的 --ids |
|
||||||
| `message search` | `conversationId` | thread get 的 --id |
|
| `message search` | `conversationId` | thread get 的 --id |
|
||||||
| `message search` | `messageId` | attachment list 的 --id |
|
| `message search` | `messageId` | attachment list 的 --id |
|
||||||
| `attachment list` | `attachments[].id` / `attachments[].name` | attachment download 的 --attachment-id / --name |
|
| `attachment list` | `attachments[].id` / `attachments[].name` | attachment download 的 --attachment-id / --name |
|
||||||
@@ -1633,6 +1791,8 @@ dws mail thread get --email user@company.com --id <conversationId> --select mess
|
|||||||
| `aisearch person` → `contact user get` / `contact user search` / `mail user search` | 用户邮箱 (orgAuthEmail / email) | message send 的 --to/--cc(三路并发,取先到结果) |
|
| `aisearch person` → `contact user get` / `contact user search` / `mail user search` | 用户邮箱 (orgAuthEmail / email) | message send 的 --to/--cc(三路并发,取先到结果) |
|
||||||
| `user search` | 用户邮箱 (email) | message send 的 --to/--cc |
|
| `user search` | 用户邮箱 (email) | message send 的 --to/--cc |
|
||||||
| `message send` / `draft send` / `message reply` / `message reply-all` / `message forward` | `internetMessageId` | `message verify` 的 --internet-message-id |
|
| `message send` / `draft send` / `message reply` / `message reply-all` / `message forward` | `internetMessageId` | `message verify` 的 --internet-message-id |
|
||||||
|
| `sent-message recall` | `id` | sent-message recall-detail 的 --id |
|
||||||
|
| `auto-reply get` | `enabled` / `startTime` / `endTime` / `scope` / `content` | auto-reply update 的当前配置基线 |
|
||||||
|
|
||||||
## 注意事项
|
## 注意事项
|
||||||
|
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ metadata:
|
|||||||
|
|
||||||
# 钉钉 AI 听记 Skill
|
# 钉钉 AI 听记 Skill
|
||||||
|
|
||||||
> 🧪 **EXPERIMENTAL · 试验版 / Preview** — multi 模式当前未达 stable 标准。20 个 dingtalk-* skill 全部通过 dispatch verifier,但接口、命名、跨 skill 引用后续可能调整;生产 / 共享环境请优先使用 mono 模式(`dws skill setup --mode mono`)。问题请提 issue 反馈。
|
> 🧪 **EXPERIMENTAL · 试验版 / Preview** — multi 模式当前未达 stable 标准。22 个 dingtalk-* skill 全部通过 dispatch verifier,但接口、命名、跨 skill 引用后续可能调整;生产 / 共享环境请优先使用 mono 模式(`dws skill setup --mode mono`)。问题请提 issue 反馈。
|
||||||
|
|
||||||
> **PREREQUISITE:** Read the `dws-shared` skill first for auth, global flags, product routing, URL preflight, error codes, and safety rules. The `dws` binary must be on PATH.
|
> **PREREQUISITE:** Read the `dws-shared` skill first for auth, global flags, product routing, URL preflight, error codes, and safety rules. The `dws` binary must be on PATH.
|
||||||
|
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ metadata:
|
|||||||
|
|
||||||
# 钉钉 OA 审批 Skill
|
# 钉钉 OA 审批 Skill
|
||||||
|
|
||||||
> 🧪 **EXPERIMENTAL · 试验版 / Preview** — multi 模式当前未达 stable 标准。20 个 dingtalk-* skill 全部通过 dispatch verifier,但接口、命名、跨 skill 引用后续可能调整;生产 / 共享环境请优先使用 mono 模式(`dws skill setup --mode mono`)。问题请提 issue 反馈。
|
> 🧪 **EXPERIMENTAL · 试验版 / Preview** — multi 模式当前未达 stable 标准。22 个 dingtalk-* skill 全部通过 dispatch verifier,但接口、命名、跨 skill 引用后续可能调整;生产 / 共享环境请优先使用 mono 模式(`dws skill setup --mode mono`)。问题请提 issue 反馈。
|
||||||
|
|
||||||
> **PREREQUISITE:** Read the `dws-shared` skill first for auth, global flags, product routing, URL preflight, error codes, and safety rules. The `dws` binary must be on PATH.
|
> **PREREQUISITE:** Read the `dws-shared` skill first for auth, global flags, product routing, URL preflight, error codes, and safety rules. The `dws` binary must be on PATH.
|
||||||
|
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ metadata:
|
|||||||
|
|
||||||
# 钉钉日志 Skill
|
# 钉钉日志 Skill
|
||||||
|
|
||||||
> 🧪 **EXPERIMENTAL · 试验版 / Preview** — multi 模式当前未达 stable 标准。20 个 dingtalk-* skill 全部通过 dispatch verifier,但接口、命名、跨 skill 引用后续可能调整;生产 / 共享环境请优先使用 mono 模式(`dws skill setup --mode mono`)。问题请提 issue 反馈。
|
> 🧪 **EXPERIMENTAL · 试验版 / Preview** — multi 模式当前未达 stable 标准。22 个 dingtalk-* skill 全部通过 dispatch verifier,但接口、命名、跨 skill 引用后续可能调整;生产 / 共享环境请优先使用 mono 模式(`dws skill setup --mode mono`)。问题请提 issue 反馈。
|
||||||
|
|
||||||
> **PREREQUISITE:** Read the `dws-shared` skill first for auth, global flags, product routing, URL preflight, error codes, and safety rules. The `dws` binary must be on PATH.
|
> **PREREQUISITE:** Read the `dws-shared` skill first for auth, global flags, product routing, URL preflight, error codes, and safety rules. The `dws` binary must be on PATH.
|
||||||
|
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ metadata:
|
|||||||
|
|
||||||
# 钉钉电子表格 Skill
|
# 钉钉电子表格 Skill
|
||||||
|
|
||||||
> 🧪 **EXPERIMENTAL · 试验版 / Preview** — multi 模式当前未达 stable 标准。20 个 dingtalk-* skill 全部通过 dispatch verifier,但接口、命名、跨 skill 引用后续可能调整;生产 / 共享环境请优先使用 mono 模式(`dws skill setup --mode mono`)。问题请提 issue 反馈。
|
> 🧪 **EXPERIMENTAL · 试验版 / Preview** — multi 模式当前未达 stable 标准。22 个 dingtalk-* skill 全部通过 dispatch verifier,但接口、命名、跨 skill 引用后续可能调整;生产 / 共享环境请优先使用 mono 模式(`dws skill setup --mode mono`)。问题请提 issue 反馈。
|
||||||
|
|
||||||
> **PREREQUISITE:** Read the `dws-shared` skill first for auth, global flags, product routing, URL preflight, error codes, and safety rules. The `dws` binary must be on PATH.
|
> **PREREQUISITE:** Read the `dws-shared` skill first for auth, global flags, product routing, URL preflight, error codes, and safety rules. The `dws` binary must be on PATH.
|
||||||
|
|
||||||
|
|||||||
@@ -108,6 +108,8 @@ dws sheet filter-view --help
|
|||||||
| `sheet update-dimension` | 更新行/列属性(显隐/行高/列宽) |
|
| `sheet update-dimension` | 更新行/列属性(显隐/行高/列宽) |
|
||||||
| `sheet move-dimension` | 移动行或列到指定位置 |
|
| `sheet move-dimension` | 移动行或列到指定位置 |
|
||||||
| `sheet add-dimension` | 在末尾追加空行或空列 |
|
| `sheet add-dimension` | 在末尾追加空行或空列 |
|
||||||
|
| `sheet group-dimension` | 创建行/列分组 |
|
||||||
|
| `sheet ungroup-dimension` | 取消行/列分组 |
|
||||||
| `sheet media-upload` | 上传附件到表格 |
|
| `sheet media-upload` | 上传附件到表格 |
|
||||||
| `sheet write-image` | 上传图片并写入单元格 |
|
| `sheet write-image` | 上传图片并写入单元格 |
|
||||||
| `sheet create-float-image` | 创建浮动图片 |
|
| `sheet create-float-image` | 创建浮动图片 |
|
||||||
@@ -156,7 +158,7 @@ dws sheet filter-view --help
|
|||||||
| 读取数据 | [sheet/sheet-read-data.md](./sheet/sheet-read-data.md) | range read / csv-get |
|
| 读取数据 | [sheet/sheet-read-data.md](./sheet/sheet-read-data.md) | range read / csv-get |
|
||||||
| 区域操作 | [sheet/sheet-range-operations.md](./sheet/sheet-range-operations.md) | range clear / sort / fill / copy-to / move-to |
|
| 区域操作 | [sheet/sheet-range-operations.md](./sheet/sheet-range-operations.md) | range clear / sort / fill / copy-to / move-to |
|
||||||
| 批量操作 | [sheet/sheet-batch-operations.md](./sheet/sheet-batch-operations.md) | range batch-clear / batch-update |
|
| 批量操作 | [sheet/sheet-batch-operations.md](./sheet/sheet-batch-operations.md) | range batch-clear / batch-update |
|
||||||
| 行列操作 | [sheet/sheet-dimension-operations.md](./sheet/sheet-dimension-operations.md) | insert / delete / update / move / add-dimension |
|
| 行列操作 | [sheet/sheet-dimension-operations.md](./sheet/sheet-dimension-operations.md) | insert / delete / update / move / add-dimension / group-dimension / ungroup-dimension |
|
||||||
| 样式与合并 | [sheet/sheet-style-format.md](./sheet/sheet-style-format.md) | range set-style / batch-set-style / merge-cells / unmerge-cells |
|
| 样式与合并 | [sheet/sheet-style-format.md](./sheet/sheet-style-format.md) | range set-style / batch-set-style / merge-cells / unmerge-cells |
|
||||||
| 条件格式 | [sheet/sheet-conditional-format.md](./sheet/sheet-conditional-format.md) | cond-format list / create / update / delete |
|
| 条件格式 | [sheet/sheet-conditional-format.md](./sheet/sheet-conditional-format.md) | cond-format list / create / update / delete |
|
||||||
| 浮动图表 | [sheet/sheet-chart.md](./sheet/sheet-chart.md) | chart list / create / update / delete |
|
| 浮动图表 | [sheet/sheet-chart.md](./sheet/sheet-chart.md) | chart list / create / update / delete |
|
||||||
@@ -254,6 +256,11 @@ dws sheet filter-view --help
|
|||||||
- 注意与 `append`(追加数据行)区分:`add-dimension` 追加的是空行/空列,`append` 追加的是带数据的行
|
- 注意与 `append`(追加数据行)区分:`add-dimension` 追加的是空行/空列,`append` 追加的是带数据的行
|
||||||
- 请勿用 `range update` 写空数据来模拟追加,`add-dimension` 直接扩展表格维度
|
- 请勿用 `range update` 写空数据来模拟追加,`add-dimension` 直接扩展表格维度
|
||||||
|
|
||||||
|
用户说"创建行分组/创建列分组/折叠几行/折叠几列/取消行列分组":
|
||||||
|
- 创建分组 → `group-dimension --range <整行或整列范围>`
|
||||||
|
- 取消分组 → `ungroup-dimension --range <整行或整列范围>`
|
||||||
|
- `--range` 只能传整行或整列范围,如 `3:7`、`C:F`、`Sheet1!3:7`,不要传 `A1:C5`
|
||||||
|
|
||||||
### 单元格格式
|
### 单元格格式
|
||||||
|
|
||||||
用户说"设置样式/改颜色/设背景色/加粗/居中/换行/字体颜色/字号":
|
用户说"设置样式/改颜色/设背景色/加粗/居中/换行/字体颜色/字号":
|
||||||
@@ -1099,6 +1106,38 @@ Flags:
|
|||||||
|
|
||||||
在工作表末尾追加指定数量的空行或空列。
|
在工作表末尾追加指定数量的空行或空列。
|
||||||
|
|
||||||
|
### 创建行或列分组
|
||||||
|
```
|
||||||
|
Usage:
|
||||||
|
dws sheet group-dimension [flags]
|
||||||
|
Example:
|
||||||
|
dws sheet group-dimension --node <NODE_ID> --sheet-id <SHEET_ID> --range "3:7"
|
||||||
|
dws sheet group-dimension --node <NODE_ID> --sheet-id <SHEET_ID> --range "C:F" --group-state fold
|
||||||
|
dws sheet group-dimension --node <NODE_ID> --sheet-id <SHEET_ID> --range "Sheet1!3:7"
|
||||||
|
Flags:
|
||||||
|
--node string 表格文档 ID 或 URL (必填)
|
||||||
|
--sheet-id string 工作表 ID 或名称 (必填)
|
||||||
|
--range string 整行/整列范围,如 "3:7" 或 "C:F" (必填)
|
||||||
|
--group-state string 创建后的分组状态: expand 或 fold,默认 expand
|
||||||
|
```
|
||||||
|
|
||||||
|
在工作表中为连续行或连续列创建分组。`--range` 必须是整行或整列范围,不支持普通单元格矩形范围。
|
||||||
|
|
||||||
|
### 取消行或列分组
|
||||||
|
```
|
||||||
|
Usage:
|
||||||
|
dws sheet ungroup-dimension [flags]
|
||||||
|
Example:
|
||||||
|
dws sheet ungroup-dimension --node <NODE_ID> --sheet-id <SHEET_ID> --range "3:7"
|
||||||
|
dws sheet ungroup-dimension --node <NODE_ID> --sheet-id <SHEET_ID> --range "C:F"
|
||||||
|
Flags:
|
||||||
|
--node string 表格文档 ID 或 URL (必填)
|
||||||
|
--sheet-id string 工作表 ID 或名称 (必填)
|
||||||
|
--range string 整行/整列范围,如 "3:7" 或 "C:F" (必填)
|
||||||
|
```
|
||||||
|
|
||||||
|
取消指定连续行或连续列的分组。
|
||||||
|
|
||||||
### 取消合并单元格
|
### 取消合并单元格
|
||||||
```
|
```
|
||||||
Usage:
|
Usage:
|
||||||
@@ -1903,6 +1942,8 @@ dws sheet export --node <NODE_ID> --output ./
|
|||||||
| `replace` | `replaceCount` 被替换的单元格数量 | 确认替换结果 |
|
| `replace` | `replaceCount` 被替换的单元格数量 | 确认替换结果 |
|
||||||
| `move-dimension` | `sheetId` 工作表 ID | 确认操作完成 |
|
| `move-dimension` | `sheetId` 工作表 ID | 确认操作完成 |
|
||||||
| `add-dimension` | `sheetId` 工作表 ID | 确认操作完成 |
|
| `add-dimension` | `sheetId` 工作表 ID | 确认操作完成 |
|
||||||
|
| `group-dimension` | `range` / `level` / `groupState` | 确认行列分组层级和展开状态 |
|
||||||
|
| `ungroup-dimension` | `range` / `level` | 确认行列分组已取消 |
|
||||||
| `unmerge-cells` | `sheetId` 工作表 ID | 确认操作完成 |
|
| `unmerge-cells` | `sheetId` 工作表 ID | 确认操作完成 |
|
||||||
| `set-dropdown` | `range` 实际设置范围、`optionCount` 选项数量、`enableMultiSelect` 是否多选 | 确认下拉列表设置成功 |
|
| `set-dropdown` | `range` 实际设置范围、`optionCount` 选项数量、`enableMultiSelect` 是否多选 | 确认下拉列表设置成功 |
|
||||||
| `get-dropdown` | `hasDropdown` 是否存在下拉、`dataValidations` 下拉配置列表(含 `conditionValues`、`ranges`、`options`) | 查看已有下拉配置 |
|
| `get-dropdown` | `hasDropdown` 是否存在下拉、`dataValidations` 下拉配置列表(含 `conditionValues`、`ranges`、`options`) | 查看已有下拉配置 |
|
||||||
@@ -2032,6 +2073,9 @@ dws sheet export --node <NODE_ID> --output ./
|
|||||||
- `add-dimension` vs `range update`:需要在末尾追加空行/空列时,必须使用 `add-dimension` 命令,禁止用 `range update` 写空数据来模拟追加效果
|
- `add-dimension` vs `range update`:需要在末尾追加空行/空列时,必须使用 `add-dimension` 命令,禁止用 `range update` 写空数据来模拟追加效果
|
||||||
- `add-dimension` 追加的是空行/空列,与 `append`(追加带数据的行)不同
|
- `add-dimension` 追加的是空行/空列,与 `append`(追加带数据的行)不同
|
||||||
- `add-dimension` 的 `--length` 必须为正整数(>= 1),行列均不超过 5000
|
- `add-dimension` 的 `--length` 必须为正整数(>= 1),行列均不超过 5000
|
||||||
|
- `group-dimension` / `ungroup-dimension` 的 `--range` 只接受整行或整列范围,如 `3:7`、`C:F`、`Sheet1!3:7`,不支持 `A1:C5`
|
||||||
|
- `group-dimension` 的 `--group-state` 只接受 `expand` 或 `fold`,默认 `expand`
|
||||||
|
- `batch-update` 支持 `group-dimension` / `ungroup-dimension`;需要创建后立即折叠时,优先使用独立 `group-dimension --group-state fold`
|
||||||
- `unmerge-cells` 取消指定范围内所有合并单元格,使用 A1 表示法指定范围
|
- `unmerge-cells` 取消指定范围内所有合并单元格,使用 A1 表示法指定范围
|
||||||
- `set-dropdown` 在指定范围内设置下拉列表,`--options` 为 JSON 数组,每个元素包含 `value`(必填)和 `color`(可选,`#RRGGBB` 格式)。选项值不能包含英文逗号。`--multi-select` 启用多选模式。如果目标范围已存在下拉列表,会被新配置覆盖
|
- `set-dropdown` 在指定范围内设置下拉列表,`--options` 为 JSON 数组,每个元素包含 `value`(必填)和 `color`(可选,`#RRGGBB` 格式)。选项值不能包含英文逗号。`--multi-select` 启用多选模式。如果目标范围已存在下拉列表,会被新配置覆盖
|
||||||
- `get-dropdown` 查询指定范围内的下拉列表配置,返回 `dataValidations` 数组,相同选项的单元格聚合为一组。无下拉列表时 `hasDropdown` 为 false
|
- `get-dropdown` 查询指定范围内的下拉列表配置,返回 `dataValidations` 数组,相同选项的单元格聚合为一组。无下拉列表时 `hasDropdown` 为 false
|
||||||
|
|||||||
@@ -26,6 +26,7 @@
|
|||||||
- 需要对**多个**不同区域执行 `merge-cells` / `unmerge-cells` 时(如按分组合并多列相同内容)
|
- 需要对**多个**不同区域执行 `merge-cells` / `unmerge-cells` 时(如按分组合并多列相同内容)
|
||||||
- 需要对**多个**不同区域执行 `update-dimension` 时(如统一调整多列列宽或多行行高,含 hidden + pixelSize)
|
- 需要对**多个**不同区域执行 `update-dimension` 时(如统一调整多列列宽或多行行高,含 hidden + pixelSize)
|
||||||
- 需要先插入行列再写入数据时(`add-dimension` + `range update` / `csv-put`)
|
- 需要先插入行列再写入数据时(`add-dimension` + `range update` / `csv-put`)
|
||||||
|
- 需要批量创建或取消行列分组时(`group-dimension` / `ungroup-dimension`)
|
||||||
- 需要对多个区域执行不同写入操作时(多次 `range update` + `range clear` 等组合)
|
- 需要对多个区域执行不同写入操作时(多次 `range update` + `range clear` 等组合)
|
||||||
|
|
||||||
当同一工具需要对多个区域重复调用时,**推荐**改用 `batch-update` 合并为单次请求——`batch-update` 是原子提交(要么全成功要么整批回滚);逐个调用非原子,中途失败会留下半成品。
|
当同一工具需要对多个区域重复调用时,**推荐**改用 `batch-update` 合并为单次请求——`batch-update` 是原子提交(要么全成功要么整批回滚);逐个调用非原子,中途失败会留下半成品。
|
||||||
@@ -80,6 +81,7 @@ Flags:
|
|||||||
toolName 用 CLI 命令名,input 的键用 CLI flag 名去掉 --:
|
toolName 用 CLI 命令名,input 的键用 CLI flag 名去掉 --:
|
||||||
range clear / range update / merge-cells / unmerge-cells / update-dimension
|
range clear / range update / merge-cells / unmerge-cells / update-dimension
|
||||||
range fill / range copy-to / add-dimension / delete-dimension / move-dimension
|
range fill / range copy-to / add-dimension / delete-dimension / move-dimension
|
||||||
|
group-dimension / ungroup-dimension
|
||||||
set-dropdown / delete-dropdown / csv-put / delete-float-image
|
set-dropdown / delete-dropdown / csv-put / delete-float-image
|
||||||
Notes:
|
Notes:
|
||||||
- 默认严格事务模式:任一子操作失败 → 整批回滚到初始状态
|
- 默认严格事务模式:任一子操作失败 → 整批回滚到初始状态
|
||||||
@@ -177,6 +179,8 @@ Cause: The requested resource was not found by the identifier 'NonExistentSheet'
|
|||||||
| `add-dimension` | [sheet-dimension-operations](./sheet-dimension-operations.md) |
|
| `add-dimension` | [sheet-dimension-operations](./sheet-dimension-operations.md) |
|
||||||
| `delete-dimension` | [sheet-dimension-operations](./sheet-dimension-operations.md) |
|
| `delete-dimension` | [sheet-dimension-operations](./sheet-dimension-operations.md) |
|
||||||
| `move-dimension` | [sheet-dimension-operations](./sheet-dimension-operations.md) |
|
| `move-dimension` | [sheet-dimension-operations](./sheet-dimension-operations.md) |
|
||||||
|
| `group-dimension` | [sheet-dimension-operations](./sheet-dimension-operations.md) |
|
||||||
|
| `ungroup-dimension` | [sheet-dimension-operations](./sheet-dimension-operations.md) |
|
||||||
| `set-dropdown` | [sheet-dropdown](./sheet-dropdown.md) |
|
| `set-dropdown` | [sheet-dropdown](./sheet-dropdown.md) |
|
||||||
| `delete-dropdown` | [sheet-dropdown](./sheet-dropdown.md) |
|
| `delete-dropdown` | [sheet-dropdown](./sheet-dropdown.md) |
|
||||||
| `csv-put` | [sheet-write-data](./sheet-write-data.md) |
|
| `csv-put` | [sheet-write-data](./sheet-write-data.md) |
|
||||||
@@ -230,6 +234,19 @@ Cause: The requested resource was not found by the identifier 'NonExistentSheet'
|
|||||||
]
|
]
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### 批量创建行列分组
|
||||||
|
|
||||||
|
```jsonc
|
||||||
|
[
|
||||||
|
{"toolName":"group-dimension",
|
||||||
|
"input":{"sheet-id":"...","range":"3:7","group-state":"expand"}},
|
||||||
|
{"toolName":"group-dimension",
|
||||||
|
"input":{"sheet-id":"...","range":"C:F"}},
|
||||||
|
{"toolName":"ungroup-dimension",
|
||||||
|
"input":{"sheet-id":"...","range":"10:12"}}
|
||||||
|
]
|
||||||
|
```
|
||||||
|
|
||||||
## 上下文传递
|
## 上下文传递
|
||||||
|
|
||||||
| 操作 | 从返回中提取 | 用于 |
|
| 操作 | 从返回中提取 | 用于 |
|
||||||
@@ -244,3 +261,5 @@ Cause: The requested resource was not found by the identifier 'NonExistentSheet'
|
|||||||
- ★ **需要组合多个不同写操作(清除+写入等)时,用 `batch-update`**:原子事务,任一操作失败则整批回滚,避免留下半成品
|
- ★ **需要组合多个不同写操作(清除+写入等)时,用 `batch-update`**:原子事务,任一操作失败则整批回滚,避免留下半成品
|
||||||
- ★ **批次完成后必须回读校验**:用 `range read` 或 `csv-get` 抽样回读受影响区域,至少校验 3-5 个代表性单元格(首 / 中 / 末),与预期值对照
|
- ★ **批次完成后必须回读校验**:用 `range read` 或 `csv-get` 抽样回读受影响区域,至少校验 3-5 个代表性单元格(首 / 中 / 末),与预期值对照
|
||||||
- ★ **`batch-update` 不支持嵌套**:`--operations` 中的 `toolName` 必须是原子操作,不可再嵌套 `batch-update`
|
- ★ **`batch-update` 不支持嵌套**:`--operations` 中的 `toolName` 必须是原子操作,不可再嵌套 `batch-update`
|
||||||
|
- `batch-update` 支持 `group-dimension` / `ungroup-dimension`;`input.range` 只接受整行或整列范围,如 `3:7`、`C:F`
|
||||||
|
- 需要创建后立即折叠分组时,优先使用独立 `dws sheet group-dimension --group-state fold`
|
||||||
@@ -26,6 +26,12 @@
|
|||||||
- 注意与 `append`(追加数据行)区分:`add-dimension` 追加的是空行/空列,`append` 追加的是带数据的行
|
- 注意与 `append`(追加数据行)区分:`add-dimension` 追加的是空行/空列,`append` 追加的是带数据的行
|
||||||
- 请勿用 `range update` 写空数据来模拟追加,`add-dimension` 直接扩展表格维度
|
- 请勿用 `range update` 写空数据来模拟追加,`add-dimension` 直接扩展表格维度
|
||||||
|
|
||||||
|
用户说"创建行分组/创建列分组/折叠几行/折叠几列/展开行列分组/取消行列分组":
|
||||||
|
- 创建分组 → `group-dimension --range <整行或整列范围>`
|
||||||
|
- 创建并折叠 → `group-dimension --group-state fold`
|
||||||
|
- 取消分组 → `ungroup-dimension --range <整行或整列范围>`
|
||||||
|
- `--range` 只能传整行或整列范围,如 `3:7`、`C:F`、`Sheet1!3:7`
|
||||||
|
|
||||||
**结构预检**:插入、删除、移动行列前,必须先执行 `dws sheet info --node <NODE_ID> --sheet-id <SHEET_ID> --format json` 查看 `mergedRanges`。合并区域跨过操作位置时,行列变更可能导致表头/分组标题断裂、空白或错位;需要先向用户说明影响,必要时取消合并后操作,再按原模式重新 `merge-cells`。
|
**结构预检**:插入、删除、移动行列前,必须先执行 `dws sheet info --node <NODE_ID> --sheet-id <SHEET_ID> --format json` 查看 `mergedRanges`。合并区域跨过操作位置时,行列变更可能导致表头/分组标题断裂、空白或错位;需要先向用户说明影响,必要时取消合并后操作,再按原模式重新 `merge-cells`。
|
||||||
|
|
||||||
## 命令详细参考
|
## 命令详细参考
|
||||||
@@ -172,6 +178,38 @@ Flags:
|
|||||||
|
|
||||||
在工作表末尾追加指定数量的空行或空列。
|
在工作表末尾追加指定数量的空行或空列。
|
||||||
|
|
||||||
|
### 创建行或列分组
|
||||||
|
```
|
||||||
|
Usage:
|
||||||
|
dws sheet group-dimension [flags]
|
||||||
|
Example:
|
||||||
|
dws sheet group-dimension --node <NODE_ID> --sheet-id <SHEET_ID> --range "3:7"
|
||||||
|
dws sheet group-dimension --node <NODE_ID> --sheet-id <SHEET_ID> --range "C:F" --group-state fold
|
||||||
|
dws sheet group-dimension --node <NODE_ID> --sheet-id <SHEET_ID> --range "Sheet1!3:7"
|
||||||
|
Flags:
|
||||||
|
--node string 表格文档 ID 或 URL (必填)
|
||||||
|
--sheet-id string 工作表 ID 或名称 (必填)
|
||||||
|
--range string 整行/整列范围,如 "3:7" 或 "C:F" (必填)
|
||||||
|
--group-state string 创建后的分组状态: expand 或 fold,默认 expand
|
||||||
|
```
|
||||||
|
|
||||||
|
为连续行或连续列创建分组。`--range` 必须是整行或整列范围,不支持 `A1:C5` 这类普通单元格矩形范围。
|
||||||
|
|
||||||
|
### 取消行或列分组
|
||||||
|
```
|
||||||
|
Usage:
|
||||||
|
dws sheet ungroup-dimension [flags]
|
||||||
|
Example:
|
||||||
|
dws sheet ungroup-dimension --node <NODE_ID> --sheet-id <SHEET_ID> --range "3:7"
|
||||||
|
dws sheet ungroup-dimension --node <NODE_ID> --sheet-id <SHEET_ID> --range "C:F"
|
||||||
|
Flags:
|
||||||
|
--node string 表格文档 ID 或 URL (必填)
|
||||||
|
--sheet-id string 工作表 ID 或名称 (必填)
|
||||||
|
--range string 整行/整列范围,如 "3:7" 或 "C:F" (必填)
|
||||||
|
```
|
||||||
|
|
||||||
|
取消指定连续行或连续列的分组。
|
||||||
|
|
||||||
## 核心工作流
|
## 核心工作流
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
@@ -244,6 +282,8 @@ dws sheet update-dimension --node <NODE_ID> --sheet-id <SHEET_ID> \
|
|||||||
| `update-dimension` | `a1Notation` 被更新区域范围、`hidden` 生效的显隐状态、`pixelSize` 生效的尺寸 | 确认更新结果 |
|
| `update-dimension` | `a1Notation` 被更新区域范围、`hidden` 生效的显隐状态、`pixelSize` 生效的尺寸 | 确认更新结果 |
|
||||||
| `move-dimension` | `sheetId` 工作表 ID | 确认操作完成 |
|
| `move-dimension` | `sheetId` 工作表 ID | 确认操作完成 |
|
||||||
| `add-dimension` | `sheetId` 工作表 ID | 确认操作完成 |
|
| `add-dimension` | `sheetId` 工作表 ID | 确认操作完成 |
|
||||||
|
| `group-dimension` | `range` / `level` / `groupState` | 确认行列分组层级和展开状态 |
|
||||||
|
| `ungroup-dimension` | `range` / `level` | 确认行列分组已取消 |
|
||||||
| `list` | 工作表的 `sheetId` | info / range read / range update / find 的 --sheet-id |
|
| `list` | 工作表的 `sheetId` | info / range read / range update / find 的 --sheet-id |
|
||||||
|
|
||||||
## 注意事项
|
## 注意事项
|
||||||
@@ -273,3 +313,7 @@ dws sheet update-dimension --node <NODE_ID> --sheet-id <SHEET_ID> \
|
|||||||
- `add-dimension` vs `range update`:需要在末尾追加空行/空列时,必须使用 `add-dimension` 命令,禁止用 `range update` 写空数据来模拟追加效果
|
- `add-dimension` vs `range update`:需要在末尾追加空行/空列时,必须使用 `add-dimension` 命令,禁止用 `range update` 写空数据来模拟追加效果
|
||||||
- `add-dimension` 追加的是空行/空列,与 `append`(追加带数据的行)不同
|
- `add-dimension` 追加的是空行/空列,与 `append`(追加带数据的行)不同
|
||||||
- `add-dimension` 的 `--length` 必须为正整数(>= 1),行列均不超过 5000
|
- `add-dimension` 的 `--length` 必须为正整数(>= 1),行列均不超过 5000
|
||||||
|
- `group-dimension` / `ungroup-dimension` 的 `--range` 只接受整行或整列范围,如 `3:7`、`C:F`、`Sheet1!3:7`,不支持 `A1:C5`
|
||||||
|
- `group-dimension` 的 `--group-state` 只接受 `expand` 或 `fold`,默认 `expand`
|
||||||
|
- 分组操作返回 `level` 表示当前分组层级;不要把它误认为固定深度参数
|
||||||
|
- `batch-update` 支持 `group-dimension` / `ungroup-dimension`;需要创建后立即折叠时,优先使用独立 `group-dimension --group-state fold`
|
||||||
@@ -12,7 +12,7 @@ metadata:
|
|||||||
|
|
||||||
# 悟空技能管理 Skill
|
# 悟空技能管理 Skill
|
||||||
|
|
||||||
> 🧪 **EXPERIMENTAL · 试验版 / Preview** — multi 模式当前未达 stable 标准。20 个 dingtalk-* skill 全部通过 dispatch verifier,但接口、命名、跨 skill 引用后续可能调整;生产 / 共享环境请优先使用 mono 模式(`dws skill setup --mode mono`)。问题请提 issue 反馈。
|
> 🧪 **EXPERIMENTAL · 试验版 / Preview** — multi 模式当前未达 stable 标准。22 个 dingtalk-* skill 全部通过 dispatch verifier,但接口、命名、跨 skill 引用后续可能调整;生产 / 共享环境请优先使用 mono 模式(`dws skill setup --mode mono`)。问题请提 issue 反馈。
|
||||||
|
|
||||||
> **PREREQUISITE:** Read the `dws-shared` skill first for auth, global flags, product routing, URL preflight, error codes, and safety rules. The `dws` binary must be on PATH.
|
> **PREREQUISITE:** Read the `dws-shared` skill first for auth, global flags, product routing, URL preflight, error codes, and safety rules. The `dws` binary must be on PATH.
|
||||||
|
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ metadata:
|
|||||||
|
|
||||||
# 钉钉待办 Skill
|
# 钉钉待办 Skill
|
||||||
|
|
||||||
> 🧪 **EXPERIMENTAL · 试验版 / Preview** — multi 模式当前未达 stable 标准。20 个 dingtalk-* skill 全部通过 dispatch verifier,但接口、命名、跨 skill 引用后续可能调整;生产 / 共享环境请优先使用 mono 模式(`dws skill setup --mode mono`)。问题请提 issue 反馈。
|
> 🧪 **EXPERIMENTAL · 试验版 / Preview** — multi 模式当前未达 stable 标准。22 个 dingtalk-* skill 全部通过 dispatch verifier,但接口、命名、跨 skill 引用后续可能调整;生产 / 共享环境请优先使用 mono 模式(`dws skill setup --mode mono`)。问题请提 issue 反馈。
|
||||||
|
|
||||||
> **PREREQUISITE:** Read the `dws-shared` skill first for auth, global flags, product routing, URL preflight, error codes, and safety rules. The `dws` binary must be on PATH.
|
> **PREREQUISITE:** Read the `dws-shared` skill first for auth, global flags, product routing, URL preflight, error codes, and safety rules. The `dws` binary must be on PATH.
|
||||||
|
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ metadata:
|
|||||||
|
|
||||||
# 钉钉知识库 Skill
|
# 钉钉知识库 Skill
|
||||||
|
|
||||||
> 🧪 **EXPERIMENTAL · 试验版 / Preview** — multi 模式当前未达 stable 标准。20 个 dingtalk-* skill 全部通过 dispatch verifier,但接口、命名、跨 skill 引用后续可能调整;生产 / 共享环境请优先使用 mono 模式(`dws skill setup --mode mono`)。问题请提 issue 反馈。
|
> 🧪 **EXPERIMENTAL · 试验版 / Preview** — multi 模式当前未达 stable 标准。22 个 dingtalk-* skill 全部通过 dispatch verifier,但接口、命名、跨 skill 引用后续可能调整;生产 / 共享环境请优先使用 mono 模式(`dws skill setup --mode mono`)。问题请提 issue 反馈。
|
||||||
|
|
||||||
> **PREREQUISITE:** Read the `dws-shared` skill first for auth, global flags, product routing, URL preflight, error codes, and safety rules. The `dws` binary must be on PATH.
|
> **PREREQUISITE:** Read the `dws-shared` skill first for auth, global flags, product routing, URL preflight, error codes, and safety rules. The `dws` binary must be on PATH.
|
||||||
|
|
||||||
|
|||||||
Reference in new issue
Block a user