u1s1-cli 0.13.0 → 0.13.1

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.
@@ -46,6 +46,7 @@ export declare function toProviderModels(models: ModelDef[]): {
46
46
  * 这是 pi 自带 llama provider 的同款兜底,本地服务会忽略 Authorization 头。
47
47
  */
48
48
  export declare function endpointProviderEntry(ep: CustomEndpoint): Record<string, unknown>;
49
+ export declare function scrubForeignProviderEnv(): void;
49
50
  /** 把各端点密钥放进环境(TUI 直接 process.env;web 传给子进程)。 */
50
51
  export declare function endpointKeyEnv(): Record<string, string>;
51
52
  export declare function ensureProviderModels(cfg: CliConfig): void;
@@ -160,6 +160,56 @@ export function endpointProviderEntry(ep) {
160
160
  models: toProviderModels(ep.models),
161
161
  };
162
162
  }
163
+ /**
164
+ * pi 会把环境里的厂商密钥(ANTHROPIC_API_KEY 等)当作「已配置的 provider」,
165
+ * /model 里冒出一大串没在用的模型(u1s1 又拦了 /login,配了也没法管理)。
166
+ * u1s1 的口径是:官方模型 + 自定义端点,所以启动 pi 前把这批**纯 LLM 厂商**
167
+ * 密钥从 pi 进程环境里剔掉。名单来自 pi-ai env-api-keys.ts;刻意保留
168
+ * AWS_WEB_IDENTITY_TOKEN_FILE / HF_TOKEN / GOOGLE_CLOUD_API_KEY /
169
+ * CLOUDFLARE_API_KEY 这类通用凭据 —— agent 终端里的其他工具还要用。
170
+ * 注意 agent 的 Bash 子进程也继承不到被剔的变量;项目 .env 文件不受影响。
171
+ */
172
+ const FOREIGN_PROVIDER_ENV_KEYS = [
173
+ "AI_GATEWAY_API_KEY",
174
+ "ANTHROPIC_API_KEY",
175
+ "ANTHROPIC_AUTH_TOKEN",
176
+ "ANTHROPIC_OAUTH_TOKEN",
177
+ "ANT_LING_API_KEY",
178
+ "AWS_BEARER_TOKEN_BEDROCK",
179
+ "AZURE_OPENAI_API_KEY",
180
+ "BASETEN_API_KEY",
181
+ "CEREBRAS_API_KEY",
182
+ "COPILOT_GITHUB_TOKEN",
183
+ "DEEPSEEK_API_KEY",
184
+ "FIREWORKS_API_KEY",
185
+ "GEMINI_API_KEY",
186
+ "GROQ_API_KEY",
187
+ "KIMI_API_KEY",
188
+ "MINIMAX_API_KEY",
189
+ "MINIMAX_CN_API_KEY",
190
+ "MISTRAL_API_KEY",
191
+ "MOONSHOT_API_KEY",
192
+ "NVIDIA_API_KEY",
193
+ "OPENAI_API_KEY",
194
+ "OPENCODE_API_KEY",
195
+ "OPENROUTER_API_KEY",
196
+ "QWEN_TOKEN_PLAN_API_KEY",
197
+ "QWEN_TOKEN_PLAN_CN_API_KEY",
198
+ "RADIUS_API_KEY",
199
+ "TOGETHER_API_KEY",
200
+ "XAI_API_KEY",
201
+ "XIAOMI_API_KEY",
202
+ "XIAOMI_TOKEN_PLAN_AMS_API_KEY",
203
+ "XIAOMI_TOKEN_PLAN_CN_API_KEY",
204
+ "XIAOMI_TOKEN_PLAN_SGP_API_KEY",
205
+ "ZAI_API_KEY",
206
+ "ZAI_CODING_CN_API_KEY",
207
+ ];
208
+ export function scrubForeignProviderEnv() {
209
+ for (const key of FOREIGN_PROVIDER_ENV_KEYS) {
210
+ delete process.env[key];
211
+ }
212
+ }
163
213
  /** 把各端点密钥放进环境(TUI 直接 process.env;web 传给子进程)。 */
164
214
  export function endpointKeyEnv() {
165
215
  const env = {};
package/dist/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import { spawnSync } from "node:child_process";
3
3
  import { writeFileSync } from "node:fs";
4
- import { cleanupBrandThemes, endpointKeyEnv, endpointProviderEntry, ensureBrandPrompt, ensureDefaultSettings, ensureProviderModels, toProviderModels, writeWebToolsExtension, } from "./agent-setup.js";
4
+ import { cleanupBrandThemes, endpointKeyEnv, endpointProviderEntry, ensureBrandPrompt, ensureDefaultSettings, ensureProviderModels, scrubForeignProviderEnv, toProviderModels, writeWebToolsExtension, } from "./agent-setup.js";
5
5
  import { printConsoleBanner } from "./brand.js";
6
6
  import { agentDir, apiModelToDef, CUSTOM_ENDPOINTS, isPortableInstall, loadConfig, MODELS, persistPreferredModel, PROVIDER_ID, readSettings, refValid, resolvePreferredModel, setModelsFromApi, VERSION, } from "./config.js";
7
7
  import { ensureSearchTools } from "./search-tools.js";
@@ -145,6 +145,8 @@ async function runAgent(cfg, args) {
145
145
  process.env["U1S1_TOOLS_VIA_EXTENSION"] = "1";
146
146
  // 自定义端点的密钥走环境变量引用(models.json 里只有 $VAR,不落明文)
147
147
  Object.assign(process.env, endpointKeyEnv());
148
+ // 剔掉环境里的其他厂商密钥,/model 只展示 u1s1 官方模型 + 自定义端点
149
+ scrubForeignProviderEnv();
148
150
  // hide pi's own "Run pi update" banner; users should run `u1s1 update` instead
149
151
  process.env["PI_SKIP_VERSION_CHECK"] = "1";
150
152
  await searchToolsReady;
package/dist/update.js CHANGED
@@ -1,5 +1,8 @@
1
- import { execSync } from "node:child_process";
1
+ import { execSync, spawn } from "node:child_process";
2
+ import { mkdtempSync, writeFileSync } from "node:fs";
2
3
  import { createRequire } from "node:module";
4
+ import { tmpdir } from "node:os";
5
+ import { join } from "node:path";
3
6
  import { isPortableInstall } from "./config.js";
4
7
  const require = createRequire(import.meta.url);
5
8
  const pkg = require("../package.json");
@@ -49,6 +52,55 @@ export function compareVersions(a, b) {
49
52
  }
50
53
  return 0;
51
54
  }
55
+ /**
56
+ * 便携版原地升级:下载官网安装脚本并替用户执行,整包替换安装目录。
57
+ *
58
+ * - Linux/macOS:同进程跑 bash 安装脚本。替换正在运行的文件在 unix 上安全
59
+ * (旧 inode 继续存活到进程退出)。
60
+ * - Windows:正在运行的 node.exe 锁着自己,同进程装不了 —— 弹一个新的
61
+ * PowerShell 窗口跑 install.ps1(脚本会先结束安装目录里残留的 node 进程,
62
+ * 包括本进程,属预期),本进程立刻退出把文件锁让出来。
63
+ */
64
+ async function portableSelfUpdate(latest) {
65
+ if (process.platform === "win32") {
66
+ console.log(`正在打开更新窗口(升级到 v${latest})…`);
67
+ console.log("更新在新窗口里进行,本窗口会退出;装完后重新打开终端运行 u1s1 即可。");
68
+ // 命令写进临时 .ps1 再 start,避开 cmd/start 对内联命令的引号地狱;
69
+ // start 的第一个引号参数是窗口标题,必须占位,否则带空格的路径会被吞掉
70
+ const tmp = join(mkdtempSync(join(tmpdir(), "u1s1-update-")), "update.ps1");
71
+ writeFileSync(tmp, "irm https://u1s1.io/releases/install.ps1 | iex\n");
72
+ const child = spawn("cmd.exe", ["/c", "start", "u1s1", "powershell", "-NoProfile", "-ExecutionPolicy", "Bypass", "-NoExit", "-File", tmp], { detached: true, stdio: "ignore" });
73
+ child.unref();
74
+ // 立刻退出释放 node.exe 的文件锁,让安装器能替换安装目录
75
+ process.exit(0);
76
+ }
77
+ console.log(`正在下载安装脚本并升级到 v${latest}…`);
78
+ let script;
79
+ try {
80
+ const res = await fetch("https://u1s1.io/releases/install.sh", {
81
+ signal: AbortSignal.timeout(30_000),
82
+ });
83
+ if (!res.ok)
84
+ throw new Error(`HTTP ${res.status}`);
85
+ script = await res.text();
86
+ }
87
+ catch {
88
+ console.error("安装脚本下载失败。请手动运行:");
89
+ console.error(" curl -fsSL https://u1s1.io/releases/install.sh | bash");
90
+ process.exit(1);
91
+ }
92
+ const tmp = join(mkdtempSync(join(tmpdir(), "u1s1-update-")), "install.sh");
93
+ writeFileSync(tmp, script, { mode: 0o755 });
94
+ try {
95
+ execSync(`bash ${JSON.stringify(tmp)}`, { stdio: "inherit" });
96
+ console.log(`\n✅ 已升级到 v${latest},重新运行 u1s1 生效。`);
97
+ }
98
+ catch {
99
+ console.error("\n自动升级失败。请手动运行:");
100
+ console.error(" curl -fsSL https://u1s1.io/releases/install.sh | bash");
101
+ process.exit(1);
102
+ }
103
+ }
52
104
  export async function update() {
53
105
  console.log("正在检查更新…");
54
106
  const latest = await getLatestVersion();
@@ -62,15 +114,9 @@ export async function update() {
62
114
  }
63
115
  console.log(`发现新版本 v${latest} (当前 v${VERSION})`);
64
116
  // 便携版:npm 装到全局目录,但 PATH 先命中便携目录,怎么更都还是旧版。
65
- // 唯一正确的升级方式是重跑官网安装命令整包替换,这里不再碰 npm。
117
+ // 正确方式是重跑官网安装脚本整包替换 —— 这里直接替用户跑,不再让人手抄命令。
66
118
  if (isPortableInstall()) {
67
- const isWin = process.platform === "win32";
68
- console.log("");
69
- console.log("你装的是便携版(自带 Node),npm 更新不了它。升级分两步:");
70
- console.log(" 1. 关闭所有 u1s1 窗口");
71
- console.log(isWin
72
- ? " 2. 打开 PowerShell 运行: irm https://u1s1.io/releases/install.ps1 | iex"
73
- : " 2. 在终端运行: curl -fsSL https://u1s1.io/releases/install.sh | bash");
119
+ await portableSelfUpdate(latest);
74
120
  return;
75
121
  }
76
122
  const pm = detectPackageManager();
package/dist/web.js CHANGED
@@ -2,7 +2,7 @@ import { spawn, spawnSync } from "node:child_process";
2
2
  import { mkdirSync } from "node:fs";
3
3
  import { createRequire } from "node:module";
4
4
  import { dirname, join } from "node:path";
5
- import { cleanupBrandThemes, endpointKeyEnv, ensureAuthCredential, ensureBrandPrompt, ensureDefaultSettings, ensureProviderModels, writeWebToolsExtension, } from "./agent-setup.js";
5
+ import { cleanupBrandThemes, endpointKeyEnv, ensureAuthCredential, ensureBrandPrompt, ensureDefaultSettings, ensureProviderModels, scrubForeignProviderEnv, writeWebToolsExtension, } from "./agent-setup.js";
6
6
  import { agentDir, apiModelToDef, resolvePreferredModel, setModelsFromApi, u1s1Dir, writeAgentDefaultModel, } from "./config.js";
7
7
  import { ensureSearchTools } from "./search-tools.js";
8
8
  import { applyWebUiBranding, applyWebUiFrontend } from "./webui-brand.js";
@@ -49,6 +49,8 @@ export async function prepareWebEnv(cfg) {
49
49
  writeAgentDefaultModel(pref.provider, pref.id);
50
50
  const dataDir = join(u1s1Dir, "web");
51
51
  mkdirSync(dataDir, { recursive: true });
52
+ // web 子进程按 {...process.env, ...返回值} 起,这里剔掉即对子进程生效
53
+ scrubForeignProviderEnv();
52
54
  await searchToolsReady;
53
55
  return {
54
56
  PI_CODING_AGENT_DIR: agentDir,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "u1s1-cli",
3
- "version": "0.13.0",
3
+ "version": "0.13.1",
4
4
  "description": "u1s1 — 有一说一,最省心的 AI 编程搭子。终端里用中文说需求,AI 帮你读文件、改代码、跑命令。",
5
5
  "type": "module",
6
6
  "bin": {
@@ -47,8 +47,8 @@
47
47
  "pi-web-ui": "0.26.0"
48
48
  },
49
49
  "devDependencies": {
50
- "@types/node": "^22.15.0",
51
- "tsx": "^4.20.0",
52
- "typescript": "^5.9.2"
50
+ "@types/node": "^22.20.1",
51
+ "tsx": "^4.23.12",
52
+ "typescript": "^5.9.3"
53
53
  }
54
54
  }