lark-remote 0.5.1 → 0.5.3
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/CHANGELOG.md +22 -0
- package/dist/bridge/index.js +27 -1
- package/dist/bridge/queue-manager.js +16 -18
- package/dist/cli.js +2 -2
- package/dist/inbound/receipt.d.ts +1 -1
- package/dist/inbound/receipt.js +1 -1
- package/dist/platform/terminator-posix.js +1 -1
- package/dist/platform/terminator-win32.js +1 -1
- package/dist/runner/codex/app-server/translator.js +10 -2
- package/dist/runner/common/connection-based-runner.d.ts +14 -0
- package/dist/runner/common/connection-based-runner.js +29 -1
- package/dist/session/codex/rollout-reader.js +24 -3
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,28 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [0.5.3] - 2026-10-12
|
|
9
|
+
|
|
10
|
+
### 修复
|
|
11
|
+
|
|
12
|
+
- **中断后紧接着发下一条消息,不再报 `thread already has an active writer`**:点停止打断正在运行的 codex 时,中断流程会把复用的运行实例从缓存摘掉并立即销毁连接,而队列链在这一步的等待窗口里就把下一条消息发了出去、并已取到同一个实例——那条运行的长驻连接于是挂在无人托管的实例上,`codex app-server` 进程要等空闲超时(默认 30 分钟)才回收,期间一直持有该会话的写锁,同一会话的下一次恢复因此被拒。现在中断只把实例标为「待替换」,统一由下一次运行收尾时回收,标过的实例不再返回缓存;被摘掉的实例也会在运行结束时主动关闭连接,不再挂 30 分钟的超时。逐条消息新建实例的 agent 不受影响,行为照旧
|
|
13
|
+
|
|
14
|
+
### 变更
|
|
15
|
+
|
|
16
|
+
- **注释里的运维现场数据与仓库外引用清理**:一处测试注释记录的崩溃进程号、实例名与精确到秒的重启时间改为占位描述;若干注释不再指向仓库之外的维护规范文件名。无行为变化
|
|
17
|
+
|
|
18
|
+
## [0.5.2] - 2026-10-11
|
|
19
|
+
|
|
20
|
+
### 修复
|
|
21
|
+
|
|
22
|
+
- **codex 运行结束卡片的 token 统计不再翻倍**:codex app-server 实时上报的输入 token 按协议口径已包含命中缓存的部分,而卡片契约要求它表示「未命中缓存的输入」。入口没做减法时,Input 会等于整个上下文用量、缓存占比的分母被重复计数、Total 再被「总量与分项之和取大」抬高一倍(实测同一轮 36 万显示成 72 万)。现在实时通知与 jsonl 兜底读取两条入口统一减掉缓存部分
|
|
23
|
+
- **压缩卡片的「压缩前」不再显示摘要调用自身的用量**:codex 把生成摘要那次调用自己的 token 记录写在压缩事件之前,此前「压缩前」取的是紧贴压缩事件的那一条,于是把摘要调用的消耗当成压缩前的上下文窗口(实测 36 万的窗口显示成 20 万)。现在按压缩事件自带的摘要调用用量回退一条,取压缩前最后一次真实窗口;更早的、不带该字段的数据保持原读法
|
|
24
|
+
- **排队卡片不再复述「你正在排队」**:占用执行位的是普通消息时,卡片仍写着「正在执行: 💬 消息」并附一句「消息将按顺序执行…」,这两行对用户没有新信息。现在只有可能挡住队列、需要用户归因的任务(Compact、命令)才自报身份;Compact 占用时仍额外说明压缩结束后会按顺序执行你的消息
|
|
25
|
+
|
|
26
|
+
### 变更
|
|
27
|
+
|
|
28
|
+
- **消除一处全量测试并发下的时序假红**:bash 退出清理用例在收到首个事件后立刻读取 shell 自己写入的 pid 文件,而重定向落盘可能晚于该事件,整套测试并发跑时会随机报文件不存在。改为与同一用例里另一处 pid 文件相同的有界等待。无行为变化
|
|
29
|
+
|
|
8
30
|
## [0.5.1] - 2026-10-09
|
|
9
31
|
|
|
10
32
|
### 修复
|
package/dist/bridge/index.js
CHANGED
|
@@ -320,6 +320,17 @@ export class Bridge {
|
|
|
320
320
|
if (evicted && typeof evicted.unregisterExitHandlers === 'function') {
|
|
321
321
|
evicted.unregisterExitHandlers();
|
|
322
322
|
}
|
|
323
|
+
// 2026-10-11:先标记 orphan 再 dispose。evict 与「run 刚拿走本实例」竞态时
|
|
324
|
+
// (queue 链在 interruptCurrentRun await 期间推进),dispose 只能关掉当时
|
|
325
|
+
// 已存在的连接,run 后续 acquire 的新连接会挂在无人托管的实例上——orphan
|
|
326
|
+
// 标记让 executeTurn 在 run 结束时主动 dispose 这批连接,而不是挂 idle
|
|
327
|
+
// timer 等待 30 分钟(期间持有 agent 侧会话写锁,codex 报
|
|
328
|
+
// "thread already has an active writer")。鸭子探测与 dispose/unregister
|
|
329
|
+
// 一致:claude(stream-json,无长驻连接)无此方法,跳过。
|
|
330
|
+
if (evicted &&
|
|
331
|
+
typeof evicted.markOrphaned === 'function') {
|
|
332
|
+
evicted.markOrphaned();
|
|
333
|
+
}
|
|
323
334
|
// App-server runner owns a persistent connection — dispose it so the
|
|
324
335
|
// `codex app-server` child process does not leak across runs/evictions.
|
|
325
336
|
if (evicted && typeof evicted.dispose === 'function') {
|
|
@@ -574,7 +585,22 @@ export class Bridge {
|
|
|
574
585
|
// (eviction must also unregister the runner from the exit dispatcher)
|
|
575
586
|
// evict by the run's captured agentKind, not the live
|
|
576
587
|
// defaultAgent (may have changed mid-run via /config).
|
|
577
|
-
|
|
588
|
+
//
|
|
589
|
+
// 2026-10-11:workspace-lifetime runner 不能在这里立即 evict+dispose。
|
|
590
|
+
// interruptCurrentRun 的 await 窗口内 queue 链会推进下一条消息,其
|
|
591
|
+
// getRunner 可能刚拿走本实例(缓存命中先于本行执行)——立即 evict 会
|
|
592
|
+
// 让那个 run 的长驻连接脱管:进程残留至 idle TTL 才回收,期间持有
|
|
593
|
+
// agent 侧会话写锁(codex "thread already has an active writer",
|
|
594
|
+
// 线上实测)。改为标 stale,由 finalizeRun 在下一次 run 结束时统一
|
|
595
|
+
// evict+dispose;「stop 后下一 run 换新实例」的语义
|
|
596
|
+
// 保留(stale 命中即最后一轮复用)。run-per-message runner 无长驻
|
|
597
|
+
// 连接,保持立即 evict 不变。
|
|
598
|
+
if (active.runner.lifetime === 'workspace') {
|
|
599
|
+
this.staleRunnerSlots.add(`${cwd}\u0000${active.agentKind}`);
|
|
600
|
+
}
|
|
601
|
+
else {
|
|
602
|
+
this.evictRunnerSlot(cwd, active.agentKind);
|
|
603
|
+
}
|
|
578
604
|
// Reset queue executing count: the task was interrupted externally, so
|
|
579
605
|
// its promise-chain settle (.then/.catch) may lag behind the process
|
|
580
606
|
// kill. Without this reset, subsequent messages would incorrectly show
|
|
@@ -432,7 +432,7 @@ export class QueueManager {
|
|
|
432
432
|
];
|
|
433
433
|
// 「队列位置 / 前面还有」只在前面确实还有等待中的消息时渲染。前面为空时
|
|
434
434
|
// 这两个数是「第 1 位 / 0 条」,与「排队中」字面矛盾;此时挡住用户的只有
|
|
435
|
-
//
|
|
435
|
+
// 正在执行的任务,由下一段的占用者说明(若占用者是普通消息则不额外渲染)。
|
|
436
436
|
if (tasksAhead > 0) {
|
|
437
437
|
elements.push({
|
|
438
438
|
tag: 'div',
|
|
@@ -443,28 +443,26 @@ export class QueueManager {
|
|
|
443
443
|
});
|
|
444
444
|
}
|
|
445
445
|
if (occupant) {
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
446
|
+
// 只有可能挡住队列、需要用户归因的占用者才自报身份。「正在执行: 💬 消息」
|
|
447
|
+
// 对普通消息占用者没有信息量——用户刚发的就是消息,「💡 消息将按顺序执行…」
|
|
448
|
+
// 也只是复述排队本身,一并省去(2026-10-11)。压缩额外说明何时轮到用户。
|
|
449
|
+
if (occupant.kind !== 'message') {
|
|
450
|
+
elements.push({
|
|
451
|
+
tag: 'div',
|
|
452
|
+
text: { tag: 'lark_md', content: `**正在执行:** ${occupant.label}` },
|
|
453
|
+
});
|
|
454
|
+
}
|
|
455
|
+
if (occupant.kind === 'compact') {
|
|
456
|
+
elements.push({
|
|
457
|
+
tag: 'div',
|
|
458
|
+
text: { tag: 'lark_md', content: '💡 压缩结束后将按顺序执行你的消息' },
|
|
459
|
+
});
|
|
460
|
+
}
|
|
459
461
|
}
|
|
460
462
|
else {
|
|
461
463
|
// 防御分支:开卡判据是 lane 非空,所以「有排队卡但没有占用者」说明计数
|
|
462
464
|
// 与占用者记录漂移了。不改行为,只留证据(与 duplicate queue key 同口径)。
|
|
463
465
|
getLogger().warn(`[queue-manager] queue card without lane occupant cwd=${cwd} tasksAhead=${tasksAhead}`);
|
|
464
|
-
elements.push({
|
|
465
|
-
tag: 'div',
|
|
466
|
-
text: { tag: 'lark_md', content: '💡 消息将按顺序执行…' },
|
|
467
|
-
});
|
|
468
466
|
}
|
|
469
467
|
elements.push({ tag: 'hr' });
|
|
470
468
|
// Show message preview with edit button (only if not executing AND the
|
package/dist/cli.js
CHANGED
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
/**
|
|
3
3
|
* CLI 引导层(bootstrap):在 import 应用模块图之前决定运行时(bun/node)并拉起它。
|
|
4
4
|
*
|
|
5
|
-
* **这是全仓唯一被认可的 spawn
|
|
6
|
-
* platform/spawn.ts
|
|
5
|
+
* **这是全仓唯一被认可的 spawn 收口例外**(红线:子进程 spawn 只走
|
|
6
|
+
* platform/spawn.ts,此处显式豁免)。三个理由缺一不可:
|
|
7
7
|
* 1. 时序:preflight(Node 版本守卫、-v/-h/--advanced-help)必须在 import 应用
|
|
8
8
|
* 模块图之前跑完——dist/cli.js 刻意不使用 ES2020+ 语法以便在 node 12 上仍
|
|
9
9
|
* 可被解析,而 dist/index.js 是 ES2022;先 import platform/spawn 就等于先把
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* 2. 拿不到的东西(不支持类型 / 超限 / 下载失败 / 媒体保存已关闭):明确告知,
|
|
7
7
|
* 而不是把占位符塞给 agent。
|
|
8
8
|
*
|
|
9
|
-
*
|
|
9
|
+
* 命名纪律:用户可见文案指代本程序时只能写 `lark-remote`。
|
|
10
10
|
*/
|
|
11
11
|
import type { PlaceholderKind } from './placeholder.js';
|
|
12
12
|
import type { RejectedItem } from './turn.js';
|
package/dist/inbound/receipt.js
CHANGED
|
@@ -9,7 +9,7 @@ function defaultLog(level, message) {
|
|
|
9
9
|
else
|
|
10
10
|
logger.info(message);
|
|
11
11
|
}
|
|
12
|
-
/**
|
|
12
|
+
/** 退出判定统一口径(红线):exitCode !== null || signalCode !== null。 */
|
|
13
13
|
function isAlive(proc) {
|
|
14
14
|
return proc.exitCode === null && proc.signalCode === null;
|
|
15
15
|
}
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
*/
|
|
13
13
|
import { spawn as nodeSpawn } from 'node:child_process';
|
|
14
14
|
import { agentStopperRegistry } from './agent-stopper.js';
|
|
15
|
-
/**
|
|
15
|
+
/** 退出判定统一口径(红线):exitCode !== null || signalCode !== null。 */
|
|
16
16
|
function isAlive(proc) {
|
|
17
17
|
return proc.exitCode === null && proc.signalCode === null;
|
|
18
18
|
}
|
|
@@ -456,10 +456,18 @@ export class CodexAppServerTranslator {
|
|
|
456
456
|
}
|
|
457
457
|
handleTokenUsageUpdated(params) {
|
|
458
458
|
const last = params.tokenUsage.last;
|
|
459
|
+
// 协议原生 inputTokens 含 cached(total = input + output),而 lark-remote 的
|
|
460
|
+
// 卡片契约是 ccusage 口径「input_tokens 表示未命中缓存的输入」(design §9.21)。
|
|
461
|
+
// 在入口归一,否则 done 卡 Input 会等于 Context、cache% 分母重复计数、
|
|
462
|
+
// Total 被 max(total, input+output+cache) 抬高一倍(实测 364K → 727K)。
|
|
463
|
+
// jsonl 链路有同一处减法(session/codex/rollout-reader.ts buildUsageFromScan)。
|
|
464
|
+
const cachedInputTokens = last.cachedInputTokens === undefined
|
|
465
|
+
? undefined
|
|
466
|
+
: Math.min(last.cachedInputTokens, last.inputTokens);
|
|
459
467
|
this.usage = {
|
|
460
|
-
inputTokens: last.inputTokens,
|
|
468
|
+
inputTokens: last.inputTokens - (cachedInputTokens ?? 0),
|
|
461
469
|
outputTokens: last.outputTokens,
|
|
462
|
-
cachedInputTokens
|
|
470
|
+
cachedInputTokens,
|
|
463
471
|
totalTokens: last.totalTokens,
|
|
464
472
|
contextLimit: params.tokenUsage.modelContextWindow,
|
|
465
473
|
};
|
|
@@ -31,6 +31,10 @@ export declare abstract class ConnectionBasedRunner<TClient, TEvent = AgentEvent
|
|
|
31
31
|
protected forceFinish: boolean;
|
|
32
32
|
/** stop() 在 turn/start 或 prompt 响应前到达时置位,完成后再补发 cancel/interrupt。 */
|
|
33
33
|
protected stopRequested: boolean;
|
|
34
|
+
/** 缓存层除名标记(evictRunnerSlot → markOrphaned)。置位后 run 结束时主动
|
|
35
|
+
* dispose 全部连接而不是挂 idle timer:被除名实例上创建的连接无人托管,
|
|
36
|
+
* 不主动收会残留进程至 idle TTL(30 分钟),期间持有 agent 侧会话写锁。 */
|
|
37
|
+
private orphaned;
|
|
34
38
|
protected constructor(opts: {
|
|
35
39
|
kind: AgentKind;
|
|
36
40
|
sessionReader: AgentSessionReader;
|
|
@@ -96,6 +100,16 @@ export declare abstract class ConnectionBasedRunner<TClient, TEvent = AgentEvent
|
|
|
96
100
|
* Dispose the runner: release the connection.
|
|
97
101
|
*/
|
|
98
102
|
dispose(): Promise<void>;
|
|
103
|
+
/**
|
|
104
|
+
* Mark this runner as evicted from the bridge's runner cache while a run may
|
|
105
|
+
* still be holding/creating connections on it (queue-advance race during
|
|
106
|
+
* interruptCurrentRun's await window, 2026-10-11). The active run's finally
|
|
107
|
+
* disposes connections eagerly instead of arming the idle TTL timer —
|
|
108
|
+
* otherwise the agent server process lingers for up to 30 minutes holding
|
|
109
|
+
* its session write lock (codex: "thread already has an active writer").
|
|
110
|
+
* One-way by design: an evicted instance never returns to the cache.
|
|
111
|
+
*/
|
|
112
|
+
markOrphaned(): void;
|
|
99
113
|
getUsageAuthority(): 'live';
|
|
100
114
|
killOrphan(): void;
|
|
101
115
|
registerExitHandlers(): void;
|
|
@@ -22,6 +22,10 @@ export class ConnectionBasedRunner {
|
|
|
22
22
|
forceFinish = false;
|
|
23
23
|
/** stop() 在 turn/start 或 prompt 响应前到达时置位,完成后再补发 cancel/interrupt。 */
|
|
24
24
|
stopRequested = false;
|
|
25
|
+
/** 缓存层除名标记(evictRunnerSlot → markOrphaned)。置位后 run 结束时主动
|
|
26
|
+
* dispose 全部连接而不是挂 idle timer:被除名实例上创建的连接无人托管,
|
|
27
|
+
* 不主动收会残留进程至 idle TTL(30 分钟),期间持有 agent 侧会话写锁。 */
|
|
28
|
+
orphaned = false;
|
|
25
29
|
constructor(opts, defaultTurnIdleTimeoutMs = TURN_IDLE_TIMEOUT_MS) {
|
|
26
30
|
this.kind = opts.kind;
|
|
27
31
|
this.sessionReader = opts.sessionReader;
|
|
@@ -130,7 +134,19 @@ export class ConnectionBasedRunner {
|
|
|
130
134
|
this.currentTurnId = null;
|
|
131
135
|
this.stopRequested = false;
|
|
132
136
|
this.clearTurnState();
|
|
133
|
-
this.
|
|
137
|
+
if (this.orphaned) {
|
|
138
|
+
// 已被缓存层除名(evictRunnerSlot → markOrphaned):本次 run 创建的
|
|
139
|
+
// 连接挂在无人托管的实例上。主动收掉而不是挂 idle timer——否则 agent
|
|
140
|
+
// server 进程残留至 idle TTL,期间持有 agent 侧会话写锁(codex
|
|
141
|
+
// "thread already has an active writer",2026-10-11 线上实测)。
|
|
142
|
+
// dispose() 内部自带 try/catch,这里 fire-and-forget 不阻塞事件流收尾。
|
|
143
|
+
void this.disposeConnections().catch((err) => {
|
|
144
|
+
getLogger().warn(`[${this.logTag}] orphan dispose error: ${err.message}`);
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
else {
|
|
148
|
+
this.notifyIdle(opts.cwd);
|
|
149
|
+
}
|
|
134
150
|
}
|
|
135
151
|
}
|
|
136
152
|
/**
|
|
@@ -178,6 +194,18 @@ export class ConnectionBasedRunner {
|
|
|
178
194
|
getLogger().warn(`[${this.logTag}] dispose error: ${err.message}`);
|
|
179
195
|
}
|
|
180
196
|
}
|
|
197
|
+
/**
|
|
198
|
+
* Mark this runner as evicted from the bridge's runner cache while a run may
|
|
199
|
+
* still be holding/creating connections on it (queue-advance race during
|
|
200
|
+
* interruptCurrentRun's await window, 2026-10-11). The active run's finally
|
|
201
|
+
* disposes connections eagerly instead of arming the idle TTL timer —
|
|
202
|
+
* otherwise the agent server process lingers for up to 30 minutes holding
|
|
203
|
+
* its session write lock (codex: "thread already has an active writer").
|
|
204
|
+
* One-way by design: an evicted instance never returns to the cache.
|
|
205
|
+
*/
|
|
206
|
+
markOrphaned() {
|
|
207
|
+
this.orphaned = true;
|
|
208
|
+
}
|
|
181
209
|
getUsageAuthority() {
|
|
182
210
|
return 'live';
|
|
183
211
|
}
|
|
@@ -42,6 +42,15 @@ function subtractRawUsage(a, b) {
|
|
|
42
42
|
total_tokens: a.total_tokens - b.total_tokens,
|
|
43
43
|
};
|
|
44
44
|
}
|
|
45
|
+
/**
|
|
46
|
+
* `compacted` 事件自带的摘要调用 usage(`latest_token_usage_record.usage`)。
|
|
47
|
+
* 用于把「压缩前窗口」与「摘要调用自身的消耗」区分开——两者都写在 compacted 前。
|
|
48
|
+
*/
|
|
49
|
+
function readCompactedCallTotal(payload) {
|
|
50
|
+
const record = payload?.latest_token_usage_record;
|
|
51
|
+
const total = record?.usage?.total_tokens;
|
|
52
|
+
return typeof total === 'number' ? total : undefined;
|
|
53
|
+
}
|
|
45
54
|
export function readCodexRollout(filePath) {
|
|
46
55
|
if (!fs.existsSync(filePath)) {
|
|
47
56
|
return null;
|
|
@@ -136,6 +145,8 @@ function scanCodexRollout(filePath, includeEvents) {
|
|
|
136
145
|
// pi/opencode /resume "last turn" display semantics). When
|
|
137
146
|
// last_token_usage is absent we derive it as total - previous_total.
|
|
138
147
|
let lastRawUsage;
|
|
148
|
+
/** 上一条被接受的 raw usage;compacted 分支回退一条时用(见下方注释)。 */
|
|
149
|
+
let previousRawUsage;
|
|
139
150
|
// Context window limit from the same token_count event as lastRawUsage
|
|
140
151
|
// (codex reports info.model_context_window per turn; absent on old data).
|
|
141
152
|
let lastContextLimit;
|
|
@@ -202,6 +213,7 @@ function scanCodexRollout(filePath, includeEvents) {
|
|
|
202
213
|
lastTotalUsage = total;
|
|
203
214
|
}
|
|
204
215
|
if (raw && (raw.input_tokens || raw.cached_input_tokens || raw.output_tokens)) {
|
|
216
|
+
previousRawUsage = lastRawUsage;
|
|
205
217
|
lastRawUsage = raw;
|
|
206
218
|
const ctxWindow = info?.model_context_window;
|
|
207
219
|
lastContextLimit = typeof ctxWindow === 'number' ? ctxWindow : undefined;
|
|
@@ -223,10 +235,19 @@ function scanCodexRollout(filePath, includeEvents) {
|
|
|
223
235
|
}
|
|
224
236
|
}
|
|
225
237
|
else if (parsed.type === 'compacted') {
|
|
226
|
-
// 顶层 compacted 事件:压缩次数 +1
|
|
238
|
+
// 顶层 compacted 事件:压缩次数 +1,压缩前水位取「压缩调用之前」那条非零
|
|
239
|
+
// token_count 的 total_tokens。真实 rollout 落盘顺序是
|
|
240
|
+
// 末轮 → 摘要调用 → compacted → 压缩后水位:摘要调用自身的 token_count 紧贴
|
|
241
|
+
// 在 compacted 之前,直接取 lastRawUsage 会把摘要调用的消耗当成压缩前窗口
|
|
242
|
+
// (实测把 363K 的窗口显示成 207K)。compacted 自带的摘要调用 usage 用于判定
|
|
243
|
+
// 并回退一条;旧数据没有 latest_token_usage_record 时保持原语义。
|
|
227
244
|
compactCount++;
|
|
228
|
-
|
|
229
|
-
|
|
245
|
+
const compactCallTotal = readCompactedCallTotal(parsed.payload);
|
|
246
|
+
const preCompactUsage = compactCallTotal !== undefined && lastRawUsage?.total_tokens === compactCallTotal
|
|
247
|
+
? previousRawUsage
|
|
248
|
+
: lastRawUsage;
|
|
249
|
+
if (preCompactUsage?.total_tokens !== undefined) {
|
|
250
|
+
compactPreContextLength = preCompactUsage.total_tokens;
|
|
230
251
|
}
|
|
231
252
|
}
|
|
232
253
|
else if (includeEvents && parsed.type === 'response_item' && parsed.payload) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "lark-remote",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.3",
|
|
4
4
|
"description": "Feishu (Lark) private-chat bridge to local coding agents — Claude Code / Codex / opencode / pi / kimi, with live-streaming CardKit 2.0 cards",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|