mocode-ai 1.1.4 → 1.1.5

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/dist/llm/index.js CHANGED
@@ -3,6 +3,13 @@ import { config, isSubAgentEnabled } from '../config/index.js';
3
3
  import { tools } from '../tools/registry.js';
4
4
  import { getPlanDisabledTools } from '../tools/constants.js';
5
5
  import { ThinkTagFilter } from './think-filter.js';
6
+ // 强制关闭第三方调试日志泄漏:openai SDK 在 process.env.DEBUG === 'true' 时用裸
7
+ // console.log 把请求/响应直写 stdout,会污染 TUI 输入框(并泄露 headers/URL)。
8
+ // 仅拦截 'true' 这一开关值——保留 namespace 形式的 DEBUG(如 DEBUG=express:*) 调试能力。
9
+ // 必须在 OpenAI 客户端实例化之前执行,确保任何实例都不再触发 debug 输出。
10
+ if (process.env.DEBUG === 'true') {
11
+ process.env.DEBUG = 'false';
12
+ }
6
13
  /**
7
14
  * LLM 调用重试策略:
8
15
  * 可重试 → 429 (rate limit) / 5xx (server) / APIConnectionError / Node 网络错 (ETIMEDOUT 等)
@@ -760,6 +760,8 @@ export async function startRepl(initialHistory, sessionId, sandboxRootOverride,
760
760
  layout.contentMode();
761
761
  if (history.some((m) => m.role === 'user')) {
762
762
  renderHistory(history);
763
+ // 强制回尾:同 /resume 命令,renderHistory 展开详情会设 scrollOffset>0,需复位避免闪烁。
764
+ layout.resetScroll();
763
765
  }
764
766
  else {
765
767
  layout.writeBanner(bannerLines(banner()));
@@ -907,6 +909,11 @@ export async function startRepl(initialHistory, sessionId, sandboxRootOverride,
907
909
  // rollbackFlow 原本漏了这一行,renderHistory 末尾的 batch 摘要行 / assistant 文本
908
910
  // 收口后,续写位未稳到新空行,下一次 echoInput 的 ❯ 气泡会黏在最后一条输出后面。
909
911
  layout.contentWrite('\n');
912
+ // 回滚后强制回尾:确保 scrollOffset=0,避免后续 showLiveBatch 在冻结视口下
913
+ // 调用 repaintViewport 导致工具信息闪烁/滚动消失(rollback 后用户继续输入触发新 agent
914
+ // 运行时,若 scrollOffset 意外 >0,contentWrite 只喂缓冲不物理写,showLiveBatch 的
915
+ // repaintViewport 画出不含新摘要的冻结窗口 → 工具信息"闪现后消失")。
916
+ layout.resetScroll();
910
917
  pendingPrefill = prefillText.split('\n');
911
918
  };
912
919
  /**
@@ -1040,6 +1047,9 @@ export async function startRepl(initialHistory, sessionId, sandboxRootOverride,
1040
1047
  lastTurnUsage = undefined; // 续接:旧会话的 token 累计已无意义,清空等下轮覆写
1041
1048
  layout.clearContent();
1042
1049
  renderHistory(history);
1050
+ // 强制回尾:renderHistory 展开 mutation 工具详情会经 contentInsertAfter 设置 scrollOffset>0;
1051
+ // 若不复位,后续 showLiveBatch 在冻结视口下 repaintViewport 会导致工具信息闪烁/滚动消失。
1052
+ layout.resetScroll();
1043
1053
  // 末尾 \n\n:与后续用户消息(❯ bubble)之间空一行。
1044
1054
  layout.contentWrite(`${ui.dim}${t('repl.resumed', { id: loaded.id })}${ui.reset}\n\n`);
1045
1055
  }
package/dist/ui/batch.js CHANGED
@@ -194,7 +194,12 @@ export function showLiveBatch(id, layout) {
194
194
  // 首条摘要通过增量 contentWrite 落屏时,markdown→普通内容的边界可能只更新了
195
195
  // buffer/续写位;直到第二个 header 的 contentReplaceLine 或后续正文重绘才完全可见。
196
196
  // 立即按 buffer 原子重画,确保慢工具执行期间摘要前的空行已经显示。
197
- layout.repaintViewport?.();
197
+ // 但滚动回看时(scrollOffset>0) contentWrite 只喂缓冲+冻结视口,此时 repaintViewport
198
+ // 会画出不含新摘要的冻结窗口(新摘要在窗口之下),造成闪烁;跳过重画,
199
+ // 等用户回底(scrollOffset=0)时自然可见。修复 rollback 后工具信息滚动消失问题。
200
+ if (!layout.isScrolled?.()) {
201
+ layout.repaintViewport?.();
202
+ }
198
203
  }
199
204
  else {
200
205
  layout.contentReplaceLine(b.summaryAbsIdx, summary);
package/dist/ui/layout.js CHANGED
@@ -1,4 +1,5 @@
1
1
  import { stdin, stdout } from 'node:process';
2
+ import { inspect } from 'node:util';
2
3
  import { charWidth, displayWidth, truncateDisplay, truncateDisplayHead, truncateAnsi, ansiDisplayWidth, wrapByDisplayWidth, fmtElapsed, stripAnsi, sliceByDisplayCol, } from './render.js';
3
4
  import { ui, applyTerminalBackground, resetTerminalBackground } from './theme.js';
4
5
  import * as content from './content.js';
@@ -9,6 +10,58 @@ import { renderMarkdown } from './markdown.js';
9
10
  import { t } from '../i18n/index.js';
10
11
  // ── 内部状态 ──
11
12
  let active = false;
13
+ // ── 裸 console 防御:第三方库(如 openai SDK)可能用 console.log 直写 stdout,
14
+ // 在 RUNNING 态会落到光标所在的底栏输入框,污染输入。进入 TUI 后把 console.*
15
+ // 劫持到 contentWrite,统一进内容区(运行态下 contentWrite 末尾会把真光标归位输入框),
16
+ // 既不再泄漏到输入框,也不会破坏 TUI 布局。TUI 外(active=false)不劫持,
17
+ // 恢复原始 console,保证 host 子进程 JSON 协议 / 退出日志正常。
18
+ let consoleHookInstalled = false;
19
+ const origConsole = {
20
+ log: console.log,
21
+ error: console.error,
22
+ warn: console.warn,
23
+ info: console.info,
24
+ };
25
+ function routeConsoleToContent(method) {
26
+ const c = console;
27
+ c[method] = (...args) => {
28
+ if (active && ui.isTTY) {
29
+ let s;
30
+ try {
31
+ s = args
32
+ .map((a) => (typeof a === 'string' ? a : inspect(a, { depth: 4 })))
33
+ .join(' ');
34
+ }
35
+ catch {
36
+ s = String(args[0]);
37
+ }
38
+ if (!s.endsWith('\n'))
39
+ s += '\n';
40
+ contentWrite(s);
41
+ }
42
+ else {
43
+ origConsole[method](...args);
44
+ }
45
+ };
46
+ }
47
+ function installConsoleGuard() {
48
+ if (consoleHookInstalled)
49
+ return;
50
+ routeConsoleToContent('log');
51
+ routeConsoleToContent('error');
52
+ routeConsoleToContent('warn');
53
+ routeConsoleToContent('info');
54
+ consoleHookInstalled = true;
55
+ }
56
+ function uninstallConsoleGuard() {
57
+ if (!consoleHookInstalled)
58
+ return;
59
+ console.log = origConsole.log;
60
+ console.error = origConsole.error;
61
+ console.warn = origConsole.warn;
62
+ console.info = origConsole.info;
63
+ consoleHookInstalled = false;
64
+ }
12
65
  let mode = 'input';
13
66
  let footerH = 6; // 1 虚拟空行 + 1 spinner行 + 1 上线 + 输入行数 + 1 下线 + 1 model行(两行式底栏)
14
67
  let contentRow = 1; // 续写位行(1-based,屏坐标,[1,contentBottom])
@@ -1944,12 +1997,14 @@ export function enterAltScreen() {
1944
1997
  resizeTimer?.unref?.();
1945
1998
  };
1946
1999
  process.on('SIGWINCH', sigwinchHandler);
2000
+ installConsoleGuard(); // 进入 TUI 即接管裸 console 输出,防第三方日志污染输入框
1947
2001
  }
1948
2002
  /** 复原:复位 margins + 显光标 + 退 alt + 还原 raw。幂等。 */
1949
2003
  export function exitAltScreen() {
1950
2004
  if (!active)
1951
2005
  return;
1952
2006
  active = false;
2007
+ uninstallConsoleGuard(); // 退出 TUI 恢复原始 console(不影响 host 子进程 / 退出日志)
1953
2008
  resetTerminalBackground();
1954
2009
  stopTurnTimer(); // 兜底清走时计时器(防异常退出泄漏)
1955
2010
  turnStart = null;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mocode-ai",
3
- "version": "1.1.4",
3
+ "version": "1.1.5",
4
4
  "description": "终端编码 agent:LLM + tool-call 循环 + 流式输出(含思考)+ 16 个工具,接任意 OpenAI 兼容后端。",
5
5
  "type": "module",
6
6
  "bin": {