@mutmutco/hub 4.2.2 → 4.2.4

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 (2) hide show
  1. package/dist/index.cjs +63 -10
  2. package/package.json +1 -1
package/dist/index.cjs CHANGED
@@ -66,8 +66,10 @@ __export(index_exports, {
66
66
  retryHint: () => retryHint,
67
67
  schedulePendingWatcher: () => schedulePendingWatcher,
68
68
  schedulerStatus: () => schedulerStatus,
69
+ selfDist: () => selfDist,
69
70
  sharedDocLockPath: () => sharedDocLockPath,
70
71
  sharedDocLockTimeoutReceipt: () => sharedDocLockTimeoutReceipt,
72
+ shouldHandOff: () => shouldHandOff,
71
73
  startSpinner: () => startSpinner,
72
74
  systemdServiceUnit: () => systemdServiceUnit,
73
75
  systemdTimerUnit: () => systemdTimerUnit,
@@ -81,6 +83,7 @@ __export(index_exports, {
81
83
  writePending: () => writePending
82
84
  });
83
85
  module.exports = __toCommonJS(index_exports);
86
+ var import_node_child_process12 = require("node:child_process");
84
87
  var import_node_fs19 = require("node:fs");
85
88
  var import_node_path15 = require("node:path");
86
89
 
@@ -1208,11 +1211,16 @@ function cliArm(options) {
1208
1211
  }
1209
1212
 
1210
1213
  // src/arm-self.ts
1214
+ var SELF_PACKAGE = "@mutmutco/hub";
1215
+ var SELF_DIST_REL = "dist/index.cjs";
1216
+ function selfDist(env) {
1217
+ return globalDist(env, SELF_PACKAGE, SELF_DIST_REL);
1218
+ }
1211
1219
  function selfArm(options) {
1212
1220
  return npmGlobalArm({
1213
1221
  surface: "updater",
1214
- packageName: "@mutmutco/hub",
1215
- distRel: "dist/index.cjs",
1222
+ packageName: SELF_PACKAGE,
1223
+ distRel: SELF_DIST_REL,
1216
1224
  ...options
1217
1225
  });
1218
1226
  }
@@ -2671,6 +2679,11 @@ function reap(options) {
2671
2679
 
2672
2680
  // src/reconcile.ts
2673
2681
  var CANDIDATE_BOUND_DEFAULT = 12;
2682
+ function shouldHandOff(self, options) {
2683
+ if (options.dryRun || options.guard) return false;
2684
+ if (!options.dist) return false;
2685
+ return self.verdict === "ok" && Boolean(self.from) && self.from !== self.to;
2686
+ }
2674
2687
  function reconcile(options) {
2675
2688
  const env = options.env ?? process.env;
2676
2689
  const say = (line) => {
@@ -2685,7 +2698,7 @@ function reconcile(options) {
2685
2698
  const stamped = options.dryRun ? { ...event, dryRun: true } : event;
2686
2699
  if (!appendJournal(paths.journalPath, { ts: (/* @__PURE__ */ new Date()).toISOString(), run, ...stamped })) summary.journalOk = false;
2687
2700
  };
2688
- const summary = { run, target: null, gate: null, atomicity: null, cli: null, updater: null, plugins: [], reap: [], surfaces: [], journalOk: true, exit: 0 };
2701
+ const summary = { run, target: null, gate: null, atomicity: null, cli: null, updater: null, plugins: [], reap: [], surfaces: [], journalOk: true, reexec: null, exit: 0 };
2689
2702
  const recordArm = (arm) => {
2690
2703
  if (arm.verdict === "fail") arm.repeatFailure = journalSurfaceRepeatedFail(paths.journalPath, arm.surface, run);
2691
2704
  journal({ kind: "arm", surface: arm.surface, from: arm.from, to: arm.to, verdict: arm.verdict, detail: arm.detail, compat: arm.compat });
@@ -2750,6 +2763,18 @@ function reconcile(options) {
2750
2763
  }
2751
2764
  say(`target v${summary.target} (${summary.gate})`);
2752
2765
  options.onTargetResolved?.(summary.target, summary.gate);
2766
+ say("updater: self-bootstrap");
2767
+ const self = selfArm({ target: summary.target, dryRun: options.dryRun, paths, env });
2768
+ summary.updater = self;
2769
+ recordArm(self);
2770
+ say(`updater: ${self.verdict}${self.detail ? " \u2014 " + self.detail : ""}`);
2771
+ const handoffDist = selfDist(env);
2772
+ if (shouldHandOff(self, { dryRun: options.dryRun, dist: handoffDist, guard: env.MMI_UPDATER_REEXEC })) {
2773
+ summary.reexec = { dist: handoffDist, target: summary.target, from: self.from };
2774
+ journal({ kind: "reexec", surface: "updater", from: self.from, to: summary.target, verdict: "ok", detail: `handing off to ${handoffDist} \u2014 the remaining surfaces converge under ${summary.target}` });
2775
+ say(`updater: handing off to ${summary.target}`);
2776
+ return summary;
2777
+ }
2753
2778
  say(`cli: converging to v${summary.target}`);
2754
2779
  const cli = cliArm({ target: summary.target, dryRun: options.dryRun, paths, env });
2755
2780
  summary.cli = cli;
@@ -2773,11 +2798,6 @@ function reconcile(options) {
2773
2798
  journal({ kind: "surface", surface: probe.id, present: true, verdict: "skip", detail: `present, its converge arm has not landed yet (target ${summary.target})` });
2774
2799
  }
2775
2800
  }
2776
- say("updater: self-bootstrap");
2777
- const self = selfArm({ target: summary.target, dryRun: options.dryRun, paths, env });
2778
- summary.updater = self;
2779
- recordArm(self);
2780
- say(`updater: ${self.verdict}${self.detail ? " \u2014 " + self.detail : ""}`);
2781
2801
  return summary;
2782
2802
  });
2783
2803
  } catch (error) {
@@ -3487,6 +3507,7 @@ function stripAnsi(text) {
3487
3507
  }
3488
3508
  function collectRows(summary) {
3489
3509
  const rows = [];
3510
+ if (summary.updater) rows.push(classify(summary.updater));
3490
3511
  if (summary.cli) rows.push(classify(summary.cli));
3491
3512
  for (const arm of summary.plugins) rows.push(classify(arm));
3492
3513
  for (const surface of summary.surfaces) {
@@ -3494,7 +3515,6 @@ function collectRows(summary) {
3494
3515
  rows.push({ name: surface.id, from: null, to: summary.target ?? "?", kind: "pending", note: "arm not landed", detail: "", repeatFailure: false });
3495
3516
  }
3496
3517
  }
3497
- if (summary.updater) rows.push(classify(summary.updater));
3498
3518
  return rows;
3499
3519
  }
3500
3520
  function headline(detail, width) {
@@ -3543,7 +3563,7 @@ function reportFooterLines(summary, options = {}) {
3543
3563
  out.push("");
3544
3564
  out.push(...noteLines(
3545
3565
  " note: ",
3546
- `hub moved ${self.from} \u2192 ${self.to} after the surfaces above ran, so they used ${self.from} logic \u2014 rerun \`mmi-hub update\` to bring them to ${self.to}`,
3566
+ `hub moved ${self.from} \u2192 ${self.to} while the surfaces above were converging, so they used ${self.from} logic \u2014 rerun \`mmi-hub update\` to bring them to ${self.to}`,
3547
3567
  width
3548
3568
  ));
3549
3569
  }
@@ -3688,6 +3708,24 @@ function printReconcile(summary, json, dryRun = false) {
3688
3708
  journalPath: statePaths(process.env).journalPath
3689
3709
  }));
3690
3710
  }
3711
+ function handOff(reexec, args, json) {
3712
+ if (!json) {
3713
+ process.stdout.write(` updater ${reexec.from ?? "absent"} \u2192 ${reexec.target} updated \u2014 rerunning under ${reexec.target}
3714
+
3715
+ `);
3716
+ }
3717
+ const child = (0, import_node_child_process12.spawnSync)(process.execPath, [reexec.dist, ...args], {
3718
+ stdio: "inherit",
3719
+ windowsHide: true,
3720
+ env: { ...process.env, MMI_UPDATER_REEXEC: "1" }
3721
+ });
3722
+ if (child.error || typeof child.status !== "number") {
3723
+ process.stderr.write(`mmi-hub: could not run the updated updater at ${reexec.dist} \u2014 ${child.error?.message ?? "no exit status"}; rerun \`mmi-hub update\`
3724
+ `);
3725
+ return 1;
3726
+ }
3727
+ return child.status;
3728
+ }
3691
3729
  function runUpdate(args, json) {
3692
3730
  const dryRun = args.includes("--dry-run");
3693
3731
  const verbose = args.includes("--verbose");
@@ -3745,11 +3783,24 @@ function main(args = process.argv.slice(2)) {
3745
3783
  }
3746
3784
  if (command === "update") {
3747
3785
  const { summary, rendered } = runUpdate(args, json);
3786
+ if (summary.reexec) return handOff(summary.reexec, args, json);
3748
3787
  if (!rendered) printReconcile(summary, json, args.includes("--dry-run"));
3749
3788
  return summary.exit;
3750
3789
  }
3751
3790
  if (command === "install") {
3752
3791
  const { summary, rendered } = runUpdate(args, json);
3792
+ if (summary.reexec) {
3793
+ const handedOff = handOff(summary.reexec, ["update", ...args.filter((arg) => arg !== "install")], json);
3794
+ const scheduled = installTask();
3795
+ if (!json) {
3796
+ process.stdout.write(scheduled.ok ? ` Auto-update on \u2014 hourly task "MMI Fleet Updater".
3797
+ ` : ` Auto-update NOT enabled \u2014 ${scheduled.detail}
3798
+ `);
3799
+ } else {
3800
+ process.stdout.write(JSON.stringify({ convergence: summary, autoupdate: scheduled }, null, 2) + "\n");
3801
+ }
3802
+ return Math.max(handedOff, scheduled.ok ? 0 : 1);
3803
+ }
3753
3804
  const schedule = installTask();
3754
3805
  if (json) {
3755
3806
  process.stdout.write(JSON.stringify({ convergence: summary, autoupdate: schedule }, null, 2) + "\n");
@@ -3826,8 +3877,10 @@ if (typeof require !== "undefined" && require.main === module) {
3826
3877
  retryHint,
3827
3878
  schedulePendingWatcher,
3828
3879
  schedulerStatus,
3880
+ selfDist,
3829
3881
  sharedDocLockPath,
3830
3882
  sharedDocLockTimeoutReceipt,
3883
+ shouldHandOff,
3831
3884
  startSpinner,
3832
3885
  systemdServiceUnit,
3833
3886
  systemdTimerUnit,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mutmutco/hub",
3
- "version": "4.2.2",
3
+ "version": "4.2.4",
4
4
  "description": "Install and maintain the MMI CLI and every present host surface from one release-gated Hub command.",
5
5
  "type": "module",
6
6
  "license": "UNLICENSED",