feishu-mcp 0.0.6 → 0.0.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.
@@ -0,0 +1,630 @@
1
+ import { BaseApiService } from './baseService.js';
2
+ import { Logger } from '../utils/logger.js';
3
+ import { Config } from '../utils/config.js';
4
+ import { CacheManager } from '../utils/cache.js';
5
+ import { ParamUtils } from '../utils/paramUtils.js';
6
+ import { BlockFactory, BlockType } from './blockFactory.js';
7
+ import axios from 'axios';
8
+ /**
9
+ * 飞书API服务类
10
+ * 提供飞书API的所有基础操作,包括认证、请求和缓存管理
11
+ */
12
+ export class FeishuApiService extends BaseApiService {
13
+ /**
14
+ * 私有构造函数,用于单例模式
15
+ */
16
+ constructor() {
17
+ super();
18
+ Object.defineProperty(this, "cacheManager", {
19
+ enumerable: true,
20
+ configurable: true,
21
+ writable: true,
22
+ value: void 0
23
+ });
24
+ Object.defineProperty(this, "blockFactory", {
25
+ enumerable: true,
26
+ configurable: true,
27
+ writable: true,
28
+ value: void 0
29
+ });
30
+ Object.defineProperty(this, "config", {
31
+ enumerable: true,
32
+ configurable: true,
33
+ writable: true,
34
+ value: void 0
35
+ });
36
+ this.cacheManager = CacheManager.getInstance();
37
+ this.blockFactory = BlockFactory.getInstance();
38
+ this.config = Config.getInstance();
39
+ }
40
+ /**
41
+ * 获取飞书API服务实例
42
+ * @returns 飞书API服务实例
43
+ */
44
+ static getInstance() {
45
+ if (!FeishuApiService.instance) {
46
+ FeishuApiService.instance = new FeishuApiService();
47
+ }
48
+ return FeishuApiService.instance;
49
+ }
50
+ /**
51
+ * 获取API基础URL
52
+ * @returns API基础URL
53
+ */
54
+ getBaseUrl() {
55
+ return this.config.feishu.baseUrl;
56
+ }
57
+ /**
58
+ * 获取API认证端点
59
+ * @returns 认证端点URL
60
+ */
61
+ getAuthEndpoint() {
62
+ return '/auth/v3/tenant_access_token/internal';
63
+ }
64
+ /**
65
+ * 获取访问令牌
66
+ * @returns 访问令牌
67
+ * @throws 如果获取令牌失败则抛出错误
68
+ */
69
+ async getAccessToken() {
70
+ // 尝试从缓存获取
71
+ const cachedToken = this.cacheManager.getToken();
72
+ if (cachedToken) {
73
+ Logger.debug('使用缓存的访问令牌');
74
+ return cachedToken;
75
+ }
76
+ try {
77
+ const requestData = {
78
+ app_id: this.config.feishu.appId,
79
+ app_secret: this.config.feishu.appSecret,
80
+ };
81
+ Logger.info('开始获取新的飞书访问令牌...');
82
+ Logger.debug('认证请求参数:', requestData);
83
+ // 不使用通用的request方法,因为这个请求不需要认证
84
+ // 为了确保正确处理响应,我们直接使用axios
85
+ const url = `${this.getBaseUrl()}${this.getAuthEndpoint()}`;
86
+ const headers = { 'Content-Type': 'application/json' };
87
+ Logger.debug(`发送认证请求到: ${url}`);
88
+ const response = await axios.post(url, requestData, { headers });
89
+ Logger.debug('认证响应:', response.data);
90
+ if (!response.data || typeof response.data !== 'object') {
91
+ throw new Error('获取飞书访问令牌失败:响应格式无效');
92
+ }
93
+ // 检查错误码
94
+ if (response.data.code !== 0) {
95
+ throw new Error(`获取飞书访问令牌失败:${response.data.msg || '未知错误'} (错误码: ${response.data.code})`);
96
+ }
97
+ if (!response.data.tenant_access_token) {
98
+ throw new Error('获取飞书访问令牌失败:响应中没有token');
99
+ }
100
+ this.accessToken = response.data.tenant_access_token;
101
+ this.tokenExpireTime = Date.now() + Math.min(response.data.expire * 1000, this.config.feishu.tokenLifetime);
102
+ // 缓存令牌
103
+ this.cacheManager.cacheToken(this.accessToken, response.data.expire);
104
+ Logger.info(`成功获取新的飞书访问令牌,有效期: ${response.data.expire} 秒`);
105
+ return this.accessToken;
106
+ }
107
+ catch (error) {
108
+ Logger.error('获取访问令牌失败:', error);
109
+ this.handleApiError(error, '获取飞书访问令牌失败');
110
+ }
111
+ }
112
+ /**
113
+ * 创建飞书文档
114
+ * @param title 文档标题
115
+ * @param folderToken 文件夹Token
116
+ * @returns 创建的文档信息
117
+ */
118
+ async createDocument(title, folderToken) {
119
+ try {
120
+ const endpoint = '/docx/v1/documents';
121
+ const payload = {
122
+ title,
123
+ folder_token: folderToken
124
+ };
125
+ const response = await this.post(endpoint, payload);
126
+ return response;
127
+ }
128
+ catch (error) {
129
+ this.handleApiError(error, '创建飞书文档失败');
130
+ }
131
+ }
132
+ /**
133
+ * 获取文档信息
134
+ * @param documentId 文档ID或URL
135
+ * @returns 文档信息
136
+ */
137
+ async getDocumentInfo(documentId) {
138
+ try {
139
+ const normalizedDocId = ParamUtils.processDocumentId(documentId);
140
+ const endpoint = `/docx/v1/documents/${normalizedDocId}`;
141
+ const response = await this.get(endpoint);
142
+ return response;
143
+ }
144
+ catch (error) {
145
+ this.handleApiError(error, '获取文档信息失败');
146
+ }
147
+ }
148
+ /**
149
+ * 获取文档内容
150
+ * @param documentId 文档ID或URL
151
+ * @param lang 语言代码,0为中文,1为英文
152
+ * @returns 文档内容
153
+ */
154
+ async getDocumentContent(documentId, lang = 0) {
155
+ try {
156
+ const normalizedDocId = ParamUtils.processDocumentId(documentId);
157
+ const endpoint = `/docx/v1/documents/${normalizedDocId}/raw_content`;
158
+ const params = { lang };
159
+ const response = await this.get(endpoint, params);
160
+ return response.content;
161
+ }
162
+ catch (error) {
163
+ this.handleApiError(error, '获取文档内容失败');
164
+ }
165
+ }
166
+ /**
167
+ * 获取文档块结构
168
+ * @param documentId 文档ID或URL
169
+ * @param pageSize 每页块数量
170
+ * @returns 文档块数组
171
+ */
172
+ async getDocumentBlocks(documentId, pageSize = 500) {
173
+ try {
174
+ const normalizedDocId = ParamUtils.processDocumentId(documentId);
175
+ const endpoint = `/docx/v1/documents/${normalizedDocId}/blocks`;
176
+ let pageToken = '';
177
+ let allBlocks = [];
178
+ // 分页获取所有块
179
+ do {
180
+ const params = {
181
+ page_size: pageSize,
182
+ document_revision_id: -1
183
+ };
184
+ if (pageToken) {
185
+ params.page_token = pageToken;
186
+ }
187
+ const response = await this.get(endpoint, params);
188
+ const blocks = response.items || [];
189
+ allBlocks = [...allBlocks, ...blocks];
190
+ pageToken = response.page_token;
191
+ } while (pageToken);
192
+ return allBlocks;
193
+ }
194
+ catch (error) {
195
+ this.handleApiError(error, '获取文档块结构失败');
196
+ }
197
+ }
198
+ /**
199
+ * 获取块内容
200
+ * @param documentId 文档ID或URL
201
+ * @param blockId 块ID
202
+ * @returns 块内容
203
+ */
204
+ async getBlockContent(documentId, blockId) {
205
+ try {
206
+ const normalizedDocId = ParamUtils.processDocumentId(documentId);
207
+ const safeBlockId = ParamUtils.processBlockId(blockId);
208
+ const endpoint = `/docx/v1/documents/${normalizedDocId}/blocks/${safeBlockId}`;
209
+ const params = { document_revision_id: -1 };
210
+ const response = await this.get(endpoint, params);
211
+ return response;
212
+ }
213
+ catch (error) {
214
+ this.handleApiError(error, '获取块内容失败');
215
+ }
216
+ }
217
+ /**
218
+ * 更新块文本内容
219
+ * @param documentId 文档ID或URL
220
+ * @param blockId 块ID
221
+ * @param textElements 文本元素数组
222
+ * @returns 更新结果
223
+ */
224
+ async updateBlockTextContent(documentId, blockId, textElements) {
225
+ try {
226
+ const docId = ParamUtils.processDocumentId(documentId);
227
+ const endpoint = `/docx/v1/documents/${docId}/blocks/${blockId}?document_revision_id=-1`;
228
+ Logger.debug(`准备请求API端点: ${endpoint}`);
229
+ const elements = textElements.map(item => ({
230
+ text_run: {
231
+ content: item.text,
232
+ text_element_style: item.style || {}
233
+ }
234
+ }));
235
+ const data = {
236
+ update_text_elements: {
237
+ elements: elements
238
+ }
239
+ };
240
+ Logger.debug(`请求数据: ${JSON.stringify(data, null, 2)}`);
241
+ const response = await this.patch(endpoint, data);
242
+ return response;
243
+ }
244
+ catch (error) {
245
+ this.handleApiError(error, '更新块文本内容失败');
246
+ return null; // 永远不会执行到这里
247
+ }
248
+ }
249
+ /**
250
+ * 创建文档块
251
+ * @param documentId 文档ID或URL
252
+ * @param parentBlockId 父块ID
253
+ * @param blockContent 块内容
254
+ * @param index 插入位置索引
255
+ * @returns 创建结果
256
+ */
257
+ async createDocumentBlock(documentId, parentBlockId, blockContent, index = 0) {
258
+ try {
259
+ const normalizedDocId = ParamUtils.processDocumentId(documentId);
260
+ const endpoint = `/docx/v1/documents/${normalizedDocId}/blocks/${parentBlockId}/children?document_revision_id=-1`;
261
+ Logger.debug(`准备请求API端点: ${endpoint}`);
262
+ const payload = {
263
+ children: [blockContent],
264
+ index
265
+ };
266
+ Logger.debug(`请求数据: ${JSON.stringify(payload, null, 2)}`);
267
+ const response = await this.post(endpoint, payload);
268
+ return response;
269
+ }
270
+ catch (error) {
271
+ this.handleApiError(error, '创建文档块失败');
272
+ return null; // 永远不会执行到这里
273
+ }
274
+ }
275
+ /**
276
+ * 批量创建文档块
277
+ * @param documentId 文档ID或URL
278
+ * @param parentBlockId 父块ID
279
+ * @param blockContents 块内容数组
280
+ * @param index 起始插入位置索引
281
+ * @returns 创建结果
282
+ */
283
+ async createDocumentBlocks(documentId, parentBlockId, blockContents, index = 0) {
284
+ try {
285
+ const normalizedDocId = ParamUtils.processDocumentId(documentId);
286
+ const endpoint = `/docx/v1/documents/${normalizedDocId}/blocks/${parentBlockId}/children?document_revision_id=-1`;
287
+ Logger.debug(`准备请求API端点: ${endpoint}`);
288
+ const payload = {
289
+ children: blockContents,
290
+ index
291
+ };
292
+ Logger.debug(`请求数据: ${JSON.stringify(payload, null, 2)}`);
293
+ const response = await this.post(endpoint, payload);
294
+ return response;
295
+ }
296
+ catch (error) {
297
+ this.handleApiError(error, '批量创建文档块失败');
298
+ return null; // 永远不会执行到这里
299
+ }
300
+ }
301
+ /**
302
+ * 创建文本块
303
+ * @param documentId 文档ID或URL
304
+ * @param parentBlockId 父块ID
305
+ * @param textContents 文本内容数组
306
+ * @param align 对齐方式,1为左对齐,2为居中,3为右对齐
307
+ * @param index 插入位置索引
308
+ * @returns 创建结果
309
+ */
310
+ async createTextBlock(documentId, parentBlockId, textContents, align = 1, index = 0) {
311
+ const blockContent = this.blockFactory.createTextBlock({
312
+ textContents,
313
+ align
314
+ });
315
+ return this.createDocumentBlock(documentId, parentBlockId, blockContent, index);
316
+ }
317
+ /**
318
+ * 创建代码块
319
+ * @param documentId 文档ID或URL
320
+ * @param parentBlockId 父块ID
321
+ * @param code 代码内容
322
+ * @param language 语言代码
323
+ * @param wrap 是否自动换行
324
+ * @param index 插入位置索引
325
+ * @returns 创建结果
326
+ */
327
+ async createCodeBlock(documentId, parentBlockId, code, language = 0, wrap = false, index = 0) {
328
+ const blockContent = this.blockFactory.createCodeBlock({
329
+ code,
330
+ language,
331
+ wrap
332
+ });
333
+ return this.createDocumentBlock(documentId, parentBlockId, blockContent, index);
334
+ }
335
+ /**
336
+ * 创建标题块
337
+ * @param documentId 文档ID或URL
338
+ * @param parentBlockId 父块ID
339
+ * @param text 标题文本
340
+ * @param level 标题级别,1-9
341
+ * @param index 插入位置索引
342
+ * @param align 对齐方式,1为左对齐,2为居中,3为右对齐
343
+ * @returns 创建结果
344
+ */
345
+ async createHeadingBlock(documentId, parentBlockId, text, level = 1, index = 0, align = 1) {
346
+ const blockContent = this.blockFactory.createHeadingBlock({
347
+ text,
348
+ level,
349
+ align
350
+ });
351
+ return this.createDocumentBlock(documentId, parentBlockId, blockContent, index);
352
+ }
353
+ /**
354
+ * 创建列表块
355
+ * @param documentId 文档ID或URL
356
+ * @param parentBlockId 父块ID
357
+ * @param text 列表项文本
358
+ * @param isOrdered 是否是有序列表
359
+ * @param index 插入位置索引
360
+ * @param align 对齐方式,1为左对齐,2为居中,3为右对齐
361
+ * @returns 创建结果
362
+ */
363
+ async createListBlock(documentId, parentBlockId, text, isOrdered = false, index = 0, align = 1) {
364
+ const blockContent = this.blockFactory.createListBlock({
365
+ text,
366
+ isOrdered,
367
+ align
368
+ });
369
+ return this.createDocumentBlock(documentId, parentBlockId, blockContent, index);
370
+ }
371
+ /**
372
+ * 创建混合块
373
+ * @param documentId 文档ID或URL
374
+ * @param parentBlockId 父块ID
375
+ * @param blocks 块配置数组
376
+ * @param index 插入位置索引
377
+ * @returns 创建结果
378
+ */
379
+ async createMixedBlocks(documentId, parentBlockId, blocks, index = 0) {
380
+ const blockContents = blocks.map(block => this.blockFactory.createBlock(block.type, block.options));
381
+ return this.createDocumentBlocks(documentId, parentBlockId, blockContents, index);
382
+ }
383
+ /**
384
+ * 删除文档中的块,支持批量删除
385
+ * @param documentId 文档ID或URL
386
+ * @param parentBlockId 父块ID(通常是文档ID)
387
+ * @param startIndex 起始索引
388
+ * @param endIndex 结束索引
389
+ * @returns 操作结果
390
+ */
391
+ async deleteDocumentBlocks(documentId, parentBlockId, startIndex, endIndex) {
392
+ try {
393
+ const normalizedDocId = ParamUtils.processDocumentId(documentId);
394
+ const endpoint = `/docx/v1/documents/${normalizedDocId}/blocks/${parentBlockId}/children/batch_delete`;
395
+ // 确保索引有效
396
+ if (startIndex < 0 || endIndex < startIndex) {
397
+ throw new Error('无效的索引范围:起始索引必须大于等于0,结束索引必须大于等于起始索引');
398
+ }
399
+ const payload = {
400
+ start_index: startIndex,
401
+ end_index: endIndex
402
+ };
403
+ Logger.info(`开始删除文档块,文档ID: ${normalizedDocId},父块ID: ${parentBlockId},索引范围: ${startIndex}-${endIndex}`);
404
+ const response = await this.delete(endpoint, payload);
405
+ Logger.info('文档块删除成功');
406
+ return response;
407
+ }
408
+ catch (error) {
409
+ this.handleApiError(error, '删除文档块失败');
410
+ }
411
+ }
412
+ /**
413
+ * 删除单个文档块(通过创建起始和结束索引相同的批量删除请求)
414
+ * @param documentId 文档ID或URL
415
+ * @param parentBlockId 父块ID
416
+ * @param blockIndex 块索引
417
+ * @returns 操作结果
418
+ */
419
+ async deleteDocumentBlock(documentId, parentBlockId, blockIndex) {
420
+ return this.deleteDocumentBlocks(documentId, parentBlockId, blockIndex, blockIndex + 1);
421
+ }
422
+ /**
423
+ * 将飞书Wiki链接转换为文档ID
424
+ * @param wikiUrl Wiki链接或Token
425
+ * @returns 文档ID
426
+ */
427
+ async convertWikiToDocumentId(wikiUrl) {
428
+ try {
429
+ const wikiToken = ParamUtils.processWikiToken(wikiUrl);
430
+ // 尝试从缓存获取
431
+ const cachedDocId = this.cacheManager.getWikiToDocId(wikiToken);
432
+ if (cachedDocId) {
433
+ Logger.debug(`使用缓存的Wiki转换结果: ${wikiToken} -> ${cachedDocId}`);
434
+ return cachedDocId;
435
+ }
436
+ // 获取Wiki节点信息
437
+ const endpoint = `/wiki/v2/spaces/get_node`;
438
+ const params = { token: wikiToken, obj_type: 'wiki' };
439
+ const response = await this.get(endpoint, params);
440
+ if (!response.node || !response.node.obj_token) {
441
+ throw new Error(`无法从Wiki节点获取文档ID: ${wikiToken}`);
442
+ }
443
+ const documentId = response.node.obj_token;
444
+ // 缓存结果
445
+ this.cacheManager.cacheWikiToDocId(wikiToken, documentId);
446
+ Logger.debug(`Wiki转换为文档ID: ${wikiToken} -> ${documentId}`);
447
+ return documentId;
448
+ }
449
+ catch (error) {
450
+ this.handleApiError(error, 'Wiki转换为文档ID失败');
451
+ return ''; // 永远不会执行到这里
452
+ }
453
+ }
454
+ /**
455
+ * 获取BlockFactory实例
456
+ * @returns BlockFactory实例
457
+ */
458
+ getBlockFactory() {
459
+ return this.blockFactory;
460
+ }
461
+ /**
462
+ * 创建块内容对象
463
+ * @param blockType 块类型
464
+ * @param options 块选项
465
+ * @returns 块内容对象
466
+ */
467
+ createBlockContent(blockType, options) {
468
+ try {
469
+ // 使用枚举类型来避免字符串错误
470
+ const blockTypeEnum = blockType;
471
+ // 构建块配置
472
+ let blockConfig = {
473
+ type: blockTypeEnum,
474
+ options: {}
475
+ };
476
+ switch (blockTypeEnum) {
477
+ case BlockType.TEXT:
478
+ if ('text' in options && options.text) {
479
+ const textOptions = options.text;
480
+ blockConfig.options = {
481
+ textContents: textOptions.textStyles || [],
482
+ align: textOptions.align || 1
483
+ };
484
+ }
485
+ break;
486
+ case BlockType.CODE:
487
+ if ('code' in options && options.code) {
488
+ const codeOptions = options.code;
489
+ blockConfig.options = {
490
+ code: codeOptions.code || '',
491
+ language: codeOptions.language === 0 ? 0 : (codeOptions.language || 0),
492
+ wrap: codeOptions.wrap || false
493
+ };
494
+ }
495
+ break;
496
+ case BlockType.HEADING:
497
+ if ('heading' in options && options.heading) {
498
+ const headingOptions = options.heading;
499
+ blockConfig.options = {
500
+ text: headingOptions.content || '',
501
+ level: headingOptions.level || 1,
502
+ align: (headingOptions.align === 1 || headingOptions.align === 2 || headingOptions.align === 3)
503
+ ? headingOptions.align : 1
504
+ };
505
+ }
506
+ break;
507
+ case BlockType.LIST:
508
+ if ('list' in options && options.list) {
509
+ const listOptions = options.list;
510
+ blockConfig.options = {
511
+ text: listOptions.content || '',
512
+ isOrdered: listOptions.isOrdered || false,
513
+ align: (listOptions.align === 1 || listOptions.align === 2 || listOptions.align === 3)
514
+ ? listOptions.align : 1
515
+ };
516
+ }
517
+ break;
518
+ }
519
+ // 使用BlockFactory创建块
520
+ return this.blockFactory.createBlock(blockConfig.type, blockConfig.options);
521
+ }
522
+ catch (error) {
523
+ Logger.error(`创建块内容对象失败: ${error}`);
524
+ return null;
525
+ }
526
+ }
527
+ /**
528
+ * 获取飞书图片资源
529
+ * @param mediaId 图片媒体ID
530
+ * @param extra 额外参数,可选
531
+ * @returns 图片二进制数据
532
+ */
533
+ async getImageResource(mediaId, extra = '') {
534
+ try {
535
+ Logger.info(`开始获取图片资源,媒体ID: ${mediaId}`);
536
+ if (!mediaId) {
537
+ throw new Error('媒体ID不能为空');
538
+ }
539
+ const endpoint = `/drive/v1/medias/${mediaId}/download`;
540
+ const params = {};
541
+ if (extra) {
542
+ params.extra = extra;
543
+ }
544
+ // 这里需要特殊处理,因为返回的是二进制数据,不是JSON
545
+ const token = await this.getAccessToken();
546
+ const url = `${this.getBaseUrl()}${endpoint}`;
547
+ const headers = {
548
+ 'Authorization': `Bearer ${token}`
549
+ };
550
+ Logger.debug(`请求图片资源URL: ${url}`);
551
+ // 使用axios直接获取二进制响应
552
+ const response = await axios.get(url, {
553
+ params,
554
+ headers,
555
+ responseType: 'arraybuffer'
556
+ });
557
+ // 检查响应状态
558
+ if (response.status !== 200) {
559
+ throw new Error(`获取图片资源失败,状态码: ${response.status}`);
560
+ }
561
+ const imageBuffer = Buffer.from(response.data);
562
+ Logger.info(`图片资源获取成功,大小: ${imageBuffer.length} 字节`);
563
+ return imageBuffer;
564
+ }
565
+ catch (error) {
566
+ this.handleApiError(error, '获取图片资源失败');
567
+ return Buffer.from([]); // 永远不会执行到这里
568
+ }
569
+ }
570
+ /**
571
+ * 获取飞书根文件夹信息
572
+ * 获取用户的根文件夹的元数据信息,包括token、id和用户id
573
+ * @returns 根文件夹信息
574
+ */
575
+ async getRootFolderInfo() {
576
+ try {
577
+ const endpoint = '/drive/explorer/v2/root_folder/meta';
578
+ const response = await this.get(endpoint);
579
+ Logger.debug('获取根文件夹信息成功:', response);
580
+ return response;
581
+ }
582
+ catch (error) {
583
+ this.handleApiError(error, '获取飞书根文件夹信息失败');
584
+ }
585
+ }
586
+ /**
587
+ * 获取文件夹中的文件清单
588
+ * @param folderToken 文件夹Token
589
+ * @param orderBy 排序方式,默认按修改时间排序
590
+ * @param direction 排序方向,默认降序
591
+ * @returns 文件清单信息
592
+ */
593
+ async getFolderFileList(folderToken, orderBy = 'EditedTime', direction = 'DESC') {
594
+ try {
595
+ const endpoint = '/drive/v1/files';
596
+ const params = {
597
+ folder_token: folderToken,
598
+ order_by: orderBy,
599
+ direction: direction
600
+ };
601
+ const response = await this.get(endpoint, params);
602
+ Logger.debug(`获取文件夹(${folderToken})中的文件清单成功,文件数量: ${response.files?.length || 0}`);
603
+ return response;
604
+ }
605
+ catch (error) {
606
+ this.handleApiError(error, '获取文件夹中的文件清单失败');
607
+ }
608
+ }
609
+ /**
610
+ * 创建文件夹
611
+ * @param folderToken 父文件夹Token
612
+ * @param name 文件夹名称
613
+ * @returns 创建的文件夹信息
614
+ */
615
+ async createFolder(folderToken, name) {
616
+ try {
617
+ const endpoint = '/drive/v1/files/create_folder';
618
+ const payload = {
619
+ folder_token: folderToken,
620
+ name: name
621
+ };
622
+ const response = await this.post(endpoint, payload);
623
+ Logger.debug(`文件夹创建成功, token: ${response.token}, url: ${response.url}`);
624
+ return response;
625
+ }
626
+ catch (error) {
627
+ this.handleApiError(error, '创建文件夹失败');
628
+ }
629
+ }
630
+ }