feishu-user-plugin 1.3.10 → 1.3.12
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/.cursor-plugin/plugin.json +27 -0
- package/.mcpb/manifest.json +91 -0
- package/CHANGELOG.md +118 -0
- package/PRIVACY.md +105 -0
- package/README.en.md +130 -413
- package/README.md +88 -258
- package/package.json +5 -3
- package/scripts/build-mcpb.js +119 -0
- package/scripts/check-description-drift.js +73 -0
- package/scripts/check-docs-sync.js +7 -16
- package/scripts/check-mcp-registry-version.js +43 -0
- package/scripts/check-mcpb-version.js +33 -0
- package/scripts/check-scopes.js +99 -0
- package/scripts/check-tool-count.js +4 -3
- package/scripts/check-version.js +5 -0
- package/scripts/sync-claude-md.sh +3 -4
- package/scripts/sync-team-skills.sh +72 -57
- package/scripts/verify-app-name.js +64 -0
- package/skills/feishu-user-plugin/SKILL.md +3 -3
- package/skills/feishu-user-plugin/references/search.md +3 -3
- package/src/auth/credentials-monitor.js +185 -0
- package/src/auth/credentials.js +49 -0
- package/src/auth/identity-state.js +204 -0
- package/src/auth/lark-desktop.js +135 -0
- package/src/auth/uat.js +49 -35
- package/src/cli.js +87 -0
- package/src/clients/official/base.js +145 -14
- package/src/clients/official/calendar.js +3 -1
- package/src/clients/official/im.js +76 -2
- package/src/clients/official/okr.js +2 -1
- package/src/error-codes.js +40 -0
- package/src/events/lockfile.js +40 -4
- package/src/events/owner.js +11 -2
- package/src/index.js +1 -1
- package/src/logger.js +11 -5
- package/src/oauth.js +46 -10
- package/src/server.js +102 -37
- package/src/setup.js +44 -0
- package/src/test-all.js +40 -0
- package/src/test-cli-tool.js +87 -0
- package/src/test-credentials-monitor.js +124 -0
- package/src/test-display-label.js +88 -0
- package/src/test-error-codes.js +85 -0
- package/src/test-identity-state.js +172 -0
- package/src/test-lark-desktop.js +300 -0
- package/src/test-lockfile-pid.js +90 -0
- package/src/test-lru-cache.js +145 -0
- package/src/test-negative-cache.js +85 -0
- package/src/test-populate-sender-names.js +98 -0
- package/src/test-search-messages.js +101 -0
- package/src/test-send-shape.js +115 -0
- package/src/test-via-user.js +94 -0
- package/src/test-with-uat-retry.js +135 -0
- package/src/tools/_registry.js +24 -1
- package/src/tools/calendar.js +5 -5
- package/src/tools/im-read.js +52 -4
- package/src/tools/messaging-user.js +1 -1
- package/src/utils.js +83 -0
- package/scripts/generate-og-image.js +0 -39
- package/skills/feishu-user-plugin/references/CLAUDE.md +0 -523
|
@@ -1,523 +0,0 @@
|
|
|
1
|
-
# feishu-user-plugin — Claude Code Instructions
|
|
2
|
-
|
|
3
|
-
## What This Is
|
|
4
|
-
All-in-one Feishu plugin for Claude Code with three auth layers:
|
|
5
|
-
- **User Identity** (cookie auth): Send messages (text, image, file, post) as yourself
|
|
6
|
-
- **Official API** (app credentials): Read group messages, docs, tables, wiki, drive, contacts, upload files
|
|
7
|
-
- **User OAuth UAT** (user_access_token): Read P2P chat history, list all user's chats
|
|
8
|
-
|
|
9
|
-
## MCP Prompts (v1.3.7)
|
|
10
|
-
|
|
11
|
-
The 9 Claude Code skills are also exposed as MCP prompts (`prompts/list` + `prompts/get`) so Codex, Cursor, OpenClaw, and Windsurf — which cannot load Claude Code skills — get the same guided UX. Prompt bodies are read at server start from `skills/feishu-user-plugin/references/`.
|
|
12
|
-
|
|
13
|
-
| Prompt | Description |
|
|
14
|
-
|--------|-------------|
|
|
15
|
-
| `/send` | Send a message as yourself (non-bot) |
|
|
16
|
-
| `/reply` | Read recent messages and reply |
|
|
17
|
-
| `/digest` | Summarise recent group or P2P messages |
|
|
18
|
-
| `/search` | Search Feishu contacts or groups |
|
|
19
|
-
| `/doc` | Search, read, or create a Feishu document |
|
|
20
|
-
| `/table` | Operate on a Feishu Bitable (multi-dimensional table) |
|
|
21
|
-
| `/wiki` | Search and browse a Feishu Wiki space |
|
|
22
|
-
| `/drive` | List files or create folders in Feishu Drive |
|
|
23
|
-
| `/status` | Check all three auth layers (cookie, app, UAT) |
|
|
24
|
-
|
|
25
|
-
Each prompt accepts a single `arguments` free-form string (mirroring the `$ARGUMENTS` convention used by Claude Code skills). `status` has no arguments.
|
|
26
|
-
|
|
27
|
-
## Tool Categories (84 tools)
|
|
28
|
-
|
|
29
|
-
Per-tool descriptions live in each tool's MCP `inputSchema.description`. This section lists names + cross-domain caveats only.
|
|
30
|
-
|
|
31
|
-
### User Identity — Messaging (cookie protobuf, 8 tools)
|
|
32
|
-
`send_to_user` / `send_to_group` / `send_as_user` / `send_image_as_user` / `send_file_as_user` / `send_post_as_user` / `send_card_as_user` / `batch_send`
|
|
33
|
-
|
|
34
|
-
- All cookie sends auto-resolve `oc_xxx` chat IDs to numeric since v1.3.7 (C1.4: `getChatInfo → search → numeric`, cached).
|
|
35
|
-
- Plain-text sends accept `ats:[{userId,name}]` — the marker `@<name>` must appear in `text`; spliced into a real AT element that triggers notifications.
|
|
36
|
-
- `send_post_as_user` paragraphs accept `{tag:"text"}` / `{tag:"a",href,text}` / `{tag:"at",userId,name}` elements; `at` element triggers a real notification.
|
|
37
|
-
- `send_image_as_user` works as-of v1.3.9 (cookie protobuf reverse-engineered via brute-force probe — see `scripts/explore-image-minimize.js`). Required Content fields: `imageKey` + `thumbnailKey`. Plugin defaults thumbnailKey = imageKey when caller omits it. Optional metadata: width / height / mime / size — all auto-derivable on Feishu's side, no pre-compute needed.
|
|
38
|
-
|
|
39
|
-
### User Identity — Contacts & Info (5 tools)
|
|
40
|
-
`search_contacts` / `create_p2p_chat` / `get_chat_info` / `get_user_info` / `get_login_status`
|
|
41
|
-
|
|
42
|
-
- `get_chat_info` accepts both `oc_xxx` and numeric chat_id (Official API + protobuf fallback).
|
|
43
|
-
|
|
44
|
-
### User OAuth UAT — P2P Chat (2 tools)
|
|
45
|
-
`read_p2p_messages` / `list_user_chats`
|
|
46
|
-
|
|
47
|
-
- `list_user_chats` returns **groups only** (Feishu API limit). For P2P chat list, use `search_contacts` → `create_p2p_chat`.
|
|
48
|
-
- All docx / bitable / drive / wiki / OKR / calendar / tasks create+edit are UAT-first by default — UAT first, bot fallback, with ⚠ warning in response when forced to bot. Resources consistently owned by the caller.
|
|
49
|
-
|
|
50
|
-
### Official API — IM (15 tools)
|
|
51
|
-
`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` / `download_message_resource`
|
|
52
|
-
|
|
53
|
-
- `read_messages` resolves chat name → bot list → `im.chat.search` → cookie `search_contacts`. Auto-falls back to UAT for external groups. `merge_forward` auto-expands; text messages get `urls[]` + `feishuDocs[]` extracted (disable with `expand_merge_forward=false`).
|
|
54
|
-
- `update_message` only supports `msg_type=text|interactive` (Feishu limit; rejected before API call).
|
|
55
|
-
- `forward_message` auto-detects `receive_id_type` from prefix (`ou_`/`on_`/`email`/...).
|
|
56
|
-
- `manage_members` requires `member_id_type` to match the IDs you pass (`open_id` default; pass `union_id`/`user_id` explicitly to avoid 9499).
|
|
57
|
-
- `download_message_resource(kind=image|file)` MUST pass `save_path` when payload > 2 MiB (Anthropic 5 MB inline cap). For `merge_forward` children use `parentMessageId`, not child id.
|
|
58
|
-
|
|
59
|
-
### Official API — Docs (7 tools)
|
|
60
|
-
`search_docs` / `read_doc` / `read_doc_markdown` / `get_doc_blocks` / `create_doc` / `manage_doc_block` / `download_doc_image`
|
|
61
|
-
|
|
62
|
-
- `read_doc_markdown` returns a markdown string instead of structured JSON — saves ~60% tokens for RAG / digest / summarisation. Embedded images / files surface as `feishu://image_token/<TOKEN>` / `feishu://file_token/<TOKEN>` placeholders; pair with `download_doc_image` for binaries. `document_id` accepts native token / wiki node / Feishu URL same as the others.
|
|
63
|
-
- `manage_doc_block(action=create)` has image (`image_path`/`image_token`) and file (`file_path`/`file_token`) shortcuts; FILE blocks (block_type=23) are auto-wrapped in VIEW container (block_type=33), plugin walks into the inner file block before `replace_file` PATCH.
|
|
64
|
-
- `download_doc_image` same 2 MiB cap as `download_message_resource`.
|
|
65
|
-
- All `document_id` / `app_token` accept native token / wiki node token / full Feishu URL (resolved via `getWikiNode`, 10 min cache).
|
|
66
|
-
|
|
67
|
-
### Official API — Bitable (5 tools, v1.3.7 consolidation)
|
|
68
|
-
`manage_bitable_app(action=create|copy|get_meta)` / `manage_bitable_table` / `manage_bitable_field` / `manage_bitable_view` / `manage_bitable_record` / `upload_bitable_attachment`
|
|
69
|
-
|
|
70
|
-
- `manage_bitable_field(action=update)` requires `type` even when only renaming (Feishu API limit).
|
|
71
|
-
- `manage_bitable_record` create/update/delete accept arrays (single or up to 500).
|
|
72
|
-
- `manage_bitable_app(action=create)` accepts optional `wiki_space_id` (+ `wiki_parent_node_token`) for direct Wiki placement.
|
|
73
|
-
- `upload_bitable_attachment` returns `file_token` → write into Attachment field via `manage_bitable_record(action=create|update, records=[{fields:{<field>:[{file_token:"..."}]}}])`.
|
|
74
|
-
|
|
75
|
-
### Official API — Wiki (9 tools)
|
|
76
|
-
`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`
|
|
77
|
-
|
|
78
|
-
- `list_wiki_spaces` / `list_wiki_nodes` are UAT-first; bot path returns `scopeHint` when empty (typically `wiki:wiki:readonly` missing).
|
|
79
|
-
- `get_wiki_node` accepts both wiki node tokens AND underlying `obj_token`s from `search_wiki` (synthesizes node-shape).
|
|
80
|
-
- `update_wiki_node` only patches `title` (Feishu wiki API doesn't take content edits — those go through docx/bitable/sheet tools).
|
|
81
|
-
- `delete_wiki_node` only removes the Wiki node pointer; underlying drive resource needs separate `manage_drive_file(action=delete)`.
|
|
82
|
-
|
|
83
|
-
### Official API — Drive (5 tools)
|
|
84
|
-
`list_files` / `create_folder` / `manage_drive_file(action=copy|move|delete)` / `upload_image` / `upload_file` / `upload_drive_file`
|
|
85
|
-
|
|
86
|
-
- `manage_drive_file` requires `type` (`file/folder/docx/sheet/bitable/mindnote/slides`) — Feishu rejects with 1061002 / 1062501 otherwise.
|
|
87
|
-
- `upload_drive_file` with `wiki_space_id` calls `attachToWiki(obj_type=file)` to place the upload as a Wiki node atomically.
|
|
88
|
-
|
|
89
|
-
### Official API — OKR (6 tools)
|
|
90
|
-
`list_user_okrs` / `get_okrs` / `list_okr_periods` / `create_okr_progress_record` / `list_okr_progress_records` / `delete_okr_progress_record`
|
|
91
|
-
|
|
92
|
-
- Writes need `okr:okr.content:write` scope.
|
|
93
|
-
- `list_okr_progress_records` extracts triples from `get_okrs` (Feishu has no native list endpoint).
|
|
94
|
-
- OKR objective/key-result CRUD doesn't exist in Feishu's open API.
|
|
95
|
-
|
|
96
|
-
### Official API — Calendar (8 tools)
|
|
97
|
-
`list_calendars` / `list_calendar_events` / `get_calendar_event` / `create_calendar_event` / `update_calendar_event` / `delete_calendar_event` / `respond_calendar_event` / `get_freebusy`
|
|
98
|
-
|
|
99
|
-
- Writes need `calendar:calendar.event:write` scope.
|
|
100
|
-
- UAT-first for read (primary + shared + subscribed); bot only sees calendars it was explicitly invited to.
|
|
101
|
-
|
|
102
|
-
### Official API — Tasks v2 (7 tools, v1.3.7 new domain)
|
|
103
|
-
`list_tasks` / `get_task` / `create_task` / `update_task` / `complete_task` / `delete_task` / `manage_task_members`
|
|
104
|
-
|
|
105
|
-
- Identifier is `task_guid`, not v1 numeric `task_id`.
|
|
106
|
-
- `update_task` requires explicit `update_fields=["summary","due","completed_at",...]` array — Feishu only patches listed fields.
|
|
107
|
-
- Needs `task:task` scope.
|
|
108
|
-
|
|
109
|
-
### Plugin — Diagnostics & Profiles (4 tools)
|
|
110
|
-
`get_login_status` / `list_profiles` / `switch_profile` / `manage_profile_hints`
|
|
111
|
-
|
|
112
|
-
- `switch_profile` invalidates cached client instances; next call rebuilds against the new profile. Multi-profile registered via `LARK_PROFILES_JSON` env or `credentials.json` profiles map.
|
|
113
|
-
- `manage_profile_hints(action=list|set|clear, resource_key?, profile?)` (v1.3.8) inspects / edits the resourceKey → profile cache the auto-switch middleware uses. No-op when credentials.json doesn't exist.
|
|
114
|
-
|
|
115
|
-
### Plugin — Realtime Events (2 tools, v1.3.9)
|
|
116
|
-
`get_new_events` / `manage_ws_status`
|
|
117
|
-
|
|
118
|
-
- **v1.3.9 machine-level**: a single MCP process per machine owns the WS via `~/.feishu-user-plugin/ws-owner.lock`. Events written to `~/.feishu-user-plugin/events.jsonl` (append-only, 10 MB soft / 20 MB hard cap). All harnesses read through a shared `events.cursor.json` — **every event delivered exactly once across all MCP processes**, no more duplicates.
|
|
119
|
-
- WS connection started at MCP boot when APP_ID + APP_SECRET configured. Connects to feishu.cn — Lark international not supported.
|
|
120
|
-
- Default subscriptions = `["im.message.receive_v1"]`. Edit `credentials.json::profiles[<active>].events` to add others (`approval.instance.created_v4`, `calendar.calendar.event.changed_v4`, etc.); call `manage_ws_status(action=reconfig)` to apply without restart.
|
|
121
|
-
- `get_new_events` filter by `event_type` / `event_types` / `chat_id` / `since_seconds` / `profile`. `peek=true` keeps cursor unchanged. **Default `profile` filter = current active**.
|
|
122
|
-
- `manage_ws_status(action=info|reconnect|claim|rotate|reconfig)` — diagnose / control the WS owner. `claim --force` steals an active lock; `rotate` forces events.jsonl rotation.
|
|
123
|
-
|
|
124
|
-
## Usage Patterns
|
|
125
|
-
|
|
126
|
-
### Wiki-hosted content (docx / bitable / sheet)
|
|
127
|
-
All docx and bitable tools now accept three input forms for their `document_id` / `app_token` parameter:
|
|
128
|
-
- Native token (unchanged): `doccnXXX`, `docxXXX`, `bascnXXX`, ...
|
|
129
|
-
- Wiki node token: `wikcnXXX`, `wikmXXX`, `wiknXXX`
|
|
130
|
-
- Full Feishu URL: `https://xxx.feishu.cn/docx/XXX`, `.../wiki/XXX`, `.../base/XXX`
|
|
131
|
-
The plugin resolves wiki nodes to their underlying `obj_token` via `getWikiNode`, then calls the normal docx / bitable endpoint. Results are cached for 10 min to avoid repeated node lookups.
|
|
132
|
-
|
|
133
|
-
Create content directly into a Wiki space:
|
|
134
|
-
- `create_doc` / `manage_bitable_app(action=create)` accept optional `wiki_space_id` (+ `wiki_parent_node_token`). The plugin creates the resource in drive, then calls `wiki/v2/spaces/{space_id}/nodes/move_docs_to_wiki` to attach it — returns `wikiNodeToken` on immediate success, or `wikiAttachTaskId` if Feishu queues the move.
|
|
135
|
-
|
|
136
|
-
### Document images
|
|
137
|
-
Read — `download_doc_image(image_token, doc_token?, save_path?)` returns the image as MCP image content (base64 + mimeType). `doc_token` accepts native id / wiki node / URL. Force `save_path` when image > 2 MiB.
|
|
138
|
-
Write — `manage_doc_block(action=create)` has image shortcuts:
|
|
139
|
-
- `image_path` (absolute local file path) → plugin creates an image block, uploads the pixels via `drive/v1/medias/upload_all`, and patches the block with the uploaded token.
|
|
140
|
-
- `image_token` (already uploaded) → plugin creates block and attaches token.
|
|
141
|
-
`manage_doc_block(action=update, image_token=...)` swaps the picture in an existing image block.
|
|
142
|
-
|
|
143
|
-
### OKR
|
|
144
|
-
1. `list_okr_periods` — find the period id for current quarter.
|
|
145
|
-
2. `list_user_okrs(user_id=<open_id>, period_ids=[...])` — list the target user's OKRs.
|
|
146
|
-
3. `get_okrs(okr_ids)` — batch fetch full objective + key result structure with progress + alignments.
|
|
147
|
-
`user_id` is required — use your own open_id (from `get_login_status` / `search_contacts`) to read your own OKRs, or a colleague's open_id for theirs (subject to permissions).
|
|
148
|
-
|
|
149
|
-
Write (v1.3.7, requires `okr:okr.content:write` scope):
|
|
150
|
-
4. `create_okr_progress_record(target_id, target_type=1|2, content_text, source_title?, source_url?, progress_percent?)` — `target_type` is 1 for objectives, 2 for key results. `content_text` is auto-wrapped into Feishu's required block format; pass `content` directly for richer payloads (lists, mentions, docs links, gallery).
|
|
151
|
-
5. `list_okr_progress_records(okr_id)` — extracts `{progress_id, target_id, target_type}` triples from `get_okrs` (Feishu has no native list endpoint).
|
|
152
|
-
6. `delete_okr_progress_record(progress_id)`.
|
|
153
|
-
|
|
154
|
-
### Calendar
|
|
155
|
-
1. `list_calendars` — get your calendars; the one with `type=primary` is your personal calendar.
|
|
156
|
-
2. `list_calendar_events(calendar_id, start_time=<unix_sec>, end_time=<unix_sec>)` — list events in a time window.
|
|
157
|
-
3. `get_calendar_event(calendar_id, event_id)` — full details (attendees, location, attachments, meeting link).
|
|
158
|
-
4. `create_calendar_event(calendar_id, summary, start_time, end_time, ...)` — `start_time` / `end_time` are objects: `{timestamp:"<unix-seconds>", timezone?:"Asia/Shanghai"}` or `{date:"YYYY-MM-DD"}` for all-day. v1.3.7+ requires `calendar:calendar.event:write` scope.
|
|
159
|
-
5. `update_calendar_event(calendar_id, event_id, ...patch)` — pass only the fields to change.
|
|
160
|
-
6. `delete_calendar_event(calendar_id, event_id, need_notification?)` — pass `meeting_chat_id` to also dissolve the linked meeting chat if any.
|
|
161
|
-
7. `respond_calendar_event(calendar_id, event_id, rsvp_status=accept|decline|tentative)` — RSVP as the current UAT identity.
|
|
162
|
-
8. `get_freebusy(time_min, time_max, user_ids=[...])` — freebusy windows in RFC3339; useful for finding meeting slots.
|
|
163
|
-
|
|
164
|
-
### Tasks (v2, v1.3.7)
|
|
165
|
-
Whole new domain. Identifier is `task_guid` (not numeric task_id like v1). Requires `task:task` scope.
|
|
166
|
-
1. `list_tasks(completed?, type?)` — current user's tasks, paginated.
|
|
167
|
-
2. `get_task(task_guid)` — full details.
|
|
168
|
-
3. `create_task(summary, due?, members?, ...)` — at minimum `summary`; `due` is `{timestamp:"<unix-millis>", is_all_day?}`.
|
|
169
|
-
4. `update_task(task_guid, update_fields=["summary","due","completed_at"], task={...})` — Feishu only patches the listed fields.
|
|
170
|
-
5. `complete_task(task_guid, completed=true|false)` — convenience for the completed_at toggle.
|
|
171
|
-
6. `delete_task(task_guid)`.
|
|
172
|
-
7. `manage_task_members(action=add|remove, task_guid, members=[{id,role:"assignee"|"follower",type?:"user",name?}])`.
|
|
173
|
-
|
|
174
|
-
### External-group message read
|
|
175
|
-
`read_messages` / `read_p2p_messages` expose a `via` field (`"bot"`/`"user"`/`"contacts"`). On known bot failures (external tenant / no permission / not in chat) the plugin hops straight to UAT; transient errors (rate limit / 5xx / ECONNRESET / timeout) retry once with 2 s delay before falling back. Without UAT, the error points to `npx feishu-user-plugin oauth`.
|
|
176
|
-
|
|
177
|
-
### Multi-profile auto-switch (v1.3.8)
|
|
178
|
-
For users with ≥2 profiles in `~/.feishu-user-plugin/credentials.json`. Read-only tools (`read_*` / `list_*` / `get_*` / `search_*` / `download_*`) auto-retry across profiles on `91403 / 1254301 / 1254000 / 99991672 / HTTP 403`. Writes never auto-switch.
|
|
179
|
-
|
|
180
|
-
Override per call with `via_profile: "<name>"` to pin, or `via_profile: "auto"` to allow auto-switch on a write. Hints persist in `credentials.json::profileHints` and are inspectable via `manage_profile_hints`.
|
|
181
|
-
|
|
182
|
-
v1.3.9: `FEISHU_PLUGIN_PROFILE` env is bootstrap-only — `credentials.json::active` is authoritative once the file exists. Cross-process sync via dispatcher mtime check (~10μs/call).
|
|
183
|
-
|
|
184
|
-
### Multi-profile registration
|
|
185
|
-
For more profiles beyond the default, set `LARK_PROFILES_JSON` in the MCP env (or use `credentials.json` profiles map):
|
|
186
|
-
```json
|
|
187
|
-
{"alt": {"LARK_COOKIE":"...","LARK_APP_ID":"...","LARK_APP_SECRET":"...","LARK_USER_ACCESS_TOKEN":"...","LARK_USER_REFRESH_TOKEN":"..."}}
|
|
188
|
-
```
|
|
189
|
-
|
|
190
|
-
## Auth & Session
|
|
191
|
-
- **LARK_COOKIE**: Required for user identity tools. Session auto-refreshed every 4h via heartbeat and persisted to credentials store.
|
|
192
|
-
- **LARK_APP_ID + LARK_APP_SECRET**: Required for official API tools.
|
|
193
|
-
- **LARK_USER_ACCESS_TOKEN + LARK_USER_REFRESH_TOKEN**: Required for P2P reading. Auto-refreshed on expiry (error codes 99991668/99991663/99991677). Token auto-persisted to credentials store on refresh.
|
|
194
|
-
- Cookie expiry: sl_session has 12h max-age, auto-refreshed by heartbeat every 4h.
|
|
195
|
-
- UAT expiry: 2h, auto-refreshed via refresh_token.
|
|
196
|
-
- Refresh token expiry: 7 days. Use `keepalive` cron to prevent expiration.
|
|
197
|
-
- `~/.feishu-user-plugin/ws-owner.lock`: lock file owned by the one MCP process driving the WS connection (O_CREAT|O_EXCL, 30 s stale).
|
|
198
|
-
- `~/.feishu-user-plugin/events.jsonl`: append-only event log written by the WS owner; 10 MB soft / 20 MB hard cap then rotated to `events.jsonl.old`.
|
|
199
|
-
- `~/.feishu-user-plugin/events.cursor.json`: global drain cursor shared across all MCP processes — advancing it marks events as consumed for all harnesses on the machine.
|
|
200
|
-
|
|
201
|
-
### Credentials store (v1.3.7+)
|
|
202
|
-
Single source of truth at `~/.feishu-user-plugin/credentials.json` (mode 0600). Schema documented at `docs/CREDENTIALS-FORMAT.md`. The MCP server reads from this file when present; cookie heartbeat and UAT refresh persist back to it atomically. Multiple harnesses (Claude Code, Codex) sharing the same file see token rotations consistently — no more "Codex still has the old UAT" drift after a refresh in Claude Code.
|
|
203
|
-
|
|
204
|
-
Opt-in migration:
|
|
205
|
-
```bash
|
|
206
|
-
npx feishu-user-plugin migrate # dry-run (default) — prints what would be written
|
|
207
|
-
npx feishu-user-plugin migrate --confirm # writes credentials.json
|
|
208
|
-
```
|
|
209
|
-
After migration the harness env blocks remain as backward-compat fallback. Delete `~/.feishu-user-plugin/credentials.json` to revert to legacy behaviour.
|
|
210
|
-
|
|
211
|
-
Backward compat: v1.3.6 users without credentials.json see zero behaviour change. The credentials file is preferred only when it exists. The MCP server's `Auth:` startup line on stderr now shows the source (`credentials.json profile=default` vs `env vars (legacy)`) so you can tell at a glance which path is active.
|
|
212
|
-
|
|
213
|
-
## Required Environment Variables (ALL are required for full functionality)
|
|
214
|
-
|
|
215
|
-
| Variable | Purpose |
|
|
216
|
-
|----------|---------|
|
|
217
|
-
| LARK_COOKIE | User identity messaging |
|
|
218
|
-
| LARK_APP_ID | Official API access |
|
|
219
|
-
| LARK_APP_SECRET | Official API access |
|
|
220
|
-
| LARK_USER_ACCESS_TOKEN | P2P chat reading |
|
|
221
|
-
| LARK_USER_REFRESH_TOKEN | UAT auto-refresh |
|
|
222
|
-
|
|
223
|
-
All 5 must be configured. Without UAT, `read_p2p_messages` and `list_user_chats` will not work.
|
|
224
|
-
|
|
225
|
-
## Installation
|
|
226
|
-
|
|
227
|
-
### Config location
|
|
228
|
-
|
|
229
|
-
Credentials are stored in `~/.claude.json` top-level `mcpServers` (global — works in all directories).
|
|
230
|
-
**Do NOT put credentials in project-level config** (`projects[*].mcpServers` or `.mcp.json`) — this causes scope issues.
|
|
231
|
-
|
|
232
|
-
### Non-interactive setup (for Claude Code agents)
|
|
233
|
-
|
|
234
|
-
```bash
|
|
235
|
-
npx feishu-user-plugin setup --app-id <APP_ID> --app-secret <APP_SECRET>
|
|
236
|
-
```
|
|
237
|
-
|
|
238
|
-
Writes config to `~/.claude.json` top-level `mcpServers` without any interactive prompts. Supports `--cookie` flag too.
|
|
239
|
-
|
|
240
|
-
### Interactive setup
|
|
241
|
-
|
|
242
|
-
```bash
|
|
243
|
-
npx feishu-user-plugin setup # Interactive setup wizard
|
|
244
|
-
npx feishu-user-plugin oauth # Get OAuth UAT tokens
|
|
245
|
-
npx feishu-user-plugin status # Check auth status
|
|
246
|
-
npx feishu-user-plugin keepalive # Refresh cookie + UAT (for cron jobs)
|
|
247
|
-
```
|
|
248
|
-
|
|
249
|
-
### Token auto-renewal via cron (optional)
|
|
250
|
-
|
|
251
|
-
To keep tokens alive even when Claude Code is closed:
|
|
252
|
-
|
|
253
|
-
```bash
|
|
254
|
-
crontab -e
|
|
255
|
-
# Add: 0 */4 * * * npx feishu-user-plugin keepalive >> /tmp/feishu-keepalive.log 2>&1
|
|
256
|
-
```
|
|
257
|
-
|
|
258
|
-
## Automated Cookie Setup via Playwright
|
|
259
|
-
|
|
260
|
-
Prerequisite: Playwright MCP installed (`npx @anthropic-ai/claude-code mcp add playwright -- npx @anthropic-ai/mcp-server-playwright` then restart).
|
|
261
|
-
|
|
262
|
-
Procedure (three gotchas embedded — skip any and you'll fail):
|
|
263
|
-
|
|
264
|
-
1. **Clear cookies first.** Playwright MCP uses Edge's persistent profile and may have a cached login from a different account. Run `browser_run_code: await context.clearCookies();` then `browser_navigate: https://www.feishu.cn/messenger/`.
|
|
265
|
-
2. **Wait for QR scan.** `browser_take_screenshot` to show the code; tell user to scan with Feishu mobile (and verify which account). Poll `browser_snapshot` until URL leaves `/accounts/`.
|
|
266
|
-
3. **Two-step cookie extraction.** `browser_run_code` output contains markdown prefix + console logs that contaminate the cookie string. Stash via `page.evaluate(s => { window.__COOKIE__ = s; }, str)` then read clean via `browser_evaluate: window.__COOKIE__`.
|
|
267
|
-
4. **Validate before writing.** Cookie must be pure ASCII (no Chinese, no `###`), contain `session=` AND `sl_session=`, length 500–5000 chars. If > 10000 it's contaminated — STOP, do not write.
|
|
268
|
-
5. **Write to config.** Use `persistToConfig` or update `~/.claude.json` → `mcpServers.feishu-user-plugin.env.LARK_COOKIE`.
|
|
269
|
-
6. **OAuth for UAT.** `npx feishu-user-plugin oauth` (browser consent flow, auto-saves tokens).
|
|
270
|
-
7. **`browser_close` + tell user to restart.** One restart is enough.
|
|
271
|
-
|
|
272
|
-
## Troubleshooting Guide
|
|
273
|
-
|
|
274
|
-
### Official API returns 401 / "token invalid" every time
|
|
275
|
-
`LARK_APP_ID` is wrong or stale (most common: agent guessed/copied an unrelated APP_ID at install time). `get_login_status` reports `App credentials: INVALID — app_id=<x> rejected by Feishu`; MCP stderr logs `LARK_APP_ID=<x> was REJECTED`. **Fix**: re-run the canonical install prompt from `team-skills/plugins/feishu-user-plugin/README.md` (correct APP_ID + SECRET), restart.
|
|
276
|
-
|
|
277
|
-
### MCP tools not available
|
|
278
|
-
1. Config must be in **top-level** `~/.claude.json` `mcpServers`, NOT under `projects[*]`. For Codex: `~/.codex/config.toml` has `[mcp_servers.feishu-user-plugin]`.
|
|
279
|
-
2. Restart after config changes; first call may briefly say "No such tool" while tools register — retry once.
|
|
280
|
-
|
|
281
|
-
### Cookie authentication fails
|
|
282
|
-
- Browser-console `document.cookie` cannot access HttpOnly cookies (`session`, `sl_session`). Use DevTools Network tab → first request → Request Headers → Cookie. Or use Playwright two-step extraction (see above).
|
|
283
|
-
- Playwright logs into the wrong account: ALWAYS `context.clearCookies()` before navigating.
|
|
284
|
-
|
|
285
|
-
### `read_messages` returns an error
|
|
286
|
-
Error includes Feishu's actual code + description. Auto-falls back to UAT for external groups. Chat name resolution: bot's group list → `im.chat.search` → cookie `search_contacts`. If all three fail, pass `oc_xxx` or numeric ID directly.
|
|
287
|
-
|
|
288
|
-
### UAT refresh fails with `invalid_grant`
|
|
289
|
-
Refresh token expired or revoked — auto-refresh cannot recover. **Fix**: `npx feishu-user-plugin oauth`, then restart Claude Code / Codex so running MCP processes load the new token.
|
|
290
|
-
|
|
291
|
-
v1.3.5+ hardening means the "6 MCP processes racing on UAT refresh and burning the token" case is fixed automatically:
|
|
292
|
-
- Cross-process file lock at `~/.claude/feishu-uat-refresh.lock` (`O_CREAT|O_EXCL`, 30 s stale)
|
|
293
|
-
- Lock holder re-reads persisted config inside the critical section, adopts a peer's fresh token if one was rotated
|
|
294
|
-
- `get_login_status` does a real UAT health check (`listChatsAsUser({pageSize:1})`) — no more "configured but actually 401" surprises
|
|
295
|
-
|
|
296
|
-
### Multiple / duplicate MCP server processes
|
|
297
|
-
Codex + Claude Code both can respawn the server per tool session without cleanup; 6 concurrent processes isn't unusual. v1.3.5 neutralises the damage (file lock above) but stale processes still hold memory. **Manual cleanup when you notice**: `pkill -f 'feishu-user-plugin/src/index.js'`. Also: a team-skills plugin must NOT ship `.mcp.json` — if both `~/.claude.json` and team-skills register the same MCP, you get duplicates; delete `.mcp.json` from the team-skills plugin dir.
|
|
298
|
-
|
|
299
|
-
v1.3.9: events are now machine-level; each event delivered exactly once across all MCP processes. Old per-process duplicates issue resolved.
|
|
300
|
-
|
|
301
|
-
### `create_*` tool warns "UAT failed, created as BOT"
|
|
302
|
-
UAT is failing (expired / scope missing / race), so the plugin fell back to bot. Resource is now owned by the shared bot, tenant-readable. **Fix**: `npx feishu-user-plugin oauth`, restart, delete the bot-owned copy and recreate.
|
|
303
|
-
|
|
304
|
-
### OAuth CLI fails with "Missing LARK_APP_ID"
|
|
305
|
-
`oauth.js` reads from `~/.claude.json` MCP config (not `.env`). Run `npx feishu-user-plugin setup` first.
|
|
306
|
-
|
|
307
|
-
### `list_user_chats` doesn't return P2P chats
|
|
308
|
-
Expected — Feishu API only returns groups. P2P flow: `search_contacts` → `create_p2p_chat` → `read_p2p_messages`.
|
|
309
|
-
|
|
310
|
-
### Realtime events (`get_new_events`) returns empty / `Realtime events are not available`
|
|
311
|
-
- **APP_ID/SECRET not configured**: `get_login_status` will show this. Fix: re-run setup.
|
|
312
|
-
- **Feishu WS handshake failed**: check server stderr for `WS start failed` — common reasons:
|
|
313
|
-
- Lark international tenant (lark.com) — not supported by Feishu's WSClient. No fix; use polling tools (`read_messages`) instead.
|
|
314
|
-
- Network restriction — corporate proxy blocking outbound WSS.
|
|
315
|
-
- **Bot not in the chat where the message was sent**: `im.message.receive_v1` only fires for chats the bot is a member of. Add the bot to the chat to receive events.
|
|
316
|
-
- **Multiple MCP processes**: v1.3.9: events are now machine-level; each event delivered exactly once across all MCP processes. Old per-process duplicates issue resolved.
|
|
317
|
-
|
|
318
|
-
## Architecture
|
|
319
|
-
|
|
320
|
-
### Two distribution channels
|
|
321
|
-
- **npm package** (`npx feishu-user-plugin`): MCP server code + skills + CLAUDE.md. For external users.
|
|
322
|
-
- **team-skills plugin**: Skills + CLAUDE.md only (no .mcp.json). For internal team members.
|
|
323
|
-
|
|
324
|
-
### Config management
|
|
325
|
-
- `src/config.js`: Unified config module. Discovers config in `~/.claude.json` (top-level + project-level), `.mcp.json`, and `~/.codex/config.toml`.
|
|
326
|
-
- `setup` writes to `~/.claude.json` (default) or `~/.codex/config.toml` (with `--client codex`), or both (`--client both`).
|
|
327
|
-
- `persistToConfig()` finds the correct config entry and writes back atomically (used by heartbeat + UAT refresh).
|
|
328
|
-
- All config writes use atomic write (tmp file + rename) to prevent race conditions with Claude Code.
|
|
329
|
-
|
|
330
|
-
### Multi-client support
|
|
331
|
-
- **Claude Code**: JSON config in `~/.claude.json` mcpServers
|
|
332
|
-
- **Codex**: TOML config in `~/.codex/config.toml` mcp_servers
|
|
333
|
-
- Setup: `npx feishu-user-plugin setup --client codex` or `--client both`
|
|
334
|
-
- MCP server code is identical for both clients — only config format differs
|
|
335
|
-
- Codex does not support Claude Code slash commands (skills) — only MCP tools are available
|
|
336
|
-
|
|
337
|
-
## Development & Publishing
|
|
338
|
-
|
|
339
|
-
### Publishing to npm
|
|
340
|
-
|
|
341
|
-
```bash
|
|
342
|
-
# 1. Update version in package.json
|
|
343
|
-
# 2. Commit and tag
|
|
344
|
-
git add -A && git commit -m "v1.2.1: description"
|
|
345
|
-
git tag v1.2.1
|
|
346
|
-
git push && git push --tags
|
|
347
|
-
# 3. GitHub Actions auto-publishes to npm on tag push
|
|
348
|
-
```
|
|
349
|
-
|
|
350
|
-
GitHub Actions workflow (`.github/workflows/publish.yml`) auto-publishes on `v*` tags.
|
|
351
|
-
NPM_TOKEN is stored as a GitHub repo secret.
|
|
352
|
-
|
|
353
|
-
### Syncing to team-skills
|
|
354
|
-
|
|
355
|
-
**IMPORTANT: team-skills 仓库禁止直接推送 main。所有变更必须走 PR。**
|
|
356
|
-
|
|
357
|
-
What is automatic now (Phase B3 hooks):
|
|
358
|
-
- **pre-commit (this repo)**: any change to `CLAUDE.md` auto-syncs `AGENTS.md` + `skills/feishu-user-plugin/references/CLAUDE.md` (script: `scripts/sync-claude-md.sh`).
|
|
359
|
-
- **post-merge (this repo, on main)**: copies `skills/` + `.claude-plugin/plugin.json` into `team-skills/plugins/feishu-user-plugin/`, creates `sync/feishu-v<version>` branch, opens a PR with `--auto --merge` (script: `scripts/sync-team-skills.sh`).
|
|
360
|
-
|
|
361
|
-
What still needs a manual touch in team-skills:
|
|
362
|
-
- `README.md` — team-skills has its own README (with team-shared APP_ID/SECRET hardcoded). Tool count, changelog, install prompt all need hand edits.
|
|
363
|
-
- `skills/feishu-user-plugin/SKILL.md` — version + `allowed-tools` list.
|
|
364
|
-
|
|
365
|
-
team-skills PR 流程:
|
|
366
|
-
1. 创建 branch: `git checkout -b sync/feishu-v1.x.x` 或 `fix/feishu-xxx`
|
|
367
|
-
2. push branch + `gh pr create` + `gh pr merge <number> --auto --merge`
|
|
368
|
-
3. CI (`validate.yml`) checks the three-way version triangle (`plugin.json` / `SKILL.md` / first `### vX.Y.Z` in README) — must match or CI fails.
|
|
369
|
-
4. If CI fails: fix + push to same branch, CI re-runs, auto-merge proceeds.
|
|
370
|
-
|
|
371
|
-
Manual sync fallback (hook failed / dry-run / first-time):
|
|
372
|
-
```bash
|
|
373
|
-
# CLAUDE.md → AGENTS.md + skill ref now handled by pre-commit hook
|
|
374
|
-
cp -r skills/. /Users/abble/team-skills/plugins/feishu-user-plugin/skills/
|
|
375
|
-
cp .claude-plugin/plugin.json /Users/abble/team-skills/plugins/feishu-user-plugin/.claude-plugin/
|
|
376
|
-
# Do NOT copy .mcp.json — team-skills plugin should not have one
|
|
377
|
-
```
|
|
378
|
-
|
|
379
|
-
## Development Workflow
|
|
380
|
-
|
|
381
|
-
### Keeping all docs in sync
|
|
382
|
-
|
|
383
|
-
When making ANY code change (new tools, bug fixes, features), update these in this repo:
|
|
384
|
-
- `CLAUDE.md` — tool count, tool list, usage patterns, known limitations
|
|
385
|
-
- `README.md` — tool count badge + heading + tool table, feature highlights, OpenClaw/Claude Code config examples
|
|
386
|
-
- `ROADMAP.md` — check off completed items, add new findings
|
|
387
|
-
- `package.json` — version + description (tool count). All three of `package.json`, `.claude-plugin/plugin.json`, and `skills/feishu-user-plugin/SKILL.md` must agree on version (CI enforces).
|
|
388
|
-
- `prompts/openclaw-setup.md` — only if OpenClaw config changed
|
|
389
|
-
|
|
390
|
-
`AGENTS.md` (Codex) and `skills/feishu-user-plugin/references/CLAUDE.md` are auto-derived from `CLAUDE.md` by the pre-commit hook — do **not** edit them by hand.
|
|
391
|
-
|
|
392
|
-
For team-skills repo: see [Syncing to team-skills](#syncing-to-team-skills) above. Bottom line: `skills/` + `plugin.json` auto-sync via post-merge hook; team-skills README + SKILL.md still need manual edits per release.
|
|
393
|
-
|
|
394
|
-
### Keeping ROADMAP.md up to date
|
|
395
|
-
ROADMAP.md is **forward-only** (open `[ ]` tasks for v1.3.8 / v1.4 candidates only). CHANGELOG.md owns the history of completed work. When you finish a task, **delete the line** — don't move it or check it off. When you discover new bugs / feature ideas, add to the matching section (A–I or v1.4). When you research a direction and rule it out, add to "已调研但暂不实施" with the reasoning.
|
|
396
|
-
|
|
397
|
-
### When adding new tools (post-v1.3.7 layout)
|
|
398
|
-
1. Add the underlying API method to the right domain file:
|
|
399
|
-
- Official API → `src/clients/official/<domain>.js` (im, docs, bitable, drive, wiki, calendar, okr, uploads, contacts, groups). Cross-domain helpers stay in `src/clients/official/base.js`.
|
|
400
|
-
- Cookie identity → `src/clients/user.js`.
|
|
401
|
-
2. Add the MCP tool schema + handler to `src/tools/<domain>.js`. Each module exports `{ schemas: [...], handlers: { [name]: async (args, ctx) => MCPResponse } }` — see existing tools for the pattern. Handlers receive `ctx` (factories, profile state, resolveDocId — see `src/tools/_registry.js` docstring).
|
|
402
|
-
3. If the new file is a brand-new domain (rare), also append it to the `TOOL_MODULES` list in `src/server.js`.
|
|
403
|
-
4. Run smoke: `npm run smoke:baseline` to update the baseline (only when adding/removing/renaming tools is intentional), then `npm run smoke` to verify no other regression. For pure body changes (no schema delta) just `npm run smoke` should pass against the existing baseline.
|
|
404
|
-
5. `node -c` lint each touched file.
|
|
405
|
-
6. Update this file (CLAUDE.md) — tool count, tool list, usage patterns. See `docs/REFACTOR-NOTES.md` for the file-responsibility matrix.
|
|
406
|
-
7. Update ROADMAP.md if relevant.
|
|
407
|
-
|
|
408
|
-
### When fixing bugs
|
|
409
|
-
1. Write a standalone test script (`node -e "..."`) to reproduce the bug before fixing
|
|
410
|
-
2. After fixing, verify with the same script
|
|
411
|
-
3. If the bug affects MCP tool behavior, test via MCP tool call after server restart
|
|
412
|
-
|
|
413
|
-
### Testing methodology
|
|
414
|
-
See `docs/TESTING-METHODOLOGY.md` for the full regression playbook (when to use unit / smoke / live MCP / `scripts/test-all-tools.js`). The semi-automated path is `node scripts/test-all-tools.js`; the smoke gate is `npm run smoke` (regenerate baseline with `npm run smoke:baseline` only when a tool schema delta is intentional).
|
|
415
|
-
|
|
416
|
-
### Commit conventions
|
|
417
|
-
- `feat:` new tools or capabilities
|
|
418
|
-
- `fix:` bug fixes
|
|
419
|
-
- `docs:` CLAUDE.md, ROADMAP.md, README updates
|
|
420
|
-
- `chore:` dependencies, CI, config changes
|
|
421
|
-
|
|
422
|
-
### Publishing
|
|
423
|
-
**IMPORTANT: User confirmation is required exactly TWICE per release** — once on target version (before any publish operation), once on the announcement card before sending. Don't ask between steps; run end-to-end.
|
|
424
|
-
|
|
425
|
-
Three-layer version safety:
|
|
426
|
-
1. **Claude rule** (this section): Confirm version once with user. Then run all publish steps without asking. Stop only on (a) failure, or (b) the announcement-preview gate.
|
|
427
|
-
2. **Local gate** (`prepublishOnly`): Interactive confirmation when running `npm publish` locally (skipped in CI)
|
|
428
|
-
3. **CI gate** (`.github/workflows/publish.yml`): Tag must match `package.json` version or publish fails
|
|
429
|
-
|
|
430
|
-
Steps:
|
|
431
|
-
1. Confirm target version with user (once)
|
|
432
|
-
2. Bump `version` in `package.json` + `.claude-plugin/plugin.json` + `skills/feishu-user-plugin/SKILL.md` (single commit; `scripts/check-version.js` enforces triangle equality)
|
|
433
|
-
3. Open release PR, wait for CI green (auto-merge enabled on this repo, so `gh pr merge --auto --squash`)
|
|
434
|
-
4. After merge, `git tag vX.Y.Z && git push origin vX.Y.Z` triggers GitHub Actions `Publish to npm` workflow
|
|
435
|
-
5. Verify: `npm view feishu-user-plugin version` returns the new version
|
|
436
|
-
6. post-merge hook runs `scripts/sync-team-skills.sh` which auto-syncs team-skills (skills + plugin.json + child README changelog + root README catalog row + catalog.yaml regen + `gh pr merge --admin --squash` on the sync PR). No manual touches in team-skills.
|
|
437
|
-
7. Run `node scripts/generate-release-artifacts.js` to produce `/tmp/feishu-release/v$VERSION/feishu-card.json`
|
|
438
|
-
8. Present the card preview to user. Wait for "发"
|
|
439
|
-
9. `send_card_as_user(chat_id="oc_0fab8e155f500f28bd437e8686921870", card=<JSON>)` — only after user explicitly approves
|
|
440
|
-
6. **After npm confirms the new version is live, draft a release announcement in Chinese for the "AI技术解决(内部)" Feishu group and show it to the user for approval BEFORE sending.** Do not send until the user explicitly approves.
|
|
441
|
-
|
|
442
|
-
### Release announcement rules (every release)
|
|
443
|
-
|
|
444
|
-
After successful publish, send announcement to "AI技术解决(内部)" group (chat_id `oc_0fab8e155f500f28bd437e8686921870`). **Never send without explicit user approval** — show preview first, wait for "发".
|
|
445
|
-
|
|
446
|
-
**Transport (v1.3.9+)**: `send_card_as_user` (interactive Feishu card). No @-mentions, no emojis, no marketing.
|
|
447
|
-
|
|
448
|
-
**Source of truth**: `CHANGELOG.md` v$VERSION section. **Never hand-write announcements** — the generator script extracts the text deterministically:
|
|
449
|
-
|
|
450
|
-
```bash
|
|
451
|
-
node scripts/generate-release-artifacts.js [version]
|
|
452
|
-
# Outputs to /tmp/feishu-release/v<version>/:
|
|
453
|
-
# feishu-card.json ← full Feishu card payload, ready for send_card_as_user
|
|
454
|
-
# team-skills-changelog.md ← markdown block injected into team-skills child README by post-merge hook
|
|
455
|
-
# team-skills-readme-row.md ← root README catalog row replacement
|
|
456
|
-
```
|
|
457
|
-
|
|
458
|
-
**CHANGELOG conventions** (the generator parses these — keep the convention or output diverges):
|
|
459
|
-
|
|
460
|
-
```markdown
|
|
461
|
-
## [X.Y.Z] - YYYY-MM-DD
|
|
462
|
-
|
|
463
|
-
<一到两句陈述式开篇,可空,generator 用作 card 第一段;不宣传不夸大>
|
|
464
|
-
|
|
465
|
-
### Added (翻译为"新增")
|
|
466
|
-
- **简短标题 (代号)**:用户可见现象。底层机制 / 错误码 / 接口名 / 文件路径。
|
|
467
|
-
- ...
|
|
468
|
-
|
|
469
|
-
### Changed ("调整")
|
|
470
|
-
### Fixed ("修复")
|
|
471
|
-
### Removed | Deprecated | Security ("移除" / "废弃" / "安全")
|
|
472
|
-
### Deferred to vN.M.P ("下版本计划 (vN.M.P)",从上版本拷过来 - 本版完成的条目)
|
|
473
|
-
|
|
474
|
-
### Test scenarios (可选;用作"升级方式"段的"建议复测"行)
|
|
475
|
-
- 调用 X 时观察 Y 出现 Z
|
|
476
|
-
- ...
|
|
477
|
-
```
|
|
478
|
-
|
|
479
|
-
**写作规范** (writes flow into the card directly):
|
|
480
|
-
- 每条 bullet:先用户可见现象,再底层机制。引用具体错误码 (91403 / 1254301)、接口名 (`manage_bitable_record`)、参数名 (`via_profile`)、文件路径 (`src/auth/profile-router.js`)
|
|
481
|
-
- 代号语:`(B)` `(D.1)` 等可保留,对应 ROADMAP / plan 编号
|
|
482
|
-
- 禁用:emoji / `@` 任何人 / "强大"等营销词 / 夸张修辞
|
|
483
|
-
- 长度:单屏,整段 400-800 汉字。每条 bullet 1-3 行
|
|
484
|
-
|
|
485
|
-
**升级方式** is generated automatically by the script:
|
|
486
|
-
- "重启 Claude Code / Codex 自动拉取 X.Y.Z" — always
|
|
487
|
-
- "推荐运行 npx feishu-user-plugin migrate --confirm ..." — added when bullets mention migrate / credentials.json / FEISHU_PLUGIN_PROFILE
|
|
488
|
-
- "启动看 stderr 带 WS connected ..." — added when bullets mention WS / WebSocket / get_new_events
|
|
489
|
-
- "建议复测 N 个场景:..." — uses `### Test scenarios` bullets if present, otherwise top-3 Added bullet titles
|
|
490
|
-
|
|
491
|
-
**Step-by-step at release time**:
|
|
492
|
-
1. Bump version → tag → push → wait for `Publish to npm` workflow success → confirm `npm view feishu-user-plugin version`
|
|
493
|
-
2. post-merge hook on this repo runs `scripts/sync-team-skills.sh`, which calls `scripts/generate-release-artifacts.js` and auto-injects v$VERSION block into team-skills child README + updates root README catalog row + opens & `--admin --squash`-merges the team-skills sync PR. Zero manual steps in team-skills.
|
|
494
|
-
3. `node scripts/generate-release-artifacts.js` (idempotent — same input gives same output) on this repo to (re)produce `feishu-card.json`
|
|
495
|
-
4. **Show the rendered card preview to user** — paste a summary or re-render via `cat /tmp/feishu-release/v$VERSION/feishu-card.json | jq` and let user inspect. Do not send.
|
|
496
|
-
5. User says "发" → `send_card_as_user(chat_id=oc_0fab8e155f500f28bd437e8686921870, card=<JSON content>)`
|
|
497
|
-
|
|
498
|
-
## OAuth Scopes (when re-running `npx feishu-user-plugin oauth`)
|
|
499
|
-
|
|
500
|
-
The v1.3.4 tools require additional scopes on the app + UAT:
|
|
501
|
-
|
|
502
|
-
| Feature | Scopes to enable on app + include in OAuth |
|
|
503
|
-
|---------|-------------------------------------------|
|
|
504
|
-
| OKR read | `okr:okr:readonly`, `okr:period:read` |
|
|
505
|
-
| OKR progress write (v1.3.7: create/delete_okr_progress_record) | `okr:okr.content:write` |
|
|
506
|
-
| Calendar read | `calendar:calendar:readonly`, `calendar:calendar.event:read` |
|
|
507
|
-
| Calendar write (v1.3.7: create/update/delete/respond_calendar_event) | `calendar:calendar.event:write` |
|
|
508
|
-
| Tasks v2 (v1.3.7: list/get/create/update/complete/delete_task, manage_task_members) | `task:task` |
|
|
509
|
-
| Docx/Bitable/Drive media upload (`uploadMedia`, `upload_drive_file`, `upload_bitable_attachment`, `manage_doc_block(action=create, image_path|file_path|...)`) | `drive:drive`, `drive:file:upload`, `docs:document.media:upload`, `sheets:spreadsheet` (only for sheet uploads) |
|
|
510
|
-
| Wiki attach (`move_docs_to_wiki`) | `wiki:wiki` (edit scope, the readonly one is insufficient) |
|
|
511
|
-
|
|
512
|
-
If a tool returns `access_denied` or error code `99991672` (scope not granted), the scope is missing on either the app or the UAT. Re-run `npx feishu-user-plugin oauth` so the UAT picks up the latest scope list (defined in `src/oauth.js`).
|
|
513
|
-
|
|
514
|
-
## Known Limitations
|
|
515
|
-
- CARD message type (type=14) not yet implemented — complex JSON schema
|
|
516
|
-
- External tenant users may not be resolvable via `get_user_info` (contact API scope limitation)
|
|
517
|
-
- Cookie auth requires human interaction (QR scan) — cannot be fully automated
|
|
518
|
-
- Refresh token expires after 7 days without use — set up `keepalive` cron to prevent this
|
|
519
|
-
- `manage_bitable_field(action=update)` requires `type` parameter even when only changing field name (Feishu API requirement)
|
|
520
|
-
- `list_wiki_spaces` may return empty if bot lacks `wiki:wiki:readonly` permission (v1.3.7+: `scopeHint` field is appended to the response when this happens)
|
|
521
|
-
- `delete_wiki_node` calls an undocumented-in-SDK endpoint (`DELETE /wiki/v2/spaces/{id}/nodes/{token}`); v1.3.7 ships it because Feishu's API console exposes it, but if Feishu retires the endpoint the tool will fail with a clear 404 — fall back to `manage_drive_file(action=delete)` on the underlying obj_token in that case.
|
|
522
|
-
- `search_wiki` uses same API as `search_docs` — `docs_types` filter may not work as expected
|
|
523
|
-
- `send_card_as_user` only routes through bot. User-identity (cookie protobuf) card sending was confirmed server-side disabled in v1.3.9 (exhaustive brute-force, `scripts/explore-card-protobuf.js`). The "as_user" suffix is historical naming kept for backward compat — the `via` parameter and the user-path codepath were removed; the tool is bot-only.
|