Skip to content

Commit e9b99b0

Browse files
committed
docs(feishu-bridge): spell out the two-identity setup, the scopes, and the failure modes
The Setup section said "a lark-cli you have already configured" and stopped there. That is not enough, because the bridge reads and writes through different identities and a half-configured app fails in a way the log does not make obvious. The bridge calls `--as user` for +chat-messages-list and +messages-resources-download, and `--as bot` for +messages-send, +messages-reply and +messages-edit. A bot cannot see a p2p conversation history, and only a bot may edit a message it sent, so both identities are genuinely required and they fail independently. Documented: - `lark-cli auth status` must show `identities.bot.status` and `identities.user.status` both `ready`. - The minimum scope set, split by identity: `im:message:readonly` and `im:resource` on the user side; `im:message` and `im:message:update` on the bot side. Called out `offline_access` separately, because without it the user token stops working after a couple of hours and the bridge goes quiet at a moment when nothing reports an error. - That the user identity is the user"s own account, so the bridge acts as them. - A p2p chat with the app as the simplest arrangement, which is what this Plugin is built and tested for. - First-run behaviour: the workspace is created on start and nothing historical is picked up, so the first message has to come after the bridge starts. - Obtaining a chat id, including the `--page-all` that the listing needs. Also added, because an operator hits them immediately and none were documented: - Running it in the background, with the reason the Plugin ships no autostart and the specific trap that a supervisor inheriting the child"s std handles blocks forever. This is the defect that cost the most time during development. - Uninstall: `--stop`, then delete the data directory. Removing the Plugin leaves that directory alone on purpose; it is user data. - Network: outbound HTTPS to the Feishu/Lark Open Platform only, no inbound listener, and `lark-cli` owns proxy configuration. - A symptom-to-cause table for the identity, scope, token-expiry, attachment, paging and lock failures, noting that every one of them writes a log line, so an empty log plus a silent chat means the watcher is not running. skills/SKILL.md carries the same identity and scope requirements in its pre-flight and troubleshooting sections, so the agent checks them before starting rather than after a user reports silence. Validation: `node scripts/validate.mjs` green, 84 passed / 0 failed.
1 parent 323deb8 commit e9b99b0

3 files changed

Lines changed: 201 additions & 33 deletions

File tree

‎plugins/antianqi/mcode-feishu-bridge/README.md‎

Lines changed: 119 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -43,31 +43,123 @@ with its normal tool access, and the answer comes back **in the same message**.
4343
The intermediate states and the final answer are the same Feishu message, so the
4444
conversation stays readable and nothing is duplicated.
4545
46-
## Install
46+
## Setup
4747
48-
The Plugin is Skill-only and has no `mcp.json` and no `package.json`. It needs two
49-
CLIs that you already have or can install yourself:
48+
The Plugin is Skill-only: no `mcp.json`, no `package.json`, no install step. But it
49+
does need three things lined up, and two of them are easy to get half-right.
5050
51-
| requirement | what it is | check |
51+
### 1. MiniMax Code
52+
53+
Installed and runnable:
54+
55+
```bash
56+
mcode --version
57+
```
58+
59+
### 2. A Feishu/Lark app, with **two** working identities
60+
61+
This is the part that trips people up. The bridge reads and writes through
62+
**different identities**, and both have to work:
63+
64+
| what | identity | why |
5265
|---|---|---|
53-
| [`lark-cli`](https://www.npmjs.com/package/@larksuite/cli) | the official Lark/Feishu CLI, already configured for your account | `lark-cli auth status` |
54-
| MiniMax Code | the agent, installed and runnable | `mcode --version` |
66+
| read the conversation, download attachments | **user** | the bot cannot see a p2p conversation's history |
67+
| send, reply, and edit the answer | **bot** | only a bot may edit a message it sent |
68+
69+
```bash
70+
lark-cli auth status
71+
```
5572

56-
Then configure a conversation and start the bridge:
73+
Both `identities.bot.status` and `identities.user.status` must read `ready`.
74+
If either is not, fix that before going further — otherwise the bridge reads
75+
nothing or writes nothing, and the log only says `fetch messages failed`.
76+
77+
The app needs at least these scopes, derived from the operations it performs:
78+
79+
| scope | needed for | identity |
80+
|---|---|---|
81+
| `im:message:readonly` | reading messages in the conversation | user |
82+
| `im:resource` | downloading an image or file you send | user |
83+
| `im:message` | sending the placeholder and the answer | bot |
84+
| `im:message:update` | editing that placeholder into the answer | bot |
85+
| `offline_access` | letting the user token refresh instead of expiring | user |
86+
87+
`offline_access` is the one people miss. Without it the user token stops working
88+
after a couple of hours and the bridge goes quiet, with no error at the time it
89+
happens. Note also that the user identity here is **your** account: the bridge acts
90+
as you, with everything that implies.
91+
92+
Publish the app (Feishu requires an app to be published before another user can
93+
talk to it), then open a chat with it. A p2p chat with your own app is the simplest
94+
arrangement and the one this Plugin is built and tested for.
95+
96+
### 3. A chat id
97+
98+
There is deliberately no default: a chat id is a private identifier, so it has to be
99+
yours to supply.
100+
101+
```bash
102+
lark-cli im +chat-list --types=p2p,group --page-all --as user --format json
103+
```
104+
105+
Find your conversation in the output. The `--page-all` matters — without it the
106+
listing silently truncates and you may not see the conversation you are looking for.
107+
108+
### 4. Start it
57109

58110
```bash
59111
node scripts/mcode-feishu-bridge.mjs --watch --chat <your-chat-id>
60112
```
61113

62-
How to obtain a chat id: send a message in the conversation, then
114+
On the first run the bridge creates a workspace for that conversation and picks up
115+
nothing historical: it starts from the newest message at the time it launches. Send
116+
it a message after starting, not before.
117+
118+
Full operating instructions are in
119+
[`skills/mcode-feishu-bridge/SKILL.md`](skills/mcode-feishu-bridge/SKILL.md).
120+
121+
## Running it in the background
122+
123+
The Plugin ships no service manager, on purpose: a login hook that runs a hidden
124+
process forever is exactly the kind of thing that should be a deliberate, visible
125+
choice by the person running the machine, not a default a Plugin installs.
126+
127+
If you do want it always on, run the watcher under whatever supervisor you already
128+
use, and make sure it inherits a real `PATH` so the bridge can find `lark-cli` and
129+
`mcode`:
130+
131+
```bash
132+
nohup node scripts/mcode-feishu-bridge.mjs --watch --chat <chat-id> >> bridge.log 2>&1 &
133+
```
134+
135+
Two rules that follow from how this is built:
136+
137+
- **Do not redirect the watcher's stdout from a supervisor that waits on the
138+
child's pipe.** A long-lived child spawned with inherited std handles keeps that
139+
pipe open, and the supervisor blocks forever. Start the watcher with std handles
140+
closed or detached, or let the bridge write its own log via `--log`.
141+
- **The bridge takes a single-instance lock, so a second copy is refused with exit
142+
code 3** rather than corrupting state. If that is not what you wanted, find the
143+
first one: `bridge.lock` in the data directory holds its pid.
144+
145+
## Uninstall
146+
147+
There is nothing to uninstall. To stop it and remove its data:
63148

64149
```bash
65-
lark-cli im +chat-messages-list --chat-id <chat-id> --as user --format json
150+
node scripts/mcode-feishu-bridge.mjs --stop
66151
```
67152

68-
or list your conversations with `lark-cli im +chat-list --types=p2p,group --page-all --as user`.
153+
Then delete the data directory (`$PLUGIN_DATA`, else `~/.mcode-feishu-bridge`).
154+
That removes the per-conversation workspaces and the downloaded media. Removing the
155+
Plugin from MiniMax Code leaves that directory alone, by design — it is the user's
156+
data, and a Plugin should not delete it on uninstall.
69157

70-
Full operating instructions are in [`skills/mcode-feishu-bridge/SKILL.md`](skills/mcode-feishu-bridge/SKILL.md).
158+
## Network
159+
160+
Outbound HTTPS to the Feishu/Lark Open Platform, through `lark-cli`. No inbound
161+
listener, no local port, no other host. If your machine reaches Feishu through a
162+
proxy, `lark-cli` needs it configured; the bridge does not set one.
71163

72164
## How it works
73165

@@ -225,6 +317,22 @@ next to the fix.
225317
if you write a launcher script for it; not applicable to this Plugin, which ships
226318
no `.ps1`.
227319

320+
## When it does not work
321+
322+
| symptom | most likely cause | check |
323+
|---|---|---|
324+
| no reply at all, log says `fetch messages failed` | the **user** identity is not usable: token expired, or `offline_access` was never granted | `lark-cli auth status` — look at `identities.user` |
325+
| the bridge never starts, exits 1 at launch | `lark-cli` or `mcode` is not on the `PATH` the bridge inherited | run `which lark-cli` / `which mcode` in that same environment |
326+
| a reply appears, but it is always the failure notice | the **bot** identity cannot send, so even the fallback reply fails | `lark-cli auth status` — look at `identities.bot` |
327+
| works for hours, then goes quiet | the user token expired because `offline_access` was not granted at authorisation time | re-authorise with `lark-cli auth login` and request that scope |
328+
| attachments are ignored | missing `im:resource` | the app's permission list |
329+
| nothing happens, and the log says the watermark is not on the page | the bridge fell too far behind and the full-pagination fallback also failed | restart the watcher; it re-reads the newest page |
330+
| a second watcher refuses to start with exit 3 | one is already running, which is the point | `bridge.lock` holds its pid; use `--stop` |
331+
| a turn is reported as timed out | mcode hung, most often on an interactive prompt | the notice names how many tool calls had already run; check the workspace |
332+
333+
None of these are silent: every one of them produces a line in `bridge.log`. If the
334+
log is empty and the chat is silent, the watcher is not running at all.
335+
228336
## License
229337

230338
Apache-2.0. See [LICENSE](LICENSE).

‎plugins/antianqi/mcode-feishu-bridge/skills/SKILL.md‎

Lines changed: 41 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,20 @@ be configured for the user. Check first:
2828
lark-cli auth status
2929
```
3030

31-
If that is not signed in, do not attempt to sign in for the user. Tell them to run
32-
`lark-cli config init` and `lark-cli auth login` themselves, and stop there.
31+
Two things must both be true, and they are the two that actually go wrong:
32+
33+
- `identities.bot.status` is `ready` — the bridge **reads with the user identity but
34+
writes with the bot identity**, because only a bot may edit a message it sent, and
35+
a bot cannot see a p2p conversation's history.
36+
- `identities.user.status` is `ready` and the token is `valid`.
37+
38+
The app needs at least `im:message:readonly` and `im:resource` for the user side, and
39+
`im:message` plus `im:message:update` for the bot side. `offline_access` matters most
40+
in practice: without it the user token stops working after a couple of hours and the
41+
bridge goes quiet with no error at the time it happens.
42+
43+
If `lark-cli` is not signed in, do not attempt to sign in for the user. Tell them to
44+
run `lark-cli config init` and `lark-cli auth login` themselves, and stop there.
3345

3446
`mcode` must be installed and runnable. The bridge discovers both CLIs from `PATH`
3547
and the install layout; it never hardcodes a path.
@@ -97,23 +109,33 @@ Useful flags:
97109

98110
## If the user says a message got no reply
99111

100-
Work through this order; the first three are the ones that actually happen:
112+
Work through this order; the first four are the ones that actually happen.
101113

102114
1. **Read the log.** The bridge logs to `<data-dir>/bridge.log` and the file is
103-
readable while it runs.
115+
readable while it runs. An empty log plus a silent chat means the watcher is not
116+
running at all — start it.
104117
- No `✓` line for that message means it was never picked up.
105-
- `✗ fetch messages failed` means `lark-cli` failed: the usual cause is an
106-
expired user token. Have the user re-run `lark-cli auth login`.
107-
- `✗ the watermark is not on this page` means the bridge fell too far behind;
108-
it escalates to full pagination on its own, so this line means that also failed.
109-
2. **Check the process and the lock.** `bridge.lock` in the data directory holds a
118+
- `✗ fetch messages failed` means the **user** identity failed. Usually an expired
119+
token, or `offline_access` was never granted. Have the user re-run
120+
`lark-cli auth login`.
121+
- The answer is a failure notice even though the message was picked up: the **bot**
122+
identity cannot send, so even the fallback reply failed.
123+
- `✗ the watermark is not on this page` means the bridge fell too far behind; it
124+
escalates to full pagination on its own, so this line means that also failed.
125+
2. **Check both identities.** `lark-cli auth status`. `identities.user` covers
126+
reading and attachments; `identities.bot` covers sending, replying and editing.
127+
They fail independently and the symptoms differ.
128+
3. **Check the process and the lock.** `bridge.lock` in the data directory holds a
110129
pid. A lock whose pid no longer exists is stale and is reclaimed automatically.
111-
3. **Check the watermark.** `state.json` records the last processed message per
130+
4. **Check the watermark.** `state.json` records the last processed message per
112131
chat. If it is ahead of the user's message, the message was already handled.
113-
4. **Run the tests.** `node scripts/mcode-feishu-bridge.test.mjs` is self-contained
132+
5. **Run the tests.** `node scripts/mcode-feishu-bridge.test.mjs` is self-contained
114133
and prints a pass/fail summary. It is the fastest way to tell whether the host
115134
is broken or the conversation simply outgrew one page.
116135

136+
`README.md` has a symptom-to-cause table covering the permission and identity
137+
failures, which are the ones users cannot diagnose from the log alone.
138+
117139
## Tests
118140

119141
```bash
@@ -128,6 +150,14 @@ module rather than a copy, so a green run says something about the shipped code.
128150
Two cases (`O` and `V`) read this plugin's own source. If you add a spawn call,
129151
keep `shell: false`; case `O` fails otherwise.
130152

153+
## Running it unattended
154+
155+
The Plugin ships no autostart mechanism, deliberately. If the user wants it always
156+
on, put it under a supervisor they already run, and warn them about one specific
157+
trap: **do not start a long-lived child with std handles inherited from a parent that
158+
waits on the pipe.** The caller blocks until the child closes it, which is forever.
159+
Start it detached, or let the bridge write its own log with `--log`.
160+
131161
## Network destinations
132162

133163
- `open.feishu.cn` / `open.larksuite.com`, through `lark-cli`, for message read,

‎plugins/antianqi/mcode-feishu-bridge/skills/mcode-feishu-bridge/SKILL.md‎

Lines changed: 41 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,20 @@ be configured for the user. Check first:
2828
lark-cli auth status
2929
```
3030

31-
If that is not signed in, do not attempt to sign in for the user. Tell them to run
32-
`lark-cli config init` and `lark-cli auth login` themselves, and stop there.
31+
Two things must both be true, and they are the two that actually go wrong:
32+
33+
- `identities.bot.status` is `ready` — the bridge **reads with the user identity but
34+
writes with the bot identity**, because only a bot may edit a message it sent, and
35+
a bot cannot see a p2p conversation's history.
36+
- `identities.user.status` is `ready` and the token is `valid`.
37+
38+
The app needs at least `im:message:readonly` and `im:resource` for the user side, and
39+
`im:message` plus `im:message:update` for the bot side. `offline_access` matters most
40+
in practice: without it the user token stops working after a couple of hours and the
41+
bridge goes quiet with no error at the time it happens.
42+
43+
If `lark-cli` is not signed in, do not attempt to sign in for the user. Tell them to
44+
run `lark-cli config init` and `lark-cli auth login` themselves, and stop there.
3345

3446
`mcode` must be installed and runnable. The bridge discovers both CLIs from `PATH`
3547
and the install layout; it never hardcodes a path.
@@ -97,23 +109,33 @@ Useful flags:
97109

98110
## If the user says a message got no reply
99111

100-
Work through this order; the first three are the ones that actually happen:
112+
Work through this order; the first four are the ones that actually happen.
101113

102114
1. **Read the log.** The bridge logs to `<data-dir>/bridge.log` and the file is
103-
readable while it runs.
115+
readable while it runs. An empty log plus a silent chat means the watcher is not
116+
running at all — start it.
104117
- No `✓` line for that message means it was never picked up.
105-
- `✗ fetch messages failed` means `lark-cli` failed: the usual cause is an
106-
expired user token. Have the user re-run `lark-cli auth login`.
107-
- `✗ the watermark is not on this page` means the bridge fell too far behind;
108-
it escalates to full pagination on its own, so this line means that also failed.
109-
2. **Check the process and the lock.** `bridge.lock` in the data directory holds a
118+
- `✗ fetch messages failed` means the **user** identity failed. Usually an expired
119+
token, or `offline_access` was never granted. Have the user re-run
120+
`lark-cli auth login`.
121+
- The answer is a failure notice even though the message was picked up: the **bot**
122+
identity cannot send, so even the fallback reply failed.
123+
- `✗ the watermark is not on this page` means the bridge fell too far behind; it
124+
escalates to full pagination on its own, so this line means that also failed.
125+
2. **Check both identities.** `lark-cli auth status`. `identities.user` covers
126+
reading and attachments; `identities.bot` covers sending, replying and editing.
127+
They fail independently and the symptoms differ.
128+
3. **Check the process and the lock.** `bridge.lock` in the data directory holds a
110129
pid. A lock whose pid no longer exists is stale and is reclaimed automatically.
111-
3. **Check the watermark.** `state.json` records the last processed message per
130+
4. **Check the watermark.** `state.json` records the last processed message per
112131
chat. If it is ahead of the user's message, the message was already handled.
113-
4. **Run the tests.** `node scripts/mcode-feishu-bridge.test.mjs` is self-contained
132+
5. **Run the tests.** `node scripts/mcode-feishu-bridge.test.mjs` is self-contained
114133
and prints a pass/fail summary. It is the fastest way to tell whether the host
115134
is broken or the conversation simply outgrew one page.
116135

136+
`README.md` has a symptom-to-cause table covering the permission and identity
137+
failures, which are the ones users cannot diagnose from the log alone.
138+
117139
## Tests
118140

119141
```bash
@@ -128,6 +150,14 @@ module rather than a copy, so a green run says something about the shipped code.
128150
Two cases (`O` and `V`) read this plugin's own source. If you add a spawn call,
129151
keep `shell: false`; case `O` fails otherwise.
130152

153+
## Running it unattended
154+
155+
The Plugin ships no autostart mechanism, deliberately. If the user wants it always
156+
on, put it under a supervisor they already run, and warn them about one specific
157+
trap: **do not start a long-lived child with std handles inherited from a parent that
158+
waits on the pipe.** The caller blocks until the child closes it, which is forever.
159+
Start it detached, or let the bridge write its own log with `--log`.
160+
131161
## Network destinations
132162

133163
- `open.feishu.cn` / `open.larksuite.com`, through `lark-cli`, for message read,

0 commit comments

Comments
 (0)