feishu-user-plugin 1.3.0 → 1.3.2
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/.claude-plugin/plugin.json +2 -2
- package/README.md +103 -39
- package/package.json +5 -3
- package/proto/lark.proto +27 -0
- package/scripts/confirm-version.js +28 -0
- package/scripts/mcp_stdio_bridge.js +97 -0
- package/skills/feishu-user-plugin/references/CLAUDE.md +105 -41
- package/src/cli.js +12 -7
- package/src/client.js +81 -10
- package/src/config.js +202 -27
- package/src/index.js +128 -246
- package/src/oauth.js +2 -1
- package/src/official.js +274 -209
- package/src/setup.js +19 -3
package/src/official.js
CHANGED
|
@@ -1,10 +1,21 @@
|
|
|
1
1
|
const lark = require('@larksuiteoapi/node-sdk');
|
|
2
2
|
|
|
3
|
+
// Redirect all Lark SDK logs to stderr.
|
|
4
|
+
// The SDK's defaultLogger.error uses console.log (stdout), which corrupts
|
|
5
|
+
// MCP's JSON-RPC stdio transport and causes session disconnects.
|
|
6
|
+
const stderrLogger = {
|
|
7
|
+
error: (...msg) => console.error('[lark-sdk][error]:', ...msg),
|
|
8
|
+
warn: (...msg) => console.error('[lark-sdk][warn]:', ...msg),
|
|
9
|
+
info: () => {},
|
|
10
|
+
debug: () => {},
|
|
11
|
+
trace: () => {},
|
|
12
|
+
};
|
|
13
|
+
|
|
3
14
|
class LarkOfficialClient {
|
|
4
15
|
constructor(appId, appSecret) {
|
|
5
16
|
this.appId = appId;
|
|
6
17
|
this.appSecret = appSecret;
|
|
7
|
-
this.client = new lark.Client({ appId, appSecret, disableTokenCache: false });
|
|
18
|
+
this.client = new lark.Client({ appId, appSecret, disableTokenCache: false, logger: stderrLogger, loggerLevel: lark.LoggerLevel.warn });
|
|
8
19
|
this._uat = null;
|
|
9
20
|
this._uatRefresh = null;
|
|
10
21
|
this._uatExpires = 0;
|
|
@@ -90,6 +101,37 @@ class LarkOfficialClient {
|
|
|
90
101
|
return data;
|
|
91
102
|
}
|
|
92
103
|
|
|
104
|
+
// Generic UAT REST helper. Returns parsed JSON ({code, msg, data}).
|
|
105
|
+
async _uatREST(method, path, { body, query } = {}) {
|
|
106
|
+
const qs = query ? '?' + new URLSearchParams(query).toString() : '';
|
|
107
|
+
const url = 'https://open.feishu.cn' + path + qs;
|
|
108
|
+
return this._withUAT(async (uat) => {
|
|
109
|
+
const headers = { 'Authorization': `Bearer ${uat}` };
|
|
110
|
+
const init = { method, headers };
|
|
111
|
+
if (body !== undefined) {
|
|
112
|
+
headers['content-type'] = 'application/json';
|
|
113
|
+
init.body = JSON.stringify(body);
|
|
114
|
+
}
|
|
115
|
+
const res = await fetch(url, init);
|
|
116
|
+
return res.json();
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// Try UAT first (for resources likely owned by the user), fall back to app SDK on failure.
|
|
121
|
+
// Returns SDK-shaped {code, msg, data}. Both paths yield the same shape.
|
|
122
|
+
async _asUserOrApp({ uatPath, method = 'GET', body, query, sdkFn, label }) {
|
|
123
|
+
if (this.hasUAT) {
|
|
124
|
+
try {
|
|
125
|
+
const data = await this._uatREST(method, uatPath, { body, query });
|
|
126
|
+
if (data.code === 0) return data;
|
|
127
|
+
console.error(`[feishu-user-plugin] ${label} as user failed (${data.code}: ${data.msg}), retrying as app`);
|
|
128
|
+
} catch (err) {
|
|
129
|
+
console.error(`[feishu-user-plugin] ${label} as user threw (${err.message}), retrying as app`);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
return this._safeSDKCall(sdkFn, label);
|
|
133
|
+
}
|
|
134
|
+
|
|
93
135
|
async listChatsAsUser({ pageSize = 20, pageToken } = {}) {
|
|
94
136
|
const params = new URLSearchParams({ page_size: String(pageSize) });
|
|
95
137
|
if (pageToken) params.set('page_token', pageToken);
|
|
@@ -234,20 +276,19 @@ class LarkOfficialClient {
|
|
|
234
276
|
|
|
235
277
|
// --- IM: Pins ---
|
|
236
278
|
|
|
237
|
-
async pinMessage(messageId) {
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
async unpinMessage(messageId) {
|
|
279
|
+
async pinMessage(messageId, pinned = true) {
|
|
280
|
+
if (pinned) {
|
|
281
|
+
const res = await this._safeSDKCall(
|
|
282
|
+
() => this.client.im.pin.create({ data: { message_id: messageId } }),
|
|
283
|
+
'pinMessage'
|
|
284
|
+
);
|
|
285
|
+
return { pin: res.data.pin };
|
|
286
|
+
}
|
|
246
287
|
await this._safeSDKCall(
|
|
247
288
|
() => this.client.im.pin.delete({ data: { message_id: messageId } }),
|
|
248
289
|
'unpinMessage'
|
|
249
290
|
);
|
|
250
|
-
return {
|
|
291
|
+
return { unpinned: true };
|
|
251
292
|
}
|
|
252
293
|
|
|
253
294
|
// --- IM: Chat Management ---
|
|
@@ -361,61 +402,77 @@ class LarkOfficialClient {
|
|
|
361
402
|
}
|
|
362
403
|
|
|
363
404
|
async readDoc(documentId) {
|
|
364
|
-
const res = await this.
|
|
365
|
-
|
|
366
|
-
'
|
|
367
|
-
|
|
405
|
+
const res = await this._asUserOrApp({
|
|
406
|
+
uatPath: `/open-apis/docx/v1/documents/${documentId}/raw_content`,
|
|
407
|
+
query: { lang: '0' },
|
|
408
|
+
sdkFn: () => this.client.docx.document.rawContent({ path: { document_id: documentId }, params: { lang: 0 } }),
|
|
409
|
+
label: 'readDoc',
|
|
410
|
+
});
|
|
368
411
|
return { content: res.data.content };
|
|
369
412
|
}
|
|
370
413
|
|
|
371
414
|
async createDoc(title, folderId) {
|
|
372
|
-
const res = await this.
|
|
373
|
-
|
|
374
|
-
'
|
|
375
|
-
|
|
415
|
+
const res = await this._asUserOrApp({
|
|
416
|
+
uatPath: `/open-apis/docx/v1/documents`,
|
|
417
|
+
method: 'POST',
|
|
418
|
+
body: { title, folder_token: folderId || '' },
|
|
419
|
+
sdkFn: () => this.client.docx.document.create({ data: { title, folder_token: folderId || '' } }),
|
|
420
|
+
label: 'createDoc',
|
|
421
|
+
});
|
|
376
422
|
return { documentId: res.data.document?.document_id };
|
|
377
423
|
}
|
|
378
424
|
|
|
379
425
|
async getDocBlocks(documentId) {
|
|
380
|
-
const res = await this.
|
|
381
|
-
|
|
382
|
-
'
|
|
383
|
-
|
|
426
|
+
const res = await this._asUserOrApp({
|
|
427
|
+
uatPath: `/open-apis/docx/v1/documents/${documentId}/blocks`,
|
|
428
|
+
query: { page_size: '500' },
|
|
429
|
+
sdkFn: () => this.client.docx.documentBlock.list({ path: { document_id: documentId }, params: { page_size: 500 } }),
|
|
430
|
+
label: 'getDocBlocks',
|
|
431
|
+
});
|
|
384
432
|
return { items: res.data.items || [] };
|
|
385
433
|
}
|
|
386
434
|
|
|
387
435
|
async createDocBlock(documentId, parentBlockId, children, index) {
|
|
388
436
|
const data = { children };
|
|
389
437
|
if (index !== undefined) data.index = index;
|
|
390
|
-
const res = await this.
|
|
391
|
-
|
|
438
|
+
const res = await this._asUserOrApp({
|
|
439
|
+
uatPath: `/open-apis/docx/v1/documents/${documentId}/blocks/${parentBlockId}/children`,
|
|
440
|
+
method: 'POST',
|
|
441
|
+
body: data,
|
|
442
|
+
sdkFn: () => this.client.docx.documentBlockChildren.create({
|
|
392
443
|
path: { document_id: documentId, block_id: parentBlockId },
|
|
393
444
|
data,
|
|
394
445
|
}),
|
|
395
|
-
'createDocBlock'
|
|
396
|
-
);
|
|
446
|
+
label: 'createDocBlock',
|
|
447
|
+
});
|
|
397
448
|
return { blocks: res.data.children || [] };
|
|
398
449
|
}
|
|
399
450
|
|
|
400
451
|
async updateDocBlock(documentId, blockId, updateBody) {
|
|
401
|
-
const res = await this.
|
|
402
|
-
|
|
452
|
+
const res = await this._asUserOrApp({
|
|
453
|
+
uatPath: `/open-apis/docx/v1/documents/${documentId}/blocks/${blockId}`,
|
|
454
|
+
method: 'PATCH',
|
|
455
|
+
body: updateBody,
|
|
456
|
+
sdkFn: () => this.client.docx.documentBlock.patch({
|
|
403
457
|
path: { document_id: documentId, block_id: blockId },
|
|
404
458
|
data: updateBody,
|
|
405
459
|
}),
|
|
406
|
-
'updateDocBlock'
|
|
407
|
-
);
|
|
460
|
+
label: 'updateDocBlock',
|
|
461
|
+
});
|
|
408
462
|
return { block: res.data.block };
|
|
409
463
|
}
|
|
410
464
|
|
|
411
465
|
async deleteDocBlocks(documentId, parentBlockId, startIndex, endIndex) {
|
|
412
|
-
|
|
413
|
-
|
|
466
|
+
await this._asUserOrApp({
|
|
467
|
+
uatPath: `/open-apis/docx/v1/documents/${documentId}/blocks/${parentBlockId}/children/batch_delete`,
|
|
468
|
+
method: 'DELETE',
|
|
469
|
+
body: { start_index: startIndex, end_index: endIndex },
|
|
470
|
+
sdkFn: () => this.client.docx.documentBlockChildren.batchDelete({
|
|
414
471
|
path: { document_id: documentId, block_id: parentBlockId },
|
|
415
472
|
data: { start_index: startIndex, end_index: endIndex },
|
|
416
473
|
}),
|
|
417
|
-
'deleteDocBlocks'
|
|
418
|
-
);
|
|
474
|
+
label: 'deleteDocBlocks',
|
|
475
|
+
});
|
|
419
476
|
return { deleted: true };
|
|
420
477
|
}
|
|
421
478
|
|
|
@@ -435,15 +492,22 @@ class LarkOfficialClient {
|
|
|
435
492
|
const data = {};
|
|
436
493
|
if (name) data.name = name;
|
|
437
494
|
if (folderId) data.folder_token = folderId;
|
|
438
|
-
const res = await this.
|
|
439
|
-
|
|
440
|
-
'
|
|
441
|
-
|
|
495
|
+
const res = await this._asUserOrApp({
|
|
496
|
+
uatPath: `/open-apis/bitable/v1/apps`,
|
|
497
|
+
method: 'POST',
|
|
498
|
+
body: data,
|
|
499
|
+
sdkFn: () => this.client.bitable.app.create({ data }),
|
|
500
|
+
label: 'createBitable',
|
|
501
|
+
});
|
|
442
502
|
return { appToken: res.data.app?.app_token, name: res.data.app?.name, url: res.data.app?.url };
|
|
443
503
|
}
|
|
444
504
|
|
|
445
505
|
async listBitableTables(appToken) {
|
|
446
|
-
const res = await this.
|
|
506
|
+
const res = await this._asUserOrApp({
|
|
507
|
+
uatPath: `/open-apis/bitable/v1/apps/${appToken}/tables`,
|
|
508
|
+
sdkFn: () => this.client.bitable.appTable.list({ path: { app_token: appToken } }),
|
|
509
|
+
label: 'listTables',
|
|
510
|
+
});
|
|
447
511
|
return { items: res.data.items || [] };
|
|
448
512
|
}
|
|
449
513
|
|
|
@@ -451,39 +515,54 @@ class LarkOfficialClient {
|
|
|
451
515
|
const data = { table: { name } };
|
|
452
516
|
if (fields && fields.length > 0) data.table.default_view_name = name;
|
|
453
517
|
if (fields && fields.length > 0) data.table.fields = fields;
|
|
454
|
-
const res = await this.
|
|
455
|
-
|
|
456
|
-
'
|
|
457
|
-
|
|
518
|
+
const res = await this._asUserOrApp({
|
|
519
|
+
uatPath: `/open-apis/bitable/v1/apps/${appToken}/tables`,
|
|
520
|
+
method: 'POST',
|
|
521
|
+
body: data,
|
|
522
|
+
sdkFn: () => this.client.bitable.appTable.create({ path: { app_token: appToken }, data }),
|
|
523
|
+
label: 'createTable',
|
|
524
|
+
});
|
|
458
525
|
return { tableId: res.data.table_id };
|
|
459
526
|
}
|
|
460
527
|
|
|
461
528
|
async listBitableFields(appToken, tableId) {
|
|
462
|
-
const res = await this.
|
|
529
|
+
const res = await this._asUserOrApp({
|
|
530
|
+
uatPath: `/open-apis/bitable/v1/apps/${appToken}/tables/${tableId}/fields`,
|
|
531
|
+
sdkFn: () => this.client.bitable.appTableField.list({ path: { app_token: appToken, table_id: tableId } }),
|
|
532
|
+
label: 'listFields',
|
|
533
|
+
});
|
|
463
534
|
return { items: res.data.items || [] };
|
|
464
535
|
}
|
|
465
536
|
|
|
466
537
|
async createBitableField(appToken, tableId, fieldConfig) {
|
|
467
|
-
const res = await this.
|
|
468
|
-
|
|
469
|
-
'
|
|
470
|
-
|
|
538
|
+
const res = await this._asUserOrApp({
|
|
539
|
+
uatPath: `/open-apis/bitable/v1/apps/${appToken}/tables/${tableId}/fields`,
|
|
540
|
+
method: 'POST',
|
|
541
|
+
body: fieldConfig,
|
|
542
|
+
sdkFn: () => this.client.bitable.appTableField.create({ path: { app_token: appToken, table_id: tableId }, data: fieldConfig }),
|
|
543
|
+
label: 'createField',
|
|
544
|
+
});
|
|
471
545
|
return { field: res.data.field };
|
|
472
546
|
}
|
|
473
547
|
|
|
474
548
|
async updateBitableField(appToken, tableId, fieldId, fieldConfig) {
|
|
475
|
-
const res = await this.
|
|
476
|
-
|
|
477
|
-
'
|
|
478
|
-
|
|
549
|
+
const res = await this._asUserOrApp({
|
|
550
|
+
uatPath: `/open-apis/bitable/v1/apps/${appToken}/tables/${tableId}/fields/${fieldId}`,
|
|
551
|
+
method: 'PUT',
|
|
552
|
+
body: fieldConfig,
|
|
553
|
+
sdkFn: () => this.client.bitable.appTableField.update({ path: { app_token: appToken, table_id: tableId, field_id: fieldId }, data: fieldConfig }),
|
|
554
|
+
label: 'updateField',
|
|
555
|
+
});
|
|
479
556
|
return { field: res.data.field };
|
|
480
557
|
}
|
|
481
558
|
|
|
482
559
|
async deleteBitableField(appToken, tableId, fieldId) {
|
|
483
|
-
const res = await this.
|
|
484
|
-
|
|
485
|
-
'
|
|
486
|
-
|
|
560
|
+
const res = await this._asUserOrApp({
|
|
561
|
+
uatPath: `/open-apis/bitable/v1/apps/${appToken}/tables/${tableId}/fields/${fieldId}`,
|
|
562
|
+
method: 'DELETE',
|
|
563
|
+
sdkFn: () => this.client.bitable.appTableField.delete({ path: { app_token: appToken, table_id: tableId, field_id: fieldId } }),
|
|
564
|
+
label: 'deleteField',
|
|
565
|
+
});
|
|
487
566
|
return { fieldId: res.data.field_id, deleted: res.data.deleted };
|
|
488
567
|
}
|
|
489
568
|
|
|
@@ -491,87 +570,172 @@ class LarkOfficialClient {
|
|
|
491
570
|
const data = {};
|
|
492
571
|
if (filter) data.filter = filter;
|
|
493
572
|
if (sort) data.sort = sort;
|
|
494
|
-
|
|
495
|
-
if (
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
573
|
+
const query = {};
|
|
574
|
+
if (pageSize) query.page_size = String(pageSize);
|
|
575
|
+
if (pageToken) query.page_token = pageToken;
|
|
576
|
+
const res = await this._asUserOrApp({
|
|
577
|
+
uatPath: `/open-apis/bitable/v1/apps/${appToken}/tables/${tableId}/records/search`,
|
|
578
|
+
method: 'POST',
|
|
579
|
+
body: data,
|
|
580
|
+
query,
|
|
581
|
+
sdkFn: () => this.client.bitable.appTableRecord.search({
|
|
582
|
+
path: { app_token: appToken, table_id: tableId },
|
|
583
|
+
params: { page_size: pageSize, ...(pageToken ? { page_token: pageToken } : {}) },
|
|
584
|
+
data,
|
|
585
|
+
}),
|
|
586
|
+
label: 'searchRecords',
|
|
587
|
+
});
|
|
500
588
|
return { items: res.data.items || [], total: res.data.total, hasMore: res.data.has_more };
|
|
501
589
|
}
|
|
502
590
|
|
|
503
591
|
async createBitableRecord(appToken, tableId, fields) {
|
|
504
|
-
const res = await this.
|
|
505
|
-
|
|
506
|
-
'
|
|
507
|
-
|
|
592
|
+
const res = await this._asUserOrApp({
|
|
593
|
+
uatPath: `/open-apis/bitable/v1/apps/${appToken}/tables/${tableId}/records`,
|
|
594
|
+
method: 'POST',
|
|
595
|
+
body: { fields },
|
|
596
|
+
sdkFn: () => this.client.bitable.appTableRecord.create({ path: { app_token: appToken, table_id: tableId }, data: { fields } }),
|
|
597
|
+
label: 'createRecord',
|
|
598
|
+
});
|
|
508
599
|
return { recordId: res.data.record?.record_id };
|
|
509
600
|
}
|
|
510
601
|
|
|
511
602
|
async updateBitableRecord(appToken, tableId, recordId, fields) {
|
|
512
|
-
const res = await this.
|
|
513
|
-
|
|
514
|
-
'
|
|
515
|
-
|
|
603
|
+
const res = await this._asUserOrApp({
|
|
604
|
+
uatPath: `/open-apis/bitable/v1/apps/${appToken}/tables/${tableId}/records/${recordId}`,
|
|
605
|
+
method: 'PUT',
|
|
606
|
+
body: { fields },
|
|
607
|
+
sdkFn: () => this.client.bitable.appTableRecord.update({ path: { app_token: appToken, table_id: tableId, record_id: recordId }, data: { fields } }),
|
|
608
|
+
label: 'updateRecord',
|
|
609
|
+
});
|
|
516
610
|
return { recordId: res.data.record?.record_id };
|
|
517
611
|
}
|
|
518
612
|
|
|
519
613
|
async deleteBitableRecord(appToken, tableId, recordId) {
|
|
520
|
-
const res = await this.
|
|
521
|
-
|
|
522
|
-
'
|
|
523
|
-
|
|
614
|
+
const res = await this._asUserOrApp({
|
|
615
|
+
uatPath: `/open-apis/bitable/v1/apps/${appToken}/tables/${tableId}/records/${recordId}`,
|
|
616
|
+
method: 'DELETE',
|
|
617
|
+
sdkFn: () => this.client.bitable.appTableRecord.delete({ path: { app_token: appToken, table_id: tableId, record_id: recordId } }),
|
|
618
|
+
label: 'deleteRecord',
|
|
619
|
+
});
|
|
524
620
|
return { deleted: res.data.deleted };
|
|
525
621
|
}
|
|
526
622
|
|
|
527
623
|
async batchCreateBitableRecords(appToken, tableId, records) {
|
|
528
|
-
const res = await this.
|
|
529
|
-
|
|
530
|
-
'
|
|
531
|
-
|
|
624
|
+
const res = await this._asUserOrApp({
|
|
625
|
+
uatPath: `/open-apis/bitable/v1/apps/${appToken}/tables/${tableId}/records/batch_create`,
|
|
626
|
+
method: 'POST',
|
|
627
|
+
body: { records },
|
|
628
|
+
sdkFn: () => this.client.bitable.appTableRecord.batchCreate({ path: { app_token: appToken, table_id: tableId }, data: { records } }),
|
|
629
|
+
label: 'batchCreateRecords',
|
|
630
|
+
});
|
|
532
631
|
return { records: res.data.records || [] };
|
|
533
632
|
}
|
|
534
633
|
|
|
535
634
|
async batchUpdateBitableRecords(appToken, tableId, records) {
|
|
536
|
-
const res = await this.
|
|
537
|
-
|
|
538
|
-
'
|
|
539
|
-
|
|
635
|
+
const res = await this._asUserOrApp({
|
|
636
|
+
uatPath: `/open-apis/bitable/v1/apps/${appToken}/tables/${tableId}/records/batch_update`,
|
|
637
|
+
method: 'POST',
|
|
638
|
+
body: { records },
|
|
639
|
+
sdkFn: () => this.client.bitable.appTableRecord.batchUpdate({ path: { app_token: appToken, table_id: tableId }, data: { records } }),
|
|
640
|
+
label: 'batchUpdateRecords',
|
|
641
|
+
});
|
|
540
642
|
return { records: res.data.records || [] };
|
|
541
643
|
}
|
|
542
644
|
|
|
543
645
|
async batchDeleteBitableRecords(appToken, tableId, recordIds) {
|
|
544
|
-
const res = await this.
|
|
545
|
-
|
|
546
|
-
'
|
|
547
|
-
|
|
646
|
+
const res = await this._asUserOrApp({
|
|
647
|
+
uatPath: `/open-apis/bitable/v1/apps/${appToken}/tables/${tableId}/records/batch_delete`,
|
|
648
|
+
method: 'POST',
|
|
649
|
+
body: { records: recordIds },
|
|
650
|
+
sdkFn: () => this.client.bitable.appTableRecord.batchDelete({ path: { app_token: appToken, table_id: tableId }, data: { records: recordIds } }),
|
|
651
|
+
label: 'batchDeleteRecords',
|
|
652
|
+
});
|
|
548
653
|
return { records: res.data.records || [] };
|
|
549
654
|
}
|
|
550
655
|
|
|
551
656
|
async listBitableViews(appToken, tableId) {
|
|
552
|
-
const res = await this.
|
|
553
|
-
|
|
554
|
-
'
|
|
555
|
-
|
|
657
|
+
const res = await this._asUserOrApp({
|
|
658
|
+
uatPath: `/open-apis/bitable/v1/apps/${appToken}/tables/${tableId}/views`,
|
|
659
|
+
query: { page_size: '50' },
|
|
660
|
+
sdkFn: () => this.client.bitable.appTableView.list({ path: { app_token: appToken, table_id: tableId }, params: { page_size: 50 } }),
|
|
661
|
+
label: 'listViews',
|
|
662
|
+
});
|
|
556
663
|
return { items: res.data.items || [] };
|
|
557
664
|
}
|
|
558
665
|
|
|
559
666
|
async getBitableRecord(appToken, tableId, recordId) {
|
|
560
|
-
const res = await this.
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
667
|
+
const res = await this._asUserOrApp({
|
|
668
|
+
uatPath: `/open-apis/bitable/v1/apps/${appToken}/tables/${tableId}/records/${recordId}`,
|
|
669
|
+
sdkFn: () => this.client.bitable.appTableRecord.get({ path: { app_token: appToken, table_id: tableId, record_id: recordId } }),
|
|
670
|
+
label: 'getRecord',
|
|
671
|
+
});
|
|
564
672
|
return { record: res.data.record };
|
|
565
673
|
}
|
|
566
674
|
|
|
567
675
|
async deleteBitableTable(appToken, tableId) {
|
|
568
|
-
await this.
|
|
569
|
-
|
|
570
|
-
'
|
|
571
|
-
|
|
676
|
+
await this._asUserOrApp({
|
|
677
|
+
uatPath: `/open-apis/bitable/v1/apps/${appToken}/tables/${tableId}`,
|
|
678
|
+
method: 'DELETE',
|
|
679
|
+
sdkFn: () => this.client.bitable.appTable.delete({ path: { app_token: appToken, table_id: tableId } }),
|
|
680
|
+
label: 'deleteTable',
|
|
681
|
+
});
|
|
682
|
+
return { deleted: true };
|
|
683
|
+
}
|
|
684
|
+
|
|
685
|
+
async getBitableMeta(appToken) {
|
|
686
|
+
const res = await this._asUserOrApp({
|
|
687
|
+
uatPath: `/open-apis/bitable/v1/apps/${appToken}`,
|
|
688
|
+
sdkFn: () => this.client.bitable.app.get({ path: { app_token: appToken } }),
|
|
689
|
+
label: 'getBitableMeta',
|
|
690
|
+
});
|
|
691
|
+
return { app: res.data.app };
|
|
692
|
+
}
|
|
693
|
+
|
|
694
|
+
async updateBitableTable(appToken, tableId, name) {
|
|
695
|
+
const res = await this._asUserOrApp({
|
|
696
|
+
uatPath: `/open-apis/bitable/v1/apps/${appToken}/tables/${tableId}`,
|
|
697
|
+
method: 'PATCH',
|
|
698
|
+
body: { name },
|
|
699
|
+
sdkFn: () => this.client.bitable.appTable.patch({ path: { app_token: appToken, table_id: tableId }, data: { name } }),
|
|
700
|
+
label: 'updateTable',
|
|
701
|
+
});
|
|
702
|
+
return { name: res.data.name };
|
|
703
|
+
}
|
|
704
|
+
|
|
705
|
+
async createBitableView(appToken, tableId, viewName, viewType = 'grid') {
|
|
706
|
+
const res = await this._asUserOrApp({
|
|
707
|
+
uatPath: `/open-apis/bitable/v1/apps/${appToken}/tables/${tableId}/views`,
|
|
708
|
+
method: 'POST',
|
|
709
|
+
body: { view_name: viewName, view_type: viewType },
|
|
710
|
+
sdkFn: () => this.client.bitable.appTableView.create({ path: { app_token: appToken, table_id: tableId }, data: { view_name: viewName, view_type: viewType } }),
|
|
711
|
+
label: 'createView',
|
|
712
|
+
});
|
|
713
|
+
return { view: res.data.view };
|
|
714
|
+
}
|
|
715
|
+
|
|
716
|
+
async deleteBitableView(appToken, tableId, viewId) {
|
|
717
|
+
await this._asUserOrApp({
|
|
718
|
+
uatPath: `/open-apis/bitable/v1/apps/${appToken}/tables/${tableId}/views/${viewId}`,
|
|
719
|
+
method: 'DELETE',
|
|
720
|
+
sdkFn: () => this.client.bitable.appTableView.delete({ path: { app_token: appToken, table_id: tableId, view_id: viewId } }),
|
|
721
|
+
label: 'deleteView',
|
|
722
|
+
});
|
|
572
723
|
return { deleted: true };
|
|
573
724
|
}
|
|
574
725
|
|
|
726
|
+
async copyBitable(appToken, name, folderId) {
|
|
727
|
+
const data = { name };
|
|
728
|
+
if (folderId) data.folder_token = folderId;
|
|
729
|
+
const res = await this._asUserOrApp({
|
|
730
|
+
uatPath: `/open-apis/bitable/v1/apps/${appToken}/copy`,
|
|
731
|
+
method: 'POST',
|
|
732
|
+
body: data,
|
|
733
|
+
sdkFn: () => this.client.bitable.app.copy({ path: { app_token: appToken }, data }),
|
|
734
|
+
label: 'copyBitable',
|
|
735
|
+
});
|
|
736
|
+
return { app: res.data.app };
|
|
737
|
+
}
|
|
738
|
+
|
|
575
739
|
// --- Wiki ---
|
|
576
740
|
|
|
577
741
|
async listWikiSpaces() {
|
|
@@ -613,10 +777,14 @@ class LarkOfficialClient {
|
|
|
613
777
|
}
|
|
614
778
|
|
|
615
779
|
async createFolder(name, parentToken) {
|
|
616
|
-
const
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
780
|
+
const body = { name, folder_token: parentToken || '' };
|
|
781
|
+
const res = await this._asUserOrApp({
|
|
782
|
+
uatPath: `/open-apis/drive/v1/files/create_folder`,
|
|
783
|
+
method: 'POST',
|
|
784
|
+
body,
|
|
785
|
+
sdkFn: () => this.client.drive.file.createFolder({ data: body }),
|
|
786
|
+
label: 'createFolder',
|
|
787
|
+
});
|
|
620
788
|
return { token: res.data.token };
|
|
621
789
|
}
|
|
622
790
|
|
|
@@ -679,111 +847,6 @@ class LarkOfficialClient {
|
|
|
679
847
|
return allChats;
|
|
680
848
|
}
|
|
681
849
|
|
|
682
|
-
// --- Calendar ---
|
|
683
|
-
|
|
684
|
-
async listCalendars() {
|
|
685
|
-
const res = await this._safeSDKCall(
|
|
686
|
-
() => this.client.calendar.calendar.list({ params: { page_size: 50 } }),
|
|
687
|
-
'listCalendars'
|
|
688
|
-
);
|
|
689
|
-
return { items: res.data.calendar_list || [] };
|
|
690
|
-
}
|
|
691
|
-
|
|
692
|
-
async createCalendarEvent(calendarId, event) {
|
|
693
|
-
const res = await this._safeSDKCall(
|
|
694
|
-
() => this.client.calendar.calendarEvent.create({
|
|
695
|
-
path: { calendar_id: calendarId },
|
|
696
|
-
data: event,
|
|
697
|
-
}),
|
|
698
|
-
'createCalendarEvent'
|
|
699
|
-
);
|
|
700
|
-
return { event: res.data.event };
|
|
701
|
-
}
|
|
702
|
-
|
|
703
|
-
async listCalendarEvents(calendarId, { startTime, endTime, pageSize = 50, pageToken } = {}) {
|
|
704
|
-
const params = { page_size: pageSize };
|
|
705
|
-
if (startTime) params.start_time = startTime;
|
|
706
|
-
if (endTime) params.end_time = endTime;
|
|
707
|
-
if (pageToken) params.page_token = pageToken;
|
|
708
|
-
const res = await this._safeSDKCall(
|
|
709
|
-
() => this.client.calendar.calendarEvent.list({
|
|
710
|
-
path: { calendar_id: calendarId },
|
|
711
|
-
params,
|
|
712
|
-
}),
|
|
713
|
-
'listCalendarEvents'
|
|
714
|
-
);
|
|
715
|
-
return { items: res.data.items || [], hasMore: res.data.has_more, pageToken: res.data.page_token };
|
|
716
|
-
}
|
|
717
|
-
|
|
718
|
-
async deleteCalendarEvent(calendarId, eventId) {
|
|
719
|
-
await this._safeSDKCall(
|
|
720
|
-
() => this.client.calendar.calendarEvent.delete({
|
|
721
|
-
path: { calendar_id: calendarId, event_id: eventId },
|
|
722
|
-
}),
|
|
723
|
-
'deleteCalendarEvent'
|
|
724
|
-
);
|
|
725
|
-
return { deleted: true };
|
|
726
|
-
}
|
|
727
|
-
|
|
728
|
-
async getFreeBusy(userIds, startTime, endTime) {
|
|
729
|
-
const res = await this._safeSDKCall(
|
|
730
|
-
() => this.client.calendar.freebusy.list({
|
|
731
|
-
data: {
|
|
732
|
-
time_min: startTime,
|
|
733
|
-
time_max: endTime,
|
|
734
|
-
user_id: { user_ids: userIds, id_type: 'open_id' },
|
|
735
|
-
},
|
|
736
|
-
}),
|
|
737
|
-
'getFreeBusy'
|
|
738
|
-
);
|
|
739
|
-
return { freebusyList: res.data.freebusy_list || [] };
|
|
740
|
-
}
|
|
741
|
-
|
|
742
|
-
// --- Tasks ---
|
|
743
|
-
|
|
744
|
-
async createTask(task) {
|
|
745
|
-
const res = await this._safeSDKCall(
|
|
746
|
-
() => this.client.task.task.create({ data: task }),
|
|
747
|
-
'createTask'
|
|
748
|
-
);
|
|
749
|
-
return { task: res.data.task };
|
|
750
|
-
}
|
|
751
|
-
|
|
752
|
-
async getTask(taskId) {
|
|
753
|
-
const res = await this._safeSDKCall(
|
|
754
|
-
() => this.client.task.task.get({ path: { task_id: taskId } }),
|
|
755
|
-
'getTask'
|
|
756
|
-
);
|
|
757
|
-
return { task: res.data.task };
|
|
758
|
-
}
|
|
759
|
-
|
|
760
|
-
async listTasks({ pageSize = 50, pageToken } = {}) {
|
|
761
|
-
const res = await this._safeSDKCall(
|
|
762
|
-
() => this.client.task.task.list({ params: { page_size: pageSize, page_token: pageToken } }),
|
|
763
|
-
'listTasks'
|
|
764
|
-
);
|
|
765
|
-
return { items: res.data.items || [], hasMore: res.data.has_more, pageToken: res.data.page_token };
|
|
766
|
-
}
|
|
767
|
-
|
|
768
|
-
async updateTask(taskId, task) {
|
|
769
|
-
const res = await this._safeSDKCall(
|
|
770
|
-
() => this.client.task.task.patch({
|
|
771
|
-
path: { task_id: taskId },
|
|
772
|
-
data: task,
|
|
773
|
-
}),
|
|
774
|
-
'updateTask'
|
|
775
|
-
);
|
|
776
|
-
return { task: res.data.task };
|
|
777
|
-
}
|
|
778
|
-
|
|
779
|
-
async completeTask(taskId) {
|
|
780
|
-
const res = await this._safeSDKCall(
|
|
781
|
-
() => this.client.task.task.complete({ path: { task_id: taskId } }),
|
|
782
|
-
'completeTask'
|
|
783
|
-
);
|
|
784
|
-
return { completed: true };
|
|
785
|
-
}
|
|
786
|
-
|
|
787
850
|
// --- Safe SDK Call (extracts real Feishu error from AxiosError) ---
|
|
788
851
|
|
|
789
852
|
async _safeSDKCall(fn, label = 'API') {
|
|
@@ -869,7 +932,7 @@ class LarkOfficialClient {
|
|
|
869
932
|
if (!m) return null;
|
|
870
933
|
let body = m.body?.content || '';
|
|
871
934
|
try { body = JSON.parse(body); } catch {}
|
|
872
|
-
|
|
935
|
+
const out = {
|
|
873
936
|
messageId: m.message_id,
|
|
874
937
|
chatId: m.chat_id,
|
|
875
938
|
senderId: m.sender?.id,
|
|
@@ -879,6 +942,8 @@ class LarkOfficialClient {
|
|
|
879
942
|
createTime: this._normalizeTimestamp(m.create_time),
|
|
880
943
|
updateTime: this._normalizeTimestamp(m.update_time),
|
|
881
944
|
};
|
|
945
|
+
if (Array.isArray(m.mentions) && m.mentions.length > 0) out.mentions = m.mentions;
|
|
946
|
+
return out;
|
|
882
947
|
}
|
|
883
948
|
|
|
884
949
|
_normalizeTimestamp(ts) {
|