u1s1-cli 1.2.4 → 1.2.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.
@@ -6,7 +6,7 @@ const BRAND_APPEND = `## u1s1
6
6
 
7
7
  You are u1s1 — a plain-spoken AI coding buddy for programming beginners. Most users are unfamiliar with jargon:
8
8
  - When introducing yourself or asked "who are you", say you are u1s1, never "pi"; only mention the underlying pi engine if the user explicitly asks about internals.
9
- - Reply in Chinese by default; keep code, commands, and verbatim error messages in English.
9
+ - Reply in the language of the user's latest request by default. If that is ambiguous, follow the conversation language, then the UI locale when available, and otherwise use Chinese. Never infer language from IP or location. Keep code, commands, and verbatim error messages in English.
10
10
  - Explain problems in plain language without jargon; use a one-sentence analogy when helpful.
11
11
  - Before making changes, briefly state what you plan to do; afterwards summarize what changed in one or two sentences.
12
12
  - If the user's request is vague, assume the most likely intent and confirm it instead of asking a long list of questions.
package/dist/embed.d.ts CHANGED
@@ -7,5 +7,5 @@ export { agentDir, loadConfig, saveConfig, u1s1Dir, VERSION, type CliConfig, } f
7
7
  export { fetchMe, fetchModels } from "./api.js";
8
8
  export { apiOrigin, pollDeviceLogin, startDeviceLogin, type DeviceStart } from "./login.js";
9
9
  export { prepareWebEnv } from "./web.js";
10
- export { applyWebUiBranding, applyWebUiFrontend } from "./webui-brand.js";
10
+ export { applyWebUiBranding } from "./webui-brand.js";
11
11
  export { DASHBOARD_URL } from "./brand.js";
package/dist/embed.js CHANGED
@@ -7,5 +7,5 @@ export { agentDir, loadConfig, saveConfig, u1s1Dir, VERSION, } from "./config.js
7
7
  export { fetchMe, fetchModels } from "./api.js";
8
8
  export { apiOrigin, pollDeviceLogin, startDeviceLogin } from "./login.js";
9
9
  export { prepareWebEnv } from "./web.js";
10
- export { applyWebUiBranding, applyWebUiFrontend } from "./webui-brand.js";
10
+ export { applyWebUiBranding } from "./webui-brand.js";
11
11
  export { DASHBOARD_URL } from "./brand.js";
@@ -8,7 +8,8 @@
8
8
  *
9
9
  * 整个探测是异步的:调用方先拿 Promise 与拉模型列表等网络请求并行,进 pi 前
10
10
  * 再 await——首启的 where/验活成本(Defender 首扫 bash.exe 可达秒级)藏进
11
- * 网络等待里。验活结果持久化到 shellPath,之后每次启动只花 existsSync。
11
+ * 网络等待里。Git Bash 验活结果持久化到 shellPath,之后每次启动只花
12
+ * existsSync;缓存的是 WSL 时仍会检查 PATH,以便发现自定义目录的 Git。
12
13
  */
13
14
  export type ShellDoctorResult = {
14
15
  status: "posix";
@@ -23,4 +24,6 @@ export type ShellDoctorResult = {
23
24
  } | {
24
25
  status: "unknown";
25
26
  };
27
+ /** 从 git.exe 路径反推 Git Bash 候选;用 win32 保证非 Windows CI 也能覆盖路径规则。 */
28
+ export declare function gitBashPathsFromGitExecutables(gitPaths: string[], skip: string[], pathExists?: (path: string) => boolean): string[];
26
29
  export declare function ensureUsableShell(): Promise<ShellDoctorResult>;
@@ -1,6 +1,6 @@
1
1
  import { spawn } from "node:child_process";
2
2
  import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
3
- import { dirname, join } from "node:path";
3
+ import { join, win32 } from "node:path";
4
4
  import { agentDir, agentSettingsFile } from "./config.js";
5
5
  /** System32/Sysnative 的 bash.exe 是 WSL 转发器,不是真 bash(与 pi 同款判断)。 */
6
6
  function isWslRelayBash(p) {
@@ -91,21 +91,38 @@ function knownGitBashPaths() {
91
91
  }
92
92
  return out;
93
93
  }
94
- /** 慢路径候选:where git.exe 反推安装根,兜住 portable 等非常规安装。 */
95
- async function discoveredGitBashPaths(skip) {
94
+ /** git.exe 路径反推 Git Bash 候选;用 win32 保证非 Windows CI 也能覆盖路径规则。 */
95
+ export function gitBashPathsFromGitExecutables(gitPaths, skip, pathExists = existsSync) {
96
96
  const out = [];
97
97
  // git.exe 在 <root>\cmd、<root>\bin 或 <root>\mingw64\bin 下,向上找 root
98
- for (const gitPath of await runWhere("git.exe")) {
99
- const dir = dirname(gitPath);
100
- for (const root of [dirname(dir), dirname(dirname(dir))]) {
101
- for (const p of [join(root, "bin", "bash.exe"), join(root, "usr", "bin", "bash.exe")]) {
102
- if (!out.includes(p) && !skip.includes(p) && existsSync(p))
98
+ for (const gitPath of gitPaths) {
99
+ const dir = win32.dirname(gitPath);
100
+ for (const root of [win32.dirname(dir), win32.dirname(win32.dirname(dir))]) {
101
+ for (const p of [win32.join(root, "bin", "bash.exe"), win32.join(root, "usr", "bin", "bash.exe")]) {
102
+ if (!out.includes(p) && !skip.includes(p) && pathExists(p))
103
103
  out.push(p);
104
104
  }
105
105
  }
106
106
  }
107
107
  return out;
108
108
  }
109
+ /** 慢路径候选:where git.exe 反推安装根,兜住 portable 等非常规安装。 */
110
+ async function discoveredGitBashPaths(skip) {
111
+ return gitBashPathsFromGitExecutables(await runWhere("git.exe"), skip);
112
+ }
113
+ /** 固定位置与 PATH 反推共用同一验活流程,缓存 WSL 和首次探测都走这里。 */
114
+ async function findUsableGitBash() {
115
+ const known = knownGitBashPaths();
116
+ for (const candidate of known) {
117
+ if (await bashWorks(candidate))
118
+ return candidate;
119
+ }
120
+ for (const candidate of await discoveredGitBashPaths(known)) {
121
+ if (await bashWorks(candidate))
122
+ return candidate;
123
+ }
124
+ return undefined;
125
+ }
109
126
  /** 持久化写 settings.json:写入时现读现改,避免覆盖并行流程刚写的其他键。 */
110
127
  function persistShellPath(p) {
111
128
  let settings = {};
@@ -139,17 +156,16 @@ export async function ensureUsableShell() {
139
156
  }
140
157
  }
141
158
  // 已配置且文件还在:信它,不每次启动都探活(坏了 pi 会在会话里报错)。
142
- // 配置的是 WSL 转发器时,零成本看一眼 Git Bash 是否新装了,装了就换轨。
159
+ // 配置的是 WSL 转发器时,同时检查固定位置与 PATH 里的 Git 安装,找到就换轨。
143
160
  const configured = settings["shellPath"];
144
161
  if (typeof configured === "string" && configured && existsSync(configured)) {
145
162
  if (!isWslRelayBash(configured))
146
163
  return { status: "bash", shellPath: configured };
147
- for (const candidate of knownGitBashPaths()) {
148
- if (await bashWorks(candidate)) {
149
- persistShellPath(candidate);
150
- console.error(` 检测到 Git Bash,命令改回 Windows 本机执行: ${candidate}`);
151
- return { status: "bash", shellPath: candidate };
152
- }
164
+ const candidate = await findUsableGitBash();
165
+ if (candidate) {
166
+ persistShellPath(candidate);
167
+ console.error(` 检测到 Git Bash,命令改回 Windows 本机执行: ${candidate}`);
168
+ return { status: "bash", shellPath: candidate };
153
169
  }
154
170
  adviseInstallGitBash();
155
171
  return { status: "wsl", shellPath: configured };
@@ -163,20 +179,14 @@ export async function ensureUsableShell() {
163
179
  }
164
180
  return { status: "bash", shellPath: candidate };
165
181
  };
166
- const known = knownGitBashPaths();
167
- for (const candidate of known) {
168
- if (await bashWorks(candidate))
169
- return adopt(candidate);
170
- }
171
- for (const candidate of await discoveredGitBashPaths(known)) {
172
- if (await bashWorks(candidate))
173
- return adopt(candidate);
174
- }
182
+ const gitBash = await findUsableGitBash();
183
+ if (gitBash)
184
+ return adopt(gitBash);
175
185
  // 没有 Git Bash:看 PATH 上还有什么(与 pi 的兜底一致,但这里要验活)
176
186
  const fallback = (await runWhere("bash.exe")).find((p) => existsSync(p));
177
187
  if (fallback && (await bashWorks(fallback))) {
178
188
  // WSL 转发器也持久化:冷启 WSL 验活要好几秒,不能每次启动都付。装了
179
- // Git Bash 后的换轨由上面的 configured-WSL 分支负责(零成本 existsSync)。
189
+ // Git Bash 后的换轨由上面的 configured-WSL 分支负责。
180
190
  persistShellPath(fallback);
181
191
  if (isWslRelayBash(fallback)) {
182
192
  adviseInstallGitBash();
@@ -1,9 +1,2 @@
1
- /**
2
- * u1s1 自有前端(packages/webui,Codex 布局复刻)整体替换 pi-web-ui 的
3
- * web/dist。产物随 CLI 包发布在 <cliPkg>/webui-dist;index.html 内容一致
4
- * 说明已同步过,跳过。目标目录先整体删除再拷贝 —— 断开 pnpm 硬链接,
5
- * 也顺带清掉上游的旧 assets。返回 false 表示产物缺失(回退到品牌补丁)。
6
- */
7
- export declare function applyWebUiFrontend(binPath: string): boolean;
8
1
  /** binPath = <pkg>/bin/pi-web-ui.mjs → 前端产物在 <pkg>/web/dist。 */
9
2
  export declare function applyWebUiBranding(binPath: string): void;
@@ -1,33 +1,5 @@
1
- import { cpSync, existsSync, readFileSync, rmSync, writeFileSync } from "node:fs";
1
+ import { existsSync, readFileSync, rmSync, writeFileSync } from "node:fs";
2
2
  import { dirname, join } from "node:path";
3
- import { fileURLToPath } from "node:url";
4
- /**
5
- * u1s1 自有前端(packages/webui,Codex 布局复刻)整体替换 pi-web-ui 的
6
- * web/dist。产物随 CLI 包发布在 <cliPkg>/webui-dist;index.html 内容一致
7
- * 说明已同步过,跳过。目标目录先整体删除再拷贝 —— 断开 pnpm 硬链接,
8
- * 也顺带清掉上游的旧 assets。返回 false 表示产物缺失(回退到品牌补丁)。
9
- */
10
- export function applyWebUiFrontend(binPath) {
11
- const src = fileURLToPath(new URL("../webui-dist/", import.meta.url));
12
- const srcIndex = join(src, "index.html");
13
- if (!existsSync(srcIndex))
14
- return false;
15
- const dist = join(dirname(binPath), "..", "web", "dist");
16
- const distIndex = join(dist, "index.html");
17
- try {
18
- if (existsSync(distIndex) &&
19
- readFileSync(distIndex, "utf8") === readFileSync(srcIndex, "utf8")) {
20
- return true;
21
- }
22
- rmSync(dist, { recursive: true, force: true });
23
- cpSync(src, dist, { recursive: true });
24
- return true;
25
- }
26
- catch (e) {
27
- console.error(" 前端替换失败,退回默认界面:", e.message);
28
- return false;
29
- }
30
- }
31
3
  /**
32
4
  * pi-web-ui 前端产物的启动时品牌补丁。不 fork 上游,只在 Desktop App
33
5
  * 启动前改它 web/dist 里的静态文件:重写 <title>、换 favicon、注入
@@ -41,9 +13,9 @@ export function applyWebUiFrontend(binPath) {
41
13
  */
42
14
  const PATCH_MARK = "u1s1-brand-v2";
43
15
  const PAGE_TITLE = "u1s1 Desktop App — 有一说一";
44
- const FAVICON_SVG = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64">
45
- <rect width="64" height="64" rx="14" fill="#101418"/>
46
- <text x="32" y="42" font-family="ui-monospace,Menlo,Consolas,monospace" font-size="26" font-weight="700" fill="#4ea1ff" text-anchor="middle">u1</text>
16
+ const FAVICON_SVG = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100">
17
+ <rect width="100" height="100" rx="18" fill="#e8442e"/>
18
+ <text x="50" y="68" font-family="ui-monospace,Menlo,Consolas,monospace" font-size="52" font-weight="700" fill="#faf5ec" text-anchor="middle">u1</text>
47
19
  </svg>
48
20
  `;
49
21
  /** 页面 chrome 品牌替换:改 .brand-logo/.brand-name,盯住 title(React 可能改回去)。 */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "u1s1-cli",
3
- "version": "1.2.4",
3
+ "version": "1.2.5",
4
4
  "description": "u1s1 — 有一说一,最省心的 AI 编程搭子。终端里用中文说需求,AI 帮你读文件、改代码、跑命令。",
5
5
  "type": "module",
6
6
  "bin": {