lark-agent-bridge 0.1.39

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 (155) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +265 -0
  3. package/README.zh.md +265 -0
  4. package/bin/lark-agent-bridge.mjs +2 -0
  5. package/dist/agent/cursor/cursor-sdk-worker.js +702 -0
  6. package/dist/cli.js +7588 -0
  7. package/dist/index.d.ts +113 -0
  8. package/dist/index.js +721 -0
  9. package/package.json +72 -0
  10. package/vendor/cursor-sdk/LICENSE.md +3 -0
  11. package/vendor/cursor-sdk/README.md +23 -0
  12. package/vendor/cursor-sdk/dist/cjs/642.index.js +1 -0
  13. package/vendor/cursor-sdk/dist/cjs/agent.d.ts +170 -0
  14. package/vendor/cursor-sdk/dist/cjs/agent.d.ts.map +1 -0
  15. package/vendor/cursor-sdk/dist/cjs/analytics.d.ts +98 -0
  16. package/vendor/cursor-sdk/dist/cjs/analytics.d.ts.map +1 -0
  17. package/vendor/cursor-sdk/dist/cjs/artifacts.d.ts +6 -0
  18. package/vendor/cursor-sdk/dist/cjs/artifacts.d.ts.map +1 -0
  19. package/vendor/cursor-sdk/dist/cjs/cloud-agent.d.ts +47 -0
  20. package/vendor/cursor-sdk/dist/cjs/cloud-agent.d.ts.map +1 -0
  21. package/vendor/cursor-sdk/dist/cjs/cloud-api-client.d.ts +197 -0
  22. package/vendor/cursor-sdk/dist/cjs/cloud-api-client.d.ts.map +1 -0
  23. package/vendor/cursor-sdk/dist/cjs/cloud-executor.d.ts +14 -0
  24. package/vendor/cursor-sdk/dist/cjs/cloud-executor.d.ts.map +1 -0
  25. package/vendor/cursor-sdk/dist/cjs/cloud-mcp-utils.d.ts +4 -0
  26. package/vendor/cursor-sdk/dist/cjs/cloud-mcp-utils.d.ts.map +1 -0
  27. package/vendor/cursor-sdk/dist/cjs/core-adapter.d.ts +2 -0
  28. package/vendor/cursor-sdk/dist/cjs/core-adapter.d.ts.map +1 -0
  29. package/vendor/cursor-sdk/dist/cjs/errors.d.ts +135 -0
  30. package/vendor/cursor-sdk/dist/cjs/errors.d.ts.map +1 -0
  31. package/vendor/cursor-sdk/dist/cjs/executor-common.d.ts +16 -0
  32. package/vendor/cursor-sdk/dist/cjs/executor-common.d.ts.map +1 -0
  33. package/vendor/cursor-sdk/dist/cjs/executor-types.d.ts +65 -0
  34. package/vendor/cursor-sdk/dist/cjs/executor-types.d.ts.map +1 -0
  35. package/vendor/cursor-sdk/dist/cjs/index.d.ts +18 -0
  36. package/vendor/cursor-sdk/dist/cjs/index.d.ts.map +1 -0
  37. package/vendor/cursor-sdk/dist/cjs/index.js +8 -0
  38. package/vendor/cursor-sdk/dist/cjs/index.js.LICENSE.txt +285 -0
  39. package/vendor/cursor-sdk/dist/cjs/local-executor.d.ts +18 -0
  40. package/vendor/cursor-sdk/dist/cjs/local-executor.d.ts.map +1 -0
  41. package/vendor/cursor-sdk/dist/cjs/local-model-validation.d.ts +6 -0
  42. package/vendor/cursor-sdk/dist/cjs/local-model-validation.d.ts.map +1 -0
  43. package/vendor/cursor-sdk/dist/cjs/messages.d.ts +110 -0
  44. package/vendor/cursor-sdk/dist/cjs/messages.d.ts.map +1 -0
  45. package/vendor/cursor-sdk/dist/cjs/options.d.ts +145 -0
  46. package/vendor/cursor-sdk/dist/cjs/options.d.ts.map +1 -0
  47. package/vendor/cursor-sdk/dist/cjs/package.json +3 -0
  48. package/vendor/cursor-sdk/dist/cjs/platform.d.ts +71 -0
  49. package/vendor/cursor-sdk/dist/cjs/platform.d.ts.map +1 -0
  50. package/vendor/cursor-sdk/dist/cjs/prefetched-blobs.d.ts +13 -0
  51. package/vendor/cursor-sdk/dist/cjs/prefetched-blobs.d.ts.map +1 -0
  52. package/vendor/cursor-sdk/dist/cjs/public-api.d.ts +12 -0
  53. package/vendor/cursor-sdk/dist/cjs/public-api.d.ts.map +1 -0
  54. package/vendor/cursor-sdk/dist/cjs/run-event-tailer.d.ts +33 -0
  55. package/vendor/cursor-sdk/dist/cjs/run-event-tailer.d.ts.map +1 -0
  56. package/vendor/cursor-sdk/dist/cjs/run-interaction-accumulator.d.ts +33 -0
  57. package/vendor/cursor-sdk/dist/cjs/run-interaction-accumulator.d.ts.map +1 -0
  58. package/vendor/cursor-sdk/dist/cjs/run.d.ts +44 -0
  59. package/vendor/cursor-sdk/dist/cjs/run.d.ts.map +1 -0
  60. package/vendor/cursor-sdk/dist/cjs/sdk-statsig.d.ts +10 -0
  61. package/vendor/cursor-sdk/dist/cjs/sdk-statsig.d.ts.map +1 -0
  62. package/vendor/cursor-sdk/dist/cjs/stubs.d.ts +98 -0
  63. package/vendor/cursor-sdk/dist/cjs/stubs.d.ts.map +1 -0
  64. package/vendor/cursor-sdk/dist/cjs/subagent-conversion.d.ts +22 -0
  65. package/vendor/cursor-sdk/dist/cjs/subagent-conversion.d.ts.map +1 -0
  66. package/vendor/cursor-sdk/dist/cjs/tool-call-utils.d.ts +7 -0
  67. package/vendor/cursor-sdk/dist/cjs/tool-call-utils.d.ts.map +1 -0
  68. package/vendor/cursor-sdk/dist/cjs/types/conversation-types.d.ts +12062 -0
  69. package/vendor/cursor-sdk/dist/cjs/types/conversation-types.d.ts.map +1 -0
  70. package/vendor/cursor-sdk/dist/cjs/types/delta-types.d.ts +2 -0
  71. package/vendor/cursor-sdk/dist/cjs/types/delta-types.d.ts.map +1 -0
  72. package/vendor/cursor-sdk/dist/cjs/types/tool-call-types.d.ts +2 -0
  73. package/vendor/cursor-sdk/dist/cjs/types/tool-call-types.d.ts.map +1 -0
  74. package/vendor/cursor-sdk/dist/cjs/utils/conversation-utils.d.ts +3 -0
  75. package/vendor/cursor-sdk/dist/cjs/utils/conversation-utils.d.ts.map +1 -0
  76. package/vendor/cursor-sdk/dist/cjs/utils/logger.d.ts +12 -0
  77. package/vendor/cursor-sdk/dist/cjs/utils/logger.d.ts.map +1 -0
  78. package/vendor/cursor-sdk/dist/cjs/utils/message-schemas.d.ts +2 -0
  79. package/vendor/cursor-sdk/dist/cjs/utils/message-schemas.d.ts.map +1 -0
  80. package/vendor/cursor-sdk/dist/esm/642.index.js +1 -0
  81. package/vendor/cursor-sdk/dist/esm/agent.d.ts +170 -0
  82. package/vendor/cursor-sdk/dist/esm/agent.d.ts.map +1 -0
  83. package/vendor/cursor-sdk/dist/esm/analytics.d.ts +98 -0
  84. package/vendor/cursor-sdk/dist/esm/analytics.d.ts.map +1 -0
  85. package/vendor/cursor-sdk/dist/esm/artifacts.d.ts +6 -0
  86. package/vendor/cursor-sdk/dist/esm/artifacts.d.ts.map +1 -0
  87. package/vendor/cursor-sdk/dist/esm/cloud-agent.d.ts +47 -0
  88. package/vendor/cursor-sdk/dist/esm/cloud-agent.d.ts.map +1 -0
  89. package/vendor/cursor-sdk/dist/esm/cloud-api-client.d.ts +197 -0
  90. package/vendor/cursor-sdk/dist/esm/cloud-api-client.d.ts.map +1 -0
  91. package/vendor/cursor-sdk/dist/esm/cloud-executor.d.ts +14 -0
  92. package/vendor/cursor-sdk/dist/esm/cloud-executor.d.ts.map +1 -0
  93. package/vendor/cursor-sdk/dist/esm/cloud-mcp-utils.d.ts +4 -0
  94. package/vendor/cursor-sdk/dist/esm/cloud-mcp-utils.d.ts.map +1 -0
  95. package/vendor/cursor-sdk/dist/esm/core-adapter.d.ts +2 -0
  96. package/vendor/cursor-sdk/dist/esm/core-adapter.d.ts.map +1 -0
  97. package/vendor/cursor-sdk/dist/esm/errors.d.ts +135 -0
  98. package/vendor/cursor-sdk/dist/esm/errors.d.ts.map +1 -0
  99. package/vendor/cursor-sdk/dist/esm/executor-common.d.ts +16 -0
  100. package/vendor/cursor-sdk/dist/esm/executor-common.d.ts.map +1 -0
  101. package/vendor/cursor-sdk/dist/esm/executor-types.d.ts +65 -0
  102. package/vendor/cursor-sdk/dist/esm/executor-types.d.ts.map +1 -0
  103. package/vendor/cursor-sdk/dist/esm/index.d.ts +18 -0
  104. package/vendor/cursor-sdk/dist/esm/index.d.ts.map +1 -0
  105. package/vendor/cursor-sdk/dist/esm/index.js +8 -0
  106. package/vendor/cursor-sdk/dist/esm/index.js.LICENSE.txt +285 -0
  107. package/vendor/cursor-sdk/dist/esm/local-executor.d.ts +18 -0
  108. package/vendor/cursor-sdk/dist/esm/local-executor.d.ts.map +1 -0
  109. package/vendor/cursor-sdk/dist/esm/local-model-validation.d.ts +6 -0
  110. package/vendor/cursor-sdk/dist/esm/local-model-validation.d.ts.map +1 -0
  111. package/vendor/cursor-sdk/dist/esm/messages.d.ts +110 -0
  112. package/vendor/cursor-sdk/dist/esm/messages.d.ts.map +1 -0
  113. package/vendor/cursor-sdk/dist/esm/options.d.ts +145 -0
  114. package/vendor/cursor-sdk/dist/esm/options.d.ts.map +1 -0
  115. package/vendor/cursor-sdk/dist/esm/platform.d.ts +71 -0
  116. package/vendor/cursor-sdk/dist/esm/platform.d.ts.map +1 -0
  117. package/vendor/cursor-sdk/dist/esm/prefetched-blobs.d.ts +13 -0
  118. package/vendor/cursor-sdk/dist/esm/prefetched-blobs.d.ts.map +1 -0
  119. package/vendor/cursor-sdk/dist/esm/public-api.d.ts +12 -0
  120. package/vendor/cursor-sdk/dist/esm/public-api.d.ts.map +1 -0
  121. package/vendor/cursor-sdk/dist/esm/run-event-tailer.d.ts +33 -0
  122. package/vendor/cursor-sdk/dist/esm/run-event-tailer.d.ts.map +1 -0
  123. package/vendor/cursor-sdk/dist/esm/run-interaction-accumulator.d.ts +33 -0
  124. package/vendor/cursor-sdk/dist/esm/run-interaction-accumulator.d.ts.map +1 -0
  125. package/vendor/cursor-sdk/dist/esm/run.d.ts +44 -0
  126. package/vendor/cursor-sdk/dist/esm/run.d.ts.map +1 -0
  127. package/vendor/cursor-sdk/dist/esm/sdk-statsig.d.ts +10 -0
  128. package/vendor/cursor-sdk/dist/esm/sdk-statsig.d.ts.map +1 -0
  129. package/vendor/cursor-sdk/dist/esm/stubs.d.ts +98 -0
  130. package/vendor/cursor-sdk/dist/esm/stubs.d.ts.map +1 -0
  131. package/vendor/cursor-sdk/dist/esm/subagent-conversion.d.ts +22 -0
  132. package/vendor/cursor-sdk/dist/esm/subagent-conversion.d.ts.map +1 -0
  133. package/vendor/cursor-sdk/dist/esm/tool-call-utils.d.ts +7 -0
  134. package/vendor/cursor-sdk/dist/esm/tool-call-utils.d.ts.map +1 -0
  135. package/vendor/cursor-sdk/dist/esm/types/conversation-types.d.ts +12062 -0
  136. package/vendor/cursor-sdk/dist/esm/types/conversation-types.d.ts.map +1 -0
  137. package/vendor/cursor-sdk/dist/esm/types/delta-types.d.ts +2 -0
  138. package/vendor/cursor-sdk/dist/esm/types/delta-types.d.ts.map +1 -0
  139. package/vendor/cursor-sdk/dist/esm/types/tool-call-types.d.ts +2 -0
  140. package/vendor/cursor-sdk/dist/esm/types/tool-call-types.d.ts.map +1 -0
  141. package/vendor/cursor-sdk/dist/esm/utils/conversation-utils.d.ts +3 -0
  142. package/vendor/cursor-sdk/dist/esm/utils/conversation-utils.d.ts.map +1 -0
  143. package/vendor/cursor-sdk/dist/esm/utils/logger.d.ts +12 -0
  144. package/vendor/cursor-sdk/dist/esm/utils/logger.d.ts.map +1 -0
  145. package/vendor/cursor-sdk/dist/esm/utils/message-schemas.d.ts +2 -0
  146. package/vendor/cursor-sdk/dist/esm/utils/message-schemas.d.ts.map +1 -0
  147. package/vendor/cursor-sdk/package.json +77 -0
  148. package/vendor/cursor-sdk-linux-x64/README.md +5 -0
  149. package/vendor/cursor-sdk-linux-x64/bin/cursorsandbox +0 -0
  150. package/vendor/cursor-sdk-linux-x64/bin/rg +0 -0
  151. package/vendor/cursor-sdk-linux-x64/package.json +8 -0
  152. package/vendor/package/README.md +5 -0
  153. package/vendor/package/bin/cursorsandbox +0 -0
  154. package/vendor/package/bin/rg +0 -0
  155. package/vendor/package/package.json +25 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Lark Channel Bridge contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,265 @@
1
+ # lark-agent-bridge
2
+
3
+ A lightweight bot that bridges Feishu / Lark messenger with your local coding-agent CLI (Claude Code, Cursor Agent, or another compatible wrapper). Run one command, scan a QR code to bind a Lark app, and talk to the agent from chat — read screenshots, edit code, anything you'd do at the terminal.
4
+
5
+ [中文 README](./README.zh.md)
6
+
7
+ ## What it does
8
+
9
+ - Forwards Feishu / Lark messages (DM directly, or `@bot` in a group) to your local coding agent, running in a working directory you control.
10
+ - **Streaming card**: the agent's text and tool calls update on a single Lark card in real time — no waiting for the final reply.
11
+ - **Resilient run UI**: failed or timed-out runs show a one-click retry button; card update failures degrade to a plain markdown fallback instead of leaving a stale running card.
12
+ - **Per-chat sessions**: each chat keeps its own agent session, so conversations resume where they left off.
13
+ - **Preempt + batch**: a new message interrupts the running run; rapid-fire messages get coalesced into one request.
14
+ - **Multiple workspaces**: `/ws` switches between named project directories, with sessions tracked per workspace.
15
+ - **Images and files**: send them to the bot directly — the agent reads the locally downloaded paths.
16
+ - **Interactive cards**: `/help`, `/ws list`, `/status` return cards with buttons you can click.
17
+
18
+ ## Prerequisites
19
+
20
+ - Node.js **>= 20**
21
+ - A supported coding-agent command installed and logged in. By default the bridge runs `claude`; it can also use compatible wrappers through `agentCommand`, or Cursor CLI's `agent` command.
22
+ - A Lark / Feishu **PersonalAgent** app (the QR-code wizard on first launch can create one for you).
23
+
24
+ ## Install
25
+
26
+ ```bash
27
+ npm i -g lark-agent-bridge
28
+ # or
29
+ pnpm add -g lark-agent-bridge
30
+ ```
31
+
32
+ Run without installing:
33
+
34
+ ```bash
35
+ npx -y lark-agent-bridge@latest start
36
+ ```
37
+
38
+ ## First run
39
+
40
+ ```bash
41
+ lark-agent-bridge start
42
+ ```
43
+
44
+ The first run detects there's no app configured and **opens a QR-code wizard**:
45
+
46
+ 1. A QR code renders in your terminal.
47
+ 2. Scan it with the Feishu / Lark app.
48
+ 3. Pick or create a PersonalAgent app.
49
+ 4. Credentials are written to `~/.lark-channel/config.json`.
50
+
51
+ ### Granting scopes and event subscriptions
52
+
53
+ The wizard creates the app shell, but you still need to confirm a few things on the Lark Developer Console:
54
+
55
+ **Permission scopes:**
56
+ - `im:message`
57
+ - `im:message:send_as_bot`
58
+ - `im:resource`
59
+
60
+ **Event subscriptions (over long-lived WebSocket):**
61
+ - `im.message.receive_v1`
62
+ - `card.action.trigger`
63
+ - `im.message.reaction.created_v1` / `deleted_v1` (optional)
64
+ - `im.chat.member.bot.added_v1` (optional)
65
+
66
+ After enabling those, run `lark-agent-bridge start` again. Once you see `✓ Connected`, find the bot in Feishu / Lark and start chatting.
67
+
68
+ ## Commands
69
+
70
+ ### Host CLI
71
+
72
+ ```
73
+ lark-agent-bridge start [-c <config>] Start the bot
74
+ lark-agent-bridge ps List all running start processes on this machine
75
+ lark-agent-bridge stop <id|#> Stop a start process (SIGTERM, SIGKILL after 2s)
76
+ lark-agent-bridge --help List all commands
77
+ ```
78
+
79
+ > When the same app is started multiple times, Lark's open platform routes events to one of the live WebSocket connections at random. `start` detects existing processes for the same app and (in a TTY) prompts: `[c]ontinue / [k]ill old / [a]bort`. In non-TTY mode it warns and continues.
80
+
81
+ `handover` / `workspace` / `service` are placeholders, planned for later releases.
82
+
83
+ ### Slash commands inside Feishu / Lark
84
+
85
+ | Command | Effect |
86
+ |---|---|
87
+ | `/new`, `/reset` | Clear the current chat's session |
88
+ | `/cd <path>` | Switch working directory (resets session) |
89
+ | `/ws list` | List named workspaces (card + buttons) |
90
+ | `/ws save <name>` | Save current cwd as a named workspace |
91
+ | `/ws use <name>` | Switch to a named workspace |
92
+ | `/ws remove <name>` | Delete a named workspace |
93
+ | `/status` | Current cwd / session / agent (card + buttons) |
94
+ | `/config` | Adjust preferences (reply style, tool-call display, ...) |
95
+ | `/stop` | Stop the run in progress (also the `⏹` button on the card) |
96
+ | `/timeout [N\|off\|default]` | Idle-watchdog (minutes) for the current session. `/config` sets the global default. See FAQ below. |
97
+ | `/retry <run-id>` | Replay a recent failed or timed-out run. Failed cards include a one-click retry button. |
98
+ | `/shell <command>` | Run a shell command in the current cwd and return stdout/stderr. Admin-only when admins are configured; capped at 30s and 12k chars. |
99
+ | `/workers` | Show Cursor SDK worker-pool health: status, queue count, current run, session/cwd, and recent error. |
100
+ | `/ps` | List all `start` processes on this host, marking the one replying |
101
+ | `/exit <id\|#>` | Stop a `start` process (your own → graceful; another's → SIGTERM) |
102
+ | `/reconnect` | Force a WebSocket reconnect (use when the bot stops responding after a network blip) |
103
+ | `/doctor [description]` | Feed recent logs, run timeline, and your description back to the agent for self-diagnosis |
104
+ | `/doctor workers` | Shortcut for the live SDK worker-pool view |
105
+ | `/help` | Help card |
106
+ | Any other `/xxx` | Forwarded verbatim to the agent |
107
+
108
+ **Reply policy**: in a DM, the bot replies to anything. In a **group (including topic groups), the bot only replies when `@`-mentioned** (default since 0.1.22); unmentioned messages are ignored. `@all` is never answered. Cloud-doc comments must mention the bot. To restore the older "always answer in groups" behaviour: `/config` → "Require @bot in groups" → No.
109
+
110
+ ## Data directories
111
+
112
+ | Path | Content |
113
+ |---|---|
114
+ | `~/.lark-channel/config.json` | App credentials (App ID / Secret), mode 600 |
115
+ | `~/.lark-channel/sessions.json` | Agent session id + cwd per chat / topic (+ optional `/timeout` override) |
116
+ | `~/.lark-channel/workspaces.json` | Named-workspace map |
117
+ | `~/.lark-channel/processes.json` | Process registry for live `start` instances (used by `ps`/`stop`); dead PIDs are auto-pruned |
118
+ | `~/.lark-channel/media/<chatId>/` | Downloaded images / files, cleaned up after 24h |
119
+ | `~/.lark-channel/logs/YYYY-MM-DD.log` | Structured run logs (JSONL), rotated daily; older than 7 days are pruned at startup (`LARK_CHANNEL_LOG_DAYS` env var overrides). `/doctor` reads these. |
120
+
121
+ > Upgrading from before 0.1.11? Run `lark-agent-bridge migrate` once — it moves anything under the legacy `~/.config/lark-channel-bridge/` and `~/.cache/lark-channel-bridge/` paths to the new location and upgrades `config.json` to the new schema.
122
+
123
+ ### Custom agent command
124
+
125
+ By default the bridge uses the Claude backend and appends Claude Code arguments to `claude`. To use a compatible wrapper, add `preferences.agentCommand` to `~/.lark-channel/config.json`:
126
+
127
+ ```json
128
+ {
129
+ "preferences": {
130
+ "agentCommand": {
131
+ "backend": "claude",
132
+ "command": "my-claude-wrapper",
133
+ "args": ["--model", "gpt-5.5"],
134
+ "claudeArgsOption": "--claude-args"
135
+ }
136
+ }
137
+ }
138
+ ```
139
+
140
+ With `claudeArgsOption`, the bridge safely joins Claude Code arguments and runs commands like `my-claude-wrapper --model gpt-5.5 --claude-args "-p ... --output-format stream-json --verbose ..."`. Without `claudeArgsOption`, it appends Claude args as normal argv entries. If `agentCommand` is omitted, it keeps using plain `claude`.
141
+
142
+ ### Cursor backend (`@cursor/sdk` or CLI)
143
+
144
+ To use Cursor Agent, configure the Cursor backend. The default runtime is `@cursor/sdk`: the bridge keeps a small LRU pool of persistent SDK agents, resumes the original Cursor session across messages, and exposes `/workers` plus `/doctor workers` for worker-pool diagnosis.
145
+
146
+ ```json
147
+ {
148
+ "preferences": {
149
+ "agentCommand": {
150
+ "backend": "cursor",
151
+ "command": "agent"
152
+ },
153
+ "agentCursorRuntime": "sdk",
154
+ "agentSessionPoolSize": 10,
155
+ "agentCursorApiKey": "${CURSOR_API_KEY}",
156
+ "agentCursorModel": "gpt-5.5-extra-high-fast"
157
+ }
158
+ }
159
+ ```
160
+
161
+ For SDK mode, provide a Cursor API key with `CURSOR_API_KEY`, or store it encrypted with `lark-agent-bridge secrets set --id cursor-api-key` and reference it from `agentCursorApiKey`. `agentCursorModel` uses Cursor CLI-style model ids; the bridge maps known variants to the SDK model-selection shape. For full control, set `agentCursorSdkModel` directly:
162
+
163
+ ```json
164
+ {
165
+ "preferences": {
166
+ "agentCursorSdkModel": {
167
+ "id": "gpt-5.5",
168
+ "params": [{ "id": "reasoning", "value": "extra-high" }]
169
+ }
170
+ }
171
+ }
172
+ ```
173
+
174
+ Set `"agentCursorRuntime": "cli"` to force the legacy Cursor CLI path. In CLI mode, make sure the `agent` command is installed, logged in, and available on `PATH`; the bridge runs `agent -p --output-format stream-json --trust --workspace <cwd> ...`. If you intentionally want Cursor to auto-allow commands, add `"-f"` or `"--force"` to `agentCommand.args`.
175
+
176
+ ## Access control (optional)
177
+
178
+ Out of the box the bot is **open**: anyone who can find it can DM it, any group member can `@`-mention it to trigger a run, and commands like `/account` or `/cd` are usable by all. **That's fine for personal use** — but for a shared team setup, or anywhere you don't want strangers calling `/cd /`, you can tighten three allowlists by sending `/config` inside Feishu.
179
+
180
+ ### Common scenarios
181
+
182
+ **Just me**
183
+
184
+ In the `/config` form:
185
+ - **Allowed users**: your own `open_id`
186
+ - Leave the other two blank
187
+
188
+ Messages from anyone else are silently dropped — no denial reply, since that would just confirm the bot exists to outsiders.
189
+
190
+ **A small team**
191
+
192
+ - **Allowed users**: comma-separated `open_id`s of team members
193
+ - Other two blank
194
+
195
+ **Bot only responds in specific work groups**
196
+
197
+ DMs are unaffected; only listed groups trigger responses:
198
+ - **Allowed chats**: comma-separated `chat_id`s of the groups
199
+ - DMs are **always** exempt from this list — so you can always DM the bot to change config later.
200
+
201
+ **Anyone can chat with the bot, but only I can change settings**
202
+
203
+ - **Admins**: your own `open_id`
204
+ - Other two blank
205
+
206
+ Others running `/account`, `/config`, `/exit`, `/reconnect`, `/doctor`, `/workers`, `/shell`, `/cd`, or `/ws` get a `❌ 此命令仅管理员可用` reply. Normal conversation (asking the bot to do things) is unaffected.
207
+
208
+ **Lock everything down**
209
+
210
+ Fill all three. The `/config` form catches common mistakes — e.g. if your admin list doesn't include yourself, or your chat allowlist doesn't include the chat you're submitting from, the submit is rejected with a message explaining why, so you can't accidentally lock yourself out.
211
+
212
+ ### Finding `open_id` and `chat_id`
213
+
214
+ Easiest path: have the target user send the bot a message (or `@`-mention it in the target group), then in your terminal:
215
+
216
+ ```bash
217
+ grep '"event":"enter"' ~/.lark-channel/logs/$(date +%Y-%m-%d).log | tail -5
218
+ ```
219
+
220
+ Every line carries `chatId` (group or DM id) and `senderId` (the user's `open_id`). Copy them from there.
221
+
222
+ The Feishu open-platform "Get user info" API also works but needs the `contact:user` scope, which is overkill if you just need a couple of IDs.
223
+
224
+ ### Worth knowing
225
+
226
+ - Changes take effect on the **next message** — no restart needed.
227
+ - An empty field means **unrestricted**, not "nobody allowed".
228
+ - To revert a restricted list back to fully open, clear that field in `/config` and submit.
229
+ - DMs are deliberately exempt from the chat allowlist — meaning if you ever accidentally restrict the bot out of every group, **DM the bot and send `/config`** to recover.
230
+
231
+ ### Advanced: editing the config file directly
232
+
233
+ The `/config` form writes to `~/.lark-channel/config.json` under `preferences.access`:
234
+
235
+ ```json
236
+ {
237
+ "preferences": {
238
+ "access": {
239
+ "allowedUsers": ["ou_xxxxxxxxxxxxx"],
240
+ "allowedChats": ["oc_xxxxxxxxxxxxx"],
241
+ "admins": ["ou_xxxxxxxxxxxxx"]
242
+ }
243
+ }
244
+ }
245
+ ```
246
+
247
+ After a manual edit, **restart the bridge** or send **`/reconnect`** from any allowed chat to pick up the changes. The form is usually faster; direct edits make sense mostly for deployment scripts where you want to pre-seed access policy.
248
+
249
+ ## FAQ
250
+
251
+ **The bot stays silent / Claude never replies.** Usually the `claude` CLI itself is not logged in, or the session points to a cwd that no longer exists. Send `/status` to inspect; `/new` to start a fresh session.
252
+
253
+ **Claude subprocess looks frozen (card stuck on the last frame).** Since 0.1.20 there's an idle watchdog: if Claude emits nothing for N minutes the process is killed and the card is annotated `⏱ N min no response, auto-terminated`. Disabled by default. Enable with `/config` (global, in minutes), or `/timeout 10` to set it on the current session; `/timeout off` disables for the session; `/timeout default` clears the session override.
254
+
255
+ **The card says the agent failed. Can I retry?** Failed and idle-timeout run cards include a one-click retry button when the bridge has the original run in recent history. You can also run `/retry <run-id>` in the same chat/topic. Retries are scoped to the original conversation so a failed task cannot be replayed from another chat by accident.
256
+
257
+ **How do I debug a stuck card or Cursor SDK worker?** Run `/doctor <what happened>` to analyze recent structured logs. The doctor prompt includes a run timeline covering `intake -> queue -> session -> agent -> card update -> done`, which helps identify whether the run stopped in Lark updates, session setup, or the agent. For Cursor SDK worker health only, run `/workers` or `/doctor workers`.
258
+
259
+ **Cursor SDK returns `status=error` with no detail.** The bridge treats opaque Cursor SDK run failures as fatal to that SDK worker: it surfaces the final reason, discards the worker, and creates a fresh worker/session on the next run. Recoverable rate-limit, network, and stale-session paths record how many automatic recovery steps happened before the final failure.
260
+
261
+ **Claude says it can't see the image I sent.** Upgrade to the latest version — releases before 0.1.0 had a filename-dedup bug.
262
+
263
+ ## License
264
+
265
+ [MIT](./LICENSE)
package/README.zh.md ADDED
@@ -0,0 +1,265 @@
1
+ # lark-agent-bridge
2
+
3
+ 把飞书 / Lark 消息和本地 coding-agent CLI(Claude Code、Cursor Agent,或其它兼容包装命令)打通的轻量 bot,用一条命令起服务,扫码绑应用,在飞书里和 agent 对话、让它读图 / 改代码。
4
+
5
+ [English README](./README.md)
6
+
7
+ ## 能干什么
8
+
9
+ - 在飞书(私聊直接发;群里 `@bot`)把消息转给本地 agent,agent 在你指定的工作目录里工作
10
+ - **流式卡片**:agent 的文本和工具调用实时出现在同一张卡片上,不用傻等
11
+ - **可靠的运行 UI**:失败或超时的任务会显示一键重试;卡片更新失败时会降级成普通 markdown,避免一直卡在“运行中”
12
+ - **会话延续**:每个 chat 独立 session,对话能接着上次说
13
+ - **抢占 + 批处理**:中途发新消息会打断旧任务;快速连发几条会合并成一次请求
14
+ - **多工作空间**:`/ws` 切换不同项目,session 自己重置
15
+ - **图片 / 文件**:直接发给 bot,agent 会读本地下载的文件路径
16
+ - **卡片按钮**:`/help` `/ws list` `/status` 返回交互卡片,点按钮直接操作
17
+
18
+ ## 前置条件
19
+
20
+ - Node.js **≥ 20**
21
+ - 已安装并登录一个受支持的 coding-agent 命令。默认运行 `claude`;也可以通过 `agentCommand` 使用兼容包装命令,或使用 Cursor CLI 的 `agent` 命令。
22
+ - 一个飞书 / Lark PersonalAgent 应用(首次启动的扫码向导能帮你创建)
23
+
24
+ ## 安装
25
+
26
+ ```bash
27
+ npm i -g lark-agent-bridge
28
+ # 或
29
+ pnpm add -g lark-agent-bridge
30
+ ```
31
+
32
+ 也可以不安装直接运行:
33
+
34
+ ```bash
35
+ npx -y lark-agent-bridge@latest start
36
+ ```
37
+
38
+ ## 首次启动
39
+
40
+ ```bash
41
+ lark-agent-bridge start
42
+ ```
43
+
44
+ 第一次跑会检测到没配置应用,**自动进入扫码向导**:
45
+
46
+ 1. 终端渲染一个二维码
47
+ 2. 用飞书 App 扫码
48
+ 3. 选择 / 创建 PersonalAgent 应用
49
+ 4. 成功后凭据写入 `~/.lark-channel/config.json`
50
+
51
+ ### 开放平台补齐 scope 和事件订阅
52
+
53
+ 向导只负责创建应用,平台侧还需要手动确认:
54
+
55
+ **权限 scope**:
56
+ - `im:message`
57
+ - `im:message:send_as_bot`
58
+ - `im:resource`
59
+
60
+ **事件订阅(使用长连接接收)**:
61
+ - `im.message.receive_v1`
62
+ - `card.action.trigger`
63
+ - `im.message.reaction.created_v1` / `deleted_v1`(可选)
64
+ - `im.chat.member.bot.added_v1`(可选)
65
+
66
+ 启用以后再次 `lark-agent-bridge start`,看到 `✓ 已连接` 就可以在飞书里找 bot 对话了。
67
+
68
+ ## 命令速查
69
+
70
+ ### 宿主 CLI
71
+
72
+ ```
73
+ lark-agent-bridge start [-c <config>] 启动 bot
74
+ lark-agent-bridge ps 列出本机所有正在跑的 start 进程
75
+ lark-agent-bridge stop <id|#> 终止指定 start 进程(SIGTERM,2s 后 SIGKILL)
76
+ lark-agent-bridge --help 列所有命令
77
+ ```
78
+
79
+ > 多开同一个 app 时,开放平台会把事件随机推到其中一个长连接。`start` 启动前会检测同 app 已有的进程,TTY 下提示 `[c]ontinue / [k]ill old / [a]bort` 三选;非 TTY 只 warn 并继续。
80
+
81
+ 其它命令(`status` / `doctor` / `handover` / `workspace` / `service`)是占位,后续版本补。
82
+
83
+ ### 在飞书里用的斜杠命令
84
+
85
+ | 命令 | 作用 |
86
+ |---|---|
87
+ | `/new` `/reset` | 清空当前 chat 的会话 |
88
+ | `/cd <path>` | 切换工作目录(会重置 session) |
89
+ | `/ws list` | 列所有命名工作空间(卡片 + 按钮) |
90
+ | `/ws save <name>` | 把当前 cwd 存为命名工作空间 |
91
+ | `/ws use <name>` | 切换到命名工作空间 |
92
+ | `/ws remove <name>` | 删除命名工作空间 |
93
+ | `/status` | 当前 cwd / session / agent(卡片 + 按钮) |
94
+ | `/config` | 调整偏好(消息回复方式、工具调用显示等) |
95
+ | `/stop` | 终止当前正在跑的 run(也可点卡片底部 ⏹ 终止 按钮) |
96
+ | `/timeout [N\|off\|default]` | 当前 session 的 idle 探活(分钟);`/config` 改全局默认。详见下方"常见问题 — Claude 子进程假死" |
97
+ | `/retry <run-id>` | 重放最近失败或超时的任务;失败卡片里也有一键重试按钮 |
98
+ | `/shell <command>` | 在当前 cwd 执行 shell 命令并回传 stdout/stderr。配置管理员后仅管理员可用;限制 30 秒和 12k 字符输出 |
99
+ | `/workers` | 查看 Cursor SDK worker pool 健康状态:状态、队列、当前 run、session/cwd 和最近错误 |
100
+ | `/ps` | 列出本机所有 start 进程,标识当前回复的是哪个 |
101
+ | `/exit <id\|#>` | 终止指定 start 进程(自己 = graceful 退出;他人 = SIGTERM) |
102
+ | `/reconnect` | 强制重连 WebSocket(网络抖动后 bot 没反应时用) |
103
+ | `/doctor [描述]` | 把最近运行日志和你的描述喂给 agent,自助诊断卡住 / 异常的原因 |
104
+ | `/doctor workers` | 直接查看当前 SDK worker pool 状态 |
105
+ | `/help` | 帮助卡片 |
106
+ | 其它 `/xxx` | 原样交给 agent |
107
+
108
+ **消息策略**:私聊 = 不需要 @,任何消息都回;**群(含话题群)= 默认要 @bot 才回**(0.1.22 起的新默认),不 @ 时 bot 完全沉默;@全员永远不响应;云文档评论必须 @bot。要恢复"群里也不强制 @"的老行为:`/config` → "群里需要 @ bot" → 选"否"。
109
+
110
+ ## 数据目录
111
+
112
+ | 路径 | 内容 |
113
+ |---|---|
114
+ | `~/.lark-channel/config.json` | 应用凭据(App ID / Secret),权限 600 |
115
+ | `~/.lark-channel/sessions.json` | 每个 chat / 话题 的 agent session id + cwd(+ 可选的 `/timeout` 覆盖) |
116
+ | `~/.lark-channel/workspaces.json` | 工作空间映射 |
117
+ | `~/.lark-channel/processes.json` | 当前在跑的 start 进程注册中心(`ps`/`stop` 用),死进程会被自动清理 |
118
+ | `~/.lark-channel/media/<chatId>/` | 下载的图片 / 文件,24h 自动清理 |
119
+ | `~/.lark-channel/logs/YYYY-MM-DD.log` | 结构化运行日志(JSON line),按天滚动;启动时清理超过 7 天的老文件(`LARK_CHANNEL_LOG_DAYS` 环境变量可改);`/doctor` 命令读它做诊断 |
120
+
121
+ > 升级自 0.1.11 之前的版本?跑一次 `lark-agent-bridge migrate` —— 自动把旧路径 `~/.config/lark-channel-bridge/` 和 `~/.cache/lark-channel-bridge/` 下的内容搬到新位置,并把 `config.json` 升级到新结构。
122
+
123
+ ### 自定义 agent 命令
124
+
125
+ 默认情况下 bridge 使用 Claude backend,并把 Claude Code 参数追加到 `claude` 后面。要使用兼容包装命令,可以在 `~/.lark-channel/config.json` 里加入 `preferences.agentCommand`:
126
+
127
+ ```json
128
+ {
129
+ "preferences": {
130
+ "agentCommand": {
131
+ "backend": "claude",
132
+ "command": "my-claude-wrapper",
133
+ "args": ["--model", "gpt-5.5"],
134
+ "claudeArgsOption": "--claude-args"
135
+ }
136
+ }
137
+ }
138
+ ```
139
+
140
+ 配置 `claudeArgsOption` 后,bridge 会把 Claude Code 参数安全拼成一个字符串,运行类似 `my-claude-wrapper --model gpt-5.5 --claude-args "-p ... --output-format stream-json --verbose ..."` 的命令。不配置 `claudeArgsOption` 时会把 Claude 参数作为普通 argv 追加;不配置 `agentCommand` 时仍然使用默认的 `claude`。
141
+
142
+ ### Cursor backend(`@cursor/sdk` 或 CLI)
143
+
144
+ 要接入 Cursor Agent,配置 Cursor backend。默认 runtime 是 `@cursor/sdk`:bridge 会维护一个小型 LRU 的持久 SDK agent 池,在多轮消息之间 resume 原 Cursor session,并提供 `/workers` 和 `/doctor workers` 用于诊断 worker pool。
145
+
146
+ ```json
147
+ {
148
+ "preferences": {
149
+ "agentCommand": {
150
+ "backend": "cursor",
151
+ "command": "agent"
152
+ },
153
+ "agentCursorRuntime": "sdk",
154
+ "agentSessionPoolSize": 10,
155
+ "agentCursorApiKey": "${CURSOR_API_KEY}",
156
+ "agentCursorModel": "gpt-5.5-extra-high-fast"
157
+ }
158
+ }
159
+ ```
160
+
161
+ SDK 模式需要 Cursor API key:可以通过环境变量 `CURSOR_API_KEY` 提供,也可以用 `lark-agent-bridge secrets set --id cursor-api-key` 加密保存后,在 `agentCursorApiKey` 里引用。`agentCursorModel` 使用 Cursor CLI 风格的模型 id;bridge 会把已知变体映射到 SDK model-selection 结构。如果想完全手写 SDK 选择,可以直接配置 `agentCursorSdkModel`:
162
+
163
+ ```json
164
+ {
165
+ "preferences": {
166
+ "agentCursorSdkModel": {
167
+ "id": "gpt-5.5",
168
+ "params": [{ "id": "reasoning", "value": "extra-high" }]
169
+ }
170
+ }
171
+ }
172
+ ```
173
+
174
+ 如果要强制使用旧的 Cursor CLI 路径,把 `"agentCursorRuntime"` 设为 `"cli"`。CLI 模式下需要确认 `agent` 命令已安装、已登录,并且在 `PATH` 中可用;bridge 会用类似 `agent -p --output-format stream-json --trust --workspace <cwd> ...` 的方式运行。如果你明确希望 Cursor 自动允许命令,可以把 `"-f"` 或 `"--force"` 加到 `agentCommand.args`。
175
+
176
+ ## 访问控制(可选)
177
+
178
+ 默认 bot 是"开放"的:任何能找到它的人都能私聊它,群里 @bot 就触发响应。**个人自己用 / 给朋友用,这就够了**——但如果想给团队用、或者怕在大群里被滥用,可以在飞书里发 `/config`,调下面三栏中的一栏或几栏。
179
+
180
+ ### 几种典型用法
181
+
182
+ **只让我自己用**
183
+
184
+ `/config` 表单里:
185
+ - "用户白名单":填你自己的 `open_id`
186
+ - 其它两栏留空
187
+
188
+ 之后非你发的消息会被 bot 静默丢弃——bot 不会回"你没权限"之类的话,免得暴露它存在。
189
+
190
+ **只让一小群同事用**
191
+
192
+ - "用户白名单":填同事们的 `open_id`,英文逗号分隔
193
+ - 其它两栏留空
194
+
195
+ **bot 只在指定工作群里干活**
196
+
197
+ 私聊不受影响;群里只有名单上的群才触发响应:
198
+ - "群白名单":填想让 bot 工作的群 `chat_id`,英文逗号分隔
199
+ - 私聊**永远**不受此约束——意味着你随时能 DM bot 调配置
200
+
201
+ **谁都能跟 bot 聊,但只有我能改设置**
202
+
203
+ - "管理员":填你自己的 `open_id`
204
+ - 其它两栏留空
205
+
206
+ 下次别人发 `/account` `/config` `/exit` `/reconnect` `/doctor` `/workers` `/shell` `/cd` `/ws` 这些敏感命令,会收到 `❌ 此命令仅管理员可用`。普通对话(让 bot 帮忙做事)不受影响。
207
+
208
+ **完全收紧**
209
+
210
+ 三栏全填。`/config` 表单会拦下常见误配——比如管理员名单里没把你自己加进去、群白名单里没包含当前会话,提交时会被拒绝并提示原因,不会让你不小心把自己锁在外面。
211
+
212
+ ### 怎么找 `open_id` 和 `chat_id`
213
+
214
+ 最快的办法:让目标用户给 bot 发一条任意消息(群的话就 @bot 一下),然后在终端:
215
+
216
+ ```bash
217
+ grep '"event":"enter"' ~/.lark-channel/logs/$(date +%Y-%m-%d).log | tail -5
218
+ ```
219
+
220
+ 每一行都带 `chatId`(= 群或私聊 ID)和 `senderId`(= 用户 `open_id`),照着复制就行。
221
+
222
+ 也可以查飞书开放平台的"获取用户信息"API,但要先给你的应用加 `contact:user` scope,没必要为了几个 ID 折腾。
223
+
224
+ ### 几点提醒
225
+
226
+ - 改完 `/config` **下一条消息**就生效,不用重启
227
+ - 把任何一栏设成**空字符串** = 不限制(不是"一个都不允许")
228
+ - 想从某种受限状态回到"完全开放",把对应栏目清空再提交即可
229
+ - 私聊不受"群白名单"约束——这是设计上故意的:万一你不小心把所有群都锁死了,**回到 bot 的私聊里发 `/config` 就能解锁**
230
+
231
+ ### 高级:直接改配置文件
232
+
233
+ 不太想登飞书也可以,`/config` 表单背后写的是 `~/.lark-channel/config.json` 的 `preferences.access`:
234
+
235
+ ```json
236
+ {
237
+ "preferences": {
238
+ "access": {
239
+ "allowedUsers": ["ou_xxxxxxxxxxxxx"],
240
+ "allowedChats": ["oc_xxxxxxxxxxxxx"],
241
+ "admins": ["ou_xxxxxxxxxxxxx"]
242
+ }
243
+ }
244
+ }
245
+ ```
246
+
247
+ 手改完之后**重启 bridge** 或者**找一个被允许的会话发 `/reconnect`** 让新配置生效。日常调整还是用 `/config` 表单更省事,直接改文件主要用在"部署脚本里预填"之类的场景。
248
+
249
+ ## 常见问题
250
+
251
+ **Claude 挂住不回复**:通常是 `claude` CLI 本身没登录,或者 session 指向了不存在的 cwd。发 `/status` 看当前状态;`/new` 重开会话往往就好。
252
+
253
+ **Claude 子进程假死(卡片停在最后一帧不动)**:从 0.1.20 起支持 idle 探活:claude 一段时间没输出就被 SIGTERM kill,卡片末尾会标 "⏱ N 分钟无响应,已自动终止"。默认关闭。开启方式:`/config` 设全局值(分钟),或 `/timeout 10` 只对当前 session 生效;`/timeout off` 关掉某个 session 的探活;`/timeout default` 清掉 session 覆盖回退到全局。
254
+
255
+ **卡片显示 agent 失败,可以重试吗?** 失败和 idle-timeout 卡片会在 bridge 保存了原始 run 的情况下显示一键重试按钮。也可以在同一个 chat / 话题里运行 `/retry <run-id>`。重试只允许在原会话范围内触发,避免一个失败任务被别的 chat 误重放。
256
+
257
+ **怎么排查卡片卡住或 Cursor SDK worker 异常?** 运行 `/doctor <现象描述>`,它会把最近结构化日志和 run timeline 喂给 agent 自诊断。timeline 覆盖 `intake -> queue -> session -> agent -> card update -> done`,能帮助判断问题停在 Lark 更新、session 准备还是 agent 本身。只看 Cursor SDK worker 状态可以运行 `/workers` 或 `/doctor workers`。
258
+
259
+ **Cursor SDK 返回没有细节的 `status=error` 怎么办?** bridge 会把这类不透明 Cursor SDK run 失败视为当前 SDK worker 的 fatal 错误:展示最终原因、丢弃该 worker,并在后续运行中使用新 worker/session。限流、网络和 stale-session 等可恢复路径会记录自动恢复步骤和最终失败原因。
260
+
261
+ **图片发过去 Claude 说看不到**:升级到最新版,0.1.0 之前的版本有文件名去重 bug。
262
+
263
+ ## 许可
264
+
265
+ [MIT](./LICENSE)
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ import '../dist/cli.js';