dingtalk-wiki 1.2.16 → 1.2.17
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 +7 -459
- package/package.json +1 -1
package/index.js
CHANGED
|
@@ -223,7 +223,7 @@ async function rebuildSearchIndex() {
|
|
|
223
223
|
method: 'POST',
|
|
224
224
|
url: `${DINGTALK_API_V2}/v1.0/storage/spaces/${child.spaceId}/dentries/${child.dentryId}/downloadInfos/query`,
|
|
225
225
|
headers: { 'x-acs-dingtalk-access-token': token, 'Content-Type': 'application/json' },
|
|
226
|
-
params: { unionId: await dingtalk.
|
|
226
|
+
params: { unionId: await dingtalk.resolveOperatorId(null) },
|
|
227
227
|
data: {}
|
|
228
228
|
});
|
|
229
229
|
const data = dlRes.data;
|
|
@@ -747,28 +747,6 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
|
747
747
|
required: ['node_id']
|
|
748
748
|
}
|
|
749
749
|
},
|
|
750
|
-
{
|
|
751
|
-
name: 'debug_resolve_doc_key',
|
|
752
|
-
description: '[调试] 诊断 docKey 解析问题,测试所有可能的 API 路径并返回原始响应',
|
|
753
|
-
inputSchema: {
|
|
754
|
-
type: 'object',
|
|
755
|
-
properties: {
|
|
756
|
-
doc_key: {
|
|
757
|
-
type: 'string',
|
|
758
|
-
description: '文档标识(dentryUuid / docKey / nodeId / URL)'
|
|
759
|
-
},
|
|
760
|
-
workspace_id: {
|
|
761
|
-
type: 'string',
|
|
762
|
-
description: '知识库 ID(可选)'
|
|
763
|
-
},
|
|
764
|
-
operator_id: {
|
|
765
|
-
type: 'string',
|
|
766
|
-
description: '操作者 unionid(不传则使用默认用户)'
|
|
767
|
-
}
|
|
768
|
-
},
|
|
769
|
-
required: ['doc_key']
|
|
770
|
-
}
|
|
771
|
-
},
|
|
772
750
|
{
|
|
773
751
|
name: 'search_wiki',
|
|
774
752
|
description: '按名称搜索知识库中的文档和文件夹(遍历目录树,无需索引即可使用)',
|
|
@@ -1329,410 +1307,6 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
1329
1307
|
};
|
|
1330
1308
|
}
|
|
1331
1309
|
|
|
1332
|
-
case 'debug_resolve_doc_key': {
|
|
1333
|
-
const { doc_key: docKey, workspace_id: workspaceId, operator_id } = args;
|
|
1334
|
-
if (operator_id) {
|
|
1335
|
-
dingtalk.setOperatorId(operator_id);
|
|
1336
|
-
}
|
|
1337
|
-
|
|
1338
|
-
const token = await dingtalk.getAccessToken();
|
|
1339
|
-
const opId = await dingtalk.resolveOperatorId(operator_id || null);
|
|
1340
|
-
const steps = [];
|
|
1341
|
-
const input = String(docKey);
|
|
1342
|
-
|
|
1343
|
-
steps.push({ step: '输入', result: input });
|
|
1344
|
-
|
|
1345
|
-
// Step 1: URL extraction
|
|
1346
|
-
const urlMatch = input.match(/\/i\/nodes\/([^\/\?#]+)/);
|
|
1347
|
-
const urlExtracted = urlMatch ? urlMatch[1] : null;
|
|
1348
|
-
steps.push({ step: 'URL 提取', result: urlExtracted || '无匹配', raw: urlMatch ? urlMatch[0] : null });
|
|
1349
|
-
|
|
1350
|
-
// Step 2: wiki/nodes API
|
|
1351
|
-
let wikiNodeResponse = null;
|
|
1352
|
-
let wikiWsId = null;
|
|
1353
|
-
try {
|
|
1354
|
-
const nodeRes = await axios({
|
|
1355
|
-
method: 'GET',
|
|
1356
|
-
url: `${DINGTALK_API_V2}/v2.0/wiki/nodes/${input}`,
|
|
1357
|
-
headers: { 'x-acs-dingtalk-access-token': token },
|
|
1358
|
-
params: { operatorId: opId }
|
|
1359
|
-
});
|
|
1360
|
-
wikiNodeResponse = nodeRes.data;
|
|
1361
|
-
wikiWsId = wikiNodeResponse.workspaceId || wikiNodeResponse.node?.workspaceId || null;
|
|
1362
|
-
const node = wikiNodeResponse;
|
|
1363
|
-
steps.push({
|
|
1364
|
-
step: 'wiki/nodes API',
|
|
1365
|
-
result: '成功',
|
|
1366
|
-
raw: node
|
|
1367
|
-
});
|
|
1368
|
-
} catch (e) {
|
|
1369
|
-
steps.push({
|
|
1370
|
-
step: 'wiki/nodes API',
|
|
1371
|
-
result: '失败',
|
|
1372
|
-
raw: { message: e.response?.data?.message || e.message, code: e.response?.data?.code || '(无)', status: e.response?.status || '(无)' }
|
|
1373
|
-
});
|
|
1374
|
-
}
|
|
1375
|
-
|
|
1376
|
-
const effectiveWsId = workspaceId || wikiWsId;
|
|
1377
|
-
|
|
1378
|
-
// Step 3: doc metadata API (GET /v1.0/doc/suites/documents/{docKey})
|
|
1379
|
-
try {
|
|
1380
|
-
const metaRes = await axios({
|
|
1381
|
-
method: 'GET',
|
|
1382
|
-
url: `${DINGTALK_API_V2}/v1.0/doc/suites/documents/${input}`,
|
|
1383
|
-
headers: { 'x-acs-dingtalk-access-token': token },
|
|
1384
|
-
params: { operatorId: opId }
|
|
1385
|
-
});
|
|
1386
|
-
steps.push({
|
|
1387
|
-
step: 'doc metadata API (GET /documents/{docKey})',
|
|
1388
|
-
result: '成功',
|
|
1389
|
-
raw: metaRes.data
|
|
1390
|
-
});
|
|
1391
|
-
} catch (e) {
|
|
1392
|
-
steps.push({
|
|
1393
|
-
step: 'doc metadata API (GET /documents/{docKey})',
|
|
1394
|
-
result: '失败',
|
|
1395
|
-
raw: { message: e.response?.data?.message || e.message, code: e.response?.data?.code || '(无)', status: e.response?.status || '(无)' }
|
|
1396
|
-
});
|
|
1397
|
-
}
|
|
1398
|
-
|
|
1399
|
-
// Step 4: blocks API
|
|
1400
|
-
try {
|
|
1401
|
-
const blocksRes = await axios({
|
|
1402
|
-
method: 'GET',
|
|
1403
|
-
url: `${DINGTALK_API_V2}/v1.0/doc/suites/documents/${input}/blocks`,
|
|
1404
|
-
headers: { 'x-acs-dingtalk-access-token': token },
|
|
1405
|
-
params: { operatorId: opId }
|
|
1406
|
-
});
|
|
1407
|
-
const blockCount = blocksRes.data?.result?.data?.length || 0;
|
|
1408
|
-
steps.push({
|
|
1409
|
-
step: 'blocks API',
|
|
1410
|
-
result: `成功 (${blockCount} 个 block)`,
|
|
1411
|
-
raw: blocksRes.data
|
|
1412
|
-
});
|
|
1413
|
-
} catch (e) {
|
|
1414
|
-
steps.push({
|
|
1415
|
-
step: 'blocks API',
|
|
1416
|
-
result: '失败',
|
|
1417
|
-
raw: { message: e.response?.data?.message || e.message, code: e.response?.data?.code || '(无)', status: e.response?.status || '(无)' }
|
|
1418
|
-
});
|
|
1419
|
-
}
|
|
1420
|
-
|
|
1421
|
-
// Step 5: overwriteContent API (test with empty content — don't actually write)
|
|
1422
|
-
try {
|
|
1423
|
-
const overwriteRes = await axios({
|
|
1424
|
-
method: 'POST',
|
|
1425
|
-
url: `${DINGTALK_API_V2}/v1.0/doc/suites/documents/${input}/overwriteContent`,
|
|
1426
|
-
headers: { 'x-acs-dingtalk-access-token': token, 'Content-Type': 'application/json' },
|
|
1427
|
-
params: { operatorId: opId },
|
|
1428
|
-
data: { content: 'test', contentType: 'markdown' }
|
|
1429
|
-
});
|
|
1430
|
-
steps.push({
|
|
1431
|
-
step: 'overwriteContent API',
|
|
1432
|
-
result: '成功(可写入)',
|
|
1433
|
-
raw: overwriteRes.data
|
|
1434
|
-
});
|
|
1435
|
-
} catch (e) {
|
|
1436
|
-
steps.push({
|
|
1437
|
-
step: 'overwriteContent API',
|
|
1438
|
-
result: '失败',
|
|
1439
|
-
raw: { message: e.response?.data?.message || e.message, code: e.response?.data?.code || '(无)', status: e.response?.status || '(无)' }
|
|
1440
|
-
});
|
|
1441
|
-
}
|
|
1442
|
-
|
|
1443
|
-
// Step 6: search docs API (v1.0/doc/docs) — 需要 workspaceId + keyword
|
|
1444
|
-
if (effectiveWsId) {
|
|
1445
|
-
// Try without keyword first (might list all)
|
|
1446
|
-
try {
|
|
1447
|
-
const searchRes = await axios({
|
|
1448
|
-
method: 'GET',
|
|
1449
|
-
url: `${DINGTALK_API_V2}/v1.0/doc/docs`,
|
|
1450
|
-
headers: { 'x-acs-dingtalk-access-token': token },
|
|
1451
|
-
params: { operatorId: opId, workspaceId: effectiveWsId, maxResults: 50 }
|
|
1452
|
-
});
|
|
1453
|
-
const docs = searchRes.data?.docs || [];
|
|
1454
|
-
const matched = docs.find(d => d.nodeBO?.nodeId === input || d.nodeBO?.dentryUuid === input || d.docKey === input || d.nodeBO?.dentryId === input);
|
|
1455
|
-
steps.push({
|
|
1456
|
-
step: `doc search API (workspace=${effectiveWsId}, no keyword)`,
|
|
1457
|
-
result: `成功 (${docs.length} 篇文档)` + (matched ? ', 找到匹配!' : ', 未找到匹配'),
|
|
1458
|
-
raw: docs.length > 0 ? { count: docs.length, firstDocDocKey: docs[0]?.docKey || '(无)', firstDocNodeId: docs[0]?.nodeBO?.nodeId || '(无)', firstDocName: docs[0]?.name || '(无)' } : '空结果'
|
|
1459
|
-
});
|
|
1460
|
-
} catch (e) {
|
|
1461
|
-
steps.push({
|
|
1462
|
-
step: `doc search API (workspace=${effectiveWsId}, no keyword)`,
|
|
1463
|
-
result: '失败',
|
|
1464
|
-
raw: { message: e.response?.data?.message || e.message, code: e.response?.data?.code || '(无)', status: e.response?.status || '(无)' }
|
|
1465
|
-
});
|
|
1466
|
-
}
|
|
1467
|
-
// Try with keyword from wiki/nodes name if available
|
|
1468
|
-
const wikiName = wikiNodeResponse?.node?.name || wikiNodeResponse?.name || '';
|
|
1469
|
-
if (wikiName) {
|
|
1470
|
-
try {
|
|
1471
|
-
const searchRes = await axios({
|
|
1472
|
-
method: 'GET',
|
|
1473
|
-
url: `${DINGTALK_API_V2}/v1.0/doc/docs`,
|
|
1474
|
-
headers: { 'x-acs-dingtalk-access-token': token },
|
|
1475
|
-
params: { operatorId: opId, workspaceId: effectiveWsId, keyword: wikiName.replace(/\.\w+$/, ''), maxResults: 10 }
|
|
1476
|
-
});
|
|
1477
|
-
const docs = searchRes.data?.docs || [];
|
|
1478
|
-
steps.push({
|
|
1479
|
-
step: `doc search API (keyword="${wikiName}")`,
|
|
1480
|
-
result: `成功 (${docs.length} 篇)`,
|
|
1481
|
-
raw: docs.length > 0 ? docs.map(d => ({ docKey: d.docKey || '(无)', name: d.name || '(无)', nodeId: d.nodeBO?.nodeId || '(无)', dentryUuid: d.nodeBO?.dentryUuid || '(无)' })) : '空结果'
|
|
1482
|
-
});
|
|
1483
|
-
} catch (e) {
|
|
1484
|
-
steps.push({
|
|
1485
|
-
step: `doc search API (keyword="${wikiName}")`,
|
|
1486
|
-
result: '失败',
|
|
1487
|
-
raw: { message: e.response?.data?.message || e.message, code: e.response?.data?.code || '(无)', status: e.response?.status || '(无)' }
|
|
1488
|
-
});
|
|
1489
|
-
}
|
|
1490
|
-
}
|
|
1491
|
-
} else {
|
|
1492
|
-
steps.push({ step: 'doc search API', result: '跳过(无法获取 workspace_id)' });
|
|
1493
|
-
}
|
|
1494
|
-
|
|
1495
|
-
// Step 7: 在知识库目录中查找此节点 + 查看是否有 docKey 字段
|
|
1496
|
-
if (effectiveWsId) {
|
|
1497
|
-
try {
|
|
1498
|
-
const dirRes = await axios({
|
|
1499
|
-
method: 'GET',
|
|
1500
|
-
url: `${DINGTALK_API_V2}/v2.0/doc/spaces/${effectiveWsId}/directories`,
|
|
1501
|
-
headers: { 'x-acs-dingtalk-access-token': token },
|
|
1502
|
-
params: { operatorId: opId, maxResults: 500 }
|
|
1503
|
-
});
|
|
1504
|
-
const children = dirRes.data?.children || [];
|
|
1505
|
-
const match = children.find(c => c.dentryUuid === input || c.dentryId === input || c.id === input);
|
|
1506
|
-
// Show first few children's available fields so user can see what fields exist
|
|
1507
|
-
const sampleKeys = children.length > 0 ? Object.keys(children[0]) : [];
|
|
1508
|
-
steps.push({
|
|
1509
|
-
step: `知识库目录 (workspace=${effectiveWsId})`,
|
|
1510
|
-
result: `${children.length} 个子节点` + (match ? ', 找到匹配!' : ', 未找到匹配'),
|
|
1511
|
-
raw: match ? match : { sampleFields: sampleKeys, firstChildSample: children[0] || null }
|
|
1512
|
-
});
|
|
1513
|
-
} catch (e) {
|
|
1514
|
-
steps.push({
|
|
1515
|
-
step: `知识库目录 (workspace=${effectiveWsId})`,
|
|
1516
|
-
result: '失败',
|
|
1517
|
-
raw: { message: e.response?.data?.message || e.message, code: e.response?.data?.code || '(无)', status: e.response?.status || '(无)' }
|
|
1518
|
-
});
|
|
1519
|
-
}
|
|
1520
|
-
}
|
|
1521
|
-
|
|
1522
|
-
// Step 8: 用 workspaceId 再次尝试 overwriteContent(确认文档套件是否完全不可用)
|
|
1523
|
-
if (effectiveWsId) {
|
|
1524
|
-
try {
|
|
1525
|
-
const overwriteRes = await axios({
|
|
1526
|
-
method: 'POST',
|
|
1527
|
-
url: `${DINGTALK_API_V2}/v1.0/doc/suites/documents/${input}/overwriteContent`,
|
|
1528
|
-
headers: { 'x-acs-dingtalk-access-token': token, 'Content-Type': 'application/json' },
|
|
1529
|
-
params: { operatorId: opId },
|
|
1530
|
-
data: { content: 'test', contentType: 'markdown' }
|
|
1531
|
-
});
|
|
1532
|
-
steps.push({
|
|
1533
|
-
step: 'overwriteContent(带 workspaceId)',
|
|
1534
|
-
result: '成功',
|
|
1535
|
-
raw: overwriteRes.data
|
|
1536
|
-
});
|
|
1537
|
-
} catch (e) {
|
|
1538
|
-
steps.push({
|
|
1539
|
-
step: 'overwriteContent(带 workspaceId)',
|
|
1540
|
-
result: '失败',
|
|
1541
|
-
raw: { message: e.response?.data?.message || e.message, code: e.response?.data?.code || '(无)', status: e.response?.status || '(无)' }
|
|
1542
|
-
});
|
|
1543
|
-
}
|
|
1544
|
-
}
|
|
1545
|
-
|
|
1546
|
-
// Step 9: 递归遍历目录树查找文档 + 存储API下载内容
|
|
1547
|
-
let foundDentry = null;
|
|
1548
|
-
let foundSpaceId = null;
|
|
1549
|
-
if (effectiveWsId) {
|
|
1550
|
-
try {
|
|
1551
|
-
// BFS 遍历目录树查找 dentry
|
|
1552
|
-
const queue = [''];
|
|
1553
|
-
const visited = new Set();
|
|
1554
|
-
while (queue.length > 0 && !foundDentry) {
|
|
1555
|
-
const parentId = queue.shift();
|
|
1556
|
-
if (visited.has(parentId)) continue;
|
|
1557
|
-
visited.add(parentId);
|
|
1558
|
-
try {
|
|
1559
|
-
const dirParams = { operatorId: opId, maxResults: 500 };
|
|
1560
|
-
if (parentId) dirParams.dentryId = parentId;
|
|
1561
|
-
const dirRes = await axios({
|
|
1562
|
-
method: 'GET',
|
|
1563
|
-
url: `${DINGTALK_API_V2}/v2.0/doc/spaces/${effectiveWsId}/directories`,
|
|
1564
|
-
headers: { 'x-acs-dingtalk-access-token': token },
|
|
1565
|
-
params: dirParams
|
|
1566
|
-
});
|
|
1567
|
-
const children = dirRes.data?.children || [];
|
|
1568
|
-
for (const child of children) {
|
|
1569
|
-
if (child.dentryUuid === input || child.dentryId === input) {
|
|
1570
|
-
foundDentry = child;
|
|
1571
|
-
foundSpaceId = child.spaceId;
|
|
1572
|
-
break;
|
|
1573
|
-
}
|
|
1574
|
-
if (child.hasChildren && child.dentryId && !visited.has(child.dentryId)) {
|
|
1575
|
-
queue.push(child.dentryId);
|
|
1576
|
-
}
|
|
1577
|
-
}
|
|
1578
|
-
} catch (e) { /* skip */ }
|
|
1579
|
-
}
|
|
1580
|
-
|
|
1581
|
-
if (foundDentry) {
|
|
1582
|
-
steps.push({
|
|
1583
|
-
step: 'BFS 目录遍历找到文档',
|
|
1584
|
-
result: `spaceId=${foundSpaceId}, dentryId=${foundDentry.dentryId}, name=${foundDentry.name}`,
|
|
1585
|
-
raw: foundDentry
|
|
1586
|
-
});
|
|
1587
|
-
|
|
1588
|
-
// 尝试存储 API 获取 dentry 详情(含 docKey)
|
|
1589
|
-
try {
|
|
1590
|
-
const dentryRes = await axios({
|
|
1591
|
-
method: 'GET',
|
|
1592
|
-
url: `${DINGTALK_API_V2}/v2.0/storage/spaces/${foundSpaceId}/dentries/${foundDentry.dentryId}`,
|
|
1593
|
-
headers: { 'x-acs-dingtalk-access-token': token },
|
|
1594
|
-
params: { operatorId: opId }
|
|
1595
|
-
});
|
|
1596
|
-
steps.push({
|
|
1597
|
-
step: '存储 API dentry 详情',
|
|
1598
|
-
result: '成功',
|
|
1599
|
-
raw: dentryRes.data
|
|
1600
|
-
});
|
|
1601
|
-
} catch (e) {
|
|
1602
|
-
steps.push({
|
|
1603
|
-
step: '存储 API dentry 详情',
|
|
1604
|
-
result: '失败',
|
|
1605
|
-
raw: { message: e.response?.data?.message || e.message, code: e.response?.data?.code || '(无)', status: e.response?.status || '(无)' }
|
|
1606
|
-
});
|
|
1607
|
-
}
|
|
1608
|
-
|
|
1609
|
-
// 尝试存储 API 下载
|
|
1610
|
-
try {
|
|
1611
|
-
const dlRes = await axios({
|
|
1612
|
-
method: 'POST',
|
|
1613
|
-
url: `${DINGTALK_API_V2}/v2.0/storage/spaces/${foundSpaceId}/dentries/${foundDentry.dentryId}/downloadInfos/query`,
|
|
1614
|
-
headers: { 'x-acs-dingtalk-access-token': token, 'Content-Type': 'application/json' },
|
|
1615
|
-
params: { operatorId: opId },
|
|
1616
|
-
data: {}
|
|
1617
|
-
});
|
|
1618
|
-
steps.push({
|
|
1619
|
-
step: '存储 API 获取下载链接',
|
|
1620
|
-
result: '成功',
|
|
1621
|
-
raw: dlRes.data
|
|
1622
|
-
});
|
|
1623
|
-
// 尝试从下载链接获取内容
|
|
1624
|
-
const dlInfo = dlRes.data;
|
|
1625
|
-
const downloadUrl = dlInfo.headerSignatureInfo?.resourceUrls?.[0] || dlInfo.downloadUrl || dlInfo.url;
|
|
1626
|
-
if (downloadUrl) {
|
|
1627
|
-
try {
|
|
1628
|
-
const contentRes = await axios({ method: 'GET', url: downloadUrl, responseType: 'text' });
|
|
1629
|
-
steps.push({
|
|
1630
|
-
step: '存储 API 下载内容',
|
|
1631
|
-
result: `成功 (${contentRes.data.length} 字符)`,
|
|
1632
|
-
raw: contentRes.data.slice(0, 500) + (contentRes.data.length > 500 ? '\n... (截断)' : '')
|
|
1633
|
-
});
|
|
1634
|
-
} catch (dlErr) {
|
|
1635
|
-
steps.push({
|
|
1636
|
-
step: '存储 API 下载内容',
|
|
1637
|
-
result: '失败',
|
|
1638
|
-
raw: dlErr.message
|
|
1639
|
-
});
|
|
1640
|
-
}
|
|
1641
|
-
}
|
|
1642
|
-
} catch (e) {
|
|
1643
|
-
steps.push({
|
|
1644
|
-
step: '存储 API 获取下载链接',
|
|
1645
|
-
result: '失败',
|
|
1646
|
-
raw: { message: e.response?.data?.message || e.message, code: e.response?.data?.code || '(无)', status: e.response?.status || '(无)' }
|
|
1647
|
-
});
|
|
1648
|
-
}
|
|
1649
|
-
} else {
|
|
1650
|
-
steps.push({ step: 'BFS 目录遍历', result: '未找到匹配(文档可能在深层目录)' });
|
|
1651
|
-
}
|
|
1652
|
-
} catch (e) {
|
|
1653
|
-
steps.push({ step: 'BFS 目录遍历', result: '失败', raw: e.message });
|
|
1654
|
-
}
|
|
1655
|
-
}
|
|
1656
|
-
|
|
1657
|
-
// Step 10: 存储 API v1.0 - 文件下载(.md 文件内容 = 文件下载)
|
|
1658
|
-
if (foundSpaceId) {
|
|
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
|
-
}
|
|
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
|
-
});
|
|
1694
|
-
}
|
|
1695
|
-
}
|
|
1696
|
-
|
|
1697
|
-
|
|
1698
|
-
|
|
1699
|
-
const lines = ['🔍 docKey 诊断报告', '', `输入: ${input}`, `operatorId: ${opId}`, ''];
|
|
1700
|
-
steps.forEach(s => {
|
|
1701
|
-
lines.push(`--- ${s.step} ---`);
|
|
1702
|
-
lines.push(`结果: ${s.result}`);
|
|
1703
|
-
if (s.raw) {
|
|
1704
|
-
lines.push(`详情: ${JSON.stringify(s.raw, null, 2)}`);
|
|
1705
|
-
}
|
|
1706
|
-
lines.push('');
|
|
1707
|
-
});
|
|
1708
|
-
const isAdoc = foundDentry?.name?.endsWith('.adoc') || input.length > 20;
|
|
1709
|
-
lines.push('💡 诊断结论:');
|
|
1710
|
-
lines.push(` 输入 ID: ${input}`);
|
|
1711
|
-
lines.push(` 操作者: ${opId}`);
|
|
1712
|
-
lines.push('');
|
|
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 在线文档有效' : '❌ 同上错误'}`);
|
|
1721
|
-
lines.push(' 4️⃣ doc metadata → ❌ 404(该端点不存在)');
|
|
1722
|
-
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');
|
|
1732
|
-
|
|
1733
|
-
return { content: [{ type: 'text', text: lines.join('\n') }] };
|
|
1734
|
-
}
|
|
1735
|
-
|
|
1736
1310
|
case 'create_wiki_doc': {
|
|
1737
1311
|
const { workspace_id, parent_node_id, name, doc_type = 'DOC', operator_id } = args;
|
|
1738
1312
|
if (operator_id) {
|
|
@@ -2063,22 +1637,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
2063
1637
|
let realDocKey = await resolveDocKey(docKey, operator_id || null, dingtalk);
|
|
2064
1638
|
console.error(`[DEBUG] get_wiki_doc_content: input=${docKey}, resolved=${realDocKey}`);
|
|
2065
1639
|
|
|
2066
|
-
//
|
|
2067
|
-
if (realDocKey === docKey && workspaceId) {
|
|
2068
|
-
try {
|
|
2069
|
-
const searchRes = await dingtalk.docRequest('GET', `/v1.0/doc/docs`, {
|
|
2070
|
-
operatorId: operator_id || null,
|
|
2071
|
-
extraParams: { workspaceId, keyword: '', maxResults: 50 }
|
|
2072
|
-
});
|
|
2073
|
-
const docs = searchRes.docs || [];
|
|
2074
|
-
const matched = docs.find(d => d.nodeBO?.nodeId === docKey || d.nodeBO?.nodeId === realDocKey) || docs[0];
|
|
2075
|
-
if (matched?.nodeBO?.nodeId) {
|
|
2076
|
-
realDocKey = matched.nodeBO.nodeId;
|
|
2077
|
-
console.error(`[DEBUG] get_wiki_doc_content: search fallback resolved to ${realDocKey}`);
|
|
2078
|
-
}
|
|
2079
|
-
} catch (e) { console.error(`[DEBUG] get_wiki_doc_content: search fallback failed: ${e.message}`); }
|
|
2080
|
-
}
|
|
2081
|
-
|
|
1640
|
+
// 尝试 blocks API 读取 .adoc 在线文档内容
|
|
2082
1641
|
try {
|
|
2083
1642
|
const result = await dingtalk.docRequest('GET', `/v1.0/doc/suites/documents/${realDocKey}/blocks`, { operatorId: operator_id || null });
|
|
2084
1643
|
const blocks = result.result?.data || [];
|
|
@@ -2164,7 +1723,9 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
2164
1723
|
if (isDocKeyIllegal) {
|
|
2165
1724
|
hint = `\n\n原因: 此文档不是 .adoc 在线文档,blocks API 无法读取。`;
|
|
2166
1725
|
if (workspaceId) {
|
|
2167
|
-
hint += `\n已尝试存储 v1.0 下载 API
|
|
1726
|
+
hint += `\n已尝试存储 v1.0 下载 API 但仍失败(需开通 Storage.DownloadInfo.Read 权限)。`;
|
|
1727
|
+
} else {
|
|
1728
|
+
hint += `\n请提供 workspace_id 以尝试存储 v1.0 下载 API。`;
|
|
2168
1729
|
}
|
|
2169
1730
|
}
|
|
2170
1731
|
return {
|
|
@@ -2185,20 +1746,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
2185
1746
|
|
|
2186
1747
|
let realDocKey = await resolveDocKey(docKey, operator_id || null, dingtalk);
|
|
2187
1748
|
|
|
2188
|
-
|
|
2189
|
-
try {
|
|
2190
|
-
const searchRes = await dingtalk.docRequest('GET', `/v1.0/doc/docs`, {
|
|
2191
|
-
operatorId: operator_id || null,
|
|
2192
|
-
extraParams: { workspaceId, keyword: '', maxResults: 50 }
|
|
2193
|
-
});
|
|
2194
|
-
const docs = searchRes.docs || [];
|
|
2195
|
-
const matched = docs.find(d => d.nodeBO?.nodeId === docKey || d.nodeBO?.nodeId === realDocKey) || docs[0];
|
|
2196
|
-
if (matched?.nodeBO?.nodeId) {
|
|
2197
|
-
realDocKey = matched.nodeBO.nodeId;
|
|
2198
|
-
}
|
|
2199
|
-
} catch (e) { /* ignore */ }
|
|
2200
|
-
}
|
|
2201
|
-
|
|
1749
|
+
// 尝试 overwriteContent API 写入 .adoc 在线文档内容
|
|
2202
1750
|
try {
|
|
2203
1751
|
await dingtalk.docRequest('POST', `/v1.0/doc/suites/documents/${realDocKey}/overwriteContent`, {
|
|
2204
1752
|
operatorId: operator_id || null,
|
|
@@ -2208,7 +1756,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
2208
1756
|
const isDocKeyIllegal = e.message.includes('doc key is illegal');
|
|
2209
1757
|
let hint = '';
|
|
2210
1758
|
if (isDocKeyIllegal) {
|
|
2211
|
-
hint = `\n\n原因:
|
|
1759
|
+
hint = `\n\n原因: 此文档不是 .adoc 在线文档(可能是上传的 .md 文件),不支持修改。overwriteContent 仅对 .adoc 在线文档有效。`;
|
|
2212
1760
|
}
|
|
2213
1761
|
return {
|
|
2214
1762
|
content: [{ type: 'text', text: `❌ 写入文档内容失败\n${e.message}${hint}` }],
|