lark-remote 0.5.19 → 0.5.20

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.
Files changed (119) hide show
  1. package/CHANGELOG.md +19 -0
  2. package/dist/bridge/approval-actions.d.ts +1 -1
  3. package/dist/bridge/approval-actions.js +1 -1
  4. package/dist/bridge/bash-run.d.ts +71 -0
  5. package/dist/bridge/bash-run.js +217 -0
  6. package/dist/bridge/compact.d.ts +155 -0
  7. package/dist/bridge/compact.js +450 -0
  8. package/dist/bridge/final-usage.js +4 -4
  9. package/dist/bridge/inbound-media.js +1 -1
  10. package/dist/bridge/index.d.ts +97 -153
  11. package/dist/bridge/index.js +182 -809
  12. package/dist/bridge/job-watch.d.ts +68 -0
  13. package/dist/bridge/job-watch.js +146 -0
  14. package/dist/bridge/queue-manager.d.ts +3 -3
  15. package/dist/bridge/queue-manager.js +10 -29
  16. package/dist/card/approval-render.d.ts +2 -2
  17. package/dist/card/approval-render.js +109 -97
  18. package/dist/card/card-budget.js +1 -1
  19. package/dist/card/card-helpers.d.ts +3 -3
  20. package/dist/card/card-helpers.js +6 -6
  21. package/dist/card/final-answer-file.js +1 -1
  22. package/dist/card/run-renderer.d.ts +3 -3
  23. package/dist/card/run-renderer.js +34 -93
  24. package/dist/card/run-state.d.ts +1 -1
  25. package/dist/card/run-state.js +28 -90
  26. package/dist/card/tool-render.d.ts +1 -1
  27. package/dist/card/tool-render.js +1 -1
  28. package/dist/card/usage-format.d.ts +12 -0
  29. package/dist/card/usage-format.js +8 -3
  30. package/dist/cli.js +15 -3
  31. package/dist/clone.js +2 -2
  32. package/dist/config/codex-config.js +130 -106
  33. package/dist/config/index.d.ts +16 -2
  34. package/dist/config/index.js +23 -8
  35. package/dist/connector/index.js +6 -6
  36. package/dist/error-classification.js +3 -2
  37. package/dist/inbound/placeholder.d.ts +1 -1
  38. package/dist/inbound/placeholder.js +1 -1
  39. package/dist/index.js +203 -133
  40. package/dist/logger/index.js +6 -6
  41. package/dist/message-handlers.d.ts +0 -5
  42. package/dist/message-handlers.js +1 -1
  43. package/dist/model-catalog.d.ts +2 -2
  44. package/dist/model-catalog.js +3 -8
  45. package/dist/platform/identity.js +1 -1
  46. package/dist/platform/path.d.ts +1 -1
  47. package/dist/platform/path.js +1 -1
  48. package/dist/platform/sleep-blocker.js +1 -1
  49. package/dist/platform/terminator-win32.js +2 -1
  50. package/dist/router/commands/resume.js +2 -20
  51. package/dist/router/config/dsh.d.ts +4 -8
  52. package/dist/router/config/dsh.js +3 -10
  53. package/dist/router/config/kimi.js +1 -1
  54. package/dist/router/config/types.d.ts +1 -1
  55. package/dist/router/config-diff.js +1 -1
  56. package/dist/router/index.d.ts +56 -11
  57. package/dist/router/index.js +219 -200
  58. package/dist/runner/claude/session.d.ts +1 -1
  59. package/dist/runner/claude/session.js +6 -4
  60. package/dist/runner/codex/app-server/runner.d.ts +9 -2
  61. package/dist/runner/codex/app-server/runner.js +30 -25
  62. package/dist/runner/codex/app-server/translator.d.ts +0 -5
  63. package/dist/runner/codex/app-server/translator.js +4 -17
  64. package/dist/runner/common/acp/acp-approval.d.ts +1 -1
  65. package/dist/runner/common/acp/acp-approval.js +1 -1
  66. package/dist/runner/common/acp/base-acp-runner.d.ts +5 -2
  67. package/dist/runner/common/acp/base-acp-runner.js +26 -65
  68. package/dist/runner/common/acp/base-translator.d.ts +12 -2
  69. package/dist/runner/common/acp/base-translator.js +31 -9
  70. package/dist/runner/common/acp/protocol-types.d.ts +1 -1
  71. package/dist/runner/common/acp/protocol-types.js +2 -2
  72. package/dist/runner/common/connection-based-runner.d.ts +32 -1
  73. package/dist/runner/common/connection-based-runner.js +32 -2
  74. package/dist/runner/common/jsonrpc/connection-manager.d.ts +2 -2
  75. package/dist/runner/common/jsonrpc/connection-manager.js +4 -4
  76. package/dist/runner/common/spawning-runner.d.ts +1 -1
  77. package/dist/runner/common/spawning-runner.js +1 -1
  78. package/dist/runner/dsh/catalog-probe.d.ts +17 -0
  79. package/dist/runner/dsh/catalog-probe.js +10 -0
  80. package/dist/runner/dsh/client.d.ts +1 -3
  81. package/dist/runner/dsh/client.js +2 -4
  82. package/dist/runner/dsh/exit-cancel.d.ts +2 -2
  83. package/dist/runner/dsh/exit-cancel.js +2 -2
  84. package/dist/runner/dsh/inflight.d.ts +1 -1
  85. package/dist/runner/dsh/inflight.js +1 -1
  86. package/dist/runner/dsh/runner.d.ts +40 -1
  87. package/dist/runner/dsh/runner.js +272 -199
  88. package/dist/runner/dsh/translator.d.ts +1 -1
  89. package/dist/runner/dsh/translator.js +1 -1
  90. package/dist/runner/index.d.ts +1 -1
  91. package/dist/runner/kimi/acp/runner.d.ts +1 -2
  92. package/dist/runner/kimi/acp/runner.js +1 -2
  93. package/dist/runner/kimi/acp/translator.d.ts +1 -1
  94. package/dist/runner/kimi/acp/translator.js +3 -4
  95. package/dist/runner/opencode/acp/runner.d.ts +2 -2
  96. package/dist/runner/opencode/acp/runner.js +4 -4
  97. package/dist/runner/pi/rpc/protocol-types.d.ts +1 -1
  98. package/dist/runner/pi/rpc/protocol-types.js +1 -1
  99. package/dist/runner/pi/rpc/runner.d.ts +3 -0
  100. package/dist/runner/pi/rpc/runner.js +27 -42
  101. package/dist/runner/types.d.ts +2 -2
  102. package/dist/runner/types.js +1 -1
  103. package/dist/session/claude/sessions.d.ts +45 -0
  104. package/dist/session/claude/sessions.js +97 -71
  105. package/dist/session/codex/rollout-reader.js +4 -4
  106. package/dist/session/common/throw-session-list-error.d.ts +10 -0
  107. package/dist/session/common/throw-session-list-error.js +16 -0
  108. package/dist/session/dsh/sessions.js +11 -5
  109. package/dist/session/kimi/sessions.d.ts +40 -1
  110. package/dist/session/kimi/sessions.js +244 -192
  111. package/dist/session/opencode/sessions.d.ts +153 -1
  112. package/dist/session/opencode/sessions.js +354 -321
  113. package/dist/session/pi/sessions.js +4 -5
  114. package/dist/session/session-store.d.ts +1 -1
  115. package/dist/session/session-store.js +1 -1
  116. package/dist/update/version-check.js +2 -1
  117. package/dist/workspace/index.d.ts +1 -1
  118. package/dist/workspace/index.js +1 -1
  119. package/package.json +1 -1
@@ -125,8 +125,6 @@ function reduceRunStateInner(state, event) {
125
125
  }
126
126
  /** Handle app-server turn_started — records the operation kind (turn vs compact). */
127
127
  function reduceTurnStartedEvent(state, event) {
128
- if (event.type !== 'turn_started')
129
- return state;
130
128
  return {
131
129
  ...state,
132
130
  operationKind: event.operationKind,
@@ -142,8 +140,6 @@ function reduceTurnStartedEvent(state, event) {
142
140
  * the active streaming content sits at the bottom, like the exec path.
143
141
  */
144
142
  function reduceTurnDiffEvent(state, event) {
145
- if (event.type !== 'turn_diff')
146
- return state;
147
143
  let next = state;
148
144
  const ts = event.timestamp;
149
145
  const complete = event.complete === true;
@@ -170,12 +166,7 @@ function reduceTurnDiffEvent(state, event) {
170
166
  ...(complete ? { completedAt: ts } : {}),
171
167
  };
172
168
  }, !complete);
173
- next = {
174
- ...next,
175
- blocks: keepLatestBlocks(capped),
176
- omittedBlocks: (next.omittedBlocks ?? 0) + Math.max(0, capped.length - MAX_BLOCKS),
177
- footer: 'streaming',
178
- };
169
+ next = withCappedBlocks(next, capped, 'streaming');
179
170
  }
180
171
  if (event.reasoning !== undefined) {
181
172
  const capped = upsertSnapshotBlock(next.blocks, (b) => b.kind === 'thinking' && b.itemId === event.itemId, () => ({
@@ -195,12 +186,7 @@ function reduceTurnDiffEvent(state, event) {
195
186
  ...(complete ? { completedAt: ts } : {}),
196
187
  };
197
188
  }, !complete);
198
- next = {
199
- ...next,
200
- blocks: keepLatestBlocks(capped),
201
- omittedBlocks: (next.omittedBlocks ?? 0) + Math.max(0, capped.length - MAX_BLOCKS),
202
- footer: 'thinking',
203
- };
189
+ next = withCappedBlocks(next, capped, 'thinking');
204
190
  }
205
191
  if (event.toolOutput !== undefined) {
206
192
  const capped = upsertSnapshotBlock(next.blocks, (b) => b.kind === 'tool' && b.tool.id === event.itemId, () => {
@@ -231,12 +217,7 @@ function reduceTurnDiffEvent(state, event) {
231
217
  ...(complete ? { completedAt: ts } : {}),
232
218
  },
233
219
  }), !complete);
234
- next = {
235
- ...next,
236
- blocks: keepLatestBlocks(capped),
237
- omittedBlocks: (next.omittedBlocks ?? 0) + Math.max(0, capped.length - MAX_BLOCKS),
238
- footer: 'tool_running',
239
- };
220
+ next = withCappedBlocks(next, capped, 'tool_running');
240
221
  }
241
222
  if (event.plan !== undefined) {
242
223
  const newPlan = keepTailMarked(event.plan, MAX_REASONING_CHARS * 2);
@@ -256,12 +237,7 @@ function reduceTurnDiffEvent(state, event) {
256
237
  ...(complete ? { completedAt: ts } : {}),
257
238
  };
258
239
  }, !complete);
259
- next = {
260
- ...next,
261
- plan: newPlan,
262
- blocks: keepLatestBlocks(capped),
263
- omittedBlocks: (next.omittedBlocks ?? 0) + Math.max(0, capped.length - MAX_BLOCKS),
264
- };
240
+ next = withCappedBlocks({ ...next, plan: newPlan }, capped);
265
241
  }
266
242
  if (event.fileChanges !== undefined) {
267
243
  let blocks = next.blocks;
@@ -278,12 +254,7 @@ function reduceTurnDiffEvent(state, event) {
278
254
  // 同一 item 的同一路径原地替换(权威 diff 校正),新路径/新 item 追加。
279
255
  blocks = upsertSnapshotBlock(blocks, (b) => b.kind === 'file_change' && b.itemId === event.itemId && b.path === fc.path, () => fileBlock, () => fileBlock, !complete);
280
256
  }
281
- const capped = blocks;
282
- next = {
283
- ...next,
284
- blocks: keepLatestBlocks(capped),
285
- omittedBlocks: (next.omittedBlocks ?? 0) + Math.max(0, capped.length - MAX_BLOCKS),
286
- };
257
+ next = withCappedBlocks(next, blocks);
287
258
  }
288
259
  return next;
289
260
  }
@@ -313,8 +284,6 @@ function upsertSnapshotBlock(blocks, predicate, fallback, update, moveToEndWhenN
313
284
  }
314
285
  /** Handle system init and compact_boundary events. */
315
286
  function reduceSystemEvent(state, event) {
316
- if (event.type !== 'system')
317
- return state;
318
287
  if (event.subtype === 'init') {
319
288
  return { ...state, sessionId: event.session_id, model: event.model, footer: 'thinking' };
320
289
  }
@@ -336,8 +305,6 @@ function reduceSystemEvent(state, event) {
336
305
  * - errorMsg: 首个非 undefined 胜出(agent 的更具体)
337
306
  */
338
307
  function reduceResultEvent(state, event) {
339
- if (event.type !== 'result')
340
- return state;
341
308
  // Pre-init result guard: Claude CLI --resume emits a historical result (from
342
309
  // the previous turn) before sending system.init for the new run. Without this
343
310
  // guard, that stale result would prematurely transition to 'finalizing',
@@ -375,8 +342,6 @@ function reduceResultEvent(state, event) {
375
342
  }
376
343
  /** Record an authoritative protocol final answer and remove its progress copy. */
377
344
  function reduceFinalAnswerEvent(state, event) {
378
- if (event.type !== 'final_answer')
379
- return state;
380
345
  if (!event.text.trim() && !state.finalAnswer)
381
346
  return state;
382
347
  const blocks = event.itemId
@@ -443,8 +408,6 @@ function promoteFinalAnswer(state, source, authoritativeText) {
443
408
  * 不再走事件(那时已经没有 run 在消费流了)。
444
409
  */
445
410
  function reduceBackgroundJobsEvent(state, event) {
446
- if (event.type !== 'background_jobs')
447
- return state;
448
411
  return { ...state, backgroundJobs: event.jobs };
449
412
  }
450
413
  /**
@@ -556,8 +519,6 @@ export function markBackgroundJobsUnknown(state) {
556
519
  * 维度(上一步算出的上下文水位)擦掉。
557
520
  */
558
521
  function reduceRunStatusEvent(state, event) {
559
- if (event.type !== 'run_status')
560
- return state;
561
522
  const prev = state.liveStatus ?? {};
562
523
  const live = {
563
524
  ...prev,
@@ -575,8 +536,6 @@ function reduceRunStatusEvent(state, event) {
575
536
  }
576
537
  /** Handle assistant thinking/text/tool_use content blocks. */
577
538
  function reduceAssistantEvent(state, event) {
578
- if (event.type !== 'assistant')
579
- return state;
580
539
  let next = state;
581
540
  for (const content of event.message.content) {
582
541
  if (content.type === 'thinking') {
@@ -605,12 +564,7 @@ function reduceAssistantEvent(state, event) {
605
564
  timestamp: event.timestamp,
606
565
  },
607
566
  ];
608
- next = {
609
- ...next,
610
- blocks: keepLatestBlocks(newBlocks),
611
- omittedBlocks: (next.omittedBlocks ?? 0) + Math.max(0, newBlocks.length - MAX_BLOCKS),
612
- footer: 'thinking',
613
- };
567
+ next = withCappedBlocks(next, newBlocks, 'thinking');
614
568
  }
615
569
  else if (content.type === 'text') {
616
570
  const blocks = markThinkingInactive(next.blocks);
@@ -634,12 +588,7 @@ function reduceAssistantEvent(state, event) {
634
588
  timestamp: event.timestamp,
635
589
  },
636
590
  ];
637
- next = {
638
- ...next,
639
- blocks: keepLatestBlocks(newBlocks),
640
- omittedBlocks: (next.omittedBlocks ?? 0) + Math.max(0, newBlocks.length - MAX_BLOCKS),
641
- footer: 'streaming',
642
- };
591
+ next = withCappedBlocks(next, newBlocks, 'streaming');
643
592
  }
644
593
  else {
645
594
  // parse the truncated stored string ONCE here (not on every render)
@@ -674,12 +623,7 @@ function reduceAssistantEvent(state, event) {
674
623
  }
675
624
  else {
676
625
  const capped = [...blocks, { kind: 'tool', tool: incomingTool }];
677
- next = {
678
- ...next,
679
- blocks: keepLatestBlocks(capped),
680
- omittedBlocks: (next.omittedBlocks ?? 0) + Math.max(0, capped.length - MAX_BLOCKS),
681
- footer: 'tool_running',
682
- };
626
+ next = withCappedBlocks(next, capped, 'tool_running');
683
627
  }
684
628
  }
685
629
  }
@@ -716,8 +660,6 @@ function mergeToolUse(existing, incoming) {
716
660
  * advances to 'streaming' on every content block (parity with old behavior);
717
661
  * only the blocks array is preserved by reference when nothing matches. */
718
662
  function reduceToolResultEvent(state, event) {
719
- if (event.type !== 'user')
720
- return state;
721
663
  let next = state;
722
664
  for (const content of event.message.content) {
723
665
  const targetId = content.tool_use_id;
@@ -745,8 +687,6 @@ function reduceToolResultEvent(state, event) {
745
687
  }
746
688
  /** Handle notice event — append a runtime notice (warning / reroute / compaction). */
747
689
  function reduceNoticeEvent(state, event) {
748
- if (event.type !== 'notice')
749
- return state;
750
690
  return {
751
691
  ...state,
752
692
  notices: [
@@ -762,8 +702,6 @@ function reduceNoticeEvent(state, event) {
762
702
  }
763
703
  /** Handle session_info event — update sessionTitle / mode only when present. */
764
704
  function reduceSessionInfoEvent(state, event) {
765
- if (event.type !== 'session_info')
766
- return state;
767
705
  return {
768
706
  ...state,
769
707
  ...(event.title !== undefined ? { sessionTitle: event.title } : {}),
@@ -772,8 +710,6 @@ function reduceSessionInfoEvent(state, event) {
772
710
  }
773
711
  /** Handle plan event — accumulate plan text for Codex-style agents. */
774
712
  function reducePlanEvent(state, event) {
775
- if (event.type !== 'plan')
776
- return state;
777
713
  const currentPlan = state.plan ?? '';
778
714
  const newPlan = keepTailMarked(currentPlan + '\n' + event.plan, MAX_REASONING_CHARS * 2);
779
715
  // Plan is a single evolving document — replace the existing plan block
@@ -783,17 +719,10 @@ function reducePlanEvent(state, event) {
783
719
  ...withoutOldPlan,
784
720
  { kind: 'plan', content: newPlan, active: true, timestamp: event.timestamp },
785
721
  ];
786
- return {
787
- ...state,
788
- plan: newPlan,
789
- blocks: keepLatestBlocks(newBlocks),
790
- omittedBlocks: (state.omittedBlocks ?? 0) + Math.max(0, newBlocks.length - MAX_BLOCKS),
791
- };
722
+ return withCappedBlocks({ ...state, plan: newPlan }, newBlocks);
792
723
  }
793
724
  /** Handle file_change event — emit a file_change block for rendering (Codex-style agents). */
794
725
  function reduceFileChangeEvent(state, event) {
795
- if (event.type !== 'file_change')
796
- return state;
797
726
  // Add file_change block for rendering (collapsed by default)
798
727
  const newBlocks = [
799
728
  ...state.blocks,
@@ -805,11 +734,7 @@ function reduceFileChangeEvent(state, event) {
805
734
  timestamp: event.timestamp,
806
735
  },
807
736
  ];
808
- return {
809
- ...state,
810
- blocks: keepLatestBlocks(newBlocks),
811
- omittedBlocks: (state.omittedBlocks ?? 0) + Math.max(0, newBlocks.length - MAX_BLOCKS),
812
- };
737
+ return withCappedBlocks(state, newBlocks);
813
738
  }
814
739
  export function finishRun(state, terminal, meta = {}) {
815
740
  const withFinalAnswer = promoteFinalAnswer(state, 'inferred');
@@ -862,14 +787,27 @@ function stringifyUnknown(value) {
862
787
  try {
863
788
  // JSON.stringify(undefined) returns the value undefined, which would
864
789
  // violate this function's string contract and crash callers that take
865
- // .length on the result (2026-08-17 kimi ACP tool_call without rawInput
866
- // → truncateDetail TypeError). Normalize to ''.
790
+ // .length on the result. Normalize to ''.
867
791
  return JSON.stringify(value) ?? '';
868
792
  }
869
793
  catch {
870
794
  return String(value);
871
795
  }
872
796
  }
797
+ /**
798
+ * 尾块统一收口:keepLatestBlocks 截断 + omittedBlocks 计数 + footer 更新一步
799
+ * 完成。所有「追加/更新块后只保留最新 MAX_BLOCKS 块」的事件归约共用(此前
800
+ * 多处逐字重复);`blocks` 必须传截断前的数组,被丢出的最旧块才会计入
801
+ * omittedBlocks。`footer` 不传时保持原值(plan/file_change 归约不改 footer)。
802
+ */
803
+ function withCappedBlocks(next, blocks, footer) {
804
+ return {
805
+ ...next,
806
+ blocks: keepLatestBlocks(blocks),
807
+ omittedBlocks: (next.omittedBlocks ?? 0) + Math.max(0, blocks.length - MAX_BLOCKS),
808
+ ...(footer === undefined ? {} : { footer }),
809
+ };
810
+ }
873
811
  function keepLatestBlocks(blocks) {
874
812
  return blocks.length <= MAX_BLOCKS ? blocks : blocks.slice(-MAX_BLOCKS);
875
813
  }
@@ -879,8 +817,8 @@ const TRUNCATION_MARKER_RE = /^…(前 (\d+) 字符已省略)\n/;
879
817
  * 给「已经不再是最新」的 text 块补上 `MAX_TEXT_CHARS` 存储窗口。
880
818
  *
881
819
  * 为什么最新一条不截:它就是卡片上的**输出**,终态还会被 `promoteFinalAnswer`
882
- * 提成最终回答。提前按窗口切掉头部,渲染层再怎么回答优先也补不回来(2026-11-01
883
- * 输出块被截断缺陷:16074 字符的回答只进到渲染层 12000 字符,且是从中间开始的)。
820
+ * 提成最终回答。提前按窗口切掉头部,渲染层再怎么回答优先也补不回来
821
+ * (16074 字符的回答只进到渲染层 12000 字符,且是从中间开始的)。
884
822
  *
885
823
  * 为什么其余 text 块仍然截:它们只是进度信息,卡片本来就只会展示最新几步;
886
824
  * 存储层必须保持有界(`MAX_BLOCKS` × 窗口),否则长 run 的内存随历史线性增长。
@@ -915,7 +853,7 @@ function capStaleTextBlocks(blocks) {
915
853
  * keepTail + 截断标记:被去头的存储内容以前缀如实标注(信息保真红线:禁止静默丢弃)。
916
854
  * 标记计入 maxChars 预算(存储总量仍 ≤ maxChars,保持既有长度不变量)。
917
855
  *
918
- * 计数口径(2026-10 修正):只统计**真实丢弃的正文**,不含标记自身开销。
856
+ * 计数口径:只统计**真实丢弃的正文**,不含标记自身开销。
919
857
  * 关键点是增量流下 `value` 往往已带上一次的标记(`marker + kept`);若直接
920
858
  * 用 `value.length - kept.length` 计费,会把上一帧的标记文本也算进「已省略」,
921
859
  * 造成每帧显示十几字符的虚高。这里先剥离旧标记,把 N 定义为**从该块起始累计
@@ -17,7 +17,7 @@ export { displayWidth, truncateMiddleToDisplayWidth, truncateToDisplayWidth, } f
17
17
  * 手机(~390px 内容宽)上折叠面板的标题只有**一行**可用:减去图标与内边距约
18
18
  * 320px,按 ~7.5px/半角算 ≈ 42 个半角宽度(iPhone SE 更窄,超出时按 `·`/空格
19
19
  * 折行仍可读,但不能整段换行)。超过就折行,断点常常落在 URL 或命令
20
- * 中间(2026-10-19 截图反馈)。完整参数在展开后的正文里,标题只负责"哪个工具、
20
+ * 中间。完整参数在展开后的正文里,标题只负责"哪个工具、
21
21
  * 动了什么",并带上 `HH:mm` 时间戳(一起计入预算)。
22
22
  */
23
23
  export declare const HEADER_DISPLAY_MAX = 42;
@@ -20,7 +20,7 @@ export { displayWidth, truncateMiddleToDisplayWidth, truncateToDisplayWidth, } f
20
20
  * 手机(~390px 内容宽)上折叠面板的标题只有**一行**可用:减去图标与内边距约
21
21
  * 320px,按 ~7.5px/半角算 ≈ 42 个半角宽度(iPhone SE 更窄,超出时按 `·`/空格
22
22
  * 折行仍可读,但不能整段换行)。超过就折行,断点常常落在 URL 或命令
23
- * 中间(2026-10-19 截图反馈)。完整参数在展开后的正文里,标题只负责"哪个工具、
23
+ * 中间。完整参数在展开后的正文里,标题只负责"哪个工具、
24
24
  * 动了什么",并带上 `HH:mm` 时间戳(一起计入预算)。
25
25
  */
26
26
  export const HEADER_DISPLAY_MAX = 42;
@@ -8,6 +8,18 @@
8
8
  import type { UsageSnapshot } from '../session-contract.js';
9
9
  /** `formatUsageStats` 只读这几项:会话/进度卡片上的紧凑用量行。 */
10
10
  export type SessionDisplayUsage = Pick<UsageSnapshot, 'contextLength' | 'contextLimit' | 'compactCount' | 'cacheReadTokens' | 'cacheCreationTokens'>;
11
+ /**
12
+ * Format number to human-readable unit string.
13
+ * - >= 999_500: M with at most one decimal (e.g., 999_600 → 1M, 1200000 → 1.2M,
14
+ * 25000000 → 25M)
15
+ * - >= 1K: rounded to nearest integer K (e.g., 1500 → 2K, 120000 → 120K)
16
+ * - < 1K: as-is (e.g., 500 → "500")
17
+ *
18
+ * 千位边界取 M 档:999_500 起按 K 四舍五入会得到 "1000K",直接按 M 档展示。
19
+ * 此前 run-renderer 另有一份 formatTokens 只在这边带此边界护栏,两处已收敛
20
+ * 到本函数(run-renderer 的状态行 `📊 N` 也走这里)。
21
+ */
22
+ export declare function formatTokenK(n: number): string;
11
23
  /**
12
24
  * Format usage stats (context length, compact count, cache tokens) into a display string.
13
25
  * Each stat on its own line with blank lines between sections.
@@ -7,12 +7,17 @@
7
7
  */
8
8
  /**
9
9
  * Format number to human-readable unit string.
10
- * - >= 1M: one decimal, M suffix (e.g., 1200000 → 1.2M, 25000000 → 25M)
10
+ * - >= 999_500: M with at most one decimal (e.g., 999_600 → 1M, 1200000 → 1.2M,
11
+ * 25000000 → 25M)
11
12
  * - >= 1K: rounded to nearest integer K (e.g., 1500 → 2K, 120000 → 120K)
12
13
  * - < 1K: as-is (e.g., 500 → "500")
14
+ *
15
+ * 千位边界取 M 档:999_500 起按 K 四舍五入会得到 "1000K",直接按 M 档展示。
16
+ * 此前 run-renderer 另有一份 formatTokens 只在这边带此边界护栏,两处已收敛
17
+ * 到本函数(run-renderer 的状态行 `📊 N` 也走这里)。
13
18
  */
14
- function formatTokenK(n) {
15
- if (n >= 1_000_000) {
19
+ export function formatTokenK(n) {
20
+ if (n >= 999_500) {
16
21
  const m = n / 1_000_000;
17
22
  const rounded = Math.round(m * 10) / 10;
18
23
  // Drop unnecessary ".0" for whole numbers (2.0M → 2M)
package/dist/cli.js CHANGED
@@ -92,10 +92,17 @@ export async function bootstrap(deps) {
92
92
  forwarders.set(sig, forwarder);
93
93
  onSignal(sig, forwarder);
94
94
  }
95
- child.on('error', () => {
95
+ child.on('error', (err) => {
96
+ // bun 拉起失败(ENOENT/EACCES)会静默切 node 兜底,但错误本身必须留痕,
97
+ // 否则「为什么跑在了 node 上」无从排查。logger 此时尚未初始化,console
98
+ // 是规则允许的启动期例外。
99
+ console.error('[lark-remote] bun spawn failed, falling back to node:', err);
96
100
  for (const [sig, forwarder] of forwarders)
97
101
  offSignal(sig, forwarder);
98
- void importEntry().catch(() => exit(1));
102
+ void importEntry().catch((importErr) => {
103
+ console.error('[lark-remote] node fallback import failed:', importErr);
104
+ exit(1);
105
+ });
99
106
  });
100
107
  child.on('exit', (code, signal) => {
101
108
  if (signal) {
@@ -146,7 +153,12 @@ function main() {
146
153
  killSelf: (sig) => process.kill(process.pid, sig),
147
154
  exit: (code) => process.exit(code),
148
155
  };
149
- void bootstrap(deps).catch(() => process.exit(1));
156
+ // bootstrap 是第一接触面:静默 exit(1) 会让安装损坏/依赖缺失表现为零输出
157
+ // 退出,必须把根因打到 stderr(logger 未初始化,console 为规则允许的例外)。
158
+ void bootstrap(deps).catch((err) => {
159
+ console.error('[lark-remote] bootstrap failed:', err);
160
+ process.exit(1);
161
+ });
150
162
  }
151
163
  if (isDirectEntry()) {
152
164
  main();
package/dist/clone.js CHANGED
@@ -237,7 +237,7 @@ export class CloneSession {
237
237
  // 图片发送失败不阻断流程:给出浏览器打开兜底。
238
238
  await this.connector
239
239
  .sendWithRetry(ctx.chatId, { text: `⚠️ 二维码图片发送失败,请在浏览器打开创建:${info.url}` }, { replyTo: ctx.messageId })
240
- .catch(() => { });
240
+ .catch((err) => getLogger().warn('[clone] fallback notice send failed:', err));
241
241
  return;
242
242
  }
243
243
  finally {
@@ -336,7 +336,7 @@ export class CloneSession {
336
336
  `新应用凭证(请妥善保管):App ID ${result.client_id}\n` +
337
337
  '可修复本机问题后用 /clone 重新走一遍流程(会创建新应用)',
338
338
  }, { replyTo: ctx.messageId })
339
- .catch(() => { });
339
+ .catch((sendErr) => getLogger().warn('[clone] finalize failure notice send failed:', sendErr));
340
340
  }
341
341
  finally {
342
342
  this.state = 'idle';