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.
Files changed (61) hide show
  1. package/.claude-plugin/plugin.json +2 -2
  2. package/.cursor-plugin/plugin.json +27 -0
  3. package/.mcpb/manifest.json +91 -0
  4. package/CHANGELOG.md +118 -0
  5. package/PRIVACY.md +105 -0
  6. package/README.en.md +130 -413
  7. package/README.md +88 -258
  8. package/package.json +5 -3
  9. package/scripts/build-mcpb.js +119 -0
  10. package/scripts/check-description-drift.js +73 -0
  11. package/scripts/check-docs-sync.js +7 -16
  12. package/scripts/check-mcp-registry-version.js +43 -0
  13. package/scripts/check-mcpb-version.js +33 -0
  14. package/scripts/check-scopes.js +99 -0
  15. package/scripts/check-tool-count.js +4 -3
  16. package/scripts/check-version.js +5 -0
  17. package/scripts/sync-claude-md.sh +3 -4
  18. package/scripts/sync-team-skills.sh +72 -57
  19. package/scripts/verify-app-name.js +64 -0
  20. package/skills/feishu-user-plugin/SKILL.md +3 -3
  21. package/skills/feishu-user-plugin/references/search.md +3 -3
  22. package/src/auth/credentials-monitor.js +185 -0
  23. package/src/auth/credentials.js +49 -0
  24. package/src/auth/identity-state.js +204 -0
  25. package/src/auth/lark-desktop.js +135 -0
  26. package/src/auth/uat.js +49 -35
  27. package/src/cli.js +87 -0
  28. package/src/clients/official/base.js +145 -14
  29. package/src/clients/official/calendar.js +3 -1
  30. package/src/clients/official/im.js +76 -2
  31. package/src/clients/official/okr.js +2 -1
  32. package/src/error-codes.js +40 -0
  33. package/src/events/lockfile.js +40 -4
  34. package/src/events/owner.js +11 -2
  35. package/src/index.js +1 -1
  36. package/src/logger.js +11 -5
  37. package/src/oauth.js +46 -10
  38. package/src/server.js +102 -37
  39. package/src/setup.js +44 -0
  40. package/src/test-all.js +40 -0
  41. package/src/test-cli-tool.js +87 -0
  42. package/src/test-credentials-monitor.js +124 -0
  43. package/src/test-display-label.js +88 -0
  44. package/src/test-error-codes.js +85 -0
  45. package/src/test-identity-state.js +172 -0
  46. package/src/test-lark-desktop.js +300 -0
  47. package/src/test-lockfile-pid.js +90 -0
  48. package/src/test-lru-cache.js +145 -0
  49. package/src/test-negative-cache.js +85 -0
  50. package/src/test-populate-sender-names.js +98 -0
  51. package/src/test-search-messages.js +101 -0
  52. package/src/test-send-shape.js +115 -0
  53. package/src/test-via-user.js +94 -0
  54. package/src/test-with-uat-retry.js +135 -0
  55. package/src/tools/_registry.js +24 -1
  56. package/src/tools/calendar.js +5 -5
  57. package/src/tools/im-read.js +52 -4
  58. package/src/tools/messaging-user.js +1 -1
  59. package/src/utils.js +83 -0
  60. package/scripts/generate-og-image.js +0 -39
  61. package/skills/feishu-user-plugin/references/CLAUDE.md +0 -523
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "feishu-user-plugin",
3
- "version": "1.3.10",
4
- "description": "All-in-one Feishu plugin for Claude Code — send messages as yourself, read chats (auto-expanded merge_forward), manage docs / bitable / wiki (full CRUD) / drive / OKR (with progress writes) / calendar (read+write) / Tasks v2 / multi-profile auto-switch / real-time WS events. 82 tools + 9 prompts, 3 auth layers.",
3
+ "version": "1.3.12",
4
+ "description": "All-in-one Feishu MCP server + CLI tool for Claude Code — send messages as yourself, read chats (auto-expanded merge_forward), manage docs / bitable / wiki (full CRUD) / drive / OKR (with progress writes) / calendar (read+write) / Tasks v2 / multi-profile auto-switch / real-time WS events. 85 tools + 9 prompts, 3 auth layers.",
5
5
  "author": {
6
6
  "name": "EthanQC"
7
7
  },
@@ -0,0 +1,27 @@
1
+ {
2
+ "name": "feishu-user-plugin",
3
+ "displayName": "Feishu MCP for Claude Code & Codex",
4
+ "description": "All-in-one Feishu MCP server + CLI tool for Claude Code / Codex / Cursor / scripts — 85 tools across 3 auth layers (cookie / app / OAuth). Send as you, read groups, manage docs / bitable / wiki / drive / calendar / tasks / OKR.",
5
+ "version": "1.3.12",
6
+ "author": {
7
+ "name": "EthanQC"
8
+ },
9
+ "homepage": "https://ethanqc.github.io/feishu-user-plugin/",
10
+ "repository": "https://github.com/EthanQC/feishu-user-plugin",
11
+ "license": "MIT",
12
+ "category": "Communication",
13
+ "keywords": ["feishu", "lark", "mcp", "claude-code", "codex"],
14
+ "mcpServers": {
15
+ "feishu-user-plugin": {
16
+ "command": "npx",
17
+ "args": ["-y", "feishu-user-plugin"],
18
+ "env": {
19
+ "LARK_COOKIE": "${LARK_COOKIE}",
20
+ "LARK_APP_ID": "${LARK_APP_ID}",
21
+ "LARK_APP_SECRET": "${LARK_APP_SECRET}",
22
+ "LARK_USER_ACCESS_TOKEN": "${LARK_USER_ACCESS_TOKEN}",
23
+ "LARK_USER_REFRESH_TOKEN": "${LARK_USER_REFRESH_TOKEN}"
24
+ }
25
+ }
26
+ }
27
+ }
@@ -0,0 +1,91 @@
1
+ {
2
+ "manifest_version": "0.3",
3
+ "name": "feishu-user-plugin",
4
+ "display_name": "Feishu MCP for Claude Code & Codex",
5
+ "version": "1.3.12",
6
+ "description": "All-in-one Feishu MCP server + CLI tool for Claude Code / Codex / Cursor / scripts — 85 tools across 3 auth layers (cookie / app / OAuth). Send as you, read groups, manage docs / bitable / wiki / drive / calendar / tasks / OKR.",
7
+ "long_description": "feishu-user-plugin is a local stdio MCP server (and shell CLI tool) that bridges Feishu / Lark and any MCP client (Claude Code, Codex, Cursor, Windsurf, OpenClaw, Claude Desktop). It exposes 85 tools across three auth layers: cookie + protobuf for sending messages as the real user (a capability not available through the official bot API), Feishu Open Platform app credentials for groups / docs / bitable / wiki / drive / calendar / tasks / OKR, and user OAuth (UAT) for P2P chat reading and user-owned resource creation.",
8
+ "author": {
9
+ "name": "EthanQC",
10
+ "url": "https://github.com/EthanQC"
11
+ },
12
+ "homepage": "https://ethanqc.github.io/feishu-user-plugin/",
13
+ "documentation": "https://github.com/EthanQC/feishu-user-plugin/blob/main/README.md",
14
+ "support": "https://github.com/EthanQC/feishu-user-plugin/issues",
15
+ "repository": {
16
+ "type": "git",
17
+ "url": "https://github.com/EthanQC/feishu-user-plugin.git"
18
+ },
19
+ "license": "MIT",
20
+ "keywords": [
21
+ "feishu",
22
+ "lark",
23
+ "claude-code",
24
+ "codex",
25
+ "mcp",
26
+ "model-context-protocol",
27
+ "im",
28
+ "messaging",
29
+ "protobuf"
30
+ ],
31
+ "privacy_policies": [
32
+ "https://github.com/EthanQC/feishu-user-plugin/blob/main/PRIVACY.md"
33
+ ],
34
+ "server": {
35
+ "type": "node",
36
+ "entry_point": "src/index.js",
37
+ "mcp_config": {
38
+ "command": "node",
39
+ "args": ["${__dirname}/src/index.js"],
40
+ "env": {
41
+ "LARK_COOKIE": "${user_config.lark_cookie}",
42
+ "LARK_APP_ID": "${user_config.lark_app_id}",
43
+ "LARK_APP_SECRET": "${user_config.lark_app_secret}",
44
+ "LARK_USER_ACCESS_TOKEN": "${user_config.lark_user_access_token}",
45
+ "LARK_USER_REFRESH_TOKEN": "${user_config.lark_user_refresh_token}"
46
+ }
47
+ }
48
+ },
49
+ "user_config": {
50
+ "lark_cookie": {
51
+ "type": "string",
52
+ "title": "LARK_COOKIE",
53
+ "description": "Feishu web cookie (from feishu.cn DevTools → Network → request headers Cookie). Required for user-identity messaging tools.",
54
+ "required": false,
55
+ "sensitive": true
56
+ },
57
+ "lark_app_id": {
58
+ "type": "string",
59
+ "title": "LARK_APP_ID",
60
+ "description": "Feishu self-built app ID (cli_xxx). Required for Official API tools (group reads, docs, bitable, wiki, drive, calendar, tasks, OKR).",
61
+ "required": false,
62
+ "sensitive": true
63
+ },
64
+ "lark_app_secret": {
65
+ "type": "string",
66
+ "title": "LARK_APP_SECRET",
67
+ "description": "Feishu self-built app secret. Pairs with LARK_APP_ID.",
68
+ "required": false,
69
+ "sensitive": true
70
+ },
71
+ "lark_user_access_token": {
72
+ "type": "string",
73
+ "title": "LARK_USER_ACCESS_TOKEN",
74
+ "description": "User OAuth UAT (run `npx feishu-user-plugin oauth` to obtain). Required for P2P chat reading and user-owned resource creation.",
75
+ "required": false,
76
+ "sensitive": true
77
+ },
78
+ "lark_user_refresh_token": {
79
+ "type": "string",
80
+ "title": "LARK_USER_REFRESH_TOKEN",
81
+ "description": "User OAuth refresh token. Pairs with LARK_USER_ACCESS_TOKEN; auto-rotates the UAT on expiry.",
82
+ "required": false,
83
+ "sensitive": true
84
+ }
85
+ },
86
+ "compatibility": {
87
+ "runtimes": {
88
+ "node": ">=18.0.0"
89
+ }
90
+ }
91
+ }
package/CHANGELOG.md CHANGED
@@ -4,6 +4,75 @@ All notable changes to this project will be documented in this file.
4
4
 
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/), and this project adheres to [Semantic Versioning](https://semver.org/).
6
6
 
7
+ ## [1.3.12] - 2026-05-15
8
+
9
+ 主线:4 个 architectural root cause(A scope drift / B silent fallback / C LLM-unfriendly 数据 / D hot-reload 缺失)一次性收口 + 1 个新工具 `search_messages`(B.5 Protobuf 阶段二)+ CLI 工具模式(`tool` 子命令,复用 85 工具)+ SEO 改造(README h1 + repo description + 4 GitHub topics)+ 5 项工程质量(gitleaks 防 secret 误提交 / CHANGELOG 回填 v1.3.0-v1.3.2 / 客户端兼容矩阵 / 战略性微调 ×2)。工具数 84 → 85。
10
+
11
+ ### Added
12
+
13
+ - **`search_messages` 工具(v1.3.12 B.5, UAT-only)**:包 `POST /open-apis/search/v2/message`。Probe 2026-05-15 确认飞书暴露该 endpoint,需 OAuth scope `search:message`(同期加入 `src/oauth.js` SCOPES + `docs/AUTH-SETUP.md` 表)。Filter 支持 `chat_ids` / `from_ids` / `at_user_ids` / `message_types` / `from_types` + 分页。返回 `{items, pageToken, hasMore}` 的 message-id 指针(不是 full bodies)—— 跨多群搜索时 response token 友好;caller 再调 `read_messages(chat_id)` 拿 full content。99991679 error 给明确 scope 指引("re-run npx feishu-user-plugin oauth")。
14
+ - **CLI 工具模式 `npx feishu-user-plugin tool …`(v1.3.12 形态扩展)**:复用 `src/server.js` HANDLERS + 新 export 的 `buildCtx()`,CLI 跟 MCP 走同一代码同一 ctx 装配。3 个子命令:`tool list`(列 85 工具名)/ `tool help <name>`(schema + description)/ `tool <name> '<json-args>'`(dispatch + 输出 response text 到 stdout)。新 `docs/CLI.md` 文档(用法 / cron / pipeline / 已知限制)。`src/logger.js` stdout guard 从 module-load side-effect 改成 opt-in `installStdoutGuard()`,CLI 模式不调它 → 结构化输出走真 stdout 供 `jq` / shell pipe 用;MCP server 模式(`src/index.js`)依旧首行调用 guard。
15
+ - **IdentityState 状态机 + `withIdentityFallback`(v1.3.12 root cause B)**:新模块 `src/auth/identity-state.js` —— 6 态枚举(VALID_USER / UAT_EXPIRED / UAT_REVOKED / UAT_MISSING_SCOPE / BOT_ONLY / NO_CREDENTIALS)+ `resolveIdentity(client)` 30s cache + `withIdentityFallback({client, uatFn, botFn, label})` 返回 `{data, via, viaReason, identity, fallbackWarning}`。`asUserOrApp` 内部 routes 过去;外部 shape 完全不变(`_viaUser` / `_fallbackWarning` / `.uatSummary` / `.appError` alias 都保留),15+ callsite 在 calendar / docs / bitable / wiki / okr / tasks / drive / im 无感升级。失败时 UAT 端返回 20064 → `UAT_REVOKED`、99991668 → `UAT_MISSING_SCOPE`、99991663 → `UAT_EXPIRED`,bot 端 fallback 时给 LLM 一行明确的 `viaReason` 而非静默吞错。
16
+ - **CredentialsMonitor hot-reload(v1.3.12 root cause D)**:新模块 `src/auth/credentials-monitor.js` —— factory `createCredentialsMonitor({path?})`,每个 tool-call entry 跑 `sync()`,stat mtime + 对 active profile 的 UAT / refresh / cookie 字段 hash 比对。Diff 触发 4 hook 之一:`onUatChange(env)` reload `officialClient._uat/_uatRefresh/_uatExpires` + invalidate identity cache;`onCookieChange(env)`(暂 no-op,userClient 下次自然 re-init);`onProfileSwitch({from,to,env})` flip in-memory `currentProfile` + null client + clear resolver wiki cache;`onCacheInvalidate(env)` 清 identity-state cache。取代了 v1.3.9 的 `_syncActiveProfileFromDisk`(只看 active 字段)+ 取代了"重启 Claude Code 才能 reload UAT"的人工补救。`switch_profile` handler 改调 `credMonitor.sync()` 让 cross-process 同步走统一 baseline。
17
+ - **LRUCache 跨切面 helper(v1.3.12 root cause D 配套)**:`src/utils.js` 新 class,max + ttlMs(默认 500 / 10 min)。完整 Map-shaped API:`set`/`get`/`has`/`delete`/`clear` + `Symbol.iterator` + `entries`/`keys`/`values` generators(自动跳过 expired)。替代 `_userNameCache` / `_appNameCache` 之前的 unbounded `new Map()`。Negative-cache sentinel(v1.3.12 self-review followup):失败 lookup 写 null sentinel 防止下次 read_messages 重发 N 个 API 调用。
18
+ - **PID liveness check for ws-owner lock(v1.3.12 root cause D 配套)**:`src/events/lockfile.js::acquireLongLived` 在 mtime fresh 时也读 body pid + `process.kill(pid, 0)`:ESRCH 立即 steal(之前要等 60s mtime 超时)。`src/events/owner.js::readOwnerInfo` 综合 `alive = mtimeFresh && pidAlive` —— SIGKILL'd owner 的 lock 在下个 30s takeover poll 就被回收。malformed body / 缺 pid 字段 fall back to mtime-only check(向后兼容)。
19
+ - **scope drift CI guard(v1.3.12 root cause A)**:`scripts/check-scopes.js` 从 `src/oauth.js` SCOPES 提 token list + 跟 `docs/AUTH-SETUP.md` 校对每条 scope 都 mentioned + banlist 已知错名(`calendar:calendar.event:write` / `okr:okr.content:write`)。修正 4 个 calendar scope(write → create / update / delete / reply)+ okr write → writeonly + 加 `im:resource` / `contact:contact.base:readonly` / `search:message`。完整 30 OAuth + 1 tenant-only 表落地 `docs/AUTH-SETUP.md`。CI `.github/workflows/validate.yml` 每个 PR 跑 check-scopes。
20
+ - **`displayLabel` + sender semantics pack(v1.3.12 root cause C)**:`read_messages` / `read_p2p_messages` 每条消息新增 `displayLabel`(如 `周宇` / `[Bot] Claude聊天助手` / `[Bot] (cli_xxx)` / `[匿名]` / `[系统]` / `[已撤回] 怪兽`)+ `senderIdType` / `senderTenantKey` / `isExternal` / `isRecalled` / `isThreadReply`。merge_forward children 加 `forwardedFromChatName`(im.js best-effort getChatInfo on each `originChatId`)。`_populateSenderNames` 新 Step 0 mention-name harvest(零 API 成本)+ Step 1 lazy self tenant_key resolve + Step 4 fill new fields。新 method `getAppName(cli_xxx)` 用 tenant-side `application:application:self_manage` scope 解析 bot 显示名(免审 scope,自助开通)。
21
+ - **gitleaks 防 cookie 误提交**:`.gitleaks.toml` 4 rules(`LARK_APP_SECRET` / `LARK_COOKIE` / `LARK_USER_ACCESS_TOKEN` / `LARK_USER_REFRESH_TOKEN`)+ allowlist 文档示例 / 测试 fixture。`.husky/pre-commit` 装了就跑(macOS `brew install gitleaks`),CI 工作流自 curl install gitleaks v8.30.1 binary。verified clean across 266 commits / 3.76 MB。
22
+ - **client compat matrix `docs/CLIENT-COMPAT.md`**:7 个 MCP 客户端 × 9 prompts × 85 tools 兼容表 + 已知 client-specific caveat(Codex 不支持 Claude Code skill;VS Code key 是 `servers` 不是 `mcpServers`;Cursor / Windsurf prompt UI 还在迭代等)+ 3 步复测 procedure(`/status` + `/send` + `list_chats`)。
23
+ - **`docs/ARCHITECTURE-NOTES.md` reference 永久化**:B / D 设计文档(5 态枚举 / 30s cache / 4 hook registry)作为实施 reference 永久保留 + 给未来类似 root cause 留 reusable pattern。14 根因清单全表 ⏸→✅。
24
+ - **CHANGELOG v1.3.0 / v1.3.1 / v1.3.2 entries 回填**:从 `git log --format='%H %ci %s' v1.3.0..v1.3.2` + tag commit body 重建 3 个 entry,跟 v1.3.6+ 同结构。CHANGELOG 现在从 v1.3.0 起连续。
25
+
26
+ ### Changed
27
+
28
+ - **`send_*_as_user` 8 工具统一返回 shape**:`{ok, viaUser, description?, status?, messageId?, fallbackWarning?}`(pre-v1.3.12 是 plain text "Text sent as user to oc_xxx",LLM / 脚本要 regex)。`sendResult` 接受 options form `sendResult(r, {desc, viaUser, fallbackWarning})`,back-compat 旧 `sendResult(r, descString)`。`send_card_as_user` 标 `viaUser: false`(cookie 不发卡片,仅 bot)。
29
+ - **`read_messages` 加 `via_user: boolean`**:`true` skip bot 直接 UAT;`false` skip UAT fallback bot-only(之前没此选项);undefined = 现有 auto-fallback。`readMessagesWithFallback` 加 `skipUat` 配套(与 `skipBot` 互斥)。Path B (cookie search_contacts 解析) 在 `via_user=false` 时短路报错而非继续走 UAT。
30
+ - **`withUAT` retry 集合扩展**:在 `classifyError(thrown).action === 'retry'` 时一次性重试(同 UAT),coverage 涵盖 ECONNRESET / fetch timeout / JSON parse error。原 99991668 / 99991663 / 99991677 refresh-and-retry 路径不变。
31
+ - **FAILURE_MAP 扩展**:加 20064 (uat_revoked, symmetry-only — 真触发在 identity-state)、91403 (bot_cross_tenant)、1254000/1/301/400 (upload_transient)。TRANSIENT_PATTERNS 加 JSON parse error 识别,`classifyError` 输出独立 reason `response_parse_error` 让监控可区分。
32
+ - **`_userNameCache` / `_appNameCache` Map → LRUCache**:500 / 10min(用户)+ 100 / 10min(app)。原 unbounded Map 在长跑 server 上无限增长 + 永不过期,rename 的用户名字一周后还显示旧的。
33
+ - **README h1 + repo description + GitHub topics 加 cli/mcp**:h1 「飞书 MCP 服务器 + CLI 工具」+ description 加"CLI tool" + topics 加 `cli` / `feishu-cli` / `mcp-server` / `feishu-mcp`。
34
+ - **工具数 84 → 85**:仅加 `search_messages`,其他都是已有 schema 改 description / field(不算 schema 新增)。
35
+
36
+ ### Fixed
37
+
38
+ - **`Promise.allSettled` 不读 status 导致 sender 解析失败被静默吞**(root cause #8):`_populateSenderNames` 现读每个 result.status,rejected ids 进 stderr `[feishu-user-plugin] sender name lookup failed for N/M ids: ou_xxx(<reason>)...`。同一份 fix 同时对 user 路径 + app 路径生效。
39
+ - **解析失败的 sender id 不 cache 导致下次 read_messages 重复 API call**(pre-existing perf bug, v1.3.12 self-review followup):`_populateSenderNames` 在每个 Promise.allSettled 后给未解析的 id 写 null sentinel。同 LRU TTL 10 min 给 rename 的 sender 一个 re-resolve 窗口。
40
+ - **oauth.js race condition (PR #45 P2)**:`saveToken` 在 OAuth callback 时不再重读 `getActiveProfileName()`,而是用 module-init 时 captured 的 `RESOLVED_PROFILE`。原 race:OAuth 期间另一进程 `switch_profile` 切了 active,token 会写错 profile。
41
+ - **`oauth.js::getAppInfo` silent catch 99991672**:原 `try {...} catch {}` 完全不报;改成 stderr warn 指明缺 tenant-side `application:application:self_manage` scope + 影响 `displayLabel`。新 `scripts/verify-app-name.js` 一次性诊断脚本可手动验证 scope 已开(exit 0 / 1 / 2 三种状态)。
42
+ - **server.js `onUatChange` 直接裸写 client 内部字段**(v1.3.12 self-review followup):改调 `loadUATFromEnv(officialClient, env)` helper(已存在,statup 用同一个)。`loadUATFromEnv` 扩展为支持 clear-on-empty(env 无 token → 主动 nil 内存里的 stale token)。
43
+ - **`LRUCache` 缺 `Symbol.iterator`**(v1.3.12 self-review followup):原注释说 "API-compatible with the old Map" 但 spread / for-of 会 TypeError。加 iterator + entries/keys/values generators(自动跳过 expired)。
44
+
45
+ ### Test scenarios
46
+
47
+ - 重启后跑 `read_messages` 看每条消息有 `displayLabel` + bot 消息形如 `[Bot] Claude聊天助手`(非 cli_xxx)+ recall 消息有 `[已撤回] ` 前缀 + cross-tenant 有 `isExternal: true`
48
+ - 跑 `npx feishu-user-plugin oauth`,**不重启**,下次 `get_login_status` UAT 应立即 Valid(D2 CredentialsMonitor onUatChange hook 工作)
49
+ - 在 Lark Desktop / shell 操作让 multiple MCP server 进程并发 → 它们的 in-memory UAT 通过 credentials.json 自动同步(D2 + B identity refine)
50
+ - 模拟 SIGKILL'd MCP server(kill -9 持有 ws-owner.lock 的进程)→ 另一进程在 30s 内 takeover(D4 PID liveness check)
51
+ - `gitleaks detect --config .gitleaks.toml`:跑过 266 commits / 3.76 MB,0 leaks
52
+ - `npx feishu-user-plugin tool list`:返回 85 tool names exit 0
53
+ - `npx feishu-user-plugin tool get_login_status '{}'`:返回 status 文本到 stdout
54
+ - `npx feishu-user-plugin tool search_messages '{"query":"周报"}'`:需 user 先 `npx oauth` 拿 `search:message` scope;之后 dispatch 该 endpoint 返回 items
55
+ - `node scripts/verify-app-name.js` 当前 APP 已开 self_manage scope → 输出 `OK — app name resolves to "Claude聊天助手"`
56
+ - `node src/test-all.js` 跑 19 个 fixture-based unit test 全 pass(含 v1.3.12 新加的 9 个:test-error-codes / test-identity-state / test-with-uat-retry / test-populate-sender-names / test-credentials-monitor / test-lru-cache / test-lockfile-pid / test-negative-cache / test-send-shape / test-via-user / test-search-messages / test-cli-tool)
57
+
58
+ ## [1.3.11] - 2026-05-09
59
+
60
+ 主线:Lark Desktop 多账号无感切换 — 在 Feishu Desktop 切账号,MCP 在 ~15 s 内自动跟到对应 profile。同期完成三项上架基建:MCP Registry CI 自动 publish 在 v1.3.11 头号 release 已自动跑通(`registry.modelcontextprotocol.io` 现 isLatest=1.3.11);Anthropic `.mcpb` 包 + `PRIVACY.md` 与 Cursor `.cursor-plugin/plugin.json` 仓库材料就绪、已上 npm,剩余只待用户去外部平台填表单。工具数 84 不变。
61
+
62
+ ### Added
63
+ - **Lark Desktop 多账号无感切换 (A)**:用户在 Feishu Desktop 切换账号 → MCP 自动跟到对应 profile,零 CLI 命令、零工具调用。`credentials.json::profiles[*].larkHash` 字段绑 profile ↔ Lark `~/Library/.../sdk_storage/<hash>/`;owner heartbeat (15 s) `stat cookie_store.db` mtime,最近活跃 hash 与当前 active 不一致 + mtime 推进时调 `setActiveProfile`(5 s debounce)。`setup` 在 `fresh` / `update` 模式下自动绑定(单账号直接绑、多账号在交互模式下用户选 / 非交互取最近活跃 + 在 stderr 列出其它),新 flag `--bind-hash <hex>` 显式绑定 / `--no-bind-hash` 跳过。未绑定但活跃的 hash 在 stderr 打一次性提示带 `setup --profile <name> --bind-hash <hash>` 命令。Lark 加密 `cookie_store.db` 全程不读不解密;cookie 仍由 `LARK_COOKIE` 按 profile 单独提供。macOS-only;Linux / Windows 默认 no-op。新模块 `src/auth/lark-desktop.js`(`getSdkStorageDir` / `listAccountHashes` / `mostRecentHash` / `detectSwitch`)+ `credentials.js` 新 API(`getProfileLarkHash` / `setProfileLarkHash` / `findProfileByHash`)+ `server.js` heartbeat 反应器接入;13 个 fixture-based 单元测试 `src/test-lark-desktop.js` 不依赖真 Lark Desktop 安装即可跑。
64
+ - **`.mcpb` 桌面扩展打包 + `PRIVACY.md`**:仓库具备 Anthropic Connectors Directory 收录所需所有材料;`node scripts/build-mcpb.js` 产出 `dist/feishu-user-plugin-1.3.11.mcpb`(250 KB),可上传 https://clau.de/desktop-extention-submission(剩余待用户填表单,ROADMAP `v1.3.12 / 上架提交`)。`.mcpb/manifest.json` 走 `manifest_version=0.3` schema(顶层 `server.mcp_config` + `user_config` 块声明 5 个 `LARK_*` 全 `sensitive=true`,Claude Desktop UI 自动提示用户填凭证后通过 `${user_config.KEY}` 替换到 `mcp_config.env`);`PRIVACY.md` 中英双语 6 维度(采集 / 处理 / 存储 / 第三方 / 留存 / 联系),README 加 "## 隐私 / Privacy" 段;CI gate `scripts/check-mcpb-version.js` 接进 `validate.yml` 校验 `.mcpb/manifest.json::version` 与 `package.json::version` 一致。
65
+ - **`.cursor-plugin/plugin.json` + 4 源版本三角**:仓库具备 Cursor Marketplace 收录所需 manifest(`mcpServers` 块镜像 README 的 Claude Code 配置),可去 https://cursor.com/marketplace/publish 提交(剩余待用户填表单,ROADMAP `v1.3.12 / 上架提交`)。校对 `cursor/plugins` 官方 schema 修正 prep 文档错误:`author` 实际是 `{name, email}` with `additionalProperties: false`(非 `{name, url}`),`repository` 是字符串(非 `{type, url}`)。`scripts/check-version.js` 由 3 源扩 4 源版本三角(`package.json` / `.claude-plugin/plugin.json` / `SKILL.md` / `.cursor-plugin/plugin.json`),任一源 mismatch 即 CI fail。
66
+ - **MCP Registry CI 自动 publish**:`v1.3.11` 头号 release 已自动跑通——`registry.modelcontextprotocol.io` 现 isLatest=1.3.11;以后每次 tag 推送自动同步,零人工。`.github/workflows/publish.yml` 增 mcp-publisher 步骤(curl 安装 `mcp-publisher` 二进制 → `login github-oidc`,runner OIDC token 取代 PAT → `publish mcp-registry.json`),publish job 加 `permissions: id-token: write`。`scripts/check-mcp-registry-version.js` 在 `publish.yml`(pre-publish)+ `validate.yml`(PR-time)双闸门校验 `mcp-registry.json::version` + `packages[0].version` 与 `package.json::version` 一致。
67
+
68
+ ### Test scenarios
69
+ - 单 profile + 单 hash:`setup` 自动绑定,stderr 一行 `Bound profile "default" to Lark account hash <hex>`,无后续噪声
70
+ - 多 profile + 多 hash:在 Lark Desktop 切到 profile B 绑的账号 → 15 s 内 stderr 出 `Lark Desktop account changed; switching profile to "B"` → `credentials.json::active` 更新 → 下一次工具调用走 B 的凭证
71
+ - 未绑定但活跃 hash:在 Lark Desktop 切到一个新账号 → stderr 出一次性提示带 `setup --profile <name> --bind-hash <hash>` 命令;后续 heartbeat 不再重复
72
+ - 非 darwin:`getSdkStorageDir()` 返回 null,反应器全部 no-op;`setup --no-bind-hash` 显式跳过
73
+ - `node scripts/build-mcpb.js` 产出 `dist/feishu-user-plugin-1.3.11.mcpb`,`unzip -p` 验证 `manifest.json` 在 archive 根
74
+ - `curl 'https://registry.modelcontextprotocol.io/v0/servers?search=feishu-user-plugin'` 返回 v1.3.11 isLatest=true
75
+
7
76
  ## [1.3.10] - 2026-05-09
8
77
 
9
78
  Growth track 一次性 ship + Official MCP Registry 上架。本版无新工具(84 不变),主体是发现入口、文档语气与发布元数据:仓库一句话描述与 npm description 同步、GitHub Pages 中文优先 SEO landing 上线、`README.md` 主版本切到中文、`docs/launch/` 13 文件 launch 草稿就位、Dockerfile 给 Glama listing introspection 用、自定义 OG image 替代 GitHub 默认渲染、CONTRIBUTING.md 双语重写。所有用户可见文档统一去除 reverse-engineering / 暴力探测 / 营销腔 / 合规免责段。
@@ -184,6 +253,55 @@ D 系列首项 ship:新增 `read_doc_markdown` 工具,用 `feishu-docx` 把
184
253
  - `getWikiNode(nodeToken, _spaceId)` — `spaceId` parameter position swapped; retained only for backward-compatibility of any external caller. The endpoint itself ignores `space_id`.
185
254
  - `create_doc_block` no longer requires `children` — callers who use the new `image_path` or `image_token` shortcut omit it. One of `children` / `image_path` / `image_token` must be provided.
186
255
 
256
+ ## [1.3.2] - 2026-04-17
257
+
258
+ 主线:以"真用户身份"补两个 longstanding gap —— 用户消息的 @-mention 现在真能通知到人 + 用户身份创建的 docx / bitable 资源现在真归你(不是 app)。
259
+
260
+ ### Fixed
261
+ - **@-mentions 作为用户发送时不通知**:飞书 Web bundle 反向工程发现 `RichText` 需要 `atIds[]` (field 6) 注册 AT element ids,没有这个字段后端会把 `user_id` 清空。`proto/lark.proto::RichText` 扩字段(`atIds` / `anchorIds` / `imageIds` 等),加上真正的 `AtProperty` / `AnchorProperty` message。Live 测试:bot-API 回读现在保留 `user_id` + `user_name`(之前两个都空字符串)。
262
+ - **`create_doc` / `create_bitable` 创建后归属错乱**:所有 docx / bitable / drive 操作改走 UAT-first → app fallback(新 helper `_uatREST` + `_asUserOrApp`)。修复 1770032(docx forbidden)+ 91403(bitable forbidden)—— 之前 UAT 创建的资源用 app 路径打开会 403,因为根本不是 UAT 创的。
263
+
264
+ ### Added
265
+ - **`ats: [{userId, name}]` 参数**给 `send_as_user` / `send_to_user` / `send_to_group` / `send_post_as_user`:在 TEXT 消息里 splice @-mention(marker `@<name>`);在 POST/RichText 消息里 `sendPost` 把 AT elem ids 汇到 `richText.atIds`,AT 编码用 `AtProperty`。
266
+ - **`_formatMessage` surface `mentions[]`**:`im.message` payload 里 mentions 数组现在被 `read_messages` / `read_p2p_messages` 透传出来,供下游用 mention 的 name 直接 narrate 而不用再查 contact API。
267
+
268
+ ### Changed
269
+ - Docs synced:`CLAUDE.md` / `skills/feishu-user-plugin/references/CLAUDE.md` / `.claude-plugin/plugin.json` 全部更新 @-mention 用法 + UAT-first 行为说明。
270
+ - Removed redundant per-resource as-user wrappers:`createDocAsUser` / `createBitableAsUser` / `createFolderAsUser` 删除,被 `_asUserOrApp` 统一替代。
271
+
272
+ ## [1.3.1] - 2026-04-17
273
+
274
+ 主线:MCP 稳定性 root fix + 用户身份创建 + Codex 双客户端支持 + 工具表收敛(81 → 66,去 calendar/tasks 这种 app 权限未开通的伪能力)。
275
+
276
+ ### Fixed
277
+ - **MCP 中途掉线(root cause #1)**:Lark SDK 的 logger 默认写 stdout 污染了 JSON-RPC channel。`src/index.js` 启动把 SDK logger 改写到 stderr(PR #2 by [@ZYAH111](https://github.com/ZYAH111))。
278
+ - **uncaughtException / unhandledRejection 兜底**:MCP server 不再因为单个 tool handler 抛错而整个 crash —— 进程级 handler 把错误吐到 stderr,server 继续接 next request。
279
+ - **config 写入 race**:`atomicWrite(tmp + rename)` 替代直接 fs.writeFile,防止 Claude Code spawn 多 MCP server 时并发改 `~/.claude.json` 互相覆盖。
280
+
281
+ ### Added
282
+ - **UAT-first creation**:`create_doc` / `create_bitable` / `create_folder` 现在用 `LARK_USER_ACCESS_TOKEN` 走 UAT 路径创建,资源归用户而非 app。
283
+ - **Codex TOML 支持**:`npx feishu-user-plugin setup --client codex|both` 写 `~/.codex/config.toml::mcp_servers`。新增 `scripts/mcp_stdio_bridge.js` 适配 Codex 协议差异。
284
+ - **3-layer 版本确认**:CLAUDE.md 规则 + `prepublishOnly` script + CI tag check,三层保护防版本号 drift。
285
+ - **5 个新 bitable 工具**:`get_bitable_meta`、`copy_bitable_app`、`update_bitable_table`、`create_bitable_view`、`delete_bitable_view`。
286
+
287
+ ### Changed
288
+ - **工具数 81 → 66**:移除 calendar(5) + tasks(5) 工具 —— 飞书 app 权限管理后台对应 scope 未开通,工具调用 100% 失败。后续在 v1.3.4 重新加回时 app 权限已申请。
289
+ - **合并 pin/unpin → `pin_message(action='pin'|'unpin')`**,`add/remove_members → manage_members(action=...)`。
290
+ - **吸收单 record CRUD 到 batch tools**:`create_bitable_record` → `batch_create_bitable_records(records=[<one>])` 等。
291
+ - **OAuth scopes**:加 `docx:document`、`drive:drive` write 权限。
292
+
293
+ ## [1.3.0] - 2026-04-03
294
+
295
+ 主线:tool surface 一次性扩张 46 → 76(+30)—— bot messaging 全套、docx block 编辑、calendar / tasks / drive 操作首次纳入。
296
+
297
+ ### Added
298
+ - **IM 域(13 工具)**:`send_message_as_bot`、`delete_message`、`update_message`、`add_reaction`、`delete_reaction`、`pin_message`、`unpin_message`、`create_group`、`update_group`、`list_members`、`add_members`、`remove_members`。
299
+ - **Docx block 编辑(3 工具)**:`create_doc_block`、`update_doc_block`、`delete_doc_blocks` —— 飞书 docx 的原子编辑单元。
300
+ - **Bitable(2 工具)**:`get_bitable_record`(按 record_id 取一条)、`delete_bitable_table`。
301
+ - **Drive 操作(3 工具)**:`copy_file`、`move_file`、`delete_file`。
302
+ - **Calendar(5 工具)**:`list_calendars`、`create_calendar_event`、`list_calendar_events`、`delete_calendar_event`、`get_freebusy`。(v1.3.1 因 scope 未开通暂时下线,v1.3.4 加回。)
303
+ - **Tasks(5 工具)**:`create_task`、`get_task`、`list_tasks`、`update_task`、`complete_task`。(v1.3.1 因 scope 未开通暂时下线,v1.3.7 v2 API 重做。)
304
+
187
305
  ## [1.3.3] - 2026-04-20
188
306
 
189
307
  ### Fixed
package/PRIVACY.md ADDED
@@ -0,0 +1,105 @@
1
+ # 隐私政策 / Privacy Policy
2
+
3
+ `feishu-user-plugin` 是一个本地运行的 MCP 服务器。本文档说明插件如何处理用户提供的飞书 / Lark 凭证以及通过 MCP 工具调用流转的数据。
4
+
5
+ ---
6
+
7
+ ## 中文
8
+
9
+ ### 1. 收集的数据
10
+
11
+ 插件本身不收集任何数据。运行需要用户主动配置以下凭证,全部来自用户自己的飞书 / Lark 账号:
12
+
13
+ - `LARK_COOKIE` —— 用户浏览器登录 feishu.cn 后从请求头复制的 cookie 串
14
+ - `LARK_APP_ID` + `LARK_APP_SECRET` —— 用户在飞书开放平台自建应用的 ID 与密钥
15
+ - `LARK_USER_ACCESS_TOKEN` + `LARK_USER_REFRESH_TOKEN` —— 用户通过 `npx feishu-user-plugin oauth` 在自己浏览器中授权后由插件本地保存的 OAuth 令牌
16
+
17
+ 以上凭证保存在用户本地,不会发送给插件作者或除飞书自身以外的任何第三方。
18
+
19
+ ### 2. 处理的数据
20
+
21
+ 插件只处理用户通过 MCP 工具调用主动请求的数据:消息、文档、多维表格、知识库、云空间、日历、任务、OKR、联系人。插件是用户与飞书开放平台之间的薄代理,不在数据通过时做额外的留存、备份、上传或分析。
22
+
23
+ ### 3. 数据存储位置
24
+
25
+ - 凭证文件:`~/.feishu-user-plugin/credentials.json`,文件权限 0600(仅当前用户可读写),由用户的操作系统强制访问控制
26
+ - 实时事件日志(启用时):`~/.feishu-user-plugin/events.jsonl`,append-only,10 MB 软上限 / 20 MB 硬上限自动轮转
27
+ - 不上报遥测,不发送埋点,不联网调用统计接口,不与插件作者维护的任何后台通信
28
+
29
+ 唯一的数据驻留点是用户本机。
30
+
31
+ ### 4. 第三方共享
32
+
33
+ 插件运行时与两类外部方通信:
34
+
35
+ - **飞书开放平台 API**(`open.feishu.cn` / `feishu.cn`)—— 用户自己的飞书租户。所有读写都直接打到这里,等价于用户自己用飞书客户端的操作
36
+ - **用户运行的 AI 客户端**(Claude Code / Codex / Cursor / Windsurf / OpenClaw / Claude Desktop 等)—— 这是 MCP 协议的另一端,由用户自行选择安装
37
+
38
+ 插件不引入任何额外的第三方依赖(无 CDN、无分析服务、无错误上报)。
39
+
40
+ ### 5. 数据保留
41
+
42
+ 完全由用户控制。插件不主动删除、归档或复制用户数据。要彻底移除:
43
+
44
+ ```bash
45
+ rm -rf ~/.feishu-user-plugin
46
+ npm uninstall -g feishu-user-plugin
47
+ ```
48
+
49
+ 撤销飞书侧的 OAuth 授权可在飞书开放平台的应用管理页操作。
50
+
51
+ ### 6. 联系方式
52
+
53
+ - 一般问题:[GitHub Issues](https://github.com/EthanQC/feishu-user-plugin/issues)
54
+ - 安全披露:在 GitHub Issue 标题前加 `[security]` 前缀
55
+
56
+ ---
57
+
58
+ ## English
59
+
60
+ ### 1. Data Collected
61
+
62
+ The plugin itself collects no data. Operation requires the user to provide the following credentials, all from the user's own Feishu / Lark account:
63
+
64
+ - `LARK_COOKIE` — cookie string the user copies from their own browser session on feishu.cn
65
+ - `LARK_APP_ID` + `LARK_APP_SECRET` — credentials of a self-built app the user registers on the Feishu Open Platform
66
+ - `LARK_USER_ACCESS_TOKEN` + `LARK_USER_REFRESH_TOKEN` — OAuth tokens issued after the user grants consent via `npx feishu-user-plugin oauth` and saved locally by the plugin
67
+
68
+ These credentials remain on the user's machine and are not transmitted to the plugin author or any third party other than Feishu itself.
69
+
70
+ ### 2. Data Processed
71
+
72
+ The plugin only processes data the user explicitly requests through MCP tool calls: messages, documents, bitable, wiki, drive, calendar, tasks, OKR, contacts. The plugin is a thin proxy between the user and the Feishu Open Platform; it does not retain, archive, replicate, upload, or analyse data in transit.
73
+
74
+ ### 3. Where Data Is Stored
75
+
76
+ - Credential file: `~/.feishu-user-plugin/credentials.json`, mode 0600 (readable / writable only by the file owner), enforced by OS-level access control
77
+ - Realtime event log (when enabled): `~/.feishu-user-plugin/events.jsonl`, append-only, 10 MB soft / 20 MB hard rotation cap
78
+ - No telemetry, no analytics, no phone-home, no communication with any backend maintained by the plugin author
79
+
80
+ The user's machine is the only retention point.
81
+
82
+ ### 4. Third-Party Sharing
83
+
84
+ At runtime the plugin communicates with two external parties:
85
+
86
+ - **Feishu Open Platform API** (`open.feishu.cn` / `feishu.cn`) — the user's own Feishu tenant. All reads and writes go directly there, equivalent to actions the user could take in the official Feishu client
87
+ - **The AI client the user runs** (Claude Code / Codex / Cursor / Windsurf / OpenClaw / Claude Desktop, etc.) — the other end of the MCP protocol, chosen and installed by the user
88
+
89
+ The plugin introduces no additional third party (no CDN, no analytics service, no error-reporting endpoint).
90
+
91
+ ### 5. Data Retention
92
+
93
+ Entirely user-controlled. The plugin does not delete, archive, or replicate user data on its own. To remove everything:
94
+
95
+ ```bash
96
+ rm -rf ~/.feishu-user-plugin
97
+ npm uninstall -g feishu-user-plugin
98
+ ```
99
+
100
+ OAuth authorization on the Feishu side can be revoked from the application management page of the Feishu Open Platform.
101
+
102
+ ### 6. Contact
103
+
104
+ - General issues: [GitHub Issues](https://github.com/EthanQC/feishu-user-plugin/issues)
105
+ - Security disclosures: prefix the issue title with `[security]`