mioku 1.1.1 → 1.2.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.
- package/dist/cli/index.cjs +318 -144
- package/dist/cli/index.cjs.map +1 -1
- package/dist/cli/index.js +318 -144
- package/dist/cli/index.js.map +1 -1
- package/dist/{exec-CoLrq043.cjs → exec-B2fmdRh2.cjs} +80 -2
- package/dist/exec-B2fmdRh2.cjs.map +1 -0
- package/dist/{exec-vSS4BZ4z.js → exec-CBlGXt5y.js} +69 -3
- package/dist/exec-CBlGXt5y.js.map +1 -0
- package/dist/index.cjs +112 -70
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +112 -70
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/dist/exec-CoLrq043.cjs.map +0 -1
- package/dist/exec-vSS4BZ4z.js.map +0 -1
package/dist/cli/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { clearCommandCache, commandExists, resolveCommand, runCommandInherit, version } from "../exec-
|
|
2
|
+
import { clearCommandCache, commandExists, compareVersions, pickHighestVersion, resolveCommand, runCommandInherit, version } from "../exec-CBlGXt5y.js";
|
|
3
3
|
import fs, { readFileSync } from "node:fs";
|
|
4
4
|
import path from "node:path";
|
|
5
5
|
import mri from "mri";
|
|
@@ -52,6 +52,10 @@ async function searchMiokuPackages(prefix) {
|
|
|
52
52
|
}
|
|
53
53
|
return hits.sort((a, b) => a.name.localeCompare(b.name));
|
|
54
54
|
}
|
|
55
|
+
/** 取官方注册表中某类别需要隐藏的包短名 */
|
|
56
|
+
function hiddenShortNames(registry, type) {
|
|
57
|
+
return new Set(registry?.hidden?.[type] ?? []);
|
|
58
|
+
}
|
|
55
59
|
async function fetchOfficialRegistry() {
|
|
56
60
|
try {
|
|
57
61
|
const res = await fetch(OFFICIAL_REGISTRY_URL, { headers: {
|
|
@@ -72,7 +76,9 @@ async function fetchNpmPackageMeta(packageName) {
|
|
|
72
76
|
} });
|
|
73
77
|
if (!res.ok) return null;
|
|
74
78
|
const data = await res.json();
|
|
75
|
-
const
|
|
79
|
+
const distTag = String(data?.["dist-tags"]?.latest || "").trim();
|
|
80
|
+
const versions = Object.keys(data?.versions ?? {});
|
|
81
|
+
const latest = distTag && versions.includes(distTag) ? distTag : pickHighestVersion(versions) ?? distTag;
|
|
76
82
|
const version$1 = latest ? data?.versions?.[latest] ?? {} : {};
|
|
77
83
|
return {
|
|
78
84
|
version: latest,
|
|
@@ -126,6 +132,15 @@ async function multiSelect(message, items, initial = [], options = {}) {
|
|
|
126
132
|
});
|
|
127
133
|
return result.map((item) => typeof item === "string" ? item : item.value);
|
|
128
134
|
}
|
|
135
|
+
/** 单选框,取消时抛出(调用方负责兜底) */
|
|
136
|
+
async function selectOne(message, items) {
|
|
137
|
+
const result = await consola.prompt(message, {
|
|
138
|
+
type: "select",
|
|
139
|
+
options: items,
|
|
140
|
+
cancel: "reject"
|
|
141
|
+
});
|
|
142
|
+
return result;
|
|
143
|
+
}
|
|
129
144
|
function runAdapterCli(name, cwd) {
|
|
130
145
|
const isWin = process.platform === "win32";
|
|
131
146
|
const exts = isWin ? [
|
|
@@ -277,11 +292,20 @@ async function getInstalledPackages(cwd) {
|
|
|
277
292
|
...pkg.dependencies,
|
|
278
293
|
...pkg.devDependencies
|
|
279
294
|
};
|
|
280
|
-
return Object.keys(deps).filter((k) => k.startsWith("mioku-"));
|
|
295
|
+
return Object.keys(deps).filter((k) => k === "mioku" || k.startsWith("mioku-"));
|
|
281
296
|
} catch {
|
|
282
297
|
return [];
|
|
283
298
|
}
|
|
284
299
|
}
|
|
300
|
+
/** 读取已安装包的版本,未安装返回 null */
|
|
301
|
+
function installedVersionOf(cwd, pkgName) {
|
|
302
|
+
try {
|
|
303
|
+
const pkg = JSON.parse(readFileSync(path.join(cwd, "node_modules", pkgName, "package.json"), "utf-8"));
|
|
304
|
+
return String(pkg.version || "") || null;
|
|
305
|
+
} catch {
|
|
306
|
+
return null;
|
|
307
|
+
}
|
|
308
|
+
}
|
|
285
309
|
function withRoot(p) {
|
|
286
310
|
return path.resolve(process.cwd(), p);
|
|
287
311
|
}
|
|
@@ -324,11 +348,14 @@ function buildHelpInfo(version$1) {
|
|
|
324
348
|
用法: mioku <命令> [选项]
|
|
325
349
|
|
|
326
350
|
命令:
|
|
327
|
-
install
|
|
328
|
-
|
|
351
|
+
install [类型] [名称] 安装包;不带参数时进入交互选择
|
|
352
|
+
install - 选择安装 mioku 框架 / 插件 / 服务 / 适配器
|
|
353
|
+
install plugin <名称> 安装插件,自动补全 mioku-plugin- 前缀
|
|
354
|
+
install service <名称> 安装服务,自动补全 mioku-service- 前缀
|
|
355
|
+
install adapter <名称> 安装适配器并运行配置向导
|
|
329
356
|
update [包名|self|all] 更新插件或服务
|
|
330
|
-
update -
|
|
331
|
-
update all - 更新所有 mioku
|
|
357
|
+
update - 检查可用更新(含 mioku 框架)并选择更新
|
|
358
|
+
update all - 更新所有 mioku 包(含框架)
|
|
332
359
|
update self - 更新 mioku 框架
|
|
333
360
|
update xxx - 更新指定包
|
|
334
361
|
|
|
@@ -364,118 +391,39 @@ async function input(message, options) {
|
|
|
364
391
|
}
|
|
365
392
|
|
|
366
393
|
//#endregion
|
|
367
|
-
//#region src/cli/
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
}
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
return
|
|
381
|
-
}
|
|
382
|
-
const prefix = type === "plugin" ? PLUGIN_PREFIX : SERVICE_PREFIX;
|
|
383
|
-
const normalized = `${prefix}${name}`;
|
|
384
|
-
if (type === "plugin") {
|
|
385
|
-
const proceed = await warnIfPrivatePlugin(normalized);
|
|
386
|
-
if (!proceed) {
|
|
387
|
-
consola.info("已取消安装");
|
|
388
|
-
return 1;
|
|
389
|
-
}
|
|
390
|
-
}
|
|
391
|
-
try {
|
|
392
|
-
execAdd([normalized], cwd);
|
|
393
|
-
consola.success(`已安装 ${normalized}`);
|
|
394
|
-
if (type === "plugin") {
|
|
395
|
-
const shortName = normalized.slice(PLUGIN_PREFIX.length);
|
|
396
|
-
if (appendToMiokuPlugins(cwd, normalized)) consola.success(`已在 mioku.plugins 中启用 ${shortName}`);
|
|
397
|
-
else consola.info(`${shortName} 已在 mioku.plugins 中,跳过`);
|
|
398
|
-
}
|
|
399
|
-
return 0;
|
|
400
|
-
} catch {
|
|
401
|
-
consola.error(`安装失败: ${normalized}`);
|
|
402
|
-
return 1;
|
|
403
|
-
}
|
|
404
|
-
}
|
|
405
|
-
async function warnIfPrivatePlugin(pkgName) {
|
|
406
|
-
const keywords = await fetchNpmKeywords(pkgName);
|
|
407
|
-
if (keywords === null) return true;
|
|
408
|
-
if (keywords.includes("mioku")) return true;
|
|
409
|
-
consola.warn(`${pkgName} 看起来是私有插件"`);
|
|
410
|
-
consola.warn("该插件未上架插件市场,可能存在风险");
|
|
411
|
-
return confirm("仍要继续安装吗?", { initial: false });
|
|
412
|
-
}
|
|
413
|
-
|
|
414
|
-
//#endregion
|
|
415
|
-
//#region src/cli/update.ts
|
|
416
|
-
async function updatePackages(packages, cwd) {
|
|
417
|
-
if (packages.length === 0) {
|
|
418
|
-
consola.info("未找到 mioku- 相关依赖");
|
|
419
|
-
return;
|
|
420
|
-
}
|
|
421
|
-
console.log(`执行: bun update ${packages.join(" ")} --latest`);
|
|
422
|
-
run("bun", [
|
|
423
|
-
"update",
|
|
424
|
-
...packages,
|
|
425
|
-
"--latest"
|
|
426
|
-
], { cwd });
|
|
427
|
-
}
|
|
428
|
-
async function updateByPrefix(prefix, cwd, name) {
|
|
429
|
-
if (name) {
|
|
430
|
-
const normalized = name.startsWith(prefix) ? name : `${prefix}${name}`;
|
|
431
|
-
console.log(`执行: bun update ${normalized} --latest`);
|
|
432
|
-
run("bun", [
|
|
433
|
-
"update",
|
|
434
|
-
normalized,
|
|
435
|
-
"--latest"
|
|
436
|
-
], { cwd });
|
|
437
|
-
return;
|
|
438
|
-
}
|
|
439
|
-
const packages = (await getInstalledPackages(cwd)).filter((p) => p.startsWith(prefix));
|
|
440
|
-
if (packages.length === 0) {
|
|
441
|
-
consola.info(`未找到 ${prefix}* 相关依赖`);
|
|
442
|
-
return;
|
|
443
|
-
}
|
|
444
|
-
await updatePackages(packages, cwd);
|
|
445
|
-
}
|
|
446
|
-
async function updateCommand(cmdArgs) {
|
|
447
|
-
ensurePackageManager();
|
|
448
|
-
const cwd = process.cwd();
|
|
449
|
-
const [target, name] = cmdArgs;
|
|
450
|
-
if (!target || target === "check") {
|
|
451
|
-
run("bun", ["update", "-i"], { cwd });
|
|
452
|
-
return 0;
|
|
453
|
-
}
|
|
454
|
-
if (target === "all") {
|
|
455
|
-
await updatePackages(await getInstalledPackages(cwd), cwd);
|
|
456
|
-
return 0;
|
|
457
|
-
}
|
|
458
|
-
if (target === "self") {
|
|
459
|
-
console.log("执行: bun update mioku --latest");
|
|
460
|
-
run("bun", [
|
|
461
|
-
"update",
|
|
462
|
-
"mioku",
|
|
463
|
-
"--latest"
|
|
464
|
-
], { cwd });
|
|
465
|
-
return 0;
|
|
466
|
-
}
|
|
467
|
-
if (target === "plugin" || target === "service") {
|
|
468
|
-
const prefix = target === "plugin" ? PLUGIN_PREFIX : SERVICE_PREFIX;
|
|
469
|
-
await updateByPrefix(prefix, cwd, name);
|
|
470
|
-
return 0;
|
|
394
|
+
//#region src/cli/market-select.ts
|
|
395
|
+
const PACKAGE_TYPE_PREFIX = {
|
|
396
|
+
plugin: PLUGIN_PREFIX,
|
|
397
|
+
service: SERVICE_PREFIX,
|
|
398
|
+
adapter: ADAPTER_PREFIX
|
|
399
|
+
};
|
|
400
|
+
/** 从 npm 拉取市场包并让用户多选;官方注册表 hidden 名单内的包不展示 */
|
|
401
|
+
async function pickMarketPackages(type, message, options = {}) {
|
|
402
|
+
const prefix = PACKAGE_TYPE_PREFIX[type];
|
|
403
|
+
console.log(`\n正在从 npm 拉取 ${prefix}* 包...`);
|
|
404
|
+
const [hits, registry] = await Promise.all([searchMiokuPackages(prefix), fetchOfficialRegistry()]);
|
|
405
|
+
if (hits.length === 0) {
|
|
406
|
+
consola.warn(`未在 npm 上找到任何 ${prefix}* 包`);
|
|
407
|
+
return [];
|
|
471
408
|
}
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
]
|
|
478
|
-
|
|
409
|
+
const official = new Set();
|
|
410
|
+
for (const group of [
|
|
411
|
+
registry?.plugins,
|
|
412
|
+
registry?.services,
|
|
413
|
+
registry?.adapters
|
|
414
|
+
]) for (const entry of Object.values(group ?? {})) if (entry?.npm) official.add(entry.npm);
|
|
415
|
+
const hidden = hiddenShortNames(registry, type);
|
|
416
|
+
const excludeSet = new Set(options.exclude ?? []);
|
|
417
|
+
const items = hits.filter((hit) => !excludeSet.has(hit.name) && !hidden.has(shortNameOfPackage(hit.name))).map((hit) => {
|
|
418
|
+
const shortName = shortNameOfPackage(hit.name);
|
|
419
|
+
const desc = hit.description || "暂无介绍";
|
|
420
|
+
const badge = official.has(hit.name) ? "官方" : "社区";
|
|
421
|
+
return {
|
|
422
|
+
label: `${shortName} (${badge} · ${desc})`,
|
|
423
|
+
value: hit.name
|
|
424
|
+
};
|
|
425
|
+
});
|
|
426
|
+
return multiSelect(message, items, [], { required: false });
|
|
479
427
|
}
|
|
480
428
|
|
|
481
429
|
//#endregion
|
|
@@ -513,30 +461,8 @@ async function createNewProject(name, fileTree) {
|
|
|
513
461
|
run("bun", ["i"], { cwd: projectPath });
|
|
514
462
|
return projectPath;
|
|
515
463
|
}
|
|
516
|
-
async function selectPackages(message,
|
|
517
|
-
|
|
518
|
-
const [hits, registry] = await Promise.all([searchMiokuPackages(prefix), fetchOfficialRegistry()]);
|
|
519
|
-
if (hits.length === 0) {
|
|
520
|
-
consola.warn(`未在 npm 上找到任何 ${prefix}* 包`);
|
|
521
|
-
return [];
|
|
522
|
-
}
|
|
523
|
-
const official = new Set();
|
|
524
|
-
for (const group of [
|
|
525
|
-
registry?.plugins,
|
|
526
|
-
registry?.services,
|
|
527
|
-
registry?.adapters
|
|
528
|
-
]) for (const entry of Object.values(group ?? {})) if (entry?.npm) official.add(entry.npm);
|
|
529
|
-
const excludeSet = new Set(options.exclude ?? []);
|
|
530
|
-
const items = hits.filter((hit) => !excludeSet.has(hit.name)).map((hit) => {
|
|
531
|
-
const shortName = shortNameOfPackage(hit.name);
|
|
532
|
-
const desc = hit.description || "暂无介绍";
|
|
533
|
-
const badge = official.has(hit.name) ? "官方" : "社区";
|
|
534
|
-
return {
|
|
535
|
-
label: `${shortName} (${badge} · ${desc})`,
|
|
536
|
-
value: hit.name
|
|
537
|
-
};
|
|
538
|
-
});
|
|
539
|
-
return multiSelect(message, items, initial, { required: false });
|
|
464
|
+
async function selectPackages(message, type, options = {}) {
|
|
465
|
+
return pickMarketPackages(type, message, options);
|
|
540
466
|
}
|
|
541
467
|
async function scaffoldCommand() {
|
|
542
468
|
const name = await input("请输入项目名称", {
|
|
@@ -551,7 +477,7 @@ async function scaffoldCommand() {
|
|
|
551
477
|
});
|
|
552
478
|
ensurePackageManager();
|
|
553
479
|
const ownersList = [...String(owners).split(",").map((o) => o.trim()).filter(Boolean), STDIN_OWNER];
|
|
554
|
-
const adapterNames = await selectPackages("选择要安装的适配器(上下键选择,空格勾选,回车确认)",
|
|
480
|
+
const adapterNames = await selectPackages("选择要安装的适配器(上下键选择,空格勾选,回车确认)", "adapter", { exclude: SYSTEM_ADAPTERS });
|
|
555
481
|
const allAdapterNames = [...SYSTEM_ADAPTERS, ...adapterNames];
|
|
556
482
|
if (adapterNames.length === 0) consola.info("未选择其他适配器,仅启用标准输入");
|
|
557
483
|
const pkgJsonObj = {
|
|
@@ -613,7 +539,7 @@ async function scaffoldCommand() {
|
|
|
613
539
|
runAdapterCli(adapterName, projectPath);
|
|
614
540
|
}
|
|
615
541
|
consola.info(`已自动安装并启用系统适配器: ${SYSTEM_ADAPTERS.map(shortNameOfPackage).join(", ")}`);
|
|
616
|
-
const pluginNames = await selectPackages("选择要安装的插件(上下键选择,空格勾选,回车确认)",
|
|
542
|
+
const pluginNames = await selectPackages("选择要安装的插件(上下键选择,空格勾选,回车确认)", "plugin", { exclude: SYSTEM_PLUGINS });
|
|
617
543
|
consola.info(`系统插件将自动安装: ${SYSTEM_PLUGINS.map(shortNameOfPackage).join(", ")}`);
|
|
618
544
|
const enabledPlugins = [
|
|
619
545
|
"demo",
|
|
@@ -644,6 +570,254 @@ async function scaffoldCommand() {
|
|
|
644
570
|
return 0;
|
|
645
571
|
}
|
|
646
572
|
|
|
573
|
+
//#endregion
|
|
574
|
+
//#region src/cli/install.ts
|
|
575
|
+
const INSTALL_TARGETS = [
|
|
576
|
+
{
|
|
577
|
+
label: "mioku 框架 —— 创建新的机器人项目(同 npx mioku)",
|
|
578
|
+
value: "mioku"
|
|
579
|
+
},
|
|
580
|
+
{
|
|
581
|
+
label: "插件 (plugin) —— 安装到当前项目",
|
|
582
|
+
value: "plugin"
|
|
583
|
+
},
|
|
584
|
+
{
|
|
585
|
+
label: "服务 (service) —— 安装到当前项目",
|
|
586
|
+
value: "service"
|
|
587
|
+
},
|
|
588
|
+
{
|
|
589
|
+
label: "适配器 (adapter) —— 安装到当前项目并运行配置向导",
|
|
590
|
+
value: "adapter"
|
|
591
|
+
}
|
|
592
|
+
];
|
|
593
|
+
async function installCommand(cmdArgs, helpInfo) {
|
|
594
|
+
const [type, name] = cmdArgs;
|
|
595
|
+
if (!type) return installInteractive();
|
|
596
|
+
if (type === "plugin" || type === "service" || type === "adapter") {
|
|
597
|
+
if (!name) return installInteractive(type);
|
|
598
|
+
ensurePackageManager();
|
|
599
|
+
return installNamed(type, name);
|
|
600
|
+
}
|
|
601
|
+
consola.error(`无效的类型 "${type}",请使用 plugin、service 或 adapter`);
|
|
602
|
+
console.log(helpInfo);
|
|
603
|
+
return 1;
|
|
604
|
+
}
|
|
605
|
+
/** 不带参数:选择安装 mioku 框架或插件/服务/适配器 */
|
|
606
|
+
async function installInteractive(type) {
|
|
607
|
+
let target;
|
|
608
|
+
if (type) target = type;
|
|
609
|
+
else try {
|
|
610
|
+
target = await selectOne("要安装什么?(上下键选择,回车确认)", INSTALL_TARGETS);
|
|
611
|
+
} catch {
|
|
612
|
+
consola.info("已取消");
|
|
613
|
+
return 0;
|
|
614
|
+
}
|
|
615
|
+
if (target === "mioku") return scaffoldCommand();
|
|
616
|
+
ensurePackageManager();
|
|
617
|
+
const cwd = process.cwd();
|
|
618
|
+
if (!fs.existsSync(path.join(cwd, "package.json"))) {
|
|
619
|
+
consola.error("当前目录不是 mioku 项目(未找到 package.json),请先运行 mioku 创建项目或切换到项目目录");
|
|
620
|
+
return 1;
|
|
621
|
+
}
|
|
622
|
+
const packages = await pickMarketPackages(target, "选择要安装的包(上下键选择,空格勾选,回车确认)");
|
|
623
|
+
if (packages.length === 0) {
|
|
624
|
+
consola.info("未选择任何包,已取消安装");
|
|
625
|
+
return 0;
|
|
626
|
+
}
|
|
627
|
+
const ok = await confirm(`确认安装以下 ${packages.length} 个包?\n${packages.map((p) => ` - ${p}`).join("\n")}`);
|
|
628
|
+
if (!ok) {
|
|
629
|
+
consola.info("已取消安装");
|
|
630
|
+
return 1;
|
|
631
|
+
}
|
|
632
|
+
return installPackages(target, packages, cwd);
|
|
633
|
+
}
|
|
634
|
+
/** 带类型和名称:直接安装指定包 */
|
|
635
|
+
async function installNamed(type, name) {
|
|
636
|
+
const cwd = process.cwd();
|
|
637
|
+
const prefix = type === "plugin" ? PLUGIN_PREFIX : type === "service" ? SERVICE_PREFIX : ADAPTER_PREFIX;
|
|
638
|
+
const normalized = name.startsWith(prefix) ? name : `${prefix}${name}`;
|
|
639
|
+
if (type === "plugin") {
|
|
640
|
+
const proceed = await warnIfPrivatePlugin(normalized);
|
|
641
|
+
if (!proceed) {
|
|
642
|
+
consola.info("已取消安装");
|
|
643
|
+
return 1;
|
|
644
|
+
}
|
|
645
|
+
const services = await resolveRequiredServices([normalized]);
|
|
646
|
+
if (services.length > 0) {
|
|
647
|
+
consola.info(`将一并安装插件声明的服务: ${services.map(shortNameOfPackage).join(", ")}`);
|
|
648
|
+
return installPackages("plugin", [normalized, ...services], cwd);
|
|
649
|
+
}
|
|
650
|
+
return installPackages("plugin", [normalized], cwd);
|
|
651
|
+
}
|
|
652
|
+
return installPackages(type, [normalized], cwd);
|
|
653
|
+
}
|
|
654
|
+
async function installPackages(type, packages, cwd) {
|
|
655
|
+
try {
|
|
656
|
+
execAdd(packages, cwd);
|
|
657
|
+
consola.success(`已安装 ${packages.join(" ")}`);
|
|
658
|
+
} catch {
|
|
659
|
+
consola.error(`安装失败: ${packages.join(" ")}`);
|
|
660
|
+
return 1;
|
|
661
|
+
}
|
|
662
|
+
if (type === "plugin") for (const pkg of packages) {
|
|
663
|
+
if (!pkg.startsWith(PLUGIN_PREFIX)) continue;
|
|
664
|
+
if (appendToMiokuPlugins(cwd, pkg)) consola.success(`已在 mioku.plugins 中启用 ${shortNameOfPackage(pkg)}`);
|
|
665
|
+
else consola.info(`${shortNameOfPackage(pkg)} 已在 mioku.plugins 中,跳过`);
|
|
666
|
+
}
|
|
667
|
+
if (type === "adapter") for (const pkg of packages) {
|
|
668
|
+
const adapterName = shortNameOfPackage(pkg);
|
|
669
|
+
consola.info(`正在运行 ${pkg} 配置向导...`);
|
|
670
|
+
runAdapterCli(adapterName, cwd);
|
|
671
|
+
}
|
|
672
|
+
return 0;
|
|
673
|
+
}
|
|
674
|
+
async function warnIfPrivatePlugin(pkgName) {
|
|
675
|
+
const keywords = await fetchNpmKeywords(pkgName);
|
|
676
|
+
if (keywords === null) return true;
|
|
677
|
+
if (keywords.includes("mioku")) return true;
|
|
678
|
+
consola.warn(`${pkgName} 看起来是私有插件"`);
|
|
679
|
+
consola.warn("该插件未上架插件市场,可能存在风险");
|
|
680
|
+
return confirm("仍要继续安装吗?", { initial: false });
|
|
681
|
+
}
|
|
682
|
+
|
|
683
|
+
//#endregion
|
|
684
|
+
//#region src/cli/update.ts
|
|
685
|
+
/** 单个包更新:latest 标签不可解析时改用 npm 上的实际最高版本 */
|
|
686
|
+
async function updateOne(name, cwd) {
|
|
687
|
+
console.log(`执行: bun update ${name} --latest`);
|
|
688
|
+
try {
|
|
689
|
+
run("bun", [
|
|
690
|
+
"update",
|
|
691
|
+
name,
|
|
692
|
+
"--latest"
|
|
693
|
+
], { cwd });
|
|
694
|
+
return true;
|
|
695
|
+
} catch {}
|
|
696
|
+
const meta = await fetchNpmPackageMeta(name);
|
|
697
|
+
const target = meta?.version ?? "";
|
|
698
|
+
if (!target) return false;
|
|
699
|
+
const current = installedVersionOf(cwd, name) ?? "0.0.0";
|
|
700
|
+
if (!isNewerVersion(target, current)) return true;
|
|
701
|
+
console.log(`执行: bun update ${name}@${target}`);
|
|
702
|
+
try {
|
|
703
|
+
run("bun", ["update", `${name}@${target}`], { cwd });
|
|
704
|
+
return true;
|
|
705
|
+
} catch {
|
|
706
|
+
return false;
|
|
707
|
+
}
|
|
708
|
+
}
|
|
709
|
+
/** 整批更新失败时逐个重试,避免一个包的 latest 标签拖垮全部 */
|
|
710
|
+
async function updatePackages(packages, cwd) {
|
|
711
|
+
if (packages.length === 0) {
|
|
712
|
+
consola.info("未找到 mioku 相关依赖");
|
|
713
|
+
return;
|
|
714
|
+
}
|
|
715
|
+
console.log(`执行: bun update ${packages.join(" ")} --latest`);
|
|
716
|
+
try {
|
|
717
|
+
run("bun", [
|
|
718
|
+
"update",
|
|
719
|
+
...packages,
|
|
720
|
+
"--latest"
|
|
721
|
+
], { cwd });
|
|
722
|
+
return;
|
|
723
|
+
} catch {
|
|
724
|
+
consola.warn("批量更新失败,改为逐个更新");
|
|
725
|
+
}
|
|
726
|
+
const failed = [];
|
|
727
|
+
for (const name of packages) if (!await updateOne(name, cwd)) failed.push(name);
|
|
728
|
+
if (failed.length > 0) {
|
|
729
|
+
consola.error(`以下包更新失败:${failed.join(", ")}`);
|
|
730
|
+
return;
|
|
731
|
+
}
|
|
732
|
+
consola.success("更新完成");
|
|
733
|
+
}
|
|
734
|
+
async function updateByPrefix(prefix, cwd, name) {
|
|
735
|
+
if (name) {
|
|
736
|
+
const normalized = name.startsWith(prefix) ? name : `${prefix}${name}`;
|
|
737
|
+
await updatePackages([normalized], cwd);
|
|
738
|
+
return;
|
|
739
|
+
}
|
|
740
|
+
const packages = (await getInstalledPackages(cwd)).filter((p) => p.startsWith(prefix));
|
|
741
|
+
if (packages.length === 0) {
|
|
742
|
+
consola.info(`未找到 ${prefix}* 相关依赖`);
|
|
743
|
+
return;
|
|
744
|
+
}
|
|
745
|
+
await updatePackages(packages, cwd);
|
|
746
|
+
}
|
|
747
|
+
/** latest 是否比 current 更新(避免把已装的新版本倒退回去) */
|
|
748
|
+
function isNewerVersion(latest, current) {
|
|
749
|
+
return compareVersions(latest, current) > 0;
|
|
750
|
+
}
|
|
751
|
+
/** 对比 npm 上的最新版本,找出所有可更新的 mioku 包(含框架本身) */
|
|
752
|
+
async function collectOutdated(cwd) {
|
|
753
|
+
const packages = await getInstalledPackages(cwd);
|
|
754
|
+
const checked = await Promise.all(packages.map(async (name) => ({
|
|
755
|
+
name,
|
|
756
|
+
current: installedVersionOf(cwd, name),
|
|
757
|
+
meta: await fetchNpmPackageMeta(name)
|
|
758
|
+
})));
|
|
759
|
+
const outdated = [];
|
|
760
|
+
for (const { name, current, meta } of checked) {
|
|
761
|
+
if (!current) continue;
|
|
762
|
+
const latest = meta?.version;
|
|
763
|
+
if (!latest || !isNewerVersion(latest, current)) continue;
|
|
764
|
+
outdated.push({
|
|
765
|
+
name,
|
|
766
|
+
current,
|
|
767
|
+
latest
|
|
768
|
+
});
|
|
769
|
+
}
|
|
770
|
+
return outdated.sort((a, b) => a.name.localeCompare(b.name));
|
|
771
|
+
}
|
|
772
|
+
/** 检查更新并列出选择框(含 mioku 框架本身),确认后更新选中项 */
|
|
773
|
+
async function updateCheckFlow(cwd) {
|
|
774
|
+
consola.info("正在检查 mioku 包更新...");
|
|
775
|
+
const outdated = await collectOutdated(cwd);
|
|
776
|
+
if (outdated.length === 0) {
|
|
777
|
+
consola.success("所有 mioku 包均已是最新版本");
|
|
778
|
+
return 0;
|
|
779
|
+
}
|
|
780
|
+
consola.info(`发现 ${outdated.length} 个可更新包`);
|
|
781
|
+
const items = outdated.map((o) => ({
|
|
782
|
+
label: `${o.name} ${o.current} → ${o.latest}`,
|
|
783
|
+
value: o.name
|
|
784
|
+
}));
|
|
785
|
+
let selected;
|
|
786
|
+
try {
|
|
787
|
+
selected = await multiSelect("选择要更新的包(空格勾选,回车确认)", items, []);
|
|
788
|
+
} catch {
|
|
789
|
+
consola.info("已取消");
|
|
790
|
+
return 0;
|
|
791
|
+
}
|
|
792
|
+
if (selected.length === 0) {
|
|
793
|
+
consola.info("未选择任何包");
|
|
794
|
+
return 0;
|
|
795
|
+
}
|
|
796
|
+
await updatePackages(selected, cwd);
|
|
797
|
+
return 0;
|
|
798
|
+
}
|
|
799
|
+
async function updateCommand(cmdArgs) {
|
|
800
|
+
ensurePackageManager();
|
|
801
|
+
const cwd = process.cwd();
|
|
802
|
+
const [target, name] = cmdArgs;
|
|
803
|
+
if (!target || target === "check") return updateCheckFlow(cwd);
|
|
804
|
+
if (target === "all") {
|
|
805
|
+
await updatePackages(await getInstalledPackages(cwd), cwd);
|
|
806
|
+
return 0;
|
|
807
|
+
}
|
|
808
|
+
if (target === "self") {
|
|
809
|
+
await updatePackages(["mioku"], cwd);
|
|
810
|
+
return 0;
|
|
811
|
+
}
|
|
812
|
+
if (target === "plugin" || target === "service") {
|
|
813
|
+
const prefix = target === "plugin" ? PLUGIN_PREFIX : SERVICE_PREFIX;
|
|
814
|
+
await updateByPrefix(prefix, cwd, name);
|
|
815
|
+
return 0;
|
|
816
|
+
}
|
|
817
|
+
await updatePackages([target], cwd);
|
|
818
|
+
return 0;
|
|
819
|
+
}
|
|
820
|
+
|
|
647
821
|
//#endregion
|
|
648
822
|
//#region src/cli/index.ts
|
|
649
823
|
const args = process.argv.slice(2);
|