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.
Files changed (56) hide show
  1. package/.claude-plugin/plugin.json +2 -2
  2. package/CHANGELOG.md +22 -0
  3. package/README.md +66 -40
  4. package/package.json +10 -3
  5. package/scripts/check-tool-count.js +15 -0
  6. package/scripts/check-version.js +40 -0
  7. package/scripts/smoke.js +224 -0
  8. package/scripts/sync-claude-md.sh +12 -0
  9. package/scripts/sync-team-skills.sh +22 -0
  10. package/scripts/test-all-tools.js +158 -0
  11. package/skills/feishu-user-plugin/SKILL.md +5 -5
  12. package/skills/feishu-user-plugin/references/CLAUDE.md +152 -96
  13. package/skills/feishu-user-plugin/references/table.md +18 -9
  14. package/src/auth/credentials.js +350 -0
  15. package/src/cli.js +42 -13
  16. package/src/clients/official/base.js +424 -0
  17. package/src/clients/official/bitable.js +269 -0
  18. package/src/clients/official/calendar.js +176 -0
  19. package/src/clients/official/contacts.js +54 -0
  20. package/src/clients/official/docs.js +301 -0
  21. package/src/clients/official/drive.js +77 -0
  22. package/src/clients/official/groups.js +68 -0
  23. package/src/clients/official/im.js +414 -0
  24. package/src/clients/official/index.js +30 -0
  25. package/src/clients/official/okr.js +127 -0
  26. package/src/clients/official/tasks.js +142 -0
  27. package/src/clients/official/uploads.js +260 -0
  28. package/src/clients/official/wiki.js +207 -0
  29. package/src/{client.js → clients/user.js} +23 -17
  30. package/src/doc-blocks.js +20 -5
  31. package/src/index.js +4 -1744
  32. package/src/logger.js +20 -0
  33. package/src/oauth.js +8 -1
  34. package/src/official.js +5 -1734
  35. package/src/prompts/_registry.js +69 -0
  36. package/src/prompts/index.js +54 -0
  37. package/src/server.js +242 -0
  38. package/src/test-all.js +2 -2
  39. package/src/test-comprehensive.js +3 -3
  40. package/src/test-send.js +1 -1
  41. package/src/tools/_registry.js +30 -0
  42. package/src/tools/bitable.js +246 -0
  43. package/src/tools/calendar.js +207 -0
  44. package/src/tools/contacts.js +66 -0
  45. package/src/tools/diagnostics.js +172 -0
  46. package/src/tools/docs.js +158 -0
  47. package/src/tools/drive.js +111 -0
  48. package/src/tools/groups.js +81 -0
  49. package/src/tools/im-read.js +259 -0
  50. package/src/tools/messaging-bot.js +151 -0
  51. package/src/tools/messaging-user.js +292 -0
  52. package/src/tools/okr.js +159 -0
  53. package/src/tools/profile.js +43 -0
  54. package/src/tools/tasks.js +168 -0
  55. package/src/tools/uploads.js +63 -0
  56. package/src/tools/wiki.js +191 -0
@@ -0,0 +1,158 @@
1
+ #!/usr/bin/env node
2
+ // scripts/test-all-tools.js — semi-automated tool regression.
3
+ //
4
+ // Spawns the MCP server (src/index.js) as a stdio child, sends `initialize` +
5
+ // `tools/list`, then calls a curated set of READ tools to verify each domain
6
+ // is wired up. Writes a per-tool pass/fail summary to stdout.
7
+ //
8
+ // Read-only by design: this script does NOT create / modify / delete any
9
+ // Feishu resources. For write-tool regression, see docs/TESTING-METHODOLOGY.md
10
+ // "Live regression checklist".
11
+ //
12
+ // Usage:
13
+ // node scripts/test-all-tools.js
14
+ // node scripts/test-all-tools.js --user-id <open_id> # to also test list_user_okrs
15
+ // node scripts/test-all-tools.js --json # machine-readable output
16
+ //
17
+ // Exit code: 0 if all calls succeed, 1 if any failed.
18
+
19
+ const { spawn } = require('child_process');
20
+ const path = require('path');
21
+ const { readCredentials } = require('../src/config');
22
+
23
+ const SERVER_PATH = path.join(__dirname, '..', 'src', 'index.js');
24
+
25
+ function jsonrpc(id, method, params) {
26
+ return JSON.stringify({ jsonrpc: '2.0', id, method, params }) + '\n';
27
+ }
28
+
29
+ function waitFor(fn, timeoutMs) {
30
+ return new Promise((resolve, reject) => {
31
+ const start = Date.now();
32
+ const tick = () => {
33
+ if (fn()) return resolve();
34
+ if (Date.now() - start > timeoutMs) return reject(new Error(`timeout after ${timeoutMs}ms`));
35
+ setTimeout(tick, 50);
36
+ };
37
+ tick();
38
+ });
39
+ }
40
+
41
+ async function runRegression() {
42
+ const cliArgs = process.argv.slice(2);
43
+ const wantJson = cliArgs.includes('--json');
44
+ const userIdIdx = cliArgs.indexOf('--user-id');
45
+ const userId = userIdIdx >= 0 ? cliArgs[userIdIdx + 1] : null;
46
+
47
+ const creds = readCredentials() || {};
48
+ const childEnv = { ...process.env };
49
+ for (const k of ['LARK_COOKIE', 'LARK_APP_ID', 'LARK_APP_SECRET', 'LARK_USER_ACCESS_TOKEN', 'LARK_USER_REFRESH_TOKEN', 'LARK_PROFILES_JSON']) {
50
+ if (creds[k] && !childEnv[k]) childEnv[k] = creds[k];
51
+ }
52
+
53
+ const child = spawn('node', [SERVER_PATH], {
54
+ stdio: ['pipe', 'pipe', 'pipe'],
55
+ env: childEnv,
56
+ });
57
+ let buf = '';
58
+ const responses = new Map();
59
+ child.stdout.on('data', (d) => {
60
+ buf += d.toString();
61
+ const lines = buf.split('\n');
62
+ buf = lines.pop();
63
+ for (const line of lines) {
64
+ if (!line.trim()) continue;
65
+ try {
66
+ const msg = JSON.parse(line);
67
+ if (msg.id != null) responses.set(msg.id, msg);
68
+ } catch {}
69
+ }
70
+ });
71
+ child.stderr.on('data', () => {});
72
+
73
+ let nextId = 1;
74
+ function call(method, params, timeoutMs = 15000) {
75
+ const id = nextId++;
76
+ child.stdin.write(jsonrpc(id, method, params));
77
+ return waitFor(() => responses.has(id), timeoutMs).then(() => responses.get(id));
78
+ }
79
+
80
+ const init = await call('initialize', {
81
+ protocolVersion: '2024-11-05',
82
+ capabilities: {},
83
+ clientInfo: { name: 'test-all-tools', version: '0' },
84
+ });
85
+ if (init.error) throw new Error(`initialize failed: ${JSON.stringify(init.error)}`);
86
+
87
+ const toolsResp = await call('tools/list', {});
88
+ const allTools = (toolsResp.result?.tools || []).map((t) => t.name).sort();
89
+
90
+ // Curated read-only suite. Each entry: [name, args, optional notes].
91
+ const SUITE = [
92
+ ['get_login_status', {}],
93
+ ['list_profiles', {}],
94
+ ['list_chats', { page_size: 5 }],
95
+ ['search_contacts', { query: 'feishu' }],
96
+ ['list_calendars', { page_size: 50 }],
97
+ ['list_okr_periods', {}],
98
+ ['list_wiki_spaces', {}],
99
+ ['search_docs', { query: 'README' }],
100
+ ['list_files', {}],
101
+ ];
102
+ if (userId) SUITE.push(['list_user_okrs', { user_id: userId, limit: 1 }]);
103
+ // list_tasks (v1.3.7) is safe but only meaningful if Tasks scope is granted.
104
+ if (allTools.includes('list_tasks')) SUITE.push(['list_tasks', { page_size: 1 }]);
105
+
106
+ const results = [];
107
+ for (const [name, args] of SUITE) {
108
+ if (!allTools.includes(name)) {
109
+ results.push({ tool: name, ok: false, skipped: true, reason: 'tool not registered' });
110
+ continue;
111
+ }
112
+ const t0 = Date.now();
113
+ try {
114
+ const r = await call('tools/call', { name, arguments: args }, 30000);
115
+ const ms = Date.now() - t0;
116
+ if (r.error) {
117
+ results.push({ tool: name, ok: false, ms, error: r.error.message });
118
+ } else {
119
+ const isError = r.result?.isError === true;
120
+ results.push({ tool: name, ok: !isError, ms, summary: summarize(r.result) });
121
+ }
122
+ } catch (e) {
123
+ results.push({ tool: name, ok: false, ms: Date.now() - t0, error: e.message });
124
+ }
125
+ }
126
+
127
+ child.kill('SIGTERM');
128
+
129
+ if (wantJson) {
130
+ process.stdout.write(JSON.stringify({ allTools: allTools.length, results }, null, 2) + '\n');
131
+ } else {
132
+ const okCount = results.filter((r) => r.ok).length;
133
+ const failCount = results.filter((r) => !r.ok && !r.skipped).length;
134
+ const skipCount = results.filter((r) => r.skipped).length;
135
+ console.log(`Tool registry size: ${allTools.length}`);
136
+ console.log(`Suite: ${okCount} ok, ${failCount} fail, ${skipCount} skipped (out of ${results.length} planned)\n`);
137
+ for (const r of results) {
138
+ const status = r.skipped ? 'SKIP' : (r.ok ? ' OK ' : 'FAIL');
139
+ const ms = r.ms !== undefined ? ` ${r.ms}ms` : '';
140
+ const tail = r.error ? ` — ${r.error}` : (r.reason ? ` — ${r.reason}` : (r.summary ? ` — ${r.summary}` : ''));
141
+ console.log(` [${status}]${ms.padStart(8)} ${r.tool}${tail}`);
142
+ }
143
+ if (failCount > 0) process.exit(1);
144
+ }
145
+ }
146
+
147
+ function summarize(result) {
148
+ const txt = result?.content?.[0]?.text;
149
+ if (!txt) return '';
150
+ // Crop multi-line summaries to the first line + length.
151
+ const firstLine = txt.split('\n', 1)[0];
152
+ return firstLine.length > 80 ? firstLine.slice(0, 77) + '…' : firstLine;
153
+ }
154
+
155
+ runRegression().catch((e) => {
156
+ console.error('Regression failed:', e.message);
157
+ process.exit(2);
158
+ });
@@ -1,15 +1,15 @@
1
1
  ---
2
2
  name: feishu-user-plugin
3
- version: "1.3.5"
4
- description: "All-in-one Feishu plugin — send messages as yourself, read group/P2P chats (auto-expands merge_forward), manage docs/tables/wiki (with wiki-node + URL transparent resolution), OKR, calendar, docx image read/write. v1.3.5: cross-process UAT refresh lock, fallback warning, download_file for chat attachments."
5
- allowed-tools: send_to_user, send_to_group, send_as_user, send_image_as_user, send_file_as_user, send_post_as_user, send_sticker_as_user, send_audio_as_user, search_contacts, create_p2p_chat, get_chat_info, get_user_info, get_login_status, read_p2p_messages, list_user_chats, list_chats, read_messages, reply_message, forward_message, search_docs, read_doc, create_doc, list_bitable_tables, list_bitable_fields, search_bitable_records, create_bitable_record, update_bitable_record, list_wiki_spaces, search_wiki, list_wiki_nodes, get_wiki_node, list_files, create_folder, find_user, download_image, download_file, list_user_okrs, get_okrs, list_okr_periods, list_calendars, list_calendar_events, get_calendar_event
3
+ version: "1.3.7"
4
+ description: "All-in-one Feishu plugin — send messages as yourself (incl. batch_send), read group/P2P chats (auto-expands merge_forward), manage docs/tables/wiki (full CRUD)/drive, OKR (with progress writes), calendar (read+write), Tasks v2, multi-profile. v1.3.7: tool consolidation (82→80), wiki write, OKR progress writes, calendar write, Tasks v2, oc_xxx auto-resolver for cookie sends."
5
+ allowed-tools: send_to_user, send_to_group, send_as_user, send_image_as_user, send_file_as_user, send_post_as_user, batch_send, send_card_as_user, search_contacts, create_p2p_chat, get_chat_info, get_user_info, get_login_status, list_profiles, switch_profile, read_p2p_messages, list_user_chats, list_chats, read_messages, send_message_as_bot, reply_message, forward_message, delete_message, update_message, add_reaction, delete_reaction, pin_message, create_group, update_group, list_members, manage_members, search_docs, read_doc, get_doc_blocks, create_doc, manage_doc_block, manage_bitable_app, manage_bitable_table, manage_bitable_field, manage_bitable_view, manage_bitable_record, upload_bitable_attachment, list_wiki_spaces, search_wiki, list_wiki_nodes, get_wiki_node, create_wiki_node, update_wiki_node, move_wiki_node, copy_wiki_node, delete_wiki_node, list_files, create_folder, upload_drive_file, manage_drive_file, upload_image, upload_file, download_message_resource, download_doc_image, list_user_okrs, get_okrs, list_okr_periods, create_okr_progress_record, list_okr_progress_records, delete_okr_progress_record, list_calendars, list_calendar_events, get_calendar_event, create_calendar_event, update_calendar_event, delete_calendar_event, respond_calendar_event, get_freebusy, list_tasks, get_task, create_task, update_task, complete_task, delete_task, manage_task_members
6
6
  user_invocable: true
7
7
  ---
8
8
 
9
9
  # Feishu User Plugin
10
10
 
11
11
  All-in-one Feishu plugin for Claude Code with three auth layers:
12
- - **User Identity** (cookie auth): Send messages as yourself — text, image, file, rich text, sticker, audio
12
+ - **User Identity** (cookie auth): Send messages as yourself — text, image, file, rich text (post)
13
13
  - **Official API** (app credentials): Read group messages, docs, tables, wiki, drive, contacts
14
14
  - **User OAuth** (user_access_token): Read P2P (direct message) chat history
15
15
 
@@ -43,7 +43,7 @@ Search users and groups by name, display results grouped by type.
43
43
  Search (search_docs), read (read_doc), create (create_doc) — three in one.
44
44
 
45
45
  ### /table — Bitable operations
46
- Query (list tables → list fields → search records), create records, update records.
46
+ Query (`manage_bitable_table(action=list)``manage_bitable_field(action=list)``manage_bitable_record(action=search)`), create / update / delete records via `manage_bitable_record(action=create|update|delete)`.
47
47
 
48
48
  ### /wiki — Wiki management
49
49
  List spaces (list_wiki_spaces), search content (search_wiki), browse nodes (list_wiki_nodes).