dingtalk-wiki 1.1.5 → 1.1.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 +88 -34
- package/package.json +1 -1
package/index.js
CHANGED
|
@@ -19,7 +19,7 @@ const DINGTALK_API_V2 = 'https://api.dingtalk.com';
|
|
|
19
19
|
const fs = require('fs');
|
|
20
20
|
const path = require('path');
|
|
21
21
|
const os = require('os');
|
|
22
|
-
const CACHE_DIR = path.join(os.homedir(), '.cache', 'dingtalk-wiki
|
|
22
|
+
const CACHE_DIR = path.join(os.homedir(), '.cache', 'dingtalk-wiki');
|
|
23
23
|
const UNIONID_CACHE_PATH = path.join(CACHE_DIR, 'unionid-cache.json');
|
|
24
24
|
|
|
25
25
|
if (!fs.existsSync(CACHE_DIR)) {
|
|
@@ -286,7 +286,7 @@ class DingTalkClient {
|
|
|
286
286
|
return this.operatorId;
|
|
287
287
|
}
|
|
288
288
|
|
|
289
|
-
async docRequest(method, pathName, { operatorId = null, data = null } = {}) {
|
|
289
|
+
async docRequest(method, pathName, { operatorId = null, data = null, extraParams = {} } = {}) {
|
|
290
290
|
const token = await this.getAccessToken();
|
|
291
291
|
const resolvedOperatorId = await this.resolveOperatorId(operatorId);
|
|
292
292
|
const url = `${DINGTALK_API_V2}${pathName}`;
|
|
@@ -300,7 +300,8 @@ class DingTalkClient {
|
|
|
300
300
|
'Content-Type': 'application/json'
|
|
301
301
|
},
|
|
302
302
|
params: {
|
|
303
|
-
operatorId: resolvedOperatorId
|
|
303
|
+
operatorId: resolvedOperatorId,
|
|
304
|
+
...extraParams
|
|
304
305
|
},
|
|
305
306
|
data
|
|
306
307
|
});
|
|
@@ -502,7 +503,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
|
502
503
|
},
|
|
503
504
|
{
|
|
504
505
|
name: 'search_wiki',
|
|
505
|
-
description: '
|
|
506
|
+
description: '搜索知识库中的文档和文件夹(遍历目录树按名称匹配)',
|
|
506
507
|
inputSchema: {
|
|
507
508
|
type: 'object',
|
|
508
509
|
properties: {
|
|
@@ -512,15 +513,11 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
|
512
513
|
},
|
|
513
514
|
workspace_id: {
|
|
514
515
|
type: 'string',
|
|
515
|
-
description: '指定知识库 ID
|
|
516
|
+
description: '指定知识库 ID(可选,不传则搜索所有知识库)'
|
|
516
517
|
},
|
|
517
518
|
max_results: {
|
|
518
519
|
type: 'number',
|
|
519
|
-
description: '返回条数上限(默认
|
|
520
|
-
},
|
|
521
|
-
next_token: {
|
|
522
|
-
type: 'string',
|
|
523
|
-
description: '分页游标(上次返回的 nextToken)'
|
|
520
|
+
description: '返回条数上限(默认 20,最大 50)'
|
|
524
521
|
},
|
|
525
522
|
operator_id: {
|
|
526
523
|
type: 'string',
|
|
@@ -1120,37 +1117,94 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
1120
1117
|
}
|
|
1121
1118
|
|
|
1122
1119
|
case 'search_wiki': {
|
|
1123
|
-
const { keyword, workspace_id, max_results =
|
|
1120
|
+
const { keyword, workspace_id, max_results = 20, operator_id } = args;
|
|
1121
|
+
if (!keyword) {
|
|
1122
|
+
return {
|
|
1123
|
+
content: [{ type: 'text', text: '⚠️ 请提供搜索关键词 keyword' }],
|
|
1124
|
+
isError: true
|
|
1125
|
+
};
|
|
1126
|
+
}
|
|
1127
|
+
|
|
1128
|
+
const MAX_RESULTS = Math.min(max_results, 50);
|
|
1124
1129
|
if (operator_id) {
|
|
1125
1130
|
dingtalk.setOperatorId(operator_id);
|
|
1126
1131
|
}
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
};
|
|
1131
|
-
if (next_token) {
|
|
1132
|
-
body.nextToken = next_token;
|
|
1133
|
-
}
|
|
1132
|
+
|
|
1133
|
+
const wsResult = await dingtalk.wikiRequest('workspaces');
|
|
1134
|
+
let workspaces = wsResult.workspaces || [];
|
|
1134
1135
|
if (workspace_id) {
|
|
1135
|
-
|
|
1136
|
+
workspaces = workspaces.filter(ws => ws.workspaceId === workspace_id);
|
|
1136
1137
|
}
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
items.forEach((item, i) => {
|
|
1144
|
-
output += `${i + 1}. ${item.name}\n`;
|
|
1145
|
-
output += ` 知识库: ${item.workspaceId}\n`;
|
|
1146
|
-
output += ` 链接: ${item.url}\n\n`;
|
|
1147
|
-
});
|
|
1148
|
-
if (!items.length) {
|
|
1149
|
-
output += '没有找到匹配的知识库。\n';
|
|
1138
|
+
|
|
1139
|
+
if (workspaces.length === 0) {
|
|
1140
|
+
return {
|
|
1141
|
+
content: [{ type: 'text', text: '⚠️ 未找到可搜索的知识库' }],
|
|
1142
|
+
isError: true
|
|
1143
|
+
};
|
|
1150
1144
|
}
|
|
1151
|
-
|
|
1152
|
-
|
|
1145
|
+
|
|
1146
|
+
const matchedNodes = [];
|
|
1147
|
+
|
|
1148
|
+
for (const ws of workspaces) {
|
|
1149
|
+
if (matchedNodes.length >= MAX_RESULTS) break;
|
|
1150
|
+
|
|
1151
|
+
const queue = [ws.rootNodeId];
|
|
1152
|
+
const visited = new Set();
|
|
1153
|
+
|
|
1154
|
+
while (queue.length > 0 && matchedNodes.length < MAX_RESULTS) {
|
|
1155
|
+
const dentryId = queue.shift();
|
|
1156
|
+
if (!dentryId || visited.has(dentryId)) continue;
|
|
1157
|
+
visited.add(dentryId);
|
|
1158
|
+
|
|
1159
|
+
try {
|
|
1160
|
+
const result = await dingtalk.docRequest('GET', `/v2.0/doc/spaces/${ws.workspaceId}/directories`, {
|
|
1161
|
+
operatorId: operator_id || null,
|
|
1162
|
+
extraParams: dentryId !== ws.rootNodeId ? { dentryId, maxResults: 500 } : { maxResults: 500 }
|
|
1163
|
+
});
|
|
1164
|
+
|
|
1165
|
+
const children = result.children || [];
|
|
1166
|
+
for (const child of children) {
|
|
1167
|
+
const name = child.name || '';
|
|
1168
|
+
if (name.includes(keyword)) {
|
|
1169
|
+
matchedNodes.push({
|
|
1170
|
+
name,
|
|
1171
|
+
nodeId: child.dentryId || child.nodeId || child.id,
|
|
1172
|
+
workspaceId: ws.workspaceId,
|
|
1173
|
+
workspaceName: ws.name,
|
|
1174
|
+
type: child.contentType === 'folder' ? '文件夹' : '文档',
|
|
1175
|
+
url: child.url || '',
|
|
1176
|
+
hasChildren: child.hasChildren
|
|
1177
|
+
});
|
|
1178
|
+
}
|
|
1179
|
+
if (child.hasChildren && matchedNodes.length < MAX_RESULTS) {
|
|
1180
|
+
const childId = child.dentryId || child.nodeId || child.id;
|
|
1181
|
+
if (childId && !visited.has(childId)) {
|
|
1182
|
+
queue.push(childId);
|
|
1183
|
+
}
|
|
1184
|
+
}
|
|
1185
|
+
}
|
|
1186
|
+
} catch (e) {
|
|
1187
|
+
// 单个节点遍历失败跳过,不中断整体搜索
|
|
1188
|
+
}
|
|
1189
|
+
}
|
|
1153
1190
|
}
|
|
1191
|
+
|
|
1192
|
+
let output = `🔍 搜索 "${keyword}" (${matchedNodes.length}条)\n\n`;
|
|
1193
|
+
matchedNodes.slice(0, MAX_RESULTS).forEach((item, i) => {
|
|
1194
|
+
const icon = item.type === '文件夹' ? '📁' : '📄';
|
|
1195
|
+
output += `${i + 1}. ${icon} ${item.name}\n`;
|
|
1196
|
+
output += ` 知识库: ${item.workspaceName} (${item.workspaceId})\n`;
|
|
1197
|
+
if (item.url) output += ` 链接: ${item.url}\n`;
|
|
1198
|
+
output += '\n';
|
|
1199
|
+
});
|
|
1200
|
+
|
|
1201
|
+
if (matchedNodes.length === 0) {
|
|
1202
|
+
output += '没有找到匹配的文档或文件夹。\n';
|
|
1203
|
+
output += `\n💡 此方式通过遍历目录树按名称匹配,非全文搜索。`;
|
|
1204
|
+
output += `如需全文搜索,请在钉钉客户端中操作:\n`;
|
|
1205
|
+
output += `https://alidocs.dingtalk.com/i/search?keyword=${encodeURIComponent(keyword)}`;
|
|
1206
|
+
}
|
|
1207
|
+
|
|
1154
1208
|
return {
|
|
1155
1209
|
content: [{ type: 'text', text: output }]
|
|
1156
1210
|
};
|