事件订阅需要本地常驻总线,请在来源设备使用应用身份
--as bot执行。云端仅支持event list和event schema,不要在云端启动 consume、stop、status 或后台总线。
Wegent 本地与云端运行
- 当前
SKILL.md所在目录的../..是插件根目录。首次调用前,macOS/Linux 运行sh "<插件根目录>/scripts/ensure-lark-ready.sh";Windows 运行powershell -NoProfile -ExecutionPolicy Bypass -File "<插件根目录>\scripts\ensure-lark-ready.ps1"。 - 下文的
lark-cli ...是逻辑命令。实际执行时,macOS/Linux 使用sh "<插件根目录>/scripts/run-lark-cli.sh" ...;Windows 使用powershell -NoProfile -ExecutionPolicy Bypass -File "<插件根目录>\scripts\run-lark-cli.ps1" ...。 - 应用配置和首次用户 OAuth 在本机原连接入口完成;Wegent 在后台托管用户 OAuth(
lark)及应用凭据(lark-app)。云端准备脚本只检查托管认证,不安装 CLI 或发起登录。实际命令必须经过上述包装器,不能直接调用裸 CLI、读取认证文件或索取 Token。用户调用默认--as user,应用调用显式--as bot,两种云端授权独立管理。托管请求正文通过参数或相对路径文件传入,不使用 stdin。托管状态失效或需要增量授权时,在本机原连接入口重新连接;不要在云端执行 auth/config/profile 命令。
Lark Events
Prerequisite: Read
../lark-shared/SKILL.mdfirst for authentication,--as user/botswitching,Permission deniedhandling, and safety rules.
Core commands
| Command | Purpose |
|---|---|
lark-cli event list [--json] |
List all subscribable EventKeys |
lark-cli event schema <EventKey> [--json] |
Show an EventKey's params and output schema |
lark-cli event consume <EventKey> [flags] |
Blocking consume; events → stdout NDJSON |
lark-cli event status [--json] [--fail-on-orphan] |
Inspect the local bus daemon status |
lark-cli event stop [--all] [--force] |
Stop the bus daemon |
Common flags
| Flag | Description |
|---|---|
--param key=value / -p |
Business params (repeatable; comma-separated for multi-value). Unknown keys fail with valid names listed inline |
--jq <expr> |
jq expression to filter / transform each event; empty output skips the event |
--max-events N |
Exit after N events. Default 0 = unlimited |
--timeout D |
Exit after duration D (e.g. 30s, 2m). Default 0 = no timeout. Whichever of --max-events / --timeout fires first wins |
--output-dir <dir> |
Write each event as a file (relative paths only; prevents traversal) |
--quiet |
Suppress stderr diagnostics. AI should not use this — it silences the ready marker |
--as user|bot|auto |
Identity for the session (see lark-shared) |
Examples
# Default: stream every event for the key (no filter, no projection)
lark-cli event consume im.message.receive_v1 --as bot
# Grab one sample event to inspect payload shape
lark-cli event consume im.message.receive_v1 --max-events 1 --timeout 30s --as bot
# Run for 10 minutes then auto-exit
lark-cli event consume im.message.receive_v1 --timeout 10m --as bot
# Consume multiple EventKeys concurrently using the host executor's background-process support.
# Start each logical command as its own process and capture each stdout stream separately.
lark-cli event consume im.message.receive_v1 --as bot
lark-cli event consume im.message.reaction.created_v1 --as bot
Call flow
lark-cli event list --json→ pick a legal keylark-cli event schema <key> --json→ readresolved_output_schema+jq_root_pathto determine field pathslark-cli event consume <key> [--jq '<expr>']→ consume
Subprocess contract
Ready marker
event consume's stderr emits a fixed line [event] ready event_key=<key>. Parent processes should block on stderr until this line appears, then start reading stdout. Do not fall back to sleep.
stdin EOF = graceful exit
event consume treats stdin close as a shutdown signal (wired for AI subprocess callers). Bounded runs are exempt: when --max-events or --timeout is set (> 0), stdin EOF is ignored and the run exits only via its own bound, timeout, or SIGTERM. For unbounded runs, < /dev/null / nohup / systemd's default StandardInput=null will cause an immediate graceful exit (stderr reason: signal). To keep an unbounded run alive:
- Feed stdin a source that never EOFs:
< <(tail -f /dev/null) - Or run bounded:
--max-events N/--timeout D
Exit codes & reason
On exit, the last stderr line is [event] exited — received N event(s) in Xs (reason: ...).
| exit code | reason | Trigger |
|---|---|---|
| 0 | reason: limit |
--max-events reached |
| 0 | reason: timeout |
--timeout reached |
| 0 | reason: signal |
Ctrl+C / SIGTERM / stdin EOF (stdin EOF applies to unbounded runs only) |
| 1 | JSON error envelope on stderr | Lark API business failure during pre-consume setup (for example subscription create/delete) |
| 2 | JSON error envelope on stderr (no exited line) |
Validation failure (unknown EventKey, bad --param / --jq, another bus already connected) |
| 3 | JSON error envelope on stderr | Auth failure (missing token, missing scopes) |
| 4 / 5 | JSON error envelope on stderr | Network / internal failure (bus startup, handshake, file I/O) |
Startup and runtime failures emit a structured JSON envelope on stderr: {"ok":false,"error":{"type","subtype","param","message","hint",...}} (the envelope may also carry top-level identity / _notice siblings). Parse error.type / error.subtype to branch (e.g. missing_scope carries a missing_scopes list), error.param to find the offending flag, and error.hint for the recovery action — do not regex-match message text.
Orchestrators should treat reason: limit/timeout/signal (all exit 0) as "business completion" and non-zero as "failure".
Never kill -9
Avoid kill -9 on consume processes: for EventKeys with a PreConsume hook (those that register server-side subscriptions via OAPI), kill -9 skips the OAPI unsubscribe and leaks server-side subscriptions (symptoms: "subscription already exists" on restart, duplicate event delivery). Prefer SIGTERM or closing stdin.
One consume, one EventKey (multi-key = multi-shell)
The command takes exactly one positional argument; k1,k2 and wildcards are unsupported. Listening to N keys means N subprocesses — this is intentional:
- One shape per process stdout; no dispatcher logic required in the AI
- Fault isolation (one key failing doesn't affect others)
- Independent
--as/--jq/--max-events/--timeoutper key
All N consumers share a single bus daemon (UDS local IPC), so the overhead is small
Writing jq via schema
event schema <key> --json is the source of truth for writing --jq. Four things to look at:
(1) Where fields start — see jq_root_path
- Value
"."→ fields are at the top level, write.chat_id - Value
".event"→ fields are inside a V2 envelope, write.event.chat_id
(2) Field list and types — see resolved_output_schema.properties.<name>
Each field carries type / description, and some also have format. Snippet (from event schema im.message.receive_v1 --json):
{
"chat_id": {"type":"string", "format":"chat_id", "description":"Chat ID, prefixed with oc_"},
"sender_id": {"type":"string", "format":"open_id", "description":"Sender open_id, prefixed with ou_"},
"create_time": {"type":"string", "format":"timestamp_ms", "description":"Send time as ms-epoch string"}
}
(3) Field semantics — see the format tag
Lark-defined semantic tags (not JSON Schema's standard format). Common values: open_id / chat_id / message_id / timestamp_ms / email. Purpose: distinguish "same string type, different meanings" fields so you can reverse-lookup via API or convert formats.
(4) Decoded state — read the field's description
event consume runs Process hooks that may pre-decode some payload fields (flattening V2 envelopes, rendering .content to plain text, etc.) — behavior differs from raw OAPI. Always read the field's description before writing jq, especially for generic field names like content / data / body / payload.
Why it matters: blindly applying fromjson to an already-decoded text field makes jq error on every event and silently drop it — the consumer looks alive but emits nothing, with only a single WARN line buried on stderr. (This is the general behavior: any jq runtime error skips the event with a one-line WARN; the loop does not abort.)
Don't shortcut the schema: when projecting event schema --json with jq, do not strip .description from properties — that's the field that tells you whether a field is already decoded. Dump the full property objects, not just keys.
Aside: --param's valid parameters also live in the schema — the params section lists name / type / required / enum / default / description; section missing = this key accepts no --param.
Topic index
| Topic | Reference | Coverage |
|---|---|---|
| IM | references/lark-event-im.md |
Catalog of 12 IM EventKeys + shape notes (flat vs V2 envelope) + im.message.receive_v1 field gotchas (sender_id is open_id only; .content is plain text except for interactive cards) + common jq recipes (filter by chat_type / message_type / sender); for card.action.trigger see also ../lark-im/references/lark-im-card-action-reply.md |
| Task | references/lark-event-task.md |
Catalog of 1 Task EventKey (task.task.update_user_access_v2) + Native V2 envelope shape + task commit types + user/bot subscription notes |
| VC | references/lark-event-vc.md |
Catalog of 4 VC EventKeys (vc.meeting.participant_meeting_started_v1, vc.meeting.participant_meeting_joined_v1, vc.meeting.participant_meeting_ended_v1, vc.note.generated_v1) + field reference + source type semantics (meeting only) |
| Minutes | references/lark-event-minutes.md |
Catalog of 1 Minutes EventKey (minutes.minute.generated_v1) + field reference + source type semantics (meeting only) |
| Whiteboard | references/lark-event-whiteboard.md |
Catalog of 1 Board EventKey (board.whiteboard.updated_v1) + per-whiteboard subscription model (requires -p whiteboard_id=<token>) + payload field reference (whiteboard_id / operator_ids triple-id) |