dingtalk-wiki 1.2.4 → 1.2.6
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 +95 -26
- package/package.json +1 -1
package/index.js
CHANGED
|
@@ -510,7 +510,7 @@ class DingTalkClient {
|
|
|
510
510
|
const url = `${DINGTALK_API_V2}${pathName}`;
|
|
511
511
|
|
|
512
512
|
try {
|
|
513
|
-
const
|
|
513
|
+
const requestConfig = {
|
|
514
514
|
method,
|
|
515
515
|
url,
|
|
516
516
|
headers: {
|
|
@@ -522,10 +522,16 @@ class DingTalkClient {
|
|
|
522
522
|
...extraParams
|
|
523
523
|
},
|
|
524
524
|
data
|
|
525
|
-
}
|
|
525
|
+
};
|
|
526
|
+
if (pathName.includes('blocks')) {
|
|
527
|
+
console.error(`[DEBUG] docRequest: ${method} ${url} operatorId=${resolvedOperatorId} extraParams=${JSON.stringify(extraParams)}`);
|
|
528
|
+
}
|
|
529
|
+
const response = await axios(requestConfig);
|
|
526
530
|
return response.data;
|
|
527
531
|
} catch (error) {
|
|
528
532
|
if (error.response) {
|
|
533
|
+
const fullErr = JSON.stringify(error.response.data);
|
|
534
|
+
console.error(`[DEBUG] docRequest error (${pathName}): status=${error.response.status} data=${fullErr}`);
|
|
529
535
|
throw new Error(`${error.response.data?.message || error.message} (code: ${error.response.data?.code})`);
|
|
530
536
|
}
|
|
531
537
|
throw error;
|
|
@@ -850,7 +856,11 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
|
850
856
|
properties: {
|
|
851
857
|
doc_key: {
|
|
852
858
|
type: 'string',
|
|
853
|
-
description: '
|
|
859
|
+
description: '文档标识。支持 wiki nodes 的 nodeId(dentryUuid)、docKey,或文档 URL。重要:create_wiki_doc 返回的 nodeId 不能用于内容读写,请使用 docKey 或 dentryUuid。若传入 create 的 nodeId,需额外提供 workspace_id 以自动查找'
|
|
860
|
+
},
|
|
861
|
+
workspace_id: {
|
|
862
|
+
type: 'string',
|
|
863
|
+
description: '知识库 ID(可选)。当 doc_key 来自 create_wiki_doc 返回的 nodeId 时,提供此参数可自动查找正确的 dentryUuid'
|
|
854
864
|
},
|
|
855
865
|
operator_id: {
|
|
856
866
|
type: 'string',
|
|
@@ -868,12 +878,16 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
|
868
878
|
properties: {
|
|
869
879
|
doc_key: {
|
|
870
880
|
type: 'string',
|
|
871
|
-
description: '
|
|
881
|
+
description: '文档标识。支持 wiki nodes 的 nodeId(dentryUuid)、docKey,或文档 URL。重要:create_wiki_doc 返回的 nodeId 不能用于内容读写,请使用 docKey 或 dentryUuid。若传入 create 的 nodeId,需额外提供 workspace_id 以自动查找'
|
|
872
882
|
},
|
|
873
883
|
content: {
|
|
874
884
|
type: 'string',
|
|
875
885
|
description: '要写入的 Markdown 内容'
|
|
876
886
|
},
|
|
887
|
+
workspace_id: {
|
|
888
|
+
type: 'string',
|
|
889
|
+
description: '知识库 ID(可选)。当 doc_key 来自 create_wiki_doc 返回的 nodeId 时,提供此参数可自动查找正确的 dentryUuid'
|
|
890
|
+
},
|
|
877
891
|
operator_id: {
|
|
878
892
|
type: 'string',
|
|
879
893
|
description: '操作者 unionid(不传则使用默认用户)'
|
|
@@ -1144,6 +1158,38 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
|
1144
1158
|
};
|
|
1145
1159
|
});
|
|
1146
1160
|
|
|
1161
|
+
async function resolveDocKey(docKey, operatorId, dingtalkInstance) {
|
|
1162
|
+
const rawInput = docKey;
|
|
1163
|
+
|
|
1164
|
+
// 从 URL 中提取 ID
|
|
1165
|
+
const urlMatch = String(docKey).match(/\/i\/nodes\/([^\/\?#]+)/);
|
|
1166
|
+
docKey = urlMatch ? urlMatch[1] : docKey;
|
|
1167
|
+
console.error(`[DEBUG] resolveDocKey: rawInput=${rawInput}, extracted=${docKey}`);
|
|
1168
|
+
|
|
1169
|
+
try {
|
|
1170
|
+
const nodeInfo = await dingtalkInstance.docRequest('GET', `/v2.0/wiki/nodes/${docKey}`, { operatorId });
|
|
1171
|
+
const node = nodeInfo.node || nodeInfo;
|
|
1172
|
+
console.error(`[DEBUG] resolveDocKey: wiki/nodes response keys=${Object.keys(node).join(',')}`);
|
|
1173
|
+
|
|
1174
|
+
// wiki/nodes 的 nodeId = dentryUuid,直接可用
|
|
1175
|
+
// 但 node.document.docKey 可能是不同的值,优先使用
|
|
1176
|
+
if (node.document?.docKey) {
|
|
1177
|
+
console.error(`[DEBUG] resolveDocKey: found document.docKey=${node.document.docKey}`);
|
|
1178
|
+
return node.document.docKey;
|
|
1179
|
+
}
|
|
1180
|
+
// 部分响应直接把 docKey 放在顶层
|
|
1181
|
+
if (node.docKey) {
|
|
1182
|
+
console.error(`[DEBUG] resolveDocKey: found top-level docKey=${node.docKey}`);
|
|
1183
|
+
return node.docKey;
|
|
1184
|
+
}
|
|
1185
|
+
console.error(`[DEBUG] resolveDocKey: no docKey in response, returning raw docKey=${docKey}`);
|
|
1186
|
+
} catch (e) {
|
|
1187
|
+
console.error(`[DEBUG] resolveDocKey: wiki/nodes failed: ${e.message}`);
|
|
1188
|
+
}
|
|
1189
|
+
|
|
1190
|
+
return docKey;
|
|
1191
|
+
}
|
|
1192
|
+
|
|
1147
1193
|
// 工具调用处理
|
|
1148
1194
|
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
1149
1195
|
const { name, arguments: args } = request.params;
|
|
@@ -1297,11 +1343,14 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
1297
1343
|
'',
|
|
1298
1344
|
`${typeIcon} ${name}`,
|
|
1299
1345
|
`🗂️ 类型: ${doc_type}`,
|
|
1300
|
-
`🆔 Node ID: ${doc.nodeId}
|
|
1346
|
+
`🆔 Node ID: ${doc.nodeId}`,
|
|
1301
1347
|
];
|
|
1302
1348
|
|
|
1303
1349
|
if (doc.docKey) {
|
|
1304
|
-
lines.push(`🔑 DocKey
|
|
1350
|
+
lines.push(`🔑 DocKey(用于内容读写): ${doc.docKey}`);
|
|
1351
|
+
}
|
|
1352
|
+
if (doc.dentryUuid) {
|
|
1353
|
+
lines.push(`📄 dentryUuid(用于内容读写): ${doc.dentryUuid}`);
|
|
1305
1354
|
}
|
|
1306
1355
|
if (doc.url) {
|
|
1307
1356
|
lines.push(`🔗 链接: ${doc.url}`);
|
|
@@ -1319,8 +1368,10 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
1319
1368
|
url: doc.url || '',
|
|
1320
1369
|
type: doc_type,
|
|
1321
1370
|
});
|
|
1322
|
-
|
|
1323
|
-
|
|
1371
|
+
const contentId = doc.docKey || doc.dentryUuid || doc.nodeId;
|
|
1372
|
+
if (args.content && contentId) {
|
|
1373
|
+
dingtalk.docRequest('POST', `/v1.0/doc/suites/documents/${contentId}/overwriteContent`, {
|
|
1374
|
+
operatorId: opId,
|
|
1324
1375
|
data: { content: args.content, contentType: 'markdown' }
|
|
1325
1376
|
}).catch(() => {});
|
|
1326
1377
|
}
|
|
@@ -1557,20 +1608,31 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
1557
1608
|
}
|
|
1558
1609
|
|
|
1559
1610
|
case 'get_wiki_doc_content': {
|
|
1560
|
-
const { doc_key: docKey, operator_id } = args;
|
|
1611
|
+
const { doc_key: docKey, workspace_id: workspaceId, operator_id } = args;
|
|
1561
1612
|
if (operator_id) {
|
|
1562
1613
|
dingtalk.setOperatorId(operator_id);
|
|
1563
1614
|
}
|
|
1564
1615
|
|
|
1565
|
-
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
|
|
1616
|
+
let realDocKey = await resolveDocKey(docKey, operator_id || null, dingtalk);
|
|
1617
|
+
console.error(`[DEBUG] get_wiki_doc_content: input=${docKey}, resolved=${realDocKey}`);
|
|
1618
|
+
|
|
1619
|
+
// 如果 resolve 后值没变(可能是 create 返回的 nodeId),尝试搜索 API 查找
|
|
1620
|
+
if (realDocKey === docKey && workspaceId) {
|
|
1621
|
+
try {
|
|
1622
|
+
const searchRes = await dingtalk.docRequest('GET', `/v1.0/doc/docs`, {
|
|
1623
|
+
operatorId: operator_id || null,
|
|
1624
|
+
extraParams: { workspaceId, keyword: '', maxResults: 50 }
|
|
1625
|
+
});
|
|
1626
|
+
const docs = searchRes.docs || [];
|
|
1627
|
+
const matched = docs.find(d => d.nodeBO?.nodeId === docKey || d.nodeBO?.nodeId === realDocKey) || docs[0];
|
|
1628
|
+
if (matched?.nodeBO?.nodeId) {
|
|
1629
|
+
realDocKey = matched.nodeBO.nodeId;
|
|
1630
|
+
console.error(`[DEBUG] get_wiki_doc_content: search fallback resolved to ${realDocKey}`);
|
|
1631
|
+
}
|
|
1632
|
+
} catch (e) { console.error(`[DEBUG] get_wiki_doc_content: search fallback failed: ${e.message}`); }
|
|
1633
|
+
}
|
|
1573
1634
|
|
|
1635
|
+
console.error(`[DEBUG] get_wiki_doc_content: calling blocks API with docKey=${realDocKey}`);
|
|
1574
1636
|
const result = await dingtalk.docRequest('GET', `/v1.0/doc/suites/documents/${realDocKey}/blocks`, { operatorId: operator_id || null });
|
|
1575
1637
|
const blocks = result.result?.data || [];
|
|
1576
1638
|
let output = '';
|
|
@@ -1590,19 +1652,26 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
1590
1652
|
}
|
|
1591
1653
|
|
|
1592
1654
|
case 'update_wiki_doc_content': {
|
|
1593
|
-
const { doc_key: docKey, content, operator_id } = args;
|
|
1655
|
+
const { doc_key: docKey, content, workspace_id: workspaceId, operator_id } = args;
|
|
1594
1656
|
if (operator_id) {
|
|
1595
1657
|
dingtalk.setOperatorId(operator_id);
|
|
1596
1658
|
}
|
|
1597
1659
|
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
|
|
1604
|
-
|
|
1605
|
-
|
|
1660
|
+
let realDocKey = await resolveDocKey(docKey, operator_id || null, dingtalk);
|
|
1661
|
+
|
|
1662
|
+
if (realDocKey === docKey && workspaceId) {
|
|
1663
|
+
try {
|
|
1664
|
+
const searchRes = await dingtalk.docRequest('GET', `/v1.0/doc/docs`, {
|
|
1665
|
+
operatorId: operator_id || null,
|
|
1666
|
+
extraParams: { workspaceId, keyword: '', maxResults: 50 }
|
|
1667
|
+
});
|
|
1668
|
+
const docs = searchRes.docs || [];
|
|
1669
|
+
const matched = docs.find(d => d.nodeBO?.nodeId === docKey || d.nodeBO?.nodeId === realDocKey) || docs[0];
|
|
1670
|
+
if (matched?.nodeBO?.nodeId) {
|
|
1671
|
+
realDocKey = matched.nodeBO.nodeId;
|
|
1672
|
+
}
|
|
1673
|
+
} catch (e) { /* ignore */ }
|
|
1674
|
+
}
|
|
1606
1675
|
|
|
1607
1676
|
await dingtalk.docRequest('POST', `/v1.0/doc/suites/documents/${realDocKey}/overwriteContent`, {
|
|
1608
1677
|
operatorId: operator_id || null,
|