feishu-user-plugin 1.2.0 → 1.2.1
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/package.json +2 -2
- package/src/index.js +188 -5
- package/src/official.js +107 -3
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "feishu-user-plugin",
|
|
3
|
-
"version": "1.2.
|
|
4
|
-
"description": "All-in-one Feishu plugin for Claude Code — send messages as yourself, read chats, manage docs/tables/wiki.
|
|
3
|
+
"version": "1.2.1",
|
|
4
|
+
"description": "All-in-one Feishu plugin for Claude Code — send messages as yourself, read chats, manage docs/tables/wiki. 46 tools + 9 skills, 3 auth layers.",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"bin": {
|
|
7
7
|
"feishu-user-plugin": "src/cli.js"
|
package/src/index.js
CHANGED
|
@@ -272,10 +272,10 @@ const TOOLS = [
|
|
|
272
272
|
},
|
|
273
273
|
{
|
|
274
274
|
name: 'get_chat_info',
|
|
275
|
-
description: '[User Identity] Get chat details: name, description, member count, owner.',
|
|
275
|
+
description: '[Official API + User Identity fallback] Get chat details: name, description, member count, owner. Supports both oc_xxx and numeric chat_id.',
|
|
276
276
|
inputSchema: {
|
|
277
277
|
type: 'object',
|
|
278
|
-
properties: { chat_id: { type: 'string', description: 'Chat ID' } },
|
|
278
|
+
properties: { chat_id: { type: 'string', description: 'Chat ID (oc_xxx or numeric)' } },
|
|
279
279
|
required: ['chat_id'],
|
|
280
280
|
},
|
|
281
281
|
},
|
|
@@ -421,6 +421,17 @@ const TOOLS = [
|
|
|
421
421
|
},
|
|
422
422
|
|
|
423
423
|
// ========== Bitable — Official API ==========
|
|
424
|
+
{
|
|
425
|
+
name: 'create_bitable',
|
|
426
|
+
description: '[Official API] Create a new Bitable (multi-dimensional table) app.',
|
|
427
|
+
inputSchema: {
|
|
428
|
+
type: 'object',
|
|
429
|
+
properties: {
|
|
430
|
+
name: { type: 'string', description: 'Bitable app name' },
|
|
431
|
+
folder_id: { type: 'string', description: 'Parent folder token (optional, defaults to root)' },
|
|
432
|
+
},
|
|
433
|
+
},
|
|
434
|
+
},
|
|
424
435
|
{
|
|
425
436
|
name: 'list_bitable_tables',
|
|
426
437
|
description: '[Official API] List all tables in a Bitable app.',
|
|
@@ -430,6 +441,23 @@ const TOOLS = [
|
|
|
430
441
|
required: ['app_token'],
|
|
431
442
|
},
|
|
432
443
|
},
|
|
444
|
+
{
|
|
445
|
+
name: 'create_bitable_table',
|
|
446
|
+
description: '[Official API] Create a new data table in a Bitable app. Optionally define initial fields.',
|
|
447
|
+
inputSchema: {
|
|
448
|
+
type: 'object',
|
|
449
|
+
properties: {
|
|
450
|
+
app_token: { type: 'string', description: 'Bitable app token' },
|
|
451
|
+
name: { type: 'string', description: 'Table name' },
|
|
452
|
+
fields: {
|
|
453
|
+
type: 'array',
|
|
454
|
+
description: 'Initial field definitions (optional). Each item: {field_name, type} where type is 1=Text, 2=Number, 3=SingleSelect, 4=MultiSelect, 5=DateTime, 7=Checkbox, 11=User, 13=Phone, 15=URL, 17=Attachment, 18=Link, 20=Formula, 21=DuplexLink, 22=Location, 23=GroupChat, 1001=CreateTime, 1002=ModifiedTime, 1003=Creator, 1004=Modifier',
|
|
455
|
+
items: { type: 'object' },
|
|
456
|
+
},
|
|
457
|
+
},
|
|
458
|
+
required: ['app_token', 'name'],
|
|
459
|
+
},
|
|
460
|
+
},
|
|
433
461
|
{
|
|
434
462
|
name: 'list_bitable_fields',
|
|
435
463
|
description: '[Official API] List all fields (columns) in a Bitable table.',
|
|
@@ -442,6 +470,62 @@ const TOOLS = [
|
|
|
442
470
|
required: ['app_token', 'table_id'],
|
|
443
471
|
},
|
|
444
472
|
},
|
|
473
|
+
{
|
|
474
|
+
name: 'create_bitable_field',
|
|
475
|
+
description: '[Official API] Create a new field (column) in a Bitable table.',
|
|
476
|
+
inputSchema: {
|
|
477
|
+
type: 'object',
|
|
478
|
+
properties: {
|
|
479
|
+
app_token: { type: 'string', description: 'Bitable app token' },
|
|
480
|
+
table_id: { type: 'string', description: 'Table ID' },
|
|
481
|
+
field_name: { type: 'string', description: 'Field display name' },
|
|
482
|
+
type: { type: 'number', description: 'Field type: 1=Text, 2=Number, 3=SingleSelect, 4=MultiSelect, 5=DateTime, 7=Checkbox, 11=User, 13=Phone, 15=URL, 17=Attachment, 18=Link, 20=Formula, 21=DuplexLink, 22=Location, 23=GroupChat, 1001=CreateTime, 1002=ModifiedTime, 1003=Creator, 1004=Modifier' },
|
|
483
|
+
property: { type: 'object', description: 'Field-type-specific properties (optional). E.g. for SingleSelect: {options: [{name:"A"},{name:"B"}]}' },
|
|
484
|
+
},
|
|
485
|
+
required: ['app_token', 'table_id', 'field_name', 'type'],
|
|
486
|
+
},
|
|
487
|
+
},
|
|
488
|
+
{
|
|
489
|
+
name: 'update_bitable_field',
|
|
490
|
+
description: '[Official API] Update an existing field (column) in a Bitable table.',
|
|
491
|
+
inputSchema: {
|
|
492
|
+
type: 'object',
|
|
493
|
+
properties: {
|
|
494
|
+
app_token: { type: 'string', description: 'Bitable app token' },
|
|
495
|
+
table_id: { type: 'string', description: 'Table ID' },
|
|
496
|
+
field_id: { type: 'string', description: 'Field ID to update' },
|
|
497
|
+
field_name: { type: 'string', description: 'New field name (optional)' },
|
|
498
|
+
type: { type: 'number', description: 'Field type (REQUIRED by Feishu API, see create_bitable_field for values)' },
|
|
499
|
+
property: { type: 'object', description: 'Field-type-specific properties (optional)' },
|
|
500
|
+
},
|
|
501
|
+
required: ['app_token', 'table_id', 'field_id', 'type'],
|
|
502
|
+
},
|
|
503
|
+
},
|
|
504
|
+
{
|
|
505
|
+
name: 'delete_bitable_field',
|
|
506
|
+
description: '[Official API] Delete a field (column) from a Bitable table.',
|
|
507
|
+
inputSchema: {
|
|
508
|
+
type: 'object',
|
|
509
|
+
properties: {
|
|
510
|
+
app_token: { type: 'string', description: 'Bitable app token' },
|
|
511
|
+
table_id: { type: 'string', description: 'Table ID' },
|
|
512
|
+
field_id: { type: 'string', description: 'Field ID to delete' },
|
|
513
|
+
},
|
|
514
|
+
required: ['app_token', 'table_id', 'field_id'],
|
|
515
|
+
},
|
|
516
|
+
},
|
|
517
|
+
{
|
|
518
|
+
name: 'list_bitable_views',
|
|
519
|
+
description: '[Official API] List all views in a Bitable table.',
|
|
520
|
+
inputSchema: {
|
|
521
|
+
type: 'object',
|
|
522
|
+
properties: {
|
|
523
|
+
app_token: { type: 'string', description: 'Bitable app token' },
|
|
524
|
+
table_id: { type: 'string', description: 'Table ID' },
|
|
525
|
+
},
|
|
526
|
+
required: ['app_token', 'table_id'],
|
|
527
|
+
},
|
|
528
|
+
},
|
|
445
529
|
{
|
|
446
530
|
name: 'search_bitable_records',
|
|
447
531
|
description: '[Official API] Search/query records in a Bitable table.',
|
|
@@ -484,6 +568,58 @@ const TOOLS = [
|
|
|
484
568
|
required: ['app_token', 'table_id', 'record_id', 'fields'],
|
|
485
569
|
},
|
|
486
570
|
},
|
|
571
|
+
{
|
|
572
|
+
name: 'delete_bitable_record',
|
|
573
|
+
description: '[Official API] Delete a record (row) from a Bitable table.',
|
|
574
|
+
inputSchema: {
|
|
575
|
+
type: 'object',
|
|
576
|
+
properties: {
|
|
577
|
+
app_token: { type: 'string', description: 'Bitable app token' },
|
|
578
|
+
table_id: { type: 'string', description: 'Table ID' },
|
|
579
|
+
record_id: { type: 'string', description: 'Record ID to delete' },
|
|
580
|
+
},
|
|
581
|
+
required: ['app_token', 'table_id', 'record_id'],
|
|
582
|
+
},
|
|
583
|
+
},
|
|
584
|
+
{
|
|
585
|
+
name: 'batch_create_bitable_records',
|
|
586
|
+
description: '[Official API] Batch create multiple records (rows) in a Bitable table. Max 500 per call.',
|
|
587
|
+
inputSchema: {
|
|
588
|
+
type: 'object',
|
|
589
|
+
properties: {
|
|
590
|
+
app_token: { type: 'string', description: 'Bitable app token' },
|
|
591
|
+
table_id: { type: 'string', description: 'Table ID' },
|
|
592
|
+
records: { type: 'array', description: 'Array of {fields: {field_name: value}} objects', items: { type: 'object' } },
|
|
593
|
+
},
|
|
594
|
+
required: ['app_token', 'table_id', 'records'],
|
|
595
|
+
},
|
|
596
|
+
},
|
|
597
|
+
{
|
|
598
|
+
name: 'batch_update_bitable_records',
|
|
599
|
+
description: '[Official API] Batch update multiple records in a Bitable table. Max 500 per call.',
|
|
600
|
+
inputSchema: {
|
|
601
|
+
type: 'object',
|
|
602
|
+
properties: {
|
|
603
|
+
app_token: { type: 'string', description: 'Bitable app token' },
|
|
604
|
+
table_id: { type: 'string', description: 'Table ID' },
|
|
605
|
+
records: { type: 'array', description: 'Array of {record_id, fields: {field_name: value}} objects', items: { type: 'object' } },
|
|
606
|
+
},
|
|
607
|
+
required: ['app_token', 'table_id', 'records'],
|
|
608
|
+
},
|
|
609
|
+
},
|
|
610
|
+
{
|
|
611
|
+
name: 'batch_delete_bitable_records',
|
|
612
|
+
description: '[Official API] Batch delete multiple records from a Bitable table. Max 500 per call.',
|
|
613
|
+
inputSchema: {
|
|
614
|
+
type: 'object',
|
|
615
|
+
properties: {
|
|
616
|
+
app_token: { type: 'string', description: 'Bitable app token' },
|
|
617
|
+
table_id: { type: 'string', description: 'Table ID' },
|
|
618
|
+
record_ids: { type: 'array', description: 'Array of record IDs to delete', items: { type: 'string' } },
|
|
619
|
+
},
|
|
620
|
+
required: ['app_token', 'table_id', 'record_ids'],
|
|
621
|
+
},
|
|
622
|
+
},
|
|
487
623
|
|
|
488
624
|
// ========== Wiki — Official API ==========
|
|
489
625
|
{
|
|
@@ -677,9 +813,24 @@ async function handleTool(name, args) {
|
|
|
677
813
|
return text(chatId ? `P2P chat: ${chatId}` : 'Failed to create P2P chat');
|
|
678
814
|
}
|
|
679
815
|
case 'get_chat_info': {
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
816
|
+
// Strategy 1: Official API im.chat.get (supports oc_xxx format)
|
|
817
|
+
if (args.chat_id.startsWith('oc_')) {
|
|
818
|
+
try {
|
|
819
|
+
const info = await getOfficialClient().getChatInfo(args.chat_id);
|
|
820
|
+
return info ? json(info) : text(`No info for chat ${args.chat_id}`);
|
|
821
|
+
} catch (e) {
|
|
822
|
+
console.error(`[feishu-user-plugin] Official getChatInfo failed: ${e.message}`);
|
|
823
|
+
}
|
|
824
|
+
}
|
|
825
|
+
// Strategy 2: Protobuf gateway (supports numeric chat_id)
|
|
826
|
+
try {
|
|
827
|
+
const c = await getUserClient();
|
|
828
|
+
const info = await c.getGroupInfo(args.chat_id);
|
|
829
|
+
if (info) return json(info);
|
|
830
|
+
} catch (e) {
|
|
831
|
+
console.error(`[feishu-user-plugin] Protobuf getChatInfo failed: ${e.message}`);
|
|
832
|
+
}
|
|
833
|
+
return text(`No info for chat ${args.chat_id}`);
|
|
683
834
|
}
|
|
684
835
|
case 'get_user_info': {
|
|
685
836
|
let n = null;
|
|
@@ -807,10 +958,34 @@ async function handleTool(name, args) {
|
|
|
807
958
|
|
|
808
959
|
// --- Official API: Bitable ---
|
|
809
960
|
|
|
961
|
+
case 'create_bitable': {
|
|
962
|
+
const r = await getOfficialClient().createBitable(args.name, args.folder_id);
|
|
963
|
+
return text(`Bitable created: ${r.appToken}${r.url ? `\nURL: ${r.url}` : ''}`);
|
|
964
|
+
}
|
|
810
965
|
case 'list_bitable_tables':
|
|
811
966
|
return json(await getOfficialClient().listBitableTables(args.app_token));
|
|
967
|
+
case 'create_bitable_table':
|
|
968
|
+
return text(`Table created: ${(await getOfficialClient().createBitableTable(args.app_token, args.name, args.fields)).tableId}`);
|
|
812
969
|
case 'list_bitable_fields':
|
|
813
970
|
return json(await getOfficialClient().listBitableFields(args.app_token, args.table_id));
|
|
971
|
+
case 'create_bitable_field': {
|
|
972
|
+
const config = { field_name: args.field_name, type: args.type };
|
|
973
|
+
if (args.property) config.property = args.property;
|
|
974
|
+
return json(await getOfficialClient().createBitableField(args.app_token, args.table_id, config));
|
|
975
|
+
}
|
|
976
|
+
case 'update_bitable_field': {
|
|
977
|
+
const config = {};
|
|
978
|
+
if (args.field_name) config.field_name = args.field_name;
|
|
979
|
+
if (args.type) config.type = args.type;
|
|
980
|
+
if (args.property) config.property = args.property;
|
|
981
|
+
return json(await getOfficialClient().updateBitableField(args.app_token, args.table_id, args.field_id, config));
|
|
982
|
+
}
|
|
983
|
+
case 'delete_bitable_field': {
|
|
984
|
+
const r = await getOfficialClient().deleteBitableField(args.app_token, args.table_id, args.field_id);
|
|
985
|
+
return text(r.deleted ? `Field ${r.fieldId} deleted` : `Field deletion returned deleted=${r.deleted}`);
|
|
986
|
+
}
|
|
987
|
+
case 'list_bitable_views':
|
|
988
|
+
return json(await getOfficialClient().listBitableViews(args.app_token, args.table_id));
|
|
814
989
|
case 'search_bitable_records':
|
|
815
990
|
return json(await getOfficialClient().searchBitableRecords(args.app_token, args.table_id, {
|
|
816
991
|
filter: args.filter, sort: args.sort, pageSize: args.page_size,
|
|
@@ -819,6 +994,14 @@ async function handleTool(name, args) {
|
|
|
819
994
|
return text(`Record created: ${(await getOfficialClient().createBitableRecord(args.app_token, args.table_id, args.fields)).recordId}`);
|
|
820
995
|
case 'update_bitable_record':
|
|
821
996
|
return text(`Record updated: ${(await getOfficialClient().updateBitableRecord(args.app_token, args.table_id, args.record_id, args.fields)).recordId}`);
|
|
997
|
+
case 'delete_bitable_record':
|
|
998
|
+
return text(`Record deleted: ${(await getOfficialClient().deleteBitableRecord(args.app_token, args.table_id, args.record_id)).deleted}`);
|
|
999
|
+
case 'batch_create_bitable_records':
|
|
1000
|
+
return json(await getOfficialClient().batchCreateBitableRecords(args.app_token, args.table_id, args.records));
|
|
1001
|
+
case 'batch_update_bitable_records':
|
|
1002
|
+
return json(await getOfficialClient().batchUpdateBitableRecords(args.app_token, args.table_id, args.records));
|
|
1003
|
+
case 'batch_delete_bitable_records':
|
|
1004
|
+
return json(await getOfficialClient().batchDeleteBitableRecords(args.app_token, args.table_id, args.record_ids));
|
|
822
1005
|
|
|
823
1006
|
// --- Official API: Wiki ---
|
|
824
1007
|
|
package/src/official.js
CHANGED
|
@@ -187,7 +187,10 @@ class LarkOfficialClient {
|
|
|
187
187
|
}),
|
|
188
188
|
'uploadImage'
|
|
189
189
|
);
|
|
190
|
-
|
|
190
|
+
// SDK multipart responses may have data at top level or nested under .data
|
|
191
|
+
const imageKey = res.data?.image_key || res.image_key;
|
|
192
|
+
if (!imageKey) throw new Error(`uploadImage: unexpected response structure: ${JSON.stringify(res).slice(0, 500)}`);
|
|
193
|
+
return { imageKey };
|
|
191
194
|
}
|
|
192
195
|
|
|
193
196
|
async uploadFile(filePath, fileType = 'stream', fileName) {
|
|
@@ -204,7 +207,10 @@ class LarkOfficialClient {
|
|
|
204
207
|
}),
|
|
205
208
|
'uploadFile'
|
|
206
209
|
);
|
|
207
|
-
|
|
210
|
+
// SDK multipart responses may have data at top level or nested under .data
|
|
211
|
+
const fileKey = res.data?.file_key || res.file_key;
|
|
212
|
+
if (!fileKey) throw new Error(`uploadFile: unexpected response structure: ${JSON.stringify(res).slice(0, 500)}`);
|
|
213
|
+
return { fileKey };
|
|
208
214
|
}
|
|
209
215
|
|
|
210
216
|
// --- Docs ---
|
|
@@ -244,18 +250,74 @@ class LarkOfficialClient {
|
|
|
244
250
|
return { items: res.data.items || [] };
|
|
245
251
|
}
|
|
246
252
|
|
|
253
|
+
// --- Chat Info (Official API) ---
|
|
254
|
+
|
|
255
|
+
async getChatInfo(chatId) {
|
|
256
|
+
const res = await this._safeSDKCall(
|
|
257
|
+
() => this.client.im.chat.get({ path: { chat_id: chatId } }),
|
|
258
|
+
'getChatInfo'
|
|
259
|
+
);
|
|
260
|
+
return res.data;
|
|
261
|
+
}
|
|
262
|
+
|
|
247
263
|
// --- Bitable ---
|
|
248
264
|
|
|
265
|
+
async createBitable(name, folderId) {
|
|
266
|
+
const data = {};
|
|
267
|
+
if (name) data.name = name;
|
|
268
|
+
if (folderId) data.folder_token = folderId;
|
|
269
|
+
const res = await this._safeSDKCall(
|
|
270
|
+
() => this.client.bitable.app.create({ data }),
|
|
271
|
+
'createBitable'
|
|
272
|
+
);
|
|
273
|
+
return { appToken: res.data.app?.app_token, name: res.data.app?.name, url: res.data.app?.url };
|
|
274
|
+
}
|
|
275
|
+
|
|
249
276
|
async listBitableTables(appToken) {
|
|
250
277
|
const res = await this._safeSDKCall(() => this.client.bitable.appTable.list({ path: { app_token: appToken } }), 'listTables');
|
|
251
278
|
return { items: res.data.items || [] };
|
|
252
279
|
}
|
|
253
280
|
|
|
281
|
+
async createBitableTable(appToken, name, fields) {
|
|
282
|
+
const data = { table: { name } };
|
|
283
|
+
if (fields && fields.length > 0) data.table.default_view_name = name;
|
|
284
|
+
if (fields && fields.length > 0) data.table.fields = fields;
|
|
285
|
+
const res = await this._safeSDKCall(
|
|
286
|
+
() => this.client.bitable.appTable.create({ path: { app_token: appToken }, data }),
|
|
287
|
+
'createTable'
|
|
288
|
+
);
|
|
289
|
+
return { tableId: res.data.table_id };
|
|
290
|
+
}
|
|
291
|
+
|
|
254
292
|
async listBitableFields(appToken, tableId) {
|
|
255
293
|
const res = await this._safeSDKCall(() => this.client.bitable.appTableField.list({ path: { app_token: appToken, table_id: tableId } }), 'listFields');
|
|
256
294
|
return { items: res.data.items || [] };
|
|
257
295
|
}
|
|
258
296
|
|
|
297
|
+
async createBitableField(appToken, tableId, fieldConfig) {
|
|
298
|
+
const res = await this._safeSDKCall(
|
|
299
|
+
() => this.client.bitable.appTableField.create({ path: { app_token: appToken, table_id: tableId }, data: fieldConfig }),
|
|
300
|
+
'createField'
|
|
301
|
+
);
|
|
302
|
+
return { field: res.data.field };
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
async updateBitableField(appToken, tableId, fieldId, fieldConfig) {
|
|
306
|
+
const res = await this._safeSDKCall(
|
|
307
|
+
() => this.client.bitable.appTableField.update({ path: { app_token: appToken, table_id: tableId, field_id: fieldId }, data: fieldConfig }),
|
|
308
|
+
'updateField'
|
|
309
|
+
);
|
|
310
|
+
return { field: res.data.field };
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
async deleteBitableField(appToken, tableId, fieldId) {
|
|
314
|
+
const res = await this._safeSDKCall(
|
|
315
|
+
() => this.client.bitable.appTableField.delete({ path: { app_token: appToken, table_id: tableId, field_id: fieldId } }),
|
|
316
|
+
'deleteField'
|
|
317
|
+
);
|
|
318
|
+
return { fieldId: res.data.field_id, deleted: res.data.deleted };
|
|
319
|
+
}
|
|
320
|
+
|
|
259
321
|
async searchBitableRecords(appToken, tableId, { filter, sort, pageSize = 20, pageToken } = {}) {
|
|
260
322
|
const data = {};
|
|
261
323
|
if (filter) data.filter = filter;
|
|
@@ -285,6 +347,46 @@ class LarkOfficialClient {
|
|
|
285
347
|
return { recordId: res.data.record?.record_id };
|
|
286
348
|
}
|
|
287
349
|
|
|
350
|
+
async deleteBitableRecord(appToken, tableId, recordId) {
|
|
351
|
+
const res = await this._safeSDKCall(
|
|
352
|
+
() => this.client.bitable.appTableRecord.delete({ path: { app_token: appToken, table_id: tableId, record_id: recordId } }),
|
|
353
|
+
'deleteRecord'
|
|
354
|
+
);
|
|
355
|
+
return { deleted: res.data.deleted };
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
async batchCreateBitableRecords(appToken, tableId, records) {
|
|
359
|
+
const res = await this._safeSDKCall(
|
|
360
|
+
() => this.client.bitable.appTableRecord.batchCreate({ path: { app_token: appToken, table_id: tableId }, data: { records } }),
|
|
361
|
+
'batchCreateRecords'
|
|
362
|
+
);
|
|
363
|
+
return { records: res.data.records || [] };
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
async batchUpdateBitableRecords(appToken, tableId, records) {
|
|
367
|
+
const res = await this._safeSDKCall(
|
|
368
|
+
() => this.client.bitable.appTableRecord.batchUpdate({ path: { app_token: appToken, table_id: tableId }, data: { records } }),
|
|
369
|
+
'batchUpdateRecords'
|
|
370
|
+
);
|
|
371
|
+
return { records: res.data.records || [] };
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
async batchDeleteBitableRecords(appToken, tableId, recordIds) {
|
|
375
|
+
const res = await this._safeSDKCall(
|
|
376
|
+
() => this.client.bitable.appTableRecord.batchDelete({ path: { app_token: appToken, table_id: tableId }, data: { records: recordIds } }),
|
|
377
|
+
'batchDeleteRecords'
|
|
378
|
+
);
|
|
379
|
+
return { records: res.data.records || [] };
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
async listBitableViews(appToken, tableId) {
|
|
383
|
+
const res = await this._safeSDKCall(
|
|
384
|
+
() => this.client.bitable.appTableView.list({ path: { app_token: appToken, table_id: tableId }, params: { page_size: 50 } }),
|
|
385
|
+
'listViews'
|
|
386
|
+
);
|
|
387
|
+
return { items: res.data.items || [] };
|
|
388
|
+
}
|
|
389
|
+
|
|
288
390
|
// --- Wiki ---
|
|
289
391
|
|
|
290
392
|
async listWikiSpaces() {
|
|
@@ -369,7 +471,9 @@ class LarkOfficialClient {
|
|
|
369
471
|
async _safeSDKCall(fn, label = 'API') {
|
|
370
472
|
try {
|
|
371
473
|
const res = await fn();
|
|
372
|
-
|
|
474
|
+
// SDK returns abbreviated responses for multipart uploads (code/msg undefined)
|
|
475
|
+
// Only treat as error if code is explicitly non-zero
|
|
476
|
+
if (res.code !== undefined && res.code !== 0) throw new Error(`${label} failed (${res.code}): ${res.msg}`);
|
|
373
477
|
return res;
|
|
374
478
|
} catch (err) {
|
|
375
479
|
// Lark SDK uses axios; extract actual Feishu error from response body
|