lark-relay 0.4.1 → 0.4.2

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.
package/README.md CHANGED
@@ -58,7 +58,10 @@ Claude Code 用 `run_in_background` 起,进程退出会主动通知 AI,这才是
58
58
  lark-relay take --app <app> --chats oc_xxx --render text
59
59
  ```
60
60
 
61
- 空参数跑一下会输出完整的照做指导(查群 ID -> 验 bot 在群 -> 监听三步)。
61
+ 空参数跑一下会输出完整的照做指导(查群 ID -> 验 bot 在群 -> 监听三步),exit 0
62
+ 起来后 stderr 先打一行「监听中」-- 没这行就是没起来,看 stderr 报错。
63
+ 退出码:0=有一批,4=超时没消息(正常,再起一个),2=参数错,3=游标被占用。
64
+ 参数拼错(如 `--chat-id`)会硬失败并给出改写后的命令,不静默吞掉。
62
65
 
63
66
  业务过滤只有 `--filter`(jq 表达式)一个口子,不为每个业务加参数。
64
67
 
package/bin/lark-relay.js CHANGED
@@ -19,8 +19,9 @@ const VERSION = require('../package.json').version
19
19
  const TAKE_KEYS = ['app', 'chats', 'name', 'filter', 'debounce', 'max-wait', 'timeout', 'render', 'since']
20
20
  const COLLECT_KEYS = ['exclude', 'retain', 'ledger-retain']
21
21
 
22
- // 未知参数 -> stderr 短错误 + 候选提示,exit 2。不打印完整用法 —— 那看着像 --help 成功
23
- function rejectUnknown(cmd, unknown, allowed) {
22
+ // 未知参数 -> stderr 短错误 + 候选提示 + 改写后的命令,exit 2
23
+ // 不打印完整用法 -- 那看着像 --help 成功,会被当成「正常退出但没消息」
24
+ function rejectUnknown(cmd, argv, unknown, allowed) {
24
25
  const names = [...new Set(unknown)]
25
26
  const shown = names.map((k) => `--${k}`).join(', ')
26
27
  let hint = ''
@@ -31,10 +32,50 @@ function rejectUnknown(cmd, unknown, allowed) {
31
32
  break
32
33
  }
33
34
  }
34
- process.stderr.write(`未知参数:${shown}${hint}\n跑 \`lark-relay ${cmd}\` 看完整用法\n`)
35
+ const rewrite = rewriteArgv(argv, names, allowed)
36
+ const tail = rewrite.length
37
+ ? `\n你是不是想跑这个?\n lark-relay ${cmd} ${rewrite.join(' ')}`
38
+ : ''
39
+ process.stderr.write(`未知参数:${shown}${hint}${tail}\n跑 \`lark-relay ${cmd}\` 看完整用法\n`)
35
40
  return 2
36
41
  }
37
42
 
43
+ // 把未知参数替换成候选,拼出可照跑的命令行;无候选的未知参数(连同它的值)丢弃
44
+ function rewriteArgv(argv, unknown, allowed) {
45
+ const map = new Map()
46
+ for (const k of unknown) {
47
+ const s = suggest(k, allowed)
48
+ if (s && !map.has(k)) map.set(k, s)
49
+ }
50
+ if (!map.size) return []
51
+ const out = []
52
+ for (let i = 0; i < argv.length; i++) {
53
+ const a = argv[i]
54
+ if (a.startsWith('--') && a !== '--') {
55
+ const eq = a.indexOf('=')
56
+ const k = eq !== -1 ? a.slice(2, eq) : a.slice(2)
57
+ if (map.has(k)) {
58
+ out.push(eq !== -1 ? `--${map.get(k)}=${a.slice(eq + 1)}` : `--${map.get(k)}`)
59
+ if (eq === -1) {
60
+ const next = argv[i + 1]
61
+ if (next !== undefined && !next.startsWith('--')) {
62
+ out.push(next)
63
+ i++
64
+ }
65
+ }
66
+ continue
67
+ }
68
+ if (eq === -1) {
69
+ const next = argv[i + 1]
70
+ if (next !== undefined && !next.startsWith('--')) i++
71
+ }
72
+ continue
73
+ }
74
+ out.push(a)
75
+ }
76
+ return out
77
+ }
78
+
38
79
  const USAGE = `lark-relay ${VERSION} —— Lark 事件中继站
39
80
 
40
81
  lark-relay collect 底座:全部 profile 各起 consume → 原子落盘(systemd 常驻)
@@ -72,15 +113,17 @@ async function main() {
72
113
  return await cmdStatus(rest)
73
114
  case 'dispatch':
74
115
  return await cmdDispatch(rest)
75
- default:
76
- process.stderr.write(`未知命令:${cmd}\n\n${USAGE}\n`)
116
+ default: {
117
+ const s = suggest(cmd, ['collect', 'take', 'dispatch', 'status', 'guide'])
118
+ process.stderr.write(`未知命令:${cmd}${s ? `。你是不是想写 ${s}?` : ''}\n\n${USAGE}\n`)
77
119
  return 2
120
+ }
78
121
  }
79
122
  }
80
123
 
81
124
  async function cmdCollect(argv) {
82
125
  const a = parseArgs(argv, { flags: ['help'], keys: COLLECT_KEYS })
83
- if (a._unknown.length) return rejectUnknown('collect', a._unknown, COLLECT_KEYS)
126
+ if (a._unknown.length) return rejectUnknown('collect', argv, a._unknown, COLLECT_KEYS)
84
127
  if (a.help) {
85
128
  process.stdout.write(`${help.COLLECT_HELP}\n`)
86
129
  return 0
@@ -95,11 +138,11 @@ async function cmdCollect(argv) {
95
138
 
96
139
  async function cmdTake(argv) {
97
140
  const a = parseArgs(argv, { flags: ['help'], keys: TAKE_KEYS })
98
- if (a._unknown.length) return rejectUnknown('take', a._unknown, TAKE_KEYS)
141
+ if (a._unknown.length) return rejectUnknown('take', argv, a._unknown, TAKE_KEYS)
99
142
  const larkcli = require('../lib/larkcli')
100
143
 
101
- // 空参数输出完整照做指导,而非报错 —— AI 的真实行为是「先空参数跑一下看看」。
102
- // 但带了参数又缺 app/chats 的走下面短错误 —— 打印完整用法和 --help 长得一样,
144
+ // 空参数输出完整照做指导,而非报错 -- AI 的真实行为是「先空参数跑一下看看」。
145
+ // 但带了参数又缺 app/chats 的走下面短错误 -- 打印完整用法和 --help 长得一样,
103
146
  // 会被当成「正常退出但没消息」(实测漏看 bot 第一条 Working)。
104
147
  if (a.help || argv.length === 0) {
105
148
  let apps = []
@@ -107,7 +150,7 @@ async function cmdTake(argv) {
107
150
  apps = (await larkcli.listProfiles()).filter((p) => p.usable).map((p) => p.name)
108
151
  } catch {}
109
152
  process.stdout.write(`${help.takeHelp(apps)}\n`)
110
- return a.help ? 0 : 2
153
+ return 0 // 空参 = 求教,和 --help 一样是成功路径
111
154
  }
112
155
 
113
156
  const store = require('../lib/store')
@@ -116,7 +159,7 @@ async function cmdTake(argv) {
116
159
  const { acquireExclusive } = require('../lib/lock')
117
160
 
118
161
  if (!a.app || a.app === true) {
119
- process.stderr.write('需要 --app <name>。跑 `lark-relay take` 看完整用法\n')
162
+ process.stderr.write('需要 --app <name>(= lark-cli profile 名)。完整步骤跑 `lark-relay take`\n')
120
163
  return 2
121
164
  }
122
165
  const chats = parseChats(a.chats === true ? '' : a.chats)
@@ -155,6 +198,14 @@ async function cmdTake(argv) {
155
198
  const maxWaitMs = num(a['max-wait'], 25) * 1000
156
199
  const timeoutMs = num(a.timeout, 12 * 3600) * 1000
157
200
 
201
+ // 起来先打一行「监听中」-- 没这行就是没起来。
202
+ // 反馈实测:进程静默退出(参数错)被当成「在监听」,漏看 bot 第一条 Working
203
+ const fmtDur = (ms) => (ms >= 3600000 ? `${ms / 3600000}h` : `${ms / 1000}s`)
204
+ process.stderr.write(
205
+ `监听中:app=${a.app} 群=${chats.length} 个(${chats.join(',')}) 游标=${name} ` +
206
+ `防抖=${debounceMs / 1000}s 最长等=${fmtDur(timeoutMs)}\n`,
207
+ )
208
+
158
209
  const ac = new AbortController()
159
210
  const onSig = () => ac.abort()
160
211
  process.on('SIGTERM', onSig)
@@ -172,7 +223,7 @@ async function cmdTake(argv) {
172
223
  })
173
224
 
174
225
  if (!res.events.length) {
175
- process.stderr.write(`没等到消息(${res.reason})。游标身份 ${name}\n`)
226
+ process.stderr.write(`没等到消息(${res.reason})。游标身份 ${name},正常退出,再起一个继续\n`)
176
227
  return res.reason === 'timeout' ? 4 : 0
177
228
  }
178
229
 
@@ -184,7 +235,7 @@ async function cmdTake(argv) {
184
235
 
185
236
  async function cmdStatus(argv) {
186
237
  const a = parseArgs(argv, { flags: ['json', 'help'], keys: [] })
187
- if (a._unknown.length) return rejectUnknown('status', a._unknown, ['json'])
238
+ if (a._unknown.length) return rejectUnknown('status', argv, a._unknown, ['json'])
188
239
  if (a.help) {
189
240
  process.stdout.write(`${help.STATUS_HELP}\n`)
190
241
  return 0
@@ -198,7 +249,7 @@ async function cmdStatus(argv) {
198
249
 
199
250
  async function cmdDispatch(argv) {
200
251
  const a = parseArgs(argv, { flags: ['list', 'once', 'help'], keys: [] })
201
- if (a._unknown.length) return rejectUnknown('dispatch', a._unknown, ['list', 'once'])
252
+ if (a._unknown.length) return rejectUnknown('dispatch', argv, a._unknown, ['list', 'once'])
202
253
  if (a.help) {
203
254
  process.stdout.write(`${help.DISPATCH_HELP}\n`)
204
255
  return 0
package/lib/help.js CHANGED
@@ -1,7 +1,8 @@
1
1
  'use strict'
2
2
 
3
- // 帮助文本是 AI 的主入口 —— 实际行为是「先空参数跑一下看看」,
3
+ // 帮助文本是 AI 的主入口 -- 实际行为是「先空参数跑一下看看」,
4
4
  // 故 take 空参输出必须是完整的照做指导,而非报错。
5
+ // 所有提示按「AI 读到后下一步该做什么」来写:给完整命令、给退出码语义、给判据。
5
6
  //
6
7
  // ⚠️ 公网包:示例只用占位符(oc_xxx/<app>/ou_xxx),绝不含真实 chat_id/open_id/profile 名。
7
8
 
@@ -9,34 +10,41 @@ function takeHelp(apps) {
9
10
  const appLine = apps && apps.length ? apps.join(' ') : '(跑 `lark-cli profile list` 看)'
10
11
  return `需要 --app 和 --chats。监听要求 bot 已在群内。
11
12
 
12
- 一、查群 ID(用户身份;bot 搜不到自己没加入的群)
13
+ 一、查群 ID(用户身份;bot 搜不到自己没加入的群)
13
14
  lark-cli --profile <app> im +chat-search --as user --query "<群名>"
14
- 取 oc_ 开头的 chat_id;多个匹配时向用户确认是哪个
15
+ -> 取 oc_ 开头的 chat_id;多个匹配时向用户确认是哪个
15
16
 
16
- 二、验 bot 在群(用户身份;bot 不在群时用 bot 身份查不到,无法作判据)
17
+ 二、验 bot 在群(用户身份;bot 不在群时用 bot 身份查不到,不能作判据)
17
18
  lark-cli --profile <app> im chat.members bots --as user --params '{"chat_id":"oc_xxx"}'
18
- bots[] 含本 app 才算在群
19
- 不在群:请用户拉 bot 进群(对群可见动作,先征得同意)
19
+ -> bots[] 含本 app 才算在群
20
+ -> 不在群:请用户拉 bot 进群(对群可见动作,先征得同意)
20
21
 
21
- 三、监听(Claude Code 用 run_in_background 起,有消息会自动通知你)
22
+ 三、监听(Claude Code 用 run_in_background 起,有消息会自动通知你)
22
23
  lark-relay take --app <app> --chats oc_xxx --render text
23
24
 
24
- 阻塞等消息,有一批就输出并退出(默认防抖 5s、最长等 12h)。
25
+ 起来后 stderr 先打一行「监听中 ...」-- 没这行就是没起来,看 stderr 报错。
26
+ 阻塞等消息,有一批就吐到 stdout 并退出(默认防抖 5s、最长等 12h)。
25
27
  处理完再起一个,如此循环。
26
28
 
27
- 可用 app:${appLine}
29
+ 可用 app:${appLine}
28
30
 
29
31
  参数
30
32
  --app <name> 必填,= lark-cli profile 名
31
- --chats <ids> 必填,oc_ 开头;逗号分隔多个;或 @文件(首列 chat_id
33
+ --chats <ids> 必填,oc_ 开头;逗号分隔多个;或 @文件(首列 chat_id)
32
34
  --name <n> 游标身份,省略时按 app+chats 自动派生
33
35
  --filter <jq> 业务过滤,如 '.sender_id != "ou_xxx"'
34
- --debounce N 防抖秒数(默认 5;聊天场景建议 15
35
- --render text 按群分组紧凑文本(默认 NDJSON
36
- --since now|all 起始位置(默认 now,不重放历史)
36
+ --debounce N 防抖秒数(默认 5;聊天场景建议 15)
37
+ --render text 按群分组紧凑文本(默认 NDJSON)
38
+ --since now|all 起始位置(默认 now,不重放历史)
39
+
40
+ 退出码
41
+ 0 吐了一批(stdout);空参求教也是 0
42
+ 2 参数错 -- stderr 有短错误和正确写法,照着重跑
43
+ 3 游标身份被另一个 take 占用 -- 换 --name
44
+ 4 超时没消息 -- 正常,再起一个继续
37
45
 
38
46
  最佳实践
39
- · 用 run_in_background 起,不要前台跑 —— 会阻塞会话最长 12h
47
+ · 用 run_in_background 起,不要前台跑 -- 会阻塞会话最长 12h
40
48
  · 一批处理完再起下一个;不需要自己写 while 循环
41
49
  · 拿不到消息先查第二步:bot 不在群是最常见原因
42
50
  · 多个消费者盯同一 app 用不同 --name,游标互不干扰`
@@ -44,109 +52,113 @@ function takeHelp(apps) {
44
52
 
45
53
  const COLLECT_HELP = `把全部 lark-cli profile 的事件收下来,原子落盘。零参数、零配置。
46
54
 
47
- lark-relay collect # 前台跑(调试)
48
- systemctl enable --now lark-relay-collect # 常驻(推荐)
55
+ lark-relay collect # 前台跑(调试)
56
+ systemctl enable --now lark-relay-collect # 常驻(推荐)
49
57
 
50
58
  app 列表实时读 \`lark-cli profile list\`,新增 profile 自动纳入,无需改配置。
51
59
  每 app 一个子进程;某个挂了单独重启,不影响其他。
52
- tokenStatus 为 expired 的 profile 自动跳过并 warn(永久失败,重试是死循环)。
60
+ tokenStatus 为 expired 的 profile 自动跳过并 warn(永久失败,重试是死循环)。
53
61
  用户重新 login 后下次启动自动纳入。
54
62
 
55
- 为什么必须常驻:lark 事件是流式的,进程不在的时刻消息永久丢失(实测:消息发出
56
- 8s 后才起 consumer,收到 0 条)。collect 独立常驻,才能让 take/dispatch 侧
63
+ 为什么必须常驻:lark 事件是流式的,进程不在的时刻消息永久丢失(实测:消息发出
64
+ 8s 后才起 consumer,收到 0 条)。collect 独立常驻,才能让 take/dispatch 侧
57
65
  崩了、claude 跑 30 分钟、会话关几小时,都不丢消息。
58
66
 
59
67
  落盘:~/.lark-relay/store/<app>/<YYYY-MM-DD>/<纳秒>_<pid>_<seq>.json
60
- 回收由 collect 进程内每小时自查一次,删超期的整个日期目录(不另起 gc 单元/timer)。
68
+ 回收由 collect 进程内每小时自查一次,删超期的整个日期目录(不另起 gc 单元/timer)。
61
69
 
62
70
  参数
63
- --exclude <apps> 排除指定 profile(逗号分隔)
64
- --retain N store 保留天数(默认 3
65
- --ledger-retain N ledger 保留天数(默认 90)`
71
+ --exclude <apps> 排除指定 profile(逗号分隔)
72
+ --retain N store 保留天数(默认 3)
73
+ --ledger-retain N ledger 保留天数(默认 90)`
66
74
 
67
75
  const STATUS_HELP = `一眼看清全局:谁在跑、积压多少、游标在哪。
68
76
 
69
77
  lark-relay status # 概览
70
78
  lark-relay status --json # 机器可读
71
79
 
72
- 排障入口:某消费者「落后」很多 它的 take/dispatch 没在跑或卡住;
80
+ 排障入口:某消费者「落后」很多 -> 它的 take/dispatch 没在跑或卡住;
73
81
  「无消费者」= 白采,可考虑 --exclude。`
74
82
 
75
- const DISPATCH_HELP = `常驻:等消息 唤起 claude 干活 结果回群。无人在场也跑。
83
+ const DISPATCH_HELP = `常驻:等消息 -> 唤起 claude 干活 -> 结果回群。无人在场也跑。
76
84
 
77
85
  lark-relay dispatch --list # 列出所有 dispatch 任务及状态
78
- lark-relay dispatch <task> --once # 前台跑一轮(调试,不进 systemd
86
+ lark-relay dispatch <task> --once # 前台跑一轮(调试,不进 systemd)
79
87
  systemctl enable --now lark-relay-dispatch@<task>
80
88
 
81
89
  --list 输出:任务名 / app / 群数 / systemd 单元是否 active / 上次 ledger 时间
82
90
 
83
- 任务发现:扫 $LR_ROOT/*/dispatch.yaml —— 有文件即是任务,无需注册表。
84
- LR_ROOT 在单元里指定(唯一的目录绑定,与代码无关)。
91
+ 任务发现:扫 $LR_ROOT/*/dispatch.yaml -- 有文件即是任务,无需注册表。
92
+ LR_ROOT 在单元里指定(唯一的目录绑定,与代码无关)。
85
93
 
86
- dispatch.yaml4 必填 + 3 可选)
94
+ dispatch.yaml(4 必填 + 3 可选)
87
95
  app: <profile 名>
88
96
  chats: [oc_xxx]
89
97
  dirs: [/path/to/repo] # 首个 = 主工作目录
90
98
  instructions: ./instructions.md # 职责/边界,--append-system-prompt-file 注入
91
99
  filter: '.mentions[]?.id == "ou_xxx"' # 可选
92
- # mode: work 默认;work | chat | agent(见下)
100
+ # mode: work 默认;work | chat | agent(见下)
93
101
  # model: <名称> 默认走终端同一套默认路由
94
102
 
95
- mode 三个场景(一个键定死全部行为,不用拼组合)
103
+ mode 三个场景(一个键定死全部行为,不用拼组合)
96
104
  work 处理工作:话题内回复 + 过程挂 COT 消息 + 结论发卡片
97
- + 按 thread_id 隔离 session + 防抖 1s(一问一答要跟手)
98
- COT 要求客户端 PC 7.70 / 移动 7.74;老客户端那条过程消息显示为
99
- 「Completed」(不崩),结论卡片不受影响
100
- chat 简单问答:直发群里 + 纯文本 + 按(群, epoch)隔离 session
101
- + 防抖 15s(等人打完多行)
105
+ + 按 thread_id 隔离 session + 防抖 1s(一问一答要跟手)
106
+ COT 要求客户端 PC >= 7.70 / 移动 >= 7.74;老客户端那条过程消息显示为
107
+ 「Completed」(不崩),结论卡片不受影响
108
+ chat 简单问答:直发群里 + 纯文本 + 按(群, epoch)隔离 session
109
+ + 防抖 15s(等人打完多行)
102
110
  agent 模型自己当运营者:引擎只拉起它 + 传消息 + 记台账,**一条消息都不发**。
103
111
  发不发 / 回哪条 / 用哪个 bot 身份 / 文字还是表情,全由模型按
104
- instructions 决定(它有 Bash + lark-cli)。适合「多数轮该沉默」的
105
- 群运营 —— work/chat 会逼模型每轮都产出一段发回群的话
112
+ instructions 决定(它有 Bash + lark-cli)。适合「多数轮该沉默」的
113
+ 群运营 -- work/chat 会逼模型每轮都产出一段发回群的话
106
114
 
107
115
  最佳实践
108
- · 边界写 instructions,别指望 --add-dir 目录的 CLAUDE.md(启动不加载)
116
+ · 边界写 instructions,别指望 --add-dir 目录的 CLAUDE.md(启动不加载)
109
117
  · 改完 yaml 先 --once 跑一轮验证,再 enable
110
118
  · 高风险发布仍建议人工执行`
111
119
 
112
- const GUIDE = `lark-relay —— Lark 事件中继站。三个命令:收下来 / 我来取 / 派给别人。
120
+ const GUIDE = `lark-relay -- Lark 事件中继站。三个命令:收下来 / 我来取 / 派给别人。
113
121
 
114
- 先判场景(两者不可混用)
122
+ 先判场景(两者不可混用)
115
123
  ┌──────────┬────────────────────────┬────────────────────────┐
116
124
  │ │ 在场取用 take │ 托管派活 dispatch │
117
125
  ├──────────┼────────────────────────┼────────────────────────┤
118
- │ 长命的 │ AI 会话(人设、上下文)│ 消息(等在队列里)
119
- │ 短命的 │ 消息(来一批处理一批) │ AI(每轮新起,跑完就没)│
126
+ │ 长命的 │ AI 会话(人设、上下文) │ 消息(等在队列里)
127
+ │ 短命的 │ 消息(来一批处理一批) │ AI(每轮新起,跑完就没)│
120
128
  │ 谁等谁 │ AI 等消息 │ 消息等 AI │
121
129
  │ 控制权 │ AI 手里 │ 服务手里 │
122
130
  │ 谁能干 │ 任何 agent │ 需内置唤起知识 │
123
131
  │ 配置 │ 纯参数,无文件 │ dispatch.yaml │
124
132
  └──────────┴────────────────────────┴────────────────────────┘
125
133
 
126
- 你(AI)在会话里盯群 take。无人在场也要干活 dispatch。
134
+ 你(AI)在会话里盯群 -> take。无人在场也要干活 -> dispatch。
127
135
 
128
136
  前置:collect 必须在跑
129
137
  lark-relay status # collect 行应为 running
130
138
  进程不在的时刻消息永久丢失,不是延迟送达。
131
139
 
132
- take 三步(照做)
140
+ take 三步(照做)
133
141
  1. 查群 ID lark-cli --profile <app> im +chat-search --as user --query "<群名>"
134
142
  2. 验 bot 在群 lark-cli --profile <app> im chat.members bots --as user \\
135
143
  --params '{"chat_id":"oc_xxx"}'
136
- 3. 监听(用 run_in_background 起,别前台)
144
+ 3. 监听(用 run_in_background 起,别前台)
137
145
  lark-relay take --app <app> --chats oc_xxx --render text
138
- 一批处理完再起一个。不要自己写 while 循环 —— 进程退出会通知你。
146
+ 起来后 stderr 有一行「监听中」-- 没有就是没起来,看 stderr。
147
+ 一批处理完再起一个。不要自己写 while 循环 -- 进程退出会通知你。
148
+ 退出码:0=有一批,4=超时没消息(正常,再起一个),2=参数错,3=游标被占用。
139
149
 
140
150
  建 dispatch 任务
141
- 1. 在任务目录写 dispatch.yaml(\`lark-relay dispatch --help\` 有模板)
151
+ 1. 在任务目录写 dispatch.yaml(\`lark-relay dispatch --help\` 有模板)
142
152
  2. lark-relay dispatch <task> --once # 前台验一轮
143
153
  3. systemctl enable --now lark-relay-dispatch@<task>
144
154
 
145
155
  常见错误
146
- · 拿不到消息 九成是 bot 不在群。验 bot 必须 --as user,
156
+ · 拿不到消息 -> 九成是 bot 不在群。验 bot 必须 --as user,
147
157
  bot 不在群时用 bot 身份查不到,「查询失败」不能推断「不在群」
148
- · --chats 只接受 oc_ 开头的 chat_id,不接群名 —— 群名会匹配错群,
149
- 而监听错群是静默失败(你会一直等一个永不来的消息)
158
+ · 起来后没有「监听中」那行 -> 没起来;参数拼错会直接报错并给正确写法,
159
+ 不会静默
160
+ · --chats 只接受 oc_ 开头的 chat_id,不接群名 -- 群名会匹配错群,
161
+ 而监听错群是静默失败(你会一直等一个永不来的消息)
150
162
  · 默认 --since now 不重放历史。要历史用 --since all
151
163
  · 多个消费者盯同一 app 必须用不同 --name,否则互相推游标撕批次
152
164
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lark-relay",
3
- "version": "0.4.1",
3
+ "version": "0.4.2",
4
4
  "description": "Lark event relay: collect events to disk, take a batch when you need it, or dispatch work to an AI agent.",
5
5
  "keywords": [
6
6
  "lark",