dingtalk-wiki 1.2.15 → 1.2.16
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/index.js +141 -63
- package/package.json +1 -1
package/index.js
CHANGED
|
@@ -213,7 +213,28 @@ async function rebuildSearchIndex() {
|
|
|
213
213
|
const blocks = await dingtalk.docRequest('GET', `/v1.0/doc/suites/documents/${childId}/blocks`);
|
|
214
214
|
const blockData = blocks.result?.data || [];
|
|
215
215
|
content = blockData.map(b => extractBlockText(b)).join('\n\n');
|
|
216
|
-
} catch (e) { /* content unavailable */ }
|
|
216
|
+
} catch (e) { /* content unavailable via blocks */ }
|
|
217
|
+
|
|
218
|
+
// Fallback: 存储 v1.0 下载 API(对上传的 .md 等文件有效)
|
|
219
|
+
if (!content && child.spaceId) {
|
|
220
|
+
try {
|
|
221
|
+
const token = await dingtalk.getAccessToken();
|
|
222
|
+
const dlRes = await axios({
|
|
223
|
+
method: 'POST',
|
|
224
|
+
url: `${DINGTALK_API_V2}/v1.0/storage/spaces/${child.spaceId}/dentries/${child.dentryId}/downloadInfos/query`,
|
|
225
|
+
headers: { 'x-acs-dingtalk-access-token': token, 'Content-Type': 'application/json' },
|
|
226
|
+
params: { unionId: await dingtalk.getCurrentUserUnionId() || '' },
|
|
227
|
+
data: {}
|
|
228
|
+
});
|
|
229
|
+
const data = dlRes.data;
|
|
230
|
+
const downloadUrl = data.downloadInfo?.resourceUrl || data.resourceUrl || data.downloadUrl || data.url;
|
|
231
|
+
const downloadHeaders = data.downloadInfo?.headers || data.headers || {};
|
|
232
|
+
if (downloadUrl) {
|
|
233
|
+
const contentRes = await axios({ method: 'GET', url: downloadUrl, headers: downloadHeaders, responseType: 'text' });
|
|
234
|
+
content = contentRes.data;
|
|
235
|
+
}
|
|
236
|
+
} catch (e) { /* storage v1.0 unavailable */ }
|
|
237
|
+
}
|
|
217
238
|
|
|
218
239
|
if (!content) {
|
|
219
240
|
try {
|
|
@@ -1633,60 +1654,43 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
1633
1654
|
}
|
|
1634
1655
|
}
|
|
1635
1656
|
|
|
1636
|
-
// Step 10: 存储 API - 文件下载(.md 文件内容 = 文件下载)
|
|
1657
|
+
// Step 10: 存储 API v1.0 - 文件下载(.md 文件内容 = 文件下载)
|
|
1637
1658
|
if (foundSpaceId) {
|
|
1638
|
-
|
|
1639
|
-
|
|
1640
|
-
|
|
1641
|
-
|
|
1642
|
-
|
|
1643
|
-
|
|
1644
|
-
|
|
1645
|
-
|
|
1646
|
-
|
|
1647
|
-
|
|
1648
|
-
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
-
|
|
1654
|
-
|
|
1655
|
-
|
|
1656
|
-
|
|
1657
|
-
|
|
1658
|
-
|
|
1659
|
-
|
|
1660
|
-
|
|
1661
|
-
|
|
1662
|
-
|
|
1663
|
-
|
|
1664
|
-
|
|
1665
|
-
|
|
1666
|
-
`/v2.0/storage/spaces/files/${input}/downloadInfo`,
|
|
1667
|
-
`/v2.0/storage/spaces/${foundSpaceId}/files/${input}/downloadInfos/query`,
|
|
1668
|
-
`/v2.0/storage/spaces/${foundSpaceId}/files/${foundDentry.dentryId}/downloadInfos/query`,
|
|
1669
|
-
]) {
|
|
1670
|
-
try {
|
|
1671
|
-
const dlRes = await axios({
|
|
1672
|
-
method: 'POST',
|
|
1673
|
-
url: `${DINGTALK_API_V2}${path}`,
|
|
1674
|
-
headers: { 'x-acs-dingtalk-access-token': token, 'Content-Type': 'application/json' },
|
|
1675
|
-
params: { operatorId: opId },
|
|
1676
|
-
data: {}
|
|
1677
|
-
});
|
|
1678
|
-
steps.push({
|
|
1679
|
-
step: `存储下载 (POST ${path})`,
|
|
1680
|
-
result: '成功',
|
|
1681
|
-
raw: dlRes.data
|
|
1682
|
-
});
|
|
1683
|
-
} catch (e) {
|
|
1684
|
-
steps.push({
|
|
1685
|
-
step: `存储下载 (POST ${path})`,
|
|
1686
|
-
result: '失败',
|
|
1687
|
-
raw: { message: e.response?.data?.message || e.message, code: e.response?.data?.code || '(无)', status: e.response?.status || '(无)' }
|
|
1688
|
-
});
|
|
1659
|
+
try {
|
|
1660
|
+
const dlRes = await axios({
|
|
1661
|
+
method: 'POST',
|
|
1662
|
+
url: `${DINGTALK_API_V2}/v1.0/storage/spaces/${foundSpaceId}/dentries/${foundDentry.dentryId}/downloadInfos/query`,
|
|
1663
|
+
headers: { 'x-acs-dingtalk-access-token': token, 'Content-Type': 'application/json' },
|
|
1664
|
+
params: { unionId: opId },
|
|
1665
|
+
data: {}
|
|
1666
|
+
});
|
|
1667
|
+
const data = dlRes.data;
|
|
1668
|
+
steps.push({
|
|
1669
|
+
step: '存储 v1.0 获取下载链接 (POST /v1.0/storage/spaces/{sid}/dentries/{did}/downloadInfos/query)',
|
|
1670
|
+
result: '成功',
|
|
1671
|
+
raw: data
|
|
1672
|
+
});
|
|
1673
|
+
// 如果有下载链接,尝试下载内容
|
|
1674
|
+
const downloadUrl = data.downloadInfo?.resourceUrl || data.resourceUrl || data.downloadUrl || data.url;
|
|
1675
|
+
const downloadHeaders = data.downloadInfo?.headers || data.headers || {};
|
|
1676
|
+
if (downloadUrl) {
|
|
1677
|
+
try {
|
|
1678
|
+
const contentRes = await axios({ method: 'GET', url: downloadUrl, headers: downloadHeaders, responseType: 'text' });
|
|
1679
|
+
steps.push({
|
|
1680
|
+
step: '文件内容下载',
|
|
1681
|
+
result: `成功 (${contentRes.data.length} 字符)`,
|
|
1682
|
+
raw: contentRes.data.slice(0, 1000) + (contentRes.data.length > 1000 ? '\n...(截断)' : '')
|
|
1683
|
+
});
|
|
1684
|
+
} catch (dlErr) {
|
|
1685
|
+
steps.push({ step: '文件内容下载', result: '失败', raw: dlErr.message });
|
|
1686
|
+
}
|
|
1689
1687
|
}
|
|
1688
|
+
} catch (e) {
|
|
1689
|
+
steps.push({
|
|
1690
|
+
step: '存储 v1.0 获取下载链接',
|
|
1691
|
+
result: '失败',
|
|
1692
|
+
raw: { message: e.response?.data?.message || e.message, code: e.response?.data?.code || '(无)', status: e.response?.status || '(无)' }
|
|
1693
|
+
});
|
|
1690
1694
|
}
|
|
1691
1695
|
}
|
|
1692
1696
|
|
|
@@ -1701,21 +1705,30 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
1701
1705
|
}
|
|
1702
1706
|
lines.push('');
|
|
1703
1707
|
});
|
|
1708
|
+
const isAdoc = foundDentry?.name?.endsWith('.adoc') || input.length > 20;
|
|
1704
1709
|
lines.push('💡 诊断结论:');
|
|
1705
1710
|
lines.push(` 输入 ID: ${input}`);
|
|
1706
1711
|
lines.push(` 操作者: ${opId}`);
|
|
1707
1712
|
lines.push('');
|
|
1708
|
-
|
|
1709
|
-
|
|
1710
|
-
|
|
1713
|
+
if (foundDentry) {
|
|
1714
|
+
lines.push(` 文档名: ${foundDentry.name}`);
|
|
1715
|
+
lines.push(` 类型推断: ${foundDentry.name?.endsWith('.adoc') ? '✅ .adoc 在线文档(可读写)' : foundDentry.name?.match(/\.(md|docx|xlsx|txt|xmind|mark|markdown)$/i) ? '❌ 导入的文档(非原生. adoc,内容 API 不支持)' : '⚠️ 未知类型'}`);
|
|
1716
|
+
}
|
|
1717
|
+
lines.push('');
|
|
1718
|
+
lines.push(' 1️⃣ wiki/nodes API → ✅ 成功。节点存在');
|
|
1719
|
+
lines.push(` 2️⃣ blocks API → ${foundDentry?.name?.endsWith('.adoc') ? '✅ 应对 .adoc 在线文档有效' : '❌ "doc key is illegal"(非 .adoc 文档无 docKey)'}`);
|
|
1720
|
+
lines.push(` 3️⃣ overwriteContent → ${foundDentry?.name?.endsWith('.adoc') ? '✅ 应对 .adoc 在线文档有效' : '❌ 同上错误'}`);
|
|
1711
1721
|
lines.push(' 4️⃣ doc metadata → ❌ 404(该端点不存在)');
|
|
1712
1722
|
lines.push('');
|
|
1713
|
-
lines.push('📌
|
|
1714
|
-
lines.push('
|
|
1715
|
-
|
|
1716
|
-
|
|
1717
|
-
|
|
1718
|
-
lines.push('📌
|
|
1723
|
+
lines.push('📌 关键区别:');
|
|
1724
|
+
lines.push(' - 知识库"导入为在线文档" → 转为 .adoc → 有 docKey → blocks API 可读写');
|
|
1725
|
+
lines.push(' - "上传文件" → 保持原格式 → 无 docKey → blocks API 不可用');
|
|
1726
|
+
lines.push(' - create_wiki_doc 创建的是 .adoc 在线文档 → blocks API 可读写');
|
|
1727
|
+
lines.push('');
|
|
1728
|
+
lines.push('📌 存储 v1.0 下载 API (POST /v1.0/storage/spaces/{sid}/dentries/{did}/downloadInfos/query)');
|
|
1729
|
+
lines.push(' 使用 unionId 参数,可获取上传文件的下载链接');
|
|
1730
|
+
lines.push('📌 DocContent API (GET /v2.0/doc/dentries/{uuid}/contents) 此前 503');
|
|
1731
|
+
lines.push('📌 请重新运行此工具测试以上 API');
|
|
1719
1732
|
|
|
1720
1733
|
return { content: [{ type: 'text', text: lines.join('\n') }] };
|
|
1721
1734
|
}
|
|
@@ -2085,9 +2098,74 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
2085
2098
|
};
|
|
2086
2099
|
} catch (blocksErr) {
|
|
2087
2100
|
const isDocKeyIllegal = blocksErr.message.includes('doc key is illegal');
|
|
2101
|
+
|
|
2102
|
+
// Fallback: 尝试存储 v1.0 下载 API(对上传的 .md 等文件有效)
|
|
2103
|
+
if (isDocKeyIllegal && workspaceId) {
|
|
2104
|
+
try {
|
|
2105
|
+
const token = await dingtalk.getAccessToken();
|
|
2106
|
+
const opId = await dingtalk.resolveOperatorId(operator_id || null);
|
|
2107
|
+
|
|
2108
|
+
// BFS 遍历查找 dentry
|
|
2109
|
+
const queue = [''];
|
|
2110
|
+
const visited = new Set();
|
|
2111
|
+
let foundDentry = null;
|
|
2112
|
+
let foundSpaceId = null;
|
|
2113
|
+
while (queue.length > 0 && !foundDentry) {
|
|
2114
|
+
const parentId = queue.shift();
|
|
2115
|
+
if (visited.has(parentId)) continue;
|
|
2116
|
+
visited.add(parentId);
|
|
2117
|
+
try {
|
|
2118
|
+
const dirParams = { operatorId: opId, maxResults: 500 };
|
|
2119
|
+
if (parentId) dirParams.dentryId = parentId;
|
|
2120
|
+
const dirRes = await axios({
|
|
2121
|
+
method: 'GET',
|
|
2122
|
+
url: `${DINGTALK_API_V2}/v2.0/doc/spaces/${workspaceId}/directories`,
|
|
2123
|
+
headers: { 'x-acs-dingtalk-access-token': token },
|
|
2124
|
+
params: dirParams
|
|
2125
|
+
});
|
|
2126
|
+
const children = dirRes.data?.children || [];
|
|
2127
|
+
for (const child of children) {
|
|
2128
|
+
if (child.dentryUuid === realDocKey || child.dentryId === realDocKey || child.id === realDocKey) {
|
|
2129
|
+
foundDentry = child;
|
|
2130
|
+
foundSpaceId = child.spaceId;
|
|
2131
|
+
break;
|
|
2132
|
+
}
|
|
2133
|
+
if (child.hasChildren && child.dentryId && !visited.has(child.dentryId)) {
|
|
2134
|
+
queue.push(child.dentryId);
|
|
2135
|
+
}
|
|
2136
|
+
}
|
|
2137
|
+
} catch (e) { /* skip */ }
|
|
2138
|
+
}
|
|
2139
|
+
|
|
2140
|
+
if (foundDentry && foundSpaceId) {
|
|
2141
|
+
const dlRes = await axios({
|
|
2142
|
+
method: 'POST',
|
|
2143
|
+
url: `${DINGTALK_API_V2}/v1.0/storage/spaces/${foundSpaceId}/dentries/${foundDentry.dentryId}/downloadInfos/query`,
|
|
2144
|
+
headers: { 'x-acs-dingtalk-access-token': token, 'Content-Type': 'application/json' },
|
|
2145
|
+
params: { unionId: opId },
|
|
2146
|
+
data: {}
|
|
2147
|
+
});
|
|
2148
|
+
const data = dlRes.data;
|
|
2149
|
+
const downloadUrl = data.downloadInfo?.resourceUrl || data.resourceUrl || data.downloadUrl || data.url;
|
|
2150
|
+
const downloadHeaders = data.downloadInfo?.headers || data.headers || {};
|
|
2151
|
+
if (downloadUrl) {
|
|
2152
|
+
const contentRes = await axios({ method: 'GET', url: downloadUrl, headers: downloadHeaders, responseType: 'text' });
|
|
2153
|
+
return {
|
|
2154
|
+
content: [{ type: 'text', text: contentRes.data }]
|
|
2155
|
+
};
|
|
2156
|
+
}
|
|
2157
|
+
}
|
|
2158
|
+
} catch (fallbackErr) {
|
|
2159
|
+
console.error(`[钉钉MCP] 存储 v1.0 fallback 失败: ${fallbackErr.message}`);
|
|
2160
|
+
}
|
|
2161
|
+
}
|
|
2162
|
+
|
|
2088
2163
|
let hint = '';
|
|
2089
2164
|
if (isDocKeyIllegal) {
|
|
2090
|
-
hint = `\n\n原因:
|
|
2165
|
+
hint = `\n\n原因: 此文档不是 .adoc 在线文档,blocks API 无法读取。`;
|
|
2166
|
+
if (workspaceId) {
|
|
2167
|
+
hint += `\n已尝试存储 v1.0 下载 API 但仍失败。`;
|
|
2168
|
+
}
|
|
2091
2169
|
}
|
|
2092
2170
|
return {
|
|
2093
2171
|
content: [{
|