mocode-ai 1.3.7 → 1.3.9
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/agent/index.js +7 -2
- package/dist/config/index.js +10 -0
- package/dist/i18n/index.js +24 -2
- package/dist/mcp/config.js +3 -0
- package/dist/repl/index.js +46 -2
- package/dist/ui/layout.js +6 -14
- package/package.json +1 -1
package/dist/agent/index.js
CHANGED
|
@@ -194,8 +194,13 @@ function finishStandaloneBatch(id, expandSingleEntry) {
|
|
|
194
194
|
batch.endBatch(id, layout);
|
|
195
195
|
if (expandSingleEntry)
|
|
196
196
|
batch.expandSingleEntryFully(id, layout);
|
|
197
|
-
// mutation
|
|
198
|
-
//
|
|
197
|
+
// mutation 独占批收尾:currentBatchId 已置空。若不告知后续正文「刚出工具块」,
|
|
198
|
+
// writeAssistantText 的 inToolBlock 会判 false,LLM 流式前缀的 \n / \n\n 就不会被剥掉,
|
|
199
|
+
// 与 expandSingleEntryFully 已写的 separator 叠成 2 行空白(edit_file 后偶发空两行)。
|
|
200
|
+
// 置位后与 sub-agent 组收口走同一条对称路径;separator 已由 expandSingleEntryFully
|
|
201
|
+
// 内部 commit,后续 flush 由 isLastContentRowBlank 守卫拦住,不会多补一行。
|
|
202
|
+
// 只读批无需此标记:它的 currentBatchId 一直挂到正文到达,inToolBlock 天然为 true。
|
|
203
|
+
subAgentGroupPendingSeparator = true;
|
|
199
204
|
}
|
|
200
205
|
/** 将跨 LLM 工具轮次累计的调用写入内容区;正文开始或整个 turn 收尾时才切批。 */
|
|
201
206
|
function flushToolBatch(expandSingleEntry = false) {
|
package/dist/config/index.js
CHANGED
|
@@ -529,6 +529,7 @@ export const config = {
|
|
|
529
529
|
subAgentEnabled: process.env.MOCODE_SUBAGENT_ENABLED === 'true',
|
|
530
530
|
subAgentMaxSteps: Number(process.env.SUB_AGENT_MAX_STEPS) || Number(process.env.MAX_STEPS) || 1000,
|
|
531
531
|
frontendToolsEnabled: process.env.MOCODE_FRONTEND_TOOLS_ENABLED === 'true',
|
|
532
|
+
mcpEnabled: process.env.MOCODE_MCP_ENABLED !== 'false',
|
|
532
533
|
sessionDir: path.join(process.cwd(), '.mocode', 'sessions'),
|
|
533
534
|
searchApiKey: process.env.ANYSEARCH_API_KEY,
|
|
534
535
|
sandboxRoot: process.env.SANDBOX_ROOT || undefined,
|
|
@@ -613,6 +614,15 @@ export function updateFrontendToolsConfig(enabled) {
|
|
|
613
614
|
config.frontendToolsEnabled = enabled;
|
|
614
615
|
process.env.MOCODE_FRONTEND_TOOLS_ENABLED = enabled ? 'true' : 'false';
|
|
615
616
|
}
|
|
617
|
+
/** MCP 总开关;关闭时下次启动跳过 MCP 配置读取与服务连接。 */
|
|
618
|
+
export function isMcpEnabled() {
|
|
619
|
+
return config.mcpEnabled;
|
|
620
|
+
}
|
|
621
|
+
/** 运行时更新 MCP 开关;连接建立或释放、工具表刷新由 REPL 调用方执行。 */
|
|
622
|
+
export function updateMcpConfig(enabled) {
|
|
623
|
+
config.mcpEnabled = enabled;
|
|
624
|
+
process.env.MOCODE_MCP_ENABLED = enabled ? 'true' : 'false';
|
|
625
|
+
}
|
|
616
626
|
/**
|
|
617
627
|
* 记忆子系统总开关:单一来源。/memory_switch、/memory_status、buildSystemPrompt、
|
|
618
628
|
* tools/builtins/index.ts、tools/constants.ts 的 plan-mode 列表都从这里查。
|
package/dist/i18n/index.js
CHANGED
|
@@ -27,6 +27,10 @@ const zhCN = {
|
|
|
27
27
|
'commands.feOn': '开启前端工具簇',
|
|
28
28
|
'commands.feOff': '关闭前端工具簇',
|
|
29
29
|
'commands.feStatus': '查看前端工具簇状态',
|
|
30
|
+
'commands.mcp': 'MCP 服务开关(重启后生效)',
|
|
31
|
+
'commands.mcpOn': '开启 MCP 服务',
|
|
32
|
+
'commands.mcpOff': '关闭 MCP 服务',
|
|
33
|
+
'commands.mcpStatus': '查看 MCP 服务状态',
|
|
30
34
|
'commands.theme': '切换颜色主题(↑↓·Enter)',
|
|
31
35
|
'commands.model': '模型配置与预设管理',
|
|
32
36
|
'commands.modelConfigure': '配置新模型(向导)',
|
|
@@ -175,7 +179,6 @@ const zhCN = {
|
|
|
175
179
|
'banner.disabled': '关闭',
|
|
176
180
|
'status.modeSwitch': 'Shift+Tab 切换',
|
|
177
181
|
'status.history': '历史 ↑{count}(PgDn 回底)',
|
|
178
|
-
'status.interruptHint': 'Esc / Ctrl+C 中断',
|
|
179
182
|
'status.measured': '实测',
|
|
180
183
|
'status.estimated': '估算',
|
|
181
184
|
'status.messages': '{count} 条消息',
|
|
@@ -260,6 +263,14 @@ const zhCN = {
|
|
|
260
263
|
'fe.changedOn': '已开启前端工具簇;browser / dev_server / screenshot / view_image 将从下一次模型请求起可用。',
|
|
261
264
|
'fe.changedOff': '已关闭前端工具簇;browser / dev_server / screenshot / view_image 已从模型工具表移除。',
|
|
262
265
|
'fe.usage': '用法:/fe on|off|status',
|
|
266
|
+
'mcp.status': 'MCP 服务:{state}',
|
|
267
|
+
'mcp.stateOn': '开启',
|
|
268
|
+
'mcp.stateOff': '关闭',
|
|
269
|
+
'mcp.changedOn': '已开启 MCP;已按当前配置连接服务并刷新工具表。',
|
|
270
|
+
'mcp.changedOff': '已关闭 MCP;已断开连接并从工具表移除 MCP 工具。',
|
|
271
|
+
'mcp.usage': '用法:/mcp on|off|status',
|
|
272
|
+
'mcp.unchanged': 'MCP 已是{state},未变更。',
|
|
273
|
+
'mcp.restartHint': '该设置已写入配置文件;下次启动将按此状态决定是否连接 MCP。',
|
|
263
274
|
'plan.ready': '计划已就绪',
|
|
264
275
|
'plan.approvalDetail': '切换到 auto 模式按上述计划执行?(plan 模式只读探查,执行需切 auto)',
|
|
265
276
|
'plan.execute': '切 auto 执行',
|
|
@@ -318,6 +329,10 @@ const en = {
|
|
|
318
329
|
'commands.feOn': 'Enable frontend tools',
|
|
319
330
|
'commands.feOff': 'Disable frontend tools',
|
|
320
331
|
'commands.feStatus': 'Show frontend tools status',
|
|
332
|
+
'commands.mcp': 'MCP service controls (applies immediately)',
|
|
333
|
+
'commands.mcpOn': 'Enable MCP services',
|
|
334
|
+
'commands.mcpOff': 'Disable MCP services',
|
|
335
|
+
'commands.mcpStatus': 'Show MCP service status',
|
|
321
336
|
'commands.theme': 'Switch color theme (↑↓·Enter)',
|
|
322
337
|
'commands.model': 'Model configuration and presets',
|
|
323
338
|
'commands.modelConfigure': 'Configure a new model (wizard)',
|
|
@@ -466,7 +481,6 @@ const en = {
|
|
|
466
481
|
'banner.disabled': 'Disabled',
|
|
467
482
|
'status.modeSwitch': 'Shift+Tab Switch',
|
|
468
483
|
'status.history': 'History ↑{count} (PgDn to bottom)',
|
|
469
|
-
'status.interruptHint': 'Esc / Ctrl+C to interrupt',
|
|
470
484
|
'status.measured': 'measured',
|
|
471
485
|
'status.estimated': 'estimated',
|
|
472
486
|
'status.messages': '{count} messages',
|
|
@@ -551,6 +565,14 @@ const en = {
|
|
|
551
565
|
'fe.changedOn': 'Frontend tools enabled; browser / dev_server / screenshot / view_image will be available from the next model request.',
|
|
552
566
|
'fe.changedOff': 'Frontend tools disabled; browser / dev_server / screenshot / view_image have been removed from the model tool list.',
|
|
553
567
|
'fe.usage': 'Usage: /fe on|off|status',
|
|
568
|
+
'mcp.status': 'MCP services: {state}',
|
|
569
|
+
'mcp.stateOn': 'enabled',
|
|
570
|
+
'mcp.stateOff': 'disabled',
|
|
571
|
+
'mcp.changedOn': 'MCP enabled; services have been connected from the current configuration and the tool list refreshed.',
|
|
572
|
+
'mcp.changedOff': 'MCP disabled; connections have been closed and MCP tools removed from the tool list.',
|
|
573
|
+
'mcp.usage': 'Usage: /mcp on|off|status',
|
|
574
|
+
'mcp.unchanged': 'MCP is already {state}; no change made.',
|
|
575
|
+
'mcp.restartHint': 'This setting is saved to the configuration file; future startups use it to decide whether to connect MCP.',
|
|
554
576
|
'plan.ready': 'Plan ready',
|
|
555
577
|
'plan.approvalDetail': 'Switch to auto mode and execute the plan above? (Plan mode is read-only; execution requires auto mode.)',
|
|
556
578
|
'plan.execute': 'Switch to auto and execute',
|
package/dist/mcp/config.js
CHANGED
|
@@ -7,6 +7,9 @@ import path from 'node:path';
|
|
|
7
7
|
* JSON 格式兼容常见的 { "mcpServers": { name: { command/url/... } } },也允许直接 server map。
|
|
8
8
|
*/
|
|
9
9
|
export function readMcpServers() {
|
|
10
|
+
// 总开关必须早于任何配置读取,以便即使用户级 MCP 配置存在也能快速启动。
|
|
11
|
+
if (process.env.MOCODE_MCP_ENABLED === 'false')
|
|
12
|
+
return { servers: [], warnings: [] };
|
|
10
13
|
const warnings = [];
|
|
11
14
|
const servers = new Map();
|
|
12
15
|
const fromFile = process.env.MCP_CONFIG_PATH;
|
package/dist/repl/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import readline from 'node:readline/promises';
|
|
2
2
|
import { emitKeypressEvents } from 'node:readline';
|
|
3
3
|
import { stdin, stdout } from 'node:process';
|
|
4
|
-
import { config, updateModelConfig, isModelConfigured, updateMemoryConfig, isMemoryEnabled, isSubAgentEnabled, updateSubAgentConfig, isFrontendToolsEnabled, updateFrontendToolsConfig, updateLanguageConfig, languageFromShell, buildBasePrompt, getPlanModeSuffix, hasCodegraphIndex, DEFAULT_CONTEXT_WINDOW_TOKENS, pinSessionModel, } from '../config/index.js';
|
|
4
|
+
import { config, updateModelConfig, isModelConfigured, updateMemoryConfig, isMemoryEnabled, isSubAgentEnabled, updateSubAgentConfig, isFrontendToolsEnabled, updateFrontendToolsConfig, isMcpEnabled, updateMcpConfig, updateLanguageConfig, languageFromShell, buildBasePrompt, getPlanModeSuffix, hasCodegraphIndex, DEFAULT_CONTEXT_WINDOW_TOKENS, pinSessionModel, } from '../config/index.js';
|
|
5
5
|
import { getLanguage, normalizeLanguage, t, } from '../i18n/index.js';
|
|
6
6
|
import { DEFAULT_BUDGET_POLICY } from '../context/budget.js';
|
|
7
7
|
import { updateConfigKey, writeConfigKeys, CONFIG_PATH } from '../config/file.js';
|
|
@@ -84,6 +84,13 @@ function buildSlashCommands() {
|
|
|
84
84
|
{ name: 'status', value: '/fe status', desc: d('commands.feStatus') },
|
|
85
85
|
],
|
|
86
86
|
},
|
|
87
|
+
{
|
|
88
|
+
name: '/mcp', desc: d('commands.mcp'), children: [
|
|
89
|
+
{ name: 'on', value: '/mcp on', desc: d('commands.mcpOn') },
|
|
90
|
+
{ name: 'off', value: '/mcp off', desc: d('commands.mcpOff') },
|
|
91
|
+
{ name: 'status', value: '/mcp status', desc: d('commands.mcpStatus') },
|
|
92
|
+
],
|
|
93
|
+
},
|
|
87
94
|
{ name: '/theme', desc: d('commands.theme') },
|
|
88
95
|
{
|
|
89
96
|
name: '/model', desc: d('commands.model'), children: [
|
|
@@ -157,7 +164,7 @@ const LLM_ERROR_HINT_KEYS = {
|
|
|
157
164
|
const HELP_GROUPS = [
|
|
158
165
|
{ key: 'help.groupFrequent', names: ['/help', '/clear', '/context', '/compact', '/resume', '/model'] },
|
|
159
166
|
{ key: 'help.groupSession', names: ['/sessions', '/rollback', '/memory', '/skills', '/skill', '/init'] },
|
|
160
|
-
{ key: 'help.groupConfig', names: ['/mode', '/subagent', '/fe', '/theme', '/pet', '/image', '/language', '/upgrade'] },
|
|
167
|
+
{ key: 'help.groupConfig', names: ['/mode', '/subagent', '/fe', '/mcp', '/theme', '/pet', '/image', '/language', '/upgrade'] },
|
|
161
168
|
];
|
|
162
169
|
/**
|
|
163
170
|
* 菜单树里全部可用命令名(含分支路径与叶子的真实命令),供未知命令纠错建议用。
|
|
@@ -2401,6 +2408,43 @@ export async function startRepl(initialHistory, sessionId, sandboxRootOverride,
|
|
|
2401
2408
|
layout.contentWrite(`${enabled ? ui.green : ui.yellow}${t(enabled ? 'fe.changedOn' : 'fe.changedOff')}${ui.reset}\n`);
|
|
2402
2409
|
continue;
|
|
2403
2410
|
}
|
|
2411
|
+
if (line === '/mcp' || line.startsWith('/mcp ')) {
|
|
2412
|
+
const arg = line.startsWith('/mcp ')
|
|
2413
|
+
? line.slice('/mcp '.length).trim().toLowerCase()
|
|
2414
|
+
: 'status';
|
|
2415
|
+
if (arg === '' || arg === 'status') {
|
|
2416
|
+
const enabled = isMcpEnabled();
|
|
2417
|
+
const state = t(enabled ? 'mcp.stateOn' : 'mcp.stateOff');
|
|
2418
|
+
layout.contentWrite(`${ui.accent}${t('mcp.status', { state })}${ui.reset}\n` +
|
|
2419
|
+
`${ui.dim}MOCODE_MCP_ENABLED=${enabled ? 'true' : 'false'} · ${CONFIG_PATH}${ui.reset}\n` +
|
|
2420
|
+
`${ui.dim}${t('mcp.restartHint')}${ui.reset}\n`);
|
|
2421
|
+
continue;
|
|
2422
|
+
}
|
|
2423
|
+
if (arg !== 'on' && arg !== 'off') {
|
|
2424
|
+
layout.contentWrite(`${ui.yellow}${t('mcp.usage')}${ui.reset}\n`);
|
|
2425
|
+
continue;
|
|
2426
|
+
}
|
|
2427
|
+
const enabled = arg === 'on';
|
|
2428
|
+
if (enabled === isMcpEnabled()) {
|
|
2429
|
+
layout.contentWrite(`${ui.dim}${t('mcp.unchanged', { state: t(enabled ? 'mcp.stateOn' : 'mcp.stateOff') })}${ui.reset}\n`);
|
|
2430
|
+
continue;
|
|
2431
|
+
}
|
|
2432
|
+
updateMcpConfig(enabled);
|
|
2433
|
+
updateConfigKey('MOCODE_MCP_ENABLED', enabled ? 'true' : 'false');
|
|
2434
|
+
if (enabled) {
|
|
2435
|
+
await initializeAllMcp();
|
|
2436
|
+
registerToolsExtension('mcp', getMcpTools());
|
|
2437
|
+
}
|
|
2438
|
+
else {
|
|
2439
|
+
await closeAllMcp();
|
|
2440
|
+
registerToolsExtension('mcp', []);
|
|
2441
|
+
}
|
|
2442
|
+
refreshChatTools();
|
|
2443
|
+
history[0] = { role: 'system', content: buildSystemMessage(getAgentMode() === 'plan') };
|
|
2444
|
+
layout.rewriteBanner(bannerLines(banner()));
|
|
2445
|
+
layout.contentWrite(`${enabled ? ui.green : ui.yellow}${t(enabled ? 'mcp.changedOn' : 'mcp.changedOff')}${ui.reset}\n`);
|
|
2446
|
+
continue;
|
|
2447
|
+
}
|
|
2404
2448
|
if (line === '/memory_switch' ||
|
|
2405
2449
|
line.startsWith('/memory_switch ') ||
|
|
2406
2450
|
line === '/memory_status' ||
|
package/dist/ui/layout.js
CHANGED
|
@@ -1406,14 +1406,6 @@ function composeSpinnerLine(status, cols) {
|
|
|
1406
1406
|
const elapsed = (mode === 'running' && turnStart != null)
|
|
1407
1407
|
? fmtElapsed(Date.now() - turnStart)
|
|
1408
1408
|
: '';
|
|
1409
|
-
// 新手中断提示:每轮开始后前 6 秒,spinner 行尾随一次 dim「(Esc / Ctrl+C 中断)」,
|
|
1410
|
-
// 6 秒后自然消失(不常驻,不打扰老用户)。只在非滚动回看时画,回看态右段让给历史指示。
|
|
1411
|
-
const INTERRUPT_HINT_WINDOW_MS = 6000;
|
|
1412
|
-
const hintActive = !scrolled && mode === 'running' && turnStart != null
|
|
1413
|
-
&& Date.now() - turnStart < INTERRUPT_HINT_WINDOW_MS;
|
|
1414
|
-
const hintText = t('status.interruptHint');
|
|
1415
|
-
const hint = hintActive ? ` ${ui.dim}(${hintText})${ui.reset}` : '';
|
|
1416
|
-
const hintW = hintActive ? 4 + displayWidth(hintText) : 0; // 2 缩进 + 左右括号
|
|
1417
1409
|
// ── 左段:帧 + 状态 + 走时,全部紧跟 ──
|
|
1418
1410
|
let lead;
|
|
1419
1411
|
let leadW;
|
|
@@ -1432,17 +1424,17 @@ function composeSpinnerLine(status, cols) {
|
|
|
1432
1424
|
leadW = 1 + 1 + displayWidth(label) + (elapsed ? 1 + displayWidth(elapsed) : 0);
|
|
1433
1425
|
}
|
|
1434
1426
|
else if (hasSpinner) {
|
|
1435
|
-
// spinner 激活(思考中/执行工具…):帧 + 状态 + 走时
|
|
1427
|
+
// spinner 激活(思考中/执行工具…):帧 + 状态 + 走时
|
|
1436
1428
|
const ePart = elapsed ? ` ${ui.dim}${elapsed}${ui.reset}` : '';
|
|
1437
|
-
lead = `${ui.bold}${ui.accent}${status.spinnerFrame}${ui.reset} ${ui.dim}${status.status}${ui.reset}${ePart}
|
|
1438
|
-
leadW = 1 + 1 + displayWidth(status.status) + (elapsed ? 1 + displayWidth(elapsed) : 0)
|
|
1429
|
+
lead = `${ui.bold}${ui.accent}${status.spinnerFrame}${ui.reset} ${ui.dim}${status.status}${ui.reset}${ePart}`;
|
|
1430
|
+
leadW = 1 + 1 + displayWidth(status.status) + (elapsed ? 1 + displayWidth(elapsed) : 0);
|
|
1439
1431
|
}
|
|
1440
1432
|
else if (spinning) {
|
|
1441
|
-
// 运行态心跳帧(流式输出中 / 命令态如 /rollback /compact /resume):帧 + 状态文字(优先)或生成中(兜底) + 走时
|
|
1433
|
+
// 运行态心跳帧(流式输出中 / 命令态如 /rollback /compact /resume):帧 + 状态文字(优先)或生成中(兜底) + 走时
|
|
1442
1434
|
const label = status.status || '生成中';
|
|
1443
1435
|
const ePart = elapsed ? ` ${ui.dim}${elapsed}${ui.reset}` : '';
|
|
1444
|
-
lead = `${ui.bold}${ui.accent}${RUNNING_FRAMES[runningFrame]}${ui.reset} ${ui.dim}${label}${ui.reset}${ePart}
|
|
1445
|
-
leadW = 1 + 1 + displayWidth(label) + (elapsed ? 1 + displayWidth(elapsed) : 0)
|
|
1436
|
+
lead = `${ui.bold}${ui.accent}${RUNNING_FRAMES[runningFrame]}${ui.reset} ${ui.dim}${label}${ui.reset}${ePart}`;
|
|
1437
|
+
leadW = 1 + 1 + displayWidth(label) + (elapsed ? 1 + displayWidth(elapsed) : 0);
|
|
1446
1438
|
}
|
|
1447
1439
|
else {
|
|
1448
1440
|
// INPUT 态:● + 状态文字(无走时)
|