dingtalk-wiki 1.2.6 → 1.2.8
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 +244 -16
- package/package.json +1 -1
package/index.js
CHANGED
|
@@ -726,6 +726,28 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
|
726
726
|
required: ['node_id']
|
|
727
727
|
}
|
|
728
728
|
},
|
|
729
|
+
{
|
|
730
|
+
name: 'debug_resolve_doc_key',
|
|
731
|
+
description: '[调试] 诊断 docKey 解析问题,测试所有可能的 API 路径并返回原始响应',
|
|
732
|
+
inputSchema: {
|
|
733
|
+
type: 'object',
|
|
734
|
+
properties: {
|
|
735
|
+
doc_key: {
|
|
736
|
+
type: 'string',
|
|
737
|
+
description: '文档标识(dentryUuid / docKey / nodeId / URL)'
|
|
738
|
+
},
|
|
739
|
+
workspace_id: {
|
|
740
|
+
type: 'string',
|
|
741
|
+
description: '知识库 ID(可选)'
|
|
742
|
+
},
|
|
743
|
+
operator_id: {
|
|
744
|
+
type: 'string',
|
|
745
|
+
description: '操作者 unionid(不传则使用默认用户)'
|
|
746
|
+
}
|
|
747
|
+
},
|
|
748
|
+
required: ['doc_key']
|
|
749
|
+
}
|
|
750
|
+
},
|
|
729
751
|
{
|
|
730
752
|
name: 'search_wiki',
|
|
731
753
|
description: '按名称搜索知识库中的文档和文件夹(遍历目录树,无需索引即可使用)',
|
|
@@ -1286,6 +1308,203 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
1286
1308
|
};
|
|
1287
1309
|
}
|
|
1288
1310
|
|
|
1311
|
+
case 'debug_resolve_doc_key': {
|
|
1312
|
+
const { doc_key: docKey, workspace_id: workspaceId, operator_id } = args;
|
|
1313
|
+
if (operator_id) {
|
|
1314
|
+
dingtalk.setOperatorId(operator_id);
|
|
1315
|
+
}
|
|
1316
|
+
|
|
1317
|
+
const token = await dingtalk.getAccessToken();
|
|
1318
|
+
const opId = await dingtalk.resolveOperatorId(operator_id || null);
|
|
1319
|
+
const steps = [];
|
|
1320
|
+
const input = String(docKey);
|
|
1321
|
+
|
|
1322
|
+
steps.push({ step: '输入', result: input });
|
|
1323
|
+
|
|
1324
|
+
// Step 1: URL extraction
|
|
1325
|
+
const urlMatch = input.match(/\/i\/nodes\/([^\/\?#]+)/);
|
|
1326
|
+
const urlExtracted = urlMatch ? urlMatch[1] : null;
|
|
1327
|
+
steps.push({ step: 'URL 提取', result: urlExtracted || '无匹配', raw: urlMatch ? urlMatch[0] : null });
|
|
1328
|
+
|
|
1329
|
+
// Step 2: wiki/nodes API
|
|
1330
|
+
try {
|
|
1331
|
+
const nodeRes = await axios({
|
|
1332
|
+
method: 'GET',
|
|
1333
|
+
url: `${DINGTALK_API_V2}/v2.0/wiki/nodes/${input}`,
|
|
1334
|
+
headers: { 'x-acs-dingtalk-access-token': token },
|
|
1335
|
+
params: { operatorId: opId }
|
|
1336
|
+
});
|
|
1337
|
+
const node = nodeRes.data;
|
|
1338
|
+
steps.push({
|
|
1339
|
+
step: 'wiki/nodes API',
|
|
1340
|
+
result: '成功',
|
|
1341
|
+
raw: {
|
|
1342
|
+
nodeId: node.nodeId || node.id || node.node?.nodeId || '(无)',
|
|
1343
|
+
name: node.name || node.node?.name || '(无)',
|
|
1344
|
+
docKey: node.document?.docKey || node.docKey || node.node?.document?.docKey || '(无)',
|
|
1345
|
+
dentryUuid: node.dentryUuid || node.node?.dentryUuid || '(无)',
|
|
1346
|
+
hasChildren: node.hasChildren || node.node?.hasChildren || '(无)',
|
|
1347
|
+
type: node.type || node.node?.type || '(无)',
|
|
1348
|
+
workspaceId: node.workspaceId || node.node?.workspaceId || '(无)',
|
|
1349
|
+
}
|
|
1350
|
+
});
|
|
1351
|
+
} catch (e) {
|
|
1352
|
+
steps.push({
|
|
1353
|
+
step: 'wiki/nodes API',
|
|
1354
|
+
result: '失败',
|
|
1355
|
+
raw: { message: e.response?.data?.message || e.message, code: e.response?.data?.code || '(无)', status: e.response?.status || '(无)' }
|
|
1356
|
+
});
|
|
1357
|
+
}
|
|
1358
|
+
|
|
1359
|
+
// Step 3: doc metadata API (GET /v1.0/doc/suites/documents/{docKey})
|
|
1360
|
+
try {
|
|
1361
|
+
const metaRes = await axios({
|
|
1362
|
+
method: 'GET',
|
|
1363
|
+
url: `${DINGTALK_API_V2}/v1.0/doc/suites/documents/${input}`,
|
|
1364
|
+
headers: { 'x-acs-dingtalk-access-token': token },
|
|
1365
|
+
params: { operatorId: opId }
|
|
1366
|
+
});
|
|
1367
|
+
steps.push({
|
|
1368
|
+
step: 'doc metadata API (GET /documents/{docKey})',
|
|
1369
|
+
result: '成功',
|
|
1370
|
+
raw: metaRes.data
|
|
1371
|
+
});
|
|
1372
|
+
} catch (e) {
|
|
1373
|
+
steps.push({
|
|
1374
|
+
step: 'doc metadata API (GET /documents/{docKey})',
|
|
1375
|
+
result: '失败',
|
|
1376
|
+
raw: { message: e.response?.data?.message || e.message, code: e.response?.data?.code || '(无)', status: e.response?.status || '(无)' }
|
|
1377
|
+
});
|
|
1378
|
+
}
|
|
1379
|
+
|
|
1380
|
+
// Step 4: blocks API
|
|
1381
|
+
try {
|
|
1382
|
+
const blocksRes = await axios({
|
|
1383
|
+
method: 'GET',
|
|
1384
|
+
url: `${DINGTALK_API_V2}/v1.0/doc/suites/documents/${input}/blocks`,
|
|
1385
|
+
headers: { 'x-acs-dingtalk-access-token': token },
|
|
1386
|
+
params: { operatorId: opId }
|
|
1387
|
+
});
|
|
1388
|
+
const blockCount = blocksRes.data?.result?.data?.length || 0;
|
|
1389
|
+
steps.push({
|
|
1390
|
+
step: 'blocks API',
|
|
1391
|
+
result: `成功 (${blockCount} 个 block)`,
|
|
1392
|
+
raw: blocksRes.data
|
|
1393
|
+
});
|
|
1394
|
+
} catch (e) {
|
|
1395
|
+
steps.push({
|
|
1396
|
+
step: 'blocks API',
|
|
1397
|
+
result: '失败',
|
|
1398
|
+
raw: { message: e.response?.data?.message || e.message, code: e.response?.data?.code || '(无)', status: e.response?.status || '(无)' }
|
|
1399
|
+
});
|
|
1400
|
+
}
|
|
1401
|
+
|
|
1402
|
+
// Step 5: overwriteContent API (test with empty content — don't actually write)
|
|
1403
|
+
try {
|
|
1404
|
+
const overwriteRes = await axios({
|
|
1405
|
+
method: 'POST',
|
|
1406
|
+
url: `${DINGTALK_API_V2}/v1.0/doc/suites/documents/${input}/overwriteContent`,
|
|
1407
|
+
headers: { 'x-acs-dingtalk-access-token': token, 'Content-Type': 'application/json' },
|
|
1408
|
+
params: { operatorId: opId },
|
|
1409
|
+
data: { content: 'test', contentType: 'markdown' }
|
|
1410
|
+
});
|
|
1411
|
+
steps.push({
|
|
1412
|
+
step: 'overwriteContent API',
|
|
1413
|
+
result: '成功(可写入)',
|
|
1414
|
+
raw: overwriteRes.data
|
|
1415
|
+
});
|
|
1416
|
+
} catch (e) {
|
|
1417
|
+
steps.push({
|
|
1418
|
+
step: 'overwriteContent API',
|
|
1419
|
+
result: '失败',
|
|
1420
|
+
raw: { message: e.response?.data?.message || e.message, code: e.response?.data?.code || '(无)', status: e.response?.status || '(无)' }
|
|
1421
|
+
});
|
|
1422
|
+
}
|
|
1423
|
+
|
|
1424
|
+
// Step 6: search docs API (requires workspaceId)
|
|
1425
|
+
if (workspaceId) {
|
|
1426
|
+
try {
|
|
1427
|
+
const searchRes = await axios({
|
|
1428
|
+
method: 'GET',
|
|
1429
|
+
url: `${DINGTALK_API_V2}/v1.0/doc/docs`,
|
|
1430
|
+
headers: { 'x-acs-dingtalk-access-token': token },
|
|
1431
|
+
params: { operatorId: opId, workspaceId, maxResults: 10 }
|
|
1432
|
+
});
|
|
1433
|
+
const docs = searchRes.data?.docs || [];
|
|
1434
|
+
const matched = docs.find(d => d.nodeBO?.nodeId === input || d.nodeBO?.dentryUuid === input || d.docKey === input);
|
|
1435
|
+
steps.push({
|
|
1436
|
+
step: 'doc search API (v1.0/doc/docs)',
|
|
1437
|
+
result: `成功 (${docs.length} 篇文档)` + (matched ? ', 找到匹配' : ', 未找到匹配'),
|
|
1438
|
+
raw: matched || `${docs.length} results, first doc docKey=${docs[0]?.docKey || '(none)'}`
|
|
1439
|
+
});
|
|
1440
|
+
} catch (e) {
|
|
1441
|
+
steps.push({
|
|
1442
|
+
step: 'doc search API (v1.0/doc/docs)',
|
|
1443
|
+
result: '失败',
|
|
1444
|
+
raw: { message: e.response?.data?.message || e.message, code: e.response?.data?.code || '(无)', status: e.response?.status || '(无)' }
|
|
1445
|
+
});
|
|
1446
|
+
}
|
|
1447
|
+
} else {
|
|
1448
|
+
steps.push({ step: 'doc search API (v1.0/doc/docs)', result: '跳过(需 workspace_id)' });
|
|
1449
|
+
}
|
|
1450
|
+
|
|
1451
|
+
// Step 7: wiki workspaces + directories search (try all workspaces)
|
|
1452
|
+
if (!workspaceId) {
|
|
1453
|
+
try {
|
|
1454
|
+
const wsRes = await axios({
|
|
1455
|
+
method: 'GET',
|
|
1456
|
+
url: `${DINGTALK_API_V2}/v2.0/wiki/workspaces`,
|
|
1457
|
+
headers: { 'x-acs-dingtalk-access-token': token },
|
|
1458
|
+
params: { operatorId: opId }
|
|
1459
|
+
});
|
|
1460
|
+
const workspaces = wsRes.data?.workspaces || [];
|
|
1461
|
+
let found = null;
|
|
1462
|
+
for (const ws of workspaces) {
|
|
1463
|
+
if (found) break;
|
|
1464
|
+
try {
|
|
1465
|
+
const dirRes = await axios({
|
|
1466
|
+
method: 'GET',
|
|
1467
|
+
url: `${DINGTALK_API_V2}/v2.0/doc/spaces/${ws.workspaceId}/directories`,
|
|
1468
|
+
headers: { 'x-acs-dingtalk-access-token': token },
|
|
1469
|
+
params: { operatorId: opId, maxResults: 500 }
|
|
1470
|
+
});
|
|
1471
|
+
const children = dirRes.data?.children || [];
|
|
1472
|
+
const match = children.find(c => c.dentryUuid === input || c.dentryId === input || c.id === input);
|
|
1473
|
+
if (match) {
|
|
1474
|
+
found = { workspaceId: ws.workspaceId, workspaceName: ws.name, node: match };
|
|
1475
|
+
}
|
|
1476
|
+
} catch (dirErr) { /* skip workspace */ }
|
|
1477
|
+
}
|
|
1478
|
+
steps.push({
|
|
1479
|
+
step: '遍历知识库目录匹配',
|
|
1480
|
+
result: found ? `在知识库 ${found.workspaceName}(${found.workspaceId}) 中找到` : '在所有知识库中未找到匹配',
|
|
1481
|
+
raw: found ? { workspaceId: found.workspaceId, name: found.node?.name, dentryUuid: found.node?.dentryUuid, dentryId: found.node?.dentryId, hasChildren: found.node?.hasChildren, contentType: found.node?.contentType, docKey: found.node?.docKey || '(无)' } : null
|
|
1482
|
+
});
|
|
1483
|
+
} catch (e) {
|
|
1484
|
+
steps.push({ step: '遍历知识库目录匹配', result: '失败', raw: e.message });
|
|
1485
|
+
}
|
|
1486
|
+
}
|
|
1487
|
+
|
|
1488
|
+
const lines = ['🔍 docKey 诊断报告', '', `输入: ${input}`, `operatorId: ${opId}`, ''];
|
|
1489
|
+
steps.forEach(s => {
|
|
1490
|
+
lines.push(`--- ${s.step} ---`);
|
|
1491
|
+
lines.push(`结果: ${s.result}`);
|
|
1492
|
+
if (s.raw) {
|
|
1493
|
+
lines.push(`详情: ${JSON.stringify(s.raw, null, 2)}`);
|
|
1494
|
+
}
|
|
1495
|
+
lines.push('');
|
|
1496
|
+
});
|
|
1497
|
+
lines.push('💡 提示: 如果 wiki/nodes API 成功并返回了 docKey,请使用该 docKey 调用 get_wiki_doc_content');
|
|
1498
|
+
lines.push(' 如果 doc metadata API 成功,说明该 docKey 在 doc suite 中有效');
|
|
1499
|
+
lines.push(' 如果 blocks API 失败但 overwriteContent 成功,问题在 blocks API 端');
|
|
1500
|
+
lines.push(' 如果所有 API 都失败,说明该 docKey 在 doc suite 中不存在');
|
|
1501
|
+
if (!workspaceId) {
|
|
1502
|
+
lines.push(' 建议提供 workspace_id 参数以启用搜索回退路径');
|
|
1503
|
+
}
|
|
1504
|
+
|
|
1505
|
+
return { content: [{ type: 'text', text: lines.join('\n') }] };
|
|
1506
|
+
}
|
|
1507
|
+
|
|
1289
1508
|
case 'create_wiki_doc': {
|
|
1290
1509
|
const { workspace_id, parent_node_id, name, doc_type = 'DOC', operator_id } = args;
|
|
1291
1510
|
if (operator_id) {
|
|
@@ -1632,23 +1851,32 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
1632
1851
|
} catch (e) { console.error(`[DEBUG] get_wiki_doc_content: search fallback failed: ${e.message}`); }
|
|
1633
1852
|
}
|
|
1634
1853
|
|
|
1635
|
-
|
|
1636
|
-
|
|
1637
|
-
|
|
1638
|
-
|
|
1639
|
-
|
|
1640
|
-
|
|
1641
|
-
|
|
1642
|
-
|
|
1643
|
-
|
|
1644
|
-
|
|
1854
|
+
try {
|
|
1855
|
+
const result = await dingtalk.docRequest('GET', `/v1.0/doc/suites/documents/${realDocKey}/blocks`, { operatorId: operator_id || null });
|
|
1856
|
+
const blocks = result.result?.data || [];
|
|
1857
|
+
let output = '';
|
|
1858
|
+
blocks.forEach((block) => {
|
|
1859
|
+
const text = extractBlockText(block);
|
|
1860
|
+
output += text + '\n\n';
|
|
1861
|
+
});
|
|
1862
|
+
if (!blocks.length) {
|
|
1863
|
+
output = '(文档为空或无可读内容)';
|
|
1864
|
+
}
|
|
1865
|
+
return {
|
|
1866
|
+
content: [{
|
|
1867
|
+
type: 'text',
|
|
1868
|
+
text: output.trim()
|
|
1869
|
+
}]
|
|
1870
|
+
};
|
|
1871
|
+
} catch (blocksErr) {
|
|
1872
|
+
return {
|
|
1873
|
+
content: [{
|
|
1874
|
+
type: 'text',
|
|
1875
|
+
text: `❌ blocks API 调用失败\n错误: ${blocksErr.message}\n\n调试信息:\n- 输入 doc_key: ${docKey}\n- 解析后 realDocKey: ${realDocKey}\n- workspaceId: ${workspaceId || '未提供'}\n- operator_id: ${operator_id || '未提供(将自动解析)'}`
|
|
1876
|
+
}],
|
|
1877
|
+
isError: true
|
|
1878
|
+
};
|
|
1645
1879
|
}
|
|
1646
|
-
return {
|
|
1647
|
-
content: [{
|
|
1648
|
-
type: 'text',
|
|
1649
|
-
text: output.trim()
|
|
1650
|
-
}]
|
|
1651
|
-
};
|
|
1652
1880
|
}
|
|
1653
1881
|
|
|
1654
1882
|
case 'update_wiki_doc_content': {
|