feat(feishu): interactive card menus and session/workspace list cards

- /m (or /menu) opens a card menu; /sessionlist and /workspacelist render
  cards with bind/switch buttons; number replies stay usable as a fallback
  when the app does not subscribe card.action.trigger.
- card.action.trigger callbacks validate the operator's open_id against the
  allowed senders: group members outside the allowlist can never drive
  binding, workspace switches or other card actions.
- Session-list pagination uses page numbers everywhere (buttons carry
  sessions:<page>), fixing the previous double page-size scaling that
  skipped pages past 20 sessions.
- Bind/workspace failures map to safe user-facing messages instead of raw
  error details.
- Apps registered after this change subscribe card.action.trigger during
  the scan flow.
This commit is contained in:
liuwenbo00 2026-08-21 10:03:23 +08:00
parent 832bd539a2
commit 6366404c42
9 changed files with 642 additions and 137 deletions

View file

@ -79,7 +79,7 @@ test('QR success stores the secret off-config and becomes immediately chat-ready
await flush();
assert.equal(fx.getSdkOptions().createOnly, true);
assert.equal(fx.getSdkOptions().addons.preset, false);
assert.deepEqual(fx.getSdkOptions().addons.events.items.tenant, ['im.message.receive_v1']);
assert.deepEqual(fx.getSdkOptions().addons.events.items.tenant, ['im.message.receive_v1', 'card.action.trigger']);
assert.ok(fx.getSdkOptions().addons.scopes.tenant.includes('im:message.p2p_msg:readonly'));
assert.ok(fx.getSdkOptions().addons.scopes.tenant.includes('im:message:readonly'));
assert.ok(fx.getSdkOptions().addons.scopes.tenant.includes('im:message:send_as_bot'));