mioku 1.2.0 → 1.2.2

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/index.js CHANGED
@@ -1,4 +1,4 @@
1
- import { buildSpawnPlan, commandExists, resolveCommand, runCommand, runCommandInherit, version } from "./exec-JBR-IZk0.js";
1
+ import { buildSpawnPlan, commandExists, compareVersions, pickHighestVersion, resolveCommand, runCommand, runCommandInherit, version } from "./exec-txJ9YlfD.js";
2
2
  import { createRequire } from "node:module";
3
3
  import * as fs$5 from "node:fs";
4
4
  import * as fs$4 from "node:fs";
@@ -450,7 +450,7 @@ var MessageImpl = class extends Array {
450
450
  return this.map((seg) => seg.toString()).join("");
451
451
  }
452
452
  text() {
453
- return this.raw_message ?? this.filterByType("text").map((seg) => String(seg.data.text ?? "")).join("");
453
+ return this.filterByType("text").map((seg) => String(seg.data.text ?? "")).join("");
454
454
  }
455
455
  filterByType(type) {
456
456
  return this.filter((seg) => seg.type === type);
@@ -3770,28 +3770,36 @@ function getInstalledVersion(pkgName) {
3770
3770
  const pkg = readPackageJson$1(path$6.join(projectRoot(), "node_modules", pkgName));
3771
3771
  return String(pkg?.version || "0.0.0");
3772
3772
  }
3773
- function listInstalledPackages() {
3774
- const modulesPath = path$6.join(projectRoot(), "node_modules");
3773
+ /** 项目 package.json 声明的 mioku 依赖,不含被 hoist 上来的传递依赖 */
3774
+ function listDeclaredPackages() {
3775
+ const pkg = readPackageJson$1(projectRoot());
3776
+ if (!pkg) return [];
3777
+ const deps = {
3778
+ ...pkg.dependencies ?? {},
3779
+ ...pkg.devDependencies ?? {},
3780
+ ...pkg.optionalDependencies ?? {}
3781
+ };
3775
3782
  const result = [];
3776
- if (!fs$5.existsSync(modulesPath)) return result;
3777
- const entries = fs$5.readdirSync(modulesPath, { withFileTypes: true });
3778
- for (const entry of entries) {
3779
- const type = detectType(entry.name);
3783
+ for (const name of Object.keys(deps)) {
3784
+ const type = detectType(name);
3780
3785
  if (!type) continue;
3781
- const fullPath = path$6.join(modulesPath, entry.name);
3782
- const stat = fs$5.lstatSync(fullPath);
3783
- if (!stat.isDirectory() && !stat.isSymbolicLink()) continue;
3784
- const pkg = readPackageJson$1(fullPath);
3786
+ const fullPath = path$6.join(projectRoot(), "node_modules", name);
3787
+ const installed = readPackageJson$1(fullPath);
3788
+ if (!installed) continue;
3785
3789
  result.push({
3786
- name: entry.name,
3790
+ name,
3787
3791
  type,
3788
- shortName: shortNameOf(entry.name),
3789
- version: String(pkg?.version || "0.0.0"),
3792
+ shortName: shortNameOf(name),
3793
+ version: String(installed.version || "0.0.0"),
3790
3794
  path: fullPath
3791
3795
  });
3792
3796
  }
3793
3797
  return result.sort((a, b) => a.name.localeCompare(b.name));
3794
3798
  }
3799
+ /** .update all 的作用范围:声明的插件与服务,不含框架与传递依赖 */
3800
+ function listManagedPackages() {
3801
+ return listDeclaredPackages().filter((pkg) => pkg.type !== "framework");
3802
+ }
3795
3803
  async function fetchJson(url) {
3796
3804
  const res = await fetch(url, { headers: {
3797
3805
  Accept: "application/json",
@@ -3803,8 +3811,13 @@ async function fetchJson(url) {
3803
3811
  async function fetchNpmMeta(pkgName) {
3804
3812
  try {
3805
3813
  const data = await fetchJson(`${NPM_REGISTRY}/${encodeURIComponent(pkgName)}`);
3806
- const latest = String(data?.["dist-tags"]?.latest || "").trim();
3807
- const version$2 = latest ? data?.versions?.[latest] || {} : {};
3814
+ const versions = Object.keys(data?.versions ?? {});
3815
+ const distTag = String(data?.["dist-tags"]?.latest || "").trim();
3816
+ const staleTag = distTag !== "" && !versions.includes(distTag);
3817
+ const latest = staleTag ? pickHighestVersion(versions) ?? distTag : distTag || (pickHighestVersion(versions) ?? "");
3818
+ if (!latest) return null;
3819
+ if (staleTag) rootLogger.debug(`[core] ${pkgName} 的 latest 标签 ${distTag} 不存在,改用 ${latest}`);
3820
+ const version$2 = data?.versions?.[latest] || {};
3808
3821
  const repository = version$2?.repository || data?.repository;
3809
3822
  let repoUrl = "";
3810
3823
  if (typeof repository === "string") repoUrl = repository;
@@ -3812,6 +3825,8 @@ async function fetchNpmMeta(pkgName) {
3812
3825
  repoUrl = repoUrl.replace(/^git\+/, "").replace(/\.git$/, "");
3813
3826
  return {
3814
3827
  latest,
3828
+ distTag,
3829
+ staleTag,
3815
3830
  description: String(version$2?.description || data?.description || "").trim(),
3816
3831
  keywords: Array.isArray(version$2?.keywords) ? version$2.keywords : [],
3817
3832
  homepage: String(version$2?.homepage || data?.homepage || "").trim(),
@@ -3824,7 +3839,7 @@ async function fetchNpmMeta(pkgName) {
3824
3839
  }
3825
3840
  }
3826
3841
  async function checkUpdates() {
3827
- const installed = listInstalledPackages();
3842
+ const installed = listDeclaredPackages();
3828
3843
  const metas = await Promise.all(installed.map(async (pkg) => {
3829
3844
  const meta = await fetchNpmMeta(pkg.name);
3830
3845
  return {
@@ -3835,7 +3850,7 @@ async function checkUpdates() {
3835
3850
  const updates = [];
3836
3851
  for (const { pkg, meta } of metas) {
3837
3852
  if (!meta || !meta.latest) continue;
3838
- if (meta.latest === pkg.version) continue;
3853
+ if (compareVersions(meta.latest, pkg.version) <= 0) continue;
3839
3854
  updates.push({
3840
3855
  name: pkg.name,
3841
3856
  type: pkg.type,
@@ -3846,53 +3861,72 @@ async function checkUpdates() {
3846
3861
  }
3847
3862
  return updates;
3848
3863
  }
3864
+ /** 取 bun 输出里最有用的一行:优先 error: 行,去掉颜色与前缀,超长截断 */
3865
+ function errorSummary(text$1) {
3866
+ const lines = String(text$1 ?? "").replace(/\u001b\[[0-9;]*m/g, "").split("\n").map((line$1) => line$1.trim()).filter(Boolean);
3867
+ const line = (lines.find((item) => /^error:/i.test(item)) ?? lines[0] ?? "更新失败").replace(/^error:\s*/i, "");
3868
+ return line.length > 160 ? `${line.slice(0, 157)}...` : line;
3869
+ }
3849
3870
  function snapshotVersions(names) {
3850
3871
  const map = new Map();
3851
3872
  for (const name of names) map.set(name, getInstalledVersion(name));
3852
3873
  return map;
3853
3874
  }
3875
+ function outcomeOf(name, before, ok = true, error) {
3876
+ const prev = before.get(name) ?? "0.0.0";
3877
+ const after = getInstalledVersion(name);
3878
+ return {
3879
+ name,
3880
+ ok,
3881
+ before: prev,
3882
+ after,
3883
+ changed: prev !== after,
3884
+ error
3885
+ };
3886
+ }
3887
+ /** latest 标签不可解析时,改用 npm 上真实存在的最高版本 */
3888
+ async function updateWithPinnedVersion(name) {
3889
+ const meta = await fetchNpmMeta(name);
3890
+ const target = meta?.latest ?? "";
3891
+ if (!target) return false;
3892
+ if (compareVersions(target, getInstalledVersion(name)) <= 0) return true;
3893
+ rootLogger.info(`[core] ${name} 的 latest 标签不可用,改用 ${target} 更新`);
3894
+ const result = await runBun(["update", `${name}@${target}`]);
3895
+ return result.code === 0;
3896
+ }
3897
+ async function updateOne(name, before) {
3898
+ const single = await runBun([
3899
+ "update",
3900
+ name,
3901
+ "--latest"
3902
+ ]);
3903
+ if (single.code === 0) return outcomeOf(name, before);
3904
+ if (await updateWithPinnedVersion(name)) return outcomeOf(name, before);
3905
+ return outcomeOf(name, before, false, errorSummary(single.stderr || single.stdout));
3906
+ }
3854
3907
  async function updatePackages(names) {
3855
3908
  if (names.length === 0) return {
3856
- code: 0,
3857
- stdout: "",
3858
- stderr: ""
3909
+ outcomes: [],
3910
+ failures: []
3859
3911
  };
3860
- return runBun([
3912
+ const before = snapshotVersions(names);
3913
+ const batch = await runBun([
3861
3914
  "update",
3862
3915
  ...names,
3863
3916
  "--latest"
3864
3917
  ]);
3865
- }
3866
- async function updateAllManaged() {
3867
- const managed = listInstalledPackages().filter((pkg) => pkg.type !== "framework").map((pkg) => pkg.name);
3868
- if (managed.length === 0) return {
3869
- result: {
3870
- code: 0,
3871
- stdout: "",
3872
- stderr: ""
3873
- },
3874
- names: []
3918
+ if (batch.code === 0) return {
3919
+ outcomes: names.map((name) => outcomeOf(name, before)),
3920
+ failures: []
3875
3921
  };
3922
+ rootLogger.warn(`[core] 批量更新失败,改为逐个更新: ${errorSummary(batch.stderr || batch.stdout)}`);
3923
+ const outcomes = [];
3924
+ for (const name of names) outcomes.push(await updateOne(name, before));
3876
3925
  return {
3877
- result: await updatePackages(managed),
3878
- names: managed
3926
+ outcomes,
3927
+ failures: outcomes.filter((outcome) => !outcome.ok)
3879
3928
  };
3880
3929
  }
3881
- function diffVersions(names, before) {
3882
- return names.map((name) => {
3883
- const after = getInstalledVersion(name);
3884
- const prev = before.get(name) || "0.0.0";
3885
- return {
3886
- name,
3887
- before: prev,
3888
- after,
3889
- changed: prev !== after
3890
- };
3891
- });
3892
- }
3893
- function snapshotAll(names) {
3894
- return snapshotVersions(names);
3895
- }
3896
3930
  function appendToMiokuPlugins(pkgName) {
3897
3931
  if (!pkgName.startsWith(PLUGIN_PREFIX)) return false;
3898
3932
  const shortName = pkgName.slice(PLUGIN_PREFIX.length);
@@ -4257,24 +4291,27 @@ async function performUpdateAndReport(ctx, event, names) {
4257
4291
  await replyText(event, "没有需要更新的项。");
4258
4292
  return;
4259
4293
  }
4260
- const before = snapshotAll(names);
4261
4294
  await replyText(event, `正在更新 ${names.length} 个包,请稍候...`);
4262
- const result = await updatePackages(names);
4263
- if (result.code !== 0) {
4264
- ctx.logger.error(`[core] 更新失败: ${result.stderr || result.stdout}`);
4265
- await replyText(event, `更新失败:${result.stderr || result.stdout}`);
4295
+ const report = await updatePackages(names);
4296
+ const changed = report.outcomes.filter((item) => item.changed);
4297
+ const failures = report.failures;
4298
+ ctx.logger.info(`[core] 更新结束:升级 ${changed.length} 个,失败 ${failures.length} 个`);
4299
+ if (failures.length > 0) ctx.logger.error(`[core] 更新失败: ${failures.map((item) => `${item.name}(${item.error})`).join("; ")}`);
4300
+ if (changed.length === 0 && failures.length === 0) {
4301
+ await replyText(event, "更新完成,所有包均已是最新版本");
4266
4302
  return;
4267
4303
  }
4268
- const diffs = diffVersions(names, before);
4269
- const changed = diffs.filter((d) => d.changed);
4270
- const unchanged = diffs.filter((d) => !d.changed);
4304
+ const parts = [];
4305
+ if (changed.length > 0) {
4306
+ parts.push(`更新完成,共 ${changed.length} 个包已升级:`, ...changed.map((item) => `• ${item.name}: ${item.before} → ${item.after}`));
4307
+ const unchanged = report.outcomes.length - changed.length - failures.length;
4308
+ if (unchanged > 0) parts.push("", `另有 ${unchanged} 个包已是最新`);
4309
+ }
4310
+ if (failures.length > 0) parts.push("", `以下 ${failures.length} 个包未能更新:`, ...failures.map((item) => `• ${item.name}: ${item.error}`), "", "通常是 npm 上的版本信息尚未同步,可稍后重试");
4271
4311
  if (changed.length === 0) {
4272
- await replyText(event, "更新完成,所有包均已是最新版本");
4312
+ await replyText(event, parts.join("\n"));
4273
4313
  return;
4274
4314
  }
4275
- const lines = changed.map((d) => `• ${d.name}: ${d.before} → ${d.after}`);
4276
- const parts = [`更新完成,共 ${changed.length} 个包已升级:`, ...lines];
4277
- if (unchanged.length > 0) parts.push("", `另有 ${unchanged.length} 个包已是最新`);
4278
4315
  parts.push("", "即将重启...");
4279
4316
  await replyText(event, parts.join("\n"));
4280
4317
  const selfId = String(event?.self_id ?? "");
@@ -4303,12 +4340,12 @@ function registerUpdateCommands(ctx) {
4303
4340
  const bot = event.bot;
4304
4341
  if (!bot) return;
4305
4342
  if (arg === "all") {
4306
- const managed = await updateAllManaged();
4307
- if (managed.names.length === 0) {
4343
+ const managed = listManagedPackages();
4344
+ if (managed.length === 0) {
4308
4345
  await replyText(event, "未找到可更新的 mioku 包");
4309
4346
  return;
4310
4347
  }
4311
- await performUpdateAndReport(ctx, event, managed.names);
4348
+ await performUpdateAndReport(ctx, event, managed.map((item) => item.name));
4312
4349
  return;
4313
4350
  }
4314
4351
  if (arg === "mioku" || arg === "self") {
@@ -4713,18 +4750,20 @@ function startAutoUpdateScheduler(enabled, time, frequency) {
4713
4750
  running = true;
4714
4751
  try {
4715
4752
  rootLogger.info("[core] 自动更新开始...");
4716
- const allInstalled = listInstalledPackages();
4753
+ const allInstalled = listDeclaredPackages();
4717
4754
  const names = allInstalled.map((pkg) => pkg.name);
4718
4755
  if (names.length === 0) {
4719
4756
  rootLogger.info("[core] 未找到需要更新的包");
4720
4757
  return;
4721
4758
  }
4722
- const result = await updatePackages(names);
4723
- if (result.code !== 0) {
4724
- rootLogger.error(`[core] 自动更新失败: ${result.stderr || result.stdout}`);
4759
+ const report = await updatePackages(names);
4760
+ const changed = report.outcomes.filter((item) => item.changed);
4761
+ if (report.failures.length > 0) rootLogger.error(`[core] 自动更新失败: ${report.failures.map((item) => `${item.name}(${item.error})`).join("; ")}`);
4762
+ if (changed.length === 0) {
4763
+ rootLogger.info("[core] 自动更新结束,没有包需要升级");
4725
4764
  return;
4726
4765
  }
4727
- rootLogger.info("[core] 自动更新成功,准备重启...");
4766
+ rootLogger.info(`[core] 自动更新成功,升级 ${changed.length} 个包,准备重启...`);
4728
4767
  const marker = {
4729
4768
  initiatedAt: Date.now(),
4730
4769
  selfId: "",