{"id":"a1109f6c-2b0e-4cae-888b-8dc696eca3e9","entityType":"agent","slug":"clawhub-shyzhen-dingtalk-doc","name":"dingtalk-doc","canonicalUrl":"https://www.xpersona.co/agent/clawhub-shyzhen-dingtalk-doc","canonicalPath":"/agent/clawhub-shyzhen-dingtalk-doc","generatedAt":"2026-10-11T03:55:07.934Z","source":"CLAWHUB","claimStatus":"UNCLAIMED","verificationTier":"NONE","summary":{"evidence":{"source":"editorial-content","verified":true,"confidence":"high","updatedAt":"2026-10-11T00:08:36.920Z","emptyReason":null},"description":"钉钉文档管理技能。当用户发送的消息中包含 alidocs.dingtalk.com 链接、要求总结/读取/查看/更新钉钉文档或钉钉知识库文档，或当前上下文已明确对象是钉钉文档时使用。关键词：钉钉文档、钉钉知识库、alidocs、总结、读取、查看、更新、修改、文档、链接。 Skill: dingtalk-doc Owner: shyzhen Summary: 钉钉文档管理技能。当用户发送的消息中包含 alidocs.dingtalk.com 链接、要求总结/读取/查看/更新钉钉文档或钉钉知识库文档，或当前上下文已明确对象是钉钉文档时使用。关键词：钉钉文档、钉钉知识库、alidocs、总结、读取、查看、更新、修改、文档、链接。 Tags: latest:1.0.8 Version history: v1.0.8 | 2026-06-11T03:04:40.846Z | user dingtalk-doc v1.0.8 - 移除 skill-card.md 文件，精简技能描述内容。 - 无其他功能或配置变更。 v1.0.7 | 2026-06-03T09:52:16.313Z | user - Remove unnecessary file: skill-card.md to simplify the","descriptionLabel":"Technical summary","evidenceSummary":"Capability contract not published. No trust telemetry is available yet. 1.2K downloads reported by the source. Last updated 10/11/2026.","installCommand":"clawhub skill install s176r4qctym5nvv0a83rs6nkex843rkg:dingtalk-doc","sourceUrl":"https://clawhub.ai/shyzhen/dingtalk-doc","homepage":"https://clawhub.ai/shyzhen/skills/dingtalk-doc","primaryLinks":[{"label":"View on ClawHub","url":"https://clawhub.ai/shyzhen/dingtalk-doc","kind":"source"},{"label":"Homepage","url":"https://clawhub.ai/shyzhen/skills/dingtalk-doc","kind":"homepage"}],"safetyScore":84,"overallRank":62,"popularityScore":62,"trustScore":null,"claimedByName":null,"isOwner":false,"seoDescription":"钉钉文档管理技能。当用户发送的消息中包含 alidocs.dingtalk.com 链接、要求总结/读取/查看/更新钉钉文档或钉钉知识库文档，或当前上下文已明确对象是钉钉文档时使用。关键词：钉钉文档、钉钉知识库、alidocs、总结、读取、查看、更新、修改、文档、链接。 Skill: dingtalk-doc Owne"},"coverage":{"evidence":{"source":"public-profile","verified":false,"confidence":"medium","updatedAt":"2026-10-11T00:08:36.920Z","emptyReason":null},"protocols":[{"protocol":"OPENCLEW","label":"OpenClaw","status":"self-declared","notes":"Declared in the public agent profile."}],"capabilities":[],"verifiedCount":0,"selfDeclaredCount":1,"capabilityMatrix":{"rows":[{"key":"OPENCLEW","type":"protocol","support":"unknown","confidenceSource":"profile","notes":"Listed on profile"}],"flattenedTokens":"protocol:OPENCLEW|unknown|profile"}},"adoption":{"evidence":{"source":"CLAWHUB","verified":false,"confidence":"medium","updatedAt":"2026-10-11T00:08:36.920Z","emptyReason":null},"stars":null,"forks":null,"downloads":1223,"packageName":null,"latestVersion":"1.0.8","tractionLabel":"1.2K downloads"},"release":{"evidence":{"source":"CLAWHUB","verified":false,"confidence":"medium","updatedAt":"2026-10-11T00:08:36.907Z","emptyReason":null},"lastUpdatedAt":"2026-10-11T00:08:36.920Z","lastCrawledAt":"2026-10-11T00:08:36.907Z","lastIndexedAt":null,"nextCrawlAt":"2026-10-12T00:08:36.907Z","lastVerifiedAt":null,"highlights":[{"version":"1.0.8","createdAt":"2026-06-11T03:04:40.846Z","changelog":"dingtalk-doc v1.0.8 - 移除 skill-card.md 文件，精简技能描述内容。 - 无其他功能或配置变更。","fileCount":9,"zipByteSize":26884},{"version":"1.0.7","createdAt":"2026-06-03T09:52:16.313Z","changelog":"- Remove unnecessary file: skill-card.md to simplify the repository. - Clean up internal metadata section in SKILL.md (metadata block removed). - No user-facing feature or behavior changes; documentation and config remain consistent.","fileCount":9,"zipByteSize":26942},{"version":"1.0.6","createdAt":"2026-04-23T01:49:46.440Z","changelog":"No user-visible changes in this version. - Version 1.0.6 was released with no detected file changes. - No updates or modifications to existing features, documentation, or configuration.","fileCount":9,"zipByteSize":26974},{"version":"1.0.5","createdAt":"2026-04-23T01:48:59.400Z","changelog":"Version 1.0.5 - No file changes detected in this release. - Behavior, usage, and documentation remain unchanged. - 更新readme.md说明","fileCount":8,"zipByteSize":25746},{"version":"1.0.4","createdAt":"2026-04-22T04:10:02.905Z","changelog":"No code changes detected, but usage environment and debug info were clarified: - Environment variable section now explicitly documents optional `OPENCLAW_SENDER_ID` / `DINGTALK_SENDER_ID` and refines `DINGTALK_DEBUG` description. - Stated that `DINGTALK_DEBUG` logs do not include document contents or request bodies, improving privacy transparency. - No impact on behavior, functionality, or APIs. This update clarifies documentation only.","fileCount":8,"zipByteSize":25811},{"version":"1.0.3","createdAt":"2026-04-21T09:03:03.600Z","changelog":"dingtalk-doc 1.0.3 - 修正 skill 路径名为 dingtalk-doc，所有命令和路径示例同步调整（原为 dingtalk-knowledge） - 块级操作部分补充说明：标明 `append-text` 已废弃，实际应优先用 `insert-block` 或 `modify-block` - 相关命令行参数范例及说明同步为 dingtalk-doc 路径 - 更精准列举块级操作有限制及推荐替代方案 - 其他内容结构未变，日常用法无破坏性更新","fileCount":8,"zipByteSize":25029},{"version":"1.0.2","createdAt":"2026-04-21T07:53:32.925Z","changelog":"Version 1.0.2 of dingtalk-doc - 优化白名单配置的说明文件，明确指出这里只支持配置 【钉钉知识库】下的一级目录id","fileCount":8,"zipByteSize":24964},{"version":"1.0.1","createdAt":"2026-04-21T07:42:04.949Z","changelog":"- 优化readme","fileCount":8,"zipByteSize":24949}]},"execution":{"evidence":{"source":"CLAWHUB","verified":false,"confidence":"low","updatedAt":null,"emptyReason":"No published capability contract is available yet."},"installCommand":"clawhub skill install s176r4qctym5nvv0a83rs6nkex843rkg:dingtalk-doc","setupComplexity":"low","setupSteps":["Setup complexity is classified as HIGH. You must provision dedicated cloud infrastructure or an isolated VM. Do not run this directly on your local workstation.","Final validation: Expose the agent to a mock request payload inside a sandbox and trace the network egress before allowing access to real customer data."],"contract":{"contractStatus":"missing","authModes":[],"requires":[],"forbidden":[],"supportsMcp":false,"supportsA2a":false,"supportsStreaming":false,"inputSchemaRef":null,"outputSchemaRef":null,"dataRegion":null,"contractUpdatedAt":null,"sourceUpdatedAt":null,"freshnessSeconds":null},"invocationGuide":{"preferredApi":{"snapshotUrl":"https://www.xpersona.co/api/v1/agents/clawhub-shyzhen-dingtalk-doc/snapshot","contractUrl":"https://www.xpersona.co/api/v1/agents/clawhub-shyzhen-dingtalk-doc/contract","trustUrl":"https://www.xpersona.co/api/v1/agents/clawhub-shyzhen-dingtalk-doc/trust"},"curlExamples":["curl -s \"https://www.xpersona.co/api/v1/agents/clawhub-shyzhen-dingtalk-doc/snapshot\"","curl -s \"https://www.xpersona.co/api/v1/agents/clawhub-shyzhen-dingtalk-doc/contract\"","curl -s \"https://www.xpersona.co/api/v1/agents/clawhub-shyzhen-dingtalk-doc/trust\""],"jsonRequestTemplate":{"query":"summarize this repo","constraints":{"maxLatencyMs":2000,"protocolPreference":["OPENCLEW"]}},"jsonResponseTemplate":{"ok":true,"result":{"summary":"...","confidence":0.9},"meta":{"source":"CLAWHUB","generatedAt":"2026-10-11T03:55:07.931Z"}},"retryPolicy":{"maxAttempts":3,"backoffMs":[500,1500,3500],"retryableConditions":["HTTP_429","HTTP_503","NETWORK_TIMEOUT"]}},"endpoints":{"dossierUrl":"https://www.xpersona.co/api/v1/agents/clawhub-shyzhen-dingtalk-doc/dossier","snapshotUrl":"https://www.xpersona.co/api/v1/agents/clawhub-shyzhen-dingtalk-doc/snapshot","contractUrl":"https://www.xpersona.co/api/v1/agents/clawhub-shyzhen-dingtalk-doc/contract","trustUrl":"https://www.xpersona.co/api/v1/agents/clawhub-shyzhen-dingtalk-doc/trust"}},"reliability":{"evidence":{"source":"runtime-metrics","verified":false,"confidence":"low","updatedAt":null,"emptyReason":"No trust, reliability, or runtime telemetry is available."},"trust":{"status":"unavailable","handshakeStatus":"UNKNOWN","verificationFreshnessHours":null,"reputationScore":null,"p95LatencyMs":null,"successRate30d":null,"fallbackRate":null,"attempts30d":null,"trustUpdatedAt":null,"trustConfidence":"unknown","sourceUpdatedAt":null,"freshnessSeconds":null},"decisionGuardrails":{"doNotUseIf":["Contract metadata is missing or unavailable for deterministic execution."],"safeUseWhen":[],"riskFlags":["missing_or_unavailable_contract","trust_data_unavailable","schema_references_missing"],"operationalConfidence":"low"},"executionMetrics":{"observedLatencyMsP50":null,"observedLatencyMsP95":null,"estimatedCostUsd":null,"uptime30d":null,"rateLimitRpm":null,"rateLimitBurst":null,"lastVerifiedAt":null,"verificationSource":null},"runtimeMetrics":{"successRate":null,"avgLatencyMs":null,"avgCostUsd":null,"hallucinationRate":null,"retryRate":null,"disputeRate":null,"p50Latency":null,"p95Latency":null,"lastUpdated":null}},"benchmarks":{"evidence":{"source":"no-benchmark-data","verified":false,"confidence":"low","updatedAt":null,"emptyReason":"No benchmark suites or observed failure patterns are available."},"suites":[],"failurePatterns":[]},"artifacts":{"evidence":{"source":"CLAWHUB","verified":false,"confidence":"high","updatedAt":"2026-10-11T00:08:36.920Z","emptyReason":null},"readme":"Skill: dingtalk-doc\n\nOwner: shyzhen\n\nSummary: 钉钉文档管理技能。当用户发送的消息中包含 alidocs.dingtalk.com 链接、要求总结/读取/查看/更新钉钉文档或钉钉知识库文档，或当前上下文已明确对象是钉钉文档时使用。关键词：钉钉文档、钉钉知识库、alidocs、总结、读取、查看、更新、修改、文档、链接。\n\nTags: latest:1.0.8\n\nVersion history:\n\nv1.0.8 | 2026-06-11T03:04:40.846Z | user\n\ndingtalk-doc v1.0.8\n\n- 移除 skill-card.md 文件，精简技能描述内容。\n- 无其他功能或配置变更。\n\nv1.0.7 | 2026-06-03T09:52:16.313Z | user\n\n- Remove unnecessary file: skill-card.md to simplify the repository.\n- Clean up internal metadata section in SKILL.md (metadata block removed).\n- No user-facing feature or behavior changes; documentation and config remain consistent.\n\nv1.0.6 | 2026-04-23T01:49:46.440Z | user\n\nNo user-visible changes in this version.\n\n- Version 1.0.6 was released with no detected file changes.\n- No updates or modifications to existing features, documentation, or configuration.\n\nv1.0.5 | 2026-04-23T01:48:59.400Z | user\n\nVersion 1.0.5\n\n- No file changes detected in this release.\n- Behavior, usage, and documentation remain unchanged.\n- 更新readme.md说明\n\nv1.0.4 | 2026-04-22T04:10:02.905Z | user\n\nNo code changes detected, but usage environment and debug info were clarified:\n\n- Environment variable section now explicitly documents optional `OPENCLAW_SENDER_ID` / `DINGTALK_SENDER_ID` and refines `DINGTALK_DEBUG` description.\n- Stated that `DINGTALK_DEBUG` logs do not include document contents or request bodies, improving privacy transparency.\n- No impact on behavior, functionality, or APIs. This update clarifies documentation only.\n\nv1.0.3 | 2026-04-21T09:03:03.600Z | user\n\ndingtalk-doc 1.0.3\n\n- 修正 skill 路径名为 dingtalk-doc，所有命令和路径示例同步调整（原为 dingtalk-knowledge）\n- 块级操作部分补充说明：标明 `append-text` 已废弃，实际应优先用 `insert-block` 或 `modify-block`\n- 相关命令行参数范例及说明同步为 dingtalk-doc 路径\n- 更精准列举块级操作有限制及推荐替代方案\n- 其他内容结构未变，日常用法无破坏性更新\n\nv1.0.2 | 2026-04-21T07:53:32.925Z | user\n\nVersion 1.0.2 of dingtalk-doc\n\n- 优化白名单配置的说明文件，明确指出这里只支持配置 【钉钉知识库】下的一级目录id\n\nv1.0.1 | 2026-04-21T07:42:04.949Z | user\n\n- 优化readme\n\nv1.0.0 | 2026-04-21T07:15:28.897Z | user\n\n钉钉文档 1.0.0 — 首次发布\n\n- 引入一项通过钉钉开放平台 API 管理钉钉文档和知识库的功能。\n\n- 基于关键词和上下文自动触发，以读取、摘要、查看或更新钉钉文档。\n\n- 支持读取（列表、搜索、获取内容）和写入（更新、追加、删除）操作，并对写入操作执行严格的白名单控制(白名单只支持‘钉钉知识库’下的路径)。\n\n- 个人文档、团队文档、钉钉知识库内文档的读取操作不受白名单控制，写入操作才会校验白名单(知识库下的一级目录)\n\n- 包含详细的环境变量要求及跨平台命令示例（Windows/macOS/Linux）。\n\n- 提供明确的钉钉文档 URL 解析规则及与 API 操作的映射说明。\n\nArchive index:\n\nArchive v1.0.8: 9 files, 26884 bytes\n\nFiles: config/whitelist.json (278b), package.json (837b), README.md (10156b), scripts/dingtalk-client.js (20641b), scripts/index.js (30897b), scripts/whitelist.js (5086b), skill-card.md (2355b), SKILL.md (17851b), _meta.json (131b)\n\nFile v1.0.8:SKILL.md\n\n---\r\nname: dingtalk-doc\r\ndescription: 钉钉文档管理技能。当用户发送的消息中包含 alidocs.dingtalk.com 链接、要求总结/读取/查看/更新钉钉文档或钉钉知识库文档，或当前上下文已明确对象是钉钉文档时使用。关键词：钉钉文档、钉钉知识库、alidocs、总结、读取、查看、更新、修改、文档、链接。\r\n---\r\n\r\n# 钉钉文档管理技能\r\n\r\n通过钉钉开放平台 API 管理钉钉文档与钉钉知识库内文档。`SKILL.md` 只保留 agent 执行所需规则;配置细节、示例、API 背景见 `README.md`。\r\n\r\n## 何时使用\r\n\r\n### 触发关键词\r\n\r\n只有在**已经确认对象是钉钉文档**时，同时消息包含以下**任一关键词**时，优先使用本 skill：\r\n\r\n| 类别 | 关键词 |\r\n|------|--------|\r\n| **平台名** | 钉钉文档、钉钉知识库、alidocs |\r\n| **读取类** | 总结、读取、查看、浏览、列出结构 |\r\n| **修改类** | 更新、修改、追加、删除、覆写 |\r\n| **对象** | 文档、链接、这篇、这个文档 |\r\n\r\n**组合示例：**\r\n- \"总结一下这篇钉钉文档\"\r\n- \"读取这个 alidocs 链接\"\r\n- \"更新文档内容\"\r\n- \"删除第三段\"\r\n\r\n### 触发场景（优先级从高到低）\r\n\r\n| 场景 | 示例 | 动作 |\r\n|------|------|------|\r\n| **钉钉文档链接** | `alidocs.dingtalk.com/i/nodes/xxx` | 根据意图选择：元数据→`get-doc`，正文内容→`get-content` |\r\n| **钉钉上下文 + 链接** | \"总结 https://alidocs.dingtalk.com/...\" | 调用 `get-content` 读取内容后总结 |\r\n| **明确命令** | \"总结这篇文档\"、\"读取这个 alidocs 链接\" | 根据意图选择命令 |\r\n| **已知上下文是钉钉文档** | 前文已给出 alidocs 链接，后续说\"更新文档\"、\"删除某段\"、\"在第三段后追加\" | 调用对应命令 |\r\n| **结构查询** | \"列出结构\"、\"这个 alidocs 有哪些章节\" | 调用 `get-content` |\r\n| **块级操作** | \"删除第三段\"、\"修改这个段落\"、\"在这里插入一段\" | 先用 `get-content` 获取 blockId 和位置，再调用 `delete-block`/`modify-block`/`insert-block` |\r\n\r\n### 不触发的场景\r\n- 查询本地文件、离线文档或普通文本内容，且不需要调用钉钉 API\r\n- 没有文档链接、docKey、或明确钉钉文档上下文，却要求修改文档\r\n- 用户只说\"总结文档\"\"更新这个链接\"等泛化请求，但上下文无法确认对象是钉钉文档\r\n- 与钉钉无关的文档系统，例如本地 Markdown、飞书文档、语雀、Google Docs\r\n\r\n## 运行前提\r\n\r\n### 环境变量\r\n\r\n必须配置以下环境变量 (在 Gateway 环境中):\r\n\r\n- `DINGTALK_CLIENTID` - 钉钉应用 Client ID (AppKey)\r\n- `DINGTALK_CLIENTSECRET` - 钉钉应用 Client Secret (AppSecret)\r\n\r\n可选环境变量:\r\n\r\n- `OPENCLAW_SENDER_ID` / `DINGTALK_SENDER_ID` - 由 OpenClaw / 钉钉连接器注入的当前用户 sender_id；也可以通过命令行 `--senderId=` 显式传入\r\n- `DINGTALK_DEBUG` - 设置为 `true` 启用调试模式；仅输出方法、路径（查询参数已脱敏）、状态码、requestId 等，不打印文档正文与完整请求体\r\n\r\n### operatorId 获取方式\r\n\r\n不需要在配置中指定!系统会自动从当前会话获取:\r\n\r\n1. 从 `OPENCLAW_SENDER_ID` 或 `DINGTALK_SENDER_ID` 获取 sender_id\r\n2. 调用钉钉 API 查询对应的 unionId\r\n3. 使用 unionId 作为 operatorId\r\n\r\n如果获取失败，会显示友好的错误提示。\r\n\r\n## 执行规则\r\n\r\n- 读取操作不受白名单限制\r\n- 默认直接在白名单配置的`workspaceId`中尝试写入\r\n- 写入操作必须通过白名单检查;未配置 workspace 或节点名不在白名单内时，一律拒绝!**没有任何方式可以绕过白名单检查!**\r\n- `whitelist: [\"/\"]` 表示允许写入该 workspace 下的所有节点;更细粒度控制请配置具体文档名，例如 `\"/三级目录测试文档.adoc\"`\r\n- 白名单配置文件 `config/whitelist.json` 只能由用户手动修改;AI 只能读取、解释、提示用户手动调整，不能替用户改\r\n- 如果用户没有给出目标文档的 `nodeId` / `docKey`,先运行 `list-docs`、`search` 或 `get-doc` 确认目标\r\n\r\n## URL 解析规则\r\n\r\n当用户提供钉钉文档 URL 时，按以下规则提取 `nodeId`:\r\n\r\n**URL 格式:** `https://alidocs.dingtalk.com/i/nodes/<nodeId>?...`\r\n\r\n**提取方法:**\r\n1. 从 URL 中提取 `nodes/` 和 `?` 之间的部分\r\n2. 该部分即为 `nodeId`\r\n\r\n**示例:**\r\n```\r\nURL: https://alidocs.dingtalk.com/i/nodes/oP0MALyR8kOd5BGacKv6NbxE83bzYmDO?utm_scene=team_space\r\nnodeId: oP0MALyR8kOd5BGacKv6NbxE83bzYmDO\r\n```\r\n\r\n**提取到 nodeId 后的操作:**\r\n1. **获取文档元数据**（名称、知识库 ID 等）：`get-doc --nodeId=<提取的 nodeId>`\r\n2. **获取文档内容**（总结、读取正文）：`get-content --docKey=<提取的 nodeId>`\r\n3. 注意：`get-doc` 只返回元数据，`get-content` 才返回正文内容\r\n\r\n## 执行入口\r\n\r\n- `scripts/index.js`:主入口\r\n- `scripts/whitelist.js`:辅助检查白名单配置\r\n\r\n**跨平台说明：**\r\n\r\n- 本 skill 的脚本基于 Node.js 内置模块实现，Windows、Linux、macOS 只要安装了 `node` 并配置好环境变量，都可以运行\r\n- 推荐优先使用相对路径执行：`node scripts/index.js ...`，这样最不容易受平台路径差异影响\r\n\r\n**路径示例：**\r\n\r\nWindows PowerShell 中 `~` 不会自动展开，建议使用以下方式之一：\r\n\r\n```bash\r\n# ✅ 使用 $env:USERPROFILE\r\nnode $env:USERPROFILE\\.openclaw\\skills\\dingtalk-doc\\scripts\\index.js\r\n\r\n# ✅ 或使用完整绝对路径\r\nnode C:\\Users\\zhenhuaixiu\\.openclaw\\skills\\dingtalk-doc\\scripts\\index.js\r\n\r\n# ❌ 错误（~ 不会展开）\r\nnode ~/.openclaw/skills/dingtalk-doc/scripts/index.js\r\n```\r\n\r\nLinux / macOS Shell 示例：\r\n\r\n```bash\r\n# ✅ 当前目录下直接运行（推荐）\r\nnode scripts/index.js list-workspaces\r\n\r\n# ✅ 或使用完整绝对路径\r\nnode ~/.openclaw/skills/dingtalk-doc/scripts/index.js list-workspaces\r\n```\r\n\r\n## 命令映射\r\n\r\n**路径说明：**\r\n\r\n- Windows 示例中的 `$env:USERPROFILE\\.openclaw` 会展开为 `C:\\Users\\<用户名>\\.openclaw`\r\n- Linux / macOS 示例中的 `~/.openclaw` 会展开为用户主目录下的 `.openclaw`\r\n- 如果当前工作目录已经在 skill 根目录，直接使用 `node scripts/index.js ...` 即可\r\n\r\n### 读取操作\r\n\r\n| 命令 | 用途 | API 端点 |\r\n|------|------|---------|\r\n| `list-workspaces` | 获取知识库列表 | `GET /v2.0/wiki/mineWorkspaces` |\r\n| `list-docs` | 获取知识库中文档列表 | `GET /v2.0/wiki/nodes` |\r\n| `get-doc` | 获取**文档元数据**（名称、ID、创建者、字数等） | `GET /v2.0/wiki/nodes/{nodeId}` |\r\n| `get-content` | 获取**文档正文内容**（段落、标题、列表等块结构） | `GET /v1.0/doc/suites/documents/{docKey}/blocks` |\r\n| `search` | 搜索文档 | `GET /v1.0/doc/workspaces/{workspaceId}/docs` |\r\n\r\n**示例：**\r\n```bash\r\n# Linux / macOS\r\nnode ~/.openclaw/skills/dingtalk-doc/scripts/index.js list-workspaces\r\nnode ~/.openclaw/skills/dingtalk-doc/scripts/index.js list-docs --workspaceId=YRBGvyxxx --parentNodeId=root\r\nnode ~/.openclaw/skills/dingtalk-doc/scripts/index.js get-doc --nodeId=YQBnd5ExVE0PDnezU2PK2RK6WyeZqMmz\r\nnode ~/.openclaw/skills/dingtalk-doc/scripts/index.js get-content --docKey=YQBnd5ExVE0PDnezU2PK2RK6WyeZqMmz\r\nnode ~/.openclaw/skills/dingtalk-doc/scripts/index.js search --workspaceId=YRBGvyxxx --keyword=\"需求\"\r\n\r\n# Windows PowerShell\r\nnode $env:USERPROFILE\\.openclaw\\skills\\dingtalk-doc\\scripts\\index.js list-workspaces\r\nnode $env:USERPROFILE\\.openclaw\\skills\\dingtalk-doc\\scripts\\index.js list-docs --workspaceId=YRBGvyxxx --parentNodeId=root\r\nnode $env:USERPROFILE\\.openclaw\\skills\\dingtalk-doc\\scripts\\index.js get-doc --nodeId=YQBnd5ExVE0PDnezU2PK2RK6WyeZqMmz\r\nnode $env:USERPROFILE\\.openclaw\\skills\\dingtalk-doc\\scripts\\index.js get-content --docKey=YQBnd5ExVE0PDnezU2PK2RK6WyeZqMmz\r\nnode $env:USERPROFILE\\.openclaw\\skills\\dingtalk-doc\\scripts\\index.js search --workspaceId=YRBGvyxxx --keyword=\"需求\"\r\n```\r\n\r\n### 写入操作\r\n\r\n```bash\r\n# Linux / macOS\r\nnode ~/.openclaw/skills/dingtalk-doc/scripts/index.js create-doc --workspaceId=YRBGvyxxx --name=\"新文档\" --docType=DOC --parentNodeId=root\r\nnode ~/.openclaw/skills/dingtalk-doc/scripts/index.js update-content --nodeId=YQBnd5ExVE0PDnezU2PK2RK6WyeZqMmz --content=\"# 标题\\n\\n内容\"\r\nnode ~/.openclaw/skills/dingtalk-doc/scripts/index.js update-content --nodeId=YQBnd5ExVE0PDnezU2PK2RK6WyeZqMmz --docKey=真实docKey --content=\"# 标题\\n\\n内容\"\r\nnode ~/.openclaw/skills/dingtalk-doc/scripts/index.js delete-doc --workspaceId=YRBGvyxxx --nodeId=YQBnd5ExVE0PDnezU2PK2RK6WyeZqMmz\r\n\r\n# Windows PowerShell\r\n# 创建文档\r\nnode $env:USERPROFILE\\.openclaw\\skills\\dingtalk-doc\\scripts\\index.js create-doc --workspaceId=YRBGvyxxx --name=\"新文档\" --docType=DOC --parentNodeId=root\r\n# 返回：{ \"docKey\": \"abc123\", \"nodeId\": \"xyz789\", ... }\r\n\r\n# 更新文档内容（整篇覆写，替换全部内容）\r\n# ✅ 推荐：只用 nodeId（大多数情况够用）\r\nnode $env:USERPROFILE\\.openclaw\\skills\\dingtalk-doc\\scripts\\index.js update-content --nodeId=YQBnd5ExVE0PDnezU2PK2RK6WyeZqMmz --content=\"# 标题\\n\\n内容\"\r\n\r\n# ✅ 备选：如果上面失败，传入真实的 docKey\r\nnode $env:USERPROFILE\\.openclaw\\skills\\dingtalk-doc\\scripts\\index.js update-content --nodeId=YQBnd5ExVE0PDnezU2PK2RK6WyeZqMmz --docKey=真实 docKey --content=\"# 标题\\n\\n内容\"\r\n\r\n# 删除文档\r\nnode $env:USERPROFILE\\.openclaw\\skills\\dingtalk-doc\\scripts\\index.js delete-doc --workspaceId=YRBGvyxxx --nodeId=YQBnd5ExVE0PDnezU2PK2RK6WyeZqMmz\r\n```\r\n\r\n### 块级操作（精细修改单个段落/元素）\r\n\r\n说明：\r\n\r\n- `insert-block`、`modify-block`、`delete-block` 已通过真实文档测试\r\n- `append-text` 对应的公开 API 当前返回 `InvalidAction.NotFound`，不要再调用或承诺\r\n- 如果用户要“追加内容”，优先改成“插入一个新段落”或“读取原段落后使用 `modify-block` 整块替换”\r\n\r\n```bash\r\n# Linux / macOS\r\nnode ~/.openclaw/skills/dingtalk-doc/scripts/index.js delete-block --nodeId=YQBnd5ExVE0PDnezU2PK2RK6WyeZqMmz --blockId=blk123\r\nnode ~/.openclaw/skills/dingtalk-doc/scripts/index.js modify-block --nodeId=YQBnd5ExVE0PDnezU2PK2RK6WyeZqMmz --blockId=blk123 --element='{\"blockType\":\"paragraph\",\"paragraph\":{\"text\":\"新内容\"}}'\r\nnode ~/.openclaw/skills/dingtalk-doc/scripts/index.js insert-block --nodeId=YQBnd5ExVE0PDnezU2PK2RK6WyeZqMmz --element='{\"blockType\":\"paragraph\",\"paragraph\":{\"text\":\"插入的内容\"}}' --position=3\r\n\r\n# Windows PowerShell\r\n# 删除块元素（删除某个段落/标题/列表项）\r\nnode $env:USERPROFILE\\.openclaw\\skills\\dingtalk-doc\\scripts\\index.js delete-block --nodeId=YQBnd5ExVE0PDnezU2PK2RK6WyeZqMmz --blockId=blk123\r\n\r\n# 修改块元素（替换单个块的内容，不影响其他部分）\r\nnode $env:USERPROFILE\\.openclaw\\skills\\dingtalk-doc\\scripts\\index.js modify-block --nodeId=YQBnd5ExVE0PDnezU2PK2RK6WyeZqMmz --blockId=blk123 --element='{\"blockType\":\"paragraph\",\"paragraph\":{\"text\":\"新内容\"}}'\r\n\r\n# 插入块元素（在指定位置插入新段落/标题等）\r\nnode $env:USERPROFILE\\.openclaw\\skills\\dingtalk-doc\\scripts\\index.js insert-block --nodeId=YQBnd5ExVE0PDnezU2PK2RK6WyeZqMmz --element='{\"blockType\":\"paragraph\",\"paragraph\":{\"text\":\"插入的内容\"}}' --position=3\r\n```\r\n\r\n### 参数说明\r\n\r\n- `docType`: `DOC`(文字) | `WORKBOOK`(表格) | `MIND`(脑图) | `FOLDER`(文件夹)\r\n- `nodeId`: **必填**，节点 ID（用于白名单检查）\r\n  - 从文档链接 `alidocs.dingtalk.com/i/nodes/xxx` 提取 `xxx` 部分\r\n  - 通过 `get-doc --nodeId=xxx` 或 `get-content --docKey=xxx` 确认\r\n- `docKey`: **可选**，真实的文档标识符（用于实际写入 API）\r\n  - 如果不传，默认使用 `nodeId` 代替\r\n  - 仅在 `nodeId` 作为 `docKey` 写入失败时，才需要传入真实的 `docKey`\r\n  - 真实 `docKey` 可通过 createDoc 返回值或钉钉 API Explorer 获取\r\n- `blockId`: 块 ID，通过 `get-content` 获取文档结构后得到（块级操作必需）\r\n- `element`: 块元素 JSON 对象（**会自动解析**，直接传 JSON 字符串即可）\r\n  - 示例：`--element='{\"blockType\":\"paragraph\",\"paragraph\":{\"text\":\"新内容\"}}'`\r\n- `position`: 插入位置（可选），数字，**支持 0**（表示插到最前面）\r\n- `workspaceId`: 知识库 ID（可选）\r\n  - 注意：写入操作会通过 `nodeId` 查询节点真实 `workspaceId`，传入 `--workspaceId` 时只能作为一致性校验，不能跳过查询\r\n  - 适用场景：已知 `workspaceId` 且想显式校验目标文档属于该知识库，但 `nodeId` 仍必需\r\n\r\n### 命令选择指南\r\n\r\n| 需求 | 使用命令 | 说明 |\r\n|------|---------|------|\r\n| 重写整篇文档 | `update-content --nodeId=xxx --content=\"...\"` | 替换全部内容 |\r\n| 只修改某个段落 | `modify-block --nodeId=xxx --blockId=blk --element='{...}'` | 只影响单个块 |\r\n| 在当前位置新增一段 | `insert-block --nodeId=xxx --element='{...}' --position=3` | 插入一个新块，更适合“追加一段”的需求 |\r\n| 删除某一段/标题 | `delete-block --nodeId=xxx --blockId=blk` | 删除块 |\r\n| 插入新段落/标题 | `insert-block --nodeId=xxx --element='{...}' --position=3` | 在指定位置插入 |\r\n\r\n## 常见问题\r\n\r\n1. **\"无法获取文档信息\"错误**: \r\n   - 确保传入的是 `nodeId`（从文档链接 `/i/nodes/xxx` 提取）\r\n   - `--workspaceId` 不能替代 `nodeId`，只能作为额外一致性校验\r\n   \r\n2. **\"paramError\" / JSON 解析失败**: \r\n   - `--element` 必须是合法的 JSON 格式，检查引号转义\r\n   - PowerShell 中用单引号包裹：`--element='{\"type\":\"paragraph\"}'`\r\n   \r\n3. **更新失败（nodeNotExist 等）**: \r\n   - 尝试传入真实的 `docKey`：`update-content --nodeId=xxx --docKey=真实 docKey --content=\"...\"`\r\n   - createDoc 返回的 `docKey` 和 `nodeId` 可能不同\r\n\r\n4. **nodeId 和 docKey 到底有什么区别？**\r\n   - `nodeId`: 目录树节点 ID（wiki_2.0 API 用），用于定位文档、获取 workspaceId 和节点名、执行白名单检查\r\n   - `docKey`: 文档内容标识符（suites/documents API 用），用于实际读写内容\r\n   - 经验：大多数情况下 `nodeId` 可直接用作 `docKey`，少数情况需要真实 `docKey`\r\n\r\n5. **为什么没有 append-text？**\r\n   - 当前公开接口 `POST /v1.0/doc/suites/documents/{docKey}/paragraphs/{blockId}/text` 在真实测试中返回 `InvalidAction.NotFound`\r\n   - 因此本 skill 不再承诺 `append-text`，请使用 `insert-block` 或 `modify-block`\r\n\r\n## 推荐流程\r\n\r\n1. 先确认凭证和 sender_id 是否可用。\r\n2. 需要定位文档所在知识库时先跑 `list-workspaces`。\r\n3. 需要定位文档时先跑 `list-docs`、`search`、`get-doc`。\r\n4. 执行写操作前，默认假设会触发白名单校验，不要跳过读取确认步骤。\r\n5. 如果写入被拒绝，只说明是哪个 workspace / 节点名未通过白名单，并提示用户手动调整 `config/whitelist.json`。\r\n\r\n## 常见失败\r\n\r\n- 缺少 sender_id:检查钉钉连接器是否注入 `OPENCLAW_SENDER_ID` / `DINGTALK_SENDER_ID`\r\n- `forbidden.accessDenied`:检查应用权限或白名单\r\n- `invalidRequest.workspaceNode.parentNotFound`:检查 `parentNodeId`\r\n- `权限拒绝：知识库 xxx 未配置白名单`:让用户手动补充 `config/whitelist.json`\r\n- `nodeNotExist`(更新内容时):**尝试使用 nodeId 代替 docKey** - 钉钉 API 中 createDoc 返回的 docKey 和 overwriteContent 需要的 docKey 可能不一致\r\n- `blockNotExist`(块级操作时):先用 `get-content` 获取文档结构，确认 blockId 正确\r\n- `paramError`(modify-block/insert-block):检查 `--element` 参数是否是合法的 JSON 格式\r\n\r\n## 重要提示\r\n\r\n### get-doc vs get-content\r\n\r\n| 命令 | 用途 | 返回内容 | 何时使用 |\r\n|------|------|---------|---------|\r\n| `get-doc` | 获取**文档元数据** | `data.node`：名称、ID、创建者、修改时间、字数、workspaceId | 确认文档存在、获取文档基本信息、定位知识库 |\r\n| `get-content` | 获取**文档正文** | `data.result.data[]`：段落、标题、列表等块结构 | 总结内容、读取正文、准备修改文档 |\r\n\r\n**关键区别：**\r\n- `get-doc` → `GET /v2.0/wiki/nodes/{nodeId}` → **不包含正文内容**\r\n- `get-content` → `GET /v1.0/doc/suites/documents/{docKey}/blocks` → **包含正文块结构**\r\n- **总结、读取内容时，始终使用 `get-content`，不要用 `get-doc`**\r\n\r\n### docKey vs nodeId\r\n\r\n- `nodeId`: 目录树节点 ID（wiki_2.0 API 用），用于定位文档、获取 workspaceId 和节点名、执行白名单检查\r\n- `docKey`: 文档内容标识符（suites/documents API 用），用于实际读写内容\r\n- `create-doc` 返回的 `docKey` 和 `nodeId` 可能是不同的值\r\n- `update-content`、`get-content` 等命令**优先使用 `nodeId`** 作为 `--docKey` 参数\r\n- 如果使用 `docKey` 更新失败 (`nodeNotExist`),请改用 `nodeId`\r\n- 经验：大多数情况下 `nodeId` 可直接用作 `docKey`，少数情况需要真实 `docKey`\r\n\r\n**示例:**\r\n\r\n```bash\r\n# 创建文档后，使用返回的 nodeId 进行更新\r\nnode .../index.js create-doc --workspaceId=xxx --name=\"新文档\"\r\n# 返回:{ \"docKey\": \"abc123\", \"nodeId\": \"xyz789\", ... }\r\n\r\n# ✅ 正确：使用 nodeId 更新\r\nnode .../index.js update-content --docKey=xyz789 --content=\"...\"\r\n\r\n# ❌ 可能失败：使用 docKey 更新\r\nnode .../index.js update-content --docKey=abc123 --content=\"...\"\r\n```\r\n\r\n## 参考\r\n\r\n- 详细说明见 `README.md`\r\n- 钉钉开放平台文档:[knowledge-base-overview](https://open.dingtalk.com/document/development/knowledge-base-overview)\r\n- API Explorer:[open-dev.dingtalk.com/apiExplorer](https://open-dev.dingtalk.com/apiExplorer)\n\nFile v1.0.8:README.md\n\n# 钉钉文档管理技能 (dingtalk-doc)\r\n\r\n> [https://clawhub.ai/shyzhen/dingtalk-doc](https://clawhub.ai/shyzhen/dingtalk-doc)\r\n> \r\n> 是 [dingtalk-doc-enterprise](https://clawhub.ai/shyzhen/dingtalk-doc-enterprise) 的升级版(支持写入范围控制)\r\n\r\n通过钉钉开放平台 API 读取和管理钉钉文档、钉钉知识库中的文档。本文件面向人类读者，重点说明配置、上手方式和排障思路；执行规则与触发策略见 `SKILL.md`。\r\n\r\n## 能力概览\r\n\r\n- 支持列出、搜索、读取钉钉文档\r\n- 支持创建、更新、删除钉钉文档\r\n- 支持多个 workspace 共用一份白名单配置\r\n- 写入操作按 workspace + 节点名白名单控制，读取操作不受白名单限制\r\n- 当前用户身份从钉钉连接器注入的 `sender_id` 自动解析\r\n\r\n## 文档分工\r\n\r\n- `SKILL.md`：给 agent 的执行手册，强调触发条件、命令映射和失败处理\r\n- `README.md`：给人的配置说明、背景信息、排障参考\r\n\r\n## 快速开始\r\n\r\n### 1. 配置环境变量\r\n\r\n本 skill 可运行在 Windows、Linux、macOS；核心要求只有：\r\n\r\n- 安装 `node`\r\n- 配置 `DINGTALK_CLIENTID` / `DINGTALK_CLIENTSECRET`\r\n\r\n**编辑 `~/.openclaw/.env` 文件（推荐，优先级最高）**\r\n\r\nOpenClaw 会在启动时自动加载此文件中的环境变量。\r\n\r\n```bash\r\n# ~/.openclaw/.env\r\nDINGTALK_CLIENTID=dingxxxxxx\r\nDINGTALK_CLIENTSECRET=your_secret\r\n```\r\n\r\n说明：\r\n\r\n- `DINGTALK_CLIENTID`：钉钉应用 Client ID\r\n- `DINGTALK_CLIENTSECRET`：钉钉应用 Client Secret\r\n- `OPENCLAW_SENDER_ID` / `DINGTALK_SENDER_ID`：通常由 OpenClaw / 钉钉连接器自动注入；如需手工调用 CLI，也可以用 `--senderId=...`\r\n- `DINGTALK_DEBUG=true`：可选，仅输出请求方法、接口路径、状态码、requestId 等调试信息，不再打印文档正文和请求体\r\n- `operatorId` 不需要手工配置，脚本会优先从 `--senderId` 或 `OPENCLAW_SENDER_ID` / `DINGTALK_SENDER_ID` 解析当前钉钉用户\r\n\r\n### 2. 确认 workspaceId\r\n\r\n- 白名单配置：只支持 【钉钉文档 -> 知识库】下新建的根目录，也就是说可写范围必须在【知识库】中。\r\n- 直接发一篇文档给你的钉钉机器人让他随便写入一段话，就会触发白名单检查，机器人会主动告知你当前文档的workspaceId，以及如何配置。\r\n\r\n### 3. 配置白名单\r\n\r\n说明：\r\n\r\n- 读取操作不依赖白名单文件\r\n- 写入操作才需要 `config/whitelist.json`\r\n- 如果该文件不存在，写操作会被拒绝，并提示用户手动创建\r\n\r\n在 `config/whitelist.json` 中声明允许写入的 workspace 和节点名：\r\n\r\n```json\r\n{\r\n  \"workspaces\": [\r\n    {\r\n      \"workspaceId\": \"eLvJDSRX3l4moO87\",\r\n      \"workspaceName\": \"AI可写知识库一级目录\",\r\n      \"allowRootWrite\": false,\r\n      \"whitelist\": [\"/\"]\r\n    }\r\n  ]\r\n}\r\n```\r\n\r\n字段说明：\r\n\r\n- `workspaceId`：知识库 ID，必填 （只支持【钉钉文档 -> 知识库】下的目录）\r\n- `workspaceName`：可选，便于识别\r\n- `allowRootWrite`：保留字段；当前写入校验主要使用 `whitelist`\r\n- `whitelist`：允许写入的节点名（文档名）列表\r\n\r\n白名单规则：\r\n\r\n- 读取操作不检查白名单\r\n- 写入操作必须命中白名单\r\n- 未配置的 workspace 默认禁止写入\r\n- `\"/\"` 表示允许写入当前 `workspaceId` 下的所有节点\r\n- 细粒度白名单按节点名匹配，例如 `\"/三级文档.adoc\"`；当前已验证的节点详情接口不会返回完整父目录路径\r\n\r\n## 常用命令\r\n\r\n这里只保留几个面向人的常见示例；完整命令映射见 `SKILL.md`。\r\n\r\n读取：\r\n\r\n```bash\r\n# 当前目录运行（跨平台通用）\r\nnode scripts/index.js list-workspaces\r\nnode scripts/index.js list-docs --workspaceId=OQ0xySj6ng7lX58B --parentNodeId=root\r\nnode scripts/index.js get-doc --nodeId=YQBnd5ExVE0PDnezU2PK2RK6WyeZqMmz\r\nnode scripts/index.js get-content --nodeId=YQBnd5ExVE0PDnezU2PK2RK6WyeZqMmz\r\nnode scripts/index.js get-content --docKey=真实docKey --nodeId=YQBnd5ExVE0PDnezU2PK2RK6WyeZqMmz\r\n```\r\n\r\n写入：\r\n\r\n```bash\r\n# 当前目录运行（跨平台通用）\r\nnode scripts/index.js create-doc --workspaceId=OQ0xySj6ng7lX58B --name=\"新文档\" --docType=DOC --parentNodeId=root\r\nnode scripts/index.js update-content --nodeId=YQBnd5ExVE0PDnezU2PK2RK6WyeZqMmz --content=\"# 标题\\n\\n内容\"\r\nnode scripts/index.js update-content --docKey=真实docKey --nodeId=YQBnd5ExVE0PDnezU2PK2RK6WyeZqMmz --content=\"# 标题\\n\\n内容\"\r\n```\r\n\r\n白名单自检：\r\n\r\n```bash\r\n# 当前目录运行（跨平台通用）\r\nnode scripts/whitelist.js check \"/三级文档.adoc\"\r\n```\r\n\r\n`docType` 支持：\r\n\r\n- `DOC`\r\n- `WORKBOOK`\r\n- `MIND`\r\n- `FOLDER`\r\n\r\n## 目录结构\r\n\r\n```text\r\ndingtalk-doc/\r\n├── SKILL.md\r\n├── README.md\r\n├── package.json\r\n├── config/\r\n│   └── whitelist.json\r\n└── scripts/\r\n    ├── index.js\r\n    ├── dingtalk-client.js\r\n    └── whitelist.js\r\n```\r\n\r\n## 已知返回结构\r\n\r\n以下结构来自实测文档 `https://alidocs.dingtalk.com/i/nodes/oP0MALyR8kOd5BGacKv6NbxE83bzYmDO?utm_scene=team_space`，文档名是“三级文档”，真实位于三级子目录中。\r\n\r\n`get-doc --nodeId=...` / `GET /v2.0/wiki/nodes/{nodeId}` 返回的 `node` 里有 `workspaceId` 和 `name`，但没有返回完整父目录链路：\r\n\r\n```json\r\n{\r\n  \"node\": {\r\n    \"name\": \"三级文档.adoc\",\r\n    \"nodeId\": \"oP0MALyR8kOd5BGacKv6NbxE83bzYmDO\",\r\n    \"workspaceId\": \"eLvJDSRX3l4moO87\",\r\n    \"type\": \"FILE\",\r\n    \"category\": \"ALIDOC\",\r\n    \"url\": \"https://alidocs.dingtalk.com/i/nodes/oP0MALyR8kOd5BGacKv6NbxE83bzYmDO\"\r\n  }\r\n}\r\n```\r\n\r\n容易犯错的点：\r\n\r\n- 不要把白名单理解为真实目录路径；当前脚本只能可靠使用 `workspaceId + node.name` 做写入白名单。\r\n- 即使文档在三级子目录中，节点详情也可能只返回 `\"name\": \"三级文档.adoc\"`，不会返回 `\"/一级/二级/三级文档.adoc\"`。\r\n- `get-content --nodeId=...` / `GET /v1.0/doc/suites/documents/{docKey}/blocks` 返回的是块结构，例如 paragraph 的 `id`、`index`、`text`，不返回文档路径或父目录信息。\r\n- `nodeId` 用于查询节点详情和校验真实 `workspaceId`；`docKey` 用于 suites/documents 内容读写接口。多数情况下可先用 `nodeId` 作为 `docKey` 尝试。\r\n- `insert-block`、`modify-block`、`delete-block` 已在真实文档上测试通过；`append-text` 对应的 `paragraphs/{blockId}/text` 当前返回 `InvalidAction.NotFound`，不要依赖它。\r\n\r\n## 权限与接口\r\n\r\n常用接口：\r\n\r\n- `POST /v1.0/doc/workspaces/{workspaceId}/docs`\r\n- `GET /v2.0/wiki/nodes`\r\n- `GET /v2.0/wiki/nodes/{nodeId}`\r\n- `DELETE /v1.0/doc/workspaces/{workspaceId}/docs/{nodeId}`\r\n- `GET /v1.0/doc/suites/documents/{docKey}/blocks`\r\n- `POST /v1.0/doc/suites/documents/{docKey}/overwriteContent`\r\n- `POST /v1.0/doc/suites/documents/{docKey}/blocks`\r\n- `PUT /v1.0/doc/suites/documents/{docKey}/blocks/{blockId}`\r\n- `DELETE /v1.0/doc/suites/documents/{docKey}/blocks/{blockId}`\r\n\r\n常见所需权限：\r\n\r\n- `Document.Workspace.Read`\r\n- `Document.Workspace.Write`\r\n- `Document.WorkspaceDocument.Read`\r\n- `Document.WorkspaceDocument.Write`\r\n- `Wiki.Node.Read`\r\n\r\n## 常见问题\r\n\r\n| 问题 | 原因 | 处理方式 |\r\n|------|------|---------|\r\n| 缺少 `sender_id` | 钉钉连接器未传递当前用户 ID | 检查是否注入了 `OPENCLAW_SENDER_ID` 或 `DINGTALK_SENDER_ID` |\r\n| workspace 未配置白名单 | `config/whitelist.json` 没有对应 workspace | 补充对应 `workspaceId` 配置 |\r\n| 节点名不在白名单内 | 写入目标节点名未命中规则 | 调整白名单节点名或使用 `\"/\"` 允许整个 workspace |\r\n| `forbidden.accessDenied` | 应用权限不足或白名单不通过 | 检查钉钉应用权限和白名单 |\r\n| `invalidRequest.workspaceNode.parentNotFound` | `parentNodeId` 错误 | 重新确认父节点 ID |\r\n\r\n## 参考\r\n\r\n- 钉钉开放平台文档：[knowledge-base-overview](https://open.dingtalk.com/document/development/knowledge-base-overview)\r\n- API Explorer：[open-dev.dingtalk.com/apiExplorer](https://open-dev.dingtalk.com/apiExplorer)\r\n\r\n### API 参考\r\n\r\n钉钉知识库 API 分为两个系列：\r\n\r\n#### doc_1.0 - 知识库管理\r\n\r\n| API | 方法 | 路径 | 权限 |\r\n|-----|------|------|------|\r\n| 新建知识库 | POST | `/v1.0/doc/workspaces` | Document.Workspace.Write |\r\n| 获取知识库 | GET | `/v1.0/doc/workspaces/{workspaceId}` | Document.Workspace.Read |\r\n| 获取知识库列表 | GET | `/v1.0/doc/workspaces` | Document.Workspace.Read |\r\n| 创建知识库文档 | POST | `/v1.0/doc/workspaces/{workspaceId}/docs` | Document.WorkspaceDocument.Write |\r\n| 删除知识库文档 | DELETE | `/v1.0/doc/workspaces/{workspaceId}/docs/{nodeId}` | Document.WorkspaceDocument.Write |\r\n\r\n#### wiki_2.0 - 目录树管理\r\n\r\n| API | 方法 | 路径 | 权限 |\r\n|-----|------|------|------|\r\n| 获取节点 | GET | `/v2.0/wiki/nodes/{nodeId}` | Wiki.Node.Read |\r\n| 获取节点列表 | GET | `/v2.0/wiki/nodes` | Wiki.Node.Read |\r\n| 通过链接获取节点 | GET | `/v2.0/wiki/nodes/url` | Wiki.Node.Read |\r\n| 复制文档 | POST | `/v2.0/wiki/nodes/{nodeId}/copy` | Document.WorkspaceDocument.Write |\r\n\r\n#### suites/documents - 文档内容\r\n\r\n| API | 方法 | 路径 | 权限 |\r\n|-----|------|------|------|\r\n| 查询块结构 | GET | `/v1.0/doc/suites/documents/{docKey}/blocks` | Document.WorkspaceDocument.Read |\r\n| 覆写内容 | POST | `/v1.0/doc/suites/documents/{docKey}/overwriteContent` | Document.WorkspaceDocument.Write |\r\n| 插入块 | POST | `/v1.0/doc/suites/documents/{docKey}/blocks` | Document.WorkspaceDocument.Write |\r\n| 删除块 | DELETE | `/v1.0/doc/suites/documents/{docKey}/blocks/{blockId}` | Document.WorkspaceDocument.Write |\r\n\r\n\r\n#### 所需权限清单\r\n\r\n调用本 skill 需要开通以下钉钉应用权限：\r\n\r\n- `Document.Workspace.Read` - 知识库读权限\r\n- `Document.Workspace.Write` - 知识库写权限\r\n- `Document.WorkspaceDocument.Read` - 文档读权限\r\n- `Document.WorkspaceDocument.Write` - 文档写权限\r\n- `Wiki.Node.Read` - 节点读权限\r\n\r\n## 许可证\r\n\r\nMIT\n\nFile v1.0.8:_meta.json\n\n{\n  \"ownerId\": \"kn73t8gpykqmgx10ksfb6706r5842gge\",\n  \"slug\": \"dingtalk-doc\",\n  \"version\": \"1.0.8\",\n  \"publishedAt\": 1781147080846\n}\n\nFile v1.0.8:skill-card.md\n\n## Description:\n\nHelps an agent read, summarize, create, update, and delete DingTalk Docs and DingTalk knowledge-base documents when a DingTalk document link or context is provided.\n\nThis skill is ready for commercial/non-commercial use.\n\n## Publisher:\n\n[shyzhen](https://clawhub.ai/user/shyzhen)\n\n### License/Terms of Use:\n\nMIT-0\n\n## Use Case:\n\nDevelopers and teams using OpenClaw with DingTalk use this skill to locate, read, summarize, create, overwrite, modify, insert into, and delete DingTalk knowledge-base documents through DingTalk APIs.\n\n### Deployment Geography for Use:\n\nGlobal\n\n## Known Risks and Mitigations:\n\nRisk: The skill exposes high-impact document overwrite, block modification, and delete operations.\n\nMitigation: Require explicit user confirmation before overwrite or delete actions and review proposed content changes before execution.\n\nRisk: The bundled whitelist configuration can allow broad workspace writes.\n\nMitigation: Replace the bundled whitelist with narrow, user-approved workspace and document targets before installation.\n\nRisk: DingTalk app credentials and token access can affect remote documents outside the local workspace.\n\nMitigation: Install only with trusted DingTalk credentials, avoid low-level client commands, and remove or disable the get-token command where it is not needed.\n\n## Reference(s):\n\n- [ClawHub skill page](https://clawhub.ai/shyzhen/skills/dingtalk-doc)\n- [DingTalk knowledge-base overview](https://open.dingtalk.com/document/development/knowledge-base-overview)\n- [DingTalk API Explorer](https://open-dev.dingtalk.com/apiExplorer)\n\n## Skill Output:\n\n**Output Type(s):** [text, markdown, shell commands, configuration, guidance]\n\n**Output Format:** [Markdown guidance, shell commands, configuration snippets, and JSON API responses.]\n\n**Output Parameters:** [1D]\n\n**Other Properties Related to Output:** [Requires DingTalk app credentials and a sender identity; write actions can modify or delete remote DingTalk documents.]\n\n## Skill Version(s):\n\n1.0.8 (source: server release evidence; artifact package.json reports 1.0.6)\n\n## Ethical Considerations:\n\nUsers should evaluate whether this skill is appropriate for their environment, review any generated or modified files before relying on them, and apply their organization's safety, security, and compliance requirements before deployment.\n\nFile v1.0.8:config/whitelist.json\n\n{\n  \"_comment\": \"⚠️ 此配置文件只能由用户手动编辑，AI 禁止修改！\",\n  \"workspaces\": [\n    {\n      \"workspaceId\": \"eLvJDSRX3l4moO87\",\n      \"workspaceName\": \"AI可写知识库一级目录\",\n      \"allowRootWrite\": true,\n      \"whitelist\": [\"/\"]\n    }\n  ]\n}\n\nFile v1.0.8:package.json\n\n{\n  \"name\": \"dingtalk-doc\",\n  \"version\": \"1.0.6\",\n  \"description\": \"钉钉文档管理技能 - 支持文档增删改查和白名单权限控制\",\n  \"main\": \"scripts/index.js\",\n  \"scripts\": {\n    \"get-token\": \"node scripts/dingtalk-client.js get-token\",\n    \"get-workspace-list\": \"node scripts/dingtalk-client.js get-workspace-list\",\n    \"list-docs\": \"node scripts/index.js list-docs\",\n    \"create-doc\": \"node scripts/index.js create-doc\",\n    \"delete-doc\": \"node scripts/index.js delete-doc\",\n    \"search\": \"node scripts/index.js search\",\n    \"get-content\": \"node scripts/index.js get-content\",\n    \"update-content\": \"node scripts/index.js update-content\",\n    \"check-permission\": \"node scripts/whitelist.js check\"\n  },\n  \"keywords\": [\n    \"dingtalk\",\n    \"knowledge-base\",\n    \"openclaw-skill\"\n  ],\n  \"author\": \"\",\n  \"license\": \"MIT\"\n}\n\nArchive v1.0.7: 9 files, 26942 bytes\n\nFiles: config/whitelist.json (278b), package.json (837b), README.md (10156b), scripts/dingtalk-client.js (20641b), scripts/index.js (30897b), scripts/whitelist.js (5086b), skill-card.md (2553b), SKILL.md (17925b), _meta.json (131b)\n\nFile v1.0.7:SKILL.md\n\n---\r\nname: dingtalk-doc\r\ndescription: 钉钉文档管理技能。当用户发送的消息中包含 alidocs.dingtalk.com 链接、要求总结/读取/查看/更新钉钉文档或钉钉知识库文档，或当前上下文已明确对象是钉钉文档时使用。关键词：钉钉文档、钉钉知识库、alidocs、总结、读取、查看、更新、修改、文档、链接。\r\n---\r\n\r\n# 钉钉文档管理技能\r\n\r\n通过钉钉开放平台 API 管理钉钉文档与钉钉知识库内文档。`SKILL.md` 只保留 agent 执行所需规则;配置细节、示例、API 背景见 `README.md`。\r\n\r\n## 何时使用\r\n\r\n### 触发关键词\r\n\r\n只有在**已经确认对象是钉钉文档**时，同时消息包含以下**任一关键词**时，优先使用本 skill：\r\n\r\n| 类别 | 关键词 |\r\n|------|--------|\r\n| **平台名** | 钉钉文档、钉钉知识库、alidocs |\r\n| **读取类** | 总结、读取、查看、浏览、列出结构 |\r\n| **修改类** | 更新、修改、追加、删除、覆写 |\r\n| **对象** | 文档、链接、这篇、这个文档 |\r\n\r\n**组合示例：**\r\n- \"总结一下这篇钉钉文档\"\r\n- \"读取这个 alidocs 链接\"\r\n- \"更新文档内容\"\r\n- \"删除第三段\"\r\n\r\n### 触发场景（优先级从高到低）\r\n\r\n| 场景 | 示例 | 动作 |\r\n|------|------|------|\r\n| **钉钉文档链接** | `alidocs.dingtalk.com/i/nodes/xxx` | 根据意图选择：元数据→`get-doc`，正文内容→`get-content` |\r\n| **钉钉上下文 + 链接** | \"总结 https://alidocs.dingtalk.com/...\" | 调用 `get-content` 读取内容后总结 |\r\n| **明确命令** | \"总结这篇文档\"、\"读取这个 alidocs 链接\" | 根据意图选择命令 |\r\n| **已知上下文是钉钉文档** | 前文已给出 alidocs 链接，后续说\"更新文档\"、\"删除某段\"、\"在第三段后追加\" | 调用对应命令 |\r\n| **结构查询** | \"列出结构\"、\"这个 alidocs 有哪些章节\" | 调用 `get-content` |\r\n| **块级操作** | \"删除第三段\"、\"修改这个段落\"、\"在这里插入一段\" | 先用 `get-content` 获取 blockId 和位置，再调用 `delete-block`/`modify-block`/`insert-block` |\r\n\r\n### 不触发的场景\r\n- 查询本地文件、离线文档或普通文本内容，且不需要调用钉钉 API\r\n- 没有文档链接、docKey、或明确钉钉文档上下文，却要求修改文档\r\n- 用户只说\"总结文档\"\"更新这个链接\"等泛化请求，但上下文无法确认对象是钉钉文档\r\n- 与钉钉无关的文档系统，例如本地 Markdown、飞书文档、语雀、Google Docs\r\n\r\n## 运行前提\r\n\r\n### 环境变量\r\n\r\n必须配置以下环境变量 (在 Gateway 环境中):\r\n\r\n- `DINGTALK_CLIENTID` - 钉钉应用 Client ID (AppKey)\r\n- `DINGTALK_CLIENTSECRET` - 钉钉应用 Client Secret (AppSecret)\r\n\r\n可选环境变量:\r\n\r\n- `OPENCLAW_SENDER_ID` / `DINGTALK_SENDER_ID` - 由 OpenClaw / 钉钉连接器注入的当前用户 sender_id；也可以通过命令行 `--senderId=` 显式传入\r\n- `DINGTALK_DEBUG` - 设置为 `true` 启用调试模式；仅输出方法、路径（查询参数已脱敏）、状态码、requestId 等，不打印文档正文与完整请求体\r\n\r\n### operatorId 获取方式\r\n\r\n不需要在配置中指定!系统会自动从当前会话获取:\r\n\r\n1. 从 `OPENCLAW_SENDER_ID` 或 `DINGTALK_SENDER_ID` 获取 sender_id\r\n2. 调用钉钉 API 查询对应的 unionId\r\n3. 使用 unionId 作为 operatorId\r\n\r\n如果获取失败，会显示友好的错误提示。\r\n\r\n## 执行规则\r\n\r\n- 读取操作不受白名单限制\r\n- 写入操作默认直接在白名单配置的 workspace 中写入\r\n- 写入操作必须通过白名单检查;未配置 workspace 或节点名不在白名单内时，一律拒绝!**没有任何方式可以绕过白名单检查!**\r\n- `whitelist: [\"/\"]` 表示允许写入该 workspace 下的所有节点;更细粒度控制请配置具体文档名，例如 `\"/三级目录测试文档.adoc\"`\r\n- 白名单配置文件 `config/whitelist.json` 只能由用户手动修改;AI 只能读取、解释、提示用户手动调整，不能替用户改\r\n- 如果用户没有给出 `workspaceId`,先运行 `list-workspaces`\r\n- 如果用户没有给出目标文档的 `nodeId` / `docKey`,先运行 `list-docs`、`search` 或 `get-doc` 确认目标\r\n\r\n## URL 解析规则\r\n\r\n当用户提供钉钉文档 URL 时，按以下规则提取 `nodeId`:\r\n\r\n**URL 格式:** `https://alidocs.dingtalk.com/i/nodes/<nodeId>?...`\r\n\r\n**提取方法:**\r\n1. 从 URL 中提取 `nodes/` 和 `?` 之间的部分\r\n2. 该部分即为 `nodeId`\r\n\r\n**示例:**\r\n```\r\nURL: https://alidocs.dingtalk.com/i/nodes/oP0MALyR8kOd5BGacKv6NbxE83bzYmDO?utm_scene=team_space\r\nnodeId: oP0MALyR8kOd5BGacKv6NbxE83bzYmDO\r\n```\r\n\r\n**提取到 nodeId 后的操作:**\r\n1. **获取文档元数据**（名称、知识库 ID 等）：`get-doc --nodeId=<提取的 nodeId>`\r\n2. **获取文档内容**（总结、读取正文）：`get-content --docKey=<提取的 nodeId>`\r\n3. 注意：`get-doc` 只返回元数据，`get-content` 才返回正文内容\r\n\r\n## 执行入口\r\n\r\n- `scripts/index.js`:主入口\r\n- `scripts/whitelist.js`:辅助检查白名单配置\r\n\r\n**跨平台说明：**\r\n\r\n- 本 skill 的脚本基于 Node.js 内置模块实现，Windows、Linux、macOS 只要安装了 `node` 并配置好环境变量，都可以运行\r\n- 推荐优先使用相对路径执行：`node scripts/index.js ...`，这样最不容易受平台路径差异影响\r\n\r\n**路径示例：**\r\n\r\nWindows PowerShell 中 `~` 不会自动展开，建议使用以下方式之一：\r\n\r\n```bash\r\n# ✅ 使用 $env:USERPROFILE\r\nnode $env:USERPROFILE\\.openclaw\\skills\\dingtalk-doc\\scripts\\index.js\r\n\r\n# ✅ 或使用完整绝对路径\r\nnode C:\\Users\\zhenhuaixiu\\.openclaw\\skills\\dingtalk-doc\\scripts\\index.js\r\n\r\n# ❌ 错误（~ 不会展开）\r\nnode ~/.openclaw/skills/dingtalk-doc/scripts/index.js\r\n```\r\n\r\nLinux / macOS Shell 示例：\r\n\r\n```bash\r\n# ✅ 当前目录下直接运行（推荐）\r\nnode scripts/index.js list-workspaces\r\n\r\n# ✅ 或使用完整绝对路径\r\nnode ~/.openclaw/skills/dingtalk-doc/scripts/index.js list-workspaces\r\n```\r\n\r\n## 命令映射\r\n\r\n**路径说明：**\r\n\r\n- Windows 示例中的 `$env:USERPROFILE\\.openclaw` 会展开为 `C:\\Users\\<用户名>\\.openclaw`\r\n- Linux / macOS 示例中的 `~/.openclaw` 会展开为用户主目录下的 `.openclaw`\r\n- 如果当前工作目录已经在 skill 根目录，直接使用 `node scripts/index.js ...` 即可\r\n\r\n### 读取操作\r\n\r\n| 命令 | 用途 | API 端点 |\r\n|------|------|---------|\r\n| `list-workspaces` | 获取知识库列表 | `GET /v2.0/wiki/mineWorkspaces` |\r\n| `list-docs` | 获取知识库中文档列表 | `GET /v2.0/wiki/nodes` |\r\n| `get-doc` | 获取**文档元数据**（名称、ID、创建者、字数等） | `GET /v2.0/wiki/nodes/{nodeId}` |\r\n| `get-content` | 获取**文档正文内容**（段落、标题、列表等块结构） | `GET /v1.0/doc/suites/documents/{docKey}/blocks` |\r\n| `search` | 搜索文档 | `GET /v1.0/doc/workspaces/{workspaceId}/docs` |\r\n\r\n**示例：**\r\n```bash\r\n# Linux / macOS\r\nnode ~/.openclaw/skills/dingtalk-doc/scripts/index.js list-workspaces\r\nnode ~/.openclaw/skills/dingtalk-doc/scripts/index.js list-docs --workspaceId=YRBGvyxxx --parentNodeId=root\r\nnode ~/.openclaw/skills/dingtalk-doc/scripts/index.js get-doc --nodeId=YQBnd5ExVE0PDnezU2PK2RK6WyeZqMmz\r\nnode ~/.openclaw/skills/dingtalk-doc/scripts/index.js get-content --docKey=YQBnd5ExVE0PDnezU2PK2RK6WyeZqMmz\r\nnode ~/.openclaw/skills/dingtalk-doc/scripts/index.js search --workspaceId=YRBGvyxxx --keyword=\"需求\"\r\n\r\n# Windows PowerShell\r\nnode $env:USERPROFILE\\.openclaw\\skills\\dingtalk-doc\\scripts\\index.js list-workspaces\r\nnode $env:USERPROFILE\\.openclaw\\skills\\dingtalk-doc\\scripts\\index.js list-docs --workspaceId=YRBGvyxxx --parentNodeId=root\r\nnode $env:USERPROFILE\\.openclaw\\skills\\dingtalk-doc\\scripts\\index.js get-doc --nodeId=YQBnd5ExVE0PDnezU2PK2RK6WyeZqMmz\r\nnode $env:USERPROFILE\\.openclaw\\skills\\dingtalk-doc\\scripts\\index.js get-content --docKey=YQBnd5ExVE0PDnezU2PK2RK6WyeZqMmz\r\nnode $env:USERPROFILE\\.openclaw\\skills\\dingtalk-doc\\scripts\\index.js search --workspaceId=YRBGvyxxx --keyword=\"需求\"\r\n```\r\n\r\n### 写入操作\r\n\r\n```bash\r\n# Linux / macOS\r\nnode ~/.openclaw/skills/dingtalk-doc/scripts/index.js create-doc --workspaceId=YRBGvyxxx --name=\"新文档\" --docType=DOC --parentNodeId=root\r\nnode ~/.openclaw/skills/dingtalk-doc/scripts/index.js update-content --nodeId=YQBnd5ExVE0PDnezU2PK2RK6WyeZqMmz --content=\"# 标题\\n\\n内容\"\r\nnode ~/.openclaw/skills/dingtalk-doc/scripts/index.js update-content --nodeId=YQBnd5ExVE0PDnezU2PK2RK6WyeZqMmz --docKey=真实docKey --content=\"# 标题\\n\\n内容\"\r\nnode ~/.openclaw/skills/dingtalk-doc/scripts/index.js delete-doc --workspaceId=YRBGvyxxx --nodeId=YQBnd5ExVE0PDnezU2PK2RK6WyeZqMmz\r\n\r\n# Windows PowerShell\r\n# 创建文档\r\nnode $env:USERPROFILE\\.openclaw\\skills\\dingtalk-doc\\scripts\\index.js create-doc --workspaceId=YRBGvyxxx --name=\"新文档\" --docType=DOC --parentNodeId=root\r\n# 返回：{ \"docKey\": \"abc123\", \"nodeId\": \"xyz789\", ... }\r\n\r\n# 更新文档内容（整篇覆写，替换全部内容）\r\n# ✅ 推荐：只用 nodeId（大多数情况够用）\r\nnode $env:USERPROFILE\\.openclaw\\skills\\dingtalk-doc\\scripts\\index.js update-content --nodeId=YQBnd5ExVE0PDnezU2PK2RK6WyeZqMmz --content=\"# 标题\\n\\n内容\"\r\n\r\n# ✅ 备选：如果上面失败，传入真实的 docKey\r\nnode $env:USERPROFILE\\.openclaw\\skills\\dingtalk-doc\\scripts\\index.js update-content --nodeId=YQBnd5ExVE0PDnezU2PK2RK6WyeZqMmz --docKey=真实 docKey --content=\"# 标题\\n\\n内容\"\r\n\r\n# 删除文档\r\nnode $env:USERPROFILE\\.openclaw\\skills\\dingtalk-doc\\scripts\\index.js delete-doc --workspaceId=YRBGvyxxx --nodeId=YQBnd5ExVE0PDnezU2PK2RK6WyeZqMmz\r\n```\r\n\r\n### 块级操作（精细修改单个段落/元素）\r\n\r\n说明：\r\n\r\n- `insert-block`、`modify-block`、`delete-block` 已通过真实文档测试\r\n- `append-text` 对应的公开 API 当前返回 `InvalidAction.NotFound`，不要再调用或承诺\r\n- 如果用户要“追加内容”，优先改成“插入一个新段落”或“读取原段落后使用 `modify-block` 整块替换”\r\n\r\n```bash\r\n# Linux / macOS\r\nnode ~/.openclaw/skills/dingtalk-doc/scripts/index.js delete-block --nodeId=YQBnd5ExVE0PDnezU2PK2RK6WyeZqMmz --blockId=blk123\r\nnode ~/.openclaw/skills/dingtalk-doc/scripts/index.js modify-block --nodeId=YQBnd5ExVE0PDnezU2PK2RK6WyeZqMmz --blockId=blk123 --element='{\"blockType\":\"paragraph\",\"paragraph\":{\"text\":\"新内容\"}}'\r\nnode ~/.openclaw/skills/dingtalk-doc/scripts/index.js insert-block --nodeId=YQBnd5ExVE0PDnezU2PK2RK6WyeZqMmz --element='{\"blockType\":\"paragraph\",\"paragraph\":{\"text\":\"插入的内容\"}}' --position=3\r\n\r\n# Windows PowerShell\r\n# 删除块元素（删除某个段落/标题/列表项）\r\nnode $env:USERPROFILE\\.openclaw\\skills\\dingtalk-doc\\scripts\\index.js delete-block --nodeId=YQBnd5ExVE0PDnezU2PK2RK6WyeZqMmz --blockId=blk123\r\n\r\n# 修改块元素（替换单个块的内容，不影响其他部分）\r\nnode $env:USERPROFILE\\.openclaw\\skills\\dingtalk-doc\\scripts\\index.js modify-block --nodeId=YQBnd5ExVE0PDnezU2PK2RK6WyeZqMmz --blockId=blk123 --element='{\"blockType\":\"paragraph\",\"paragraph\":{\"text\":\"新内容\"}}'\r\n\r\n# 插入块元素（在指定位置插入新段落/标题等）\r\nnode $env:USERPROFILE\\.openclaw\\skills\\dingtalk-doc\\scripts\\index.js insert-block --nodeId=YQBnd5ExVE0PDnezU2PK2RK6WyeZqMmz --element='{\"blockType\":\"paragraph\",\"paragraph\":{\"text\":\"插入的内容\"}}' --position=3\r\n```\r\n\r\n### 参数说明\r\n\r\n- `docType`: `DOC`(文字) | `WORKBOOK`(表格) | `MIND`(脑图) | `FOLDER`(文件夹)\r\n- `nodeId`: **必填**，节点 ID（用于白名单检查）\r\n  - 从文档链接 `alidocs.dingtalk.com/i/nodes/xxx` 提取 `xxx` 部分\r\n  - 通过 `get-doc --nodeId=xxx` 或 `get-content --docKey=xxx` 确认\r\n- `docKey`: **可选**，真实的文档标识符（用于实际写入 API）\r\n  - 如果不传，默认使用 `nodeId` 代替\r\n  - 仅在 `nodeId` 作为 `docKey` 写入失败时，才需要传入真实的 `docKey`\r\n  - 真实 `docKey` 可通过 createDoc 返回值或钉钉 API Explorer 获取\r\n- `blockId`: 块 ID，通过 `get-content` 获取文档结构后得到（块级操作必需）\r\n- `element`: 块元素 JSON 对象（**会自动解析**，直接传 JSON 字符串即可）\r\n  - 示例：`--element='{\"blockType\":\"paragraph\",\"paragraph\":{\"text\":\"新内容\"}}'`\r\n- `position`: 插入位置（可选），数字，**支持 0**（表示插到最前面）\r\n- `workspaceId`: 知识库 ID（可选）\r\n  - 注意：写入操作会通过 `nodeId` 查询节点真实 `workspaceId`，传入 `--workspaceId` 时只能作为一致性校验，不能跳过查询\r\n  - 适用场景：已知 `workspaceId` 且想显式校验目标文档属于该知识库，但 `nodeId` 仍必需\r\n\r\n### 命令选择指南\r\n\r\n| 需求 | 使用命令 | 说明 |\r\n|------|---------|------|\r\n| 重写整篇文档 | `update-content --nodeId=xxx --content=\"...\"` | 替换全部内容 |\r\n| 只修改某个段落 | `modify-block --nodeId=xxx --blockId=blk --element='{...}'` | 只影响单个块 |\r\n| 在当前位置新增一段 | `insert-block --nodeId=xxx --element='{...}' --position=3` | 插入一个新块，更适合“追加一段”的需求 |\r\n| 删除某一段/标题 | `delete-block --nodeId=xxx --blockId=blk` | 删除块 |\r\n| 插入新段落/标题 | `insert-block --nodeId=xxx --element='{...}' --position=3` | 在指定位置插入 |\r\n\r\n## 常见问题\r\n\r\n1. **\"无法获取文档信息\"错误**: \r\n   - 确保传入的是 `nodeId`（从文档链接 `/i/nodes/xxx` 提取）\r\n   - `--workspaceId` 不能替代 `nodeId`，只能作为额外一致性校验\r\n   \r\n2. **\"paramError\" / JSON 解析失败**: \r\n   - `--element` 必须是合法的 JSON 格式，检查引号转义\r\n   - PowerShell 中用单引号包裹：`--element='{\"type\":\"paragraph\"}'`\r\n   \r\n3. **更新失败（nodeNotExist 等）**: \r\n   - 尝试传入真实的 `docKey`：`update-content --nodeId=xxx --docKey=真实 docKey --content=\"...\"`\r\n   - createDoc 返回的 `docKey` 和 `nodeId` 可能不同\r\n\r\n4. **nodeId 和 docKey 到底有什么区别？**\r\n   - `nodeId`: 目录树节点 ID（wiki_2.0 API 用），用于定位文档、获取 workspaceId 和节点名、执行白名单检查\r\n   - `docKey`: 文档内容标识符（suites/documents API 用），用于实际读写内容\r\n   - 经验：大多数情况下 `nodeId` 可直接用作 `docKey`，少数情况需要真实 `docKey`\r\n\r\n5. **为什么没有 append-text？**\r\n   - 当前公开接口 `POST /v1.0/doc/suites/documents/{docKey}/paragraphs/{blockId}/text` 在真实测试中返回 `InvalidAction.NotFound`\r\n   - 因此本 skill 不再承诺 `append-text`，请使用 `insert-block` 或 `modify-block`\r\n\r\n## 推荐流程\r\n\r\n1. 先确认凭证和 sender_id 是否可用。\r\n2. 需要定位文档所在知识库时先跑 `list-workspaces`。\r\n3. 需要定位文档时先跑 `list-docs`、`search`、`get-doc`。\r\n4. 执行写操作前，默认假设会触发白名单校验，不要跳过读取确认步骤。\r\n5. 如果写入被拒绝，只说明是哪个 workspace / 节点名未通过白名单，并提示用户手动调整 `config/whitelist.json`。\r\n\r\n## 常见失败\r\n\r\n- 缺少 sender_id:检查钉钉连接器是否注入 `OPENCLAW_SENDER_ID` / `DINGTALK_SENDER_ID`\r\n- `forbidden.accessDenied`:检查应用权限或白名单\r\n- `invalidRequest.workspaceNode.parentNotFound`:检查 `parentNodeId`\r\n- `权限拒绝：知识库 xxx 未配置白名单`:让用户手动补充 `config/whitelist.json`\r\n- `nodeNotExist`(更新内容时):**尝试使用 nodeId 代替 docKey** - 钉钉 API 中 createDoc 返回的 docKey 和 overwriteContent 需要的 docKey 可能不一致\r\n- `blockNotExist`(块级操作时):先用 `get-content` 获取文档结构，确认 blockId 正确\r\n- `paramError`(modify-block/insert-block):检查 `--element` 参数是否是合法的 JSON 格式\r\n\r\n## 重要提示\r\n\r\n### get-doc vs get-content\r\n\r\n| 命令 | 用途 | 返回内容 | 何时使用 |\r\n|------|------|---------|---------|\r\n| `get-doc` | 获取**文档元数据** | `data.node`：名称、ID、创建者、修改时间、字数、workspaceId | 确认文档存在、获取文档基本信息、定位知识库 |\r\n| `get-content` | 获取**文档正文** | `data.result.data[]`：段落、标题、列表等块结构 | 总结内容、读取正文、准备修改文档 |\r\n\r\n**关键区别：**\r\n- `get-doc` → `GET /v2.0/wiki/nodes/{nodeId}` → **不包含正文内容**\r\n- `get-content` → `GET /v1.0/doc/suites/documents/{docKey}/blocks` → **包含正文块结构**\r\n- **总结、读取内容时，始终使用 `get-content`，不要用 `get-doc`**\r\n\r\n### docKey vs nodeId\r\n\r\n- `nodeId`: 目录树节点 ID（wiki_2.0 API 用），用于定位文档、获取 workspaceId 和节点名、执行白名单检查\r\n- `docKey`: 文档内容标识符（suites/documents API 用），用于实际读写内容\r\n- `create-doc` 返回的 `docKey` 和 `nodeId` 可能是不同的值\r\n- `update-content`、`get-content` 等命令**优先使用 `nodeId`** 作为 `--docKey` 参数\r\n- 如果使用 `docKey` 更新失败 (`nodeNotExist`),请改用 `nodeId`\r\n- 经验：大多数情况下 `nodeId` 可直接用作 `docKey`，少数情况需要真实 `docKey`\r\n\r\n**示例:**\r\n\r\n```bash\r\n# 创建文档后，使用返回的 nodeId 进行更新\r\nnode .../index.js create-doc --workspaceId=xxx --name=\"新文档\"\r\n# 返回:{ \"docKey\": \"abc123\", \"nodeId\": \"xyz789\", ... }\r\n\r\n# ✅ 正确：使用 nodeId 更新\r\nnode .../index.js update-content --docKey=xyz789 --content=\"...\"\r\n\r\n# ❌ 可能失败：使用 docKey 更新\r\nnode .../index.js update-content --docKey=abc123 --content=\"...\"\r\n```\r\n\r\n## 参考\r\n\r\n- 详细说明见 `README.md`\r\n- 钉钉开放平台文档:[knowledge-base-overview](https://open.dingtalk.com/document/development/knowledge-base-overview)\r\n- API Explorer:[open-dev.dingtalk.com/apiExplorer](https://open-dev.dingtalk.com/apiExplorer)\n\nFile v1.0.7:README.md\n\n# 钉钉文档管理技能 (dingtalk-doc)\r\n\r\n> [https://clawhub.ai/shyzhen/dingtalk-doc](https://clawhub.ai/shyzhen/dingtalk-doc)\r\n> \r\n> 是 [dingtalk-doc-enterprise](https://clawhub.ai/shyzhen/dingtalk-doc-enterprise) 的升级版(支持写入范围控制)\r\n\r\n通过钉钉开放平台 API 读取和管理钉钉文档、钉钉知识库中的文档。本文件面向人类读者，重点说明配置、上手方式和排障思路；执行规则与触发策略见 `SKILL.md`。\r\n\r\n## 能力概览\r\n\r\n- 支持列出、搜索、读取钉钉文档\r\n- 支持创建、更新、删除钉钉文档\r\n- 支持多个 workspace 共用一份白名单配置\r\n- 写入操作按 workspace + 节点名白名单控制，读取操作不受白名单限制\r\n- 当前用户身份从钉钉连接器注入的 `sender_id` 自动解析\r\n\r\n## 文档分工\r\n\r\n- `SKILL.md`：给 agent 的执行手册，强调触发条件、命令映射和失败处理\r\n- `README.md`：给人的配置说明、背景信息、排障参考\r\n\r\n## 快速开始\r\n\r\n### 1. 配置环境变量\r\n\r\n本 skill 可运行在 Windows、Linux、macOS；核心要求只有：\r\n\r\n- 安装 `node`\r\n- 配置 `DINGTALK_CLIENTID` / `DINGTALK_CLIENTSECRET`\r\n\r\n**编辑 `~/.openclaw/.env` 文件（推荐，优先级最高）**\r\n\r\nOpenClaw 会在启动时自动加载此文件中的环境变量。\r\n\r\n```bash\r\n# ~/.openclaw/.env\r\nDINGTALK_CLIENTID=dingxxxxxx\r\nDINGTALK_CLIENTSECRET=your_secret\r\n```\r\n\r\n说明：\r\n\r\n- `DINGTALK_CLIENTID`：钉钉应用 Client ID\r\n- `DINGTALK_CLIENTSECRET`：钉钉应用 Client Secret\r\n- `OPENCLAW_SENDER_ID` / `DINGTALK_SENDER_ID`：通常由 OpenClaw / 钉钉连接器自动注入；如需手工调用 CLI，也可以用 `--senderId=...`\r\n- `DINGTALK_DEBUG=true`：可选，仅输出请求方法、接口路径、状态码、requestId 等调试信息，不再打印文档正文和请求体\r\n- `operatorId` 不需要手工配置，脚本会优先从 `--senderId` 或 `OPENCLAW_SENDER_ID` / `DINGTALK_SENDER_ID` 解析当前钉钉用户\r\n\r\n### 2. 确认 workspaceId\r\n\r\n- 白名单配置：只支持 【钉钉文档 -> 知识库】下新建的根目录，也就是说可写范围必须在【知识库】中。\r\n- 直接发一篇文档给你的钉钉机器人让他随便写入一段话，就会触发白名单检查，机器人会主动告知你当前文档的workspaceId，以及如何配置。\r\n\r\n### 3. 配置白名单\r\n\r\n说明：\r\n\r\n- 读取操作不依赖白名单文件\r\n- 写入操作才需要 `config/whitelist.json`\r\n- 如果该文件不存在，写操作会被拒绝，并提示用户手动创建\r\n\r\n在 `config/whitelist.json` 中声明允许写入的 workspace 和节点名：\r\n\r\n```json\r\n{\r\n  \"workspaces\": [\r\n    {\r\n      \"workspaceId\": \"eLvJDSRX3l4moO87\",\r\n      \"workspaceName\": \"AI可写知识库一级目录\",\r\n      \"allowRootWrite\": false,\r\n      \"whitelist\": [\"/\"]\r\n    }\r\n  ]\r\n}\r\n```\r\n\r\n字段说明：\r\n\r\n- `workspaceId`：知识库 ID，必填 （只支持【钉钉文档 -> 知识库】下的目录）\r\n- `workspaceName`：可选，便于识别\r\n- `allowRootWrite`：保留字段；当前写入校验主要使用 `whitelist`\r\n- `whitelist`：允许写入的节点名（文档名）列表\r\n\r\n白名单规则：\r\n\r\n- 读取操作不检查白名单\r\n- 写入操作必须命中白名单\r\n- 未配置的 workspace 默认禁止写入\r\n- `\"/\"` 表示允许写入当前 `workspaceId` 下的所有节点\r\n- 细粒度白名单按节点名匹配，例如 `\"/三级文档.adoc\"`；当前已验证的节点详情接口不会返回完整父目录路径\r\n\r\n## 常用命令\r\n\r\n这里只保留几个面向人的常见示例；完整命令映射见 `SKILL.md`。\r\n\r\n读取：\r\n\r\n```bash\r\n# 当前目录运行（跨平台通用）\r\nnode scripts/index.js list-workspaces\r\nnode scripts/index.js list-docs --workspaceId=OQ0xySj6ng7lX58B --parentNodeId=root\r\nnode scripts/index.js get-doc --nodeId=YQBnd5ExVE0PDnezU2PK2RK6WyeZqMmz\r\nnode scripts/index.js get-content --nodeId=YQBnd5ExVE0PDnezU2PK2RK6WyeZqMmz\r\nnode scripts/index.js get-content --docKey=真实docKey --nodeId=YQBnd5ExVE0PDnezU2PK2RK6WyeZqMmz\r\n```\r\n\r\n写入：\r\n\r\n```bash\r\n# 当前目录运行（跨平台通用）\r\nnode scripts/index.js create-doc --workspaceId=OQ0xySj6ng7lX58B --name=\"新文档\" --docType=DOC --parentNodeId=root\r\nnode scripts/index.js update-content --nodeId=YQBnd5ExVE0PDnezU2PK2RK6WyeZqMmz --content=\"# 标题\\n\\n内容\"\r\nnode scripts/index.js update-content --docKey=真实docKey --nodeId=YQBnd5ExVE0PDnezU2PK2RK6WyeZqMmz --content=\"# 标题\\n\\n内容\"\r\n```\r\n\r\n白名单自检：\r\n\r\n```bash\r\n# 当前目录运行（跨平台通用）\r\nnode scripts/whitelist.js check \"/三级文档.adoc\"\r\n```\r\n\r\n`docType` 支持：\r\n\r\n- `DOC`\r\n- `WORKBOOK`\r\n- `MIND`\r\n- `FOLDER`\r\n\r\n## 目录结构\r\n\r\n```text\r\ndingtalk-doc/\r\n├── SKILL.md\r\n├── README.md\r\n├── package.json\r\n├── config/\r\n│   └── whitelist.json\r\n└── scripts/\r\n    ├── index.js\r\n    ├── dingtalk-client.js\r\n    └── whitelist.js\r\n```\r\n\r\n## 已知返回结构\r\n\r\n以下结构来自实测文档 `https://alidocs.dingtalk.com/i/nodes/oP0MALyR8kOd5BGacKv6NbxE83bzYmDO?utm_scene=team_space`，文档名是“三级文档”，真实位于三级子目录中。\r\n\r\n`get-doc --nodeId=...` / `GET /v2.0/wiki/nodes/{nodeId}` 返回的 `node` 里有 `workspaceId` 和 `name`，但没有返回完整父目录链路：\r\n\r\n```json\r\n{\r\n  \"node\": {\r\n    \"name\": \"三级文档.adoc\",\r\n    \"nodeId\": \"oP0MALyR8kOd5BGacKv6NbxE83bzYmDO\",\r\n    \"workspaceId\": \"eLvJDSRX3l4moO87\",\r\n    \"type\": \"FILE\",\r\n    \"category\": \"ALIDOC\",\r\n    \"url\": \"https://alidocs.dingtalk.com/i/nodes/oP0MALyR8kOd5BGacKv6NbxE83bzYmDO\"\r\n  }\r\n}\r\n```\r\n\r\n容易犯错的点：\r\n\r\n- 不要把白名单理解为真实目录路径；当前脚本只能可靠使用 `workspaceId + node.name` 做写入白名单。\r\n- 即使文档在三级子目录中，节点详情也可能只返回 `\"name\": \"三级文档.adoc\"`，不会返回 `\"/一级/二级/三级文档.adoc\"`。\r\n- `get-content --nodeId=...` / `GET /v1.0/doc/suites/documents/{docKey}/blocks` 返回的是块结构，例如 paragraph 的 `id`、`index`、`text`，不返回文档路径或父目录信息。\r\n- `nodeId` 用于查询节点详情和校验真实 `workspaceId`；`docKey` 用于 suites/documents 内容读写接口。多数情况下可先用 `nodeId` 作为 `docKey` 尝试。\r\n- `insert-block`、`modify-block`、`delete-block` 已在真实文档上测试通过；`append-text` 对应的 `paragraphs/{blockId}/text` 当前返回 `InvalidAction.NotFound`，不要依赖它。\r\n\r\n## 权限与接口\r\n\r\n常用接口：\r\n\r\n- `POST /v1.0/doc/workspaces/{workspaceId}/docs`\r\n- `GET /v2.0/wiki/nodes`\r\n- `GET /v2.0/wiki/nodes/{nodeId}`\r\n- `DELETE /v1.0/doc/workspaces/{workspaceId}/docs/{nodeId}`\r\n- `GET /v1.0/doc/suites/documents/{docKey}/blocks`\r\n- `POST /v1.0/doc/suites/documents/{docKey}/overwriteContent`\r\n- `POST /v1.0/doc/suites/documents/{docKey}/blocks`\r\n- `PUT /v1.0/doc/suites/documents/{docKey}/blocks/{blockId}`\r\n- `DELETE /v1.0/doc/suites/documents/{docKey}/blocks/{blockId}`\r\n\r\n常见所需权限：\r\n\r\n- `Document.Workspace.Read`\r\n- `Document.Workspace.Write`\r\n- `Document.WorkspaceDocument.Read`\r\n- `Document.WorkspaceDocument.Write`\r\n- `Wiki.Node.Read`\r\n\r\n## 常见问题\r\n\r\n| 问题 | 原因 | 处理方式 |\r\n|------|------|---------|\r\n| 缺少 `sender_id` | 钉钉连接器未传递当前用户 ID | 检查是否注入了 `OPENCLAW_SENDER_ID` 或 `DINGTALK_SENDER_ID` |\r\n| workspace 未配置白名单 | `config/whitelist.json` 没有对应 workspace | 补充对应 `workspaceId` 配置 |\r\n| 节点名不在白名单内 | 写入目标节点名未命中规则 | 调整白名单节点名或使用 `\"/\"` 允许整个 workspace |\r\n| `forbidden.accessDenied` | 应用权限不足或白名单不通过 | 检查钉钉应用权限和白名单 |\r\n| `invalidRequest.workspaceNode.parentNotFound` | `parentNodeId` 错误 | 重新确认父节点 ID |\r\n\r\n## 参考\r\n\r\n- 钉钉开放平台文档：[knowledge-base-overview](https://open.dingtalk.com/document/development/knowledge-base-overview)\r\n- API Explorer：[open-dev.dingtalk.com/apiExplorer](https://open-dev.dingtalk.com/apiExplorer)\r\n\r\n### API 参考\r\n\r\n钉钉知识库 API 分为两个系列：\r\n\r\n#### doc_1.0 - 知识库管理\r\n\r\n| API | 方法 | 路径 | 权限 |\r\n|-----|------|------|------|\r\n| 新建知识库 | POST | `/v1.0/doc/workspaces` | Document.Workspace.Write |\r\n| 获取知识库 | GET | `/v1.0/doc/workspaces/{workspaceId}` | Document.Workspace.Read |\r\n| 获取知识库列表 | GET | `/v1.0/doc/workspaces` | Document.Workspace.Read |\r\n| 创建知识库文档 | POST | `/v1.0/doc/workspaces/{workspaceId}/docs` | Document.WorkspaceDocument.Write |\r\n| 删除知识库文档 | DELETE | `/v1.0/doc/workspaces/{workspaceId}/docs/{nodeId}` | Document.WorkspaceDocument.Write |\r\n\r\n#### wiki_2.0 - 目录树管理\r\n\r\n| API | 方法 | 路径 | 权限 |\r\n|-----|------|------|------|\r\n| 获取节点 | GET | `/v2.0/wiki/nodes/{nodeId}` | Wiki.Node.Read |\r\n| 获取节点列表 | GET | `/v2.0/wiki/nodes` | Wiki.Node.Read |\r\n| 通过链接获取节点 | GET | `/v2.0/wiki/nodes/url` | Wiki.Node.Read |\r\n| 复制文档 | POST | `/v2.0/wiki/nodes/{nodeId}/copy` | Document.WorkspaceDocument.Write |\r\n\r\n#### suites/documents - 文档内容\r\n\r\n| API | 方法 | 路径 | 权限 |\r\n|-----|------|------|------|\r\n| 查询块结构 | GET | `/v1.0/doc/suites/documents/{docKey}/blocks` | Document.WorkspaceDocument.Read |\r\n| 覆写内容 | POST | `/v1.0/doc/suites/documents/{docKey}/overwriteContent` | Document.WorkspaceDocument.Write |\r\n| 插入块 | POST | `/v1.0/doc/suites/documents/{docKey}/blocks` | Document.WorkspaceDocument.Write |\r\n| 删除块 | DELETE | `/v1.0/doc/suites/documents/{docKey}/blocks/{blockId}` | Document.WorkspaceDocument.Write |\r\n\r\n\r\n#### 所需权限清单\r\n\r\n调用本 skill 需要开通以下钉钉应用权限：\r\n\r\n- `Document.Workspace.Read` - 知识库读权限\r\n- `Document.Workspace.Write` - 知识库写权限\r\n- `Document.WorkspaceDocument.Read` - 文档读权限\r\n- `Document.WorkspaceDocument.Write` - 文档写权限\r\n- `Wiki.Node.Read` - 节点读权限\r\n\r\n## 许可证\r\n\r\nMIT\n\nFile v1.0.7:_meta.json\n\n{\n  \"ownerId\": \"kn73t8gpykqmgx10ksfb6706r5842gge\",\n  \"slug\": \"dingtalk-doc\",\n  \"version\": \"1.0.7\",\n  \"publishedAt\": 1780480336313\n}\n\nFile v1.0.7:skill-card.md\n\n## Description: <br>\nManages DingTalk documents and DingTalk knowledge-base documents through DingTalk Open Platform APIs, including reading, summarizing, creating, updating, and deleting content. <br>\n\nThis skill is ready for commercial/non-commercial use. <br>\n\n## Publisher: <br>\n[shyzhen](https://clawhub.ai/user/shyzhen) <br>\n\n### License/Terms of Use: <br>\nMIT-0 <br>\n\n\n## Use Case: <br>\nDevelopers and operators use this skill to let an agent read, summarize, search, create, update, and delete DingTalk workspace documents when the user provides a DingTalk document link or an established DingTalk document context. <br>\n\n### Deployment Geography for Use: <br>\nGlobal <br>\n\n## Known Risks and Mitigations: <br>\nRisk: Write operations can overwrite or delete DingTalk documents. <br>\nMitigation: Use a least-privilege DingTalk app, confirm the target workspace and document before write/delete commands, and keep write access limited by whitelist. <br>\nRisk: A workspace-wide '/' whitelist can allow writes across an entire DingTalk workspace. <br>\nMitigation: Use specific document-name whitelist entries unless broad workspace write access is intentional and reviewed. <br>\nRisk: The direct DingTalk client path can bypass the advertised whitelist guardrail if exposed as an agent-callable entrypoint. <br>\nMitigation: Expose the guarded scripts/index.js entrypoint, or remove or wrap direct write/delete commands in scripts/dingtalk-client.js with the same whitelist and confirmation checks. <br>\n\n\n## Reference(s): <br>\n- [ClawHub skill page](https://clawhub.ai/shyzhen/dingtalk-doc) <br>\n- [DingTalk knowledge-base overview](https://open.dingtalk.com/document/development/knowledge-base-overview) <br>\n- [DingTalk API Explorer](https://open-dev.dingtalk.com/apiExplorer) <br>\n\n\n## Skill Output: <br>\n**Output Type(s):** [text, markdown, shell commands, configuration, guidance] <br>\n**Output Format:** [Markdown guidance with inline shell commands and JSON examples] <br>\n**Output Parameters:** [1D] <br>\n**Other Properties Related to Output:** [Requires Node.js, DingTalk app credentials, and a configured write whitelist; write commands can modify or delete remote DingTalk documents.] <br>\n\n## Skill Version(s): <br>\n1.0.7 (source: server release evidence) <br>\n\n## Ethical Considerations: <br>\nUsers should evaluate whether this skill is appropriate for their environment, review any generated or modified files before relying on them, and apply their organization's safety, security, and compliance requirements before deployment. <br>\n\nFile v1.0.7:config/whitelist.json\n\n{\n  \"_comment\": \"⚠️ 此配置文件只能由用户手动编辑，AI 禁止修改！\",\n  \"workspaces\": [\n    {\n      \"workspaceId\": \"eLvJDSRX3l4moO87\",\n      \"workspaceName\": \"AI可写知识库一级目录\",\n      \"allowRootWrite\": true,\n      \"whitelist\": [\"/\"]\n    }\n  ]\n}\n\nFile v1.0.7:package.json\n\n{\n  \"name\": \"dingtalk-doc\",\n  \"version\": \"1.0.6\",\n  \"description\": \"钉钉文档管理技能 - 支持文档增删改查和白名单权限控制\",\n  \"main\": \"scripts/index.js\",\n  \"scripts\": {\n    \"get-token\": \"node scripts/dingtalk-client.js get-token\",\n    \"get-workspace-list\": \"node scripts/dingtalk-client.js get-workspace-list\",\n    \"list-docs\": \"node scripts/index.js list-docs\",\n    \"create-doc\": \"node scripts/index.js create-doc\",\n    \"delete-doc\": \"node scripts/index.js delete-doc\",\n    \"search\": \"node scripts/index.js search\",\n    \"get-content\": \"node scripts/index.js get-content\",\n    \"update-content\": \"node scripts/index.js update-content\",\n    \"check-permission\": \"node scripts/whitelist.js check\"\n  },\n  \"keywords\": [\n    \"dingtalk\",\n    \"knowledge-base\",\n    \"openclaw-skill\"\n  ],\n  \"author\": \"\",\n  \"license\": \"MIT\"\n}\n\nArchive v1.0.6: 9 files, 26974 bytes\n\nFiles: config/whitelist.json (278b), package.json (837b), README.md (10156b), scripts/dingtalk-client.js (20641b), scripts/index.js (30897b), scripts/whitelist.js (5086b), skill-card.md (2427b), SKILL.md (18086b), _meta.json (131b)\n\nFile v1.0.6:SKILL.md\n\n---\r\nname: dingtalk-doc\r\ndescription: 钉钉文档管理技能。当用户发送的消息中包含 alidocs.dingtalk.com 链接、要求总结/读取/查看/更新钉钉文档或钉钉知识库文档，或当前上下文已明确对象是钉钉文档时使用。关键词：钉钉文档、钉钉知识库、alidocs、总结、读取、查看、更新、修改、文档、链接。\r\nmetadata:\r\n  {\r\n    \"openclaw\":\r\n      {\r\n        \"emoji\": \"📚\",\r\n        \"requires\":\r\n          {\r\n            \"bins\": [\"node\"],\r\n            \"env\": [\"DINGTALK_CLIENTID\", \"DINGTALK_CLIENTSECRET\"],\r\n          },\r\n      },\r\n  }\r\n---\r\n\r\n# 钉钉文档管理技能\r\n\r\n通过钉钉开放平台 API 管理钉钉文档与钉钉知识库内文档。`SKILL.md` 只保留 agent 执行所需规则;配置细节、示例、API 背景见 `README.md`。\r\n\r\n## 何时使用\r\n\r\n### 触发关键词\r\n\r\n只有在**已经确认对象是钉钉文档**时，同时消息包含以下**任一关键词**时，优先使用本 skill：\r\n\r\n| 类别 | 关键词 |\r\n|------|--------|\r\n| **平台名** | 钉钉文档、钉钉知识库、alidocs |\r\n| **读取类** | 总结、读取、查看、浏览、列出结构 |\r\n| **修改类** | 更新、修改、追加、删除、覆写 |\r\n| **对象** | 文档、链接、这篇、这个文档 |\r\n\r\n**组合示例：**\r\n- \"总结一下这篇钉钉文档\"\r\n- \"读取这个 alidocs 链接\"\r\n- \"更新文档内容\"\r\n- \"删除第三段\"\r\n\r\n### 触发场景（优先级从高到低）\r\n\r\n| 场景 | 示例 | 动作 |\r\n|------|------|------|\r\n| **钉钉文档链接** | `alidocs.dingtalk.com/i/nodes/xxx` | 根据意图选择：元数据→`get-doc`，正文内容→`get-content` |\r\n| **钉钉上下文 + 链接** | \"总结 https://alidocs.dingtalk.com/...\" | 调用 `get-content` 读取内容后总结 |\r\n| **明确命令** | \"总结这篇文档\"、\"读取这个 alidocs 链接\" | 根据意图选择命令 |\r\n| **已知上下文是钉钉文档** | 前文已给出 alidocs 链接，后续说\"更新文档\"、\"删除某段\"、\"在第三段后追加\" | 调用对应命令 |\r\n| **结构查询** | \"列出结构\"、\"这个 alidocs 有哪些章节\" | 调用 `get-content` |\r\n| **块级操作** | \"删除第三段\"、\"修改这个段落\"、\"在这里插入一段\" | 先用 `get-content` 获取 blockId 和位置，再调用 `delete-block`/`modify-block`/`insert-block` |\r\n\r\n### 不触发的场景\r\n- 查询本地文件、离线文档或普通文本内容，且不需要调用钉钉 API\r\n- 没有文档链接、docKey、或明确钉钉文档上下文，却要求修改文档\r\n- 用户只说\"总结文档\"\"更新这个链接\"等泛化请求，但上下文无法确认对象是钉钉文档\r\n- 与钉钉无关的文档系统，例如本地 Markdown、飞书文档、语雀、Google Docs\r\n\r\n## 运行前提\r\n\r\n### 环境变量\r\n\r\n必须配置以下环境变量 (在 Gateway 环境中):\r\n\r\n- `DINGTALK_CLIENTID` - 钉钉应用 Client ID (AppKey)\r\n- `DINGTALK_CLIENTSECRET` - 钉钉应用 Client Secret (AppSecret)\r\n\r\n可选环境变量:\r\n\r\n- `OPENCLAW_SENDER_ID` / `DINGTALK_SENDER_ID` - 由 OpenClaw / 钉钉连接器注入的当前用户 sender_id；也可以通过命令行 `--senderId=` 显式传入\r\n- `DINGTALK_DEBUG` - 设置为 `true` 启用调试模式；仅输出方法、路径（查询参数已脱敏）、状态码、requestId 等，不打印文档正文与完整请求体\r\n\r\n### operatorId 获取方式\r\n\r\n不需要在配置中指定!系统会自动从当前会话获取:\r\n\r\n1. 从 `OPENCLAW_SENDER_ID` 或 `DINGTALK_SENDER_ID` 获取 sender_id\r\n2. 调用钉钉 API 查询对应的 unionId\r\n3. 使用 unionId 作为 operatorId\r\n\r\n如果获取失败，会显示友好的错误提示。\r\n\r\n## 执行规则\r\n\r\n- 读取操作不受白名单限制\r\n- 写入操作必须通过白名单检查;未配置 workspace 或节点名不在白名单内时，一律拒绝!**没有任何方式可以绕过白名单检查!**\r\n- `whitelist: [\"/\"]` 表示允许写入该 workspace 下的所有节点;更细粒度控制请配置具体文档名，例如 `\"/三级目录测试文档.adoc\"`\r\n- 白名单配置文件 `config/whitelist.json` 只能由用户手动修改;AI 只能读取、解释、提示用户手动调整，不能替用户改\r\n- 如果用户没有给出 `workspaceId`,先运行 `list-workspaces`\r\n- 如果用户没有给出目标文档的 `nodeId` / `docKey`,先运行 `list-docs`、`search` 或 `get-doc` 确认目标\r\n\r\n## URL 解析规则\r\n\r\n当用户提供钉钉文档 URL 时，按以下规则提取 `nodeId`:\r\n\r\n**URL 格式:** `https://alidocs.dingtalk.com/i/nodes/<nodeId>?...`\r\n\r\n**提取方法:**\r\n1. 从 URL 中提取 `nodes/` 和 `?` 之间的部分\r\n2. 该部分即为 `nodeId`\r\n\r\n**示例:**\r\n```\r\nURL: https://alidocs.dingtalk.com/i/nodes/oP0MALyR8kOd5BGacKv6NbxE83bzYmDO?utm_scene=team_space\r\nnodeId: oP0MALyR8kOd5BGacKv6NbxE83bzYmDO\r\n```\r\n\r\n**提取到 nodeId 后的操作:**\r\n1. **获取文档元数据**（名称、知识库 ID 等）：`get-doc --nodeId=<提取的 nodeId>`\r\n2. **获取文档内容**（总结、读取正文）：`get-content --docKey=<提取的 nodeId>`\r\n3. 注意：`get-doc` 只返回元数据，`get-content` 才返回正文内容\r\n\r\n## 执行入口\r\n\r\n- `scripts/index.js`:主入口\r\n- `scripts/whitelist.js`:辅助检查白名单配置\r\n\r\n**跨平台说明：**\r\n\r\n- 本 skill 的脚本基于 Node.js 内置模块实现，Windows、Linux、macOS 只要安装了 `node` 并配置好环境变量，都可以运行\r\n- 推荐优先使用相对路径执行：`node scripts/index.js ...`，这样最不容易受平台路径差异影响\r\n\r\n**路径示例：**\r\n\r\nWindows PowerShell 中 `~` 不会自动展开，建议使用以下方式之一：\r\n\r\n```bash\r\n# ✅ 使用 $env:USERPROFILE\r\nnode $env:USERPROFILE\\.openclaw\\skills\\dingtalk-doc\\scripts\\index.js\r\n\r\n# ✅ 或使用完整绝对路径\r\nnode C:\\Users\\zhenhuaixiu\\.openclaw\\skills\\dingtalk-doc\\scripts\\index.js\r\n\r\n# ❌ 错误（~ 不会展开）\r\nnode ~/.openclaw/skills/dingtalk-doc/scripts/index.js\r\n```\r\n\r\nLinux / macOS Shell 示例：\r\n\r\n```bash\r\n# ✅ 当前目录下直接运行（推荐）\r\nnode scripts/index.js list-workspaces\r\n\r\n# ✅ 或使用完整绝对路径\r\nnode ~/.openclaw/skills/dingtalk-doc/scripts/index.js list-workspaces\r\n```\r\n\r\n## 命令映射\r\n\r\n**路径说明：**\r\n\r\n- Windows 示例中的 `$env:USERPROFILE\\.openclaw` 会展开为 `C:\\Users\\<用户名>\\.openclaw`\r\n- Linux / macOS 示例中的 `~/.openclaw` 会展开为用户主目录下的 `.openclaw`\r\n- 如果当前工作目录已经在 skill 根目录，直接使用 `node scripts/index.js ...` 即可\r\n\r\n### 读取操作\r\n\r\n| 命令 | 用途 | API 端点 |\r\n|------|------|---------|\r\n| `list-workspaces` | 获取知识库列表 | `GET /v2.0/wiki/mineWorkspaces` |\r\n| `list-docs` | 获取知识库中文档列表 | `GET /v2.0/wiki/nodes` |\r\n| `get-doc` | 获取**文档元数据**（名称、ID、创建者、字数等） | `GET /v2.0/wiki/nodes/{nodeId}` |\r\n| `get-content` | 获取**文档正文内容**（段落、标题、列表等块结构） | `GET /v1.0/doc/suites/documents/{docKey}/blocks` |\r\n| `search` | 搜索文档 | `GET /v1.0/doc/workspaces/{workspaceId}/docs` |\r\n\r\n**示例：**\r\n```bash\r\n# Linux / macOS\r\nnode ~/.openclaw/skills/dingtalk-doc/scripts/index.js list-workspaces\r\nnode ~/.openclaw/skills/dingtalk-doc/scripts/index.js list-docs --workspaceId=YRBGvyxxx --parentNodeId=root\r\nnode ~/.openclaw/skills/dingtalk-doc/scripts/index.js get-doc --nodeId=YQBnd5ExVE0PDnezU2PK2RK6WyeZqMmz\r\nnode ~/.openclaw/skills/dingtalk-doc/scripts/index.js get-content --docKey=YQBnd5ExVE0PDnezU2PK2RK6WyeZqMmz\r\nnode ~/.openclaw/skills/dingtalk-doc/scripts/index.js search --workspaceId=YRBGvyxxx --keyword=\"需求\"\r\n\r\n# Windows PowerShell\r\nnode $env:USERPROFILE\\.openclaw\\skills\\dingtalk-doc\\scripts\\index.js list-workspaces\r\nnode $env:USERPROFILE\\.openclaw\\skills\\dingtalk-doc\\scripts\\index.js list-docs --workspaceId=YRBGvyxxx --parentNodeId=root\r\nnode $env:USERPROFILE\\.openclaw\\skills\\dingtalk-doc\\scripts\\index.js get-doc --nodeId=YQBnd5ExVE0PDnezU2PK2RK6WyeZqMmz\r\nnode $env:USERPROFILE\\.openclaw\\skills\\dingtalk-doc\\scripts\\index.js get-content --docKey=YQBnd5ExVE0PDnezU2PK2RK6WyeZqMmz\r\nnode $env:USERPROFILE\\.openclaw\\skills\\dingtalk-doc\\scripts\\index.js search --workspaceId=YRBGvyxxx --keyword=\"需求\"\r\n```\r\n\r\n### 写入操作\r\n\r\n```bash\r\n# Linux / macOS\r\nnode ~/.openclaw/skills/dingtalk-doc/scripts/index.js create-doc --workspaceId=YRBGvyxxx --name=\"新文档\" --docType=DOC --parentNodeId=root\r\nnode ~/.openclaw/skills/dingtalk-doc/scripts/index.js update-content --nodeId=YQBnd5ExVE0PDnezU2PK2RK6WyeZqMmz --content=\"# 标题\\n\\n内容\"\r\nnode ~/.openclaw/skills/dingtalk-doc/scripts/index.js update-content --nodeId=YQBnd5ExVE0PDnezU2PK2RK6WyeZqMmz --docKey=真实docKey --content=\"# 标题\\n\\n内容\"\r\nnode ~/.openclaw/skills/dingtalk-doc/scripts/index.js delete-doc --workspaceId=YRBGvyxxx --nodeId=YQBnd5ExVE0PDnezU2PK2RK6WyeZqMmz\r\n\r\n# Windows PowerShell\r\n# 创建文档\r\nnode $env:USERPROFILE\\.openclaw\\skills\\dingtalk-doc\\scripts\\index.js create-doc --workspaceId=YRBGvyxxx --name=\"新文档\" --docType=DOC --parentNodeId=root\r\n# 返回：{ \"docKey\": \"abc123\", \"nodeId\": \"xyz789\", ... }\r\n\r\n# 更新文档内容（整篇覆写，替换全部内容）\r\n# ✅ 推荐：只用 nodeId（大多数情况够用）\r\nnode $env:USERPROFILE\\.openclaw\\skills\\dingtalk-doc\\scripts\\index.js update-content --nodeId=YQBnd5ExVE0PDnezU2PK2RK6WyeZqMmz --content=\"# 标题\\n\\n内容\"\r\n\r\n# ✅ 备选：如果上面失败，传入真实的 docKey\r\nnode $env:USERPROFILE\\.openclaw\\skills\\dingtalk-doc\\scripts\\index.js update-content --nodeId=YQBnd5ExVE0PDnezU2PK2RK6WyeZqMmz --docKey=真实 docKey --content=\"# 标题\\n\\n内容\"\r\n\r\n# 删除文档\r\nnode $env:USERPROFILE\\.openclaw\\skills\\dingtalk-doc\\scripts\\index.js delete-doc --workspaceId=YRBGvyxxx --nodeId=YQBnd5ExVE0PDnezU2PK2RK6WyeZqMmz\r\n```\r\n\r\n### 块级操作（精细修改单个段落/元素）\r\n\r\n说明：\r\n\r\n- `insert-block`、`modify-block`、`delete-block` 已通过真实文档测试\r\n- `append-text` 对应的公开 API 当前返回 `InvalidAction.NotFound`，不要再调用或承诺\r\n- 如果用户要“追加内容”，优先改成“插入一个新段落”或“读取原段落后使用 `modify-block` 整块替换”\r\n\r\n```bash\r\n# Linux / macOS\r\nnode ~/.openclaw/skills/dingtalk-doc/scripts/index.js delete-block --nodeId=YQBnd5ExVE0PDnezU2PK2RK6WyeZqMmz --blockId=blk123\r\nnode ~/.openclaw/skills/dingtalk-doc/scripts/index.js modify-block --nodeId=YQBnd5ExVE0PDnezU2PK2RK6WyeZqMmz --blockId=blk123 --element='{\"blockType\":\"paragraph\",\"paragraph\":{\"text\":\"新内容\"}}'\r\nnode ~/.openclaw/skills/dingtalk-doc/scripts/index.js insert-block --nodeId=YQBnd5ExVE0PDnezU2PK2RK6WyeZqMmz --element='{\"blockType\":\"paragraph\",\"paragraph\":{\"text\":\"插入的内容\"}}' --position=3\r\n\r\n# Windows PowerShell\r\n# 删除块元素（删除某个段落/标题/列表项）\r\nnode $env:USERPROFILE\\.openclaw\\skills\\dingtalk-doc\\scripts\\index.js delete-block --nodeId=YQBnd5ExVE0PDnezU2PK2RK6WyeZqMmz --blockId=blk123\r\n\r\n# 修改块元素（替换单个块的内容，不影响其他部分）\r\nnode $env:USERPROFILE\\.openclaw\\skills\\dingtalk-doc\\scripts\\index.js modify-block --nodeId=YQBnd5ExVE0PDnezU2PK2RK6WyeZqMmz --blockId=blk123 --element='{\"blockType\":\"paragraph\",\"paragraph\":{\"text\":\"新内容\"}}'\r\n\r\n# 插入块元素（在指定位置插入新段落/标题等）\r\nnode $env:USERPROFILE\\.openclaw\\skills\\dingtalk-doc\\scripts\\index.js insert-block --nodeId=YQBnd5ExVE0PDnezU2PK2RK6WyeZqMmz --element='{\"blockType\":\"paragraph\",\"paragraph\":{\"text\":\"插入的内容\"}}' --position=3\r\n```\r\n\r\n### 参数说明\r\n\r\n- `docType`: `DOC`(文字) | `WORKBOOK`(表格) | `MIND`(脑图) | `FOLDER`(文件夹)\r\n- `nodeId`: **必填**，节点 ID（用于白名单检查）\r\n  - 从文档链接 `alidocs.dingtalk.com/i/nodes/xxx` 提取 `xxx` 部分\r\n  - 通过 `get-doc --nodeId=xxx` 或 `get-content --docKey=xxx` 确认\r\n- `docKey`: **可选**，真实的文档标识符（用于实际写入 API）\r\n  - 如果不传，默认使用 `nodeId` 代替\r\n  - 仅在 `nodeId` 作为 `docKey` 写入失败时，才需要传入真实的 `docKey`\r\n  - 真实 `docKey` 可通过 createDoc 返回值或钉钉 API Explorer 获取\r\n- `blockId`: 块 ID，通过 `get-content` 获取文档结构后得到（块级操作必需）\r\n- `element`: 块元素 JSON 对象（**会自动解析**，直接传 JSON 字符串即可）\r\n  - 示例：`--element='{\"blockType\":\"paragraph\",\"paragraph\":{\"text\":\"新内容\"}}'`\r\n- `position`: 插入位置（可选），数字，**支持 0**（表示插到最前面）\r\n- `workspaceId`: 知识库 ID（可选）\r\n  - 注意：写入操作会通过 `nodeId` 查询节点真实 `workspaceId`，传入 `--workspaceId` 时只能作为一致性校验，不能跳过查询\r\n  - 适用场景：已知 `workspaceId` 且想显式校验目标文档属于该知识库，但 `nodeId` 仍必需\r\n\r\n### 命令选择指南\r\n\r\n| 需求 | 使用命令 | 说明 |\r\n|------|---------|------|\r\n| 重写整篇文档 | `update-content --nodeId=xxx --content=\"...\"` | 替换全部内容 |\r\n| 只修改某个段落 | `modify-block --nodeId=xxx --blockId=blk --element='{...}'` | 只影响单个块 |\r\n| 在当前位置新增一段 | `insert-block --nodeId=xxx --element='{...}' --position=3` | 插入一个新块，更适合“追加一段”的需求 |\r\n| 删除某一段/标题 | `delete-block --nodeId=xxx --blockId=blk` | 删除块 |\r\n| 插入新段落/标题 | `insert-block --nodeId=xxx --element='{...}' --position=3` | 在指定位置插入 |\r\n\r\n## 常见问题\r\n\r\n1. **\"无法获取文档信息\"错误**: \r\n   - 确保传入的是 `nodeId`（从文档链接 `/i/nodes/xxx` 提取）\r\n   - `--workspaceId` 不能替代 `nodeId`，只能作为额外一致性校验\r\n   \r\n2. **\"paramError\" / JSON 解析失败**: \r\n   - `--element` 必须是合法的 JSON 格式，检查引号转义\r\n   - PowerShell 中用单引号包裹：`--element='{\"type\":\"paragraph\"}'`\r\n   \r\n3. **更新失败（nodeNotExist 等）**: \r\n   - 尝试传入真实的 `docKey`：`update-content --nodeId=xxx --docKey=真实 docKey --content=\"...\"`\r\n   - createDoc 返回的 `docKey` 和 `nodeId` 可能不同\r\n\r\n4. **nodeId 和 docKey 到底有什么区别？**\r\n   - `nodeId`: 目录树节点 ID（wiki_2.0 API 用），用于定位文档、获取 workspaceId 和节点名、执行白名单检查\r\n   - `docKey`: 文档内容标识符（suites/documents API 用），用于实际读写内容\r\n   - 经验：大多数情况下 `nodeId` 可直接用作 `docKey`，少数情况需要真实 `docKey`\r\n\r\n5. **为什么没有 append-text？**\r\n   - 当前公开接口 `POST /v1.0/doc/suites/documents/{docKey}/paragraphs/{blockId}/text` 在真实测试中返回 `InvalidAction.NotFound`\r\n   - 因此本 skill 不再承诺 `append-text`，请使用 `insert-block` 或 `modify-block`\r\n\r\n## 推荐流程\r\n\r\n1. 先确认凭证和 sender_id 是否可用。\r\n2. 需要定位文档所在知识库时先跑 `list-workspaces`。\r\n3. 需要定位文档时先跑 `list-docs`、`search`、`get-doc`。\r\n4. 执行写操作前，默认假设会触发白名单校验，不要跳过读取确认步骤。\r\n5. 如果写入被拒绝，只说明是哪个 workspace / 节点名未通过白名单，并提示用户手动调整 `config/whitelist.json`。\r\n\r\n## 常见失败\r\n\r\n- 缺少 sender_id:检查钉钉连接器是否注入 `OPENCLAW_SENDER_ID` / `DINGTALK_SENDER_ID`\r\n- `forbidden.accessDenied`:检查应用权限或白名单\r\n- `invalidRequest.workspaceNode.parentNotFound`:检查 `parentNodeId`\r\n- `权限拒绝：知识库 xxx 未配置白名单`:让用户手动补充 `config/whitelist.json`\r\n- `nodeNotExist`(更新内容时):**尝试使用 nodeId 代替 docKey** - 钉钉 API 中 createDoc 返回的 docKey 和 overwriteContent 需要的 docKey 可能不一致\r\n- `blockNotExist`(块级操作时):先用 `get-content` 获取文档结构，确认 blockId 正确\r\n- `paramError`(modify-block/insert-block):检查 `--element` 参数是否是合法的 JSON 格式\r\n\r\n## 重要提示\r\n\r\n### get-doc vs get-content\r\n\r\n| 命令 | 用途 | 返回内容 | 何时使用 |\r\n|------|------|---------|---------|\r\n| `get-doc` | 获取**文档元数据** | `data.node`：名称、ID、创建者、修改时间、字数、workspaceId | 确认文档存在、获取文档基本信息、定位知识库 |\r\n| `get-content` | 获取**文档正文** | `data.result.data[]`：段落、标题、列表等块结构 | 总结内容、读取正文、准备修改文档 |\r\n\r\n**关键区别：**\r\n- `get-doc` → `GET /v2.0/wiki/nodes/{nodeId}` → **不包含正文内容**\r\n- `get-content` → `GET /v1.0/doc/suites/documents/{docKey}/blocks` → **包含正文块结构**\r\n- **总结、读取内容时，始终使用 `get-content`，不要用 `get-doc`**\r\n\r\n### docKey vs nodeId\r\n\r\n- `nodeId`: 目录树节点 ID（wiki_2.0 API 用），用于定位文档、获取 workspaceId 和节点名、执行白名单检查\r\n- `docKey`: 文档内容标识符（suites/documents API 用），用于实际读写内容\r\n- `create-doc` 返回的 `docKey` 和 `nodeId` 可能是不同的值\r\n- `update-content`、`get-content` 等命令**优先使用 `nodeId`** 作为 `--docKey` 参数\r\n- 如果使用 `docKey` 更新失败 (`nodeNotExist`),请改用 `nodeId`\r\n- 经验：大多数情况下 `nodeId` 可直接用作 `docKey`，少数情况需要真实 `docKey`\r\n\r\n**示例:**\r\n\r\n```bash\r\n# 创建文档后，使用返回的 nodeId 进行更新\r\nnode .../index.js create-doc --workspaceId=xxx --name=\"新文档\"\r\n# 返回:{ \"docKey\": \"abc123\", \"nodeId\": \"xyz789\", ... }\r\n\r\n# ✅ 正确：使用 nodeId 更新\r\nnode .../index.js update-content --docKey=xyz789 --content=\"...\"\r\n\r\n# ❌ 可能失败：使用 docKey 更新\r\nnode .../index.js update-content --docKey=abc123 --content=\"...\"\r\n```\r\n\r\n## 参考\r\n\r\n- 详细说明见 `README.md`\r\n- 钉钉开放平台文档:[knowledge-base-overview](https://open.dingtalk.com/document/development/knowledge-base-overview)\r\n- API Explorer:[open-dev.dingtalk.com/apiExplorer](https://open-dev.dingtalk.com/apiExplorer)\n\nFile v1.0.6:README.md\n\n# 钉钉文档管理技能 (dingtalk-doc)\r\n\r\n> [https://clawhub.ai/shyzhen/dingtalk-doc](https://clawhub.ai/shyzhen/dingtalk-doc)\r\n> \r\n> 是 [dingtalk-doc-enterprise](https://clawhub.ai/shyzhen/dingtalk-doc-enterprise) 的升级版(支持写入范围控制)\r\n\r\n通过钉钉开放平台 API 读取和管理钉钉文档、钉钉知识库中的文档。本文件面向人类读者，重点说明配置、上手方式和排障思路；执行规则与触发策略见 `SKILL.md`。\r\n\r\n## 能力概览\r\n\r\n- 支持列出、搜索、读取钉钉文档\r\n- 支持创建、更新、删除钉钉文档\r\n- 支持多个 workspace 共用一份白名单配置\r\n- 写入操作按 workspace + 节点名白名单控制，读取操作不受白名单限制\r\n- 当前用户身份从钉钉连接器注入的 `sender_id` 自动解析\r\n\r\n## 文档分工\r\n\r\n- `SKILL.md`：给 agent 的执行手册，强调触发条件、命令映射和失败处理\r\n- `README.md`：给人的配置说明、背景信息、排障参考\r\n\r\n## 快速开始\r\n\r\n### 1. 配置环境变量\r\n\r\n本 skill 可运行在 Windows、Linux、macOS；核心要求只有：\r\n\r\n- 安装 `node`\r\n- 配置 `DINGTALK_CLIENTID` / `DINGTALK_CLIENTSECRET`\r\n\r\n**编辑 `~/.openclaw/.env` 文件（推荐，优先级最高）**\r\n\r\nOpenClaw 会在启动时自动加载此文件中的环境变量。\r\n\r\n```bash\r\n# ~/.openclaw/.env\r\nDINGTALK_CLIENTID=dingxxxxxx\r\nDINGTALK_CLIENTSECRET=your_secret\r\n```\r\n\r\n说明：\r\n\r\n- `DINGTALK_CLIENTID`：钉钉应用 Client ID\r\n- `DINGTALK_CLIENTSECRET`：钉钉应用 Client Secret\r\n- `OPENCLAW_SENDER_ID` / `DINGTALK_SENDER_ID`：通常由 OpenClaw / 钉钉连接器自动注入；如需手工调用 CLI，也可以用 `--senderId=...`\r\n- `DINGTALK_DEBUG=true`：可选，仅输出请求方法、接口路径、状态码、requestId 等调试信息，不再打印文档正文和请求体\r\n- `operatorId` 不需要手工配置，脚本会优先从 `--senderId` 或 `OPENCLAW_SENDER_ID` / `DINGTALK_SENDER_ID` 解析当前钉钉用户\r\n\r\n### 2. 确认 workspaceId\r\n\r\n- 白名单配置：只支持 【钉钉文档 -> 知识库】下新建的根目录，也就是说可写范围必须在【知识库】中。\r\n- 直接发一篇文档给你的钉钉机器人让他随便写入一段话，就会触发白名单检查，机器人会主动告知你当前文档的workspaceId，以及如何配置。\r\n\r\n### 3. 配置白名单\r\n\r\n说明：\r\n\r\n- 读取操作不依赖白名单文件\r\n- 写入操作才需要 `config/whitelist.json`\r\n- 如果该文件不存在，写操作会被拒绝，并提示用户手动创建\r\n\r\n在 `config/whitelist.json` 中声明允许写入的 workspace 和节点名：\r\n\r\n```json\r\n{\r\n  \"workspaces\": [\r\n    {\r\n      \"workspaceId\": \"eLvJDSRX3l4moO87\",\r\n      \"workspaceName\": \"AI可写知识库一级目录\",\r\n      \"allowRootWrite\": false,\r\n      \"whitelist\": [\"/\"]\r\n    }\r\n  ]\r\n}\r\n```\r\n\r\n字段说明：\r\n\r\n- `workspaceId`：知识库 ID，必填 （只支持【钉钉文档 -> 知识库】下的目录）\r\n- `workspaceName`：可选，便于识别\r\n- `allowRootWrite`：保留字段；当前写入校验主要使用 `whitelist`\r\n- `whitelist`：允许写入的节点名（文档名）列表\r\n\r\n白名单规则：\r\n\r\n- 读取操作不检查白名单\r\n- 写入操作必须命中白名单\r\n- 未配置的 workspace 默认禁止写入\r\n- `\"/\"` 表示允许写入当前 `workspaceId` 下的所有节点\r\n- 细粒度白名单按节点名匹配，例如 `\"/三级文档.adoc\"`；当前已验证的节点详情接口不会返回完整父目录路径\r\n\r\n## 常用命令\r\n\r\n这里只保留几个面向人的常见示例；完整命令映射见 `SKILL.md`。\r\n\r\n读取：\r\n\r\n```bash\r\n# 当前目录运行（跨平台通用）\r\nnode scripts/index.js list-workspaces\r\nnode scripts/index.js list-docs --workspaceId=OQ0xySj6ng7lX58B --parentNodeId=root\r\nnode scripts/index.js get-doc --nodeId=YQBnd5ExVE0PDnezU2PK2RK6WyeZqMmz\r\nnode scripts/index.js get-content --nodeId=YQBnd5ExVE0PDnezU2PK2RK6WyeZqMmz\r\nnode scripts/index.js get-content --docKey=真实docKey --nodeId=YQBnd5ExVE0PDnezU2PK2RK6WyeZqMmz\r\n```\r\n\r\n写入：\r\n\r\n```bash\r\n# 当前目录运行（跨平台通用）\r\nnode scripts/index.js create-doc --workspaceId=OQ0xySj6ng7lX58B --name=\"新文档\" --docType=DOC --parentNodeId=root\r\nnode scripts/index.js update-content --nodeId=YQBnd5ExVE0PDnezU2PK2RK6WyeZqMmz --content=\"# 标题\\n\\n内容\"\r\nnode scripts/index.js update-content --docKey=真实docKey --nodeId=YQBnd5ExVE0PDnezU2PK2RK6WyeZqMmz --content=\"# 标题\\n\\n内容\"\r\n```\r\n\r\n白名单自检：\r\n\r\n```bash\r\n# 当前目录运行（跨平台通用）\r\nnode scripts/whitelist.js check \"/三级文档.adoc\"\r\n```\r\n\r\n`docType` 支持：\r\n\r\n- `DOC`\r\n- `WORKBOOK`\r\n- `MIND`\r\n- `FOLDER`\r\n\r\n## 目录结构\r\n\r\n```text\r\ndingtalk-doc/\r\n├── SKILL.md\r\n├── README.md\r\n├── package.json\r\n├── config/\r\n│   └── whitelist.json\r\n└── scripts/\r\n    ├── index.js\r\n    ├── dingtalk-client.js\r\n    └── whitelist.js\r\n```\r\n\r\n## 已知返回结构\r\n\r\n以下结构来自实测文档 `https://alidocs.dingtalk.com/i/nodes/oP0MALyR8kOd5BGacKv6NbxE83bzYmDO?utm_scene=team_space`，文档名是“三级文档”，真实位于三级子目录中。\r\n\r\n`get-doc --nodeId=...` / `GET /v2.0/wiki/nodes/{nodeId}` 返回的 `node` 里有 `workspaceId` 和 `name`，但没有返回完整父目录链路：\r\n\r\n```json\r\n{\r\n  \"node\": {\r\n    \"name\": \"三级文档.adoc\",\r\n    \"nodeId\": \"oP0MALyR8kOd5BGacKv6NbxE83bzYmDO\",\r\n    \"workspaceId\": \"eLvJDSRX3l4moO87\",\r\n    \"type\": \"FILE\",\r\n    \"category\": \"ALIDOC\",\r\n    \"url\": \"https://alidocs.dingtalk.com/i/nodes/oP0MALyR8kOd5BGacKv6NbxE83bzYmDO\"\r\n  }\r\n}\r\n```\r\n\r\n容易犯错的点：\r\n\r\n- 不要把白名单理解为真实目录路径；当前脚本只能可靠使用 `workspaceId + node.name` 做写入白名单。\r\n- 即使文档在三级子目录中，节点详情也可能只返回 `\"name\": \"三级文档.adoc\"`，不会返回 `\"/一级/二级/三级文档.adoc\"`。\r\n- `get-content --nodeId=...` / `GET /v1.0/doc/suites/documents/{docKey}/blocks` 返回的是块结构，例如 paragraph 的 `id`、`index`、`text`，不返回文档路径或父目录信息。\r\n- `nodeId` 用于查询节点详情和校验真实 `workspaceId`；`docKey` 用于 suites/documents 内容读写接口。多数情况下可先用 `nodeId` 作为 `docKey` 尝试。\r\n- `insert-block`、`modify-block`、`delete-block` 已在真实文档上测试通过；`append-text` 对应的 `paragraphs/{blockId}/text` 当前返回 `InvalidAction.NotFound`，不要依赖它。\r\n\r\n## 权限与接口\r\n\r\n常用接口：\r\n\r\n- `POST /v1.0/doc/workspaces/{workspaceId}/docs`\r\n- `GET /v2.0/wiki/nodes`\r\n- `GET /v2.0/wiki/nodes/{nodeId}`\r\n- `DELETE /v1.0/doc/workspaces/{workspaceId}/docs/{nodeId}`\r\n- `GET /v1.0/doc/suites/documents/{docKey}/blocks`\r\n- `POST /v1.0/doc/suites/documents/{docKey}/overwriteContent`\r\n- `POST /v1.0/doc/suites/documents/{docKey}/blocks`\r\n- `PUT /v1.0/doc/suites/documents/{docKey}/blocks/{blockId}`\r\n- `DELETE /v1.0/doc/suites/documents/{docKey}/blocks/{blockId}`\r\n\r\n常见所需权限：\r\n\r\n- `Document.Workspace.Read`\r\n- `Document.Workspace.Write`\r\n- `Document.WorkspaceDocument.Read`\r\n- `Document.WorkspaceDocument.Write`\r\n- `Wiki.Node.Read`\r\n\r\n## 常见问题\r\n\r\n| 问题 | 原因 | 处理方式 |\r\n|------|------|---------|\r\n| 缺少 `sender_id` | 钉钉连接器未传递当前用户 ID | 检查是否注入了 `OPENCLAW_SENDER_ID` 或 `DINGTALK_SENDER_ID` |\r\n| workspace 未配置白名单 | `config/whitelist.json` 没有对应 workspace | 补充对应 `workspaceId` 配置 |\r\n| 节点名不在白名单内 | 写入目标节点名未命中规则 | 调整白名单节点名或使用 `\"/\"` 允许整个 workspace |\r\n| `forbidden.accessDenied` | 应用权限不足或白名单不通过 | 检查钉钉应用权限和白名单 |\r\n| `invalidRequest.workspaceNode.parentNotFound` | `parentNodeId` 错误 | 重新确认父节点 ID |\r\n\r\n## 参考\r\n\r\n- 钉钉开放平台文档：[knowledge-base-overview](https://open.dingtalk.com/document/development/knowledge-base-overview)\r\n- API Explorer：[open-dev.dingtalk.com/apiExplorer](https://open-dev.dingtalk.com/apiExplorer)\r\n\r\n### API 参考\r\n\r\n钉钉知识库 API 分为两个系列：\r\n\r\n#### doc_1.0 - 知识库管理\r\n\r\n| API | 方法 | 路径 | 权限 |\r\n|-----|------|------|------|\r\n| 新建知识库 | POST | `/v1.0/doc/workspaces` | Document.Workspace.Write |\r\n| 获取知识库 | GET | `/v1.0/doc/workspaces/{workspaceId}` | Document.Workspace.Read |\r\n| 获取知识库列表 | GET | `/v1.0/doc/workspaces` | Document.Workspace.Read |\r\n| 创建知识库文档 | POST | `/v1.0/doc/workspaces/{workspaceId}/docs` | Document.WorkspaceDocument.Write |\r\n| 删除知识库文档 | DELETE | `/v1.0/doc/workspaces/{workspaceId}/docs/{nodeId}` | Document.WorkspaceDocument.Write |\r\n\r\n#### wiki_2.0 - 目录树管理\r\n\r\n| API | 方法 | 路径 | 权限 |\r\n|-----|------|------|------|\r\n| 获取节点 | GET | `/v2.0/wiki/nodes/{nodeId}` | Wiki.Node.Read |\r\n| 获取节点列表 | GET | `/v2.0/wiki/nodes` | Wiki.Node.Read |\r\n| 通过链接获取节点 | GET | `/v2.0/wiki/nodes/url` | Wiki.Node.Read |\r\n| 复制文档 | POST | `/v2.0/wiki/nodes/{nodeId}/copy` | Document.WorkspaceDocument.Write |\r\n\r\n#### suites/documents - 文档内容\r\n\r\n| API | 方法 | 路径 | 权限 |\r\n|-----|------|------|------|\r\n| 查询块结构 | GET | `/v1.0/doc/suites/documents/{docKey}/blocks` | Document.WorkspaceDocument.Read |\r\n| 覆写内容 | POST | `/v1.0/doc/suites/documents/{docKey}/overwriteContent` | Document.WorkspaceDocument.Write |\r\n| 插入块 | POST | `/v1.0/doc/suites/documents/{docKey}/blocks` | Document.WorkspaceDocument.Write |\r\n| 删除块 | DELETE | `/v1.0/doc/suites/documents/{docKey}/blocks/{blockId}` | Document.WorkspaceDocument.Write |\r\n\r\n\r\n#### 所需权限清单\r\n\r\n调用本 skill 需要开通以下钉钉应用权限：\r\n\r\n- `Document.Workspace.Read` - 知识库读权限\r\n- `Document.Workspace.Write` - 知识库写权限\r\n- `Document.WorkspaceDocument.Read` - 文档读权限\r\n- `Document.WorkspaceDocument.Write` - 文档写权限\r\n- `Wiki.Node.Read` - 节点读权限\r\n\r\n## 许可证\r\n\r\nMIT\n\nFile v1.0.6:_meta.json\n\n{\n  \"ownerId\": \"kn73t8gpykqmgx10ksfb6706r5842gge\",\n  \"slug\": \"dingtalk-doc\",\n  \"version\": \"1.0.6\",\n  \"publishedAt\": 1776908986440\n}\n\nFile v1.0.6:skill-card.md\n\n## Description: <br>\nManages DingTalk documents and knowledge-base documents when an agent is given an alidocs.dingtalk.com link or an explicit DingTalk document read, summarize, update, or delete task. <br>\n\nThis skill is ready for commercial/non-commercial use. <br>\n\n## Publisher: <br>\n[shyzhen](https://clawhub.ai/user/shyzhen) <br>\n\n### License/Terms of Use: <br>\nMIT-0 <br>\n\n\n## Use Case: <br>\nDevelopers and operators use this skill to let an agent read, summarize, search, create, update, and delete DingTalk knowledge-base documents through DingTalk APIs, with configured credentials and write-scope controls. <br>\n\n### Deployment Geography for Use: <br>\nGlobal <br>\n\n## Known Risks and Mitigations: <br>\nRisk: The bundled whitelist can allow broad workspace-wide writes. <br>\nMitigation: Remove or restrict the bundled workspace-wide whitelist before use and allow only the specific workspaces or document names needed. <br>\nRisk: Raw token and client commands can expose DingTalk access authority to agent workflows. <br>\nMitigation: Do not expose get-token in normal agent workflows, and protect DingTalk credentials and tokens from prompts, logs, and shared outputs. <br>\nRisk: Delete and overwrite actions can permanently change DingTalk documents. <br>\nMitigation: Require explicit human approval before delete, overwrite, update-content, or block-level delete/modify operations. <br>\n\n\n## Reference(s): <br>\n- [ClawHub skill page](https://clawhub.ai/shyzhen/dingtalk-doc) <br>\n- [DingTalk knowledge-base overview](https://open.dingtalk.com/document/development/knowledge-base-overview) <br>\n- [DingTalk API Explorer](https://open-dev.dingtalk.com/apiExplorer) <br>\n\n\n## Skill Output: <br>\n**Output Type(s):** [text, markdown, shell commands, configuration, guidance] <br>\n**Output Format:** [Markdown guidance with shell command examples and JSON API responses] <br>\n**Output Parameters:** [1D] <br>\n**Other Properties Related to Output:** [Requires Node.js and DingTalk client credentials; write actions depend on whitelist configuration.] <br>\n\n## Skill Version(s): <br>\n1.0.6 (source: package.json and server release metadata) <br>\n\n## Ethical Considerations: <br>\nUsers should evaluate whether this skill is appropriate for their environment, review any generated or modified files before relying on them, and apply their organization's safety, security, and compliance requirements before deployment. <br>\n\nFile v1.0.6:config/whitelist.json\n\n{\n  \"_comment\": \"⚠️ 此配置文件只能由用户手动编辑，AI 禁止修改！\",\n  \"workspaces\": [\n    {\n      \"workspaceId\": \"eLvJDSRX3l4moO87\",\n      \"workspaceName\": \"AI可写知识库一级目录\",\n      \"allowRootWrite\": true,\n      \"whitelist\": [\"/\"]\n    }\n  ]\n}\n\nFile v1.0.6:package.json\n\n{\n  \"name\": \"dingtalk-doc\",\n  \"version\": \"1.0.6\",\n  \"description\": \"钉钉文档管理技能 - 支持文档增删改查和白名单权限控制\",\n  \"main\": \"scripts/index.js\",\n  \"scripts\": {\n    \"get-token\": \"node scripts/dingtalk-client.js get-token\",\n    \"get-workspace-list\": \"node scripts/dingtalk-client.js get-workspace-list\",\n    \"list-docs\": \"node scripts/index.js list-docs\",\n    \"create-doc\": \"node scripts/index.js create-doc\",\n    \"delete-doc\": \"node scripts/index.js delete-doc\",\n    \"search\": \"node scripts/index.js search\",\n    \"get-content\": \"node scripts/index.js get-content\",\n    \"update-content\": \"node scripts/index.js update-content\",\n    \"check-permission\": \"node scripts/whitelist.js check\"\n  },\n  \"keywords\": [\n    \"dingtalk\",\n    \"knowledge-base\",\n    \"openclaw-skill\"\n  ],\n  \"author\": \"\",\n  \"license\": \"MIT\"\n}\n\nArchive v1.0.5: 8 files, 25746 bytes\n\nFiles: config/whitelist.json (278b), package.json (837b), README.md (10156b), scripts/dingtalk-client.js (20641b), scripts/index.js (30897b), scripts/whitelist.js (5086b), SKILL.md (18086b), _meta.json (131b)\n\nFile v1.0.5:SKILL.md\n\n---\r\nname: dingtalk-doc\r\ndescription: 钉钉文档管理技能。当用户发送的消息中包含 alidocs.dingtalk.com 链接、要求总结/读取/查看/更新钉钉文档或钉钉知识库文档，或当前上下文已明确对象是钉钉文档时使用。关键词：钉钉文档、钉钉知识库、alidocs、总结、读取、查看、更新、修改、文档、链接。\r\nmetadata:\r\n  {\r\n    \"openclaw\":\r\n      {\r\n        \"emoji\": \"📚\",\r\n        \"requires\":\r\n          {\r\n            \"bins\": [\"node\"],\r\n            \"env\": [\"DINGTALK_CLIENTID\", \"DINGTALK_CLIENTSECRET\"],\r\n          },\r\n      },\r\n  }\r\n---\r\n\r\n# 钉钉文档管理技能\r\n\r\n通过钉钉开放平台 API 管理钉钉文档与钉钉知识库内文档。`SKILL.md` 只保留 agent 执行所需规则;配置细节、示例、API 背景见 `README.md`。\r\n\r\n## 何时使用\r\n\r\n### 触发关键词\r\n\r\n只有在**已经确认对象是钉钉文档**时，同时消息包含以下**任一关键词**时，优先使用本 skill：\r\n\r\n| 类别 | 关键词 |\r\n|------|--------|\r\n| **平台名** | 钉钉文档、钉钉知识库、alidocs |\r\n| **读取类** | 总结、读取、查看、浏览、列出结构 |\r\n| **修改类** | 更新、修改、追加、删除、覆写 |\r\n| **对象** | 文档、链接、这篇、这个文档 |\r\n\r\n**组合示例：**\r\n- \"总结一下这篇钉钉文档\"\r\n- \"读取这个 alidocs 链接\"\r\n- \"更新文档内容\"\r\n- \"删除第三段\"\r\n\r\n### 触发场景（优先级从高到低）\r\n\r\n| 场景 | 示例 | 动作 |\r\n|------|------|------|\r\n| **钉钉文档链接** | `alidocs.dingtalk.com/i/nodes/xxx` | 根据意图选择：元数据→`get-doc`，正文内容→`get-content` |\r\n| **钉钉上下文 + 链接** | \"总结 https://alidocs.dingtalk.com/...\" | 调用 `get-content` 读取内容后总结 |\r\n| **明确命令** | \"总结这篇文档\"、\"读取这个 alidocs 链接\" | 根据意图选择命令 |\r\n| **已知上下文是钉钉文档** | 前文已给出 alidocs 链接，后续说\"更新文档\"、\"删除某段\"、\"在第三段后追加\" | 调用对应命令 |\r\n| **结构查询** | \"列出结构\"、\"这个 alidocs 有哪些章节\" | 调用 `get-content` |\r\n| **块级操作** | \"删除第三段\"、\"修改这个段落\"、\"在这里插入一段\" | 先用 `get-content` 获取 blockId 和位置，再调用 `delete-block`/`modify-block`/`insert-block` |\r\n\r\n### 不触发的场景\r\n- 查询本地文件、离线文档或普通文本内容，且不需要调用钉钉 API\r\n- 没有文档链接、docKey、或明确钉钉文档上下文，却要求修改文档\r\n- 用户只说\"总结文档\"\"更新这个链接\"等泛化请求，但上下文无法确认对象是钉钉文档\r\n- 与钉钉无关的文档系统，例如本地 Markdown、飞书文档、语雀、Google Docs\r\n\r\n## 运行前提\r\n\r\n### 环境变量\r\n\r\n必须配置以下环境变量 (在 Gateway 环境中):\r\n\r\n- `DINGTALK_CLIENTID` - 钉钉应用 Client ID (AppKey)\r\n- `DINGTALK_CLIENTSECRET` - 钉钉应用 Client Secret (AppSecret)\r\n\r\n可选环境变量:\r\n\r\n- `OPENCLAW_SENDER_ID` / `DINGTALK_SENDER_ID` - 由 OpenClaw / 钉钉连接器注入的当前用户 sender_id；也可以通过命令行 `--senderId=` 显式传入\r\n- `DINGTALK_DEBUG` - 设置为 `true` 启用调试模式；仅输出方法、路径（查询参数已脱敏）、状态码、requestId 等，不打印文档正文与完整请求体\r\n\r\n### operatorId 获取方式\r\n\r\n不需要在配置中指定!系统会自动从当前会话获取:\r\n\r\n1. 从 `OPENCLAW_SENDER_ID` 或 `DINGTALK_SENDER_ID` 获取 sender_id\r\n2. 调用钉钉 API 查询对应的 unionId\r\n3. 使用 unionId 作为 operatorId\r\n\r\n如果获取失败，会显示友好的错误提示。\r\n\r\n## 执行规则\r\n\r\n- 读取操作不受白名单限制\r\n- 写入操作必须通过白名单检查;未配置 workspace 或节点名不在白名单内时，一律拒绝!**没有任何方式可以绕过白名单检查!**\r\n- `whitelist: [\"/\"]` 表示允许写入该 workspace 下的所有节点;更细粒度控制请配置具体文档名，例如 `\"/三级目录测试文档.adoc\"`\r\n- 白名单配置文件 `config/whitelist.json` 只能由用户手动修改;AI 只能读取、解释、提示用户手动调整，不能替用户改\r\n- 如果用户没有给出 `workspaceId`,先运行 `list-workspaces`\r\n- 如果用户没有给出目标文档的 `nodeId` / `docKey`,先运行 `list-docs`、`search` 或 `get-doc` 确认目标\r\n\r\n## URL 解析规则\r\n\r\n当用户提供钉钉文档 URL 时，按以下规则提取 `nodeId`:\r\n\r\n**URL 格式:** `https://alidocs.dingtalk.com/i/nodes/<nodeId>?...`\r\n\r\n**提取方法:**\r\n1. 从 URL 中提取 `nodes/` 和 `?` 之间的部分\r\n2. 该部分即为 `nodeId`\r\n\r\n**示例:**\r\n```\r\nURL: https://alidocs.dingtalk.com/i/nodes/oP0MALyR8kOd5BGacKv6NbxE83bzYmDO?utm_scene=team_space\r\nnodeId: oP0MALyR8kOd5BGacKv6NbxE83bzYmDO\r\n```\r\n\r\n**提取到 nodeId 后的操作:**\r\n1. **获取文档元数据**（名称、知识库 ID 等）：`get-doc --nodeId=<提取的 nodeId>`\r\n2. **获取文档内容**（总结、读取正文）：`get-content --docKey=<提取的 nodeId>`\r\n3. 注意：`get-doc` 只返回元数据，`get-content` 才返回正文内容\r\n\r\n## 执行入口\r\n\r\n- `scripts/index.js`:主入口\r\n- `scripts/whitelist.js`:辅助检查白名单配置\r\n\r\n**跨平台说明：**\r\n\r\n- 本 skill 的脚本基于 Node.js 内置模块实现，Windows、Linux、macOS 只要安装了 `node` 并配置好环境变量，都可以运行\r\n- 推荐优先使用相对路径执行：`node scripts/index.js ...`，这样最不容易受平台路径差异影响\r\n\r\n**路径示例：**\r\n\r\nWindows PowerShell 中 `~` 不会自动展开，建议使用以下方式之一：\r\n\r\n```bash\r\n# ✅ 使用 $env:USERPROFILE\r\nnode $env:USERPROFILE\\.openclaw\\skills\\dingtalk-doc\\scripts\\index.js\r\n\r\n# ✅ 或使用完整绝对路径\r\nnode C:\\Users\\zhenhuaixiu\\.openclaw\\skills\\dingtalk-doc\\scripts\\index.js\r\n\r\n# ❌ 错误（~ 不会展开）\r\nnode ~/.openclaw/skills/dingtalk-doc/scripts/index.js\r\n```\r\n\r\nLinux / macOS Shell 示例：\r\n\r\n```bash\r\n# ✅ 当前目录下直接运行（推荐）\r\nnode scripts/index.js list-workspaces\r\n\r\n# ✅ 或使用完整绝对路径\r\nnode ~/.openclaw/skills/dingtalk-doc/scripts/index.js list-workspaces\r\n```\r\n\r\n## 命令映射\r\n\r\n**路径说明：**\r\n\r\n- Windows 示例中的 `$env:USERPROFILE\\.openclaw` 会展开为 `C:\\Users\\<用户名>\\.openclaw`\r\n- Linux / macOS 示例中的 `~/.openclaw` 会展开为用户主目录下的 `.openclaw`\r\n- 如果当前工作目录已经在 skill 根目录，直接使用 `node scripts/index.js ...` 即可\r\n\r\n### 读取操作\r\n\r\n| 命令 | 用途 | API 端点 |\r\n|------|------|---------|\r\n| `list-workspaces` | 获取知识库列表 | `GET /v2.0/wiki/mineWorkspaces` |\r\n| `list-docs` | 获取知识库中文档列表 | `GET /v2.0/wiki/nodes` |\r\n| `get-doc` | 获取**文档元数据**（名称、ID、创建者、字数等） | `GET /v2.0/wiki/nodes/{nodeId}` |\r\n| `get-content` | 获取**文档正文内容**（段落、标题、列表等块结构） | `GET /v1.0/doc/suites/documents/{docKey}/blocks` |\r\n| `search` | 搜索文档 | `GET /v1.0/doc/workspaces/{workspaceId}/docs` |\r\n\r\n**示例：**\r\n```bash\r\n# Linux / macOS\r\nnode ~/.openclaw/skills/dingtalk-doc/scripts/index.js list-workspaces\r\nnode ~/.openclaw/skills/dingtalk-doc/scripts/index.js list-docs --workspaceId=YRBGvyxxx --parentNodeId=root\r\nnode ~/.openclaw/skills/dingtalk-doc/scripts/index.js get-doc --nodeId=YQBnd5ExVE0PDnezU2PK2RK6WyeZqMmz\r\nnode ~/.openclaw/skills/dingtalk-doc/scripts/index.js get-content --docKey=YQBnd5ExVE0PDnezU2PK2RK6WyeZqMmz\r\nnode ~/.openclaw/skills/dingtalk-doc/scripts/index.js search --workspaceId=YRBGvyxxx --keyword=\"需求\"\r\n\r\n# Windows PowerShell\r\nnode $env:USERPROFILE\\.openclaw\\skills\\dingtalk-doc\\scripts\\index.js list-workspaces\r\nnode $env:USERPROFILE\\.openclaw\\skills\\dingtalk-doc\\scripts\\index.js list-docs --workspaceId=YRBGvyxxx --parentNodeId=root\r\nnode $env:USERPROFILE\\.openclaw\\skills\\dingtalk-doc\\scripts\\index.js get-doc --nodeId=YQBnd5ExVE0PDnezU2PK2RK6WyeZqMmz\r\nnode $env:USERPROFILE\\.openclaw\\skills\\dingtalk-doc\\scripts\\index.js get-content --docKey=YQBnd5ExVE0PDnezU2PK2RK6WyeZqMmz\r\nnode $env:USERPROFILE\\.openclaw\\skills\\dingtalk-doc\\scripts\\index.js search --workspaceId=YRBGvyxxx --keyword=\"需求\"\r\n```\r\n\r\n### 写入操作\r\n\r\n```bash\r\n# Linux / macOS\r\nnode ~/.openclaw/skills/dingtalk-doc/scripts/index.js create-doc --workspaceId=YRBGvyxxx --name=\"新文档\" --docType=DOC --parentNodeId=root\r\nnode ~/.openclaw/skills/dingtalk-doc/scripts/index.js update-content --nodeId=YQBnd5ExVE0PDnezU2PK2RK6WyeZqMmz --content=\"# 标题\\n\\n内容\"\r\nnode ~/.openclaw/skills/dingtalk-doc/scripts/index.js update-content --nodeId=YQBnd5ExVE0PDnezU2PK2RK6WyeZqMmz --docKey=真实docKey --content=\"# 标题\\n\\n内容\"\r\nnode ~/.openclaw/skills/dingtalk-doc/scripts/index.js delete-doc --workspaceId=YRBGvyxxx --nodeId=YQBnd5ExVE0PDnezU2PK2RK6WyeZqMmz\r\n\r\n# Windows PowerShell\r\n# 创建文档\r\nnode $env:USERPROFILE\\.openclaw\\skills\\dingtalk-doc\\scripts\\index.js create-doc --workspaceId=YRBGvyxxx --name=\"新文档\" --docType=DOC --parentNodeId=root\r\n# 返回：{ \"docKey\": \"abc123\", \"nodeId\": \"xyz789\", ... }\r\n\r\n# 更新文档内容（整篇覆写，替换全部内容）\r\n# ✅ 推荐：只用 nodeId（大多数情况够用）\r\nnode $env:USERPROFILE\\.openclaw\\skills\\dingtalk-doc\\scripts\\index.js update-content --nodeId=YQBnd5ExVE0PDnezU2PK2RK6WyeZqMmz --content=\"# 标题\\n\\n内容\"\r\n\r\n# ✅ 备选：如果上面失败，传入真实的 docKey\r\nnode $env:USERPROFILE\\.openclaw\\skills\\dingtalk-doc\\scripts\\index.js update-content --nodeId=YQBnd5ExVE0PDnezU2PK2RK6WyeZqMmz --docKey=真实 docKey --content=\"# 标题\\n\\n内容\"\r\n\r\n# 删除文档\r\nnode $env:USERPROFILE\\.openclaw\\skills\\dingtalk-doc\\scripts\\index.js delete-doc --workspaceId=YRBGvyxxx --nodeId=YQBnd5ExVE0PDnezU2PK2RK6WyeZqMmz\r\n```\r\n\r\n### 块级操作（精细修改单个段落/元素）\r\n\r\n说明：\r\n\r\n- `insert-block`、`modify-block`、`delete-block` 已通过真实文档测试\r\n- `append-text` 对应的公开 API 当前返回 `InvalidAction.NotFound`，不要再调用或承诺\r\n- 如果用户要“追加内容”，优先改成“插入一个新段落”或“读取原段落后使用 `modify-block` 整块替换”\r\n\r\n```bash\r\n# Linux / macOS\r\nnode ~/.openclaw/skills/dingtalk-doc/scripts/index.js delete-block --nodeId=YQBnd5ExVE0PDnezU2PK2RK6WyeZqMmz --blockId=blk123\r\nnode ~/.openclaw/skills/dingtalk-doc/scripts/index.js modify-block --nodeId=YQBnd5ExVE0PDnezU2PK2RK6WyeZqMmz --blockId=blk123 --element='{\"blockType\":\"paragraph\",\"paragraph\":{\"text\":\"新内容\"}}'\r\nnode ~/.openclaw/skills/dingtalk-doc/scripts/index.js insert-block --nodeId=YQBnd5ExVE0PDnezU2PK2RK6WyeZqMmz --element='{\"blockType\":\"paragraph\",\"paragraph\":{\"text\":\"插入的内容\"}}' --position=3\r\n\r\n# Windows PowerShell\r\n# 删除块元素（删除某个段落/标题/列表项）\r\nnode $env:USERPROFILE\\.openclaw\\skills\\dingtalk-doc\\scripts\\index.js delete-block --nodeId=YQBnd5ExVE0PDnezU2PK2RK6WyeZqMmz --blockId=blk123\r\n\r\n# 修改块元素（替换单个块的内容，不影响其他部分）\r\nnode $env:USERPROFILE\\.openclaw\\skills\\dingtalk-doc\\scripts\\index.js modify-block --nodeId=YQBnd5ExVE0PDnezU2PK2RK6WyeZqMmz --blockId=blk123 --element='{\"blockType\":\"paragraph\",\"paragraph\":{\"text\":\"新内容\"}}'\r\n\r\n# 插入块元素（在指定位置插入新段落/标题等）\r\nnode $env:USERPROFILE\\.openclaw\\skills\\dingtalk-doc\\scripts\\index.js insert-block --nodeId=YQBnd5ExVE0PDnezU2PK2RK6WyeZqMmz --element='{\"blockType\":\"paragraph\",\"paragraph\":{\"text\":\"插入的内容\"}}' --position=3\r\n```\r\n\r\n### 参数说明\r\n\r\n- `docType`: `DOC`(文字) | `WORKBOOK`(表格) | `MIND`(脑图) | `FOLDER`(文件夹)\r\n- `nodeId`: **必填**，节点 ID（用于白名单检查）\r\n  - 从文档链接 `alidocs.dingtalk.com/i/nodes/xxx` 提取 `xxx` 部分\r\n  - 通过 `get-doc --nodeId=xxx` 或 `get-content --docKey=xxx` 确认\r\n- `docKey`: **可选**，真实的文档标识符（用于实际写入 API）\r\n  - 如果不传，默认使用 `nodeId` 代替\r\n  - 仅在 `nodeId` 作为 `docKey` 写入失败时，才需要传入真实的 `docKey`\r\n  - 真实 `docKey` 可通过 createDoc 返回值或钉钉 API Explorer 获取\r\n- `blockId`: 块 ID，通过 `get-content` 获取文档结构后得到（块级操作必需）\r\n- `element`: 块元素 JSON 对象（**会自动解析**，直接传 JSON 字符串即可）\r\n  - 示例：`--element='{\"blockType\":\"paragraph\",\"paragraph\":{\"text\":\"新内容\"}}'`\r\n- `position`: 插入位置（可选），数字，**支持 0**（表示插到最前面）\r\n- `workspaceId`: 知识库 ID（可选）\r\n  - 注意：写入操作会通过 `nodeId` 查询节点真实 `workspaceId`，传入 `--workspaceId` 时只能作为一致性校验，不能跳过查询\r\n  - 适用场景：已知 `workspaceId` 且想显式校验目标文档属于该知识库，但 `nodeId` 仍必需\r\n\r\n### 命令选择指南\r\n\r\n| 需求 | 使用命令 | 说明 |\r\n|------|---------|------|\r\n| 重写整篇文档 | `update-content --nodeId=xxx --content=\"...\"` | 替换全部内容 |\r\n| 只修改某个段落 | `modify-block --nodeId=xxx --blockId=blk --element='{...}'` | 只影响单个块 |\r\n| 在当前位置新增一段 | `insert-block --nodeId=xxx --element='{...}' --position=3` | 插入一个新块，更适合“追加一段”的需求 |\r\n| 删除某一段/标题 | `delete-block --nodeId=xxx --blockId=blk` | 删除块 |\r\n| 插入新段落/标题 | `insert-block --nodeId=xxx --element='{...}' --position=3` | 在指定位置插入 |\r\n\r\n## 常见问题\r\n\r\n1. **\"无法获取文档信息\"错误**: \r\n   - 确保传入的是 `nodeId`（从文档链接 `/i/nodes/xxx` 提取）\r\n   - `--workspaceId` 不能替代 `nodeId`，只能作为额外一致性校验\r\n   \r\n2. **\"paramError\" / JSON 解析失败**: \r\n   - `--element` 必须是合法的 JSON 格式，检查引号转义\r\n   - PowerShell 中用单引号包裹：`--element='{\"type\":\"paragraph\"}'`\r\n   \r\n3. **更新失败（nodeNotExist 等）**: \r\n   - 尝试传入真实的 `docKey`：`update-content --nodeId=xxx --docKey=真实 docKey --content=\"...\"`\r\n   - createDoc 返回的 `docKey` 和 `nodeId` 可能不同\r\n\r\n4. **nodeId 和 docKey 到底有什么区别？**\r\n   - `nodeId`: 目录树节点 ID（wiki_2.0 API 用），用于定位文档、获取 workspaceId 和节点名、执行白名单检查\r\n   - `docKey`: 文档内容标识符（suites/documents API 用），用于实际读写内容\r\n   - 经验：大多数情况下 `nodeId` 可直接用作 `docKey`，少数情况需要真实 `docKey`\r\n\r\n5. **为什么没有 append-text？**\r\n   - 当前公开接口 `POST /v1.0/doc/suites/documents/{docKey}/paragraphs/{blockId}/text` 在真实测试中返回 `InvalidAction.NotFound`\r\n   - 因此本 skill 不再承诺 `append-text`，请使用 `insert-block` 或 `modify-block`\r\n\r\n## 推荐流程\r\n\r\n1. 先确认凭证和 sender_id 是否可用。\r\n2. 需要定位文档所在知识库时先跑 `list-workspaces`。\r\n3. 需要定位文档时先跑 `list-docs`、`search`、`get-doc`。\r\n4. 执行写操作前，默认假设会触发白名单校验，不要跳过读取确认步骤。\r\n5. 如果写入被拒绝，只说明是哪个 workspace / 节点名未通过白名单，并提示用户手动调整 `config/whitelist.json`。\r\n\r\n## 常见失败\r\n\r\n- 缺少 sender_id:检查钉钉连接器是否注入 `OPENCLAW_SENDER_ID` / `DINGTALK_SENDER_ID`\r\n- `forbidden.accessDenied`:检查应用权限或白名单\r\n- `invalidRequest.workspaceNode.parentNotFound`:检查 `parentNodeId`\r\n- `权限拒绝：知识库 xxx 未配置白名单`:让用户手动补充 `config/whitelist.json`\r\n- `nodeNotExist`(更新内容时):**尝试使用 nodeId 代替 docKey** - 钉钉 API 中 createDoc 返回的 docKey 和 overwriteContent 需要的 docKey 可能不一致\r\n- `blockNotExist`(块级操作时):先用 `get-content` 获取文档结构，确认 blockId 正确\r\n- `paramError`(modify-block/insert-block):检查 `--element` 参数是否是合法的 JSON 格式\r\n\r\n## 重要提示\r\n\r\n### get-doc vs get-content\r\n\r\n| 命令 | 用途 | 返回内容 | 何时使用 |\r\n|------|------|---------|---------|\r\n| `get-doc` | 获取**文档元数据** | `data.node`：名称、ID、创建者、修改时间、字数、workspaceId | 确认文档存在、获取文档基本信息、定位知识库 |\r\n| `get-content` | 获取**文档正文** | `data.result.data[]`：段落、标题、列表等块结构 | 总结内容、读取正文、准备修改文档 |\r\n\r\n**关键区别：**\r\n- `get-doc` → `GET /v2.0/wiki/nodes/{nodeId}` → **不包含正文内容**\r\n- `get-content` → `GET /v1.0/doc/suites/documents/{docKey}/blocks` → **包含正文块结构**\r\n- **总结、读取内容时，始终使用 `get-content`，不要用 `get-doc`**\r\n\r\n### docKey vs nodeId\r\n\r\n- `nodeId`: 目录树节点 ID（wiki_2.0 API 用），用于定位文档、获取 workspaceId 和节点名、执行白名单检查\r\n- `docKey`: 文档内容标识符（suites/documents API 用），用于实际读写内容\r\n- `create-doc` 返回的 `docKey` 和 `nodeId` 可能是不同的值\r\n- `update-content`、`get-content` 等命令**优先使用 `nodeId`** 作为 `--docKey` 参数\r\n- 如果使用 `docKey` 更新失败 (`nodeNotExist`),请改用 `nodeId`\r\n- 经验：大多数情况下 `nodeId` 可直接用作 `docKey`，少数情况需要真实 `docKey`\r\n\r\n**示例:**\r\n\r\n```bash\r\n# 创建文档后，使用返回的 nodeId 进行更新\r\nnode .../index.js create-doc --workspaceId=xxx --name=\"新文档\"\r\n# 返回:{ \"docKey\": \"abc123\", \"nodeId\": \"xyz789\", ... }\r\n\r\n# ✅ 正确：使用 nodeId 更新\r\nnode .../index.js update-content --docKey=xyz789 --content=\"...\"\r\n\r\n# ❌ 可能失败：使用 docKey 更新\r\nnode .../index.js update-content --docKey=abc123 --content=\"...\"\r\n```\r\n\r\n## 参考\r\n\r\n- 详细说明见 `README.md`\r\n- 钉钉开放平台文档:[knowledge-base-overview](https://open.dingtalk.com/document/development/knowledge-base-overview)\r\n- API Explorer:[open-dev.dingtalk.com/apiExplorer](https://open-dev.dingtalk.com/apiExplorer)\n\nFile v1.0.5:README.md\n\n# 钉钉文档管理技能 (dingtalk-doc)\r\n\r\n> [https://clawhub.ai/shyzhen/dingtalk-doc](https://clawhub.ai/shyzhen/dingtalk-doc)\r\n> \r\n> 是 [dingtalk-doc-enterprise](https://clawhub.ai/shyzhen/dingtalk-doc-enterprise) 的升级版(支持写入范围控制)\r\n\r\n通过钉钉开放平台 API 读取和管理钉钉文档、钉钉知识库中的文档。本文件面向人类读者，重点说明配置、上手方式和排障思路；执行规则与触发策略见 `SKILL.md`。\r\n\r\n## 能力概览\r\n\r\n- 支持列出、搜索、读取钉钉文档\r\n- 支持创建、更新、删除钉钉文档\r\n- 支持多个 workspace 共用一份白名单配置\r\n- 写入操作按 workspace + 节点名白名单控制，读取操作不受白名单限制\r\n- 当前用户身份从钉钉连接器注入的 `sender_id` 自动解析\r\n\r\n## 文档分工\r\n\r\n- `SKILL.md`：给 agent 的执行手册，强调触发条件、命令映射和失败处理\r\n- `README.md`：给人的配置说明、背景信息、排障参考\r\n\r\n## 快速开始\r\n\r\n### 1. 配置环境变量\r\n\r\n本 skill 可运行在 Windows、Linux、macOS；核心要求只有：\r\n\r\n- 安装 `node`\r\n- 配置 `DINGTALK_CLIENTID` / `DINGTALK_CLIENTSECRET`\r\n\r\n**编辑 `~/.openclaw/.env` 文件（推荐，优先级最高）**\r\n\r\nOpenClaw 会在启动时自动加载此文件中的环境变量。\r\n\r\n```bash\r\n# ~/.openclaw/.env\r\nDINGTALK_CLIENTID=dingxxxxxx\r\nDINGTALK_CLIENTSECRET=your_secret\r\n```\r\n\r\n说明：\r\n\r\n- `DINGTALK_CLIENTID`：钉钉应用 Client ID\r\n- `DINGTALK_CLIENTSECRET`：钉钉应用 Client Secret\r\n- `OPENCLAW_SENDER_ID` / `DINGTALK_SENDER_ID`：通常由 OpenClaw / 钉钉连接器自动注入；如需手工调用 CLI，也可以用 `--senderId=...`\r\n- `DINGTALK_DEBUG=true`：可选，仅输出请求方法、接口路径、状态码、requestId 等调试信息，不再打印文档正文和请求体\r\n- `operatorId` 不需要手工配置，脚本会优先从 `--senderId` 或 `OPENCLAW_SENDER_ID` / `DINGTALK_SENDER_ID` 解析当前钉钉用户\r\n\r\n### 2. 确认 workspaceId\r\n\r\n- 白名单配置：只支持 【钉钉文档 -> 知识库】下新建的根目录，也就是说可写范围必须在【知识库】中。\r\n- 直接发一篇文档给你的钉钉机器人让他随便写入一段话，就会触发白名单检查，机器人会主动告知你当前文档的workspaceId，以及如何配置。\r\n\r\n### 3. 配置白名单\r\n\r\n说明：\r\n\r\n- 读取操作不依赖白名单文件\r\n- 写入操作才需要 `config/whitelist.json`\r\n- 如果该文件不存在，写操作会被拒绝，并提示用户手动创建\r\n\r\n在 `config/whitelist.json` 中声明允许写入的 workspace 和节点名：\r\n\r\n```json\r\n{\r\n  \"workspaces\": [\r\n    {\r\n      \"workspaceId\": \"eLvJDSRX3l4moO87\",\r\n      \"workspaceName\": \"AI可写知识库一级目录\",\r\n      \"allowRootWrite\": false,\r\n      \"whitelist\": [\"/\"]\r\n    }\r\n  ]\r\n}\r\n```\r\n\r\n字段说明：\r\n\r\n- `workspaceId`：知识库 ID，必填 （只支持【钉钉文档 -> 知识库】下的目录）\r\n- `workspaceName`：可选，便于识别\r\n- `allowRootWrite`：保留字段；当前写入校验主要使用 `whitelist`\r\n- `whitelist`：允许写入的节点名（文档名）列表\r\n\r\n白名单规则：\r\n\r\n- 读取操作不检查白名单\r\n- 写入操作必须命中白名单\r\n- 未配置的 workspace 默认禁止写入\r\n- `\"/\"` 表示允许写入当前 `workspaceId` 下的所有节点\r\n- 细粒度白名单按节点名匹配，例如 `\"/三级文档.adoc\"`；当前已验证的节点详情接口不会返回完整父目录路径\r\n\r\n## 常用命令\r\n\r\n这里只保留几个面向人的常见示例；完整命令映射见 `SKILL.md`。\r\n\r\n读取：\r\n\r\n```bash\r\n# 当前目录运行（跨平台通用）\r\nnode scripts/index.js list-workspaces\r\nnode scripts/index.js list-docs --workspaceId=OQ0xySj6ng7lX58B --parentNodeId=root\r\nnode scripts/index.js get-doc --nodeId=YQBnd5ExVE0PDnezU2PK2RK6WyeZqMmz\r\nnode scripts/index.js get-content --nodeId=YQBnd5ExVE0PDnezU2PK2RK6WyeZqMmz\r\nnode scripts/index.js get-content --docKey=真实docKey --nodeId=YQBnd5ExVE0PDnezU2PK2RK6WyeZqMmz\r\n```\r\n\r\n写入：\r\n\r\n```bash\r\n# 当前目录运行（跨平台通用）\r\nnode scripts/index.js create-doc --workspaceId=OQ0xySj6ng7lX58B --name=\"新文档\" --docType=DOC --parentNodeId=root\r\nnode scripts/index.js update-content --nodeId=YQBnd5ExVE0PDnezU2PK2RK6WyeZqMmz --content=\"# 标题\\n\\n内容\"\r\nnode scripts/index.js update-content --docKey=真实docKey --nodeId=YQBnd5ExVE0PDnezU2PK2RK6WyeZqMmz --content=\"# 标题\\n\\n内容\"\r\n```\r\n\r\n白名单自检：\r\n\r\n```bash\r\n# 当前目录运行（跨平台通用）\r\nnode scripts/whitelist.js check \"/三级文档.adoc\"\r\n```\r\n\r\n`docType` 支持：\r\n\r\n- `DOC`\r\n- `WORKBOOK`\r\n- `MIND`\r\n- `FOLDER`\r\n\r\n## 目录结构\r\n\r\n```text\r\ndingtalk-doc/\r\n├── SKILL.md\r\n├── README.md\r\n├── package.json\r\n├── config/\r\n│   └── whitelist.json\r\n└── scripts/\r\n    ├── index.js\r\n    ├── dingtalk-client.js\r\n    └── whitelist.js\r\n```\r\n\r\n## 已知返回结构\r\n\r\n以下结构来自实测文档 `https://alidocs.dingtalk.com/i/nodes/oP0MALyR8kOd5BGacKv6NbxE83bzYmDO?utm_scene=team_space`，文档名是“三级文档”，真实位于三级子目录中。\r\n\r\n`get-doc --nodeId=...` / `GET /v2.0/wiki/nodes/{nodeId}` 返回的 `node` 里有 `workspaceId` 和 `name`，但没有返回完整父目录链路：\r\n\r\n```json\r\n{\r\n  \"node\": {\r\n    \"name\": \"三级文档.adoc\",\r\n    \"nodeId\": \"oP0MALyR8kOd5BGacKv6NbxE83bzYmDO\",\r\n    \"workspaceId\": \"eLvJDSRX3l4moO87\",\r\n    \"type\": \"FILE\",\r\n    \"category\": \"ALIDOC\",\r\n    \"url\": \"https://alidocs.dingtalk.com/i/nodes/oP0MALyR8kOd5BGacKv6NbxE83bzYmDO\"\r\n  }\r\n}\r\n```\r\n\r\n容易犯错的点：\r\n\r\n- 不要把白名单理解为真实目录路径；当前脚本只能可靠使用 `workspaceId + node.name` 做写入白名单。\r\n- 即使文档在三级子目录中，节点详情也可能只返回 `\"name\": \"三级文档.adoc\"`，不会返回 `\"/一级/二级/三级文档.adoc\"`。\r\n- `get-content --nodeId=...` / `GET /v1.0/doc/suites/documents/{docKey}/blocks` 返回的是块结构，例如 paragraph 的 `id`、`index`、`text`，不返回文档路径或父目录信息。\r\n- `nodeId` 用于查询节点详情和校验真实 `workspaceId`；`docKey` 用于 suites/documents 内容读写接口。多数情况下可先用 `nodeId` 作为 `docKey` 尝试。\r\n- `insert-block`、`modify-block`、`delete-block` 已在真实文档上测试通过；`append-text` 对应的 `paragraphs/{blockId}/text` 当前返回 `InvalidAction.NotFound`，不要依赖它。\r\n\r\n## 权限与接口\r\n\r\n常用接口：\r\n\r\n- `POST /v1.0/doc/workspaces/{workspaceId}/docs`\r\n- `GET /v2.0/wiki/nodes`\r\n- `GET /v2.0/wiki/nodes/{nodeId}`\r\n- `DELETE /v1.0/doc/workspaces/{workspaceId}/docs/{nodeId}`\r\n- `GET /v1.0/doc/suites/documents/{docKey}/blocks`\r\n- `POST /v1.0/doc/suites/documents/{docKey}/overwriteContent`\r\n- `POST /v1.0/doc/suites/documents/{docKey}/blocks`\r\n- `PUT /v1.0/doc/suites/documents/{docKey}/blocks/{blockId}`\r\n- `DELETE /v1.0/doc/suites/documents/{docKey}/blocks/{blockId}`\r\n\r\n常见所需权限：\r\n\r\n- `Document.Workspace.Read`\r\n- `Document.Workspace.Write`\r\n- `Document.WorkspaceDocument.Read`\r\n- `Document.WorkspaceDocument.Write`\r\n- `Wiki.Node.Read`\r\n\r\n## 常见问题\r\n\r\n| 问题 | 原因 | 处理方式 |\r\n|------|------|---------|\r\n| 缺少 `sender_id` | 钉钉连接器未传递当前用户 ID | 检查是否注入了 `OPENCLAW_SENDER_ID` 或 `DINGTALK_SENDER_ID` |\r\n| workspace 未配置白名单 | `config/whitelist.json` 没有对应 workspace | 补充对应 `workspaceId` 配置 |\r\n| 节点名不在白名单内 | 写入目标节点名未命中规则 | 调整白名单节点名或使用 `\"/\"` 允许整个 workspace |\r\n| `forbidden.accessDenied` | 应用权限不足或白名单不通过 | 检查钉钉应用权限和白名单 |\r\n| `invalidRequest.workspaceNode.parentNotFound` | `parentNodeId` 错误 | 重新确认父节点 ID |\r\n\r\n## 参考\r\n\r\n- 钉钉开放平台文档：[knowledge-base-overview](https://open.dingtalk.com/document/development/knowledge-base-overview)\r\n- API Explorer：[open-dev.dingtalk.com/apiExplorer](https://open-dev.dingtalk.com/apiExplorer)\r\n\r\n### API 参考\r\n\r\n钉钉知识库 API 分为两个系列：\r\n\r\n#### doc_1.0 - 知识库管理\r\n\r\n| API | 方法 | 路径 | 权限 |\r\n|-----|------|------|------|\r\n| 新建知识库 | POST | `/v1.0/doc/workspaces` | Document.Workspace.Write |\r\n| 获取知识库 | GET | `/v1.0/doc/workspaces/{workspaceId}` | Document.Workspace.Read |\r\n| 获取知识库列表 | GET | `/v1.0/doc/workspaces` | Document.Workspace.Read |\r\n| 创建知识库文档 | POST | `/v1.0/doc/workspaces/{workspaceId}/docs` | Document.WorkspaceDocument.Write |\r\n| 删除知识库文档 | DELETE | `/v1.0/doc/workspaces/{workspaceId}/docs/{nodeId}` | Document.WorkspaceDocument.Write |\r\n\r\n#### wiki_2.0 - 目录树管理\r\n\r\n| API | 方法 | 路径 | 权限 |\r\n|-----|------|------|------|\r\n| 获取节点 | GET | `/v2.0/wiki/nodes/{nodeId}` | Wiki.Node.Read |\r\n| 获取节点列表 | GET | `/v2.0/wiki/nodes` | Wiki.Node.Read |\r\n| 通过链接获取节点 | GET | `/v2.0/wiki/nodes/url` | Wiki.Node.Read |\r\n| 复制文档 | POST | `/v2.0/wiki/nodes/{nodeId}/copy` | Document.WorkspaceDocument.Write |\r\n\r\n#### suites/documents - 文档内容\r\n\r\n| API | 方法 | 路径 | 权限 |\r\n|-----|------|------|------|\r\n| 查询块结构 | GET | `/v1.0/doc/suites/documents/{docKey}/blocks` | Document.WorkspaceDocument.Read |\r\n| 覆写内容 | POST | `/v1.0/doc/suites/documents/{docKey}/overwriteContent` | Document.WorkspaceDocument.Write |\r\n| 插入块 | POST | `/v1.0/doc/suites/documents/{docKey}/blocks` | Document.WorkspaceDocument.Write |\r\n| 删除块 | DELETE | `/v1.0/doc/suites/documents/{docKey}/blocks/{blockId}` | Document.WorkspaceDocument.Write |\r\n\r\n\r\n#### 所需权限清单\r\n\r\n调用本 skill 需要开通以下钉钉应用权限：\r\n\r\n- `Document.Workspace.Read` - 知识库读权限\r\n- `Document.Workspace.Write` - 知识库写权限\r\n- `Document.WorkspaceDocument.Read` - 文档读权限\r\n- `Document.WorkspaceDocument.Write` - 文档写权限\r\n- `Wiki.Node.Read` - 节点读权限\r\n\r\n## 许可证\r\n\r\nMIT\n\nFile v1.0.5:_meta.json\n\n{\n  \"ownerId\": \"kn73t8gpykqmgx10ksfb6706r5842gge\",\n  \"slug\": \"dingtalk-doc\",\n  \"version\": \"1.0.5\",\n  \"publishedAt\": 1776908939400\n}\n\nFile v1.0.5:config/whitelist.json\n\n{\n  \"_comment\": \"⚠️ 此配置文件只能由用户手动编辑，AI 禁止修改！\",\n  \"workspaces\": [\n    {\n      \"workspaceId\": \"eLvJDSRX3l4moO87\",\n      \"workspaceName\": \"AI可写知识库一级目录\",\n      \"allowRootWrite\": true,\n      \"whitelist\": [\"/\"]\n    }\n  ]\n}\n\nFile v1.0.5:package.json\n\n{\n  \"name\": \"dingtalk-doc\",\n  \"version\": \"1.0.4\",\n  \"description\": \"钉钉文档管理技能 - 支持文档增删改查和白名单权限控制\",\n  \"main\": \"scripts/index.js\",\n  \"scripts\": {\n    \"get-token\": \"node scripts/dingtalk-client.js get-token\",\n    \"get-workspace-list\": \"node scripts/dingtalk-client.js get-workspace-list\",\n    \"list-docs\": \"node scripts/index.js list-docs\",\n    \"create-doc\": \"node scripts/index.js create-doc\",\n    \"delete-doc\": \"node scripts/index.js delete-doc\",\n    \"search\": \"node scripts/index.js search\",\n    \"get-content\": \"node scripts/index.js get-content\",\n    \"update-content\": \"node scripts/index.js update-content\",\n    \"check-permission\": \"node scripts/whitelist.js check\"\n  },\n  \"keywords\": [\n    \"dingtalk\",\n    \"knowledge-base\",\n    \"openclaw-skill\"\n  ],\n  \"author\": \"\",\n  \"license\": \"MIT\"\n}\n\nArchive v1.0.4: 8 files, 25811 bytes\n\nFiles: config/whitelist.json (278b), package.json (837b), README.md (10476b), scripts/dingtalk-client.js (20641b), scripts/index.js (30897b), scripts/whitelist.js (5086b), SKILL.md (18086b), _meta.json (131b)\n\nFile v1.0.4:SKILL.md\n\n---\r\nname: dingtalk-doc\r\ndescription: 钉钉文档管理技能。当用户发送的消息中包含 alidocs.dingtalk.com 链接、要求总结/读取/查看/更新钉钉文档或钉钉知识库文档，或当前上下文已明确对象是钉钉文档时使用。关键词：钉钉文档、钉钉知识库、alidocs、总结、读取、查看、更新、修改、文档、链接。\r\nmetadata:\r\n  {\r\n    \"openclaw\":\r\n      {\r\n        \"emoji\": \"📚\",\r\n        \"requires\":\r\n          {\r\n            \"bins\": [\"node\"],\r\n            \"env\": [\"DINGTALK_CLIENTID\", \"DINGTALK_CLIENTSECRET\"],\r\n          },\r\n      },\r\n  }\r\n---\r\n\r\n# 钉钉文档管理技能\r\n\r\n通过钉钉开放平台 API 管理钉钉文档与钉钉知识库内文档。`SKILL.md` 只保留 agent 执行所需规则;配置细节、示例、API 背景见 `README.md`。\r\n\r\n## 何时使用\r\n\r\n### 触发关键词\r\n\r\n只有在**已经确认对象是钉钉文档**时，同时消息包含以下**任一关键词**时，优先使用本 skill：\r\n\r\n| 类别 | 关键词 |\r\n|------|--------|\r\n| **平台名** | 钉钉文档、钉钉知识库、alidocs |\r\n| **读取类** | 总结、读取、查看、浏览、列出结构 |\r\n| **修改类** | 更新、修改、追加、删除、覆写 |\r\n| **对象** | 文档、链接、这篇、这个文档 |\r\n\r\n**组合示例：**\r\n- \"总结一下这篇钉钉文档\"\r\n- \"读取这个 alidocs 链接\"\r\n- \"更新文档内容\"\r\n- \"删除第三段\"\r\n\r\n### 触发场景（优先级从高到低）\r\n\r\n| 场景 | 示例 | 动作 |\r\n|------|------|------|\r\n| **钉钉文档链接** | `alidocs.dingtalk.com/i/nodes/xxx` | 根据意图选择：元数据→`get-doc`，正文内容→`get-content` |\r\n| **钉钉上下文 + 链接** | \"总结 https://alidocs.dingtalk.com/...\" | 调用 `get-content` 读取内容后总结 |\r\n| **明确命令** | \"总结这篇文档\"、\"读取这个 alidocs 链接\" | 根据意图选择命令 |\r\n| **已知上下文是钉钉文档** | 前文已给出 alidocs 链接，后续说\"更新文档\"、\"删除某段\"、\"在第三段后追加\" | 调用对应命令 |\r\n| **结构查询** | \"列出结构\"、\"这个 alidocs 有哪些章节\" | 调用 `get-content` |\r\n| **块级操作** | \"删除第三段\"、\"修改这个段落\"、\"在这里插入一段\" | 先用 `get-content` 获取 blockId 和位置，再调用 `delete-block`/`modify-block`/`insert-block` |\r\n\r\n### 不触发的场景\r\n- 查询本地文件、离线文档或普通文本内容，且不需要调用钉钉 API\r\n- 没有文档链接、docKey、或明确钉钉文档上下文，却要求修改文档\r\n- 用户只说\"总结文档\"\"更新这个链接\"等泛化请求，但上下文无法确认对象是钉钉文档\r\n- 与钉钉无关的文档系统，例如本地 Markdown、飞书文档、语雀、Google Docs\r\n\r\n## 运行前提\r\n\r\n### 环境变量\r\n\r\n必须配置以下环境变量 (在 Gateway 环境中):\r\n\r\n- `DINGTALK_CLIENTID` - 钉钉应用 Client ID (AppKey)\r\n- `DINGTALK_CLIENTSECRET` - 钉钉应用 Client Secret (AppSecret)\r\n\r\n可选环境变量:\r\n\r\n- `OPENCLAW_SENDER_ID` / `DINGTALK_SENDER_ID` - 由 OpenClaw / 钉钉连接器注入的当前用户 sender_id；也可以通过命令行 `--senderId=` 显式传入\r\n- `DINGTALK_DEBUG` - 设置为 `true` 启用调试模式；仅输出方法、路径（查询参数已脱敏）、状态码、requestId 等，不打印文档正文与完整请求体\r\n\r\n### operatorId 获取方式\r\n\r\n不需要在配置中指定!系统会自动从当前会话获取:\r\n\r\n1. 从 `OPENCLAW_SENDER_ID` 或 `DINGTALK_SENDER_ID` 获取 sender_id\r\n2. 调用钉钉 API 查询对应的 unionId\r\n3. 使用 unionId 作为 operatorId\r\n\r\n如果获取失败，会显示友好的错误提示。\r\n\r\n## 执行规则\r\n\r\n- 读取操作不受白名单限制\r\n- 写入操作必须通过白名单检查;未配置 workspace 或节点名不在白名单内时，一律拒绝!**没有任何方式可以绕过白名单检查!**\r\n- `whitelist: [\"/\"]` 表示允许写入该 workspace 下的所有节点;更细粒度控制请配置具体文档名，例如 `\"/三级目录测试文档.adoc\"`\r\n- 白名单配置文件 `config/whitelist.json` 只能由用户手动修改;AI 只能读取、解释、提示用户手动调整，不能替用户改\r\n- 如果用户没有给出 `workspaceId`,先运行 `list-workspaces`\r\n- 如果用户没有给出目标文档的 `nodeId` / `docKey`,先运行 `list-docs`、`search` 或 `get-doc` 确认目标\r\n\r\n## URL 解析规则\r\n\r\n当用户提供钉钉文档 URL 时，按以下规则提取 `nodeId`:\r\n\r\n**URL 格式:** `https://alidocs.dingtalk.com/i/nodes/<nodeId>?...`\r\n\r\n**提取方法:**\r\n1. 从 URL 中提取 `nodes/` 和 `?` 之间的部分\r\n2. 该部分即为 `nodeId`\r\n\r\n**示例:**\r\n```\r\nURL: https://alidocs.dingtalk.com/i/nodes/oP0MALyR8kOd5BGacKv6NbxE83bzYmDO?utm_scene=team_space\r\nnodeId: oP0MALyR8kOd5BGacKv6NbxE83bzYmDO\r\n```\r\n\r\n**提取到 nodeId 后的操作:**\r\n1. **获取文档元数据**（名称、知识库 ID 等）：`get-doc --nodeId=<提取的 nodeId>`\r\n2. **获取文档内容**（总结、读取正文）：`get-content --docKey=<提取的 nodeId>`\r\n3. 注意：`get-doc` 只返回元数据，`get-content` 才返回正文内容\r\n\r\n## 执行入口\r\n\r\n- `scripts/index.js`:主入口\r\n- `scripts/whitelist.js`:辅助检查白名单配置\r\n\r\n**跨平台说明：**\r\n\r\n- 本 skill 的脚本基于 Node.js 内置模块实现，Windows、Linux、macOS 只要安装了 `node` 并配置好环境变量，都可以运行\r\n- 推荐优先使用相对路径执行：`node scripts/index.js ...`，这样最不容易受平台路径差异影响\r\n\r\n**路径示例：**\r\n\r\nWindows PowerShell 中 `~` 不会自动展开，建议使用以下方式之一：\r\n\r\n```bash\r\n# ✅ 使用 $env:USERPROFILE\r\nnode $env:USERPROFILE\\.openclaw\\skills\\dingtalk-doc\\scripts\\index.js\r\n\r\n# ✅ 或使用完整绝对路径\r\nnode C:\\Users\\zhenhuaixiu\\.openclaw\\skills\\dingtalk-doc\\scripts\\index.js\r\n\r\n# ❌ 错误（~ 不会展开）\r\nnode ~/.openclaw/skills/dingtalk-doc/scripts/index.js\r\n```\r\n\r\nLinux / macOS Shell 示例：\r\n\r\n```bash\r\n# ✅ 当前目录下直接运行（推荐）\r\nnode scripts/index.js list-workspaces\r\n\r\n# ✅ 或使用完整绝对路径\r\nnode ~/.openclaw/skills/dingtalk-doc/scripts/index.js list-workspaces\r\n```\r\n\r\n## 命令映射\r\n\r\n**路径说明：**\r\n\r\n- Windows 示例中的 `$env:USERPROFILE\\.openclaw` 会展开为 `C:\\Users\\<用户名>\\.openclaw`\r\n- Linux / macOS 示例中的 `~/.openclaw` 会展开为用户主目录下的 `.openclaw`\r\n- 如果当前工作目录已经在 skill 根目录，直接使用 `node scripts/index.js ...` 即可\r\n\r\n### 读取操作\r\n\r\n| 命令 | 用途 | API 端点 |\r\n|------|------|---------|\r\n| `list-workspaces` | 获取知识库列表 | `GET /v2.0/wiki/mineWorkspaces` |\r\n| `list-docs` | 获取知识库中文档列表 | `GET /v2.0/wiki/nodes` |\r\n| `get-doc` | 获取**文档元数据**（名称、ID、创建者、字数等） | `GET /v2.0/wiki/nodes/{nodeId}` |\r\n| `get-content` | 获取**文档正文内容**（段落、标题、列表等块结构） | `GET /v1.0/doc/suites/documents/{docKey}/blocks` |\r\n| `search` | 搜索文档 | `GET /v1.0/doc/workspaces/{workspaceId}/docs` |\r\n\r\n**示例：**\r\n```bash\r\n# Linux / macOS\r\nnode ~/.openclaw/skills/dingtalk-doc/scripts/index.js list-workspaces\r\nnode ~/.openclaw/skills/dingtalk-doc/scripts/index.js list-docs --workspaceId=YRBGvyxxx --parentNodeId=root\r\nnode ~/.openclaw/skills/dingtalk-doc/scripts/index.js get-doc --nodeId=YQBnd5ExVE0PDnezU2PK2RK6WyeZqMmz\r\nnode ~/.openclaw/skills/dingtalk-doc/scripts/index.js get-content --docKey=YQBnd5ExVE0PDnezU2PK2RK6WyeZqMmz\r\nnode ~/.openclaw/skills/dingtalk-doc/scripts/index.js search --workspaceId=YRBGvyxxx --keyword=\"需求\"\r\n\r\n# Windows PowerShell\r\nnode $env:USERPROFILE\\.openclaw\\skills\\dingtalk-doc\\scripts\\index.js list-workspaces\r\nnode $env:USERPROFILE\\.openclaw\\skills\\dingtalk-doc\\scripts\\index.js list-docs --workspaceId=YRBGvyxxx --parentNodeId=root\r\nnode $env:USERPROFILE\\.openclaw\\skills\\dingtalk-doc\\scripts\\index.js get-doc --nodeId=YQBnd5ExVE0PDnezU2PK2RK6WyeZqMmz\r\nnode $env:USERPROFILE\\.openclaw\\skills\\dingtalk-doc\\scripts\\index.js get-content --docKey=YQBnd5ExVE0PDnezU2PK2RK6WyeZqMmz\r\nnode $env:USERPROFILE\\.openclaw\\skills\\dingtalk-doc\\scripts\\index.js search --workspaceId=YRBGvyxxx --keyword=\"需求\"\r\n```\r\n\r\n### 写入操作\r\n\r\n```bash\r\n# Linux / macOS\r\nnode ~/.openclaw/skills/dingtalk-doc/scripts/index.js create-doc --workspaceId=YRBGvyxxx --name=\"新文档\" --docType=DOC --parentNodeId=root\r\nnode ~/.openclaw/skills/dingtalk-doc/scripts/index.js update-content --nodeId=YQBnd5ExVE0PDnezU2PK2RK6WyeZqMmz --content=\"# 标题\\n\\n内容\"\r\nnode ~/.openclaw/skills/dingtalk-doc/scripts/index.js update-content --nodeId=YQBnd5ExVE0PDnezU2PK2RK6WyeZqMmz --docKey=真实docKey --content=\"# 标题\\n\\n内容\"\r\nnode ~/.openclaw/skills/dingtalk-doc/scripts/index.js delete-doc --workspaceId=YRBGvyxxx --nodeId=YQBnd5ExVE0PDnezU2PK2RK6WyeZqMmz\r\n\r\n# Windows PowerShell\r\n# 创建文档\r\nnode $env:USERPROFILE\\.openclaw\\skills\\dingtalk-doc\\scripts\\index.js create-doc --workspaceId=YRBGvyxxx --name=\"新文档\" --docType=DOC --parentNodeId=root\r\n# 返回：{ \"docKey\": \"abc123\", \"nodeId\": \"xyz789\", ... }\r\n\r\n# 更新文档内容（整篇覆写，替换全部内容）\r\n# ✅ 推荐：只用 nodeId（大多数情况够用）\r\nnode $env:USERPROFILE\\.openclaw\\skills\\dingtalk-doc\\scripts\\index.js update-content --nodeId=YQBnd5ExVE0PDnezU2PK2RK6WyeZqMmz --content=\"# 标题\\n\\n内容\"\r\n\r\n# ✅ 备选：如果上面失败，传入真实的 docKey\r\nnode $env:USERPROFILE\\.openclaw\\skills\\dingtalk-doc\\scripts\\index.js update-content --nodeId=YQBnd5ExVE0PDnezU2PK2RK6WyeZqMmz --docKey=真实 docKey --content=\"# 标题\\n\\n内容\"\r\n\r\n# 删除文档\r\nnode $env:USERPROFILE\\.openclaw\\skills\\dingtalk-doc\\scripts\\index.js delete-doc --workspaceId=YRBGvyxxx --nodeId=YQBnd5ExVE0PDnezU2PK2RK6WyeZqMmz\r\n```\r\n\r\n### 块级操作（精细修改单个段落/元素）\r\n\r\n说明：\r\n\r\n- `insert-block`、`modify-block`、`delete-block` 已通过真实文档测试\r\n- `append-text` 对应的公开 API 当前返回 `InvalidAction.NotFound`，不要再调用或承诺\r\n- 如果用户要“追加内容”，优先改成“插入一个新段落”或“读取原段落后使用 `modify-block` 整块替换”\r\n\r\n```bash\r\n# Linux / macOS\r\nnode ~/.openclaw/skills/dingtalk-doc/scripts/index.js delete-block --nodeId=YQBnd5ExVE0PDnezU2PK2RK6WyeZqMmz --blockId=blk123\r\nnode ~/.openclaw/skills/dingtalk-doc/scripts/index.js modify-block --nodeId=YQBnd5ExVE0PDnezU2PK2RK6WyeZqMmz --blockId=blk123 --element='{\"blockType\":\"paragraph\",\"paragraph\":{\"text\":\"新内容\"}}'\r\nnode ~/.openclaw/skills/dingtalk-doc/scripts/index.js insert-block --nodeId=YQBnd5ExVE0PDnezU2PK2RK6WyeZqMmz --element='{\"blockType\":\"paragraph\",\"paragraph\":{\"text\":\"插入的内容\"}}' --position=3\r\n\r\n# Windows PowerShell\r\n# 删除块元素（删除某个段落/标题/列表项）\r\nnode $env:USERPROFILE\\.openclaw\\skills\\dingtalk-doc\\scripts\\index.js delete-block --nodeId=YQBnd5ExVE0PDnezU2PK2RK6WyeZqMmz --blockId=blk123\r\n\r\n# 修改块元素（替换单个块的内容，不影响其他部分）\r\nnode $env:USERPROFILE\\.openclaw\\skills\\dingtalk-doc\\scripts\\index.js modify-block --nodeId=YQBnd5ExVE0PDnezU2PK2RK6WyeZqMmz --blockId=blk123 --element='{\"blockType\":\"paragraph\",\"paragraph\":{\"text\":\"新内容\"}}'\r\n\r\n# 插入块元素（在指定位置插入新段落/标题等）\r\nnode $env:USERPROFILE\\.openclaw\\skills\\dingtalk-doc\\scripts\\index.js insert-block --nodeId=YQBnd5ExVE0PDnezU2PK2RK6WyeZqMmz --element='{\"blockType\":\"paragraph\",\"paragraph\":{\"text\":\"插入的内容\"}}' --position=3\r\n```\r\n\r\n### 参数说明\r\n\r\n- `docType`: `DOC`(文字) | `WORKBOOK`(表格) | `MIND`(脑图) | `FOLDER`(文件夹)\r\n- `nodeId`: **必填**，节点 ID（用于白名单检查）\r\n  - 从文档链接 `alidocs.dingtalk.com/i/nodes/xxx` 提取 `xxx` 部分\r\n  - 通过 `get-doc --nodeId=xxx` 或 `get-content --docKey=xxx` 确认\r\n- `docKey`: **可选**，真实的文档标识符（用于实际写入 API）\r\n  - 如果不传，默认使用 `nodeId` 代替\r\n  - 仅在 `nodeId` 作为 `docKey` 写入失败时，才需要传入真实的 `docKey`\r\n  - 真实 `docKey` 可通过 createDoc 返回值或钉钉 API Explorer 获取\r\n- `blockId`: 块 ID，通过 `get-content` 获取文档结构后得到（块级操作必需）\r\n- `element`: 块元素 JSON 对象（**会自动解析**，直接传 JSON 字符串即可）\r\n  - 示例：`--element='{\"blockType\":\"paragraph\",\"paragraph\":{\"text\":\"新内容\"}}'`\r\n- `position`: 插入位置（可选），数字，**支持 0**（表示插到最前面）\r\n- `workspaceId`: 知识库 ID（可选）\r\n  - 注意：写入操作会通过 `nodeId` 查询节点真实 `workspaceId`，传入 `--workspaceId` 时只能作为一致性校验，不能跳过查询\r\n  - 适用场景：已知 `workspaceId` 且想显式校验目标文档属于该知识库，但 `nodeId` 仍必需\r\n\r\n### 命令选择指南\r\n\r\n| 需求 | 使用命令 | 说明 |\r\n|------|---------|------|\r\n| 重写整篇文档 | `update-content --nodeId=xxx --content=\"...\"` | 替换全部内容 |\r\n| 只修改某个段落 | `modify-block --nodeId=xxx --blockId=blk --element='{...}'` | 只影响单个块 |\r\n| 在当前位置新增一段 | `insert-block --nodeId=xxx --element='{...}' --position=3` | 插入一个新块，更适合“追加一段”的需求 |\r\n| 删除某一段/标题 | `delete-block --nodeId=xxx --blockId=blk` | 删除块 |\r\n| 插入新段落/标题 | `insert-block --nodeId=xxx --element='{...}' --position=3` | 在指定位置插入 |\r\n\r\n## 常见问题\r\n\r\n1. **\"无法获取文档信息\"错误**: \r\n   - 确保传入的是 `nodeId`（从文档链接 `/i/nodes/xxx` 提取）\r\n   - `--workspaceId` 不能替代 `nodeId`，只能作为额外一致性校验\r\n   \r\n2. **\"paramError\" / JSON 解析失败**: \r\n   - `--element` 必须是合法的 JSON 格式，检查引号转义\r\n   - PowerShell 中用单引号包裹：`--element='{\"type\":\"paragraph\"}'`\r\n   \r\n3. **更新失败（nodeNotExist 等）**: \r\n   - 尝试传入真实的 `docKey`：`update-content --nodeId=xxx --docKey=真实 docKey --content=\"...\"`\r\n   - createDoc 返回的 `docKey` 和 `nodeId` 可能不同\r\n\r\n4. **nodeId 和 docKey 到底有什么区别？**\r\n   - `nodeId`: 目录树节点 ID（wiki_2.0 API 用），用于定位文档、获取 workspaceId 和节点名、执行白名单检查\r\n   - `docKey`: 文档内容标识符（suites/documents API 用），用于实际读写内容\r\n   - 经验：大多数情况下 `nodeId` 可直接用作 `docKey`，少数情况需要真实 `docKey`\r\n\r\n5. **为什么没有 append-text？**\r\n   - 当前公开接口 `POST /v1.0/doc/suites/documents/{docKey}/paragraphs/{blockId}/text` 在真实测试中返回 `InvalidAction.NotFound`\r\n   - 因此本 skill 不再承诺 `append-text`，请使用 `insert-block` 或 `modify-block`\r\n\r\n## 推荐流程\r\n\r\n1. 先确认凭证和 sender_id 是否可用。\r\n2. 需要定位文档所在知识库时先跑 `list-workspaces`。\r\n3. 需要定位文档时先跑 `list-docs`、`search`、`get-doc`。\r\n4. 执行写操作前，默认假设会触发白名单校验，不要跳过读取确认步骤。\r\n5. 如果写入被拒绝，只说明是哪个 workspace / 节点名未通过白名单，并提示用户手动调整 `config/whitelist.json`。\r\n\r\n## 常见失败\r\n\r\n- 缺少 sender_id:检查钉钉连接器是否注入 `OPENCLAW_SENDER_ID` / `DINGTALK_SENDER_ID`\r\n- `forbidden.accessDenied`:检查应用权限或白名单\r\n- `invalidRequest.workspaceNode.parentNotFound`:检查 `parentNodeId`\r\n- `权限拒绝：知识库 xxx 未配置白名单`:让用户手动补充 `config/whitelist.json`\r\n- `nodeNotExist`(更新内容时):**尝试使用 nodeId 代替 docKey** - 钉钉 API 中 createDoc 返回的 docKey 和 overwriteContent 需要的 docKey 可能不一致\r\n- `blockNotExist`(块级操作时):先用 `get-content` 获取文档结构，确认 blockId 正确\r\n- `paramError`(modify-block/insert-block):检查 `--element` 参数是否是合法的 JSON 格式\r\n\r\n## 重要提示\r\n\r\n### get-doc vs get-content\r\n\r\n| 命令 | 用途 | 返回内容 | 何时使用 |\r\n|------|------|---------|---------|\r\n| `get-doc` | 获取**文档元数据** | `data.node`：名称、ID、创建者、修改时间、字数、workspaceId | 确认文档存在、获取文档基本信息、定位知识库 |\r\n| `get-content` | 获取**文档正文** | `data.result.data[]`：段落、标题、列表等块结构 | 总结内容、读取正文、准备修改文档 |\r\n\r\n**关键区别：**\r\n- `get-doc` → `GET /v2.0/wiki/nodes/{nodeId}` → **不包含正文内容**\r\n- `get-content` → `GET /v1.0/doc/suites/documents/{docKey}/blocks` → **包含正文块结构**\r\n- **总结、读取内容时，始终使用 `get-content`，不要用 `get-doc`**\r\n\r\n### docKey vs nodeId\r\n\r\n- `nodeId`: 目录树节点 ID（wiki_2.0 API 用），用于定位文档、获取 workspaceId 和节点名、执行白名单检查\r\n- `docKey`: 文档内容标识符（suites/documents API 用），用于实际读写内容\r\n- `create-doc` 返回的 `docKey` 和 `nodeId` 可能是不同的值\r\n- `update-content`、`get-content` 等命令**优先使用 `nodeId`** 作为 `--docKey` 参数\r\n- 如果使用 `docKey` 更新失败 (`nodeNotExist`),请改用 `nodeId`\r\n- 经验：大多数情况下 `nodeId` 可直接用作 `docKey`，少数情况需要真实 `docKey`\r\n\r\n**示例:**\r\n\r\n```bash\r\n# 创建文档后，使用返回的 nodeId 进行更新\r\nnode .../index.js create-doc --workspaceId=xxx --name=\"新文档\"\r\n# 返回:{ \"docKey\": \"abc123\", \"nodeId\": \"xyz789\", ... }\r\n\r\n# ✅ 正确：使用 nodeId 更新\r\nnode .../index.js update-content --docKey=xyz789 --content=\"...\"\r\n\r\n# ❌ 可能失败：使用 docKey 更新\r\nnode .../index.js update-content --docKey=abc123 --content=\"...\"\r\n```\r\n\r\n## 参考\r\n\r\n- 详细说明见 `README.md`\r\n- 钉钉开放平台文档:[knowledge-base-overview](https://open.dingtalk.com/document/development/knowledge-base-overview)\r\n- API Explorer:[open-dev.dingtalk.com/apiExplorer](https://open-dev.dingtalk.com/apiExplorer)\n\nFile v1.0.4:README.md\n\n# 钉钉文档管理技能 (dingtalk-doc)\r\n\r\n通过钉钉开放平台 API 读取和管理钉钉文档、钉钉知识库中的文档。本文件面向人类读者，重点说明配置、上手方式和排障思路；执行规则与触发策略见 `SKILL.md`。\r\n\r\n## 能力概览\r\n\r\n- 支持列出、搜索、读取钉钉文档\r\n- 支持创建、更新、删除钉钉文档\r\n- 支持多个 workspace 共用一份白名单配置\r\n- 写入操作按 workspace + 节点名白名单控制，读取操作不受白名单限制\r\n- 当前用户身份从钉钉连接器注入的 `sender_id` 自动解析\r\n\r\n## 文档分工\r\n\r\n- `SKILL.md`：给 agent 的执行手册，强调触发条件、命令映射和失败处理\r\n- `README.md`：给人的配置说明、背景信息、排障参考\r\n\r\n## 快速开始\r\n\r\n### 1. 配置环境变量\r\n\r\n本 skill 可运行在 Windows、Linux、macOS；核心要求只有：\r\n\r\n- 安装 `node`\r\n- 配置 `DINGTALK_CLIENTID` / `DINGTALK_CLIENTSECRET`\r\n\r\n**编辑 `~/.openclaw/.env` 文件（推荐，优先级最高）**\r\n\r\nOpenClaw 会在启动时自动加载此文件中的环境变量。\r\n\r\n```bash\r\n# ~/.openclaw/.env\r\nDINGTALK_CLIENTID=dingxxxxxx\r\nDINGTALK_CLIENTSECRET=your_secret\r\n```\r\n\r\n说明：\r\n\r\n- `DINGTALK_CLIENTID`：钉钉应用 Client ID\n- `DINGTALK_CLIENTSECRET`：钉钉应用 Client Secret\n- `OPENCLAW_SENDER_ID` / `DINGTALK_SENDER_ID`：通常由 OpenClaw / 钉钉连接器自动注入；如需手工调用 CLI，也可以用 `--senderId=...`\n- `DINGTALK_DEBUG=true`：可选，仅输出请求方法、接口路径、状态码、requestId 等调试信息，不再打印文档正文和请求体\n- `operatorId` 不需要手工配置，脚本会优先从 `--senderId` 或 `OPENCLAW_SENDER_ID` / `DINGTALK_SENDER_ID` 解析当前钉钉用户\n\r\n### 1.1 运行路径说明\r\n\r\n推荐在 skill 目录下直接运行：\r\n\r\n```bash\r\nnode scripts/index.js list-workspaces\r\n```\r\n\r\n如果需要使用绝对路径：\r\n\r\nLinux / macOS：\r\n\r\n```bash\r\nnode ~/.openclaw/skills/dingtalk-doc/scripts/index.js list-workspaces\n```\r\n\r\nWindows PowerShell：\r\n\r\n```powershell\r\nnode $env:USERPROFILE\\.openclaw\\skills\\dingtalk-doc\\scripts\\index.js list-workspaces\n```\r\n\r\n注意：\r\n\r\n- Linux / macOS 中 `~` 会展开到用户主目录\r\n- Windows PowerShell 中 `~` 不适合这里，建议使用 `$env:USERPROFILE` 或完整绝对路径\r\n\r\n### 2. 确认 workspaceId\r\n\r\n最直接的方式是运行：\r\n\r\n```bash\r\nnode scripts/index.js list-workspaces\r\n```\r\n\r\n如果手里只有钉钉文档链接，也可以先拿 `nodeId` 再查详情。常见链接形态如下：\r\n\r\n```text\r\nhttps://alidocs.dingtalk.com/i/nodes/{nodeId}?utm_scene=team_space\r\n```\r\n\r\n然后运行：\r\n\r\n```bash\r\nnode scripts/index.js get-doc --nodeId=vy20BglGWOq9ZLj3F0M9ajK0JA7depqY\r\n```\r\n\r\n### 3. 配置白名单\n\n说明：\n\n- 读取操作不依赖白名单文件\n- 写入操作才需要 `config/whitelist.json`\n- 如果该文件不存在，写操作会被拒绝，并提示用户手动创建\n\n在 `config/whitelist.json` 中声明允许写入的 workspace 和节点名：\n\r\n```json\r\n{\r\n  \"workspaces\": [\r\n    {\r\n      \"workspaceId\": \"OQ0xySj6ng7lX58B\",\r\n      \"workspaceName\": \"主知识库\",\r\n      \"allowRootWrite\": false,\r\n      \"whitelist\": [\"/\"]\r\n    }\r\n  ]\r\n}\r\n```\r\n\r\n字段说明：\r\n\r\n- `workspaceId`：知识库 ID，必填\r\n- `workspaceName`：可选，便于识别\r\n- `allowRootWrite`：保留字段；当前写入校验主要使用 `whitelist`\r\n- `whitelist`：允许写入的节点名（文档名）列表\r\n\r\n白名单规则：\r\n\r\n- 读取操作不检查白名单\r\n- 写入操作必须命中白名单\r\n- 未配置的 workspace 默认禁止写入\r\n- `\"/\"` 表示允许写入当前 `workspaceId` 下的所有节点\r\n- 细粒度白名单按节点名匹配，例如 `\"/三级文档.adoc\"`；当前已验证的节点详情接口不会返回完整父目录路径\r\n\r\n## 常用命令\r\n\r\n这里只保留几个面向人的常见示例；完整命令映射见 `SKILL.md`。\r\n\r\n读取：\r\n\r\n```bash\r\n# 当前目录运行（跨平台通用）\r\nnode scripts/index.js list-workspaces\r\nnode scripts/index.js list-docs --workspaceId=OQ0xySj6ng7lX58B --parentNodeId=root\r\nnode scripts/index.js get-doc --nodeId=YQBnd5ExVE0PDnezU2PK2RK6WyeZqMmz\r\nnode scripts/index.js get-content --nodeId=YQBnd5ExVE0PDnezU2PK2RK6WyeZqMmz\r\nnode scripts/index.js get-content --docKey=真实docKey --nodeId=YQBnd5ExVE0PDnezU2PK2RK6WyeZqMmz\r\n```\r\n\r\n写入：\r\n\r\n```bash\r\n# 当前目录运行（跨平台通用）\r\nnode scripts/index.js create-doc --workspaceId=OQ0xySj6ng7lX58B --name=\"新文档\" --docType=DOC --parentNodeId=root\r\nnode scripts/index.js update-content --nodeId=YQBnd5ExVE0PDnezU2PK2RK6WyeZqMmz --content=\"# 标题\\n\\n内容\"\r\nnode scripts/index.js update-content --docKey=真实docKey --nodeId=YQBnd5ExVE0PDnezU2PK2RK6WyeZqMmz --content=\"# 标题\\n\\n内容\"\r\n```\r\n\r\n白名单自检：\r\n\r\n```bash\r\n# 当前目录运行（跨平台通用）\r\nnode scripts/whitelist.js check \"/三级文档.adoc\"\r\n```\r\n\r\n`docType` 支持：\r\n\r\n- `DOC`\r\n- `WORKBOOK`\r\n- `MIND`\r\n- `FOLDER`\r\n\r\n## 目录结构\r\n\r\n```text\ndingtalk-doc/\n├── SKILL.md\r\n├── README.md\r\n├── package.json\r\n├── config/\r\n│   └── whitelist.json\r\n└── scripts/\r\n    ├── index.js\r\n    ├── dingtalk-client.js\r\n    └── whitelist.js\r\n```\r\n\r\n## 已知返回结构\r\n\r\n以下结构来自实测文档 `https://alidocs.dingtalk.com/i/nodes/oP0MALyR8kOd5BGacKv6NbxE83bzYmDO?utm_scene=team_space`，文档名是“三级文档”，真实位于三级子目录中。\r\n\r\n`get-doc --nodeId=...` / `GET /v2.0/wiki/nodes/{nodeId}` 返回的 `node` 里有 `workspaceId` 和 `name`，但没有返回完整父目录链路：\r\n\r\n```json\r\n{\r\n  \"node\": {\r\n    \"name\": \"三级文档.adoc\",\r\n    \"nodeId\": \"oP0MALyR8kOd5BGacKv6NbxE83bzYmDO\",\r\n    \"workspaceId\": \"eLvJDSRX3l4moO87\",\r\n    \"type\": \"FILE\",\r\n    \"category\": \"ALIDOC\",\r\n    \"url\": \"https://alidocs.dingtalk.com/i/nodes/oP0MALyR8kOd5BGacKv6NbxE83bzYmDO\"\r\n  }\r\n}\r\n```\r\n\r\n容易犯错的点：\r\n\r\n- 不要把白名单理解为真实目录路径；当前脚本只能可靠使用 `workspaceId + node.name` 做写入白名单。\r\n- 即使文档在三级子目录中，节点详情也可能只返回 `\"name\": \"三级文档.adoc\"`，不会返回 `\"/一级/二级/三级文档.adoc\"`。\r\n- `get-content --nodeId=...` / `GET /v1.0/doc/suites/documents/{docKey}/blocks` 返回的是块结构，例如 paragraph 的 `id`、`index`、`text`，不返回文档路径或父目录信息。\n- `nodeId` 用于查询节点详情和校验真实 `workspaceId`；`docKey` 用于 suites/documents 内容读写接口。多数情况下可先用 `nodeId` 作为 `docKey` 尝试。\n- `insert-block`、`modify-block`、`delete-block` 已在真实文档上测试通过；`append-text` 对应的 `paragraphs/{blockId}/text` 当前返回 `InvalidAction.NotFound`，不要依赖它。\n\r\n## 权限与接口\r\n\r\n常用接口：\r\n\r\n- `POST /v1.0/doc/workspaces/{workspaceId}/docs`\r\n- `GET /v2.0/wiki/nodes`\r\n- `GET /v2.0/wiki/nodes/{nodeId}`\r\n- `DELETE /v1.0/doc/workspaces/{workspaceId}/docs/{nodeId}`\r\n- `GET /v1.0/doc/suites/documents/{docKey}/blocks`\n- `POST /v1.0/doc/suites/documents/{docKey}/overwriteContent`\n- `POST /v1.0/doc/suites/documents/{docKey}/blocks`\n- `PUT /v1.0/doc/suites/documents/{docKey}/blocks/{blockId}`\n- `DELETE /v1.0/doc/suites/documents/{docKey}/blocks/{blockId}`\n\r\n常见所需权限：\r\n\r\n- `Document.Workspace.Read`\r\n- `Document.Workspace.Write`\r\n- `Document.WorkspaceDocument.Read`\r\n- `Document.WorkspaceDocument.Write`\r\n- `Wiki.Node.Read`\r\n\r\n## 常见问题\r\n\r\n| 问题 | 原因 | 处理方式 |\r\n|------|------|---------|\r\n| 缺少 `sender_id` | 钉钉连接器未传递当前用户 ID | 检查是否注入了 `OPENCLAW_SENDER_ID` 或 `DINGTALK_SENDER_ID` |\r\n| workspace 未配置白名单 | `config/whitelist.json` 没有对应 workspace | 补充对应 `workspaceId` 配置 |\r\n| 节点名不在白名单内 | 写入目标节点名未命中规则 | 调整白名单节点名或使用 `\"/\"` 允许整个 workspace |\r\n| `forbidden.accessDenied` | 应用权限不足或白名单不通过 | 检查钉钉应用权限和白名单 |\r\n| `invalidRequest.workspaceNode.parentNotFound` | `parentNodeId` 错误 | 重新确认父节点 ID |\r\n\r\n## 参考\r\n\r\n- 钉钉开放平台文档：[knowledge-base-overview](https://open.dingtalk.com/document/development/knowledge-base-overview)\r\n- API Explorer：[open-dev.dingtalk.com/apiExplorer](https://open-dev.dingtalk.com/apiExplorer)\r\n\r\n### API 参考\r\n\r\n钉钉知识库 API 分为两个系列：\r\n\r\n#### doc_1.0 - 知识库管理\r\n\r\n| API | 方法 | 路径 | 权限 |\r\n|-----|------|------|------|\r\n| 新建知识库 | POST | `/v1.0/doc/workspaces` | Document.Workspace.Write |\r\n| 获取知识库 | GET | `/v1.0/doc/workspaces/{workspaceId}` | Document.Workspace.Read |\r\n| 获取知识库列表 | GET | `/v1.0/doc/workspaces` | Document.Workspace.Read |\r\n| 创建知识库文档 | POST | `/v1.0/doc/workspaces/{workspaceId}/docs` | Document.WorkspaceDocument.Write |\r\n| 删除知识库文档 | DELETE | `/v1.0/doc/workspaces/{workspaceId}/docs/{nodeId}` | Document.WorkspaceDocument.Write |\r\n\r\n#### wiki_2.0 - 目录树管理\r\n\r\n| API | 方法 | 路径 | 权限 |\r\n|-----|------|------|------|\r\n| 获取节点 | GET | `/v2.0/wiki/nodes/{nodeId}` | Wiki.Node.Read |\r\n| 获取节点列表 | GET | `/v2.0/wiki/nodes` | Wiki.Node.Read |\r\n| 通过链接获取节点 | GET | `/v2.0/wiki/nodes/url` | Wiki.Node.Read |\r\n| 复制文档 | POST | `/v2.0/wiki/nodes/{nodeId}/copy` | Document.WorkspaceDocument.Write |\r\n\r\n#### suites/documents - 文档内容\r\n\r\n| API | 方法 | 路径 | 权限 |\r\n|-----|------|------|------|\r\n| 查询块结构 | GET | `/v1.0/doc/suites/documents/{docKey}/blocks` | Document.WorkspaceDocument.Read |\r\n| 覆写内容 | POST | `/v1.0/doc/suites/documents/{docKey}/overwriteContent` | Document.WorkspaceDocument.Write |\r\n| 插入块 | POST | `/v1.0/doc/suites/documents/{docKey}/blocks` | Document.WorkspaceDocument.Write |\r\n| 删除块 | DELETE | `/v1.0/doc/suites/documents/{docKey}/blocks/{blockId}` | Document.WorkspaceDocument.Write |\r\n\r\n\r\n#### 所需权限清单\r\n\r\n调用本 skill 需要开通以下钉钉应用权限：\r\n\r\n- `Document.Workspace.Read` - 知识库读权限\r\n- `Document.Workspace.Write` - 知识库写权限\r\n- `Document.WorkspaceDocument.Read` - 文档读权限\r\n- `Document.WorkspaceDocument.Write` - 文档写权限\r\n- `Wiki.Node.Read` - 节点读权限\r\n\r\n## 许可证\r\n\r\nMIT\n\nFile v1.0.4:_meta.json\n\n{\n  \"ownerId\": \"kn73t8gpykqmgx10ksfb6706r5842gge\",\n  \"slug\": \"dingtalk-doc\",\n  \"version\": \"1.0.4\",\n  \"publishedAt\": 1776831002905\n}\n\nFile v1.0.4:config/whitelist.json\n\n{\n  \"_comment\": \"⚠️ 此配置文件只能由用户手动编辑，AI 禁止修改！\",\n  \"workspaces\": [\n    {\n      \"workspaceId\": \"eLvJDSRX3l4moO87\",\n      \"workspaceName\": \"AI可写知识库一级目录\",\n      \"allowRootWrite\": true,\n      \"whitelist\": [\"/\"]\n    }\n  ]\n}\n\nFile v1.0.4:package.json\n\n{\n  \"name\": \"dingtalk-doc\",\n  \"version\": \"1.0.4\",\n  \"description\": \"钉钉文档管理技能 - 支持文档增删改查和白名单权限控制\",\n  \"main\": \"scripts/index.js\",\n  \"scripts\": {\n    \"get-token\": \"node scripts/dingtalk-client.js get-token\",\n    \"get-workspace-list\": \"node scripts/dingtalk-client.js get-workspace-list\",\n    \"list-docs\": \"node scripts/index.js list-docs\",\n    \"create-doc\": \"node scripts/index.js create-doc\",\n    \"delete-doc\": \"node scripts/index.js delete-doc\",\n    \"search\": \"node scripts/index.js search\",\n    \"get-content\": \"node scripts/index.js get-content\",\n    \"update-content\": \"node scripts/index.js update-content\",\n    \"check-permission\": \"node scripts/whitelist.js check\"\n  },\n  \"keywords\": [\n    \"dingtalk\",\n    \"knowledge-base\",\n    \"openclaw-skill\"\n  ],\n  \"author\": \"\",\n  \"license\": \"MIT\"\n}\n\nArchive v1.0.3: 8 files, 25029 bytes\n\nFiles: config/whitelist.json (278b), package.json (837b), README.md (10050b), scripts/dingtalk-client.js (19717b), scripts/index.js (30147b), scripts/whitelist.js (5039b), SKILL.md (17767b), _meta.json (131b)\n\nFile v1.0.3:SKILL.md\n\n---\r\nname: dingtalk-doc\r\ndescription: 钉钉文档管理技能。当用户发送的消息中包含 alidocs.dingtalk.com 链接、要求总结/读取/查看/更新钉钉文档或钉钉知识库文档，或当前上下文已明确对象是钉钉文档时使用。关键词：钉钉文档、钉钉知识库、alidocs、总结、读取、查看、更新、修改、文档、链接。\r\nmetadata:\r\n  {\r\n    \"openclaw\":\r\n      {\r\n        \"emoji\": \"📚\",\r\n        \"requires\":\r\n          {\r\n            \"bins\": [\"node\"],\r\n            \"env\": [\"DINGTALK_CLIENTID\", \"DINGTALK_CLIENTSECRET\"],\r\n          },\r\n      },\r\n  }\r\n---\r\n\r\n# 钉钉文档管理技能\r\n\r\n通过钉钉开放平台 API 管理钉钉文档与钉钉知识库内文档。`SKILL.md` 只保留 agent 执行所需规则;配置细节、示例、API 背景见 `README.md`。\r\n\r\n## 何时使用\r\n\r\n### 触发关键词\r\n\r\n只有在**已经确认对象是钉钉文档**时，同时消息包含以下**任一关键词**时，优先使用本 skill：\r\n\r\n| 类别 | 关键词 |\r\n|------|--------|\r\n| **平台名** | 钉钉文档、钉钉知识库、alidocs |\r\n| **读取类** | 总结、读取、查看、浏览、列出结构 |\r\n| **修改类** | 更新、修改、追加、删除、覆写 |\r\n| **对象** | 文档、链接、这篇、这个文档 |\r\n\r\n**组合示例：**\r\n- \"总结一下这篇钉钉文档\"\r\n- \"读取这个 alidocs 链接\"\r\n- \"更新文档内容\"\r\n- \"删除第三段\"\r\n\r\n### 触发场景（优先级从高到低）\r\n\r\n| 场景 | 示例 | 动作 |\r\n|------|------|------|\r\n| **钉钉文档链接** | `alidocs.dingtalk.com/i/nodes/xxx` | 根据意图选择：元数据→`get-doc`，正文内容→`get-content` |\r\n| **钉钉上下文 + 链接** | \"总结 https://alidocs.dingtalk.com/...\" | 调用 `get-content` 读取内容后总结 |\r\n| **明确命令** | \"总结这篇文档\"、\"读取这个 alidocs 链接\" | 根据意图选择命令 |\r\n| **已知上下文是钉钉文档** | 前文已给出 alidocs 链接，后续说\"更新文档\"、\"删除某段\"、\"在第三段后追加\" | 调用对应命令 |\r\n| **结构查询** | \"列出结构\"、\"这个 alidocs 有哪些章节\" | 调用 `get-content` |\r\n| **块级操作** | \"删除第三段\"、\"修改这个段落\"、\"在这里插入一段\" | 先用 `get-content` 获取 blockId 和位置，再调用 `delete-block`/`modify-block`/`insert-block` |\n\r\n### 不触发的场景\r\n- 查询本地文件、离线文档或普通文本内容，且不需要调用钉钉 API\r\n- 没有文档链接、docKey、或明确钉钉文档上下文，却要求修改文档\r\n- 用户只说\"总结文档\"\"更新这个链接\"等泛化请求，但上下文无法确认对象是钉钉文档\r\n- 与钉钉无关的文档系统，例如本地 Markdown、飞书文档、语雀、Google Docs\r\n\r\n## 运行前提\r\n\r\n### 环境变量\r\n\r\n必须配置以下环境变量 (在 Gateway 环境中):\r\n\r\n- `DINGTALK_CLIENTID` - 钉钉应用 Client ID (AppKey)\r\n- `DINGTALK_CLIENTSECRET` - 钉钉应用 Client Secret (AppSecret)\r\n\r\n可选环境变量:\r\n\r\n- `DINGTALK_DEBUG` - 设置为 `true` 启用调试模式，打印详细请求/响应日志\r\n\r\n### operatorId 获取方式\r\n\r\n不需要在配置中指定!系统会自动从当前会话获取:\r\n\r\n1. 从 `OPENCLAW_SENDER_ID` 或 `DINGTALK_SENDER_ID` 获取 sender_id\r\n2. 调用钉钉 API 查询对应的 unionId\r\n3. 使用 unionId 作为 operatorId\r\n\r\n如果获取失败，会显示友好的错误提示。\r\n\r\n## 执行规则\r\n\r\n- 读取操作不受白名单限制\r\n- 写入操作必须通过白名单检查;未配置 workspace 或节点名不在白名单内时，一律拒绝!**没有任何方式可以绕过白名单检查!**\r\n- `whitelist: [\"/\"]` 表示允许写入该 workspace 下的所有节点;更细粒度控制请配置具体文档名，例如 `\"/三级目录测试文档.adoc\"`\r\n- 白名单配置文件 `config/whitelist.json` 只能由用户手动修改;AI 只能读取、解释、提示用户手动调整，不能替用户改\r\n- 如果用户没有给出 `workspaceId`,先运行 `list-workspaces`\r\n- 如果用户没有给出目标文档的 `nodeId` / `docKey`,先运行 `list-docs`、`search` 或 `get-doc` 确认目标\r\n\r\n## URL 解析规则\r\n\r\n当用户提供钉钉文档 URL 时，按以下规则提取 `nodeId`:\r\n\r\n**URL 格式:** `https://alidocs.dingtalk.com/i/nodes/<nodeId>?...`\r\n\r\n**提取方法:**\r\n1. 从 URL 中提取 `nodes/` 和 `?` 之间的部分\r\n2. 该部分即为 `nodeId`\r\n\r\n**示例:**\r\n```\r\nURL: https://alidocs.dingtalk.com/i/nodes/oP0MALyR8kOd5BGacKv6NbxE83bzYmDO?utm_scene=team_space\r\nnodeId: oP0MALyR8kOd5BGacKv6NbxE83bzYmDO\r\n```\r\n\r\n**提取到 nodeId 后的操作:**\r\n1. **获取文档元数据**（名称、知识库 ID 等）：`get-doc --nodeId=<提取的 nodeId>`\r\n2. **获取文档内容**（总结、读取正文）：`get-content --docKey=<提取的 nodeId>`\r\n3. 注意：`get-doc` 只返回元数据，`get-content` 才返回正文内容\r\n\r\n## 执行入口\r\n\r\n- `scripts/index.js`:主入口\r\n- `scripts/whitelist.js`:辅助检查白名单配置\r\n\r\n**跨平台说明：**\r\n\r\n- 本 skill 的脚本基于 Node.js 内置模块实现，Windows、Linux、macOS 只要安装了 `node` 并配置好环境变量，都可以运行\r\n- 推荐优先使用相对路径执行：`node scripts/index.js ...`，这样最不容易受平台路径差异影响\r\n\r\n**路径示例：**\r\n\r\nWindows PowerShell 中 `~` 不会自动展开，建议使用以下方式之一：\r\n\r\n```bash\r\n# ✅ 使用 $env:USERPROFILE\r\nnode $env:USERPROFILE\\.openclaw\\skills\\dingtalk-doc\\scripts\\index.js\n\r\n# ✅ 或使用完整绝对路径\r\nnode C:\\Users\\zhenhuaixiu\\.openclaw\\skills\\dingtalk-doc\\scripts\\index.js\n\r\n# ❌ 错误（~ 不会展开）\r\nnode ~/.openclaw/skills/dingtalk-doc/scripts/index.js\n```\r\n\r\nLinux / macOS Shell 示例：\r\n\r\n```bash\r\n# ✅ 当前目录下直接运行（推荐）\r\nnode scripts/index.js list-workspaces\r\n\r\n# ✅ 或使用完整绝对路径\r\nnode ~/.openclaw/skills/dingtalk-doc/scripts/index.js list-workspaces\n```\r\n\r\n## 命令映射\r\n\r\n**路径说明：**\r\n\r\n- Windows 示例中的 `$env:USERPROFILE\\.openclaw` 会展开为 `C:\\Users\\<用户名>\\.openclaw`\r\n- Linux / macOS 示例中的 `~/.openclaw` 会展开为用户主目录下的 `.openclaw`\r\n- 如果当前工作目录已经在 skill 根目录，直接使用 `node scripts/index.js ...` 即可\r\n\r\n### 读取操作\r\n\r\n| 命令 | 用途 | API 端点 |\r\n|------|------|---------|\r\n| `list-workspaces` | 获取知识库列表 | `GET /v2.0/wiki/mineWorkspaces` |\r\n| `list-docs` | 获取知识库中文档列表 | `GET /v2.0/wiki/nodes` |\r\n| `get-doc` | 获取**文档元数据**（名称、ID、创建者、字数等） | `GET /v2.0/wiki/nodes/{nodeId}` |\r\n| `get-content` | 获取**文档正文内容**（段落、标题、列表等块结构） | `GET /v1.0/doc/suites/documents/{docKey}/blocks` |\r\n| `search` | 搜索文档 | `GET /v1.0/doc/workspaces/{workspaceId}/docs` |\r\n\r\n**示例：**\r\n```bash\r\n# Linux / macOS\r\nnode ~/.openclaw/skills/dingtalk-doc/scripts/index.js list-workspaces\nnode ~/.openclaw/skills/dingtalk-doc/scripts/index.js list-docs --workspaceId=YRBGvyxxx --parentNodeId=root\nnode ~/.openclaw/skills/dingtalk-doc/scripts/index.js get-doc --nodeId=YQBnd5ExVE0PDnezU2PK2RK6WyeZqMmz\nnode ~/.openclaw/skills/dingtalk-doc/scripts/index.js get-content --docKey=YQBnd5ExVE0PDnezU2PK2RK6WyeZqMmz\nnode ~/.openclaw/skills/dingtalk-doc/scripts/index.js search --workspaceId=YRBGvyxxx --keyword=\"需求\"\n\r\n# Windows PowerShell\r\nnode $env:USERPROFILE\\.openclaw\\skills\\dingtalk-doc\\scripts\\index.js list-workspaces\nnode $env:USERPROFILE\\.openclaw\\skills\\dingtalk-doc\\scripts\\index.js list-docs --workspaceId=YRBGvyxxx --parentNodeId=root\nnode $env:USERPROFILE\\.openclaw\\skills\\dingtalk-doc\\scripts\\index.js get-doc --nodeId=YQBnd5ExVE0PDnezU2PK2RK6WyeZqMmz\nnode $env:USERPROFILE\\.openclaw\\skills\\dingtalk-doc\\scripts\\index.js get-content --docKey=YQBnd5ExVE0PDnezU2PK2RK6WyeZqMmz\nnode $env:USERPROFILE\\.openclaw\\skills\\dingtalk-doc\\scripts\\index.js search --workspaceId=YRBGvyxxx --keyword=\"需求\"\n```\r\n\r\n### 写入操作\r\n\r\n```bash\r\n# Linux / macOS\r\nnode ~/.openclaw/skills/dingtalk-doc/scripts/index.js create-doc --workspaceId=YRBGvyxxx --name=\"新文档\" --docType=DOC --parentNodeId=root\nnode ~/.openclaw/skills/dingtalk-doc/scripts/index.js update-content --nodeId=YQBnd5ExVE0PDnezU2PK2RK6WyeZqMmz --content=\"# 标题\\n\\n内容\"\nnode ~/.openclaw/skills/dingtalk-doc/scripts/index.js update-content --nodeId=YQBnd5ExVE0PDnezU2PK2RK6WyeZqMmz --docKey=真实docKey --content=\"# 标题\\n\\n内容\"\nnode ~/.openclaw/skills/dingtalk-doc/scripts/index.js delete-doc --workspaceId=YRBGvyxxx --nodeId=YQBnd5ExVE0PDnezU2PK2RK6WyeZqMmz\n\r\n# Windows PowerShell\r\n# 创建文档\r\nnode $env:USERPROFILE\\.openclaw\\skills\\dingtalk-doc\\scripts\\index.js create-doc --workspaceId=YRBGvyxxx --name=\"新文档\" --docType=DOC --parentNodeId=root\n# 返回：{ \"docKey\": \"abc123\", \"nodeId\": \"xyz789\", ... }\r\n\r\n# 更新文档内容（整篇覆写，替换全部内容）\r\n# ✅ 推荐：只用 nodeId（大多数情况够用）\r\nnode $env:USERPROFILE\\.openclaw\\skills\\dingtalk-doc\\scripts\\index.js update-content --nodeId=YQBnd5ExVE0PDnezU2PK2RK6WyeZqMmz --content=\"# 标题\\n\\n内容\"\n\r\n# ✅ 备选：如果上面失败，传入真实的 docKey\r\nnode $env:USERPROFILE\\.openclaw\\skills\\dingtalk-doc\\scripts\\index.js update-content --nodeId=YQBnd5ExVE0PDnezU2PK2RK6WyeZqMmz --docKey=真实 docKey --content=\"# 标题\\n\\n内容\"\n\r\n# 删除文档\r\nnode $env:USERPROFILE\\.openclaw\\skills\\dingtalk-doc\\scripts\\index.js delete-doc --workspaceId=YRBGvyxxx --nodeId=YQBnd5ExVE0PDnezU2PK2RK6WyeZqMmz\n```\r\n\r\n### 块级操作（精细修改单个段落/元素）\n\n说明：\n\n- `insert-block`、`modify-block`、`delete-block` 已通过真实文档测试\n- `append-text` 对应的公开 API 当前返回 `InvalidAction.NotFound`，不要再调用或承诺\n- 如果用户要“追加内容”，优先改成“插入一个新段落”或“读取原段落后使用 `modify-block` 整块替换”\n\n```bash\n# Linux / macOS\nnode ~/.openclaw/skills/dingtalk-doc/scripts/index.js delete-block --nodeId=YQBnd5ExVE0PDnezU2PK2RK6WyeZqMmz --blockId=blk123\nnode ~/.openclaw/skills/dingtalk-doc/scripts/index.js modify-block --nodeId=YQBnd5ExVE0PDnezU2PK2RK6WyeZqMmz --blockId=blk123 --element='{\"blockType\":\"paragraph\",\"paragraph\":{\"text\":\"新内容\"}}'\nnode ~/.openclaw/skills/dingtalk-doc/scripts/index.js insert-block --nodeId=YQBnd5ExVE0PDnezU2PK2RK6WyeZqMmz --element='{\"blockType\":\"paragraph\",\"paragraph\":{\"text\":\"插入的内容\"}}' --position=3\n\n# Windows PowerShell\n# 删除块元素（删除某个段落/标题/列表项）\nnode $env:USERPROFILE\\.openclaw\\skills\\dingtalk-doc\\scripts\\index.js delete-block --nodeId=YQBnd5ExVE0PDnezU2PK2RK6WyeZqMmz --blockId=blk123\n\n# 修改块元素（替换单个块的内容，不影响其他部分）\nnode $env:USERPROFILE\\.openclaw\\skills\\dingtalk-doc\\scripts\\index.js modify-block --nodeId=YQBnd5ExVE0PDnezU2PK2RK6WyeZqMmz --blockId=blk123 --element='{\"blockType\":\"paragraph\",\"paragraph\":{\"text\":\"新内容\"}}'\n\n# 插入块元素（在指定位置插入新段落/标题等）\nnode $env:USERPROFILE\\.openclaw\\skills\\dingtalk-doc\\scripts\\index.js insert-block --nodeId=YQBnd5ExVE0PDnezU2PK2RK6WyeZqMmz --element='{\"blockType\":\"paragraph\",\"paragraph\":{\"text\":\"插入的内容\"}}' --position=3\n```\r\n\r\n### 参数说明\r\n\r\n- `docType`: `DOC`(文字) | `WORKBOOK`(表格) | `MIND`(脑图) | `FOLDER`(文件夹)\r\n- `nodeId`: **必填**，节点 ID（用于白名单检查）\r\n  - 从文档链接 `alidocs.dingtalk.com/i/nodes/xxx` 提取 `xxx` 部分\r\n  - 通过 `get-doc --nodeId=xxx` 或 `get-content --docKey=xxx` 确认\r\n- `docKey`: **可选**，真实的文档标识符（用于实际写入 API）\r\n  - 如果不传，默认使用 `nodeId` 代替\r\n  - 仅在 `nodeId` 作为 `docKey` 写入失败时，才需要传入真实的 `docKey`\r\n  - 真实 `docKey` 可通过 createDoc 返回值或钉钉 API Explorer 获取\r\n- `blockId`: 块 ID，通过 `get-content` 获取文档结构后得到（块级操作必需）\r\n- `element`: 块元素 JSON 对象（**会自动解析**，直接传 JSON 字符串即可）\r\n  - 示例：`--element='{\"blockType\":\"paragraph\",\"paragraph\":{\"text\":\"新内容\"}}'`\n- `position`: 插入位置（可选），数字，**支持 0**（表示插到最前面）\r\n- `workspaceId`: 知识库 ID（可选）\r\n  - 注意：写入操作会通过 `nodeId` 查询节点真实 `workspaceId`，传入 `--workspaceId` 时只能作为一致性校验，不能跳过查询\r\n  - 适用场景：已知 `workspaceId` 且想显式校验目标文档属于该知识库，但 `nodeId` 仍必需\r\n\r\n### 命令选择指南\r\n\r\n| 需求 | 使用命令 | 说明 |\n|------|---------|------|\n| 重写整篇文档 | `update-content --nodeId=xxx --content=\"...\"` | 替换全部内容 |\n| 只修改某个段落 | `modify-block --nodeId=xxx --blockId=blk --element='{...}'` | 只影响单个块 |\n| 在当前位置新增一段 | `insert-block --nodeId=xxx --element='{...}' --position=3` | 插入一个新块，更适合“追加一段”的需求 |\n| 删除某一段/标题 | `delete-block --nodeId=xxx --blockId=blk` | 删除块 |\n| 插入新段落/标题 | `insert-block --nodeId=xxx --element='{...}' --position=3` | 在指定位置插入 |\n\r\n## 常见问题\r\n\r\n1. **\"无法获取文档信息\"错误**: \r\n   - 确保传入的是 `nodeId`（从文档链接 `/i/nodes/xxx` 提取）\r\n   - `--workspaceId` 不能替代 `nodeId`，只能作为额外一致性校验\r\n   \r\n2. **\"paramError\" / JSON 解析失败**: \r\n   - `--element` 必须是合法的 JSON 格式，检查引号转义\r\n   - PowerShell 中用单引号包裹：`--element='{\"type\":\"paragraph\"}'`\r\n   \r\n3. **更新失败（nodeNotExist 等）**: \r\n   - 尝试传入真实的 `docKey`：`update-content --nodeId=xxx --docKey=真实 docKey --content=\"...\"`\r\n   - createDoc 返回的 `docKey` 和 `nodeId` 可能不同\r\n\r\n4. **nodeId 和 docKey 到底有什么区别？**\n   - `nodeId`: 目录树节点 ID（wiki_2.0 API 用），用于定位文档、获取 workspaceId 和节点名、执行白名单检查\n   - `docKey`: 文档内容标识符（suites/documents API 用），用于实际读写内容\n   - 经验：大多数情况下 `nodeId` 可直接用作 `docKey`，少数情况需要真实 `docKey`\n\n5. **为什么没有 append-text？**\n   - 当前公开接口 `POST /v1.0/doc/suites/documents/{docKey}/paragraphs/{blockId}/text` 在真实测试中返回 `InvalidAction.NotFound`\n   - 因此本 skill 不再承诺 `append-text`，请使用 `insert-block` 或 `modify-block`\n\r\n## 推荐流程\r\n\r\n1. 先确认凭证和 sender_id 是否可用。\r\n2. 需要定位文档所在知识库时先跑 `list-workspaces`。\r\n3. 需要定位文档时先跑 `list-docs`、`search`、`get-doc`。\r\n4. 执行写操作前，默认假设会触发白名单校验，不要跳过读取确认步骤。\r\n5. 如果写入被拒绝，只说明是哪个 workspace / 节点名未通过白名单，并提示用户手动调整 `config/whitelist.json`。\r\n\r\n## 常见失败\r\n\r\n- 缺少 sender_id:检查钉钉连接器是否注入 `OPENCLAW_SENDER_ID` / `DINGTALK_SENDER_ID`\r\n- `forbidden.accessDenied`:检查应用权限或白名单\r\n- `invalidRequest.workspaceNode.parentNotFound`:检查 `parentNodeId`\r\n- `权限拒绝：知识库 xxx 未配置白名单`:让用户手动补充 `config/whitelist.json`\r\n- `nodeNotExist`(更新内容时):**尝试使用 nodeId 代替 docKey** - 钉钉 API 中 createDoc 返回的 docKey 和 overwriteContent 需要的 docKey 可能不一致\r\n- `blockNotExist`(块级操作时):先用 `get-content` 获取文档结构，确认 blockId 正确\n- `paramError`(modify-block/insert-block):检查 `--element` 参数是否是合法的 JSON 格式\n\r\n## 重要提示\r\n\r\n### get-doc vs get-content\r\n\r\n| 命令 | 用途 | 返回内容 | 何时使用 |\r\n|------|------|---------|---------|\r\n| `get-doc` | 获取**文档元数据** | `data.node`：名称、ID、创建者、修改时间、字数、workspaceId | 确认文档存在、获取文档基本信息、定位知识库 |\r\n| `get-content` | 获取**文档正文** | `data.result.data[]`：段落、标题、列表等块结构 | 总结内容、读取正文、准备修改文档 |\r\n\r\n**关键区别：**\r\n- `get-doc` → `GET /v2.0/wiki/nodes/{nodeId}` → **不包含正文内容**\r\n- `get-content` → `GET /v1.0/doc/suites/documents/{docKey}/blocks` → **包含正文块结构**\r\n- **总结、读取内容时，始终使用 `get-content`，不要用 `get-doc`**\r\n\r\n### docKey vs nodeId\r\n\r\n- `nodeId`: 目录树节点 ID（wiki_2.0 API 用），用于定位文档、获取 workspaceId 和节点名、执行白名单检查\r\n- `docKey`: 文档内容标识符（suites/documents API 用），用于实际读写内容\r\n- `create-doc` 返回的 `docKey` 和 `nodeId` 可能是不同的值\r\n- `update-content`、`get-content` 等命令**优先使用 `nodeId`** 作为 `--docKey` 参数\r\n- 如果使用 `docKey` 更新失败 (`nodeNotExist`),请改用 `nodeId`\r\n- 经验：大多数情况下 `nodeId` 可直接用作 `docKey`，少数情况需要真实 `docKey`\r\n\r\n**示例:**\r\n\r\n```bash\r\n# 创建文档后，使用返回的 nodeId 进行更新\r\nnode .../index.js create-doc --workspaceId=xxx --name=\"新文档\"\r\n# 返回:{ \"docKey\": \"abc123\", \"nodeId\": \"xyz789\", ... }\r\n\r\n# ✅ 正确：使用 nodeId 更新\r\nnode .../index.js update-content --docKey=xyz789 --content=\"...\"\r\n\r\n# ❌ 可能失败：使用 docKey 更新\r\nnode .../index.js update-content --docKey=abc123 --content=\"...\"\r\n```\r\n\r\n## 参考\r\n\r\n- 详细说明见 `README.md`\r\n- 钉钉开放平台文档:[knowledge-base-overview](https://open.dingtalk.com/document/development/knowledge-base-overview)\r\n- API Explorer:[open-dev.dingtalk.com/apiExplorer](https://open-dev.dingtalk.com/apiExplorer)\n\nFile v1.0.3:README.md\n\n# 钉钉文档管理技能 (dingtalk-doc)\r\n\r\n通过钉钉开放平台 API 读取和管理钉钉文档、钉钉知识库中的文档。本文件面向人类读者，重点说明配置、上手方式和排障思路；执行规则与触发策略见 `SKILL.md`。\r\n\r\n## 能力概览\r\n\r\n- 支持列出、搜索、读取钉钉文档\r\n- 支持创建、更新、删除钉钉文档\r\n- 支持多个 workspace 共用一份白名单配置\r\n- 写入操作按 workspace + 节点名白名单控制，读取操作不受白名单限制\r\n- 当前用户身份从钉钉连接器注入的 `sender_id` 自动解析\r\n\r\n## 文档分工\r\n\r\n- `SKILL.md`：给 agent 的执行手册，强调触发条件、命令映射和失败处理\r\n- `README.md`：给人的配置说明、背景信息、排障参考\r\n\r\n## 快速开始\r\n\r\n### 1. 配置环境变量\r\n\r\n本 skill 可运行在 Windows、Linux、macOS；核心要求只有：\r\n\r\n- 安装 `node`\r\n- 配置 `DINGTALK_CLIENTID` / `DINGTALK_CLIENTSECRET`\r\n\r\n**编辑 `~/.openclaw/.env` 文件（推荐，优先级最高）**\r\n\r\nOpenClaw 会在启动时自动加载此文件中的环境变量。\r\n\r\n```bash\r\n# ~/.openclaw/.env\r\nDINGTALK_CLIENTID=dingxxxxxx\r\nDINGTALK_CLIENTSECRET=your_secret\r\n```\r\n\r\n说明：\r\n\r\n- `DINGTALK_CLIENTID`：钉钉应用 Client ID\r\n- `DINGTALK_CLIENTSECRET`：钉钉应用 Client Secret\r\n- `DINGTALK_DEBUG=true`：可选，输出更详细的请求和响应日志\r\n- `operatorId` 不需要手工配置，脚本会从 `OPENCLAW_SENDER_ID` / `DINGTALK_SENDER_ID` 解析当前钉钉用户\r\n\r\n### 1.1 运行路径说明\r\n\r\n推荐在 skill 目录下直接运行：\r\n\r\n```bash\r\nnode scripts/index.js list-workspaces\r\n```\r\n\r\n如果需要使用绝对路径：\r\n\r\nLinux / macOS：\r\n\r\n```bash\r\nnode ~/.openclaw/skills/dingtalk-doc/scripts/index.js list-workspaces\n```\r\n\r\nWindows PowerShell：\r\n\r\n```powershell\r\nnode $env:USERPROFILE\\.openclaw\\skills\\dingtalk-doc\\scripts\\index.js list-workspaces\n```\r\n\r\n注意：\r\n\r\n- Linux / macOS 中 `~` 会展开到用户主目录\r\n- Windows PowerShell 中 `~` 不适合这里，建议使用 `$env:USERPROFILE` 或完整绝对路径\r\n\r\n### 2. 确认 workspaceId\r\n\r\n最直接的方式是运行：\r\n\r\n```bash\r\nnode scripts/index.js list-workspaces\r\n```\r\n\r\n如果手里只有钉钉文档链接，也可以先拿 `nodeId` 再查详情。常见链接形态如下：\r\n\r\n```text\r\nhttps://alidocs.dingtalk.com/i/nodes/{nodeId}?utm_scene=team_space\r\n```\r\n\r\n然后运行：\r\n\r\n```bash\r\nnode scripts/index.js get-doc --nodeId=vy20BglGWOq9ZLj3F0M9ajK0JA7depqY\r\n```\r\n\r\n### 3. 配置白名单\r\n\r\n在 `config/whitelist.json` 中声明允许写入的 workspace 和节点名：\r\n\r\n```json\r\n{\r\n  \"workspaces\": [\r\n    {\r\n      \"workspaceId\": \"OQ0xySj6ng7lX58B\",\r\n      \"workspaceName\": \"主知识库\",\r\n      \"allowRootWrite\": false,\r\n      \"whitelist\": [\"/\"]\r\n    }\r\n  ]\r\n}\r\n```\r\n\r\n字段说明：\r\n\r\n- `workspaceId`：知识库 ID，必填\r\n- `workspaceName`：可选，便于识别\r\n- `allowRootWrite`：保留字段；当前写入校验主要使用 `whitelist`\r\n- `whitelist`：允许写入的节点名（文档名）列表\r\n\r\n白名单规则：\r\n\r\n- 读取操作不检查白名单\r\n- 写入操作必须命中白名单\r\n- 未配置的 workspace 默认禁止写入\r\n- `\"/\"` 表示允许写入当前 `workspaceId` 下的所有节点\r\n- 细粒度白名单按节点名匹配，例如 `\"/三级文档.adoc\"`；当前已验证的节点详情接口不会返回完整父目录路径\r\n\r\n## 常用命令\r\n\r\n这里只保留几个面向人的常见示例；完整命令映射见 `SKILL.md`。\r\n\r\n读取：\r\n\r\n```bash\r\n# 当前目录运行（跨平台通用）\r\nnode scripts/index.js list-workspaces\r\nnode scripts/index.js list-docs --workspaceId=OQ0xySj6ng7lX58B --parentNodeId=root\r\nnode scripts/index.js get-doc --nodeId=YQBnd5ExVE0PDnezU2PK2RK6WyeZqMmz\r\nnode scripts/index.js get-content --nodeId=YQBnd5ExVE0PDnezU2PK2RK6WyeZqMmz\r\nnode scripts/index.js get-content --docKey=真实docKey --nodeId=YQBnd5ExVE0PDnezU2PK2RK6WyeZqMmz\r\n```\r\n\r\n写入：\r\n\r\n```bash\r\n# 当前目录运行（跨平台通用）\r\nnode scripts/index.js create-doc --workspaceId=OQ0xySj6ng7lX58B --name=\"新文档\" --docType=DOC --parentNodeId=root\r\nnode scripts/index.js update-content --nodeId=YQBnd5ExVE0PDnezU2PK2RK6WyeZqMmz --content=\"# 标题\\n\\n内容\"\r\nnode scripts/index.js update-content --docKey=真实docKey --nodeId=YQBnd5ExVE0PDnezU2PK2RK6WyeZqMmz --content=\"# 标题\\n\\n内容\"\r\n```\r\n\r\n白名单自检：\r\n\r\n```bash\r\n# 当前目录运行（跨平台通用）\r\nnode scripts/whitelist.js check \"/三级文档.adoc\"\r\n```\r\n\r\n`docType` 支持：\r\n\r\n- `DOC`\r\n- `WORKBOOK`\r\n- `MIND`\r\n- `FOLDER`\r\n\r\n## 目录结构\r\n\r\n```text\ndingtalk-doc/\n├── SKILL.md\r\n├── README.md\r\n├── package.json\r\n├── config/\r\n│   └── whitelist.json\r\n└── scripts/\r\n    ├── index.js\r\n    ├── dingtalk-client.js\r\n    └── whitelist.js\r\n```\r\n\r\n## 已知返回结构\r\n\r\n以下结构来自实测文档 `https://alidocs.dingtalk.com/i/nodes/oP0MALyR8kOd5BGacKv6NbxE83bzYmDO?utm_scene=team_space`，文档名是“三级文档”，真实位于三级子目录中。\r\n\r\n`get-doc --nodeId=...` / `GET /v2.0/wiki/nodes/{nodeId}` 返回的 `node` 里有 `workspaceId` 和 `name`，但没有返回完整父目录链路：\r\n\r\n```json\r\n{\r\n  \"node\": {\r\n    \"name\": \"三级文档.adoc\",\r\n    \"nodeId\": \"oP0MALyR8kOd5BGacKv6NbxE83bzYmDO\",\r\n    \"workspaceId\": \"eLvJDSRX3l4moO87\",\r\n    \"type\": \"FILE\",\r\n    \"category\": \"ALIDOC\",\r\n    \"url\": \"https://alidocs.dingtalk.com/i/nodes/oP0MALyR8kOd5BGacKv6NbxE83bzYmDO\"\r\n  }\r\n}\r\n```\r\n\r\n容易犯错的点：\r\n\r\n- 不要把白名单理解为真实目录路径；当前脚本只能可靠使用 `workspaceId + node.name` 做写入白名单。\r\n- 即使文档在三级子目录中，节点详情也可能只返回 `\"name\": \"三级文档.adoc\"`，不会返回 `\"/一级/二级/三级文档.adoc\"`。\r\n- `get-content --nodeId=...` / `GET /v1.0/doc/suites/documents/{docKey}/blocks` 返回的是块结构，例如 paragraph 的 `id`、`index`、`text`，不返回文档路径或父目录信息。\n- `nodeId` 用于查询节点详情和校验真实 `workspaceId`；`docKey` 用于 suites/documents 内容读写接口。多数情况下可先用 `nodeId` 作为 `docKey` 尝试。\n- `insert-block`、`modify-block`、`delete-block` 已在真实文档上测试通过；`append-text` 对应的 `paragraphs/{blockId}/text` 当前返回 `InvalidAction.NotFound`，不要依赖它。\n\r\n## 权限与接口\r\n\r\n常用接口：\r\n\r\n- `POST /v1.0/doc/workspaces/{workspaceId}/docs`\r\n- `GET /v2.0/wiki/nodes`\r\n- `GET /v2.0/wiki/nodes/{nodeId}`\r\n- `DELETE /v1.0/doc/workspaces/{workspaceId}/docs/{nodeId}`\r\n- `GET /v1.0/doc/suites/documents/{docKey}/blocks`\n- `POST /v1.0/doc/suites/documents/{docKey}/overwriteContent`\n- `POST /v1.0/doc/suites/documents/{docKey}/blocks`\n- `PUT /v1.0/doc/suites/documents/{docKey}/blocks/{blockId}`\n- `DELETE /v1.0/doc/suites/documents/{docKey}/blocks/{blockId}`\n\r\n常见所需权限：\r\n\r\n- `Document.Workspace.Read`\r\n- `Document.Workspace.Write`\r\n- `Document.WorkspaceDocument.Read`\r\n- `Document.WorkspaceDocument.Write`\r\n- `Wiki.Node.Read`\r\n\r\n## 常见问题\r\n\r\n| 问题 | 原因 | 处理方式 |\r\n|------|------|---------|\r\n| 缺少 `sender_id` | 钉钉连接器未传递当前用户 ID | 检查是否注入了 `OPENCLAW_SENDER_ID` 或 `DINGTALK_SENDER_ID` |\r\n| workspace 未配置白名单 | `config/whitelist.json` 没有对应 workspace | 补充对应 `workspaceId` 配置 |\r\n| 节点名不在白名单内 | 写入目标节点名未命中规则 | 调整白名单节点名或使用 `\"/\"` 允许整个 workspace |\r\n| `forbidden.accessDenied` | 应用权限不足或白名单不通过 | 检查钉钉应用权限和白名单 |\r\n| `invalidRequest.workspaceNode.parentNotFound` | `parentNodeId` 错误 | 重新确认父节点 ID |\r\n\r\n## 参考\r\n\r\n- 钉钉开放平台文档：[knowledge-base-overview](https://open.dingtalk.com/document/development/knowledge-base-overview)\r\n- API Explorer：[open-dev.dingtalk.com/apiExplorer](https://open-dev.dingtalk.com/apiExplorer)\r\n\r\n### API 参考\r\n\r\n钉钉知识库 API 分为两个系列：\r\n\r\n#### doc_1.0 - 知识库管理\r\n\r\n| API | 方法 | 路径 | 权限 |\r\n|-----|------|------|------|\r\n| 新建知识库 | POST | `/v1.0/doc/workspaces` | Document.Workspace.Write |\r\n| 获取知识库 | GET | `/v1.0/doc/workspaces/{workspaceId}` | Document.Workspace.Read |\r\n| 获取知识库列表 | GET | `/v1.0/doc/workspaces` | Document.Workspace.Read |\r\n| 创建知识库文档 | POST | `/v1.0/doc/workspaces/{workspaceId}/docs` | Document.WorkspaceDocument.Write |\r\n| 删除知识库文档 | DELETE | `/v1.0/doc/workspaces/{workspaceId}/docs/{nodeId}` | Document.WorkspaceDocument.Write |\r\n\r\n#### wiki_2.0 - 目录树管理\r\n\r\n| API | 方法 | 路径 | 权限 |\r\n|-----|------|------|------|\r\n| 获取节点 | GET | `/v2.0/wiki/nodes/{nodeId}` | Wiki.Node.Read |\r\n| 获取节点列表 | GET | `/v2.0/wiki/nodes` | Wiki.Node.Read |\r\n| 通过链接获取节点 | GET | `/v2.0/wiki/nodes/url` | Wiki.Node.Read |\r\n| 复制文档 | POST | `/v2.0/wiki/nodes/{nodeId}/copy` | Document.WorkspaceDocument.Write |\r\n\r\n#### suites/documents - 文档内容\r\n\r\n| API | 方法 | 路径 | 权限 |\r\n|-----|------|------|------|\r\n| 查询块结构 | GET | `/v1.0/doc/suites/documents/{docKey}/blocks` | Document.WorkspaceDocument.Read |\r\n| 覆写内容 | POST | `/v1.0/doc/suites/documents/{docKey}/overwriteContent` | Document.WorkspaceDocument.Write |\r\n| 插入块 | POST | `/v1.0/doc/suites/documents/{docKey}/blocks` | Document.WorkspaceDocument.Write |\r\n| 删除块 | DELETE | `/v1.0/doc/suites/documents/{docKey}/blocks/{blockId}` | Document.WorkspaceDocument.Write |\r\n\r\n\r\n#### 所需权限清单\r\n\r\n调用本 skill 需要开通以下钉钉应用权限：\r\n\r\n- `Document.Workspace.Read` - 知识库读权限\r\n- `Document.Workspace.Write` - 知识库写权限\r\n- `Document.WorkspaceDocument.Read` - 文档读权限\r\n- `Document.WorkspaceDocument.Write` - 文档写权限\r\n- `Wiki.Node.Read` - 节点读权限\r\n\r\n## 许可证\r\n\r\nMIT\n\nFile v1.0.3:_meta.json\n\n{\n  \"ownerId\": \"kn73t8gpykqmgx10ksfb6706r5842gge\",\n  \"slug\": \"dingtalk-doc\",\n  \"version\": \"1.0.3\",\n  \"publishedAt\": 1776762183600\n}\n\nFile v1.0.3:config/whitelist.json\n\n{\n  \"_comment\": \"⚠️ 此配置文件只能由用户手动编辑，AI 禁止修改！\",\n  \"workspaces\": [\n    {\n      \"workspaceId\": \"eLvJDSRX3l4moO87\",\n      \"workspaceName\": \"AI可写知识库一级目录\",\n      \"allowRootWrite\": true,\n      \"whitelist\": [\"/\"]\n    }\n  ]\n}\n\nFile v1.0.3:package.json\n\n{\n  \"name\": \"dingtalk-doc\",\n  \"version\": \"2.0.0\",\n  \"description\": \"钉钉文档管理技能 - 支持文档增删改查和白名单权限控制\",\n  \"main\": \"scripts/index.js\",\n  \"scripts\": {\n    \"get-token\": \"node scripts/dingtalk-client.js get-token\",\n    \"get-workspace-list\": \"node scripts/dingtalk-client.js get-workspace-list\",\n    \"list-docs\": \"node scripts/index.js list-docs\",\n    \"create-doc\": \"node scripts/index.js create-doc\",\n    \"delete-doc\": \"node scripts/index.js delete-doc\",\n    \"search\": \"node scripts/index.js search\",\n    \"get-content\": \"node scripts/index.js get-content\",\n    \"update-content\": \"node scripts/index.js update-content\",\n    \"check-permission\": \"node scripts/whitelist.js check\"\n  },\n  \"keywords\": [\n    \"dingtalk\",\n    \"knowledge-base\",\n    \"openclaw-skill\"\n  ],\n  \"author\": \"\",\n  \"license\": \"MIT\"\n}\n\nArchive v1.0.2: 8 files, 24964 bytes\n\nFiles: config/whitelist.json (278b), package.json (837b), README.md (9701b), scripts/dingtalk-client.js (20105b), scripts/index.js (32208b), scripts/whitelist.js (5039b), SKILL.md (17642b), _meta.json (131b)\n\nFile v1.0.2:SKILL.md\n\n---\r\nname: dingtalk-doc\r\ndescription: 钉钉文档管理技能。当用户发送的消息中包含 alidocs.dingtalk.com 链接、要求总结/读取/查看/更新钉钉文档或钉钉知识库文档，或当前上下文已明确对象是钉钉文档时使用。关键词：钉钉文档、钉钉知识库、alidocs、总结、读取、查看、更新、修改、文档、链接。\r\nmetadata:\r\n  {\r\n    \"openclaw\":\r\n      {\r\n        \"emoji\": \"📚\",\r\n        \"requires\":\r\n          {\r\n            \"bins\": [\"node\"],\r\n            \"env\": [\"DINGTALK_CLIENTID\", \"DINGTALK_CLIENTSECRET\"],\r\n          },\r\n      },\r\n  }\r\n---\r\n\r\n# 钉钉文档管理技能\r\n\r\n通过钉钉开放平台 API 管理钉钉文档与钉钉知识库内文档。`SKILL.md` 只保留 agent 执行所需规则;配置细节、示例、API 背景见 `README.md`。\r\n\r\n## 何时使用\r\n\r\n### 触发关键词\r\n\r\n只有在**已经确认对象是钉钉文档**时，同时消息包含以下**任一关键词**时，优先使用本 skill：\r\n\r\n| 类别 | 关键词 |\r\n|------|--------|\r\n| **平台名** | 钉钉文档、钉钉知识库、alidocs |\r\n| **读取类** | 总结、读取、查看、浏览、列出结构 |\r\n| **修改类** | 更新、修改、追加、删除、覆写 |\r\n| **对象** | 文档、链接、这篇、这个文档 |\r\n\r\n**组合示例：**\r\n- \"总结一下这篇钉钉文档\"\r\n- \"读取这个 alidocs 链接\"\r\n- \"更新文档内容\"\r\n- \"删除第三段\"\r\n\r\n### 触发场景（优先级从高到低）\r\n\r\n| 场景 | 示例 | 动作 |\r\n|------|------|------|\r\n| **钉钉文档链接** | `alidocs.dingtalk.com/i/nodes/xxx` | 根据意图选择：元数据→`get-doc`，正文内容→`get-content` |\r\n| **钉钉上下文 + 链接** | \"总结 https://alidocs.dingtalk.com/...\" | 调用 `get-content` 读取内容后总结 |\r\n| **明确命令** | \"总结这篇文档\"、\"读取这个 alidocs 链接\" | 根据意图选择命令 |\r\n| **已知上下文是钉钉文档** | 前文已给出 alidocs 链接，后续说\"更新文档\"、\"删除某段\"、\"在第三段后追加\" | 调用对应命令 |\r\n| **结构查询** | \"列出结构\"、\"这个 alidocs 有哪些章节\" | 调用 `get-content` |\r\n| **块级操作** | \"删除第三段\"、\"在这段后面追加内容\"、\"修改这个段落\" | 先用 `get-content` 获取 blockId，再调用 `delete-block`/`append-text`/`modify-block` |\r\n\r\n### 不触发的场景\r\n- 查询本地文件、离线文档或普通文本内容，且不需要调用钉钉 API\r\n- 没有文档链接、docKey、或明确钉钉文档上下文，却要求修改文档\r\n- 用户只说\"总结文档\"\"更新这个链接\"等泛化请求，但上下文无法确认对象是钉钉文档\r\n- 与钉钉无关的文档系统，例如本地 Markdown、飞书文档、语雀、Google Docs\r\n\r\n## 运行前提\r\n\r\n### 环境变量\r\n\r\n必须配置以下环境变量 (在 Gateway 环境中):\r\n\r\n- `DINGTALK_CLIENTID` - 钉钉应用 Client ID (AppKey)\r\n- `DINGTALK_CLIENTSECRET` - 钉钉应用 Client Secret (AppSecret)\r\n\r\n可选环境变量:\r\n\r\n- `DINGTALK_DEBUG` - 设置为 `true` 启用调试模式，打印详细请求/响应日志\r\n\r\n### operatorId 获取方式\r\n\r\n不需要在配置中指定!系统会自动从当前会话获取:\r\n\r\n1. 从 `OPENCLAW_SENDER_ID` 或 `DINGTALK_SENDER_ID` 获取 sender_id\r\n2. 调用钉钉 API 查询对应的 unionId\r\n3. 使用 unionId 作为 operatorId\r\n\r\n如果获取失败，会显示友好的错误提示。\r\n\r\n## 执行规则\r\n\r\n- 读取操作不受白名单限制\r\n- 写入操作必须通过白名单检查;未配置 workspace 或节点名不在白名单内时，一律拒绝!**没有任何方式可以绕过白名单检查!**\r\n- `whitelist: [\"/\"]` 表示允许写入该 workspace 下的所有节点;更细粒度控制请配置具体文档名，例如 `\"/三级目录测试文档.adoc\"`\r\n- 白名单配置文件 `config/whitelist.json` 只能由用户手动修改;AI 只能读取、解释、提示用户手动调整，不能替用户改\r\n- 如果用户没有给出 `workspaceId`,先运行 `list-workspaces`\r\n- 如果用户没有给出目标文档的 `nodeId` / `docKey`,先运行 `list-docs`、`search` 或 `get-doc` 确认目标\r\n\r\n## URL 解析规则\r\n\r\n当用户提供钉钉文档 URL 时，按以下规则提取 `nodeId`:\r\n\r\n**URL 格式:** `https://alidocs.dingtalk.com/i/nodes/<nodeId>?...`\r\n\r\n**提取方法:**\r\n1. 从 URL 中提取 `nodes/` 和 `?` 之间的部分\r\n2. 该部分即为 `nodeId`\r\n\r\n**示例:**\r\n```\r\nURL: https://alidocs.dingtalk.com/i/nodes/oP0MALyR8kOd5BGacKv6NbxE83bzYmDO?utm_scene=team_space\r\nnodeId: oP0MALyR8kOd5BGacKv6NbxE83bzYmDO\r\n```\r\n\r\n**提取到 nodeId 后的操作:**\r\n1. **获取文档元数据**（名称、知识库 ID 等）：`get-doc --nodeId=<提取的 nodeId>`\r\n2. **获取文档内容**（总结、读取正文）：`get-content --docKey=<提取的 nodeId>`\r\n3. 注意：`get-doc` 只返回元数据，`get-content` 才返回正文内容\r\n\r\n## 执行入口\r\n\r\n- `scripts/index.js`:主入口\r\n- `scripts/whitelist.js`:辅助检查白名单配置\r\n\r\n**跨平台说明：**\r\n\r\n- 本 skill 的脚本基于 Node.js 内置模块实现，Windows、Linux、macOS 只要安装了 `node` 并配置好环境变量，都可以运行\r\n- 推荐优先使用相对路径执行：`node scripts/index.js ...`，这样最不容易受平台路径差异影响\r\n\r\n**路径示例：**\r\n\r\nWindows PowerShell 中 `~` 不会自动展开，建议使用以下方式之一：\r\n\r\n```bash\r\n# ✅ 使用 $env:USERPROFILE\r\nnode $env:USERPROFILE\\.openclaw\\skills\\dingtalk-knowledge\\scripts\\index.js\r\n\r\n# ✅ 或使用完整绝对路径\r\nnode C:\\Users\\zhenhuaixiu\\.openclaw\\skills\\dingtalk-knowledge\\scripts\\index.js\r\n\r\n# ❌ 错误（~ 不会展开）\r\nnode ~/.openclaw/skills/dingtalk-knowledge/scripts/index.js\r\n```\r\n\r\nLinux / macOS Shell 示例：\r\n\r\n```bash\r\n# ✅ 当前目录下直接运行（推荐）\r\nnode scripts/index.js list-workspaces\r\n\r\n# ✅ 或使用完整绝对路径\r\nnode ~/.openclaw/skills/dingtalk-knowledge/scripts/index.js list-workspaces\r\n```\r\n\r\n## 命令映射\r\n\r\n**路径说明：**\r\n\r\n- Windows 示例中的 `$env:USERPROFILE\\.openclaw` 会展开为 `C:\\Users\\<用户名>\\.openclaw`\r\n- Linux / macOS 示例中的 `~/.openclaw` 会展开为用户主目录下的 `.openclaw`\r\n- 如果当前工作目录已经在 skill 根目录，直接使用 `node scripts/index.js ...` 即可\r\n\r\n### 读取操作\r\n\r\n| 命令 | 用途 | API 端点 |\r\n|------|------|---------|\r\n| `list-workspaces` | 获取知识库列表 | `GET /v2.0/wiki/mineWorkspaces` |\r\n| `list-docs` | 获取知识库中文档列表 | `GET /v2.0/wiki/nodes` |\r\n| `get-doc` | 获取**文档元数据**（名称、ID、创建者、字数等） | `GET /v2.0/wiki/nodes/{nodeId}` |\r\n| `get-content` | 获取**文档正文内容**（段落、标题、列表等块结构） | `GET /v1.0/doc/suites/documents/{docKey}/blocks` |\r\n| `search` | 搜索文档 | `GET /v1.0/doc/workspaces/{workspaceId}/docs` |\r\n\r\n**示例：**\r\n```bash\r\n# Linux / macOS\r\nnode ~/.openclaw/skills/dingtalk-knowledge/scripts/index.js list-workspaces\r\nnode ~/.openclaw/skills/dingtalk-knowledge/scripts/index.js list-docs --workspaceId=YRBGvyxxx --parentNodeId=root\r\nnode ~/.openclaw/skills/dingtalk-knowledge/scripts/index.js get-doc --nodeId=YQBnd5ExVE0PDnezU2PK2RK6WyeZqMmz\r\nnode ~/.openclaw/skills/dingtalk-knowledge/scripts/index.js get-content --docKey=YQBnd5ExVE0PDnezU2PK2RK6WyeZqMmz\r\nnode ~/.openclaw/skills/dingtalk-knowledge/scripts/index.js search --workspaceId=YRBGvyxxx --keyword=\"需求\"\r\n\r\n# Windows PowerShell\r\nnode $env:USERPROFILE\\.openclaw\\skills\\dingtalk-knowledge\\scripts\\index.js list-workspaces\r\nnode $env:USERPROFILE\\.openclaw\\skills\\dingtalk-knowledge\\scripts\\index.js list-docs --workspaceId=YRBGvyxxx --parentNodeId=root\r\nnode $env:USERPROFILE\\.openclaw\\skills\\dingtalk-knowledge\\scripts\\index.js get-doc --nodeId=YQBnd5ExVE0PDnezU2PK2RK6WyeZqMmz\r\nnode $env:USERPROFILE\\.openclaw\\skills\\dingtalk-knowledge\\scripts\\index.js get-content --docKey=YQBnd5ExVE0PDnezU2PK2RK6Wye\n\nArchive v1.0.1: 8 files, 24949 bytes\n\nFiles: config/whitelist.json (264b), package.json (837b), README.md (9701b), scripts/dingtalk-client.js (20105b), scripts/index.js (32208b), scripts/whitelist.js (5039b), SKILL.md (17642b), _meta.json (131b)\n\nArchive v1.0.0: 8 files, 24929 bytes\n\nFiles: config/whitelist.json (264b), package.json (837b), README.md (9825b), scripts/dingtalk-client.js (20105b), scripts/index.js (32208b), scripts/whitelist.js (5039b), SKILL.md (17642b), _meta.json (131b)","readmeExcerpt":"Skill: dingtalk-doc Owner: shyzhen Summary: 钉钉文档管理技能。当用户发送的消息中包含 alidocs.dingtalk.com 链接、要求总结/读取/查看/更新钉钉文档或钉钉知识库文档，或当前上下文已明确对象是钉钉文档时使用。关键词：钉钉文档、钉钉知识库、alidocs、总结、读取、查看、更新、修改、文档、链接。 Tags: latest:1.0.8 Version history: v1.0.8 | 2026-06-11T03:04:40.846Z | user dingtalk-doc v1.0.8 - 移除 skill-card.md 文件，精简技能描述内容。 - 无其他功能或配置变更。 v1.0.7 | 2026-06-03T09:52:16.313Z | user - Remove unnecessary file: skill-card.md to simplify the","codeSnippets":[],"executableExamples":[{"language":"text","snippet":"dingtalk-doc/\n├── SKILL.md\r\n├── README.md\r\n├── package.json\r\n├── config/\r\n│   └── whitelist.json\r\n└── scripts/\r\n    ├── index.js\r\n    ├── dingtalk-client.js\r\n    └── whitelist.js"},{"language":"bash","snippet":"# Linux / macOS\nnode ~/.openclaw/skills/dingtalk-doc/scripts/index.js delete-block --nodeId=YQBnd5ExVE0PDnezU2PK2RK6WyeZqMmz --blockId=blk123\nnode ~/.openclaw/skills/dingtalk-doc/scripts/index.js modify-block --nodeId=YQBnd5ExVE0PDnezU2PK2RK6WyeZqMmz --blockId=blk123 --element='{\"blockType\":\"paragraph\",\"paragraph\":{\"text\":\"新内容\"}}'\nnode ~/.openclaw/skills/dingtalk-doc/scripts/index.js insert-block --nodeId=YQBnd5ExVE0PDnezU2PK2RK6WyeZqMmz --element='{\"blockType\":\"paragraph\",\"paragraph\":{\"text\":\"插入的内容\"}}' --position=3\n\n# Windows PowerShell\n# 删除块元素（删除某个段落/标题/列表项）\nnode $env:USERPROFILE\\.openclaw\\skills\\dingtalk-doc\\scripts\\index.js delete-block --nodeId=YQBnd5ExVE0PDnezU2PK2RK6WyeZqMmz --blockId=blk123\n\n# 修改块元素（替换单个块的内容，不影响其他部分）\nnode $env:USERPROFILE\\.openclaw\\skills\\dingtalk-doc\\scripts\\index.js modify-block --nodeId=YQBnd5ExVE0PDnezU2PK2RK6WyeZqMmz --blockId=blk123 --element='{\"blockType\":\"paragraph\",\"paragraph\":{\"text\":\"新内容\"}}'\n\n# 插入块元素（在指定位置插入新段落/标题等）\nnode $env:USERPROFILE\\.openclaw\\skills\\dingtalk-doc\\scripts\\index.js insert-block --nodeId=YQBnd5ExVE0PDnezU2PK2RK6WyeZqMmz --element='{\"blockType\":\"paragraph\",\"paragraph\":{\"text\":\"插入的内容\"}}' --position=3"},{"language":"text","snippet":"dingtalk-doc/\n├── SKILL.md\r\n├── README.md\r\n├── package.json\r\n├── config/\r\n│   └── whitelist.json\r\n└── scripts/\r\n    ├── index.js\r\n    ├── dingtalk-client.js\r\n    └── whitelist.js"}],"parameters":null,"dependencies":[],"permissions":[],"extractedFiles":[{"path":"SKILL.md","content":"---\r\nname: dingtalk-doc\r\ndescription: 钉钉文档管理技能。当用户发送的消息中包含 alidocs.dingtalk.com 链接、要求总结/读取/查看/更新钉钉文档或钉钉知识库文档，或当前上下文已明确对象是钉钉文档时使用。关键词：钉钉文档、钉钉知识库、alidocs、总结、读取、查看、更新、修改、文档、链接。\r\n---\r\n\r\n# 钉钉文档管理技能\r\n\r\n通过钉钉开放平台 API 管理钉钉文档与钉钉知识库内文档。`SKILL.md` 只保留 agent 执行所需规则;配置细节、示例、API 背景见 `README.md`。\r\n\r\n## 何时使用\r\n\r\n### 触发关键词\r\n\r\n只有在**已经确认对象是钉钉文档**时，同时消息包含以下**任一关键词**时，优先使用本 skill：\r\n\r\n| 类别 | 关键词 |\r\n|------|--------|\r\n| **平台名** | 钉钉文档、钉钉知识库、alidocs |\r\n| **读取类** | 总结、读取、查看、浏览、列出结构 |\r\n| **修改类** | 更新、修改、追加、删除、覆写 |\r\n| **对象** | 文档、链接、这篇、这个文档 |\r\n\r\n**组合示例：**\r\n- \"总结一下这篇钉钉文档\"\r\n- \"读取这个 alidocs 链接\"\r\n- \"更新文档内容\"\r\n- \"删除第三段\"\r\n\r\n### 触发场景（优先级从高到低）\r\n\r\n| 场景 | 示例 | 动作 |\r\n|------|------|------|\r\n| **钉钉文档链接** | `alidocs.dingtalk.com/i/nodes/xxx` | 根据意图选择：元数据→`get-doc`，正文内容→`get-content` |\r\n| **钉钉上下文 + 链接** | \"总结 https://alidocs.dingtalk.com/...\" | 调用 `get-content` 读取内容后总结 |\r\n| **明确命令** | \"总结这篇文档\"、\"读取这个 alidocs 链接\" | 根据意图选择命令 |\r\n| **已知上下文是钉钉文档** | 前文已给出 alidocs 链接，后续说\"更新文档\"、\"删除某段\"、\"在第三段后追加\" | 调用对应命令 |\r\n| **结构查询** | \"列出结构\"、\"这个 alidocs 有哪些章节\" | 调用 `get-content` |\r\n| **块级操作** | \"删除第三段\"、\"修改这个段落\"、\"在这里插入一段\" | 先用 `get-content` 获取 blockId 和位置，再调用 `delete-block`/`modify-block`/`insert-block` |\r\n\r\n### 不触发的场景\r\n- 查询本地文件、离线文档或普通文本内容，且不需要调用钉钉 API\r\n- 没有文档链接、docKey、或明确钉钉文档上下文，却要求修改文档\r\n- 用户只说\"总结文档\"\"更新这个链接\"等泛化请求，但上下文无法确认对象是钉钉文档\r\n- 与钉钉无关的文档系统，例如本地 Markdown、飞书文档、语雀、Google Docs\r\n\r\n## 运行前提\r\n\r\n### 环境变量\r\n\r\n必须配置以下环境变量 (在 Gateway 环境中):\r\n\r\n- `DINGTALK_CLIENTID` - 钉钉应用 Client ID (AppKey)\r\n- `DINGTALK_CLIENTSECRET` - 钉钉应用 Client Secret (AppSecret)\r\n\r\n可选环境变量:\r\n\r\n- `OPENCLAW_SENDER_ID` / `DINGTALK_SENDER_ID` - 由 OpenClaw / 钉钉连接器注入的当前用户 sender_id；也可以通过命令行 `--senderId=` 显式传入\r\n- `DINGTALK_DEBUG` - 设置为 `true` 启用调试模式；仅输出方法、路径（查询参数已脱敏）、状态码、requestId 等，不打印文档正文与完整请求体\r\n\r\n### operatorId 获取方式\r\n\r\n不需要在配置中指定!系统会自动从当前会话获取:\r\n\r\n1. 从 `OPENCLAW_SENDER_ID` 或 `DINGTALK_SENDER_ID` 获取 sender_id\r\n2. 调用钉钉 API 查询对应的 unionId\r\n3. 使用 unionId 作为 operatorId\r\n\r\n如果获取失败，会显示友好的错误提示。\r\n\r\n## 执行规则\r\n\r\n- 读取操作不受白名单限制\r\n- 默认直接在白名单配置的`workspaceId`中尝试写入\r\n- 写入操作必须通过白名单检查;未配置 workspace 或节点名不在白名单内时，一律拒绝!**没有任何方式可以绕过白名单检查!**\r\n- `whitelist: [\"/\"]` 表示允许写入该 workspace 下的所有节点;更细粒度控制请配置具体文档名，例如 `\"/三级目录测试文档.adoc\"`\r\n- 白名单配置文件 `config/whitelist.json` 只能由用户手动修改;AI 只能读取、解释、提示用户手动调整，不能替用户改\r\n- 如果用户没有给出目标文档的 `nodeId` / `docKey`,先运行 `list-docs`、`search` 或 `get-doc` 确认目标\r\n\r\n## URL 解析规则\r\n\r\n当用户提供钉钉文档 URL 时，按以下规则提取 `nodeId`:\r\n\r\n**URL 格式:** `https://alidocs.dingtalk.com/i/nodes/<nodeId>?...`\r\n\r\n**提取方法:**\r\n1. 从 URL 中提取 `nodes/` 和 `?` 之间的部分\r\n2. 该部分即为 `nodeId`\r\n\r\n**示例:**\r\n```\r\nURL: https://alidocs.dingtalk.com/i/nodes/oP0MALyR8kOd5BGacKv6NbxE83bzYmDO?utm_scene=team_space\r\nnodeId: oP0MALyR8kOd5BGacKv6NbxE83bzYmDO\r\n```\r\n\r\n**提取到 nodeId 后的操作:**\r\n1. **获取文档元数据**（名称、知识库 ID 等）：`get-doc --nodeId=<提取的 nodeId>`\r\n2. **获取文档内容**（总结、读取正文）：`get-content --docKey=<提取的 nodeId>`\r\n3. 注意：`get-doc` 只返回元数据，`get-content` 才返回正文内容\r\n\r\n## 执行入口\r\n\r\n- `scripts/index.js`:主入口\r\n- `scripts/whitelist.js`:辅助检查白名单配置\r\n\r\n**跨平台说明：**\r\n\r\n- 本 skill 的脚本基于 Node.js 内置模块实现，Windows、Linux、macOS 只要安装了 `node` 并配置好环境变量，都可以运行\r\n- 推荐优先使用相对路径执行：`node s"},{"path":"README.md","content":"# 钉钉文档管理技能 (dingtalk-doc)\r\n\r\n> [https://clawhub.ai/shyzhen/dingtalk-doc](https://clawhub.ai/shyzhen/dingtalk-doc)\r\n> \r\n> 是 [dingtalk-doc-enterprise](https://clawhub.ai/shyzhen/dingtalk-doc-enterprise) 的升级版(支持写入范围控制)\r\n\r\n通过钉钉开放平台 API 读取和管理钉钉文档、钉钉知识库中的文档。本文件面向人类读者，重点说明配置、上手方式和排障思路；执行规则与触发策略见 `SKILL.md`。\r\n\r\n## 能力概览\r\n\r\n- 支持列出、搜索、读取钉钉文档\r\n- 支持创建、更新、删除钉钉文档\r\n- 支持多个 workspace 共用一份白名单配置\r\n- 写入操作按 workspace + 节点名白名单控制，读取操作不受白名单限制\r\n- 当前用户身份从钉钉连接器注入的 `sender_id` 自动解析\r\n\r\n## 文档分工\r\n\r\n- `SKILL.md`：给 agent 的执行手册，强调触发条件、命令映射和失败处理\r\n- `README.md`：给人的配置说明、背景信息、排障参考\r\n\r\n## 快速开始\r\n\r\n### 1. 配置环境变量\r\n\r\n本 skill 可运行在 Windows、Linux、macOS；核心要求只有：\r\n\r\n- 安装 `node`\r\n- 配置 `DINGTALK_CLIENTID` / `DINGTALK_CLIENTSECRET`\r\n\r\n**编辑 `~/.openclaw/.env` 文件（推荐，优先级最高）**\r\n\r\nOpenClaw 会在启动时自动加载此文件中的环境变量。\r\n\r\n```bash\r\n# ~/.openclaw/.env\r\nDINGTALK_CLIENTID=dingxxxxxx\r\nDINGTALK_CLIENTSECRET=your_secret\r\n```\r\n\r\n说明：\r\n\r\n- `DINGTALK_CLIENTID`：钉钉应用 Client ID\r\n- `DINGTALK_CLIENTSECRET`：钉钉应用 Client Secret\r\n- `OPENCLAW_SENDER_ID` / `DINGTALK_SENDER_ID`：通常由 OpenClaw / 钉钉连接器自动注入；如需手工调用 CLI，也可以用 `--senderId=...`\r\n- `DINGTALK_DEBUG=true`：可选，仅输出请求方法、接口路径、状态码、requestId 等调试信息，不再打印文档正文和请求体\r\n- `operatorId` 不需要手工配置，脚本会优先从 `--senderId` 或 `OPENCLAW_SENDER_ID` / `DINGTALK_SENDER_ID` 解析当前钉钉用户\r\n\r\n### 2. 确认 workspaceId\r\n\r\n- 白名单配置：只支持 【钉钉文档 -> 知识库】下新建的根目录，也就是说可写范围必须在【知识库】中。\r\n- 直接发一篇文档给你的钉钉机器人让他随便写入一段话，就会触发白名单检查，机器人会主动告知你当前文档的workspaceId，以及如何配置。\r\n\r\n### 3. 配置白名单\r\n\r\n说明：\r\n\r\n- 读取操作不依赖白名单文件\r\n- 写入操作才需要 `config/whitelist.json`\r\n- 如果该文件不存在，写操作会被拒绝，并提示用户手动创建\r\n\r\n在 `config/whitelist.json` 中声明允许写入的 workspace 和节点名：\r\n\r\n```json\r\n{\r\n  \"workspaces\": [\r\n    {\r\n      \"workspaceId\": \"eLvJDSRX3l4moO87\",\r\n      \"workspaceName\": \"AI可写知识库一级目录\",\r\n      \"allowRootWrite\": false,\r\n      \"whitelist\": [\"/\"]\r\n    }\r\n  ]\r\n}\r\n```\r\n\r\n字段说明：\r\n\r\n- `workspaceId`：知识库 ID，必填 （只支持【钉钉文档 -> 知识库】下的目录）\r\n- `workspaceName`：可选，便于识别\r\n- `allowRootWrite`：保留字段；当前写入校验主要使用 `whitelist`\r\n- `whitelist`：允许写入的节点名（文档名）列表\r\n\r\n白名单规则：\r\n\r\n- 读取操作不检查白名单\r\n- 写入操作必须命中白名单\r\n- 未配置的 workspace 默认禁止写入\r\n- `\"/\"` 表示允许写入当前 `workspaceId` 下的所有节点\r\n- 细粒度白名单按节点名匹配，例如 `\"/三级文档.adoc\"`；当前已验证的节点详情接口不会返回完整父目录路径\r\n\r\n## 常用命令\r\n\r\n这里只保留几个面向人的常见示例；完整命令映射见 `SKILL.md`。\r\n\r\n读取：\r\n\r\n```bash\r\n# 当前目录运行（跨平台通用）\r\nnode scripts/index.js list-workspaces\r\nnode scripts/index.js list-docs --workspaceId=OQ0xySj6ng7lX58B --parentNodeId=root\r\nnode scripts/index.js get-doc --nodeId=YQBnd5ExVE0PDnezU2PK2RK6WyeZqMmz\r\nnode scripts/index.js get-content --nodeId=YQBnd5ExVE0PDnezU2PK2RK6WyeZqMmz\r\nnode scripts/index.js get-content --docKey=真实docKey --nodeId=YQBnd5ExVE0PDnezU2PK2RK6WyeZqMmz\r\n```\r\n\r\n写入：\r\n\r\n```bash\r\n# 当前目录运行（跨平台通用）\r\nnode scripts/index.js create-doc --workspaceId=OQ0xySj6ng7lX58B --name=\"新文档\" --docType=DOC --parentNodeId=root\r\nnode scripts/index.js update-content --nodeId=YQBnd5ExVE0PDnezU2PK2RK6WyeZqMmz --content=\"# 标题\\n\\n内容\"\r\nnode scripts/index.js update-content --docKey=真实docKey --nodeId=YQBnd5ExVE0PDnezU2PK2RK6WyeZqMmz --content=\"# 标题\\n\\n内容\"\r\n```\r\n\r\n白名单自检：\r\n\r\n```bash\r\n# 当前目录运行（跨平台通用）\r\nnode scripts/whitelist.js check \"/三级文档.adoc\"\r\n```\r\n\r\n"},{"path":"_meta.json","content":"{\n  \"ownerId\": \"kn73t8gpykqmgx10ksfb6706r5842gge\",\n  \"slug\": \"dingtalk-doc\",\n  \"version\": \"1.0.8\",\n  \"publishedAt\": 1781147080846\n}"},{"path":"skill-card.md","content":"## Description:\n\nHelps an agent read, summarize, create, update, and delete DingTalk Docs and DingTalk knowledge-base documents when a DingTalk document link or context is provided.\n\nThis skill is ready for commercial/non-commercial use.\n\n## Publisher:\n\n[shyzhen](https://clawhub.ai/user/shyzhen)\n\n### License/Terms of Use:\n\nMIT-0\n\n## Use Case:\n\nDevelopers and teams using OpenClaw with DingTalk use this skill to locate, read, summarize, create, overwrite, modify, insert into, and delete DingTalk knowledge-base documents through DingTalk APIs.\n\n### Deployment Geography for Use:\n\nGlobal\n\n## Known Risks and Mitigations:\n\nRisk: The skill exposes high-impact document overwrite, block modification, and delete operations.\n\nMitigation: Require explicit user confirmation before overwrite or delete actions and review proposed content changes before execution.\n\nRisk: The bundled whitelist configuration can allow broad workspace writes.\n\nMitigation: Replace the bundled whitelist with narrow, user-approved workspace and document targets before installation.\n\nRisk: DingTalk app credentials and token access can affect remote documents outside the local workspace.\n\nMitigation: Install only with trusted DingTalk credentials, avoid low-level client commands, and remove or disable the get-token command where it is not needed.\n\n## Reference(s):\n\n- [ClawHub skill page](https://clawhub.ai/shyzhen/skills/dingtalk-doc)\n- [DingTalk knowledge-base overview](https://open.dingtalk.com/document/development/knowledge-base-overview)\n- [DingTalk API Explorer](https://open-dev.dingtalk.com/apiExplorer)\n\n## Skill Output:\n\n**Output Type(s):** [text, markdown, shell commands, configuration, guidance]\n\n**Output Format:** [Markdown guidance, shell commands, configuration snippets, and JSON API responses.]\n\n**Output Parameters:** [1D]\n\n**Other Properties Related to Output:** [Requires DingTalk app credentials and a sender identity; write actions can modify or delete remote DingTalk documents.]\n\n## Skill Version(s):\n\n1.0.8 (source: server release evidence; artifact package.json reports 1.0.6)\n\n## Ethical Considerations:\n\nUsers should evaluate whether this skill is appropriate for their environment, review any generated or modified files before relying on them, and apply their organization's safety, security, and compliance requirements before deployment."},{"path":"config/whitelist.json","content":"{\n  \"_comment\": \"⚠️ 此配置文件只能由用户手动编辑，AI 禁止修改！\",\n  \"workspaces\": [\n    {\n      \"workspaceId\": \"eLvJDSRX3l4moO87\",\n      \"workspaceName\": \"AI可写知识库一级目录\",\n      \"allowRootWrite\": true,\n      \"whitelist\": [\"/\"]\n    }\n  ]\n}"}],"languages":[],"docsSourceLabel":"CLAWHUB","editorialOverview":"钉钉文档管理技能。当用户发送的消息中包含 alidocs.dingtalk.com 链接、要求总结/读取/查看/更新钉钉文档或钉钉知识库文档，或当前上下文已明确对象是钉钉文档时使用。关键词：钉钉文档、钉钉知识库、alidocs、总结、读取、查看、更新、修改、文档、链接。 Skill: dingtalk-doc Owner: shyzhen Summary: 钉钉文档管理技能。当用户发送的消息中包含 alidocs.dingtalk.com 链接、要求总结/读取/查看/更新钉钉文档或钉钉知识库文档，或当前上下文已明确对象是钉钉文档时使用。关键词：钉钉文档、钉钉知识库、alidocs、总结、读取、查看、更新、修改、文档、链接。 Tags: latest:1.0.8 Version history: v1.0.8 | 2026-06-11T03:04:40.846Z | user dingtalk-doc v1.0.8 - 移除 skill-card.md 文件，精简技能描述内容。 - 无其他功能或配置变更。 v1.0.7 | 2026-06-03T09:52:16.313Z | user - Remove unnecessary file: skill-card.md to simplify the","editorialQuality":{"score":100,"threshold":65,"status":"ready","wordCount":1045,"uniquenessScore":49,"reasons":[]}},"media":{"evidence":{"source":"no-media","verified":false,"confidence":"low","updatedAt":"2026-10-11T00:08:36.920Z","emptyReason":"No screenshots, media assets, or demo links are available."},"primaryImageUrl":null,"mediaAssetCount":0,"assets":[],"demoUrl":null},"ownerResources":{"evidence":{"source":"unclaimed","verified":false,"confidence":"low","updatedAt":"2026-10-11T00:08:36.920Z","emptyReason":"This page has not been claimed by the agent owner."},"hasCustomPage":false,"customPageUpdatedAt":null,"customLinks":[],"structuredLinks":{"docsUrl":null,"demoUrl":null,"supportUrl":null,"pricingUrl":null,"statusUrl":null},"customPage":null},"relatedAgents":{"evidence":{"source":"protocol-neighbors","verified":false,"confidence":"medium","updatedAt":"2026-10-11T03:55:07.934Z","emptyReason":null},"items":[{"id":"8ebccd8e-3863-4187-8355-c3f14e1f9edf","entityType":"agent","canonicalPath":"/agent/iofficeai-aionui","slug":"iofficeai-aionui","name":"AionUi","description":"Free, local, open-source 24/7 Cowork app and OpenClaw for Gemini CLI, Claude Code, Codex, OpenCode, Qwen Code, Goose CLI, Auggie, and more | 🌟 Star if you like it!","url":"https://github.com/iOfficeAI/AionUi","homepage":"https://www.aionui.com","source":"GITHUB_REPOS","protocols":["MCP","OPENCLAW"],"capabilities":[],"safetyScore":100,"overallRank":70,"updatedAt":"2026-10-09T19:11:12.944Z","createdAt":"2026-02-25T03:38:16.584Z","downloads":null},{"id":"b917f68a-ebff-438e-84f8-3f4b2494c0bc","entityType":"agent","canonicalPath":"/agent/activepieces-activepieces","slug":"activepieces-activepieces","name":"activepieces","description":"AI Agents & MCPs & AI Workflow Automation • (~400 MCP servers for AI agents) • AI Automation / AI Agent with MCPs • AI Workflows & AI Agents • MCPs for AI Agents","url":"https://github.com/activepieces/activepieces","homepage":"https://www.activepieces.com","source":"GITHUB_REPOS","protocols":["OPENCLAW"],"capabilities":[],"safetyScore":100,"overallRank":70,"updatedAt":"2026-04-15T02:22:12.426Z","createdAt":"2026-02-25T03:38:12.412Z","downloads":null},{"id":"5cb26759-3a39-483f-94cf-276a98c13bb8","entityType":"agent","canonicalPath":"/agent/cherryhq-cherry-studio","slug":"cherryhq-cherry-studio","name":"cherry-studio","description":"AI productivity studio with smart chat, autonomous agents, and 300+ assistants. Unified access to frontier LLMs","url":"https://github.com/CherryHQ/cherry-studio","homepage":"https://cherry-ai.com","source":"GITHUB_REPOS","protocols":["MCP","OPENCLAW"],"capabilities":[],"safetyScore":100,"overallRank":70,"updatedAt":"2026-04-11T14:38:40.986Z","createdAt":"2026-02-25T03:38:19.379Z","downloads":null},{"id":"6f6582d0-5d76-4f0f-b81d-86520247950b","entityType":"agent","canonicalPath":"/agent/copilotkit-copilotkit","slug":"copilotkit-copilotkit","name":"CopilotKit","description":"The Frontend for Agents & Generative UI. React + Angular","url":"https://github.com/CopilotKit/CopilotKit","homepage":"https://docs.copilotkit.ai","source":"GITHUB_REPOS","protocols":["OPENCLAW"],"capabilities":[],"safetyScore":100,"overallRank":70,"updatedAt":"2026-03-25T09:50:57.846Z","createdAt":"2026-02-25T03:39:14.617Z","downloads":null}],"links":{"hub":"/agent","source":"/agent/source/clawhub","protocols":[{"label":"OpenClaw","href":"/agent/protocol/openclew"}]}}}