@mutmutco/hub 4.3.63 → 4.3.65

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 +65 -10
  2. package/package.json +1 -1
package/dist/index.cjs CHANGED
@@ -34,6 +34,7 @@ __export(index_exports, {
34
34
  heldOutput: () => heldOutput,
35
35
  hermesArm: () => hermesArm,
36
36
  hermesHome: () => hermesHome,
37
+ hermesHomes: () => hermesHomes,
37
38
  hermesHostCommand: () => hermesHostCommand,
38
39
  hermesHostEvidence: () => hermesHostEvidence,
39
40
  hermesHostPresent: () => hermesHostPresent,
@@ -1772,9 +1773,60 @@ function materializeHermesSkills(home) {
1772
1773
  return { ok: false, detail: message2(error) };
1773
1774
  }
1774
1775
  }
1776
+ function enableHermesPlugin(env, runner) {
1777
+ const command = hermesHostCommand(env);
1778
+ let result;
1779
+ try {
1780
+ result = runner(command, ["plugins", "enable", "mmi", "--no-allow-tool-override"], env);
1781
+ } catch (error) {
1782
+ return { ok: false, detail: `\`${command} plugins enable mmi\` could not run: ${message2(error)}` };
1783
+ }
1784
+ if (result.status === 0) return { ok: true, detail: "enabled in plugins.enabled" };
1785
+ const reason = message2(result.stderr?.trim() || result.stdout?.trim() || `exit ${result.status ?? "unknown"}`);
1786
+ return { ok: false, detail: `\`${command} plugins enable mmi\` failed: ${reason}` };
1787
+ }
1788
+ function hermesHomes(env = process.env) {
1789
+ const root = hermesHome(env);
1790
+ const homes = [root];
1791
+ try {
1792
+ for (const entry of (0, import_node_fs13.readdirSync)((0, import_node_path10.join)(root, "profiles"), { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name))) {
1793
+ if (entry.isDirectory() && !entry.name.startsWith(".")) homes.push((0, import_node_path10.join)(root, "profiles", entry.name));
1794
+ }
1795
+ } catch {
1796
+ }
1797
+ return homes;
1798
+ }
1799
+ var VERDICT_RANK = { fail: 3, defer: 2, ok: 1, skip: 0 };
1775
1800
  function hermesArm(options) {
1776
1801
  const env = options.env ?? process.env;
1777
- const home = hermesHome(env);
1802
+ if (!hermesHostPresent(env, options.hostCommandRunner)) {
1803
+ return {
1804
+ surface: "hermes",
1805
+ from: null,
1806
+ to: options.target,
1807
+ verdict: "skip",
1808
+ detail: `host absent (${hermesHostCommand(env)} CLI not on PATH) \u2014 skipped without writes`
1809
+ };
1810
+ }
1811
+ const homes = hermesHomes(env);
1812
+ const results = homes.map((home, index) => ({
1813
+ home,
1814
+ result: armHermesHome(home, { ...env, HERMES_HOME: home }, options, index === 0)
1815
+ }));
1816
+ const primary = results[0].result;
1817
+ if (results.length === 1) return primary;
1818
+ const worst = results.reduce((a, b) => VERDICT_RANK[b.result.verdict] > VERDICT_RANK[a.result.verdict] ? b : a);
1819
+ const detail = results.map(({ home, result }, index) => `${index === 0 ? "home" : `profile ${(0, import_node_path10.basename)(home)}`}: ${result.detail}`).join(" | ");
1820
+ return {
1821
+ surface: "hermes",
1822
+ from: primary.from,
1823
+ to: primary.to,
1824
+ verdict: worst.result.verdict,
1825
+ detail: `${homes.length} Hermes homes \u2014 ${detail}`,
1826
+ launchStaged: results.some(({ result }) => result.launchStaged) || void 0
1827
+ };
1828
+ }
1829
+ function armHermesHome(home, env, options, primary) {
1778
1830
  const parent = (0, import_node_path10.join)(home, "plugins");
1779
1831
  const live = (0, import_node_path10.join)(parent, "mmi");
1780
1832
  const installed = yamlVersion(live);
@@ -1786,21 +1838,21 @@ function hermesArm(options) {
1786
1838
  detail,
1787
1839
  launchStaged: launchStaged || void 0
1788
1840
  });
1789
- if (!hermesHostPresent(env, options.hostCommandRunner)) {
1790
- return result(null, options.target, "skip", `host absent (${hermesHostCommand(env)} CLI not on PATH) \u2014 skipped without writes`);
1791
- }
1792
1841
  if ((0, import_node_fs13.existsSync)(live) && !mmiOwned2(live)) {
1793
1842
  return result(installed, options.target, "defer", `${live} carries no ${PACKAGE2} package identity \u2014 refusing to displace an unmanaged Hermes mmi plugin`);
1794
1843
  }
1795
1844
  if (installed && compareSemver(installed, options.target) > 0) return result(installed, installed, "skip", `installed ${installed} is above candidate ${options.target} (monotonic)`);
1796
- const highWater = journalHighWater(options.paths.journalPath, "hermes");
1845
+ const highWater = primary ? journalHighWater(options.paths.journalPath, "hermes") : null;
1797
1846
  if (highWater && compareSemver(highWater, options.target) > 0) return result(installed, highWater, "skip", `journal high-water ${highWater} is above candidate ${options.target} (monotonic)`);
1847
+ const hostRunner = options.hostCommandRunner ?? runHostCommand;
1798
1848
  if (installed === options.target && treeHealthy2(live)) {
1799
1849
  const skills2 = materializeHermesSkills(home);
1800
1850
  if (!skills2.ok) return result(installed, options.target, "fail", `plugin at target ${options.target}, but skills provisioning failed: ${skills2.detail}`);
1801
- return result(installed, options.target, "ok", `already at target ${options.target}; ${skills2.detail}`);
1851
+ const enabled2 = enableHermesPlugin(env, hostRunner);
1852
+ if (!enabled2.ok) return result(installed, options.target, "fail", `plugin at target ${options.target}, but ${enabled2.detail}`);
1853
+ return result(installed, options.target, "ok", `already at target ${options.target}; ${skills2.detail}; ${enabled2.detail}`);
1802
1854
  }
1803
- if (options.dryRun) return result(installed, options.target, "ok", `dry-run: would stage ${PACKAGE2}@${options.target}, atomically switch ${installed ?? "absent"} -> ${options.target}, then provision skills into ${(0, import_node_path10.join)(home, "skills", "mmi")}`);
1855
+ if (options.dryRun) return result(installed, options.target, "ok", `dry-run: would stage ${PACKAGE2}@${options.target}, atomically switch ${installed ?? "absent"} -> ${options.target}, provision skills into ${(0, import_node_path10.join)(home, "skills", "mmi")}, then enable mmi in plugins.enabled`);
1804
1856
  const staged = stagePackage({ surface: "hermes", packageName: PACKAGE2, target: options.target, witness: MANIFEST2, paths: options.paths, env });
1805
1857
  if ("defer" in staged) return result(installed, options.target, "defer", staged.defer);
1806
1858
  const stagedVersion = yamlVersion(staged.packageRoot);
@@ -1836,7 +1888,8 @@ function hermesArm(options) {
1836
1888
  return result(installed, options.target, "defer", incomingTree);
1837
1889
  }
1838
1890
  }
1839
- const busy = hermesHostEvidence(env, home, options.processListProvider);
1891
+ const displaced = (0, import_node_fs13.existsSync)(live);
1892
+ const busy = displaced ? hermesHostEvidence(env, home, options.processListProvider) : null;
1840
1893
  if (busy) {
1841
1894
  const pending = noteLaunchStaged(options.paths, env, {
1842
1895
  surface: "hermes",
@@ -1849,7 +1902,6 @@ function hermesArm(options) {
1849
1902
  });
1850
1903
  return result(installed, options.target, "ok", `staged ${options.target} for next launch while Hermes is running (${busy}) \u2014 ${pending}`, true);
1851
1904
  }
1852
- const displaced = (0, import_node_fs13.existsSync)(live);
1853
1905
  try {
1854
1906
  if (displaced) (0, import_node_fs13.renameSync)(live, quarantine);
1855
1907
  (0, import_node_fs13.renameSync)(incoming, live);
@@ -1881,7 +1933,9 @@ function hermesArm(options) {
1881
1933
  clearLaunchTarget(launchTargetPath);
1882
1934
  const skills = materializeHermesSkills(home);
1883
1935
  if (!skills.ok) return result(installed, options.target, "fail", `plugin converged to ${options.target}, but skills provisioning failed: ${skills.detail}`);
1884
- return result(installed, options.target, "ok", `converged ${installed ?? "absent"} -> ${options.target} (staged, verified, atomically switched${displaced ? `; previous generation quarantined at ${quarantine}` : ""}); ${skills.detail}`);
1936
+ const enabled = enableHermesPlugin(env, hostRunner);
1937
+ if (!enabled.ok) return result(installed, options.target, "fail", `plugin converged to ${options.target}, but ${enabled.detail}`);
1938
+ return result(installed, options.target, "ok", `converged ${installed ?? "absent"} -> ${options.target} (staged, verified, atomically switched${displaced ? `; previous generation quarantined at ${quarantine}` : ""}); ${skills.detail}; ${enabled.detail}`);
1885
1939
  }
1886
1940
  function probeHermesLaunchStaging(env = process.env) {
1887
1941
  return probeLaunchTargetVersion((0, import_node_path10.join)(hermesHome(env), ".mmi-updater", "launch-target.json"), yamlVersion);
@@ -3274,6 +3328,7 @@ if (typeof require !== "undefined" && require.main === module) {
3274
3328
  heldOutput,
3275
3329
  hermesArm,
3276
3330
  hermesHome,
3331
+ hermesHomes,
3277
3332
  hermesHostCommand,
3278
3333
  hermesHostEvidence,
3279
3334
  hermesHostPresent,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mutmutco/hub",
3
- "version": "4.3.63",
3
+ "version": "4.3.65",
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",