feishu-user-plugin 1.3.5 → 1.3.7
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/CHANGELOG.md +22 -0
- package/README.md +66 -40
- package/package.json +10 -3
- package/scripts/check-tool-count.js +15 -0
- package/scripts/check-version.js +40 -0
- package/scripts/smoke.js +224 -0
- package/scripts/sync-claude-md.sh +12 -0
- package/scripts/sync-team-skills.sh +22 -0
- package/scripts/test-all-tools.js +158 -0
- package/skills/feishu-user-plugin/SKILL.md +5 -5
- package/skills/feishu-user-plugin/references/CLAUDE.md +152 -96
- package/skills/feishu-user-plugin/references/table.md +18 -9
- package/src/auth/credentials.js +350 -0
- package/src/cli.js +42 -13
- package/src/clients/official/base.js +424 -0
- package/src/clients/official/bitable.js +269 -0
- package/src/clients/official/calendar.js +176 -0
- package/src/clients/official/contacts.js +54 -0
- package/src/clients/official/docs.js +301 -0
- package/src/clients/official/drive.js +77 -0
- package/src/clients/official/groups.js +68 -0
- package/src/clients/official/im.js +414 -0
- package/src/clients/official/index.js +30 -0
- package/src/clients/official/okr.js +127 -0
- package/src/clients/official/tasks.js +142 -0
- package/src/clients/official/uploads.js +260 -0
- package/src/clients/official/wiki.js +207 -0
- package/src/{client.js → clients/user.js} +23 -17
- package/src/doc-blocks.js +20 -5
- package/src/index.js +4 -1744
- package/src/logger.js +20 -0
- package/src/oauth.js +8 -1
- package/src/official.js +5 -1734
- package/src/prompts/_registry.js +69 -0
- package/src/prompts/index.js +54 -0
- package/src/server.js +242 -0
- package/src/test-all.js +2 -2
- package/src/test-comprehensive.js +3 -3
- package/src/test-send.js +1 -1
- package/src/tools/_registry.js +30 -0
- package/src/tools/bitable.js +246 -0
- package/src/tools/calendar.js +207 -0
- package/src/tools/contacts.js +66 -0
- package/src/tools/diagnostics.js +172 -0
- package/src/tools/docs.js +158 -0
- package/src/tools/drive.js +111 -0
- package/src/tools/groups.js +81 -0
- package/src/tools/im-read.js +259 -0
- package/src/tools/messaging-bot.js +151 -0
- package/src/tools/messaging-user.js +292 -0
- package/src/tools/okr.js +159 -0
- package/src/tools/profile.js +43 -0
- package/src/tools/tasks.js +168 -0
- package/src/tools/uploads.js +63 -0
- package/src/tools/wiki.js +191 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
const path = require('path');
|
|
2
2
|
const protobuf = require('protobufjs');
|
|
3
|
-
const { generateRequestId, generateCid, parseCookie, formatCookie, fetchWithTimeout } = require('
|
|
3
|
+
const { generateRequestId, generateCid, parseCookie, formatCookie, fetchWithTimeout } = require('../utils');
|
|
4
4
|
|
|
5
5
|
const GATEWAY_URL = 'https://internal-api-lark-api.feishu.cn/im/gateway/';
|
|
6
6
|
const CSRF_URL = 'https://internal-api-lark-api.feishu.cn/accounts/csrf';
|
|
@@ -34,7 +34,10 @@ class LarkUserClient {
|
|
|
34
34
|
}
|
|
35
35
|
|
|
36
36
|
async init() {
|
|
37
|
-
|
|
37
|
+
// Path: clients/user.js → ../../proto/lark.proto. Phase A refactor moved
|
|
38
|
+
// the file from src/client.js to src/clients/user.js but didn't deepen the
|
|
39
|
+
// relative path, so cookie init would ENOENT. Fixed here as part of B2.
|
|
40
|
+
this.proto = await protobuf.load(path.join(__dirname, '..', '..', 'proto', 'lark.proto'));
|
|
38
41
|
await this._getCsrfToken();
|
|
39
42
|
await this._getUserInfo();
|
|
40
43
|
if (!this.userId) {
|
|
@@ -90,8 +93,10 @@ class LarkUserClient {
|
|
|
90
93
|
this._heartbeatTimer = setInterval(async () => {
|
|
91
94
|
try {
|
|
92
95
|
await this._getCsrfToken();
|
|
93
|
-
// Lazy require to avoid circular dependency at module load time
|
|
94
|
-
|
|
96
|
+
// Lazy require to avoid circular dependency at module load time.
|
|
97
|
+
// auth/credentials writes to credentials.json (single source of truth)
|
|
98
|
+
// when it exists; falls back to legacy mcpServers persistence otherwise.
|
|
99
|
+
const { persistToConfig } = require('../auth/credentials');
|
|
95
100
|
persistToConfig({ LARK_COOKIE: this.cookieStr });
|
|
96
101
|
console.error('[feishu-user-plugin] Cookie heartbeat: session refreshed and persisted');
|
|
97
102
|
} catch (e) {
|
|
@@ -195,7 +200,20 @@ class LarkUserClient {
|
|
|
195
200
|
if (rootId) req.rootId = rootId;
|
|
196
201
|
if (parentId) req.parentId = parentId;
|
|
197
202
|
const { packet, ok } = await this._gateway(5, 'PutMessageRequest', req, '5.7.0');
|
|
198
|
-
|
|
203
|
+
if (!ok) {
|
|
204
|
+
// The cookie protobuf gateway returns HTTP 400 when our wire format is
|
|
205
|
+
// missing required fields. Verified for IMAGE (v1.3.7 testing): the
|
|
206
|
+
// simple {imageKey} content payload is rejected — Feishu Web encodes
|
|
207
|
+
// images with extra metadata (image dimensions, mime type, etc.) that
|
|
208
|
+
// we don't have in proto/lark.proto. Reverse-engineering requires Chrome
|
|
209
|
+
// DevTools capture and is deferred to v1.3.8. Surface a clear error
|
|
210
|
+
// routing the user to send_message_as_bot, which works.
|
|
211
|
+
if (type === MsgType.IMAGE) {
|
|
212
|
+
throw new Error('send_image_as_user: Feishu cookie protobuf gateway rejected the IMAGE wire format (HTTP 400). User-identity image sends are not yet supported — wire format reverse-engineering is deferred to v1.3.8. Workaround: use send_message_as_bot(chat_id, msg_type="image", payload={image_key:"..."}).');
|
|
213
|
+
}
|
|
214
|
+
throw new Error(`_sendMsg: cookie protobuf gateway returned non-2xx for type=${type}. The wire format likely doesn't match what Feishu expects.`);
|
|
215
|
+
}
|
|
216
|
+
return { success: true, status: packet.status };
|
|
199
217
|
}
|
|
200
218
|
|
|
201
219
|
// --- Send Text Message ---
|
|
@@ -275,18 +293,6 @@ class LarkUserClient {
|
|
|
275
293
|
return this._sendMsg(MsgType.FILE, chatId, { fileKey, fileName }, opts);
|
|
276
294
|
}
|
|
277
295
|
|
|
278
|
-
// --- Send Audio ---
|
|
279
|
-
|
|
280
|
-
async sendAudio(chatId, audioKey, opts = {}) {
|
|
281
|
-
return this._sendMsg(MsgType.AUDIO, chatId, { audioKey }, opts);
|
|
282
|
-
}
|
|
283
|
-
|
|
284
|
-
// --- Send Sticker ---
|
|
285
|
-
|
|
286
|
-
async sendSticker(chatId, stickerId, stickerSetId, opts = {}) {
|
|
287
|
-
return this._sendMsg(MsgType.STICKER, chatId, { stickerId, stickerSetId }, opts);
|
|
288
|
-
}
|
|
289
|
-
|
|
290
296
|
// --- Send Rich Text / POST ---
|
|
291
297
|
|
|
292
298
|
async sendPost(chatId, title, paragraphs, opts = {}) {
|
package/src/doc-blocks.js
CHANGED
|
@@ -1,10 +1,8 @@
|
|
|
1
1
|
// Helpers for constructing docx block payloads.
|
|
2
2
|
//
|
|
3
|
-
//
|
|
4
|
-
// create_doc_block / update_doc_block
|
|
5
|
-
// (
|
|
6
|
-
// → wiki-docx sync feature is built; parking the skeleton here now so the
|
|
7
|
-
// later additions don't introduce a new file.
|
|
3
|
+
// v1.3.4 added image block builders. v1.3.6 adds file block builders so the
|
|
4
|
+
// create_doc_block / update_doc_block tools can attach arbitrary file
|
|
5
|
+
// attachments (PDF / zip / etc.) the same way they handle images.
|
|
8
6
|
|
|
9
7
|
// docx v1 block_type enum (relevant subset).
|
|
10
8
|
// Docs: https://open.feishu.cn/document/server-docs/docs/docs/docx-v1/document-block/create
|
|
@@ -63,8 +61,25 @@ function buildReplaceImagePayload(imageToken) {
|
|
|
63
61
|
return { replace_image: { token: imageToken } };
|
|
64
62
|
}
|
|
65
63
|
|
|
64
|
+
// File-block flow mirrors the image-block flow but uses block_type=23 (FILE)
|
|
65
|
+
// and parent_type=docx_file when uploading the binary, and replace_file in
|
|
66
|
+
// the PATCH body. See official.js::createDocBlockWithFile.
|
|
67
|
+
|
|
68
|
+
/** Empty file block placeholder for step 1 of file attachment flow. */
|
|
69
|
+
function buildEmptyFileBlock() {
|
|
70
|
+
return { block_type: BLOCK_TYPE.FILE, file: {} };
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/** Patch body that swaps an empty file block's content with an uploaded file token. */
|
|
74
|
+
function buildReplaceFilePayload(fileToken) {
|
|
75
|
+
if (!fileToken) throw new Error('buildReplaceFilePayload: fileToken is required');
|
|
76
|
+
return { replace_file: { token: fileToken } };
|
|
77
|
+
}
|
|
78
|
+
|
|
66
79
|
module.exports = {
|
|
67
80
|
BLOCK_TYPE,
|
|
68
81
|
buildEmptyImageBlock,
|
|
69
82
|
buildReplaceImagePayload,
|
|
83
|
+
buildEmptyFileBlock,
|
|
84
|
+
buildReplaceFilePayload,
|
|
70
85
|
};
|