dingtalk-wiki 1.2.14 → 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 +134 -28
- 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 {
|
|
@@ -1523,11 +1544,11 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
1523
1544
|
}
|
|
1524
1545
|
|
|
1525
1546
|
// Step 9: 递归遍历目录树查找文档 + 存储API下载内容
|
|
1547
|
+
let foundDentry = null;
|
|
1548
|
+
let foundSpaceId = null;
|
|
1526
1549
|
if (effectiveWsId) {
|
|
1527
1550
|
try {
|
|
1528
1551
|
// BFS 遍历目录树查找 dentry
|
|
1529
|
-
let foundDentry = null;
|
|
1530
|
-
let foundSpaceId = null;
|
|
1531
1552
|
const queue = [''];
|
|
1532
1553
|
const visited = new Set();
|
|
1533
1554
|
while (queue.length > 0 && !foundDentry) {
|
|
@@ -1633,29 +1654,40 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
1633
1654
|
}
|
|
1634
1655
|
}
|
|
1635
1656
|
|
|
1636
|
-
// Step 10:
|
|
1637
|
-
|
|
1638
|
-
{ label: 'DocContent /v2.0/doc/dentries/{uuid}/contents', params: { operatorId: opId }, data: undefined },
|
|
1639
|
-
{ label: 'DocContent + targetFormat query', params: { operatorId: opId, targetFormat: 'markdown' }, data: undefined },
|
|
1640
|
-
{ label: 'QueryDocContent /v2.0/doc/query/{uuid}/contents', path: `/v2.0/doc/query/${input}/contents`, params: { operatorId: opId, targetFormat: 'markdown' }, data: undefined },
|
|
1641
|
-
]) {
|
|
1657
|
+
// Step 10: 存储 API v1.0 - 文件下载(.md 文件内容 = 文件下载)
|
|
1658
|
+
if (foundSpaceId) {
|
|
1642
1659
|
try {
|
|
1643
|
-
const
|
|
1644
|
-
|
|
1645
|
-
|
|
1646
|
-
url: `${DINGTALK_API_V2}${path}`,
|
|
1660
|
+
const dlRes = await axios({
|
|
1661
|
+
method: 'POST',
|
|
1662
|
+
url: `${DINGTALK_API_V2}/v1.0/storage/spaces/${foundSpaceId}/dentries/${foundDentry.dentryId}/downloadInfos/query`,
|
|
1647
1663
|
headers: { 'x-acs-dingtalk-access-token': token, 'Content-Type': 'application/json' },
|
|
1648
|
-
params:
|
|
1649
|
-
data:
|
|
1664
|
+
params: { unionId: opId },
|
|
1665
|
+
data: {}
|
|
1650
1666
|
});
|
|
1667
|
+
const data = dlRes.data;
|
|
1651
1668
|
steps.push({
|
|
1652
|
-
step:
|
|
1669
|
+
step: '存储 v1.0 获取下载链接 (POST /v1.0/storage/spaces/{sid}/dentries/{did}/downloadInfos/query)',
|
|
1653
1670
|
result: '成功',
|
|
1654
|
-
raw:
|
|
1671
|
+
raw: data
|
|
1655
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
|
+
}
|
|
1687
|
+
}
|
|
1656
1688
|
} catch (e) {
|
|
1657
1689
|
steps.push({
|
|
1658
|
-
step:
|
|
1690
|
+
step: '存储 v1.0 获取下载链接',
|
|
1659
1691
|
result: '失败',
|
|
1660
1692
|
raw: { message: e.response?.data?.message || e.message, code: e.response?.data?.code || '(无)', status: e.response?.status || '(无)' }
|
|
1661
1693
|
});
|
|
@@ -1673,21 +1705,30 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
1673
1705
|
}
|
|
1674
1706
|
lines.push('');
|
|
1675
1707
|
});
|
|
1708
|
+
const isAdoc = foundDentry?.name?.endsWith('.adoc') || input.length > 20;
|
|
1676
1709
|
lines.push('💡 诊断结论:');
|
|
1677
1710
|
lines.push(` 输入 ID: ${input}`);
|
|
1678
1711
|
lines.push(` 操作者: ${opId}`);
|
|
1679
1712
|
lines.push('');
|
|
1680
|
-
|
|
1681
|
-
|
|
1682
|
-
|
|
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 在线文档有效' : '❌ 同上错误'}`);
|
|
1683
1721
|
lines.push(' 4️⃣ doc metadata → ❌ 404(该端点不存在)');
|
|
1684
1722
|
lines.push('');
|
|
1685
|
-
lines.push('📌
|
|
1686
|
-
lines.push('
|
|
1687
|
-
|
|
1688
|
-
|
|
1689
|
-
|
|
1690
|
-
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');
|
|
1691
1732
|
|
|
1692
1733
|
return { content: [{ type: 'text', text: lines.join('\n') }] };
|
|
1693
1734
|
}
|
|
@@ -2057,9 +2098,74 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
2057
2098
|
};
|
|
2058
2099
|
} catch (blocksErr) {
|
|
2059
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
|
+
|
|
2060
2163
|
let hint = '';
|
|
2061
2164
|
if (isDocKeyIllegal) {
|
|
2062
|
-
hint = `\n\n原因:
|
|
2165
|
+
hint = `\n\n原因: 此文档不是 .adoc 在线文档,blocks API 无法读取。`;
|
|
2166
|
+
if (workspaceId) {
|
|
2167
|
+
hint += `\n已尝试存储 v1.0 下载 API 但仍失败。`;
|
|
2168
|
+
}
|
|
2063
2169
|
}
|
|
2064
2170
|
return {
|
|
2065
2171
|
content: [{
|