u1s1-cli 1.16.1 → 1.18.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/README.md CHANGED
@@ -1,72 +1,47 @@
1
- # u1s1 — 有一说一,最省心的 AI 编程搭子
1
+ # u1s1 — an AI coding buddy in your terminal
2
2
 
3
- 在终端里用中文说需求,AI 帮你读文件、改代码、跑命令。不用买模型、不用配 API、不用懂那些名词,默认 DeepSeek Flash **每天免费 $2**(北京时间 0 点恢复),新用户另送一次性 $10 加量。
3
+ Describe what you need in plain words; u1s1 reads your files, edits code and runs commands. No model setup, no API keys to manage: sign in with your u1s1.dev account and start.
4
4
 
5
- ## 三步开始
5
+ ## Install
6
6
 
7
- ### 🚀 一键安装(推荐)
8
- 不需要提前装任何东西,复制下面一行到终端粘贴运行:
7
+ **One-line installer (recommended, bundles its own Node.js)**
9
8
 
10
- **Linux / macOS**
9
+ macOS / Linux
11
10
  ```bash
12
- curl -fsSL https://u1s1app.com/releases/install.sh | bash
11
+ curl -fsSL https://u1s1.dev/releases/install.sh | bash
13
12
  ```
14
13
 
15
- **Windows**
14
+ Windows (PowerShell)
16
15
  ```powershell
17
- powershell -c "irm https://u1s1app.com/releases/install.ps1 | iex"
16
+ irm https://u1s1.dev/releases/install.ps1 | iex
18
17
  ```
19
18
 
20
- 脚本会自动帮你安装 Node.js 和 u1s1,一行搞定。
21
-
22
- ### 或者,手动安装(需要 Node.js 20+)
19
+ **Or with npm (Node.js 22.19+)**
23
20
  ```bash
24
21
  npm i -g u1s1-cli
25
22
  ```
26
23
 
27
- ### 安装好后
24
+ This package connects to the international site, u1s1.dev. If this computer was already set up for another site, u1s1 keeps that setup; to switch, run `u1s1 login --site global`.
25
+
26
+ ## Get started
28
27
  ```bash
29
- # 第一次运行会引导你去 u1s1app.com 注册领 Key(30 秒,不用绑卡)
28
+ # First run walks you through signing in at u1s1.dev
30
29
  u1s1
31
30
 
32
- # 进到你的项目文件夹,像发微信一样说需求
33
- cd 我的项目
31
+ # Then, inside your project
32
+ cd my-project
34
33
  u1s1
35
34
  ```
36
35
 
37
- ## 常用命令
36
+ ## Common commands
38
37
 
39
- | 命令 | 作用 |
38
+ | Command | What it does |
40
39
  |---|---|
41
- | `u1s1` | 进入交互模式,直接开聊 |
42
- | `u1s1 -p "把 README 里的错别字修一下"` | 一句话模式,干完就退出 |
43
- | `u1s1 usage` | 看额度用了多少 |
44
- | `u1s1 model` | 看/切默认模型:`u1s1 model grok` 切 Grok 4.6,`u1s1 model deepseek` 切回(对话里 `/model` 同样会记住) |
45
- | `u1s1 update` | 升级 u1s1 到最新版 |
46
- | `u1s1 import` | 从 Claude Code / Codex 导入历史对话,之后 `/resume` 就能接着聊 |
47
- | `u1s1 login` / `u1s1 logout` | 登录 / 退出 |
48
-
49
- ## 长会话自动压缩
50
-
51
- 对话越长,每一轮上传的历史越多,回复会变慢、也更容易出错。u1s1 会在上下文接近 **200K token** 时自动压缩(把早期对话浓缩成摘要,最近 2 万 token 原文保留),压缩时界面有提示;`/context` 可以随时看当前占用、预算和触发点,`/compact` 可以手动压缩。
52
-
53
- 这只改变压缩时机,不改变模型能力。想调整或关闭,编辑 `~/.u1s1/agent/settings.json`:
54
-
55
- ```json
56
- { "compaction": { "maxContextTokens": 200000 } }
57
- ```
58
-
59
- `0` 表示不限(按模型自身窗口,如 DeepSeek Flash 的 1M)。改完重启 u1s1 生效。
60
-
61
- ## 在终端粘贴图片
62
-
63
- 默认模型 DeepSeek Flash 支持视觉输入,复制截图或图片后,在 u1s1 输入框按 `Ctrl+V` 即可附加图片。macOS 这里也使用 `Ctrl+V`;`Cmd+V` 由 Terminal/iTerm 接管,只适合粘贴文本,无法把剪贴板图片交给 CLI。
64
-
65
- ## 有一说一
66
-
67
- - **背后模型**:默认 DeepSeek Flash(1M 上下文,每天免费 $2);难题可随时 `u1s1 model grok` 切 Grok 4.6。对话里 `/model` 也会记住,下次启动还是这个。
68
- - **怎么收费**:DeepSeek Flash 每天有 $2 免费用量,北京时间 0 点恢复且不累计。免费用完或切换其他模型后,按 API 实际成本从永久余额扣,不加价;新用户另送一次性 $10 加量。
69
- - **额度不够**:邀请朋友,你俩各得 $1 永久加量 → [u1s1app.com/dashboard](https://u1s1app.com/dashboard)
70
- - **内核**:基于开源的 [pi](https://pi.dev) coding agent,会话管理、斜杠命令、皮肤等能力全都有。
71
-
72
- 官网:[u1s1app.com](https://u1s1app.com)
40
+ | `u1s1` | Interactive mode |
41
+ | `u1s1 -p "fix the typos in README"` | One-shot mode: do the task, then exit |
42
+ | `u1s1 usage` | See how much credit you've used |
43
+ | `u1s1 model` | Show or change the default model |
44
+ | `u1s1 update` | Update u1s1 to the latest version |
45
+ | `u1s1 login` / `u1s1 logout` | Sign in / sign out |
46
+
47
+ Website: [u1s1.dev](https://u1s1.dev)
@@ -43,6 +43,13 @@ export declare function applyLlamaDisabledDefault(settings: Record<string, unkno
43
43
  * 用户再在 /settings 里选的值就不会被动了。
44
44
  */
45
45
  export declare function applyNativeUiRestore(settings: Record<string, unknown>): boolean;
46
+ /**
47
+ * Pi 1.3+ 的两个对外上传开关,只在用户没显式设过时写入(用户在 /settings 里选的值不动):
48
+ * - sessionShareDestination:Pi 默认 "auto",登录过上游自家托管服务时 /share 会优先传到那里;u1s1 固定用 GitHub 私密 gist。
49
+ * - enableInstallTelemetry:检测到版本更新后向 Pi 上游发一次版本 ping,开着时还会给部分第三方渠道的请求加 Pi 的
50
+ * 归属头。以前只靠 ensureDefaultSettings 钉 lastChangelogVersion 间接压住 ping,这里显式关掉。
51
+ */
52
+ export declare function applyShareAndTelemetryDefaults(settings: Record<string, unknown>): boolean;
46
53
  /** Defaults that don't overwrite values the user already set. */
47
54
  export declare function ensureDefaultSettings(models?: ModelDef[]): void;
48
55
  /** ≤0.4.0 wrote u1s1-dark/u1s1-light into the pi themes dir; remove them. */
@@ -225,6 +225,24 @@ export function applyNativeUiRestore(settings) {
225
225
  settings[NATIVE_UI_MARKER] = true;
226
226
  return true;
227
227
  }
228
+ /**
229
+ * Pi 1.3+ 的两个对外上传开关,只在用户没显式设过时写入(用户在 /settings 里选的值不动):
230
+ * - sessionShareDestination:Pi 默认 "auto",登录过上游自家托管服务时 /share 会优先传到那里;u1s1 固定用 GitHub 私密 gist。
231
+ * - enableInstallTelemetry:检测到版本更新后向 Pi 上游发一次版本 ping,开着时还会给部分第三方渠道的请求加 Pi 的
232
+ * 归属头。以前只靠 ensureDefaultSettings 钉 lastChangelogVersion 间接压住 ping,这里显式关掉。
233
+ */
234
+ export function applyShareAndTelemetryDefaults(settings) {
235
+ let changed = false;
236
+ if (!("sessionShareDestination" in settings)) {
237
+ settings["sessionShareDestination"] = "gist";
238
+ changed = true;
239
+ }
240
+ if (!("enableInstallTelemetry" in settings)) {
241
+ settings["enableInstallTelemetry"] = false;
242
+ changed = true;
243
+ }
244
+ return changed;
245
+ }
228
246
  /** Defaults that don't overwrite values the user already set. */
229
247
  export function ensureDefaultSettings(models = []) {
230
248
  mkdirSync(agentDir, { recursive: true });
@@ -268,6 +286,8 @@ export function ensureDefaultSettings(models = []) {
268
286
  settings["tuiMode"] = "regular";
269
287
  changed = true;
270
288
  }
289
+ if (applyShareAndTelemetryDefaults(settings))
290
+ changed = true;
271
291
  if (applyNativeUiRestore(settings))
272
292
  changed = true;
273
293
  if (applyCodemodeDefault(settings))
@@ -18,6 +18,12 @@ export declare function authorizedFetch(cfg: CliConfig, input: string | URL, ini
18
18
  * (zlib "incorrect header check"), hiding the real message.
19
19
  */
20
20
  export declare function forwardedResponseHeaders(upstream: Headers): Record<string, string>;
21
+ /**
22
+ * 把 Pi ≥1.5 的模型请求 User-Agent 换回 Pi 1.2 的 `pi (<platform> <release>; <arch>)`。
23
+ * Gateway 按这个形状认官方 Pi(client-signal.ts isOfficialU1s1UserAgent);不换的话升级后的官方客户端会被记成
24
+ * 外部客户端。别的 UA(Claude Code、Codex、工具请求)原样不动。
25
+ */
26
+ export declare function normalizePiUserAgent(ua: string): string;
21
27
  /** Buffer a loopback request only up to the same JSON limit enforced by Gateway. */
22
28
  export declare function readSigningProxyRequestBody(request: IncomingMessage, maxBytes?: number): Promise<Buffer | undefined>;
23
29
  /**
@@ -1,5 +1,6 @@
1
1
  import { createHash, randomBytes, randomUUID, webcrypto } from "node:crypto";
2
2
  import { createServer } from "node:http";
3
+ import { arch, platform, release } from "node:os";
3
4
  import { VERSION } from "./config.js";
4
5
  import { tr } from "./i18n.js";
5
6
  import { describeFetchFailure, gatewayFetch, plainGatewaySender } from "./gateway-route.js";
@@ -111,8 +112,21 @@ function requestHeaders(input) {
111
112
  else
112
113
  headers.set(name, value);
113
114
  }
115
+ const ua = headers.get("user-agent");
116
+ if (ua)
117
+ headers.set("user-agent", normalizePiUserAgent(ua));
114
118
  return headers;
115
119
  }
120
+ /** Pi ≥1.5 的模型请求 User-Agent 形如 `pi/1.5.2 (darwin; node/v22.12.0; arm64)`。 */
121
+ const PI_DETAILED_UA_RE = /^pi\/\d+\.\d+\.\d+\S* \([^)]*\)$/;
122
+ /**
123
+ * 把 Pi ≥1.5 的模型请求 User-Agent 换回 Pi 1.2 的 `pi (<platform> <release>; <arch>)`。
124
+ * Gateway 按这个形状认官方 Pi(client-signal.ts isOfficialU1s1UserAgent);不换的话升级后的官方客户端会被记成
125
+ * 外部客户端。别的 UA(Claude Code、Codex、工具请求)原样不动。
126
+ */
127
+ export function normalizePiUserAgent(ua) {
128
+ return PI_DETAILED_UA_RE.test(ua.trim()) ? `pi (${platform()} ${release()}; ${arch()})` : ua;
129
+ }
116
130
  /** Buffer a loopback request only up to the same JSON limit enforced by Gateway. */
117
131
  export async function readSigningProxyRequestBody(request, maxBytes = SIGNING_PROXY_REQUEST_LIMIT_BYTES) {
118
132
  const declaredLength = Number(request.headers["content-length"]);
@@ -27,6 +27,11 @@ export interface BuiltinCommandText {
27
27
  argumentHint?: string;
28
28
  /** Pi 上游英文原文 */
29
29
  upstream: string;
30
+ /**
31
+ * 上游以前的原文。上一版投影的 u1s1-ui-text.js 残留、或旧补丁在旧 Pi 上求值出的英文是这些措辞,
32
+ * 也要认出来换成当前文案,否则会停在旧英文上。
33
+ */
34
+ formerUpstream?: readonly string[];
30
35
  /** 中国站文案 */
31
36
  zh?: string;
32
37
  /** 国际站文案;不写就是上游原文 */
@@ -34,7 +39,7 @@ export interface BuiltinCommandText {
34
39
  /** 从补全里去掉(/bug:诊断会上传给 Pi 上游,u1s1 用户的问题走 /feedback) */
35
40
  hide?: true;
36
41
  }
37
- /** 顺序与 Pi 1.2.0 的 BUILTIN_SLASH_COMMANDS 相同。 */
42
+ /** 顺序与 Pi 1.5.2 的 BUILTIN_SLASH_COMMANDS 相同。 */
38
43
  export declare const BUILTIN_COMMAND_TEXT: readonly BuiltinCommandText[];
39
44
  /** 流式回复时的工作提示(Pi 默认 "Working")。 */
40
45
  export declare const WORKING_MESSAGE: {
@@ -20,7 +20,7 @@
20
20
  export const LEGACY_UI_TEXT_EXTENSION_FILE = "u1s1-ui-text.js";
21
21
  /** 描述里的应用名占位:/quit 的上游原文是 `Quit ${APP_NAME}`,运行时从现有描述里取回实际名字。 */
22
22
  export const APP_NAME_PLACEHOLDER = "{app}";
23
- /** 顺序与 Pi 1.2.0 的 BUILTIN_SLASH_COMMANDS 相同。 */
23
+ /** 顺序与 Pi 1.5.2 的 BUILTIN_SLASH_COMMANDS 相同。 */
24
24
  export const BUILTIN_COMMAND_TEXT = [
25
25
  { name: "settings", upstream: "Open settings menu", zh: "打开设置菜单" },
26
26
  { name: "model", argumentHint: "<provider/model>", upstream: "Select model (opens selector UI)", zh: "切换模型(列表里有免费/价格提示)" },
@@ -28,8 +28,20 @@ export const BUILTIN_COMMAND_TEXT = [
28
28
  { name: "thinking", argumentHint: "<level>", upstream: "Set thinking level", zh: "调整思考强度(想得越深越费额度)" },
29
29
  { name: "scoped-models", upstream: "Enable/disable models for Ctrl+P cycling", zh: "设置 Ctrl+P 轮换的模型范围" },
30
30
  { name: "export", upstream: "Export session (HTML default, or specify path: .html/.jsonl)", zh: "导出会话(默认 HTML,也可指定 .html/.jsonl 路径)" },
31
- { name: "import", upstream: "Import and resume a session from a JSONL file", zh: "从 JSONL 文件导入并继续会话" },
32
- { name: "share", upstream: "Share session as a secret GitHub gist", zh: "把会话分享为 GitHub 私密 gist" },
31
+ {
32
+ name: "import",
33
+ upstream: "Import a JSONL session, preserving its ID and working directory",
34
+ formerUpstream: ["Import and resume a session from a JSONL file"],
35
+ zh: "从 JSONL 文件导入并继续会话",
36
+ },
37
+ // u1s1 把 sessionShareDestination 写成 gist(agent-setup.ts),国际站也照实际去向写,不提上游的另一个上传目标
38
+ {
39
+ name: "share",
40
+ upstream: "Share session via a Radius artifact or a GitHub gist",
41
+ formerUpstream: ["Share session as a secret GitHub gist"],
42
+ zh: "把会话分享为 GitHub 私密 gist",
43
+ en: "Share session as a secret GitHub gist",
44
+ },
33
45
  { name: "bug", argumentHint: "<description>", upstream: "Report a bug to the Pi developers", hide: true },
34
46
  { name: "copy", upstream: "Copy last agent message to clipboard", zh: "复制上一条回复到剪贴板" },
35
47
  { name: "name", upstream: "Set session display name", zh: "给当前会话起个名字" },
@@ -55,7 +67,7 @@ export const BUILTIN_COMMAND_TEXT = [
55
67
  },
56
68
  { name: "new", upstream: "Start a new session", zh: "开始新会话" },
57
69
  { name: "compact", upstream: "Manually compact the session context", zh: "压缩会话上下文,腾出空间继续聊" },
58
- { name: "resume", upstream: "Resume a different session", zh: "恢复或切换到其他会话" },
70
+ { name: "resume", upstream: "Resume another session", formerUpstream: ["Resume a different session"], zh: "恢复或切换到其他会话" },
59
71
  { name: "reload", upstream: "Reload keybindings, extensions, skills, prompts, themes, and context files", zh: "重新加载扩展、技能、主题等资源" },
60
72
  { name: "quit", upstream: `Quit ${APP_NAME_PLACEHOLDER}`, zh: `退出 ${APP_NAME_PLACEHOLDER}` },
61
73
  ];
@@ -66,12 +78,12 @@ export const HIDDEN_THINKING_LABEL = { zh: "思考中…", en: "Thinking..." };
66
78
  const escapeRe = (s) => s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
67
79
  /** 与 pi-tui CombinedAutocompleteProvider 拼补全描述的方式相同 */
68
80
  const withHint = (hint, text) => (hint ? (text ? `${hint} — ${text}` : hint) : text);
69
- // 每条命令认三种现有描述:上游原文,以及旧版 u1s1 补丁打过的中文、英文(从旧版原地升级时
81
+ // 每条命令认几种现有描述:上游原文(含以前版本的原文),以及旧版 u1s1 补丁打过的中文、英文(从旧版原地升级时
70
82
  // node_modules 里的 Pi 可能还带着那份补丁;上一版投影的 u1s1-ui-text.js 还在时它已先换过一遍)。
71
83
  // 都换成当前语言的文案,不叠加、不混语言。
72
84
  const RULES = new Map(BUILTIN_COMMAND_TEXT.map((c) => {
73
85
  const en = c.en ?? c.upstream;
74
- const patterns = [c.upstream, c.zh, en]
86
+ const patterns = [c.upstream, ...(c.formerUpstream ?? []), c.zh, en]
75
87
  .filter((t) => typeof t === "string")
76
88
  .map((t) => new RegExp(`^${escapeRe(withHint(c.argumentHint, t)).split(escapeRe(APP_NAME_PLACEHOLDER)).join("(.+)")}$`));
77
89
  return [c.name, { argumentHint: c.argumentHint, zh: c.zh ?? en, en, hide: c.hide === true, patterns }];
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "u1s1-cli",
3
- "version": "1.16.1",
4
- "description": "u1s1 — 有一说一,最省心的 AI 编程搭子。终端里用中文说需求,AI 帮你读文件、改代码、跑命令。",
3
+ "version": "1.18.0",
4
+ "description": "u1s1 — an AI coding buddy in your terminal. Describe what you need; it reads files, edits code and runs commands.",
5
5
  "type": "module",
6
6
  "bin": {
7
7
  "u1s1": "dist/index.js"
@@ -39,17 +39,18 @@
39
39
  "u1s1"
40
40
  ],
41
41
  "license": "MIT",
42
- "homepage": "https://u1s1app.com",
42
+ "homepage": "https://u1s1.dev",
43
43
  "dependencies": {
44
- "@earendil-works/pi-agent-core": "1.2.0",
45
- "@earendil-works/pi-ai": "1.2.0",
46
- "@earendil-works/pi-codemode": "1.2.0",
47
- "@earendil-works/pi-coding-agent": "1.2.0",
48
- "@earendil-works/pi-mcp": "1.2.0",
49
- "@earendil-works/pi-telemetry": "1.2.0",
50
- "@earendil-works/pi-tui": "1.2.0",
44
+ "@earendil-works/pi-agent-core": "1.5.2",
45
+ "@earendil-works/pi-ai": "1.5.2",
46
+ "@earendil-works/pi-codemode": "1.5.2",
47
+ "@earendil-works/pi-coding-agent": "1.5.2",
48
+ "@earendil-works/pi-mcp": "1.5.2",
49
+ "@earendil-works/pi-schema": "1.5.2",
50
+ "@earendil-works/pi-telemetry": "1.5.2",
51
+ "@earendil-works/pi-tui": "1.5.2",
51
52
  "typebox": "1.3.7",
52
- "undici": "8.10.2"
53
+ "undici": "8.12.3"
53
54
  },
54
55
  "devDependencies": {
55
56
  "@types/node": "^22.20.1",
@@ -57,6 +58,6 @@
57
58
  "typescript": "^5.9.3"
58
59
  },
59
60
  "u1s1": {
60
- "defaultSite": "cn"
61
+ "defaultSite": "intl"
61
62
  }
62
63
  }
@@ -198,8 +198,8 @@ const KEY_HINT_REPLACEMENTS = [
198
198
  { from: 'theme.fg("muted", ` ${description}`)', to: 'theme.fg("muted", ` ${u1s1Hint(description)}`)', expected: 2 },
199
199
  ];
200
200
  const MODEL_SELECTOR_REPLACEMENTS = [
201
- ['const modelText = isSelected ? theme.fg("accent", item.id) : item.id;', 'const modelText = isSelected ? theme.fg("accent", item.model.name || item.id) : (item.model.name || item.id);'],
202
- ['theme.fg("muted", ` Model Name: ${selected.model.name}`)', 'theme.fg("muted", ` ${process.env.U1S1_LANG === "en" ? "Model ID" : "模型 ID"}: ${selected.model.id}`)'],
201
+ // Pi 1.5 起列表行本身显示「id (名字)」,这里只汉化详情行的标签,不再把 id/名字互换
202
+ ['theme.fg("muted", ` Model Name: ${selected.model.name}`)', 'theme.fg("muted", ` ${process.env.U1S1_LANG === "en" ? "Model Name" : "模型名称"}: ${selected.model.name}`)'],
203
203
  ['theme.fg("muted", " No matching models")', 'theme.fg("muted", process.env.U1S1_LANG === "en" ? " No matching models" : " 没有匹配的模型")'],
204
204
  ['theme.fg("muted", " · default")', 'theme.fg("muted", process.env.U1S1_LANG === "en" ? " · default" : " · 默认")'],
205
205
  ['const allText = this.scope === "all" ? theme.fg("accent", "all") : theme.fg("muted", "all");', 'const allText = this.scope === "all" ? theme.fg("accent", process.env.U1S1_LANG === "en" ? "all" : "全部") : theme.fg("muted", process.env.U1S1_LANG === "en" ? "all" : "全部");'],
@@ -260,6 +260,8 @@ const SETTINGS_REPLACEMENTS = [
260
260
  ['description: "Show condensed changelog after updates",', 'description: process.env.U1S1_LANG === "en" ? "Show condensed changelog after updates" : "更新后只显示精简的更新日志",'],
261
261
  ['label: "Quiet startup",', 'label: process.env.U1S1_LANG === "en" ? "Quiet startup" : "安静启动",'],
262
262
  ['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:只保留启动标题)",'],
263
+ ['label: "Session share destination",', 'label: process.env.U1S1_LANG === "en" ? "Session share destination" : "/share 上传位置",'],
264
+ ['description: "Where to upload sessions with /share (auto prefers Radius when authenticated, otherwise GitHub Gist)",', 'description: process.env.U1S1_LANG === "en" ? "Where to upload sessions with /share (auto prefers Radius when authenticated, otherwise GitHub Gist)" : "/share 分享会话时上传到哪里(auto:登录过 Radius 时优先上传到 Radius,否则用 GitHub Gist)",'],
263
265
  ['label: "Install telemetry",', 'label: process.env.U1S1_LANG === "en" ? "Install telemetry" : "安装统计",'],
264
266
  ['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" : "更新后匿名上报一次版本信息",'],
265
267
  ['label: "Default project trust",', 'label: process.env.U1S1_LANG === "en" ? "Default project trust" : "默认项目信任",'],
@@ -316,6 +318,30 @@ const LOGIN_PROVIDER_REPLACEMENTS = [
316
318
  // Pi 0.99 起内置的 MCP 扩展(dist/extensions/mcp/):/mcp 管理面板、登录、状态与提示。
317
319
  // 只换给人看的文案;mcp_servers 系统提示段、工具描述等给模型看的保持英文。
318
320
  // "Sign-in cancelled." 既是返回值又被 === 比较,两处必须一起换。
321
+ // 会话恢复、导入、分叉的状态提示,启动时的更新日志标题、模型范围、技能警告(Pi 1.5 新增或改写的文字)。
322
+ const SESSION_STATUS_REPLACEMENTS = [
323
+ ['this.showStatus(`Resumed session in ${error.issue.fallbackCwd}`);', 'this.showStatus(process.env.U1S1_LANG === "en" ? `Resumed session in ${error.issue.fallbackCwd}` : `已在 ${error.issue.fallbackCwd} 恢复会话`);'],
324
+ ['this.showStatus("Resume cancelled");', 'this.showStatus(process.env.U1S1_LANG === "en" ? "Resume cancelled" : "已取消恢复会话");'],
325
+ ['this.showStatus(`Forked into new session ${fork.getSessionId()} in ${cwd}`);', 'this.showStatus(process.env.U1S1_LANG === "en" ? `Forked into new session ${fork.getSessionId()} in ${cwd}` : `已分叉为新会话 ${fork.getSessionId()},工作目录 ${cwd}`);'],
326
+ { from: 'this.showStatus("Import cancelled");', to: 'this.showStatus(process.env.U1S1_LANG === "en" ? "Import cancelled" : "已取消导入");', expected: 4 },
327
+ ['this.showStatus(`Session imported from: ${inputPath}`);', 'this.showStatus(process.env.U1S1_LANG === "en" ? `Session imported from: ${inputPath}` : `已导入会话: ${inputPath}`);'],
328
+ ['this.showStatus(result.cancelled ? "Import cancelled" : `Session imported from: ${inputPath}`);', 'this.showStatus(result.cancelled ? (process.env.U1S1_LANG === "en" ? "Import cancelled" : "已取消导入") : process.env.U1S1_LANG === "en" ? `Session imported from: ${inputPath}` : `已导入会话: ${inputPath}`);'],
329
+ ['const condensedText = `Updated to v${latestVersion}. Use ${theme.bold("/changelog")} to view full changelog.`;', 'const condensedText = process.env.U1S1_LANG === "en" ? `Updated to v${latestVersion}. Use ${theme.bold("/changelog")} to view full changelog.` : `已更新到 v${latestVersion}。输入 ${theme.bold("/changelog")} 查看完整更新日志。`;'],
330
+ ['.map((update) => `Updated ${update.name} to v${update.version}.`)', '.map((update) => process.env.U1S1_LANG === "en" ? `Updated ${update.name} to v${update.version}.` : `${update.name} 已更新到 v${update.version}。`)'],
331
+ { from: 'theme.bold(theme.fg("accent", "What\'s New"))', to: 'theme.bold(theme.fg("accent", process.env.U1S1_LANG === "en" ? "What\'s New" : "更新内容"))', expected: 2 },
332
+ ['theme.bold(theme.fg("accent", "Extension Updates"))', 'theme.bold(theme.fg("accent", process.env.U1S1_LANG === "en" ? "Extension Updates" : "扩展更新"))'],
333
+ ['theme.fg("warning", "[Skill warnings]")', 'theme.fg("warning", process.env.U1S1_LANG === "en" ? "[Skill warnings]" : "[技能警告]")'],
334
+ ['` (${formatKeyText(cycleKeys.join("/"), { capitalize: true })} to cycle)`', 'process.env.U1S1_LANG === "en" ? ` (${formatKeyText(cycleKeys.join("/"), { capitalize: true })} to cycle)` : ` (按 ${formatKeyText(cycleKeys.join("/"), { capitalize: true })} 轮换)`'],
335
+ ['return theme.fg("dim", `Model scope: ${modelList}${cycleHint}`);', 'return theme.fg("dim", `${process.env.U1S1_LANG === "en" ? "Model scope" : "模型范围"}: ${modelList}${cycleHint}`);'],
336
+ ];
337
+ // 会话记录的工作目录已不存在时的选择框(Pi 1.5 新增;启动时 --session 和会话内 /resume、/import 共用)。
338
+ // 选项按 label 找回 value,标题和选项在同一个函数里一起换语言,不会对不上。
339
+ const SESSION_CWD_REPLACEMENTS = [
340
+ ['return `Session cwd does not exist: ${issue.sessionCwd}`;', 'return process.env.U1S1_LANG === "en" ? `Session cwd does not exist: ${issue.sessionCwd}` : `会话的工作目录已不存在: ${issue.sessionCwd}`;'],
341
+ ['{ label: `Continue in ${issue.fallbackCwd} (asks again next time)`, value: "continue" },', '{ label: process.env.U1S1_LANG === "en" ? `Continue in ${issue.fallbackCwd} (asks again next time)` : `在 ${issue.fallbackCwd} 继续(下次还会询问)`, value: "continue" },'],
342
+ ['{ label: `Fork into ${issue.fallbackCwd} (new session ID, original kept)`, value: "fork" },', '{ label: process.env.U1S1_LANG === "en" ? `Fork into ${issue.fallbackCwd} (new session ID, original kept)` : `分叉到 ${issue.fallbackCwd}(新会话 ID,保留原会话)`, value: "fork" },'],
343
+ ['{ label: "Cancel", value: undefined },', '{ label: process.env.U1S1_LANG === "en" ? "Cancel" : "取消", value: undefined },'],
344
+ ];
319
345
  const MCP_TEXT_REPLACEMENTS = [
320
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() 查找)\","],
321
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\","],
@@ -417,6 +443,8 @@ const TUI_TEXT_TARGETS = [
417
443
  [["modes", "interactive", "interactive-mode.js"], HOTKEYS_REPLACEMENTS, "/hotkeys"],
418
444
  [["modes", "interactive", "interactive-mode.js"], BUG_REPORT_REPLACEMENTS, "/bug 入口"],
419
445
  [["modes", "interactive", "interactive-mode.js"], LOGIN_PROVIDER_REPLACEMENTS, "/login 渠道"],
446
+ [["modes", "interactive", "interactive-mode.js"], SESSION_STATUS_REPLACEMENTS, "会话状态提示"],
447
+ [["core", "session-cwd.js"], SESSION_CWD_REPLACEMENTS, "会话目录选择框"],
420
448
  [["modes", "interactive", "components", "keybinding-hints.js"], KEY_HINT_REPLACEMENTS, "按键脚注"],
421
449
  [["modes", "interactive", "components", "model-selector.js"], MODEL_SELECTOR_REPLACEMENTS, "模型选择器"],
422
450
  [["modes", "interactive", "components", "settings-selector.js"], SETTINGS_REPLACEMENTS, "设置菜单"],