u1s1-cli 1.11.8 → 1.12.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/dist/agent-setup.d.ts +5 -0
- package/dist/agent-setup.js +31 -7
- package/dist/api.js +42 -34
- package/dist/bench-report.js +12 -9
- package/dist/bench.js +34 -31
- package/dist/brand.d.ts +2 -1
- package/dist/brand.js +38 -12
- package/dist/config.d.ts +11 -1
- package/dist/config.js +25 -8
- package/dist/context-usage.js +12 -9
- package/dist/deploy.js +49 -45
- package/dist/device-auth.js +7 -5
- package/dist/error-humanize.d.ts +1 -0
- package/dist/error-humanize.js +18 -6
- package/dist/feedback.js +48 -31
- package/dist/i18n.d.ts +21 -0
- package/dist/i18n.js +46 -0
- package/dist/import/index.js +64 -28
- package/dist/import/skills.js +72 -41
- package/dist/import/util.js +3 -2
- package/dist/import/write.js +2 -1
- package/dist/index.js +139 -48
- package/dist/login.js +39 -28
- package/dist/loop.js +18 -12
- package/dist/mcp/client.js +15 -14
- package/dist/mcp/command.js +76 -48
- package/dist/mcp/extension.js +5 -3
- package/dist/mcp/tools.js +6 -5
- package/dist/model.js +11 -10
- package/dist/nudges.d.ts +8 -2
- package/dist/nudges.js +22 -3
- package/dist/search-tools.js +7 -6
- package/dist/shell-doctor.js +9 -8
- package/dist/subagent.js +5 -4
- package/dist/tools.js +53 -37
- package/dist/update.js +29 -28
- package/dist/usage.d.ts +1 -1
- package/dist/usage.js +96 -43
- package/dist/web.js +2 -1
- package/dist/workflow/runner.js +33 -29
- package/dist/workflow/templates.js +70 -45
- package/dist/workflow/tool.js +15 -12
- package/package.json +2 -2
- package/scripts/patch-pi.js +243 -278
- package/scripts/render-regression.mjs +55 -4
package/dist/import/skills.js
CHANGED
|
@@ -4,13 +4,17 @@ import { basename, join, resolve } from "node:path";
|
|
|
4
4
|
import { loadSkillsFromDir, parseFrontmatter } from "@earendil-works/pi-coding-agent";
|
|
5
5
|
import { formatHomePath } from "../brand.js";
|
|
6
6
|
import { agentDir } from "../config.js";
|
|
7
|
+
import { isEnglish, tr } from "../i18n.js";
|
|
7
8
|
import { asRecord, asString, formatBytes, listHomeClaudeDirs, oneLine, uniqueExistingDirs, confirm } from "./util.js";
|
|
8
9
|
import { readJsonIfExists, writeJson } from "./write.js";
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
10
|
+
// 用时求值:界面语言要等配置读完才定
|
|
11
|
+
function toolLabel(tool) {
|
|
12
|
+
if (tool === "claude")
|
|
13
|
+
return "Claude Code";
|
|
14
|
+
if (tool === "codex")
|
|
15
|
+
return "Codex";
|
|
16
|
+
return tr("自定义目录", "Custom directory");
|
|
17
|
+
}
|
|
14
18
|
const SCRIPT_EXTENSIONS = new Set([".sh", ".bash", ".zsh", ".ps1", ".bat", ".cmd", ".py", ".js", ".mjs", ".cjs", ".ts", ".rb", ".pl"]);
|
|
15
19
|
/** 超过这个体积的技能目录多半塞了数据集或依赖,先不动。 */
|
|
16
20
|
export const MAX_SKILL_BYTES = 20 * 1024 * 1024;
|
|
@@ -38,12 +42,12 @@ export function defaultSkillSources(cwd, home = homedir()) {
|
|
|
38
42
|
if (!dir || seen.has(dir))
|
|
39
43
|
continue;
|
|
40
44
|
seen.add(dir);
|
|
41
|
-
out.push({ tool: c.tool, label:
|
|
45
|
+
out.push({ tool: c.tool, label: toolLabel(c.tool), dir, scope: c.scope });
|
|
42
46
|
}
|
|
43
47
|
return out;
|
|
44
48
|
}
|
|
45
49
|
export function customSkillSource(dir) {
|
|
46
|
-
return { tool: "custom", label:
|
|
50
|
+
return { tool: "custom", label: toolLabel("custom"), dir: resolve(dir), scope: "custom" };
|
|
47
51
|
}
|
|
48
52
|
function walkStats(dir, stats, depth = 0) {
|
|
49
53
|
if (stats.truncated || depth > 16)
|
|
@@ -87,9 +91,9 @@ function walkStats(dir, stats, depth = 0) {
|
|
|
87
91
|
function skillHints(stats) {
|
|
88
92
|
const hints = [];
|
|
89
93
|
if (stats.scripts > 0)
|
|
90
|
-
hints.push(`含 ${stats.scripts}
|
|
94
|
+
hints.push(tr(`含 ${stats.scripts} 个脚本,只导可信来源`, `contains ${stats.scripts} script(s); only import from trusted sources`));
|
|
91
95
|
if (stats.bytes > LARGE_SKILL_BYTES)
|
|
92
|
-
hints.push(`体积 ${formatBytes(stats.bytes)}`);
|
|
96
|
+
hints.push(tr(`体积 ${formatBytes(stats.bytes)}`, `size ${formatBytes(stats.bytes)}`));
|
|
93
97
|
return hints;
|
|
94
98
|
}
|
|
95
99
|
const SKILL_NAME_RE = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
|
|
@@ -137,7 +141,8 @@ export function discoverSkills(sources) {
|
|
|
137
141
|
const singleFile = basename(skill.filePath) !== "SKILL.md";
|
|
138
142
|
const name = singleFile ? singleFileSkillName(skill.filePath, skill.name) : skill.name;
|
|
139
143
|
if (!SKILL_NAME_RE.test(name) || name.length > 64) {
|
|
140
|
-
problems.push({ path: skill.filePath, reason: `技能名「${name}
|
|
144
|
+
problems.push({ path: skill.filePath, reason: tr(`技能名「${name}」不合规:只能用小写字母、数字和连字符`, `invalid skill name "${name}": only lowercase letters, digits and hyphens are allowed`),
|
|
145
|
+
});
|
|
141
146
|
continue;
|
|
142
147
|
}
|
|
143
148
|
const stats = { files: 0, bytes: 0, scripts: 0, truncated: false };
|
|
@@ -156,7 +161,7 @@ export function discoverSkills(sources) {
|
|
|
156
161
|
if (stats.truncated) {
|
|
157
162
|
problems.push({
|
|
158
163
|
path: singleFile ? skill.filePath : skill.baseDir,
|
|
159
|
-
reason: `技能目录太大(超过 ${formatBytes(MAX_SKILL_BYTES)} 或 ${MAX_SKILL_FILES} 个文件),先跳过`,
|
|
164
|
+
reason: tr(`技能目录太大(超过 ${formatBytes(MAX_SKILL_BYTES)} 或 ${MAX_SKILL_FILES} 个文件),先跳过`, `skill directory too large (over ${formatBytes(MAX_SKILL_BYTES)} or ${MAX_SKILL_FILES} files), skipped`),
|
|
160
165
|
});
|
|
161
166
|
continue;
|
|
162
167
|
}
|
|
@@ -227,7 +232,7 @@ export function planSkillImport(skills, opts) {
|
|
|
227
232
|
export function copySkill(skill, destDir) {
|
|
228
233
|
const dest = join(destDir, skill.name);
|
|
229
234
|
if (resolve(skill.baseDir) === resolve(dest))
|
|
230
|
-
throw new Error("来源就是目标目录");
|
|
235
|
+
throw new Error(tr("来源就是目标目录", "Source is the destination directory"));
|
|
231
236
|
mkdirSync(destDir, { recursive: true });
|
|
232
237
|
rmSync(dest, { recursive: true, force: true });
|
|
233
238
|
if (skill.singleFile) {
|
|
@@ -270,10 +275,13 @@ export function runSkillImport(pending, index, destDir) {
|
|
|
270
275
|
export function removeImportedSkill(name, index, destDir) {
|
|
271
276
|
const record = index.items[name];
|
|
272
277
|
if (!record)
|
|
273
|
-
return { ok: false, reason: "不是 u1s1 import skills 导入的技能,请手动处理" };
|
|
278
|
+
return { ok: false, reason: tr("不是 u1s1 import skills 导入的技能,请手动处理", "not imported by u1s1 import skills; please handle it manually") };
|
|
274
279
|
const expected = join(destDir, name);
|
|
275
280
|
if (resolve(record.destPath) !== resolve(expected)) {
|
|
276
|
-
return {
|
|
281
|
+
return {
|
|
282
|
+
ok: false,
|
|
283
|
+
reason: tr(`记录的位置 ${record.destPath} 与技能目录不一致,请手动处理`, `recorded location ${record.destPath} doesn't match the skills directory; please handle it manually`),
|
|
284
|
+
};
|
|
277
285
|
}
|
|
278
286
|
try {
|
|
279
287
|
if (existsSync(expected) && lstatSync(expected).isDirectory())
|
|
@@ -299,7 +307,7 @@ function parseSkillsArgs(args) {
|
|
|
299
307
|
const needValue = (flag, i) => {
|
|
300
308
|
const next = args[i + 1];
|
|
301
309
|
if (!next || next.startsWith("-")) {
|
|
302
|
-
console.error(` ${flag}
|
|
310
|
+
console.error(tr(` ${flag} 后面要跟一个值`, ` ${flag} requires a value`));
|
|
303
311
|
process.exit(1);
|
|
304
312
|
}
|
|
305
313
|
return next;
|
|
@@ -321,7 +329,7 @@ function parseSkillsArgs(args) {
|
|
|
321
329
|
else if (a === "--remove")
|
|
322
330
|
opts.remove.push(needValue(a, i++));
|
|
323
331
|
else if (a.startsWith("-")) {
|
|
324
|
-
console.error(` 不认识参数 ${a}`);
|
|
332
|
+
console.error(tr(` 不认识参数 ${a}`, ` Unknown option ${a}`));
|
|
325
333
|
process.exit(1);
|
|
326
334
|
}
|
|
327
335
|
else {
|
|
@@ -342,12 +350,32 @@ function parseToolArg(raw) {
|
|
|
342
350
|
};
|
|
343
351
|
const tool = aliases[raw.toLowerCase()];
|
|
344
352
|
if (!tool) {
|
|
345
|
-
console.error(` 不认识来源「${raw}」,可选: claude / codex;其他工具用 --from
|
|
353
|
+
console.error(tr(` 不认识来源「${raw}」,可选: claude / codex;其他工具用 --from 目录`, ` Unknown source "${raw}"; choose claude / codex, or use --from <dir> for other tools`));
|
|
346
354
|
process.exit(1);
|
|
347
355
|
}
|
|
348
356
|
return tool;
|
|
349
357
|
}
|
|
350
358
|
export function printSkillsHelp() {
|
|
359
|
+
if (isEnglish()) {
|
|
360
|
+
console.log("");
|
|
361
|
+
console.log(" Import skills (SKILL.md) from Claude Code / Codex and other tools into u1s1");
|
|
362
|
+
console.log("");
|
|
363
|
+
console.log(" Usage:");
|
|
364
|
+
console.log(" u1s1 import skills Find Claude Code / Codex skills on this machine");
|
|
365
|
+
console.log(" u1s1 import skills claude Claude Code only");
|
|
366
|
+
console.log(" u1s1 import skills codex Codex only");
|
|
367
|
+
console.log(" u1s1 import skills --from <dir> Other tools: a directory containing SKILL.md files (repeatable)");
|
|
368
|
+
console.log(" u1s1 import skills --cwd <dir> Look for project skills in this directory (default: current directory)");
|
|
369
|
+
console.log(" u1s1 import skills --dry-run Show what would be imported without writing anything");
|
|
370
|
+
console.log(" u1s1 import skills --update Re-copy skills already imported (same as --force)");
|
|
371
|
+
console.log(" u1s1 import skills --list List imported skills");
|
|
372
|
+
console.log(" u1s1 import skills --remove <name> Remove an imported skill");
|
|
373
|
+
console.log("");
|
|
374
|
+
console.log(` Skills are copied to ${formatHomePath(skillsDestDir())} and are available from your next chat:`);
|
|
375
|
+
console.log(" the model invokes them automatically based on their description, or type /skill:<name> to invoke one manually.");
|
|
376
|
+
console.log("");
|
|
377
|
+
return;
|
|
378
|
+
}
|
|
351
379
|
console.log("");
|
|
352
380
|
console.log(" 把 Claude Code / Codex 等工具的技能(SKILL.md)导入 u1s1");
|
|
353
381
|
console.log("");
|
|
@@ -373,37 +401,38 @@ function printList(index) {
|
|
|
373
401
|
const items = Object.values(index.items).sort((a, b) => a.name.localeCompare(b.name));
|
|
374
402
|
console.log("");
|
|
375
403
|
if (items.length === 0) {
|
|
376
|
-
console.log(" 还没导入过技能。跑 u1s1 import skills 试试。");
|
|
404
|
+
console.log(tr(" 还没导入过技能。跑 u1s1 import skills 试试。", " No skills imported yet. Try u1s1 import skills."));
|
|
377
405
|
console.log("");
|
|
378
406
|
return;
|
|
379
407
|
}
|
|
380
|
-
console.log(` 已导入 ${items.length} 个技能(${formatHomePath(skillsDestDir())}):`);
|
|
408
|
+
console.log(tr(` 已导入 ${items.length} 个技能(${formatHomePath(skillsDestDir())}):`, ` ${items.length} imported skill(s) (${formatHomePath(skillsDestDir())}):`));
|
|
381
409
|
for (const item of items) {
|
|
382
|
-
const missing = existsSync(item.destPath) ? "" : "(目录已不在)";
|
|
383
|
-
console.log(` · ${item.name} [${
|
|
384
|
-
console.log(` 来自 ${formatHomePath(item.sourcePath)}`);
|
|
410
|
+
const missing = existsSync(item.destPath) ? "" : tr("(目录已不在)", "(directory missing)");
|
|
411
|
+
console.log(` · ${item.name} [${toolLabel(item.tool)}] ${missing}`);
|
|
412
|
+
console.log(tr(` 来自 ${formatHomePath(item.sourcePath)}`, ` from ${formatHomePath(item.sourcePath)}`));
|
|
385
413
|
}
|
|
386
414
|
console.log("");
|
|
387
415
|
}
|
|
388
416
|
function printPreview(skills, plan, problems, sources) {
|
|
389
417
|
console.log("");
|
|
390
|
-
console.log(` 在 ${sources.length} 个目录里找到 ${skills.length}
|
|
418
|
+
console.log(tr(` 在 ${sources.length} 个目录里找到 ${skills.length} 个技能:`, ` Found ${skills.length} skill(s) in ${sources.length} director${sources.length === 1 ? "y" : "ies"}:`));
|
|
391
419
|
for (const source of sources)
|
|
392
420
|
console.log(` ${formatHomePath(source.dir)} [${source.label}]`);
|
|
393
421
|
console.log("");
|
|
394
422
|
for (const skill of plan.pending) {
|
|
395
423
|
console.log(` + ${skill.name} [${skill.label}] ${oneLine(skill.description, 60)}`);
|
|
396
|
-
console.log(` ${describeSource(skill)}${skill.hints.length ? ` ⚠ ${skill.hints.join(";")}` : ""}`);
|
|
424
|
+
console.log(` ${describeSource(skill)}${skill.hints.length ? ` ⚠ ${skill.hints.join(tr(";", "; "))}` : ""}`);
|
|
397
425
|
}
|
|
398
426
|
for (const skill of plan.alreadyImported)
|
|
399
|
-
console.log(` · ${skill.name} 已导过(加 --update 重导)`);
|
|
427
|
+
console.log(tr(` · ${skill.name} 已导过(加 --update 重导)`, ` · ${skill.name} already imported (add --update to re-import)`));
|
|
400
428
|
for (const skill of plan.conflicts) {
|
|
401
|
-
console.log(` · ${skill.name} 目标目录已有同名技能(不是本命令导的)
|
|
429
|
+
console.log(tr(` · ${skill.name} 目标目录已有同名技能(不是本命令导的),不覆盖`, ` · ${skill.name} a skill with this name already exists (not imported by this command); not overwriting`));
|
|
430
|
+
}
|
|
431
|
+
for (const skill of plan.duplicates) {
|
|
432
|
+
console.log(tr(` · ${skill.name} 与前面同名,只保留先发现的(${describeSource(skill)})`, ` · ${skill.name} duplicate name; keeping the first one found (${describeSource(skill)})`));
|
|
402
433
|
}
|
|
403
|
-
for (const skill of plan.duplicates)
|
|
404
|
-
console.log(` · ${skill.name} 与前面同名,只保留先发现的(${describeSource(skill)})`);
|
|
405
434
|
for (const problem of problems) {
|
|
406
|
-
console.log(` × ${formatHomePath(problem.path)} 不兼容,跳过: ${oneLine(problem.reason, 80)}`);
|
|
435
|
+
console.log(tr(` × ${formatHomePath(problem.path)} 不兼容,跳过: ${oneLine(problem.reason, 80)}`, ` × ${formatHomePath(problem.path)} incompatible, skipped: ${oneLine(problem.reason, 80)}`));
|
|
407
436
|
}
|
|
408
437
|
}
|
|
409
438
|
export async function importSkillsCommand(args) {
|
|
@@ -418,7 +447,7 @@ export async function importSkillsCommand(args) {
|
|
|
418
447
|
for (const name of opts.remove) {
|
|
419
448
|
const result = removeImportedSkill(name, index, destDir);
|
|
420
449
|
if (result.ok)
|
|
421
|
-
console.log(` ✓ 已删除技能 ${name}`);
|
|
450
|
+
console.log(tr(` ✓ 已删除技能 ${name}`, ` ✓ Removed skill ${name}`));
|
|
422
451
|
else
|
|
423
452
|
console.log(` × ${name}: ${result.reason}`);
|
|
424
453
|
}
|
|
@@ -435,11 +464,11 @@ export async function importSkillsCommand(args) {
|
|
|
435
464
|
];
|
|
436
465
|
const missingFrom = opts.from.filter((dir) => !existsSync(dir));
|
|
437
466
|
for (const dir of missingFrom)
|
|
438
|
-
console.log(` --from 目录不存在: ${dir}`);
|
|
467
|
+
console.log(tr(` --from 目录不存在: ${dir}`, ` --from directory doesn't exist: ${dir}`));
|
|
439
468
|
if (sources.length === 0) {
|
|
440
469
|
console.log("");
|
|
441
|
-
console.log(" 没找到 Claude Code / Codex 的技能目录(~/.claude/skills、~/.codex/skills、项目里的 .claude/skills 或 .agents/skills)。");
|
|
442
|
-
console.log(" 其他工具的技能可以用 --from 指定目录,只要里面有 SKILL.md。");
|
|
470
|
+
console.log(tr(" 没找到 Claude Code / Codex 的技能目录(~/.claude/skills、~/.codex/skills、项目里的 .claude/skills 或 .agents/skills)。", " No Claude Code / Codex skill directories found (~/.claude/skills, ~/.codex/skills, or .claude/skills / .agents/skills in the project)."));
|
|
471
|
+
console.log(tr(" 其他工具的技能可以用 --from 指定目录,只要里面有 SKILL.md。", " For other tools, point --from at any directory containing SKILL.md files."));
|
|
443
472
|
console.log("");
|
|
444
473
|
return;
|
|
445
474
|
}
|
|
@@ -448,18 +477,20 @@ export async function importSkillsCommand(args) {
|
|
|
448
477
|
printPreview(skills, plan, problems, sources);
|
|
449
478
|
if (plan.pending.length === 0) {
|
|
450
479
|
console.log("");
|
|
451
|
-
console.log(skills.length === 0
|
|
480
|
+
console.log(skills.length === 0
|
|
481
|
+
? tr(" 这些目录里没有能用的技能。", " No usable skills in these directories.")
|
|
482
|
+
: tr(" 没有新的可导。想重导一遍就加 --update。", " Nothing new to import. Add --update to re-import."));
|
|
452
483
|
console.log("");
|
|
453
484
|
return;
|
|
454
485
|
}
|
|
455
486
|
if (opts.dryRun) {
|
|
456
487
|
console.log("");
|
|
457
|
-
console.log(` 预演结束,以上 ${plan.pending.length}
|
|
488
|
+
console.log(tr(` 预演结束,以上 ${plan.pending.length} 个还没真正导入。`, ` Dry run complete; the ${plan.pending.length} skill(s) above were not imported.`));
|
|
458
489
|
console.log("");
|
|
459
490
|
return;
|
|
460
491
|
}
|
|
461
|
-
if (!opts.yes && !(await confirm(` 导入这 ${plan.pending.length} 个技能?(回车=好 / n=取消) `))) {
|
|
462
|
-
console.log(" 已取消。");
|
|
492
|
+
if (!opts.yes && !(await confirm(tr(` 导入这 ${plan.pending.length} 个技能?(回车=好 / n=取消) `, ` Import these ${plan.pending.length} skill(s)? (Enter = yes / n = cancel) `)))) {
|
|
493
|
+
console.log(tr(" 已取消。", " Canceled."));
|
|
463
494
|
return;
|
|
464
495
|
}
|
|
465
496
|
const results = runSkillImport(plan.pending, index, destDir);
|
|
@@ -467,12 +498,12 @@ export async function importSkillsCommand(args) {
|
|
|
467
498
|
const imported = results.filter((r) => r.status === "imported").length;
|
|
468
499
|
const errors = results.filter((r) => r.status === "error");
|
|
469
500
|
console.log("");
|
|
470
|
-
console.log(` ✓ 导入 ${imported} 个技能到 ${formatHomePath(destDir)}${errors.length ? `,失败 ${errors.length}` : ""}
|
|
501
|
+
console.log(tr(` ✓ 导入 ${imported} 个技能到 ${formatHomePath(destDir)}${errors.length ? `,失败 ${errors.length}` : ""}。`, ` ✓ Imported ${imported} skill(s) to ${formatHomePath(destDir)}${errors.length ? `, ${errors.length} failed` : ""}.`));
|
|
471
502
|
for (const item of errors)
|
|
472
|
-
console.log(` × ${item.name}: ${item.detail ?? "失败"}`);
|
|
503
|
+
console.log(` × ${item.name}: ${item.detail ?? tr("失败", "failed")}`);
|
|
473
504
|
if (imported > 0) {
|
|
474
|
-
console.log(" 下次进 u1s1 对话就能用:模型会按说明自动调用,或输入 /skill:名称 手动调用。");
|
|
475
|
-
console.log(
|
|
505
|
+
console.log(tr(" 下次进 u1s1 对话就能用:模型会按说明自动调用,或输入 /skill:名称 手动调用。", " Available from your next u1s1 chat: the model invokes them based on their description, or type /skill:<name>."));
|
|
506
|
+
console.log(tr(" 管理: u1s1 import skills --list / --update / --remove 名称", " Manage: u1s1 import skills --list / --update / --remove <name>"));
|
|
476
507
|
}
|
|
477
508
|
console.log("");
|
|
478
509
|
}
|
package/dist/import/util.js
CHANGED
|
@@ -3,6 +3,7 @@ import { stdin as input, stdout as output } from "node:process";
|
|
|
3
3
|
import { closeSync, existsSync, openSync, readSync, readdirSync, realpathSync, statSync } from "node:fs";
|
|
4
4
|
import { homedir } from "node:os";
|
|
5
5
|
import { join } from "node:path";
|
|
6
|
+
import { tr } from "../i18n.js";
|
|
6
7
|
export const MAX_TOOL_RESULT_CHARS = 80_000;
|
|
7
8
|
export const MAX_TEXT_CHARS = 200_000;
|
|
8
9
|
export const PREVIEW_TITLE_CHARS = 48;
|
|
@@ -133,11 +134,11 @@ export function truncateText(text, max) {
|
|
|
133
134
|
if (text.length <= max)
|
|
134
135
|
return text;
|
|
135
136
|
const omitted = text.length - max;
|
|
136
|
-
return `${text.slice(0, max)}\n\n[已截断,原文还有约 ${formatBytes(omitted)} ]`;
|
|
137
|
+
return `${text.slice(0, max)}\n\n${tr(`[已截断,原文还有约 ${formatBytes(omitted)} ]`, `[truncated, about ${formatBytes(omitted)} more omitted]`)}`;
|
|
137
138
|
}
|
|
138
139
|
export function formatBytes(n) {
|
|
139
140
|
if (n < 1024)
|
|
140
|
-
return `${n}
|
|
141
|
+
return tr(`${n} 字`, `${n} B`);
|
|
141
142
|
if (n < 1024 * 1024)
|
|
142
143
|
return `${(n / 1024).toFixed(1)} KB`;
|
|
143
144
|
return `${(n / (1024 * 1024)).toFixed(1)} MB`;
|
package/dist/import/write.js
CHANGED
|
@@ -2,6 +2,7 @@ import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
|
2
2
|
import { dirname } from "node:path";
|
|
3
3
|
import { SessionManager } from "@earendil-works/pi-coding-agent";
|
|
4
4
|
import { agentDir, loadConfig, resolvePreferredModel } from "../config.js";
|
|
5
|
+
import { tr } from "../i18n.js";
|
|
5
6
|
const EMPTY_USAGE = {
|
|
6
7
|
input: 0,
|
|
7
8
|
output: 0,
|
|
@@ -78,7 +79,7 @@ export function writeConvertedSession(converted, destCwd) {
|
|
|
78
79
|
sm.appendModelChange(pref.provider, pref.id);
|
|
79
80
|
const dest = sm.getSessionFile();
|
|
80
81
|
if (!dest)
|
|
81
|
-
throw new Error("会话写盘失败");
|
|
82
|
+
throw new Error(tr("会话写盘失败", "Failed to write session to disk"));
|
|
82
83
|
return dest;
|
|
83
84
|
}
|
|
84
85
|
export function readJsonIfExists(path) {
|