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
|
@@ -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.
|
|
4
|
-
description: "All-in-one Feishu plugin — send messages as yourself, read group/P2P chats (auto-expands merge_forward), manage docs/tables/wiki (
|
|
5
|
-
allowed-tools: send_to_user, send_to_group, send_as_user, send_image_as_user, send_file_as_user, send_post_as_user,
|
|
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
|
|
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
|
|
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).
|