botmux 3.18.4 → 3.18.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.
Files changed (45) hide show
  1. package/dist/.runtime-build-id +1 -1
  2. package/dist/cli.d.ts.map +1 -1
  3. package/dist/cli.js +98 -5
  4. package/dist/cli.js.map +1 -1
  5. package/dist/core/binary-install-shape.d.ts +77 -0
  6. package/dist/core/binary-install-shape.d.ts.map +1 -0
  7. package/dist/core/binary-install-shape.js +117 -0
  8. package/dist/core/binary-install-shape.js.map +1 -0
  9. package/dist/core/binary-self-update.d.ts +82 -0
  10. package/dist/core/binary-self-update.d.ts.map +1 -0
  11. package/dist/core/binary-self-update.js +239 -0
  12. package/dist/core/binary-self-update.js.map +1 -0
  13. package/dist/core/maintenance.d.ts +88 -1
  14. package/dist/core/maintenance.d.ts.map +1 -1
  15. package/dist/core/maintenance.js +149 -8
  16. package/dist/core/maintenance.js.map +1 -1
  17. package/dist/core/release-download.d.ts +13 -0
  18. package/dist/core/release-download.d.ts.map +1 -0
  19. package/dist/core/release-download.js +130 -0
  20. package/dist/core/release-download.js.map +1 -0
  21. package/dist/dashboard/hd2d-assets.d.ts +2 -3
  22. package/dist/dashboard/hd2d-assets.d.ts.map +1 -1
  23. package/dist/dashboard/hd2d-assets.js +7 -109
  24. package/dist/dashboard/hd2d-assets.js.map +1 -1
  25. package/dist/dashboard/web/app.d.ts.map +1 -1
  26. package/dist/dashboard/web/app.js +7 -1
  27. package/dist/dashboard/web/app.js.map +1 -1
  28. package/dist/dashboard/web/settings-page.d.ts.map +1 -1
  29. package/dist/dashboard/web/settings-page.js.map +1 -1
  30. package/dist/dashboard-web/app.js +1 -1
  31. package/dist/dashboard.js +120 -13
  32. package/dist/dashboard.js.map +1 -1
  33. package/dist/utils/file-lock.d.ts +25 -0
  34. package/dist/utils/file-lock.d.ts.map +1 -1
  35. package/dist/utils/file-lock.js +34 -4
  36. package/dist/utils/file-lock.js.map +1 -1
  37. package/dist/utils/global-install.d.ts +19 -0
  38. package/dist/utils/global-install.d.ts.map +1 -1
  39. package/dist/utils/global-install.js +28 -1
  40. package/dist/utils/global-install.js.map +1 -1
  41. package/dist/utils/install-info.d.ts +37 -1
  42. package/dist/utils/install-info.d.ts.map +1 -1
  43. package/dist/utils/install-info.js +45 -3
  44. package/dist/utils/install-info.js.map +1 -1
  45. package/package.json +7 -7
@@ -1 +1 @@
1
- a00e695f90038d1f1a1a3b2e10f626edb0f26749fdeaf4eb41724a87de90b768
1
+ cd39121384378fc2a1e3b9dc8cd4ef4523b48386c926712eaaa7d5e5e92715c6
package/dist/cli.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AA8BA,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAiLhD,OAAO,EAAE,KAAK,KAAK,EAA0C,MAAM,yBAAyB,CAAC;AAujB7F;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAsB,iBAAiB,CAAC,EAAE,EAAE,UAAU,CAAC,OAAO,eAAe,CAAC,GAAG,OAAO,CACpF;IACE,EAAE,EAAE,IAAI,CAAC;IACT,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,KAAK,CAAC;IACb,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,oDAAoD;IACpD,cAAc,CAAC,EAAE,OAAO,CAAC;CAC1B,GACD;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,WAAW,CAAA;CAAE,CACrC,CA6LA;AAmlDD,MAAM,MAAM,kBAAkB,GAC1B;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,KAAK,EAAE,SAAS,GAAG,gBAAgB,CAAC;IAAC,WAAW,EAAE,MAAM,CAAA;CAAE,GACtE;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,WAAW,GAAG,WAAW,GAAG,YAAY,GAAG,SAAS,GAAG,kBAAkB,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC;AAEtH,MAAM,MAAM,iBAAiB,GACzB;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,KAAK,EAAE,SAAS,GAAG,iBAAiB,CAAC;IAAC,WAAW,EAAE,MAAM,CAAA;CAAE,GACvE;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,WAAW,GAAG,YAAY,GAAG,SAAS,GAAG,kBAAkB,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC;AAooQxG;;;;;;;;;;;GAWG;AACH,wBAAsB,OAAO,CAC3B,OAAO,EAAE,OAAO,EAChB,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,EACvC,SAAS,EAAE,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,OAAO,CAAC,OAAO,qBAAqB,EAAE,SAAS,CAAC,EAC9F,KAAK,EAAE,MAAM,EACb,mBAAmB,CAAC,EAAE,MAAM,OAAO,CAAC,OAAO,2BAA2B,EAAE,UAAU,GAAG,IAAI,CAAC;AAC1F;qDACqD;AACrD,wBAAwB,CAAC,EAAE,CAAC,YAAY,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,2BAA2B,EAAE,UAAU,GAAG,IAAI,CAAC,GAClH,OAAO,CAAC;IAAE,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC,CAuL7B"}
1
+ {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AA8BA,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAoLhD,OAAO,EAAE,KAAK,KAAK,EAA0C,MAAM,yBAAyB,CAAC;AAujB7F;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAsB,iBAAiB,CAAC,EAAE,EAAE,UAAU,CAAC,OAAO,eAAe,CAAC,GAAG,OAAO,CACpF;IACE,EAAE,EAAE,IAAI,CAAC;IACT,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,KAAK,CAAC;IACb,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,oDAAoD;IACpD,cAAc,CAAC,EAAE,OAAO,CAAC;CAC1B,GACD;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,WAAW,CAAA;CAAE,CACrC,CA6LA;AAmlDD,MAAM,MAAM,kBAAkB,GAC1B;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,KAAK,EAAE,SAAS,GAAG,gBAAgB,CAAC;IAAC,WAAW,EAAE,MAAM,CAAA;CAAE,GACtE;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,WAAW,GAAG,WAAW,GAAG,YAAY,GAAG,SAAS,GAAG,kBAAkB,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC;AAEtH,MAAM,MAAM,iBAAiB,GACzB;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,KAAK,EAAE,SAAS,GAAG,iBAAiB,CAAC;IAAC,WAAW,EAAE,MAAM,CAAA;CAAE,GACvE;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,WAAW,GAAG,YAAY,GAAG,SAAS,GAAG,kBAAkB,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC;AA4rQxG;;;;;;;;;;;GAWG;AACH,wBAAsB,OAAO,CAC3B,OAAO,EAAE,OAAO,EAChB,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,EACvC,SAAS,EAAE,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,OAAO,CAAC,OAAO,qBAAqB,EAAE,SAAS,CAAC,EAC9F,KAAK,EAAE,MAAM,EACb,mBAAmB,CAAC,EAAE,MAAM,OAAO,CAAC,OAAO,2BAA2B,EAAE,UAAU,GAAG,IAAI,CAAC;AAC1F;qDACqD;AACrD,wBAAwB,CAAC,EAAE,CAAC,YAAY,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,2BAA2B,EAAE,UAAU,GAAG,IAAI,CAAC,GAClH,OAAO,CAAC;IAAE,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC,CAuL7B"}
package/dist/cli.js CHANGED
@@ -56,7 +56,7 @@ import { validateCodexAppManagedSendOrigin, } from './utils/codex-app-dispatch-l
56
56
  import { hasProtectedSessionMutationOwnership } from './core/session-mutation-guard.js';
57
57
  import { logger } from './utils/logger.js';
58
58
  import { reapLegacyPm2, liveGodAt } from './core/legacy-pm2-reaper.js';
59
- import { withFileLock, withFileLockSync } from './utils/file-lock.js';
59
+ import { withFileLock, withFileLockSync, FileLockTimeoutError } from './utils/file-lock.js';
60
60
  import { scheduleTimeZone } from './utils/timezone.js';
61
61
  import { expandHomePath, invalidWorkingDirs } from './utils/working-dir.js';
62
62
  import { firstPositional, hasFlagOrEq } from './cli/arg-utils.js';
@@ -78,7 +78,10 @@ import { isRemoteAccessEnabled } from './global-config.js';
78
78
  import { DASHBOARD_COMMAND_USAGE, executeDashboardCommand, formatDashboardFallbackFailure, formatDashboardSuccessLines, } from './cli/dashboard-command.js';
79
79
  import { globalInstallUpdateLockTarget, globalInstallUpdateLockTargetIn, installLatestBotmuxSync } from './core/maintenance.js';
80
80
  import { formatGlobalInstallCommand, resolveGlobalInstallPlan, UnsupportedGlobalInstallError, } from './utils/global-install.js';
81
- import { isLocalDevInstall, botmuxCliEntryAt, bakedBinaryVersion } from './utils/install-info.js';
81
+ import { isLocalDevInstall, botmuxCliEntryAt, bakedBinaryVersion, botmuxInstallRoot } from './utils/install-info.js';
82
+ import { currentUpdateStrategy, replaceStandaloneBinary } from './core/binary-self-update.js';
83
+ import { fetchLatestVersion, isNewerVersion } from './core/update-check.js';
84
+ import { resolveCurrentVersion } from './utils/install-diagnostics.js';
82
85
  import { resolveLocalDevCheckoutDir, isGitWorktree, gitPorcelainStatus, localDevUpdateSteps, describeSpawnFailure, } from './utils/local-dev-update.js';
83
86
  import { loadDashboardSecret } from './dashboard/auth.js';
84
87
  import { postWorkflowDaemonMutation, } from './workflows/v3/daemon-ipc-client.js';
@@ -2668,7 +2671,7 @@ async function cmdStatus() {
2668
2671
  // registry to reconcile against. Legacy pm2 still gets surfaced by
2669
2672
  // warnIfLegacyBotmuxAlive above, which is what a pre-migration host needs.
2670
2673
  }
2671
- function cmdUpgrade() {
2674
+ async function cmdUpgrade() {
2672
2675
  // 本地 checkout(有 .git/src):走 git pull --ff-only → 重新 build → 从本
2673
2676
  // checkout 重启,而不是拿全局包管理器去升级(那对 dev 部署无效,见
2674
2677
  // install-info.ts 的 isLocalDevInstall 说明)。
@@ -2676,8 +2679,66 @@ function cmdUpgrade() {
2676
2679
  cmdUpgradeLocalDev();
2677
2680
  return;
2678
2681
  }
2682
+ // 编译版单文件二进制没有 package.json 落盘 ⟹ resolveGlobalInstallPlan 恒抛
2683
+ // Unsupported(实测真实 v3.18.4 二进制就是这条)。改为先按「二进制装在哪」
2684
+ // 判形态:npm 子包形态交回 npm/pnpm/bun,install.sh 形态自己换二进制。
2685
+ const strategy = currentUpdateStrategy(botmuxInstallRoot());
2686
+ if (strategy.kind === 'self-replace') {
2687
+ try {
2688
+ const latest = await fetchLatestVersion();
2689
+ if (!latest) {
2690
+ console.error('❌ 无法获取最新版本号(网络不可达或 registry 异常)。');
2691
+ process.exit(1);
2692
+ }
2693
+ const current = resolveCurrentVersion();
2694
+ if (!isNewerVersion(latest, current)) {
2695
+ console.log(`✅ 已是最新版本(${current})。`);
2696
+ return;
2697
+ }
2698
+ console.log(`🔄 升级中:下载 v${latest} 二进制并替换 ${strategy.target}`);
2699
+ // 握与 dashboard / maintenance 同一把跨进程锁:这条路径是**写同一个文件**,
2700
+ // 两个 update 并发跑会互相盖掉临时文件与 rename。锁文件父目录可能还不存在
2701
+ // (daemon 从未在本机起过就先跑 update),先建再握,否则 ENOENT 会盖掉真实错误。
2702
+ const lockTarget = globalInstallUpdateLockTarget();
2703
+ mkdirSync(dirname(lockTarget), { recursive: true });
2704
+ let acquired = false;
2705
+ try {
2706
+ await withFileLock(lockTarget, async () => {
2707
+ acquired = true;
2708
+ const r = await replaceStandaloneBinary(latest, strategy.target);
2709
+ console.log(`✅ 升级完成:${r.asset} → ${r.target}(${current} → ${latest})。运行 botmux restart 以应用更新。`);
2710
+ }, { maxWaitMs: 2_000 });
2711
+ }
2712
+ catch (error) {
2713
+ // ⚠️ 三态,不是二态。`withFileLock` 拿不到锁时是**抛异常**不是安静返回,
2714
+ // 但 `acquired === false` 只证明「回调没执行」,**不等于「别人持锁」**:
2715
+ // 回调之前还可能因 `open` 的 EACCES/ENOSPC、holder 写入失败、holder 元数据
2716
+ // 不可读、stale-claim `link` 失败而抛错。只看 `acquired` 会把这些**真实故障
2717
+ // 全部误报成「另一个更新正在进行」**——磁盘满被说成并发冲突,是最坏的那种
2718
+ // 误导。所以必须同时要求它是 timeout 类型:
2719
+ // ① 超时且回调未进入 → 友好并发提示(正常互斥结果,不是故障)
2720
+ // ② 回调已进入后失败 → 透出真实错误(下载/校验/替换)
2721
+ // ③ 回调前的基础设施失败 → 同样透出真实错误
2722
+ // 判类型而不是匹文案:file-lock 的文案被多处按字符串匹配,不能动,但新代码
2723
+ // 应该用 FileLockTimeoutError(async/sync 两处语义一致)。
2724
+ if (!acquired && error instanceof FileLockTimeoutError) {
2725
+ console.error('❌ 另一个更新正在进行中(dashboard 或定时任务),请稍后重试。');
2726
+ process.exit(1);
2727
+ }
2728
+ throw error; // ②③ 交给外层统一报错,不被友好文案吞掉
2729
+ }
2730
+ }
2731
+ catch (error) {
2732
+ console.error(`❌ 升级失败:${error instanceof Error ? error.message : error}`);
2733
+ process.exit(1);
2734
+ }
2735
+ return;
2736
+ }
2679
2737
  try {
2680
- const plan = resolveGlobalInstallPlan();
2738
+ if (strategy.kind === 'unsupported') {
2739
+ throw new UnsupportedGlobalInstallError('unknown', process.execPath);
2740
+ }
2741
+ const plan = resolveGlobalInstallPlan(strategy.packageRoot);
2681
2742
  console.log(`🔄 升级中:${formatGlobalInstallCommand(plan)}`);
2682
2743
  installLatestBotmuxSync(plan);
2683
2744
  console.log('\n✅ 升级完成。运行 botmux restart 以应用更新。');
@@ -11388,6 +11449,38 @@ if (__entrySubcommand) {
11388
11449
  // below never runs. This awaits forever; the imported module owns exit().
11389
11450
  await new Promise(() => { });
11390
11451
  }
11452
+ // Hidden: perform a compiled-binary self-replace and exit. Invoked by
11453
+ // `runSelfReplaceBlocking` (src/core/maintenance.ts) so the synchronous
11454
+ // maintenance tick / dashboard handler can wait on one child instead of
11455
+ // restructuring around an await. Not a user-facing command; it only ever makes
11456
+ // sense for the standalone binary, which owns its own file.
11457
+ if (command === '__self-update') {
11458
+ const requested = process.argv[3] || 'latest';
11459
+ try {
11460
+ const { replaceStandaloneBinary, currentBinaryInstallShape } = await import('./core/binary-self-update.js');
11461
+ if (currentBinaryInstallShape() !== 'curl-binary') {
11462
+ // Fail closed rather than write into a tree a package manager owns.
11463
+ console.error('__self-update: 当前不是独立二进制安装(install.sh 形态),拒绝自替换');
11464
+ process.exit(1);
11465
+ }
11466
+ const { fetchLatestVersion } = await import('./core/update-check.js');
11467
+ const version = requested === 'latest' ? await fetchLatestVersion() : requested;
11468
+ if (!version) {
11469
+ console.error('__self-update: 无法解析最新版本(网络不可达或 registry 异常)');
11470
+ process.exit(1);
11471
+ }
11472
+ const r = await replaceStandaloneBinary(version);
11473
+ // The caller parses this line: after a self-replace the running process still
11474
+ // reports its OWN baked version, so it cannot discover the new one by re-reading.
11475
+ console.log(`BOTMUX_SELF_UPDATE_VERSION=${version}`);
11476
+ console.log(`__self-update: ${r.asset} → ${r.target} (${r.bytes} bytes)`);
11477
+ process.exit(0);
11478
+ }
11479
+ catch (error) {
11480
+ console.error(`__self-update: ${error instanceof Error ? error.message : error}`);
11481
+ process.exit(1);
11482
+ }
11483
+ }
11391
11484
  const ROOT_FLEET_MUTATION_COMMANDS = new Set(['start', 'stop', 'restart', 'upgrade', 'update']);
11392
11485
  if (ROOT_FLEET_MUTATION_COMMANDS.has(command ?? '')
11393
11486
  && process.argv.slice(3).some(arg => arg === '--help' || arg === '-h')) {
@@ -12245,7 +12338,7 @@ switch (command) {
12245
12338
  break;
12246
12339
  case 'upgrade':
12247
12340
  case 'update':
12248
- cmdUpgrade();
12341
+ await cmdUpgrade();
12249
12342
  break;
12250
12343
  case 'dashboard':
12251
12344
  await cmdDashboard(process.argv.slice(3));