u1s1-cli 1.12.0 → 1.13.0
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-setup.d.ts +14 -3
- package/dist/agent-setup.js +54 -3
- package/dist/index.js +1 -1
- package/dist/mcp/command.d.ts +2 -2
- package/dist/mcp/command.js +39 -27
- package/dist/mcp/config.d.ts +22 -6
- package/dist/mcp/config.js +0 -0
- package/dist/mcp/tools.d.ts +2 -0
- package/dist/mcp/tools.js +4 -0
- package/package.json +3 -3
- package/scripts/patch-pi.js +109 -5
package/dist/agent-setup.d.ts
CHANGED
|
@@ -14,6 +14,16 @@ export declare function ensureBrandPrompt(shell: ShellDoctorResult): void;
|
|
|
14
14
|
* them while a diverged user value becomes user-owned.
|
|
15
15
|
*/
|
|
16
16
|
export declare function applyModelThinkingDefaults(settings: Record<string, unknown>, models: ModelDef[]): boolean;
|
|
17
|
+
/**
|
|
18
|
+
* pi 内置 codemode 默认不开,u1s1 给每个用户开一次(`+codemode` 只加不替换默认工具)。
|
|
19
|
+
* 标记写下后不再碰:用户在 defaultTools 里删掉或写 -codemode 都算用户的选择。
|
|
20
|
+
*/
|
|
21
|
+
export declare function applyCodemodeDefault(settings: Record<string, unknown>): boolean;
|
|
22
|
+
/**
|
|
23
|
+
* pi 1.0 内置 llama.cpp 扩展(本地模型 provider + /llama),u1s1 走网关用不上,默认关一次。
|
|
24
|
+
* 用户在 extensions 里自己写过 builtin:llama.cpp 的任何开关就不动;标记写下后同样不再碰。
|
|
25
|
+
*/
|
|
26
|
+
export declare function applyLlamaDisabledDefault(settings: Record<string, unknown>): boolean;
|
|
17
27
|
/** Defaults that don't overwrite values the user already set. */
|
|
18
28
|
export declare function ensureDefaultSettings(models?: ModelDef[]): void;
|
|
19
29
|
/** ≤0.4.0 wrote u1s1-dark/u1s1-light into the pi themes dir; remove them. */
|
|
@@ -56,9 +66,10 @@ export declare function writeErrorHumanizeExtension(): void;
|
|
|
56
66
|
*/
|
|
57
67
|
export declare function writeAnnouncementsExtension(): void;
|
|
58
68
|
/**
|
|
59
|
-
* 生成「自定义 MCP 服务」扩展到 <agentDir>/extensions/u1s1-mcp.js
|
|
60
|
-
*
|
|
61
|
-
*
|
|
69
|
+
* 生成「自定义 MCP 服务」扩展到 <agentDir>/extensions/u1s1-mcp.js,只给没有内置 MCP 的
|
|
70
|
+
* 旧引擎用(Desktop App 的 pi-web-ui 还是 0.83):读 agent/mcp.json,把工具注册成
|
|
71
|
+
* mcp_<服务>_<工具>,逻辑在 mcp/extension.ts。pi ≥0.99 有 pi.registerMcpServer,说明内置
|
|
72
|
+
* MCP 会读同一份配置,扩展直接让位。投影前先把 ≤1.12 的 ~/.u1s1/mcp.json 迁过去。
|
|
62
73
|
* 子会话(spawn_subagent)里不挂 MCP 工具:避免每个子进程都拉起一份服务。
|
|
63
74
|
*/
|
|
64
75
|
export declare function writeMcpExtension(): void;
|
package/dist/agent-setup.js
CHANGED
|
@@ -3,6 +3,7 @@ import { fileURLToPath } from "node:url";
|
|
|
3
3
|
import { dirname, join } from "node:path";
|
|
4
4
|
import { activeSite, agentDir, CUSTOM_ENDPOINTS, ENDPOINT_ID_RE, effectiveContextWindow, endpointKeyEnvName, MODELS, readCompactionBudgetSetting, PROVIDER_ID, UNKNOWN_MODEL_COST, VERSION, } from "./config.js";
|
|
5
5
|
import { tr } from "./i18n.js";
|
|
6
|
+
import { migrateLegacyMcpConfig } from "./mcp/config.js";
|
|
6
7
|
/**
|
|
7
8
|
* 系统提示里的品牌人设。中国站面向编程新手、兜底语言中文(保持原文一字不差);
|
|
8
9
|
* 国际站面向开发者、兜底语言英文。按站点而不是界面语言判定,用时求值(配置可能晚于 import 才读)。
|
|
@@ -85,6 +86,8 @@ function bundledPiVersion() {
|
|
|
85
86
|
}
|
|
86
87
|
}
|
|
87
88
|
const THINKING_DEFAULTS_MARKER = "u1s1ModelThinkingDefaults";
|
|
89
|
+
const CODEMODE_DEFAULT_MARKER = "u1s1CodemodeDefault";
|
|
90
|
+
const LLAMA_DISABLED_MARKER = "u1s1LlamaDisabledDefault";
|
|
88
91
|
function recordValue(value) {
|
|
89
92
|
return typeof value === "object" && value !== null && !Array.isArray(value)
|
|
90
93
|
? value
|
|
@@ -128,6 +131,37 @@ export function applyModelThinkingDefaults(settings, models) {
|
|
|
128
131
|
}
|
|
129
132
|
return changed;
|
|
130
133
|
}
|
|
134
|
+
/**
|
|
135
|
+
* pi 内置 codemode 默认不开,u1s1 给每个用户开一次(`+codemode` 只加不替换默认工具)。
|
|
136
|
+
* 标记写下后不再碰:用户在 defaultTools 里删掉或写 -codemode 都算用户的选择。
|
|
137
|
+
*/
|
|
138
|
+
export function applyCodemodeDefault(settings) {
|
|
139
|
+
if (settings[CODEMODE_DEFAULT_MARKER] === true)
|
|
140
|
+
return false;
|
|
141
|
+
const tools = Array.isArray(settings["defaultTools"])
|
|
142
|
+
? settings["defaultTools"].filter((t) => typeof t === "string")
|
|
143
|
+
: [];
|
|
144
|
+
if (!tools.some((t) => t.replace(/^[+-]/, "") === "codemode"))
|
|
145
|
+
settings["defaultTools"] = [...tools, "+codemode"];
|
|
146
|
+
settings[CODEMODE_DEFAULT_MARKER] = true;
|
|
147
|
+
return true;
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* pi 1.0 内置 llama.cpp 扩展(本地模型 provider + /llama),u1s1 走网关用不上,默认关一次。
|
|
151
|
+
* 用户在 extensions 里自己写过 builtin:llama.cpp 的任何开关就不动;标记写下后同样不再碰。
|
|
152
|
+
*/
|
|
153
|
+
export function applyLlamaDisabledDefault(settings) {
|
|
154
|
+
if (settings[LLAMA_DISABLED_MARKER] === true)
|
|
155
|
+
return false;
|
|
156
|
+
const entries = Array.isArray(settings["extensions"])
|
|
157
|
+
? settings["extensions"].filter((e) => typeof e === "string")
|
|
158
|
+
: [];
|
|
159
|
+
if (!entries.some((e) => e.replace(/^[+!-]/, "") === "builtin:llama.cpp")) {
|
|
160
|
+
settings["extensions"] = [...entries, "-builtin:llama.cpp"];
|
|
161
|
+
}
|
|
162
|
+
settings[LLAMA_DISABLED_MARKER] = true;
|
|
163
|
+
return true;
|
|
164
|
+
}
|
|
131
165
|
/** Defaults that don't overwrite values the user already set. */
|
|
132
166
|
export function ensureDefaultSettings(models = []) {
|
|
133
167
|
mkdirSync(agentDir, { recursive: true });
|
|
@@ -176,6 +210,15 @@ export function ensureDefaultSettings(models = []) {
|
|
|
176
210
|
settings["autoUpdate"] = true;
|
|
177
211
|
changed = true;
|
|
178
212
|
}
|
|
213
|
+
// pi 1.0 起默认全屏;u1s1 的精简 UI 与滚动习惯按常规模式调的,未选过的用户保持常规
|
|
214
|
+
if (!("tuiMode" in settings)) {
|
|
215
|
+
settings["tuiMode"] = "regular";
|
|
216
|
+
changed = true;
|
|
217
|
+
}
|
|
218
|
+
if (applyCodemodeDefault(settings))
|
|
219
|
+
changed = true;
|
|
220
|
+
if (applyLlamaDisabledDefault(settings))
|
|
221
|
+
changed = true;
|
|
179
222
|
if (applyModelThinkingDefaults(settings, models))
|
|
180
223
|
changed = true;
|
|
181
224
|
// ≤0.4.0 shipped branded themes and forced them as default; the files are
|
|
@@ -353,12 +396,19 @@ export function writeAnnouncementsExtension() {
|
|
|
353
396
|
`}\n`);
|
|
354
397
|
}
|
|
355
398
|
/**
|
|
356
|
-
* 生成「自定义 MCP 服务」扩展到 <agentDir>/extensions/u1s1-mcp.js
|
|
357
|
-
*
|
|
358
|
-
*
|
|
399
|
+
* 生成「自定义 MCP 服务」扩展到 <agentDir>/extensions/u1s1-mcp.js,只给没有内置 MCP 的
|
|
400
|
+
* 旧引擎用(Desktop App 的 pi-web-ui 还是 0.83):读 agent/mcp.json,把工具注册成
|
|
401
|
+
* mcp_<服务>_<工具>,逻辑在 mcp/extension.ts。pi ≥0.99 有 pi.registerMcpServer,说明内置
|
|
402
|
+
* MCP 会读同一份配置,扩展直接让位。投影前先把 ≤1.12 的 ~/.u1s1/mcp.json 迁过去。
|
|
359
403
|
* 子会话(spawn_subagent)里不挂 MCP 工具:避免每个子进程都拉起一份服务。
|
|
360
404
|
*/
|
|
361
405
|
export function writeMcpExtension() {
|
|
406
|
+
try {
|
|
407
|
+
migrateLegacyMcpConfig();
|
|
408
|
+
}
|
|
409
|
+
catch {
|
|
410
|
+
// 迁移失败不挡启动:旧文件还在,下次再试
|
|
411
|
+
}
|
|
362
412
|
const dir = join(agentDir, "extensions");
|
|
363
413
|
mkdirSync(dir, { recursive: true });
|
|
364
414
|
const url = new URL("./mcp/extension.js", import.meta.url).href;
|
|
@@ -366,6 +416,7 @@ export function writeMcpExtension() {
|
|
|
366
416
|
writeFileSync(join(dir, "u1s1-mcp.js"), `// 由 u1s1 每次启动自动生成,请勿手改\n` +
|
|
367
417
|
`export default async function (pi) {\n` +
|
|
368
418
|
` if (process.env.U1S1_IN_SUBAGENT === "1") return;\n` +
|
|
419
|
+
` if (typeof pi.registerMcpServer === "function") return;\n` +
|
|
369
420
|
` let registerMcpServers;\n` +
|
|
370
421
|
` try {\n` +
|
|
371
422
|
` registerMcpServers = (await import(${JSON.stringify(url)})).registerMcpServers;\n` +
|
package/dist/index.js
CHANGED
|
@@ -260,7 +260,7 @@ async function runAgent(cfg, args) {
|
|
|
260
260
|
writeAnnouncementsExtension();
|
|
261
261
|
// OpenRouter 应用归因:直连流量计入 u1s1 的公开排行
|
|
262
262
|
writeAttributionExtension();
|
|
263
|
-
// 自定义 MCP
|
|
263
|
+
// 自定义 MCP 服务:TUI 用 pi 内置 MCP 读 agent/mcp.json;这里迁旧配置,并给 Desktop 的旧引擎投影兜底扩展
|
|
264
264
|
writeMcpExtension();
|
|
265
265
|
ensureTmuxKeyboardProtocol();
|
|
266
266
|
// must be set before pi reads them (getAgentDir() reads at call time);
|
package/dist/mcp/command.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { type McpServerConfig } from "./config.js";
|
|
2
2
|
/**
|
|
3
|
-
* `u1s1 mcp` 子命令:管理自定义 MCP
|
|
4
|
-
* add
|
|
3
|
+
* `u1s1 mcp` 子命令:管理自定义 MCP 服务,写的是 pi 内置 MCP 读的 agent/mcp.json。
|
|
4
|
+
* add 时先连一次确认能用再落盘;对话里的连接、登录、停用交给 pi 的 /mcp。
|
|
5
5
|
*/
|
|
6
6
|
export declare function printMcpHelp(): void;
|
|
7
7
|
export declare class McpUsageError extends Error {
|
package/dist/mcp/command.js
CHANGED
|
@@ -4,11 +4,11 @@ import { join, resolve } from "node:path";
|
|
|
4
4
|
import { formatHomePath } from "../brand.js";
|
|
5
5
|
import { isEnglish, tr } from "../i18n.js";
|
|
6
6
|
import { McpClient } from "./client.js";
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
7
|
+
import { loadMcpConfigDetailed, loadMcpToolCache, maskSecret, MCP_SERVER_NAME_RE, mcpConfigPath, migrateLegacyMcpConfig, normalizeServerConfig, parseKeyValue, saveMcpToolCache, updateMcpServers, } from "./config.js";
|
|
8
|
+
import { piMcpToolName } from "./tools.js";
|
|
9
9
|
/**
|
|
10
|
-
* `u1s1 mcp` 子命令:管理自定义 MCP
|
|
11
|
-
* add
|
|
10
|
+
* `u1s1 mcp` 子命令:管理自定义 MCP 服务,写的是 pi 内置 MCP 读的 agent/mcp.json。
|
|
11
|
+
* add 时先连一次确认能用再落盘;对话里的连接、登录、停用交给 pi 的 /mcp。
|
|
12
12
|
*/
|
|
13
13
|
export function printMcpHelp() {
|
|
14
14
|
if (isEnglish()) {
|
|
@@ -27,7 +27,8 @@ export function printMcpHelp() {
|
|
|
27
27
|
console.log(" u1s1 mcp import [--dry-run] Import from Claude Code config (~/.claude.json, project .mcp.json)");
|
|
28
28
|
console.log("");
|
|
29
29
|
console.log(` Config is stored in ${formatHomePath(mcpConfigPath())} (same format as Claude Code's mcpServers; safe to edit by hand).`);
|
|
30
|
-
console.log(" It stays on this machine and is never uploaded. Tools are available from your next chat, named
|
|
30
|
+
console.log(" It stays on this machine and is never uploaded. Tools are available from your next chat, named mcp__<name>__<tool>.");
|
|
31
|
+
console.log(" In chat, /mcp shows server status and lets you sign in (OAuth), reconnect, or disable a server.");
|
|
31
32
|
console.log(" Command servers run locally with your permissions; only add sources you trust.");
|
|
32
33
|
console.log("");
|
|
33
34
|
return;
|
|
@@ -47,7 +48,8 @@ export function printMcpHelp() {
|
|
|
47
48
|
console.log(" u1s1 mcp import [--dry-run] 从 Claude Code 的配置(~/.claude.json、项目 .mcp.json)导入");
|
|
48
49
|
console.log("");
|
|
49
50
|
console.log(` 配置存在 ${formatHomePath(mcpConfigPath())}(与 Claude Code 的 mcpServers 同格式,可手改),`);
|
|
50
|
-
console.log(" 只在本机,不会上传。下次进对话工具即生效,名字形如
|
|
51
|
+
console.log(" 只在本机,不会上传。下次进对话工具即生效,名字形如 mcp__名称__工具。");
|
|
52
|
+
console.log(" 对话里输入 /mcp 可看连接状态,还能登录(OAuth)、重连、停用服务。");
|
|
51
53
|
console.log(" 命令型服务会以你的权限在本机运行,只添加可信来源。");
|
|
52
54
|
console.log("");
|
|
53
55
|
}
|
|
@@ -82,7 +84,7 @@ function printTools(name, tools) {
|
|
|
82
84
|
}
|
|
83
85
|
for (const t of tools) {
|
|
84
86
|
const desc = t.description.replace(/\s+/g, " ").trim();
|
|
85
|
-
console.log(` · ${
|
|
87
|
+
console.log(` · ${piMcpToolName(name, t.name)}${desc ? ` ${desc.length > 80 ? `${desc.slice(0, 79)}…` : desc}` : ""}`);
|
|
86
88
|
}
|
|
87
89
|
}
|
|
88
90
|
function cacheTools(name, tools, serverName) {
|
|
@@ -176,8 +178,8 @@ export function parseAddArgs(args) {
|
|
|
176
178
|
}
|
|
177
179
|
async function addCommand(args) {
|
|
178
180
|
const { name, server, force } = parseAddArgs(args);
|
|
179
|
-
const config =
|
|
180
|
-
const existed = Boolean(config.mcpServers[name]);
|
|
181
|
+
const { config, disabled } = loadMcpConfigDetailed();
|
|
182
|
+
const existed = Boolean(config.mcpServers[name]) || disabled.includes(name);
|
|
181
183
|
console.log(tr(`${existed ? "更新" : "添加"} MCP 服务 ${name}:${describeServer(server)}`, `${existed ? "Updating" : "Adding"} MCP server ${name}: ${describeServer(server)}`));
|
|
182
184
|
console.log(tr("连接中…", "Connecting…"));
|
|
183
185
|
let tools = [];
|
|
@@ -190,12 +192,15 @@ async function addCommand(args) {
|
|
|
190
192
|
catch (err) {
|
|
191
193
|
const message = err instanceof Error ? err.message : String(err);
|
|
192
194
|
if (!force) {
|
|
193
|
-
|
|
195
|
+
// 无 headers 的 HTTP 服务被拒,多半是要 OAuth 登录:先存下,进对话在 /mcp 里登
|
|
196
|
+
const oauthHint = server.type === "http" && !Object.keys(server.headers).length && /HTTP 40[13]/.test(message)
|
|
197
|
+
? tr("\n 如果这个服务用浏览器登录(OAuth),加 --force 保存,进对话后输入 /mcp 选它登录", "\n If this server signs in through the browser (OAuth), add --force to save it, then sign in from /mcp in chat")
|
|
198
|
+
: "";
|
|
199
|
+
fail(tr(`连接失败,未保存:${message}\n 确认命令/地址和密钥无误后重试;想先保存加 --force${oauthHint}`, `Connection failed; nothing saved: ${message}\n Check the command/URL and keys, then retry. To save anyway, add --force${oauthHint}`));
|
|
194
200
|
}
|
|
195
201
|
console.error(tr(`⚠ 连接失败:${message}\n 已按 --force 保存,进对话前请用 u1s1 mcp test ${name} 确认可用`, `⚠ Connection failed: ${message}\n Saved anyway (--force). Run u1s1 mcp test ${name} before using it in chat`));
|
|
196
202
|
}
|
|
197
|
-
|
|
198
|
-
saveMcpConfig(config);
|
|
203
|
+
updateMcpServers({ [name]: server });
|
|
199
204
|
if (probed) {
|
|
200
205
|
cacheTools(name, tools, serverName);
|
|
201
206
|
console.log(tr(`✓ 已连接${serverName ? ` ${serverName}` : ""},提供 ${tools.length} 个工具:`, `✓ Connected${serverName ? ` to ${serverName}` : ""}, ${tools.length} tool(s):`));
|
|
@@ -210,32 +215,41 @@ async function addCommand(args) {
|
|
|
210
215
|
}
|
|
211
216
|
}
|
|
212
217
|
function listCommand() {
|
|
213
|
-
const config =
|
|
218
|
+
const { config, invalid, disabled } = loadMcpConfigDetailed();
|
|
214
219
|
const names = Object.keys(config.mcpServers);
|
|
215
|
-
if (names.length === 0) {
|
|
220
|
+
if (names.length === 0 && disabled.length === 0 && invalid.length === 0) {
|
|
216
221
|
console.log(tr("还没有配置 MCP 服务。用 u1s1 mcp add 添加,或 u1s1 mcp import 从 Claude Code 导入。", "No MCP servers configured yet. Add one with u1s1 mcp add, or import from Claude Code with u1s1 mcp import."));
|
|
217
222
|
return;
|
|
218
223
|
}
|
|
219
224
|
const cache = loadMcpToolCache();
|
|
220
|
-
|
|
225
|
+
const total = names.length + disabled.length + invalid.length;
|
|
226
|
+
console.log(tr(`已配置 ${total} 个 MCP 服务(${formatHomePath(mcpConfigPath())}):`, `${total} MCP server(s) configured (${formatHomePath(mcpConfigPath())}):`));
|
|
221
227
|
for (const name of names) {
|
|
222
228
|
const server = config.mcpServers[name];
|
|
223
229
|
const cached = cache.servers[name];
|
|
224
230
|
console.log("");
|
|
225
231
|
console.log(`${name} [${server.type}] ${describeServer(server)}`);
|
|
226
232
|
if (!cached) {
|
|
227
|
-
console.log(tr(
|
|
233
|
+
console.log(tr(` 还没检测过工具清单,u1s1 mcp test ${name} 看看`, ` Tool list not checked yet; run u1s1 mcp test ${name}`));
|
|
228
234
|
continue;
|
|
229
235
|
}
|
|
230
236
|
const fetched = cached.fetchedAt.slice(0, 16).replace("T", " ");
|
|
231
|
-
console.log(tr(` ${cached.tools.length} 个工具(${fetched}
|
|
237
|
+
console.log(tr(` ${cached.tools.length} 个工具(${fetched} 检测):`, ` ${cached.tools.length} tool(s) (checked ${fetched}):`));
|
|
232
238
|
printTools(name, cached.tools);
|
|
233
239
|
}
|
|
240
|
+
for (const name of disabled) {
|
|
241
|
+
console.log("");
|
|
242
|
+
console.log(tr(`${name} [已停用,对话里 /mcp 可重新启用]`, `${name} [disabled; re-enable it from /mcp in chat]`));
|
|
243
|
+
}
|
|
244
|
+
for (const name of invalid) {
|
|
245
|
+
console.log("");
|
|
246
|
+
console.log(tr(`${name} [配置无效,请检查这一条]`, `${name} [invalid entry; please check it]`));
|
|
247
|
+
}
|
|
234
248
|
}
|
|
235
249
|
async function testCommand(name) {
|
|
236
250
|
if (!name)
|
|
237
251
|
fail(tr("用法:u1s1 mcp test 名称", "Usage: u1s1 mcp test <name>"));
|
|
238
|
-
const config =
|
|
252
|
+
const { config } = loadMcpConfigDetailed();
|
|
239
253
|
const server = config.mcpServers[name];
|
|
240
254
|
if (!server)
|
|
241
255
|
fail(tr(`没有叫 ${name} 的 MCP 服务,u1s1 mcp list 看看已有的`, `No MCP server named ${name}; run u1s1 mcp list to see configured servers`));
|
|
@@ -258,11 +272,10 @@ async function testCommand(name) {
|
|
|
258
272
|
function removeCommand(name) {
|
|
259
273
|
if (!name)
|
|
260
274
|
fail(tr("用法:u1s1 mcp remove 名称", "Usage: u1s1 mcp remove <name>"));
|
|
261
|
-
const config =
|
|
262
|
-
if (!config.mcpServers[name])
|
|
275
|
+
const { config, disabled } = loadMcpConfigDetailed();
|
|
276
|
+
if (!config.mcpServers[name] && !disabled.includes(name))
|
|
263
277
|
fail(tr(`没有叫 ${name} 的 MCP 服务`, `No MCP server named ${name}`));
|
|
264
|
-
|
|
265
|
-
saveMcpConfig(config);
|
|
278
|
+
updateMcpServers({ [name]: undefined });
|
|
266
279
|
dropCache(name);
|
|
267
280
|
console.log(tr(`✓ 已删除 ${name}`, `✓ Removed ${name}`));
|
|
268
281
|
}
|
|
@@ -323,19 +336,17 @@ async function importCommand(args) {
|
|
|
323
336
|
console.log(tr("没有在 Claude Code 的配置里找到可导入的 MCP 服务。", "No importable MCP servers found in Claude Code's config."));
|
|
324
337
|
return;
|
|
325
338
|
}
|
|
326
|
-
const config =
|
|
339
|
+
const { config, disabled } = loadMcpConfigDetailed();
|
|
327
340
|
console.log(tr(`找到 ${candidates.length} 个 MCP 服务:`, `Found ${candidates.length} MCP server(s):`));
|
|
328
341
|
for (const c of candidates) {
|
|
329
|
-
const tag = config.mcpServers[c.name] ? tr("(已存在,将覆盖)", "(exists, will be overwritten)") : "";
|
|
342
|
+
const tag = config.mcpServers[c.name] || disabled.includes(c.name) ? tr("(已存在,将覆盖)", "(exists, will be overwritten)") : "";
|
|
330
343
|
console.log(` ${c.name} ${describeServer(c.server)} ← ${c.source} ${tag}`);
|
|
331
344
|
}
|
|
332
345
|
if (dryRun) {
|
|
333
346
|
console.log(tr("--dry-run:未写盘", "--dry-run: nothing written"));
|
|
334
347
|
return;
|
|
335
348
|
}
|
|
336
|
-
|
|
337
|
-
config.mcpServers[c.name] = c.server;
|
|
338
|
-
saveMcpConfig(config);
|
|
349
|
+
updateMcpServers(Object.fromEntries(candidates.map((c) => [c.name, c.server])));
|
|
339
350
|
console.log(tr(`✓ 已保存到 ${formatHomePath(mcpConfigPath())}`, `✓ Saved to ${formatHomePath(mcpConfigPath())}`));
|
|
340
351
|
let failed = 0;
|
|
341
352
|
for (const c of candidates) {
|
|
@@ -358,6 +369,7 @@ async function importCommand(args) {
|
|
|
358
369
|
export async function mcpCommand(args) {
|
|
359
370
|
const sub = args[0];
|
|
360
371
|
try {
|
|
372
|
+
migrateLegacyMcpConfig();
|
|
361
373
|
if (!sub || sub === "help" || sub === "-h" || sub === "--help") {
|
|
362
374
|
printMcpHelp();
|
|
363
375
|
return;
|
package/dist/mcp/config.d.ts
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* 自定义 MCP 服务配置:~/.u1s1/mcp.json
|
|
3
|
-
*
|
|
4
|
-
* writePrivateFile(0600),并且永远不上传、不进遥测。
|
|
2
|
+
* 自定义 MCP 服务配置:~/.u1s1/agent/mcp.json,即 pi 内置 MCP 读的用户级配置,
|
|
3
|
+
* 形状与 Claude Code 的 mcpServers 一致,方便手改或从别的工具照抄。里面可能有
|
|
4
|
+
* API Key(env / headers),所以走 writePrivateFile(0600),并且永远不上传、不进遥测。
|
|
5
|
+
* pi 的 /mcp 会往条目里写 exposure、enabled 等字段,这里改写时逐条保留原样。
|
|
5
6
|
*/
|
|
6
7
|
export interface McpStdioServer {
|
|
7
8
|
type: "stdio";
|
|
@@ -32,9 +33,11 @@ export interface McpToolCache {
|
|
|
32
33
|
version: 1;
|
|
33
34
|
servers: Record<string, McpCachedServer>;
|
|
34
35
|
}
|
|
35
|
-
/**
|
|
36
|
+
/** u1s1 mcp add 收的服务名:比 pi 的规则更严,工具名前缀短且全小写。 */
|
|
36
37
|
export declare const MCP_SERVER_NAME_RE: RegExp;
|
|
37
38
|
export declare function mcpConfigPath(): string;
|
|
39
|
+
/** ≤1.12 的配置位置,启动时迁到 mcpConfigPath()。 */
|
|
40
|
+
export declare function legacyMcpConfigPath(): string;
|
|
38
41
|
export declare function mcpCachePath(): string;
|
|
39
42
|
/**
|
|
40
43
|
* 把一条 mcpServers 记录规整成内部形状。兼容 Claude Code 的写法:
|
|
@@ -42,13 +45,26 @@ export declare function mcpCachePath(): string;
|
|
|
42
45
|
* SSE 传输)不支持,返回 undefined 让调用方提示。
|
|
43
46
|
*/
|
|
44
47
|
export declare function normalizeServerConfig(raw: unknown): McpServerConfig | undefined;
|
|
48
|
+
/** enabled:false(在 /mcp 里停用)的服务不进 config,名字放进 disabled。 */
|
|
45
49
|
export declare function parseMcpConfig(raw: unknown): {
|
|
46
50
|
config: McpConfig;
|
|
47
51
|
invalid: string[];
|
|
52
|
+
disabled: string[];
|
|
48
53
|
};
|
|
49
54
|
export declare function loadMcpConfig(path?: string): McpConfig;
|
|
50
|
-
|
|
51
|
-
|
|
55
|
+
export declare function loadMcpConfigDetailed(path?: string): ReturnType<typeof parseMcpConfig>;
|
|
56
|
+
/**
|
|
57
|
+
* 改写 mcpServers:value 为 undefined 即删除。其余顶层字段与没动到的条目原样保留;
|
|
58
|
+
* 覆盖已有条目时只换传输字段(命令/地址/env/headers),description、exposure 等留着。
|
|
59
|
+
*/
|
|
60
|
+
export declare function updateMcpServers(changes: Record<string, McpServerConfig | undefined>, path?: string): void;
|
|
61
|
+
/**
|
|
62
|
+
* 把 ≤1.12 的 ~/.u1s1/mcp.json 并进 pi 读的 agent/mcp.json(同名以新文件为准),
|
|
63
|
+
* 旧文件改名留底。条目原样搬(补上 direct,保持原来工具直接给模型),格式本来就一致;
|
|
64
|
+
* sse 等 pi 不支持的条目由 pi 报错跳过。
|
|
65
|
+
* 返回搬过去的服务名;没有旧文件时什么也不做。
|
|
66
|
+
*/
|
|
67
|
+
export declare function migrateLegacyMcpConfig(legacyPath?: string, path?: string): string[];
|
|
52
68
|
export declare function loadMcpToolCache(path?: string): McpToolCache;
|
|
53
69
|
export declare function saveMcpToolCache(cache: McpToolCache, path?: string): void;
|
|
54
70
|
/** 工具列表是否变化(名称 + 参数结构),用来决定要不要提示重启。 */
|
package/dist/mcp/config.js
CHANGED
|
Binary file
|
package/dist/mcp/tools.d.ts
CHANGED
|
@@ -9,6 +9,8 @@ import type { McpServerConfig, McpToolInfo } from "./config.js";
|
|
|
9
9
|
*/
|
|
10
10
|
export declare const MCP_MAX_TEXT_CHARS = 100000;
|
|
11
11
|
export declare function mcpToolName(server: string, tool: string): string;
|
|
12
|
+
/** pi 内置 MCP 给模型的工具名(mcp__<服务>__<工具>),u1s1 mcp list/test 展示用。 */
|
|
13
|
+
export declare function piMcpToolName(server: string, tool: string): string;
|
|
12
14
|
export declare function truncateMcpText(text: string, max?: number): string;
|
|
13
15
|
/** 模型 API 只认 object 顶层;$schema 之类元字段去掉,缺 properties 补空 */
|
|
14
16
|
export declare function toParameterSchema(inputSchema: Record<string, unknown>): TSchema;
|
package/dist/mcp/tools.js
CHANGED
|
@@ -13,6 +13,10 @@ export function mcpToolName(server, tool) {
|
|
|
13
13
|
const clean = tool.replace(/[^a-zA-Z0-9_-]+/g, "_").replace(/^_+|_+$/g, "") || "tool";
|
|
14
14
|
return `mcp_${server}_${clean}`.slice(0, MODEL_TOOL_NAME_MAX);
|
|
15
15
|
}
|
|
16
|
+
/** pi 内置 MCP 给模型的工具名(mcp__<服务>__<工具>),u1s1 mcp list/test 展示用。 */
|
|
17
|
+
export function piMcpToolName(server, tool) {
|
|
18
|
+
return `mcp__${server}__${tool}`.replace(/[^A-Za-z0-9_]/g, "_");
|
|
19
|
+
}
|
|
16
20
|
export function truncateMcpText(text, max = MCP_MAX_TEXT_CHARS) {
|
|
17
21
|
if (text.length <= max)
|
|
18
22
|
return text;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "u1s1-cli",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.13.0",
|
|
4
4
|
"description": "u1s1 — 有一说一,最省心的 AI 编程搭子。终端里用中文说需求,AI 帮你读文件、改代码、跑命令。",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -42,8 +42,8 @@
|
|
|
42
42
|
"license": "MIT",
|
|
43
43
|
"homepage": "https://u1s1app.com",
|
|
44
44
|
"dependencies": {
|
|
45
|
-
"@earendil-works/pi-coding-agent": "0.
|
|
46
|
-
"@earendil-works/pi-tui": "0.
|
|
45
|
+
"@earendil-works/pi-coding-agent": "1.0.0",
|
|
46
|
+
"@earendil-works/pi-tui": "1.0.0",
|
|
47
47
|
"typebox": "1.3.7"
|
|
48
48
|
},
|
|
49
49
|
"devDependencies": {
|
package/scripts/patch-pi.js
CHANGED
|
@@ -193,7 +193,7 @@ const HOTKEYS_REPLACEMENTS = [
|
|
|
193
193
|
["| Copy selection or last assistant message |", '| ${process.env.U1S1_LANG === "en" ? "Copy selection or last assistant message" : "复制选中内容或最近一条回复"} |'],
|
|
194
194
|
["| Queue follow-up message |", '| ${process.env.U1S1_LANG === "en" ? "Queue follow-up message" : "把消息排到当前回复之后"} |'],
|
|
195
195
|
["| Restore queued messages |", '| ${process.env.U1S1_LANG === "en" ? "Restore queued messages" : "取回排队中的消息"} |'],
|
|
196
|
-
["| Paste
|
|
196
|
+
["| Paste files on macOS, images, or text from clipboard |", '| ${process.env.U1S1_LANG === "en" ? "Paste files on macOS, images, or text from clipboard" : "从剪贴板粘贴图片、文本或(macOS)文件"} |'],
|
|
197
197
|
["| Slash commands |", '| ${process.env.U1S1_LANG === "en" ? "Slash commands (/help for common actions)" : "斜杠命令(/help 看常用操作)"} |'],
|
|
198
198
|
["| Run bash command |", '| ${process.env.U1S1_LANG === "en" ? "Run bash command" : "运行终端命令"} |'],
|
|
199
199
|
["| Run bash command (excluded from context) |", '| ${process.env.U1S1_LANG === "en" ? "Run bash command (excluded from context)" : "运行终端命令(结果不进上下文)"} |'],
|
|
@@ -249,8 +249,9 @@ const SETTINGS_REPLACEMENTS = [
|
|
|
249
249
|
['ask: "Ask",', 'ask: process.env.U1S1_LANG === "en" ? "Ask" : "每次询问",'],
|
|
250
250
|
['always: "Always trust",', 'always: process.env.U1S1_LANG === "en" ? "Always trust" : "始终信任",'],
|
|
251
251
|
['never: "Never trust",', 'never: process.env.U1S1_LANG === "en" ? "Never trust" : "从不信任",'],
|
|
252
|
-
['new SelectSubmenu("Theme", "Select a theme, or choose
|
|
253
|
-
['
|
|
252
|
+
['new SelectSubmenu("Theme", "Select a theme, or choose automatic to follow terminal appearance."', 'new SelectSubmenu(process.env.U1S1_LANG === "en" ? "Theme" : "主题", process.env.U1S1_LANG === "en" ? "Select a theme, or choose automatic to follow terminal appearance." : "选择一个主题,或选「自动」跟随终端明暗。"'],
|
|
253
|
+
['{ description: "Theme created from your terminal\'s colors" }', '{ description: process.env.U1S1_LANG === "en" ? "Theme created from your terminal\'s colors" : "按终端自身配色生成的主题" }'],
|
|
254
|
+
['label: " automatic",', 'label: process.env.U1S1_LANG === "en" ? " automatic" : " 自动",'],
|
|
254
255
|
['description: "Use separate themes for light and dark terminal appearance",', 'description: process.env.U1S1_LANG === "en" ? "Use separate themes for light and dark terminal appearance" : "终端明暗各用一套主题",'],
|
|
255
256
|
['theme.fg("accent", "Automatic Theme")', 'theme.fg("accent", process.env.U1S1_LANG === "en" ? "Automatic Theme" : "自动主题")'],
|
|
256
257
|
['theme.fg("muted", "Choose themes for terminal light and dark appearance.")', 'theme.fg("muted", process.env.U1S1_LANG === "en" ? "Choose themes for terminal light and dark appearance." : "分别为终端的浅色/深色外观选主题。")'],
|
|
@@ -284,7 +285,7 @@ const SETTINGS_REPLACEMENTS = [
|
|
|
284
285
|
['label: "Collapse changelog",', 'label: process.env.U1S1_LANG === "en" ? "Collapse changelog" : "折叠更新日志",'],
|
|
285
286
|
['description: "Show condensed changelog after updates",', 'description: process.env.U1S1_LANG === "en" ? "Show condensed changelog after updates" : "更新后只显示精简的更新日志",'],
|
|
286
287
|
['label: "Quiet startup",', 'label: process.env.U1S1_LANG === "en" ? "Quiet startup" : "安静启动",'],
|
|
287
|
-
['description: "Disable verbose printing at startup",', 'description: process.env.U1S1_LANG === "en" ? "Disable verbose printing at startup" : "启动时不打印冗长信息",'],
|
|
288
|
+
['description: "Disable verbose printing at startup (header: keep only the startup header)",', 'description: process.env.U1S1_LANG === "en" ? "Disable verbose printing at startup (header: keep only the startup header)" : "启动时不打印冗长信息(header:只保留启动标题)",'],
|
|
288
289
|
['label: "Install telemetry",', 'label: process.env.U1S1_LANG === "en" ? "Install telemetry" : "安装统计",'],
|
|
289
290
|
['description: "Send an anonymous version/update ping after changelog-detected updates",', 'description: process.env.U1S1_LANG === "en" ? "Send an anonymous version/update ping after changelog-detected updates" : "更新后匿名上报一次版本信息",'],
|
|
290
291
|
['label: "Default project trust",', 'label: process.env.U1S1_LANG === "en" ? "Default project trust" : "默认项目信任",'],
|
|
@@ -305,13 +306,15 @@ const SETTINGS_REPLACEMENTS = [
|
|
|
305
306
|
['label: " (clear override)",', 'label: process.env.U1S1_LANG === "en" ? " (clear override)" : " (清除覆盖)",'],
|
|
306
307
|
['description: `Revert to global default (${config.thinkingLevel})`,', 'description: process.env.U1S1_LANG === "en" ? `Revert to global default (${config.thinkingLevel})` : `恢复为全局默认(${config.thinkingLevel})`,'],
|
|
307
308
|
['label: "TUI mode",', 'label: process.env.U1S1_LANG === "en" ? "TUI mode" : "界面模式",'],
|
|
308
|
-
['description: "Interface layout;
|
|
309
|
+
['description: "Interface layout; regular mode uses the terminal\'s normal scrollback",', 'description: process.env.U1S1_LANG === "en" ? "Interface layout; regular mode uses the terminal\'s normal scrollback" : "界面布局;regular 模式保留终端原生的滚动记录",'],
|
|
309
310
|
['label: "Fullscreen exit output",', 'label: process.env.U1S1_LANG === "en" ? "Fullscreen exit output" : "退出全屏时的输出",'],
|
|
310
311
|
['description: "Print the transcript or only a session resume hint when exiting fullscreen mode",', 'description: process.env.U1S1_LANG === "en" ? "Print the transcript or only a session resume hint when exiting fullscreen mode" : "退出全屏时打印完整对话,还是只留一条恢复会话提示",'],
|
|
311
312
|
['label: "Fullscreen scrollbar",', 'label: process.env.U1S1_LANG === "en" ? "Fullscreen scrollbar" : "全屏滚动条",'],
|
|
312
313
|
['description: "Scrollbar behavior in fullscreen mode; has no effect in regular mode",', 'description: process.env.U1S1_LANG === "en" ? "Scrollbar behavior in fullscreen mode; has no effect in regular mode" : "全屏模式下的滚动条行为;普通模式无效",'],
|
|
313
314
|
['label: "Fullscreen copy on select",', 'label: process.env.U1S1_LANG === "en" ? "Fullscreen copy on select" : "全屏选中即复制",'],
|
|
314
315
|
['description: "Automatically copy selected text in fullscreen mode; disable to copy selections with Ctrl+X",', 'description: process.env.U1S1_LANG === "en" ? "Automatically copy selected text in fullscreen mode; disable to copy selections with Ctrl+X" : "全屏模式下选中文字自动复制;关闭后用 Ctrl+X 复制",'],
|
|
316
|
+
['label: "Fullscreen wheel scrolling",', 'label: process.env.U1S1_LANG === "en" ? "Fullscreen wheel scrolling" : "全屏滚轮速度",'],
|
|
317
|
+
['description: "Lines per mouse-wheel event in fullscreen mode; \'auto\' speeds up fast wheel spins where the terminal does not",', 'description: process.env.U1S1_LANG === "en" ? "Lines per mouse-wheel event in fullscreen mode; \'auto\' speeds up fast wheel spins where the terminal does not" : "全屏模式下滚轮每格滚动的行数;auto 在快速滚动时自动加速",'],
|
|
315
318
|
['label: "Theme",', 'label: process.env.U1S1_LANG === "en" ? "Theme" : "主题",'],
|
|
316
319
|
['description: "Color theme for the interface",', 'description: process.env.U1S1_LANG === "en" ? "Color theme for the interface" : "界面配色主题",'],
|
|
317
320
|
];
|
|
@@ -325,6 +328,103 @@ const BUG_REPORT_REPLACEMENTS = [
|
|
|
325
328
|
["`If this looks like a ${APP_NAME} bug, /bug sends a report to the developers.`", 'process.env.U1S1_LANG === "en" ? "If this looks like a u1s1 bug, report it with /feedback." : "如果这看起来是 u1s1 自身的问题,可以用 /feedback 反馈。"'],
|
|
326
329
|
];
|
|
327
330
|
|
|
331
|
+
// /login 去掉 Pi 自家渠道(dist/modes/interactive/interactive-mode.js):
|
|
332
|
+
// Radius(Pi 开发方的付费服务,登录后还会往 mcp.json 写它的 MCP)和 OpenAI 的「Sign in with ChatGPT」。
|
|
333
|
+
// u1s1 用户在 /login 里点进去只会配上与 u1s1 账号无关的第三方渠道;菜单、/login radius、/login openai 都走这个列表。
|
|
334
|
+
const LOGIN_PROVIDER_REPLACEMENTS = [
|
|
335
|
+
[
|
|
336
|
+
"const authStatus = this.session.modelRuntime.getProviderAuthStatus(provider.id);",
|
|
337
|
+
'if (provider.id === "radius") continue;\n const authStatus = this.session.modelRuntime.getProviderAuthStatus(provider.id);',
|
|
338
|
+
],
|
|
339
|
+
['if ((!authType || authType === "oauth") && provider.auth.oauth) {', 'if ((!authType || authType === "oauth") && provider.auth.oauth && provider.id !== "openai") {'],
|
|
340
|
+
];
|
|
341
|
+
|
|
342
|
+
// Pi 0.99 起内置的 MCP 扩展(dist/extensions/mcp/):/mcp 管理面板、登录、状态与提示。
|
|
343
|
+
// 只换给人看的文案;mcp_servers 系统提示段、工具描述等给模型看的保持英文。
|
|
344
|
+
// "Sign-in cancelled." 既是返回值又被 === 比较,两处必须一起换。
|
|
345
|
+
const MCP_TEXT_REPLACEMENTS = [
|
|
346
|
+
['codemode: "called from codemode scripts, which find them with searchTools()",', 'codemode: process.env.U1S1_LANG === "en" ? "called from codemode scripts, which find them with searchTools()" : "由 codemode 脚本调用(脚本用 searchTools() 查找)",'],
|
|
347
|
+
['deferred: "not declared until tool_search loads them, then called directly; no codemode needed",', 'deferred: process.env.U1S1_LANG === "en" ? "not declared until tool_search loads them, then called directly; no codemode needed" : "先不告诉模型,tool_search 找到后再直接调用;不需要 codemode",'],
|
|
348
|
+
['direct: "declared to the model like built-in tools",', 'direct: process.env.U1S1_LANG === "en" ? "declared to the model like built-in tools" : "像内置工具一样直接提供给模型",'],
|
|
349
|
+
['return "disabled";', 'return process.env.U1S1_LANG === "en" ? "disabled" : "已停用";'],
|
|
350
|
+
['return "starting";', 'return process.env.U1S1_LANG === "en" ? "starting" : "启动中";'],
|
|
351
|
+
['return "needs sign-in";', 'return process.env.U1S1_LANG === "en" ? "needs sign-in" : "需要登录";'],
|
|
352
|
+
['return withError ? `failed: ${firstLine(connection.error ?? "unknown error")}` : "failed";', 'return withError ? (process.env.U1S1_LANG === "en" ? `failed: ${firstLine(connection.error ?? "unknown error")}` : `连接失败:${firstLine(connection.error ?? "未知错误")}`) : (process.env.U1S1_LANG === "en" ? "failed" : "连接失败");'],
|
|
353
|
+
['const resourceCount = count > 0 ? ` · ${count} resource${count === 1 ? "" : "s"}` : "";', 'const resourceCount = count > 0 ? (process.env.U1S1_LANG === "en" ? ` · ${count} resource${count === 1 ? "" : "s"}` : ` · ${count} 个资源`) : "";'],
|
|
354
|
+
['return `connected · ${tools.length} tool${tools.length === 1 ? "" : "s"}${resourceCount}`;', 'return process.env.U1S1_LANG === "en" ? `connected · ${tools.length} tool${tools.length === 1 ? "" : "s"}${resourceCount}` : `已连接 · ${tools.length} 个工具${resourceCount}`;'],
|
|
355
|
+
['return "connecting…";', 'return process.env.U1S1_LANG === "en" ? "connecting…" : "连接中…";'],
|
|
356
|
+
['const MCP_USAGE = "Usage: /mcp, /mcp login [server], /mcp logout [server], /mcp reconnect [server]";', 'const MCP_USAGE = process.env.U1S1_LANG === "en" ? "Usage: /mcp, /mcp login [server], /mcp logout [server], /mcp reconnect [server]" : "用法:/mcp、/mcp login [服务名]、/mcp logout [服务名]、/mcp reconnect [服务名]";'],
|
|
357
|
+
['ctx.ui.notify(`MCP tools are only reachable from the codemode or tool_search tool, but neither is active${reason}; they cannot be called.`, "warning");', 'ctx.ui.notify(process.env.U1S1_LANG === "en" ? `MCP tools are only reachable from the codemode or tool_search tool, but neither is active${reason}; they cannot be called.` : `MCP 工具只能经 codemode 或 tool_search 调用,但两者都没开启${reason},暂时用不了。`, "warning");'],
|
|
358
|
+
{ from: '`config: ${error}`', to: '(process.env.U1S1_LANG === "en" ? `config: ${error}` : `配置错误:${error}`)', expected: 2 },
|
|
359
|
+
['ctx.ui.notify(`MCP servers need attention:\\n${lines.map((line) => ` ${line}`).join("\\n")}\\nRun /mcp to fix.`, "warning");', 'ctx.ui.notify(process.env.U1S1_LANG === "en" ? `MCP servers need attention:\\n${lines.map((line) => ` ${line}`).join("\\n")}\\nRun /mcp to fix.` : `MCP 服务需要处理:\\n${lines.map((line) => ` ${line}`).join("\\n")}\\n输入 /mcp 处理。`, "warning");'],
|
|
360
|
+
['return `Could not update ${server.entry.source}: ${errorMessage(error)}`;', 'return process.env.U1S1_LANG === "en" ? `Could not update ${server.entry.source}: ${errorMessage(error)}` : `无法写入 ${server.entry.source}:${errorMessage(error)}`;'],
|
|
361
|
+
['return `MCP server "${server.entry.name}" does not use OAuth.`;', 'return process.env.U1S1_LANG === "en" ? `MCP server "${server.entry.name}" does not use OAuth.` : `MCP 服务「${server.entry.name}」不用 OAuth 登录。`;'],
|
|
362
|
+
['return "Sign-in cancelled.";', 'return process.env.U1S1_LANG === "en" ? "Sign-in cancelled." : "已取消登录。";'],
|
|
363
|
+
['failure === "Sign-in cancelled." ? "info" : "error"', 'failure === (process.env.U1S1_LANG === "en" ? "Sign-in cancelled." : "已取消登录。") ? "info" : "error"'],
|
|
364
|
+
['return `Sign-in failed: ${errorMessage(error)}`;', 'return process.env.U1S1_LANG === "en" ? `Sign-in failed: ${errorMessage(error)}` : `登录失败:${errorMessage(error)}`;'],
|
|
365
|
+
['return `Signed in, but ${errorMessage(error)}`;', 'return process.env.U1S1_LANG === "en" ? `Signed in, but ${errorMessage(error)}` : `已登录,但连接失败:${errorMessage(error)}`;'],
|
|
366
|
+
['return `MCP server "${server.entry.name}" is disabled.`;', 'return process.env.U1S1_LANG === "en" ? `MCP server "${server.entry.name}" is disabled.` : `MCP 服务「${server.entry.name}」已停用。`;'],
|
|
367
|
+
{ from: '`overridden: ${line}`', to: '(process.env.U1S1_LANG === "en" ? `overridden: ${line}` : `被覆盖:${line}`)', expected: 2 },
|
|
368
|
+
// 面板脚注经 keyHint 走按键脚注的对照表;close/select/save 已在表里,这两个表里没有,就地换
|
|
369
|
+
['confirmLabel: "manage",', 'confirmLabel: process.env.U1S1_LANG === "en" ? "manage" : "管理",'],
|
|
370
|
+
{ from: 'Label: "back",', to: 'Label: process.env.U1S1_LANG === "en" ? "back" : "返回",', expected: 5 },
|
|
371
|
+
['title: "MCP servers",', 'title: process.env.U1S1_LANG === "en" ? "MCP servers" : "MCP 服务",'],
|
|
372
|
+
// 空列表引导到 u1s1 mcp add(CLI 命令),英文同义
|
|
373
|
+
{ from: '`No MCP servers configured. Add them to ${resolve(getAgentDir(), "mcp.json")} or .pi/mcp.json.`', to: '(process.env.U1S1_LANG === "en" ? `No MCP servers configured. Add one with u1s1 mcp add, or edit ${resolve(getAgentDir(), "mcp.json")}.` : `还没有配置 MCP 服务。用 u1s1 mcp add 添加,或直接编辑 ${resolve(getAgentDir(), "mcp.json")}。`)', expected: 2 },
|
|
374
|
+
['empty: "This server is no longer configured.",', 'empty: process.env.U1S1_LANG === "en" ? "This server is no longer configured." : "这个服务已不在配置里了。",'],
|
|
375
|
+
['? "for this session"', '? (process.env.U1S1_LANG === "en" ? "for this session" : "仅本次会话")'],
|
|
376
|
+
['? `saved to the ${entry.scope} mcp.json`', '? (process.env.U1S1_LANG === "en" ? `saved to the ${entry.scope} mcp.json` : `保存到 mcp.json(${entry.scope})`)'],
|
|
377
|
+
[': "saved to mcp.json";', ': (process.env.U1S1_LANG === "en" ? "saved to mcp.json" : "保存到 mcp.json");'],
|
|
378
|
+
['label: "Enable", description: saved', 'label: process.env.U1S1_LANG === "en" ? "Enable" : "启用", description: saved'],
|
|
379
|
+
['label: "Sign in", description: "opens the browser"', 'label: process.env.U1S1_LANG === "en" ? "Sign in" : "登录", description: process.env.U1S1_LANG === "en" ? "opens the browser" : "会打开浏览器"'],
|
|
380
|
+
['label: "Tools", description: `${connection.tools.length} offered`', 'label: process.env.U1S1_LANG === "en" ? "Tools" : "工具", description: process.env.U1S1_LANG === "en" ? `${connection.tools.length} offered` : `共 ${connection.tools.length} 个`'],
|
|
381
|
+
['label: "Reconnect" }', 'label: process.env.U1S1_LANG === "en" ? "Reconnect" : "重新连接" }'],
|
|
382
|
+
['label: "Sign out", description: "deletes the stored credentials"', 'label: process.env.U1S1_LANG === "en" ? "Sign out" : "退出登录", description: process.env.U1S1_LANG === "en" ? "deletes the stored credentials" : "删除已保存的登录凭据"'],
|
|
383
|
+
['label: "Exposure", description: exposureOf(entry)', 'label: process.env.U1S1_LANG === "en" ? "Exposure" : "暴露方式", description: exposureOf(entry)'],
|
|
384
|
+
['label: "Disable", description: saved', 'label: process.env.U1S1_LANG === "en" ? "Disable" : "停用", description: saved'],
|
|
385
|
+
['`State: ${describeState(server, false)}`', '(process.env.U1S1_LANG === "en" ? `State: ${describeState(server, false)}` : `状态:${describeState(server, false)}`)'],
|
|
386
|
+
{ from: '`MCP server ${name}`', to: '(process.env.U1S1_LANG === "en" ? `MCP server ${name}` : `MCP 服务 ${name}`)', expected: 3 },
|
|
387
|
+
['title: `Tools of ${server.entry.name}`,', 'title: process.env.U1S1_LANG === "en" ? `Tools of ${server.entry.name}` : `${server.entry.name} 的工具`,'],
|
|
388
|
+
['details: `Exposure ${exposure}: ${exposure === "hidden" ? "unreachable" : EXPOSURE_DESCRIPTIONS[exposure]}${overridden ? "\\nSome tools override it with toolExposure." : ""}`,', 'details: process.env.U1S1_LANG === "en" ? `Exposure ${exposure}: ${exposure === "hidden" ? "unreachable" : EXPOSURE_DESCRIPTIONS[exposure]}${overridden ? "\\nSome tools override it with toolExposure." : ""}` : `暴露方式 ${exposure}:${exposure === "hidden" ? "不可调用" : EXPOSURE_DESCRIPTIONS[exposure]}${overridden ? "\\n部分工具用 toolExposure 单独设置了。" : ""}`,'],
|
|
389
|
+
['empty: "The server offers no tools.",', 'empty: process.env.U1S1_LANG === "en" ? "The server offers no tools." : "这个服务没有提供工具。",'],
|
|
390
|
+
['title: `Exposure of ${server.entry.name}`,', 'title: process.env.U1S1_LANG === "en" ? `Exposure of ${server.entry.name}` : `${server.entry.name} 的暴露方式`,'],
|
|
391
|
+
['? `Applies to this session; the server is registered by ${server.entry.source}.`', '? (process.env.U1S1_LANG === "en" ? `Applies to this session; the server is registered by ${server.entry.source}.` : `仅本次会话生效;这个服务由 ${server.entry.source} 注册。`)'],
|
|
392
|
+
[': `Saved to ${server.entry.source}.`,', ': (process.env.U1S1_LANG === "en" ? `Saved to ${server.entry.source}.` : `保存到 ${server.entry.source}。`),'],
|
|
393
|
+
['const title = `Sign in to ${name}`;', 'const title = process.env.U1S1_LANG === "en" ? `Sign in to ${name}` : `登录 ${name}`;'],
|
|
394
|
+
['ui.status(title, "Contacting the authorization server…");', 'ui.status(title, process.env.U1S1_LANG === "en" ? "Contacting the authorization server…" : "正在联系授权服务器…");'],
|
|
395
|
+
['ui.status(title, "Connecting…");', 'ui.status(title, process.env.U1S1_LANG === "en" ? "Connecting…" : "连接中…");'],
|
|
396
|
+
['"Reconnecting…"', '(process.env.U1S1_LANG === "en" ? "Reconnecting…" : "重新连接中…")'],
|
|
397
|
+
['action === "enable" ? "Connecting…" : "Disconnecting…"', 'action === "enable" ? (process.env.U1S1_LANG === "en" ? "Connecting…" : "连接中…") : (process.env.U1S1_LANG === "en" ? "Disconnecting…" : "断开中…")'],
|
|
398
|
+
['ctx.ui.notify(`No MCP server named "${name}".`, "error");', 'ctx.ui.notify(process.env.U1S1_LANG === "en" ? `No MCP server named "${name}".` : `没有叫「${name}」的 MCP 服务。`, "error");'],
|
|
399
|
+
['ctx.ui.select("MCP server", ', 'ctx.ui.select(process.env.U1S1_LANG === "en" ? "MCP server" : "选择 MCP 服务", '],
|
|
400
|
+
['none: "No enabled MCP server uses OAuth. Only HTTP servers without an Authorization header do.",', 'none: process.env.U1S1_LANG === "en" ? "No enabled MCP server uses OAuth. Only HTTP servers without an Authorization header do." : "没有用 OAuth 登录的已启用 MCP 服务(只有没配 Authorization 头的 HTTP 服务才用)。",'],
|
|
401
|
+
['ctx.ui.notify(`Signing in to MCP server "${name}" requires interactive mode.`, "error");', 'ctx.ui.notify(process.env.U1S1_LANG === "en" ? `Signing in to MCP server "${name}" requires interactive mode.` : `登录 MCP 服务「${name}」需要在交互界面里进行。`, "error");'],
|
|
402
|
+
['process.platform === "darwin" ? "Cmd+click to open" : "Ctrl+click to open"', 'process.platform === "darwin" ? (process.env.U1S1_LANG === "en" ? "Cmd+click to open" : "Cmd+点击打开") : (process.env.U1S1_LANG === "en" ? "Ctrl+click to open" : "Ctrl+点击打开")'],
|
|
403
|
+
['ctx.ui.notify(`Sign in to MCP server "${name}" in your browser:\\n${lines}`, "info");', 'ctx.ui.notify(process.env.U1S1_LANG === "en" ? `Sign in to MCP server "${name}" in your browser:\\n${lines}` : `请在浏览器里登录 MCP 服务「${name}」:\\n${lines}`, "info");'],
|
|
404
|
+
['ctx.ui.input(`Waiting for sign-in to "${name}". If the browser cannot reach this machine, paste the URL it was redirected to.`,', 'ctx.ui.input(process.env.U1S1_LANG === "en" ? `Waiting for sign-in to "${name}". If the browser cannot reach this machine, paste the URL it was redirected to.` : `等待登录「${name}」。浏览器连不到这台机器的话,把它跳转到的网址粘贴进来。`,'],
|
|
405
|
+
['ctx.ui.notify(`Signed in to MCP server "${name}" (${server.connection?.tools.length ?? 0} tools).`, "info");', 'ctx.ui.notify(process.env.U1S1_LANG === "en" ? `Signed in to MCP server "${name}" (${server.connection?.tools.length ?? 0} tools).` : `已登录 MCP 服务「${name}」(${server.connection?.tools.length ?? 0} 个工具)。`, "info");'],
|
|
406
|
+
{ from: '`MCP failed to load: ${errorMessage(error)}`', to: '(process.env.U1S1_LANG === "en" ? `MCP failed to load: ${errorMessage(error)}` : `MCP 加载失败:${errorMessage(error)}`)', expected: 2 },
|
|
407
|
+
['ctx.ui.notify("MCP servers are still connecting; their tools become available once connected.", "info");', 'ctx.ui.notify(process.env.U1S1_LANG === "en" ? "MCP servers are still connecting; their tools become available once connected." : "MCP 服务还在连接,连上后工具就能用了。", "info");'],
|
|
408
|
+
['description: "Manage MCP servers: sign in, reconnect, enable or disable, and change exposure",', 'description: process.env.U1S1_LANG === "en" ? "Manage MCP servers: sign in, reconnect, enable or disable, and change exposure" : "管理 MCP 服务:登录、重连、启用/停用、调整暴露方式",'],
|
|
409
|
+
['? `Signed out of MCP server "${server.entry.name}".`', '? (process.env.U1S1_LANG === "en" ? `Signed out of MCP server "${server.entry.name}".` : `已退出 MCP 服务「${server.entry.name}」的登录。`)'],
|
|
410
|
+
[': `No stored credentials for MCP server "${server.entry.name}".`, "info");', ': (process.env.U1S1_LANG === "en" ? `No stored credentials for MCP server "${server.entry.name}".` : `MCP 服务「${server.entry.name}」没有保存的登录凭据。`), "info");'],
|
|
411
|
+
['none: "No enabled MCP server to reconnect.",', 'none: process.env.U1S1_LANG === "en" ? "No enabled MCP server to reconnect." : "没有可重连的已启用 MCP 服务。",'],
|
|
412
|
+
['ctx.ui.notify(`Reconnected to MCP server "${server.entry.name}" (${describeState(server)}).`, "info");', 'ctx.ui.notify(process.env.U1S1_LANG === "en" ? `Reconnected to MCP server "${server.entry.name}" (${describeState(server)}).` : `已重新连接 MCP 服务「${server.entry.name}」(${describeState(server)})。`, "info");'],
|
|
413
|
+
];
|
|
414
|
+
// /mcp 面板外框与登录页(dist/extensions/mcp/ui.js)
|
|
415
|
+
const MCP_UI_REPLACEMENTS = [
|
|
416
|
+
['frame(theme, "MCP servers", [new Text(theme.fg("muted", "Loading…"), 1, 1)])', 'frame(theme, process.env.U1S1_LANG === "en" ? "MCP servers" : "MCP 服务", [new Text(theme.fg("muted", process.env.U1S1_LANG === "en" ? "Loading…" : "加载中…"), 1, 1)])'],
|
|
417
|
+
['menu.empty ?? "Nothing to show."', 'menu.empty ?? (process.env.U1S1_LANG === "en" ? "Nothing to show." : "没有内容。")'],
|
|
418
|
+
['process.platform === "darwin" ? "Cmd+click to open" : "Ctrl+click to open"', 'process.platform === "darwin" ? (process.env.U1S1_LANG === "en" ? "Cmd+click to open" : "Cmd+点击打开") : (process.env.U1S1_LANG === "en" ? "Ctrl+click to open" : "Ctrl+点击打开")'],
|
|
419
|
+
['theme.fg("muted", "Approve access in your browser. If it did not open, visit:")', 'theme.fg("muted", process.env.U1S1_LANG === "en" ? "Approve access in your browser. If it did not open, visit:" : "请在浏览器里同意授权。浏览器没自动打开的话,访问:")'],
|
|
420
|
+
['theme.fg("muted", "If the browser runs on another machine, paste the URL it was redirected to:")', 'theme.fg("muted", process.env.U1S1_LANG === "en" ? "If the browser runs on another machine, paste the URL it was redirected to:" : "浏览器在另一台机器上的话,把它跳转到的网址粘贴到这里:")'],
|
|
421
|
+
];
|
|
422
|
+
// codemode 工具结果(dist/extensions/codemode/renderer.js)的固定文案
|
|
423
|
+
const CODEMODE_RENDER_REPLACEMENTS = [
|
|
424
|
+
['theme.fg("muted", `Model calls: ${formatCost(total)}`)', 'theme.fg("muted", process.env.U1S1_LANG === "en" ? `Model calls: ${formatCost(total)}` : `模型调用:${formatCost(total)}`)'],
|
|
425
|
+
['theme.fg("muted", `Full output: ${fullOutputPath}`)', 'theme.fg("muted", process.env.U1S1_LANG === "en" ? `Full output: ${fullOutputPath}` : `完整输出:${fullOutputPath}`)'],
|
|
426
|
+
];
|
|
427
|
+
|
|
328
428
|
const TUI_TEXT_TARGETS = [
|
|
329
429
|
[["core", "slash-commands.js"], SLASH_COMMANDS_REPLACEMENTS, "命令描述"],
|
|
330
430
|
[["modes", "interactive", "components", "assistant-message.js"], ASSISTANT_TEXT_REPLACEMENTS, "回复状态文案"],
|
|
@@ -332,9 +432,13 @@ const TUI_TEXT_TARGETS = [
|
|
|
332
432
|
[["modes", "interactive", "interactive-mode.js"], HOTKEYS_REPLACEMENTS, "/hotkeys"],
|
|
333
433
|
[["modes", "interactive", "interactive-mode.js"], WORKING_TEXT_REPLACEMENTS, "工作中提示"],
|
|
334
434
|
[["modes", "interactive", "interactive-mode.js"], BUG_REPORT_REPLACEMENTS, "/bug 入口"],
|
|
435
|
+
[["modes", "interactive", "interactive-mode.js"], LOGIN_PROVIDER_REPLACEMENTS, "/login 渠道"],
|
|
335
436
|
[["modes", "interactive", "components", "keybinding-hints.js"], KEY_HINT_REPLACEMENTS, "按键脚注"],
|
|
336
437
|
[["modes", "interactive", "components", "model-selector.js"], MODEL_SELECTOR_REPLACEMENTS, "模型选择器"],
|
|
337
438
|
[["modes", "interactive", "components", "settings-selector.js"], SETTINGS_REPLACEMENTS, "设置菜单"],
|
|
439
|
+
[["extensions", "mcp", "index.js"], MCP_TEXT_REPLACEMENTS, "/mcp 面板"],
|
|
440
|
+
[["extensions", "mcp", "ui.js"], MCP_UI_REPLACEMENTS, "/mcp 界面"],
|
|
441
|
+
[["extensions", "codemode", "renderer.js"], CODEMODE_RENDER_REPLACEMENTS, "codemode 结果"],
|
|
338
442
|
];
|
|
339
443
|
|
|
340
444
|
/**
|