u1s1-cli 0.9.2 → 0.9.3
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/brand.js +3 -2
- package/dist/config.js +16 -1
- package/dist/index.js +18 -11
- package/dist/style.js +20 -7
- package/dist/update.js +37 -3
- package/dist/usage.js +32 -5
- package/package.json +1 -1
package/dist/brand.js
CHANGED
|
@@ -46,8 +46,9 @@ function paintArt(theme, line) {
|
|
|
46
46
|
* Startup hero, responsive to terminal width:
|
|
47
47
|
* wide → wordmark with info column beside it; medium → stacked; narrow → one-liner.
|
|
48
48
|
*/
|
|
49
|
-
export function renderBrandHeader(theme, version, cwd, width) {
|
|
50
|
-
const name = theme.bold(theme.fg("text", `${BRAND_NAME} v${version}`))
|
|
49
|
+
export function renderBrandHeader(theme, version, cwd, width, notice) {
|
|
50
|
+
const name = theme.bold(theme.fg("text", `${BRAND_NAME} v${version}`)) +
|
|
51
|
+
(notice ? ` ${theme.fg("accent", notice)}` : "");
|
|
51
52
|
const brand = theme.fg("muted", `${BRAND_CN} · ${BRAND_TAGLINE}`);
|
|
52
53
|
const dir = theme.fg("dim", `cwd: ${formatHomePath(cwd)}`);
|
|
53
54
|
const hints = theme.fg("dim", "/help 看命令 · Shift+Enter 换行 · Esc 中断");
|
package/dist/config.js
CHANGED
|
@@ -1,9 +1,24 @@
|
|
|
1
1
|
import { chmodSync, existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
2
2
|
import { createRequire } from "node:module";
|
|
3
3
|
import { homedir } from "node:os";
|
|
4
|
-
import { join } from "node:path";
|
|
4
|
+
import { basename, dirname, join } from "node:path";
|
|
5
|
+
import { fileURLToPath } from "node:url";
|
|
5
6
|
const require = createRequire(import.meta.url);
|
|
6
7
|
export const VERSION = require("../package.json").version;
|
|
8
|
+
/**
|
|
9
|
+
* 便携包安装(install.sh / install.ps1):包根旁边带自己的 node 运行时,
|
|
10
|
+
* npm 更新碰不到这份拷贝,升级只能整包重装。npm 全局安装的包在 node_modules
|
|
11
|
+
* 下,按父目录名先排除,避免撞上恰好叫 node 的目录误判。
|
|
12
|
+
*/
|
|
13
|
+
export function isPortableInstall() {
|
|
14
|
+
const pkgRoot = join(dirname(fileURLToPath(import.meta.url)), ".."); // dist/.. = 包根
|
|
15
|
+
if (basename(dirname(pkgRoot)) === "node_modules")
|
|
16
|
+
return false;
|
|
17
|
+
const portableNode = process.platform === "win32"
|
|
18
|
+
? join(pkgRoot, "..", "node", "node.exe")
|
|
19
|
+
: join(pkgRoot, "..", "node", "bin", "node");
|
|
20
|
+
return existsSync(portableNode);
|
|
21
|
+
}
|
|
7
22
|
export const DEFAULT_BASE_URL = "https://api.u1s1.io/v1";
|
|
8
23
|
export const PROVIDER_ID = "u1s1";
|
|
9
24
|
/** Make a short alias from a model id by stripping common prefixes. */
|
package/dist/index.js
CHANGED
|
@@ -3,18 +3,16 @@ import { spawnSync } from "node:child_process";
|
|
|
3
3
|
import { writeFileSync } from "node:fs";
|
|
4
4
|
import { cleanupBrandThemes, ensureBrandPrompt, ensureDefaultSettings, ensureProviderModels, writeWebToolsExtension, } from "./agent-setup.js";
|
|
5
5
|
import { printConsoleBanner } from "./brand.js";
|
|
6
|
-
import { agentDir, apiModelToDef, loadConfig, MODELS, persistPreferredModel, PROVIDER_ID, readSettings, resolvePreferredModel, setModelsFromApi, VERSION, } from "./config.js";
|
|
7
|
-
import { applyBrandUi } from "./style.js";
|
|
6
|
+
import { agentDir, apiModelToDef, isPortableInstall, loadConfig, MODELS, persistPreferredModel, PROVIDER_ID, readSettings, resolvePreferredModel, setModelsFromApi, VERSION, } from "./config.js";
|
|
7
|
+
import { applyBrandUi, setUpdateNotice } from "./style.js";
|
|
8
8
|
import { fetchModels } from "./api.js";
|
|
9
9
|
const PACKAGE_NAME = "u1s1-cli";
|
|
10
10
|
/**
|
|
11
|
-
* 启动时自动检查 npm
|
|
12
|
-
*
|
|
11
|
+
* 启动时自动检查 npm 最新版:autoUpdate 开着就静默安装,关着也在启动横幅的
|
|
12
|
+
* 版本号后面提示一句(u1s1 vX.Y.Z 后跟升级状态,见 setUpdateNotice)。
|
|
13
|
+
* 不阻塞启动流程,失败也不报错(留到手动 `u1s1 update`)。
|
|
13
14
|
*/
|
|
14
15
|
async function checkAndAutoUpdate() {
|
|
15
|
-
const settings = readSettings();
|
|
16
|
-
if (settings.autoUpdate === false)
|
|
17
|
-
return;
|
|
18
16
|
let latest;
|
|
19
17
|
try {
|
|
20
18
|
const url = `https://registry.npmjs.org/${PACKAGE_NAME}/latest`;
|
|
@@ -46,6 +44,12 @@ async function checkAndAutoUpdate() {
|
|
|
46
44
|
}
|
|
47
45
|
if (!newer)
|
|
48
46
|
return;
|
|
47
|
+
const settings = readSettings();
|
|
48
|
+
// 便携版没法用 npm 自更新;autoUpdate 关闭同理只提示。u1s1 update 会给出正确升级方式
|
|
49
|
+
if (settings.autoUpdate === false || isPortableInstall()) {
|
|
50
|
+
setUpdateNotice(`⬆ 新版 v${latest} 可用 · u1s1 update 升级`);
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
49
53
|
// 检测包管理器
|
|
50
54
|
const userAgent = process.env.npm_config_user_agent ?? "";
|
|
51
55
|
const pm = userAgent.startsWith("pnpm") ? "pnpm"
|
|
@@ -55,14 +59,17 @@ async function checkAndAutoUpdate() {
|
|
|
55
59
|
const installCmd = pm === "npm"
|
|
56
60
|
? `npm install -g ${PACKAGE_NAME}@latest`
|
|
57
61
|
: `${pm} add -g ${PACKAGE_NAME}@latest`;
|
|
62
|
+
setUpdateNotice(`⬆ 发现新版 v${latest},自动更新中…`);
|
|
58
63
|
try {
|
|
59
|
-
//
|
|
60
|
-
const {
|
|
61
|
-
|
|
62
|
-
|
|
64
|
+
// 异步安装:execSync 会卡死事件循环,TUI 打字会冻住
|
|
65
|
+
const { exec } = await import("node:child_process");
|
|
66
|
+
const { promisify } = await import("node:util");
|
|
67
|
+
await promisify(exec)(installCmd, { timeout: 60_000 });
|
|
68
|
+
setUpdateNotice(`✨ 已更新到 v${latest},重启后生效`);
|
|
63
69
|
}
|
|
64
70
|
catch {
|
|
65
71
|
// 自动更新失败不阻塞,用户可手动 `u1s1 update`
|
|
72
|
+
setUpdateNotice(`⬆ 新版 v${latest} 可用 · u1s1 update 升级`);
|
|
66
73
|
}
|
|
67
74
|
}
|
|
68
75
|
/**
|
package/dist/style.js
CHANGED
|
@@ -2,6 +2,16 @@ import { basename } from "node:path";
|
|
|
2
2
|
import { truncateToWidth } from "@earendil-works/pi-tui";
|
|
3
3
|
import { readSettings } from "./config.js";
|
|
4
4
|
import { renderBrandHeader } from "./brand.js";
|
|
5
|
+
/**
|
|
6
|
+
* 启动横幅版本号后面的升级状态(如「发现新版 v0.9.3,自动更新中…」)。
|
|
7
|
+
* checkAndAutoUpdate 异步写入;横幅已挂载时主动触发一次重绘,否则等首帧渲染。
|
|
8
|
+
*/
|
|
9
|
+
let updateNotice = "";
|
|
10
|
+
let refreshHeader;
|
|
11
|
+
export function setUpdateNotice(notice) {
|
|
12
|
+
updateNotice = notice;
|
|
13
|
+
refreshHeader?.();
|
|
14
|
+
}
|
|
5
15
|
/**
|
|
6
16
|
* Brand chrome is just the startup hero + window title; everything else
|
|
7
17
|
* (tool rows, thinking blocks, spinner) stays Pi's default UI.
|
|
@@ -13,13 +23,16 @@ export function applyBrandUi(pi, version) {
|
|
|
13
23
|
// 检查设置:关掉就不显示启动横幅
|
|
14
24
|
const settings = readSettings();
|
|
15
25
|
if (settings.showStartupBanner !== false) {
|
|
16
|
-
ctx.ui.setHeader((
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
26
|
+
ctx.ui.setHeader((tui, theme) => {
|
|
27
|
+
refreshHeader = () => tui.requestRender();
|
|
28
|
+
return {
|
|
29
|
+
render(width) {
|
|
30
|
+
// pi-tui crashes on lines wider than the terminal, so truncate defensively.
|
|
31
|
+
return renderBrandHeader(theme, version, process.cwd(), width, updateNotice).map((line) => truncateToWidth(line, width));
|
|
32
|
+
},
|
|
33
|
+
invalidate() { },
|
|
34
|
+
};
|
|
35
|
+
});
|
|
23
36
|
}
|
|
24
37
|
ctx.ui.setTitle(`u1s1 — ${basename(process.cwd())}`);
|
|
25
38
|
});
|
package/dist/update.js
CHANGED
|
@@ -1,8 +1,15 @@
|
|
|
1
1
|
import { execSync } from "node:child_process";
|
|
2
2
|
import { createRequire } from "node:module";
|
|
3
|
+
import { isPortableInstall } from "./config.js";
|
|
3
4
|
const require = createRequire(import.meta.url);
|
|
4
|
-
|
|
5
|
+
const pkg = require("../package.json");
|
|
6
|
+
export const VERSION = pkg.version;
|
|
5
7
|
export const PACKAGE_NAME = "u1s1-cli";
|
|
8
|
+
/** engines.node 里要求的最低版本(如 ">=22.19.0" → "22.19.0");解析不出返回 undefined。 */
|
|
9
|
+
function requiredNodeVersion() {
|
|
10
|
+
const m = /(\d+\.\d+\.\d+)/.exec(pkg.engines?.node ?? "");
|
|
11
|
+
return m?.[1];
|
|
12
|
+
}
|
|
6
13
|
/** Detect the package manager that installed u1s1. */
|
|
7
14
|
export function detectPackageManager() {
|
|
8
15
|
// Check common global install locations for clues
|
|
@@ -53,11 +60,38 @@ export async function update() {
|
|
|
53
60
|
console.log(`当前 v${VERSION} 已是最新版本 ✓`);
|
|
54
61
|
return;
|
|
55
62
|
}
|
|
56
|
-
const pm = detectPackageManager();
|
|
57
63
|
console.log(`发现新版本 v${latest} (当前 v${VERSION})`);
|
|
64
|
+
// 便携版:npm 装到全局目录,但 PATH 先命中便携目录,怎么更都还是旧版。
|
|
65
|
+
// 唯一正确的升级方式是重跑官网安装命令整包替换,这里不再碰 npm。
|
|
66
|
+
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");
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
const pm = detectPackageManager();
|
|
77
|
+
// 系统 Node 低于新版要求时,先说人话:推荐官网安装命令(便携包自带合适的
|
|
78
|
+
// Node,不用自己折腾)。npm 更新仍继续——引擎不匹配只是警告,装上大概率能跑。
|
|
79
|
+
const required = requiredNodeVersion();
|
|
80
|
+
if (required && compareVersions(process.versions.node, required) < 0) {
|
|
81
|
+
const cmd = process.platform === "win32"
|
|
82
|
+
? "irm https://u1s1.io/releases/install.ps1 | iex (在 PowerShell 里运行)"
|
|
83
|
+
: "curl -fsSL https://u1s1.io/releases/install.sh | bash";
|
|
84
|
+
console.log("");
|
|
85
|
+
console.log(`⚠ 你电脑的 Node.js 是 v${process.versions.node},新版 u1s1 建议 v${required} 或更新。`);
|
|
86
|
+
console.log(" 推荐用官网安装命令重装,自带合适的 Node,一步到位:");
|
|
87
|
+
console.log(` ${cmd}`);
|
|
88
|
+
console.log(" 下面仍会继续用 npm 更新;更新后如果运行异常,再用上面的命令重装即可。");
|
|
89
|
+
console.log("");
|
|
90
|
+
}
|
|
58
91
|
console.log(`正在用 ${pm} 更新 ${PACKAGE_NAME}…`);
|
|
59
92
|
try {
|
|
60
|
-
|
|
93
|
+
// npm 压掉 EBADENGINE 等警告墙,对新手只有噪音;出错时 error 仍会显示
|
|
94
|
+
const installCmd = pm === "npm" ? `npm install -g --loglevel=error ${PACKAGE_NAME}@latest` : `${pm} add -g ${PACKAGE_NAME}@latest`;
|
|
61
95
|
execSync(installCmd, { stdio: "inherit" });
|
|
62
96
|
console.log(`\n✅ 已更新到 v${latest},重启 u1s1 后生效。`);
|
|
63
97
|
}
|
package/dist/usage.js
CHANGED
|
@@ -4,6 +4,21 @@ function bar(ratio, width = 24) {
|
|
|
4
4
|
const filled = Math.round(Math.max(0, Math.min(1, ratio)) * width);
|
|
5
5
|
return "█".repeat(filled) + "░".repeat(width - filled);
|
|
6
6
|
}
|
|
7
|
+
// 与官网 app.js 的 fmtTokensCn 保持一致:万/亿单位,2 位有效数字
|
|
8
|
+
function fmtTokensCn(tokens) {
|
|
9
|
+
const t = Number(tokens);
|
|
10
|
+
if (!Number.isFinite(t) || t <= 0)
|
|
11
|
+
return "0";
|
|
12
|
+
const sig2 = (n) => {
|
|
13
|
+
const m = Math.pow(10, Math.max(0, String(Math.round(n)).length - 2));
|
|
14
|
+
return Math.round(n / m) * m;
|
|
15
|
+
};
|
|
16
|
+
if (t >= 1e8)
|
|
17
|
+
return (Math.round((t / 1e8) * 10) / 10).toLocaleString("en-US") + " 亿";
|
|
18
|
+
if (t >= 1e4)
|
|
19
|
+
return sig2(t / 1e4).toLocaleString("en-US") + " 万";
|
|
20
|
+
return sig2(t).toLocaleString("en-US");
|
|
21
|
+
}
|
|
7
22
|
export async function usage() {
|
|
8
23
|
const cfg = loadConfig();
|
|
9
24
|
if (!cfg.apiKey) {
|
|
@@ -14,16 +29,28 @@ export async function usage() {
|
|
|
14
29
|
console.error(e.message);
|
|
15
30
|
process.exit(1);
|
|
16
31
|
});
|
|
17
|
-
const remain = me.remaining_usd;
|
|
18
32
|
const freeRemain = me.daily_free_remaining_usd;
|
|
19
33
|
const freeTotal = me.daily_free_usd;
|
|
20
34
|
const freeRatio = freeTotal > 0 ? freeRemain / freeTotal : 0;
|
|
35
|
+
const tpu = me.tokens_per_usd ?? 0;
|
|
21
36
|
console.log("");
|
|
22
37
|
console.log(` 账号 ${me.email ?? "(未绑定邮箱)"}`);
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
38
|
+
if (tpu > 0) {
|
|
39
|
+
const tok = (usd) => `${fmtTokensCn(usd * tpu)} Token`;
|
|
40
|
+
console.log(` 今日免费 还剩 ${fmtTokensCn(freeRemain * tpu)} / ${tok(freeTotal)} ${bar(freeRatio)}`);
|
|
41
|
+
console.log(" DeepSeek V4 Flash · 北京时间 0 点恢复");
|
|
42
|
+
console.log(` 永久余额 ${tok(me.remaining_usd)}`);
|
|
43
|
+
console.log(` 本月已用 约 ${tok(me.mtd_usd)}($${me.mtd_usd.toFixed(2)})`);
|
|
44
|
+
console.log("");
|
|
45
|
+
console.log(" Token 数按默认模型单价折算,为约数;用更贵的模型时消耗更快。");
|
|
46
|
+
}
|
|
47
|
+
else {
|
|
48
|
+
// 老网关没下发 tokens_per_usd,退回金额显示
|
|
49
|
+
console.log(` 今日免费 $${freeRemain} / $${freeTotal} ${bar(freeRatio)}`);
|
|
50
|
+
console.log(" DeepSeek V4 Flash · 北京时间 0 点恢复");
|
|
51
|
+
console.log(` 永久余额 $${me.remaining_usd}`);
|
|
52
|
+
console.log(` 本月成本 $${me.mtd_usd}`);
|
|
53
|
+
}
|
|
27
54
|
console.log("");
|
|
28
55
|
console.log(" 免费额度每天自动恢复;邀请朋友双方各得永久加量 → https://u1s1.io/dashboard");
|
|
29
56
|
console.log("");
|