@mutmutco/installer-launcher 1.4.3 → 1.5.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/launcher.js CHANGED
@@ -976,6 +976,15 @@ parentPort.on('message', (next) => {
976
976
  });
977
977
  setInterval(draw, workerData.intervalMs);
978
978
  `;
979
+ function awaitWorkerIdle(control, timeoutMs = 250) {
980
+ const deadline = Date.now() + timeoutMs;
981
+ while (Atomics.load(control, 1)) {
982
+ const left = deadline - Date.now();
983
+ if (left <= 0) return false;
984
+ Atomics.wait(control, 1, 1, left);
985
+ }
986
+ return true;
987
+ }
979
988
  function createSpinner(face, { animate, stream, intervalMs = 90, transcriptPath, env = process.env, platform = process.platform }) {
980
989
  animate = animate && !face.nested && !face.emitsProgress;
981
990
  const glyphs = spinnerFramesFor(env, platform);
@@ -1008,7 +1017,7 @@ function createSpinner(face, { animate, stream, intervalMs = 90, transcriptPath,
1008
1017
  const pause = () => {
1009
1018
  if (!control) return;
1010
1019
  Atomics.store(control, 0, 1);
1011
- while (Atomics.load(control, 1)) Atomics.wait(control, 1, 1);
1020
+ awaitWorkerIdle(control);
1012
1021
  };
1013
1022
  const halt = () => {
1014
1023
  if (timer) clearInterval(timer);
@@ -1699,15 +1708,21 @@ function querySchedule(config, options = {}) {
1699
1708
  if (result.code !== 0) return { supported: true, enabled: false };
1700
1709
  const lastRun = valueOf(result.stdout, "Last Run Time:");
1701
1710
  const nextRun = valueOf(result.stdout, "Next Run Time:");
1711
+ const lastResult = valueOf(result.stdout, "Last Result:");
1702
1712
  const state2 = { supported: true, enabled: true, cadence: "hourly" };
1703
1713
  if (lastRun && !/disabled|never|not run/i.test(lastRun)) state2.lastRun = lastRun;
1704
1714
  else if (nextRun && !/disabled|never/i.test(nextRun)) state2.lastRun = `next run ${nextRun}`;
1715
+ if (lastResult) state2.lastResult = lastResult;
1705
1716
  return state2;
1706
1717
  }
1707
1718
  if (platform === "darwin") {
1708
1719
  const plist = join5(home, "Library", "LaunchAgents", `${scheduleLabel(config)}.plist`);
1709
1720
  if (!existsSync6(plist)) return { supported: true, enabled: false };
1710
- return { supported: true, enabled: true, cadence: "hourly" };
1721
+ const state2 = { supported: true, enabled: true, cadence: "hourly" };
1722
+ const printed = exec("launchctl", ["print", `gui/${process.getuid?.() ?? 501}/${scheduleLabel(config)}`]);
1723
+ const exitCode = /last exit code = (-?\d+)/.exec(`${printed.stdout ?? ""}${printed.stderr ?? ""}`)?.[1];
1724
+ if (printed.code === 0 && exitCode) state2.lastResult = exitCode;
1725
+ return state2;
1711
1726
  }
1712
1727
  const timer = join5(home, ".config", "systemd", "user", `${scheduleLabel(config)}.timer`);
1713
1728
  if (!existsSync6(timer)) return { supported: true, enabled: false };
@@ -1715,6 +1730,9 @@ function querySchedule(config, options = {}) {
1715
1730
  const shown = exec("systemctl", ["--user", "show", `${scheduleLabel(config)}.service`, "-p", "ExecMainStartTimestamp", "--value"]);
1716
1731
  const stamp = (shown.stdout ?? "").trim();
1717
1732
  if (shown.code === 0 && stamp && stamp !== "n/a") state.lastRun = stamp;
1733
+ const status = exec("systemctl", ["--user", "show", `${scheduleLabel(config)}.service`, "-p", "ExecMainStatus", "--value"]);
1734
+ const code = (status.stdout ?? "").trim();
1735
+ if (status.code === 0 && /^\d+$/.test(code)) state.lastResult = code;
1718
1736
  return state;
1719
1737
  }
1720
1738
  function darwinPlist(label, command2) {
@@ -1766,6 +1784,36 @@ function valueOf(output, key) {
1766
1784
  function firstLine(text) {
1767
1785
  return String(text).split(/\r?\n/).map((line) => line.trim()).filter(Boolean)[0] ?? "";
1768
1786
  }
1787
+ var SCHEDULE_SIGNAL = "MM_LAUNCHER_SCHEDULE";
1788
+ var OPT_OUT_FILE = "auto-update-off";
1789
+ function autoUpdateOptOut(dir) {
1790
+ return existsSync6(join5(dir, OPT_OUT_FILE));
1791
+ }
1792
+ function persistAutoUpdateOptOut(dir, off) {
1793
+ try {
1794
+ if (off) {
1795
+ mkdirSync5(dir, { recursive: true });
1796
+ writeFileSync6(join5(dir, OPT_OUT_FILE), "");
1797
+ } else rmSync5(join5(dir, OPT_OUT_FILE), { force: true });
1798
+ } catch {
1799
+ }
1800
+ }
1801
+ function armVerifiedSchedule(config, options) {
1802
+ try {
1803
+ enableSchedule(config, options.command, options);
1804
+ const verified = querySchedule(config, options);
1805
+ if (verified.enabled) return { signal: scheduleName(config), note: null };
1806
+ return { signal: null, note: `The launcher's hourly update task (${scheduleName(config)}) could not be verified \u2014 the product's own auto-update task stays.` };
1807
+ } catch (error) {
1808
+ return { signal: null, note: `The launcher could not arm its hourly update task: ${error.message} The product's own auto-update task stays.` };
1809
+ }
1810
+ }
1811
+ function lastResultFailed(value) {
1812
+ const text = value?.trim() ?? "";
1813
+ if (!text) return false;
1814
+ if (!/^-?(?:0x[0-9a-f]+|\d+)$/i.test(text)) return true;
1815
+ return ![0, 267011, 267009].includes(Number(text));
1816
+ }
1769
1817
 
1770
1818
  // src/login-github.ts
1771
1819
  import { spawn, spawnSync as spawnSync3 } from "node:child_process";
@@ -2265,7 +2313,7 @@ async function refreshLauncher(target, options = {}) {
2265
2313
  }
2266
2314
 
2267
2315
  // src/index.ts
2268
- var LAUNCHER_VERSION = true ? "1.4.3" : readVersionFromPackage();
2316
+ var LAUNCHER_VERSION = true ? "1.5.1" : readVersionFromPackage();
2269
2317
  function defaultPrint(message) {
2270
2318
  process.stdout.write(`${message}
2271
2319
  `);
@@ -2722,6 +2770,8 @@ async function installOrUpdateRun(config, dir, options, print, update, flags2, m
2722
2770
  installer.finish({ ...pointer ? { version: pointer } : {}, total: 1, updated: pointer && installed !== pointer ? 1 : 0, failed: 0, dryRun: true });
2723
2771
  return 0;
2724
2772
  }
2773
+ const armNote = armScheduleForInstall(config, dir, options);
2774
+ if (armNote) installer.phase("arm", { state: "note", detail: armNote });
2725
2775
  if (pointer && current?.version === pointer && payloadPresent) {
2726
2776
  version = pointer;
2727
2777
  installer.phase("resolve", { seconds: (Date.now() - started) / 1e3 });
@@ -2814,6 +2864,18 @@ async function installOrUpdateRun(config, dir, options, print, update, flags2, m
2814
2864
  installer.stop();
2815
2865
  }
2816
2866
  }
2867
+ function armScheduleForInstall(config, dir, options) {
2868
+ if (autoUpdateOptOut(dir)) return null;
2869
+ if (!isSea() && !options.autoupdate) return null;
2870
+ const result = armVerifiedSchedule(config, {
2871
+ command: options.autoupdate?.command ?? [process.execPath, "update"],
2872
+ stateDir: dir,
2873
+ ...options.autoupdate
2874
+ });
2875
+ if (!result.signal) return result.note;
2876
+ process.env[SCHEDULE_SIGNAL] = result.signal;
2877
+ return null;
2878
+ }
2817
2879
  function doInstall(config, dir, options, print, flags2) {
2818
2880
  return installOrUpdate(config, dir, options, print, false, flags2);
2819
2881
  }
@@ -3023,8 +3085,13 @@ function doAutoupdate(config, dir, options, args, ui) {
3023
3085
  const name = identityFor(ui.product).name;
3024
3086
  if (mode === "on" || mode === "off") {
3025
3087
  try {
3026
- if (mode === "on") enableSchedule(config, command2, scheduleOptions);
3027
- else disableSchedule(config, scheduleOptions);
3088
+ if (mode === "on") {
3089
+ enableSchedule(config, command2, scheduleOptions);
3090
+ persistAutoUpdateOptOut(dir, false);
3091
+ } else {
3092
+ disableSchedule(config, scheduleOptions);
3093
+ persistAutoUpdateOptOut(dir, true);
3094
+ }
3028
3095
  } catch (error) {
3029
3096
  return ui.refuse(sentence(error.message), `${ui.bin} doctor`);
3030
3097
  }
@@ -3035,8 +3102,16 @@ function doAutoupdate(config, dir, options, args, ui) {
3035
3102
  if (mode !== "status") return ui.refuse(`Unknown autoupdate mode: ${mode}.`, `${ui.bin} autoupdate status`, 2);
3036
3103
  const state = querySchedule(config, scheduleOptions);
3037
3104
  if (!state.supported) return ui.refuse("Auto-update is not supported on this platform.", `${ui.bin} update`);
3038
- if (state.enabled) ui.notice("ok", `Auto-update is on and runs every hour.${state.lastRun ? ` Last run: ${state.lastRun}.` : ""}`);
3039
- else ui.notice("note", "Auto-update is off.", `${ui.bin} autoupdate on`);
3105
+ if (state.enabled) {
3106
+ const failed = lastResultFailed(state.lastResult);
3107
+ ui.notice(
3108
+ failed ? "note" : "ok",
3109
+ `Auto-update is on and runs every hour.${state.lastRun ? ` Last run: ${state.lastRun}.` : ""}${failed ? ` Last result: ${state.lastResult}.` : ""}`,
3110
+ ...failed ? [`${ui.bin} doctor`] : []
3111
+ );
3112
+ return 0;
3113
+ }
3114
+ ui.notice("note", "Auto-update is off.", `${ui.bin} autoupdate on`);
3040
3115
  return 0;
3041
3116
  }
3042
3117
  function localFacts(config, dir, options) {
@@ -3055,10 +3130,12 @@ function signInRow(local) {
3055
3130
  if (local.signedIn) return { surface: "sign-in", state: local.expired ? "signed in \xB7 renews on next use" : "signed in", mark: "ok" };
3056
3131
  return { surface: "sign-in", state: local.expired ? "expired" : "signed out", mark: "fail" };
3057
3132
  }
3058
- function autoUpdateRow({ supported, enabled, cadence, lastRun }) {
3133
+ function autoUpdateRow({ supported, enabled, cadence, lastRun, lastResult }) {
3059
3134
  if (!supported) return { surface: AUTO_UPDATE, state: "unsupported on this platform", mark: "note" };
3060
3135
  if (!enabled) return { surface: AUTO_UPDATE, state: "off", mark: "note" };
3061
- return { surface: AUTO_UPDATE, state: `on \xB7 ${cadence ?? "hourly"}`, mark: "ok", ...lastRun ? { note: `Last run: ${lastRun}` } : {} };
3136
+ const updateFailed = lastResultFailed(lastResult);
3137
+ const note = updateFailed ? lastRun ? `Last run: ${lastRun}. Last run failed (exit ${lastResult})` : `Last run failed (exit ${lastResult})` : lastRun ? `Last run: ${lastRun}` : void 0;
3138
+ return { surface: AUTO_UPDATE, state: `on \xB7 ${cadence ?? "hourly"}`, mark: updateFailed ? "fail" : "ok", ...note ? { note } : {} };
3062
3139
  }
3063
3140
  function verdictFor(ui, local, updateAvailable, ready) {
3064
3141
  if (!local.installed) return { state: "fail", headline: "is not installed.", next: `${ui.bin} install` };
@@ -3113,7 +3190,12 @@ async function doStatus(config, dir, options, ui, print, json) {
3113
3190
  installed: local.installed,
3114
3191
  release,
3115
3192
  signIn: local.signedIn ? local.expired ? "renews" : "signed-in" : local.expired ? "expired" : "signed-out",
3116
- autoupdate: { supported: local.schedule.supported, enabled: local.schedule.enabled, lastRun: local.schedule.lastRun ?? null },
3193
+ autoupdate: {
3194
+ supported: local.schedule.supported,
3195
+ enabled: local.schedule.enabled,
3196
+ lastRun: local.schedule.lastRun ?? null,
3197
+ lastResult: local.schedule.lastResult ?? null
3198
+ },
3117
3199
  verdict: verdict.state,
3118
3200
  next: verdict.next
3119
3201
  }, null, 2));
@@ -1009,6 +1009,15 @@ parentPort.on('message', (next) => {
1009
1009
  });
1010
1010
  setInterval(draw, workerData.intervalMs);
1011
1011
  `;
1012
+ function awaitWorkerIdle(control, timeoutMs = 250) {
1013
+ const deadline = Date.now() + timeoutMs;
1014
+ while (Atomics.load(control, 1)) {
1015
+ const left = deadline - Date.now();
1016
+ if (left <= 0) return false;
1017
+ Atomics.wait(control, 1, 1, left);
1018
+ }
1019
+ return true;
1020
+ }
1012
1021
  function createSpinner(face, { animate, stream, intervalMs = 90, transcriptPath, env = process.env, platform = process.platform }) {
1013
1022
  animate = animate && !face.nested && !face.emitsProgress;
1014
1023
  const glyphs = spinnerFramesFor(env, platform);
@@ -1041,7 +1050,7 @@ function createSpinner(face, { animate, stream, intervalMs = 90, transcriptPath,
1041
1050
  const pause = () => {
1042
1051
  if (!control) return;
1043
1052
  Atomics.store(control, 0, 1);
1044
- while (Atomics.load(control, 1)) Atomics.wait(control, 1, 1);
1053
+ awaitWorkerIdle(control);
1045
1054
  };
1046
1055
  const halt = () => {
1047
1056
  if (timer) clearInterval(timer);
@@ -1732,15 +1741,21 @@ function querySchedule(config, options = {}) {
1732
1741
  if (result.code !== 0) return { supported: true, enabled: false };
1733
1742
  const lastRun = valueOf(result.stdout, "Last Run Time:");
1734
1743
  const nextRun = valueOf(result.stdout, "Next Run Time:");
1744
+ const lastResult = valueOf(result.stdout, "Last Result:");
1735
1745
  const state2 = { supported: true, enabled: true, cadence: "hourly" };
1736
1746
  if (lastRun && !/disabled|never|not run/i.test(lastRun)) state2.lastRun = lastRun;
1737
1747
  else if (nextRun && !/disabled|never/i.test(nextRun)) state2.lastRun = `next run ${nextRun}`;
1748
+ if (lastResult) state2.lastResult = lastResult;
1738
1749
  return state2;
1739
1750
  }
1740
1751
  if (platform === "darwin") {
1741
1752
  const plist = (0, import_node_path5.join)(home, "Library", "LaunchAgents", `${scheduleLabel(config)}.plist`);
1742
1753
  if (!(0, import_node_fs10.existsSync)(plist)) return { supported: true, enabled: false };
1743
- return { supported: true, enabled: true, cadence: "hourly" };
1754
+ const state2 = { supported: true, enabled: true, cadence: "hourly" };
1755
+ const printed = exec("launchctl", ["print", `gui/${process.getuid?.() ?? 501}/${scheduleLabel(config)}`]);
1756
+ const exitCode = /last exit code = (-?\d+)/.exec(`${printed.stdout ?? ""}${printed.stderr ?? ""}`)?.[1];
1757
+ if (printed.code === 0 && exitCode) state2.lastResult = exitCode;
1758
+ return state2;
1744
1759
  }
1745
1760
  const timer = (0, import_node_path5.join)(home, ".config", "systemd", "user", `${scheduleLabel(config)}.timer`);
1746
1761
  if (!(0, import_node_fs10.existsSync)(timer)) return { supported: true, enabled: false };
@@ -1748,6 +1763,9 @@ function querySchedule(config, options = {}) {
1748
1763
  const shown = exec("systemctl", ["--user", "show", `${scheduleLabel(config)}.service`, "-p", "ExecMainStartTimestamp", "--value"]);
1749
1764
  const stamp = (shown.stdout ?? "").trim();
1750
1765
  if (shown.code === 0 && stamp && stamp !== "n/a") state.lastRun = stamp;
1766
+ const status = exec("systemctl", ["--user", "show", `${scheduleLabel(config)}.service`, "-p", "ExecMainStatus", "--value"]);
1767
+ const code = (status.stdout ?? "").trim();
1768
+ if (status.code === 0 && /^\d+$/.test(code)) state.lastResult = code;
1751
1769
  return state;
1752
1770
  }
1753
1771
  function darwinPlist(label, command2) {
@@ -1799,6 +1817,36 @@ function valueOf(output, key) {
1799
1817
  function firstLine(text) {
1800
1818
  return String(text).split(/\r?\n/).map((line) => line.trim()).filter(Boolean)[0] ?? "";
1801
1819
  }
1820
+ var SCHEDULE_SIGNAL = "MM_LAUNCHER_SCHEDULE";
1821
+ var OPT_OUT_FILE = "auto-update-off";
1822
+ function autoUpdateOptOut(dir) {
1823
+ return (0, import_node_fs10.existsSync)((0, import_node_path5.join)(dir, OPT_OUT_FILE));
1824
+ }
1825
+ function persistAutoUpdateOptOut(dir, off) {
1826
+ try {
1827
+ if (off) {
1828
+ (0, import_node_fs10.mkdirSync)(dir, { recursive: true });
1829
+ (0, import_node_fs10.writeFileSync)((0, import_node_path5.join)(dir, OPT_OUT_FILE), "");
1830
+ } else (0, import_node_fs10.rmSync)((0, import_node_path5.join)(dir, OPT_OUT_FILE), { force: true });
1831
+ } catch {
1832
+ }
1833
+ }
1834
+ function armVerifiedSchedule(config, options) {
1835
+ try {
1836
+ enableSchedule(config, options.command, options);
1837
+ const verified = querySchedule(config, options);
1838
+ if (verified.enabled) return { signal: scheduleName(config), note: null };
1839
+ return { signal: null, note: `The launcher's hourly update task (${scheduleName(config)}) could not be verified \u2014 the product's own auto-update task stays.` };
1840
+ } catch (error) {
1841
+ return { signal: null, note: `The launcher could not arm its hourly update task: ${error.message} The product's own auto-update task stays.` };
1842
+ }
1843
+ }
1844
+ function lastResultFailed(value) {
1845
+ const text = value?.trim() ?? "";
1846
+ if (!text) return false;
1847
+ if (!/^-?(?:0x[0-9a-f]+|\d+)$/i.test(text)) return true;
1848
+ return ![0, 267011, 267009].includes(Number(text));
1849
+ }
1802
1850
 
1803
1851
  // src/login-github.ts
1804
1852
  var import_node_child_process3 = require("node:child_process");
@@ -2298,7 +2346,7 @@ async function refreshLauncher(target, options = {}) {
2298
2346
  }
2299
2347
 
2300
2348
  // src/index.ts
2301
- var LAUNCHER_VERSION = true ? "1.4.3" : readVersionFromPackage();
2349
+ var LAUNCHER_VERSION = true ? "1.5.1" : readVersionFromPackage();
2302
2350
  function defaultPrint(message) {
2303
2351
  process.stdout.write(`${message}
2304
2352
  `);
@@ -2755,6 +2803,8 @@ async function installOrUpdateRun(config, dir, options, print, update, flags2, m
2755
2803
  installer.finish({ ...pointer ? { version: pointer } : {}, total: 1, updated: pointer && installed !== pointer ? 1 : 0, failed: 0, dryRun: true });
2756
2804
  return 0;
2757
2805
  }
2806
+ const armNote = armScheduleForInstall(config, dir, options);
2807
+ if (armNote) installer.phase("arm", { state: "note", detail: armNote });
2758
2808
  if (pointer && current?.version === pointer && payloadPresent) {
2759
2809
  version = pointer;
2760
2810
  installer.phase("resolve", { seconds: (Date.now() - started) / 1e3 });
@@ -2847,6 +2897,18 @@ async function installOrUpdateRun(config, dir, options, print, update, flags2, m
2847
2897
  installer.stop();
2848
2898
  }
2849
2899
  }
2900
+ function armScheduleForInstall(config, dir, options) {
2901
+ if (autoUpdateOptOut(dir)) return null;
2902
+ if (!(0, import_node_sea2.isSea)() && !options.autoupdate) return null;
2903
+ const result = armVerifiedSchedule(config, {
2904
+ command: options.autoupdate?.command ?? [process.execPath, "update"],
2905
+ stateDir: dir,
2906
+ ...options.autoupdate
2907
+ });
2908
+ if (!result.signal) return result.note;
2909
+ process.env[SCHEDULE_SIGNAL] = result.signal;
2910
+ return null;
2911
+ }
2850
2912
  function doInstall(config, dir, options, print, flags2) {
2851
2913
  return installOrUpdate(config, dir, options, print, false, flags2);
2852
2914
  }
@@ -3056,8 +3118,13 @@ function doAutoupdate(config, dir, options, args, ui) {
3056
3118
  const name = identityFor(ui.product).name;
3057
3119
  if (mode === "on" || mode === "off") {
3058
3120
  try {
3059
- if (mode === "on") enableSchedule(config, command2, scheduleOptions);
3060
- else disableSchedule(config, scheduleOptions);
3121
+ if (mode === "on") {
3122
+ enableSchedule(config, command2, scheduleOptions);
3123
+ persistAutoUpdateOptOut(dir, false);
3124
+ } else {
3125
+ disableSchedule(config, scheduleOptions);
3126
+ persistAutoUpdateOptOut(dir, true);
3127
+ }
3061
3128
  } catch (error) {
3062
3129
  return ui.refuse(sentence(error.message), `${ui.bin} doctor`);
3063
3130
  }
@@ -3068,8 +3135,16 @@ function doAutoupdate(config, dir, options, args, ui) {
3068
3135
  if (mode !== "status") return ui.refuse(`Unknown autoupdate mode: ${mode}.`, `${ui.bin} autoupdate status`, 2);
3069
3136
  const state = querySchedule(config, scheduleOptions);
3070
3137
  if (!state.supported) return ui.refuse("Auto-update is not supported on this platform.", `${ui.bin} update`);
3071
- if (state.enabled) ui.notice("ok", `Auto-update is on and runs every hour.${state.lastRun ? ` Last run: ${state.lastRun}.` : ""}`);
3072
- else ui.notice("note", "Auto-update is off.", `${ui.bin} autoupdate on`);
3138
+ if (state.enabled) {
3139
+ const failed = lastResultFailed(state.lastResult);
3140
+ ui.notice(
3141
+ failed ? "note" : "ok",
3142
+ `Auto-update is on and runs every hour.${state.lastRun ? ` Last run: ${state.lastRun}.` : ""}${failed ? ` Last result: ${state.lastResult}.` : ""}`,
3143
+ ...failed ? [`${ui.bin} doctor`] : []
3144
+ );
3145
+ return 0;
3146
+ }
3147
+ ui.notice("note", "Auto-update is off.", `${ui.bin} autoupdate on`);
3073
3148
  return 0;
3074
3149
  }
3075
3150
  function localFacts(config, dir, options) {
@@ -3088,10 +3163,12 @@ function signInRow(local) {
3088
3163
  if (local.signedIn) return { surface: "sign-in", state: local.expired ? "signed in \xB7 renews on next use" : "signed in", mark: "ok" };
3089
3164
  return { surface: "sign-in", state: local.expired ? "expired" : "signed out", mark: "fail" };
3090
3165
  }
3091
- function autoUpdateRow({ supported, enabled, cadence, lastRun }) {
3166
+ function autoUpdateRow({ supported, enabled, cadence, lastRun, lastResult }) {
3092
3167
  if (!supported) return { surface: AUTO_UPDATE, state: "unsupported on this platform", mark: "note" };
3093
3168
  if (!enabled) return { surface: AUTO_UPDATE, state: "off", mark: "note" };
3094
- return { surface: AUTO_UPDATE, state: `on \xB7 ${cadence ?? "hourly"}`, mark: "ok", ...lastRun ? { note: `Last run: ${lastRun}` } : {} };
3169
+ const updateFailed = lastResultFailed(lastResult);
3170
+ const note = updateFailed ? lastRun ? `Last run: ${lastRun}. Last run failed (exit ${lastResult})` : `Last run failed (exit ${lastResult})` : lastRun ? `Last run: ${lastRun}` : void 0;
3171
+ return { surface: AUTO_UPDATE, state: `on \xB7 ${cadence ?? "hourly"}`, mark: updateFailed ? "fail" : "ok", ...note ? { note } : {} };
3095
3172
  }
3096
3173
  function verdictFor(ui, local, updateAvailable, ready) {
3097
3174
  if (!local.installed) return { state: "fail", headline: "is not installed.", next: `${ui.bin} install` };
@@ -3146,7 +3223,12 @@ async function doStatus(config, dir, options, ui, print, json) {
3146
3223
  installed: local.installed,
3147
3224
  release,
3148
3225
  signIn: local.signedIn ? local.expired ? "renews" : "signed-in" : local.expired ? "expired" : "signed-out",
3149
- autoupdate: { supported: local.schedule.supported, enabled: local.schedule.enabled, lastRun: local.schedule.lastRun ?? null },
3226
+ autoupdate: {
3227
+ supported: local.schedule.supported,
3228
+ enabled: local.schedule.enabled,
3229
+ lastRun: local.schedule.lastRun ?? null,
3230
+ lastResult: local.schedule.lastResult ?? null
3231
+ },
3150
3232
  verdict: verdict.state,
3151
3233
  next: verdict.next
3152
3234
  }, null, 2));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mutmutco/installer-launcher",
3
- "version": "1.4.3",
3
+ "version": "1.5.1",
4
4
  "description": "Single-executable (SEA) launcher: sign-in, gated payload fetch, self-update. One copy ships per product.",
5
5
  "type": "module",
6
6
  "license": "UNLICENSED",