u1s1-cli 1.2.3 → 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.
- package/dist/agent-setup.js +3 -3
- package/dist/api.d.ts +5 -1
- package/dist/api.js +6 -6
- package/dist/bench-report.d.ts +22 -0
- package/dist/bench-report.js +158 -0
- package/dist/bench-scoring.d.ts +8 -0
- package/dist/bench-scoring.js +101 -0
- package/dist/bench-types.d.ts +50 -0
- package/dist/bench-types.js +1 -0
- package/dist/bench.js +101 -367
- package/dist/brand.d.ts +9 -3
- package/dist/brand.js +12 -12
- package/dist/deploy.js +72 -49
- package/dist/embed.d.ts +1 -1
- package/dist/embed.js +1 -1
- package/dist/import/claude.d.ts +2 -1
- package/dist/import/claude.js +162 -129
- package/dist/import/codex.d.ts +2 -1
- package/dist/import/codex.js +184 -157
- package/dist/import/index.js +62 -47
- package/dist/index.js +13 -13
- package/dist/shell-doctor.d.ts +4 -1
- package/dist/shell-doctor.js +35 -25
- package/dist/style.js +7 -1
- package/dist/subagent.d.ts +6 -1
- package/dist/subagent.js +4 -4
- package/dist/tools.d.ts +18 -12
- package/dist/tools.js +22 -16
- package/dist/usage.js +95 -74
- package/dist/webui-brand.d.ts +0 -7
- package/dist/webui-brand.js +4 -32
- package/dist/workflow/runner.js +36 -26
- package/dist/workflow/tool.js +67 -55
- package/package.json +1 -1
- package/scripts/render-regression.mjs +3 -1
package/dist/shell-doctor.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { spawn } from "node:child_process";
|
|
2
2
|
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
3
|
-
import {
|
|
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
|
-
/**
|
|
95
|
-
|
|
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
|
|
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) &&
|
|
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
|
|
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
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
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
|
|
167
|
-
|
|
168
|
-
|
|
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
|
|
189
|
+
// Git Bash 后的换轨由上面的 configured-WSL 分支负责。
|
|
180
190
|
persistShellPath(fallback);
|
|
181
191
|
if (isWslRelayBash(fallback)) {
|
|
182
192
|
adviseInstallGitBash();
|
package/dist/style.js
CHANGED
|
@@ -34,7 +34,13 @@ export function applyBrandUi(pi, version) {
|
|
|
34
34
|
return {
|
|
35
35
|
render(width) {
|
|
36
36
|
// pi-tui crashes on lines wider than the terminal, so truncate defensively.
|
|
37
|
-
return renderBrandHeader(theme,
|
|
37
|
+
return renderBrandHeader(theme, {
|
|
38
|
+
version,
|
|
39
|
+
cwd: process.cwd(),
|
|
40
|
+
width,
|
|
41
|
+
notice: updateNotice,
|
|
42
|
+
announcement,
|
|
43
|
+
}).map((line) => truncateToWidth(line, width));
|
|
38
44
|
},
|
|
39
45
|
invalidate() { },
|
|
40
46
|
};
|
package/dist/subagent.d.ts
CHANGED
|
@@ -41,4 +41,9 @@ export declare function runSubagent(opts: SubagentOptions): Promise<SubagentOutc
|
|
|
41
41
|
* 固定并发的工作池:limit 个工人依次领任务跑完一个补位一个。
|
|
42
42
|
* worker 抛错由调用方在 fn 内部兜住;signal 中止后不再领新任务。
|
|
43
43
|
*/
|
|
44
|
-
export declare function runPool<T>(
|
|
44
|
+
export declare function runPool<T>(input: {
|
|
45
|
+
items: readonly T[];
|
|
46
|
+
limit: number;
|
|
47
|
+
run: (item: T, index: number) => Promise<void>;
|
|
48
|
+
signal?: AbortSignal;
|
|
49
|
+
}): Promise<void>;
|
package/dist/subagent.js
CHANGED
|
@@ -119,15 +119,15 @@ export async function runSubagent(opts) {
|
|
|
119
119
|
* 固定并发的工作池:limit 个工人依次领任务跑完一个补位一个。
|
|
120
120
|
* worker 抛错由调用方在 fn 内部兜住;signal 中止后不再领新任务。
|
|
121
121
|
*/
|
|
122
|
-
export async function runPool(
|
|
122
|
+
export async function runPool(input) {
|
|
123
123
|
let next = 0;
|
|
124
124
|
const worker = async () => {
|
|
125
125
|
for (;;) {
|
|
126
126
|
const i = next++;
|
|
127
|
-
if (i >= items.length || signal?.aborted)
|
|
127
|
+
if (i >= input.items.length || input.signal?.aborted)
|
|
128
128
|
return;
|
|
129
|
-
await
|
|
129
|
+
await input.run(input.items[i], i);
|
|
130
130
|
}
|
|
131
131
|
};
|
|
132
|
-
await Promise.all(Array.from({ length: Math.max(1, Math.min(limit, items.length)) }, worker));
|
|
132
|
+
await Promise.all(Array.from({ length: Math.max(1, Math.min(input.limit, input.items.length)) }, worker));
|
|
133
133
|
}
|
package/dist/tools.d.ts
CHANGED
|
@@ -4,18 +4,24 @@ import { Type } from "typebox";
|
|
|
4
4
|
import type { CliConfig } from "./config.js";
|
|
5
5
|
export declare function truncate(text: string): string;
|
|
6
6
|
/** 联网工具通用渲染:调用行不占位,收起时只显一行摘要,ctrl+o 展开全文,出错显一行 ✗ */
|
|
7
|
-
export declare function compactResultRender(
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
|
|
7
|
+
export declare function compactResultRender(input: {
|
|
8
|
+
result: {
|
|
9
|
+
content: Array<{
|
|
10
|
+
type: string;
|
|
11
|
+
text?: string;
|
|
12
|
+
}>;
|
|
13
|
+
};
|
|
14
|
+
options: {
|
|
15
|
+
expanded: boolean;
|
|
16
|
+
};
|
|
17
|
+
theme: {
|
|
18
|
+
fg: (color: any, text: string) => string;
|
|
19
|
+
};
|
|
20
|
+
context: {
|
|
21
|
+
isError: boolean;
|
|
22
|
+
};
|
|
23
|
+
summaryLine: string;
|
|
24
|
+
}): Text;
|
|
19
25
|
/** 联网搜索工具:走 u1s1 网关代理,上游 key 不落到用户机器上。 */
|
|
20
26
|
export declare function createSearchTool(cfg: Pick<CliConfig, "baseUrl" | "apiKey">): import("@earendil-works/pi-coding-agent").ToolDefinition<Type.TObject<{
|
|
21
27
|
query: Type.TString;
|
package/dist/tools.js
CHANGED
|
@@ -88,10 +88,8 @@ function resultText(result) {
|
|
|
88
88
|
return c && c.type === "text" ? (c.text ?? "") : "";
|
|
89
89
|
}
|
|
90
90
|
/** 联网工具通用渲染:调用行不占位,收起时只显一行摘要,ctrl+o 展开全文,出错显一行 ✗ */
|
|
91
|
-
export function compactResultRender(
|
|
92
|
-
|
|
93
|
-
// 实际传入的就是 pi 的 Theme,取值只用 "warning"/"muted" 两色
|
|
94
|
-
theme, context, summaryLine) {
|
|
91
|
+
export function compactResultRender(input) {
|
|
92
|
+
const { result, options, theme, context, summaryLine } = input;
|
|
95
93
|
if (context.isError) {
|
|
96
94
|
// 低调处理:✗ 用警示色,内容用灰色弱化(与内置工具的错误展示一致)
|
|
97
95
|
const msg = previewLine(resultText(result) || "error", 160);
|
|
@@ -123,14 +121,15 @@ export function createSearchTool(cfg) {
|
|
|
123
121
|
renderCall() {
|
|
124
122
|
return new Text("", 0, 0);
|
|
125
123
|
},
|
|
126
|
-
renderResult(
|
|
124
|
+
renderResult(...args) {
|
|
125
|
+
const [result, options, theme, context] = args;
|
|
127
126
|
const d = result.details;
|
|
128
127
|
const q = d?.query ? `"${previewLine(d.query, 80)}"` : "…";
|
|
129
128
|
const n = typeof d?.count === "number" ? ` · ${d.count} 条结果` : "";
|
|
130
|
-
return compactResultRender(result, options, theme, context, `🔍 搜索 ${q}${n}`);
|
|
129
|
+
return compactResultRender({ result, options, theme, context, summaryLine: `🔍 搜索 ${q}${n}` });
|
|
131
130
|
},
|
|
132
131
|
async execute(_toolCallId, params, signal) {
|
|
133
|
-
const data = await searchWeb(cfg, params.query, params.maxResults, signal);
|
|
132
|
+
const data = await searchWeb(cfg, { query: params.query, maxResults: params.maxResults, signal });
|
|
134
133
|
const lines = [];
|
|
135
134
|
if (data.answer)
|
|
136
135
|
lines.push(`Answer: ${data.answer}`, "");
|
|
@@ -187,14 +186,15 @@ export function createSubagentTool(getParentModel) {
|
|
|
187
186
|
renderCall() {
|
|
188
187
|
return new Text("", 0, 0);
|
|
189
188
|
},
|
|
190
|
-
renderResult(
|
|
189
|
+
renderResult(...args) {
|
|
190
|
+
const [result, options, theme, context] = args;
|
|
191
191
|
const d = result.details;
|
|
192
192
|
const summary = d && typeof d.count === "number"
|
|
193
193
|
? `🤖 子任务 ×${d.count}` +
|
|
194
194
|
(typeof d.ok === "number" && d.failed ? ` · ✓${d.ok} ✗${d.failed}` : "") +
|
|
195
195
|
(typeof d.seconds === "number" ? ` · ${d.seconds}s` : "")
|
|
196
196
|
: "🤖 子任务";
|
|
197
|
-
return compactResultRender(result, options, theme, context, summary);
|
|
197
|
+
return compactResultRender({ result, options, theme, context, summaryLine: summary });
|
|
198
198
|
},
|
|
199
199
|
async execute(_toolCallId, params, signal) {
|
|
200
200
|
const list = (params.tasks?.length ? params.tasks : params.task ? [params.task] : [])
|
|
@@ -210,11 +210,16 @@ export function createSubagentTool(getParentModel) {
|
|
|
210
210
|
const prevFlag = process.env.U1S1_IN_SUBAGENT;
|
|
211
211
|
process.env.U1S1_IN_SUBAGENT = "1";
|
|
212
212
|
const started = Date.now();
|
|
213
|
-
const outcomes =
|
|
213
|
+
const outcomes = Array.from({ length: list.length });
|
|
214
214
|
try {
|
|
215
|
-
await runPool(
|
|
216
|
-
|
|
217
|
-
|
|
215
|
+
await runPool({
|
|
216
|
+
items: list,
|
|
217
|
+
limit: SUBAGENT_CONCURRENCY,
|
|
218
|
+
run: async (task, i) => {
|
|
219
|
+
outcomes[i] = await runSubagent({ task, parentModel, model: params.model, timeoutMs, signal }).catch((e) => ({ ok: false, text: e.message, usage: { totalTokens: 0, costUsd: 0 } }));
|
|
220
|
+
},
|
|
221
|
+
signal,
|
|
222
|
+
});
|
|
218
223
|
}
|
|
219
224
|
finally {
|
|
220
225
|
if (prevFlag === undefined)
|
|
@@ -458,12 +463,13 @@ export function createFetchTool(cfg) {
|
|
|
458
463
|
renderCall() {
|
|
459
464
|
return new Text("", 0, 0);
|
|
460
465
|
},
|
|
461
|
-
renderResult(
|
|
466
|
+
renderResult(...args) {
|
|
467
|
+
const [result, options, theme, context] = args;
|
|
462
468
|
const d = result.details;
|
|
463
469
|
const u = previewLine(d?.url ?? "…", 90);
|
|
464
470
|
const n = typeof d?.chars === "number" ? ` · ${d.chars} 字符` : "";
|
|
465
471
|
const cloud = d?.via === "render" ? " · ☁ 浏览器渲染" : "";
|
|
466
|
-
return compactResultRender(result, options, theme, context, `🌐 ${u}${n}${cloud}`);
|
|
472
|
+
return compactResultRender({ result, options, theme, context, summaryLine: `🌐 ${u}${n}${cloud}` });
|
|
467
473
|
},
|
|
468
474
|
async execute(_toolCallId, params, signal) {
|
|
469
475
|
let url;
|
|
@@ -492,7 +498,7 @@ export function createFetchTool(cfg) {
|
|
|
492
498
|
return ok(await renderViaGateway(url, signal), "render", "text/markdown");
|
|
493
499
|
}
|
|
494
500
|
catch (re) {
|
|
495
|
-
throw new Error(`${e.message};云端浏览器渲染也失败: ${re.message}
|
|
501
|
+
throw new Error(`${e.message};云端浏览器渲染也失败: ${re.message}`, { cause: re });
|
|
496
502
|
}
|
|
497
503
|
}
|
|
498
504
|
// 直连 200 但正文近乎空:大概率是 JS 渲染的 SPA,换浏览器再试;渲染失败就退回空壳
|
package/dist/usage.js
CHANGED
|
@@ -20,89 +20,91 @@ function fmtTokensCn(tokens) {
|
|
|
20
20
|
return sig2(t / 1e4).toLocaleString("en-US") + " 万";
|
|
21
21
|
return sig2(t).toLocaleString("en-US");
|
|
22
22
|
}
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
23
|
+
const PACKAGE_LABELS = {
|
|
24
|
+
free_first: "首月免费包",
|
|
25
|
+
free_yearly: "年度免费包",
|
|
26
|
+
invite: "邀请赠送",
|
|
27
|
+
new_user: "新用户赠送",
|
|
28
|
+
login_checkin: "登录打卡",
|
|
29
|
+
payment_delay_gift: "临时加量包",
|
|
30
|
+
topup_daily: "每日加量包",
|
|
31
|
+
admin_grant: "官方赠送",
|
|
32
|
+
};
|
|
33
|
+
function sumOptional(a, b) {
|
|
34
|
+
return a == null && b == null ? null : (a ?? 0) + (b ?? 0);
|
|
35
|
+
}
|
|
36
|
+
function groupPackages(packages) {
|
|
37
|
+
const groups = new Map();
|
|
38
|
+
for (const pkg of packages) {
|
|
39
|
+
const key = `${pkg.kind}|${pkg.scope}|${pkg.daily_tokens != null}|${pkg.expires_at || ""}`;
|
|
40
|
+
const group = groups.get(key);
|
|
41
|
+
if (!group) {
|
|
42
|
+
groups.set(key, { ...pkg, count: 1 });
|
|
43
|
+
continue;
|
|
44
|
+
}
|
|
45
|
+
group.count += 1;
|
|
46
|
+
group.daily_tokens = sumOptional(group.daily_tokens, pkg.daily_tokens);
|
|
47
|
+
group.total_tokens = sumOptional(group.total_tokens, pkg.total_tokens);
|
|
48
|
+
group.remaining += pkg.remaining;
|
|
28
49
|
}
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
50
|
+
return [...groups.values()];
|
|
51
|
+
}
|
|
52
|
+
function packageScopeNote(pkg) {
|
|
53
|
+
if (pkg.kind === "login_checkin") {
|
|
54
|
+
return "仅限 u1s1 客户端使用 · 全模型可用";
|
|
55
|
+
}
|
|
56
|
+
return pkg.scope === "free"
|
|
57
|
+
? "免费包适用模型 · 0 点恢复"
|
|
58
|
+
: "仅限 u1s1 客户端使用 · 免费包适用模型";
|
|
59
|
+
}
|
|
60
|
+
function packageLabel(pkg) {
|
|
61
|
+
const count = pkg.count > 1 ? ` ×${pkg.count}` : "";
|
|
62
|
+
return `${PACKAGE_LABELS[pkg.kind] ?? pkg.kind}${count}`.padEnd(5, " ");
|
|
63
|
+
}
|
|
64
|
+
function printPackage(pkg) {
|
|
65
|
+
const isDaily = pkg.daily_tokens != null;
|
|
66
|
+
const total = isDaily ? (pkg.daily_tokens ?? 0) : (pkg.total_tokens ?? 0);
|
|
67
|
+
const ratio = total > 0 ? pkg.remaining / total : 0;
|
|
68
|
+
console.log(` ${packageLabel(pkg)} 还剩 ${fmtTokensCn(pkg.remaining)} / ${fmtTokensCn(total)}${isDaily ? "/天" : ""} ${bar(ratio)}`);
|
|
69
|
+
const expiry = pkg.expires_at ? `${pkg.expires_at.slice(0, 10)} 到期` : "永不过期";
|
|
70
|
+
console.log(` ${packageScopeNote(pkg)} · ${expiry}`);
|
|
71
|
+
}
|
|
72
|
+
function printPackageUsage(me, tokensPerUsd) {
|
|
73
|
+
const packages = me.packages ?? [];
|
|
74
|
+
if (packages.length === 0)
|
|
75
|
+
console.log(" 用量包 (无生效中的用量包)");
|
|
76
|
+
for (const pkg of groupPackages(packages))
|
|
77
|
+
printPackage(pkg);
|
|
78
|
+
if (me.bonus_balance_usd > 0) {
|
|
79
|
+
const balance = tokensPerUsd > 0
|
|
80
|
+
? `${fmtTokensCn(me.bonus_balance_usd * tokensPerUsd)} Token`
|
|
81
|
+
: `$${me.bonus_balance_usd.toFixed(2)}`;
|
|
82
|
+
console.log(` 余额(按量) ${balance}`);
|
|
83
|
+
}
|
|
84
|
+
const monthlyUsage = tokensPerUsd > 0
|
|
85
|
+
? `约 ${fmtTokensCn(me.mtd_usd * tokensPerUsd)} Token($${me.mtd_usd.toFixed(2)})`
|
|
86
|
+
: `$${me.mtd_usd.toFixed(2)}`;
|
|
87
|
+
console.log(` 本月已用 ${monthlyUsage}`);
|
|
88
|
+
console.log("");
|
|
89
|
+
console.log(" 免费用量包覆盖 DeepSeek V4 Flash / Vision、GLM-5.3 Flash、Qwen3.8 Flash 和联网搜索。");
|
|
90
|
+
console.log(" DeepSeek V4 Pro 仅可使用余额 → https://u1s1.io/dashboard");
|
|
91
|
+
console.log(" Token 数按默认模型单价折算,为约数;用更贵的模型时消耗更快。");
|
|
92
|
+
console.log("");
|
|
93
|
+
}
|
|
94
|
+
function printLegacyUsage(me, tokensPerUsd) {
|
|
33
95
|
const freeRemain = me.daily_free_remaining_usd;
|
|
34
96
|
const freeTotal = me.daily_free_usd;
|
|
35
97
|
const freeRatio = freeTotal > 0 ? freeRemain / freeTotal : 0;
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
if (me.packages) {
|
|
40
|
-
// 新网关:按用量包逐个展示,token 原生数字
|
|
41
|
-
const PKG_LABEL = {
|
|
42
|
-
free_first: "首月免费包",
|
|
43
|
-
free_yearly: "年度免费包",
|
|
44
|
-
invite: "邀请赠送",
|
|
45
|
-
new_user: "新用户赠送",
|
|
46
|
-
payment_delay_gift: "临时加量包",
|
|
47
|
-
topup_daily: "每日加量包",
|
|
48
|
-
admin_grant: "官方赠送",
|
|
49
|
-
};
|
|
50
|
-
if (me.packages.length === 0)
|
|
51
|
-
console.log(" 用量包 (无生效中的用量包)");
|
|
52
|
-
// 同种类、同范围、同有效期的包合并成一条展示(比如邀请多个好友拿到的多份邀请赠送)
|
|
53
|
-
const groups = new Map();
|
|
54
|
-
for (const p of me.packages) {
|
|
55
|
-
const key = `${p.kind}|${p.scope}|${p.daily_tokens != null}|${p.expires_at || ""}`;
|
|
56
|
-
const g = groups.get(key);
|
|
57
|
-
// 可空字段求和:两边都是 null 就保持 null,别把「一次性包没有日额度」污染成 0
|
|
58
|
-
const sumOpt = (a, b) => (a == null && b == null ? null : (a ?? 0) + (b ?? 0));
|
|
59
|
-
if (g) {
|
|
60
|
-
g.count += 1;
|
|
61
|
-
g.daily_tokens = sumOpt(g.daily_tokens, p.daily_tokens);
|
|
62
|
-
g.total_tokens = sumOpt(g.total_tokens, p.total_tokens);
|
|
63
|
-
g.remaining += p.remaining;
|
|
64
|
-
}
|
|
65
|
-
else {
|
|
66
|
-
groups.set(key, { ...p, count: 1 });
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
for (const p of groups.values()) {
|
|
70
|
-
const daily = p.daily_tokens != null;
|
|
71
|
-
const total = daily ? (p.daily_tokens ?? 0) : (p.total_tokens ?? 0);
|
|
72
|
-
const ratio = total > 0 ? p.remaining / total : 0;
|
|
73
|
-
const label = ((PKG_LABEL[p.kind] ?? p.kind) + (p.count > 1 ? ` ×${p.count}` : "")).padEnd(5, " ");
|
|
74
|
-
const per = daily ? "/天" : "";
|
|
75
|
-
console.log(` ${label} 还剩 ${fmtTokensCn(p.remaining)} / ${fmtTokensCn(total)}${per} ${bar(ratio)}`);
|
|
76
|
-
const scopeNote = p.kind === "login_checkin"
|
|
77
|
-
? "仅限 u1s1 客户端使用 · 全模型可用"
|
|
78
|
-
: p.scope === "free"
|
|
79
|
-
? "免费包适用模型 · 0 点恢复"
|
|
80
|
-
: "仅限 u1s1 客户端使用 · 免费包适用模型";
|
|
81
|
-
console.log(` ${scopeNote} · ${p.expires_at ? `${p.expires_at.slice(0, 10)} 到期` : "永不过期"}`);
|
|
82
|
-
}
|
|
83
|
-
if (me.bonus_balance_usd > 0) {
|
|
84
|
-
const balText = tpu > 0 ? `${fmtTokensCn(me.bonus_balance_usd * tpu)} Token` : `$${me.bonus_balance_usd.toFixed(2)}`;
|
|
85
|
-
console.log(` 余额(按量) ${balText}`);
|
|
86
|
-
}
|
|
87
|
-
console.log(` 本月已用 ${tpu > 0 ? `约 ${fmtTokensCn(me.mtd_usd * tpu)} Token($${me.mtd_usd.toFixed(2)})` : `$${me.mtd_usd.toFixed(2)}`}`);
|
|
88
|
-
console.log("");
|
|
89
|
-
console.log(" 免费用量包覆盖 DeepSeek V4 Flash / Vision、GLM-5.3 Flash、Qwen3.8 Flash 和联网搜索。");
|
|
90
|
-
console.log(" DeepSeek V4 Pro 仅可使用余额 → https://u1s1.io/dashboard");
|
|
91
|
-
console.log(" Token 数按默认模型单价折算,为约数;用更贵的模型时消耗更快。");
|
|
92
|
-
console.log("");
|
|
93
|
-
return;
|
|
94
|
-
}
|
|
95
|
-
if (tpu > 0) {
|
|
96
|
-
const tok = (usd) => `${fmtTokensCn(usd * tpu)} Token`;
|
|
97
|
-
console.log(` 今日免费 还剩 ${fmtTokensCn(freeRemain * tpu)} / ${tok(freeTotal)} ${bar(freeRatio)}`);
|
|
98
|
+
if (tokensPerUsd > 0) {
|
|
99
|
+
const tokens = (usd) => `${fmtTokensCn(usd * tokensPerUsd)} Token`;
|
|
100
|
+
console.log(` 今日免费 还剩 ${fmtTokensCn(freeRemain * tokensPerUsd)} / ${tokens(freeTotal)} ${bar(freeRatio)}`);
|
|
98
101
|
console.log(" DeepSeek V4 Flash · 北京时间 0 点恢复");
|
|
99
|
-
console.log(` 永久余额 ${
|
|
100
|
-
console.log(` 本月已用 约 ${
|
|
102
|
+
console.log(` 永久余额 ${tokens(me.remaining_usd)}`);
|
|
103
|
+
console.log(` 本月已用 约 ${tokens(me.mtd_usd)}($${me.mtd_usd.toFixed(2)})`);
|
|
101
104
|
console.log("");
|
|
102
105
|
console.log(" Token 数按默认模型单价折算,为约数;用更贵的模型时消耗更快。");
|
|
103
106
|
}
|
|
104
107
|
else {
|
|
105
|
-
// 老网关没下发 tokens_per_usd,退回金额显示
|
|
106
108
|
console.log(` 今日免费 $${freeRemain.toFixed(2)} / $${freeTotal.toFixed(2)} ${bar(freeRatio)}`);
|
|
107
109
|
console.log(" DeepSeek V4 Flash · 北京时间 0 点恢复");
|
|
108
110
|
console.log(` 永久余额 $${me.remaining_usd.toFixed(2)}`);
|
|
@@ -112,3 +114,22 @@ export async function usage() {
|
|
|
112
114
|
console.log(" 免费额度每天自动恢复;邀请朋友双方各得永久加量 → https://u1s1.io/dashboard");
|
|
113
115
|
console.log("");
|
|
114
116
|
}
|
|
117
|
+
export async function usage() {
|
|
118
|
+
const cfg = loadConfig();
|
|
119
|
+
if (!hasDeviceCredential(cfg)) {
|
|
120
|
+
console.error("还没登录,先跑 u1s1 login");
|
|
121
|
+
process.exit(1);
|
|
122
|
+
}
|
|
123
|
+
const me = await fetchMe(cfg).catch((e) => {
|
|
124
|
+
console.error(e.message);
|
|
125
|
+
process.exit(1);
|
|
126
|
+
});
|
|
127
|
+
const tokensPerUsd = me.tokens_per_usd ?? 0;
|
|
128
|
+
console.log("");
|
|
129
|
+
console.log(` 账号 ${me.email ?? "(未绑定邮箱)"}`);
|
|
130
|
+
if (me.packages) {
|
|
131
|
+
printPackageUsage(me, tokensPerUsd);
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
printLegacyUsage(me, tokensPerUsd);
|
|
135
|
+
}
|
package/dist/webui-brand.d.ts
CHANGED
|
@@ -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;
|
package/dist/webui-brand.js
CHANGED
|
@@ -1,33 +1,5 @@
|
|
|
1
|
-
import {
|
|
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
|
|
45
|
-
<rect width="
|
|
46
|
-
<text x="
|
|
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/dist/workflow/runner.js
CHANGED
|
@@ -189,12 +189,12 @@ export async function runWorkflow(opts) {
|
|
|
189
189
|
store.append({ key, task: task.slice(0, 120), ok: true, text: out.text, ms: Date.now() - t0 });
|
|
190
190
|
return out.text;
|
|
191
191
|
}
|
|
192
|
-
catch (
|
|
192
|
+
catch (error) {
|
|
193
193
|
stats.failed++;
|
|
194
194
|
progress.failed = stats.failed;
|
|
195
|
-
const msg =
|
|
195
|
+
const msg = error.message || "未知错误";
|
|
196
196
|
store.append({ key, task: task.slice(0, 120), ok: false, text: msg, ms: Date.now() - t0 });
|
|
197
|
-
throw new Error(msg);
|
|
197
|
+
throw new Error(msg, { cause: error });
|
|
198
198
|
}
|
|
199
199
|
finally {
|
|
200
200
|
if (worktreeDir)
|
|
@@ -221,15 +221,20 @@ export async function runWorkflow(opts) {
|
|
|
221
221
|
async function parallel(thunks) {
|
|
222
222
|
if (!Array.isArray(thunks))
|
|
223
223
|
throw new Error("parallel() 需要一个 thunk 数组");
|
|
224
|
-
const results =
|
|
225
|
-
await runPool(
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
224
|
+
const results = [];
|
|
225
|
+
await runPool({
|
|
226
|
+
items: thunks,
|
|
227
|
+
limit: SUBAGENT_CONCURRENCY,
|
|
228
|
+
run: async (thunk, i) => {
|
|
229
|
+
results[i] = await Promise.resolve()
|
|
230
|
+
.then(thunk)
|
|
231
|
+
.catch((e) => {
|
|
232
|
+
logs.push(`✗ 任务 ${i + 1} 失败:${e.message}`);
|
|
233
|
+
return null;
|
|
234
|
+
});
|
|
235
|
+
},
|
|
236
|
+
signal: internal.signal,
|
|
237
|
+
});
|
|
233
238
|
return results;
|
|
234
239
|
}
|
|
235
240
|
/** 无屏障流水线:每个 item 独立穿过所有 stage,item 失败只废自己。 */
|
|
@@ -238,21 +243,26 @@ export async function runWorkflow(opts) {
|
|
|
238
243
|
throw new Error("pipeline() 第一个参数需要数组");
|
|
239
244
|
if (!Array.isArray(stages) || stages.length === 0)
|
|
240
245
|
throw new Error("pipeline() 第二个参数需要非空 stage 数组");
|
|
241
|
-
const results =
|
|
242
|
-
await runPool(
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
246
|
+
const results = [];
|
|
247
|
+
await runPool({
|
|
248
|
+
items: items,
|
|
249
|
+
limit: SUBAGENT_CONCURRENCY,
|
|
250
|
+
run: async (item, i) => {
|
|
251
|
+
let current = item;
|
|
252
|
+
for (let s = 0; s < stages.length; s++) {
|
|
253
|
+
try {
|
|
254
|
+
current = await stages[s](current);
|
|
255
|
+
}
|
|
256
|
+
catch (e) {
|
|
257
|
+
logs.push(`✗ item ${i + 1} 在第 ${s + 1} 阶段失败:${e.message}`);
|
|
258
|
+
current = null;
|
|
259
|
+
break;
|
|
260
|
+
}
|
|
252
261
|
}
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
262
|
+
results[i] = current;
|
|
263
|
+
},
|
|
264
|
+
signal: internal.signal,
|
|
265
|
+
});
|
|
256
266
|
return results;
|
|
257
267
|
}
|
|
258
268
|
// 沙箱:只有编排原语 + 受限 log/timer,标准 JS 内建(Promise/JSON/Math…)天然可用
|