omnius 1.0.604 → 1.0.605

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
@@ -363,6 +363,7 @@ __export(update_service_exports, {
363
363
  UpdateInProgressError: () => UpdateInProgressError,
364
364
  assertExactUpdateTarget: () => assertExactUpdateTarget,
365
365
  inspectGlobalOmniusInstall: () => inspectGlobalOmniusInstall,
366
+ readReconciledUpdateState: () => readReconciledUpdateState,
366
367
  readUpdateLogTail: () => readUpdateLogTail,
367
368
  readUpdateState: () => readUpdateState,
368
369
  releaseUpdateLock: () => releaseUpdateLock,
@@ -373,6 +374,7 @@ __export(update_service_exports, {
373
374
  updateStateFileIsPrivate: () => updateStateFileIsPrivate,
374
375
  updateStateIsFreshRunning: () => updateStateIsFreshRunning,
375
376
  updateStatusSnapshot: () => updateStatusSnapshot,
377
+ waitForUpdateTransaction: () => waitForUpdateTransaction,
376
378
  writeUpdateState: () => writeUpdateState
377
379
  });
378
380
  import { spawn, spawnSync } from "node:child_process";
@@ -613,6 +615,68 @@ function transition(current, patch, paths) {
613
615
  function permissionRemediation(evidence) {
614
616
  return /EACCES|EPERM|permission denied/i.test(evidence) ? "The discovered npm global prefix is not writable. Configure a user-owned npm prefix or rerun the explicit update from a privileged terminal; Omnius never silently elevates." : void 0;
615
617
  }
618
+ function installGlobalPackageStreaming(input) {
619
+ return new Promise((resolve87) => {
620
+ const stderrTail = [];
621
+ let settled = false;
622
+ let timedOut = false;
623
+ const finish = (exitCode, error) => {
624
+ if (settled) return;
625
+ settled = true;
626
+ clearTimeout(timer);
627
+ resolve87({ exitCode, ...error ? { error } : {} });
628
+ };
629
+ const child = spawn(
630
+ input.npmPath,
631
+ [
632
+ "install",
633
+ "-g",
634
+ `omnius@${input.targetVersion}`,
635
+ "--prefer-online",
636
+ "--no-audit",
637
+ "--no-fund",
638
+ "--no-progress"
639
+ ],
640
+ {
641
+ env: input.env,
642
+ stdio: ["ignore", "pipe", "pipe"],
643
+ windowsHide: true
644
+ }
645
+ );
646
+ child.stdout?.on("data", (chunk) => {
647
+ process.stdout.write(chunk);
648
+ });
649
+ child.stderr?.on("data", (chunk) => {
650
+ const text2 = String(chunk);
651
+ process.stderr.write(text2);
652
+ stderrTail.push(text2);
653
+ while (stderrTail.join("").length > 8e3 && stderrTail.length > 1) stderrTail.shift();
654
+ });
655
+ child.once("error", (error) => finish(-1, error.message));
656
+ child.once("close", (code8, signal) => {
657
+ const exitCode = code8 ?? -1;
658
+ const evidence = stderrTail.join("").trim();
659
+ finish(
660
+ exitCode,
661
+ exitCode === 0 ? void 0 : timedOut ? "npm install timed out after 5 minutes" : evidence || `npm install exited with ${signal ? `signal ${signal}` : `code ${exitCode}`}`
662
+ );
663
+ });
664
+ const timer = setTimeout(() => {
665
+ timedOut = true;
666
+ try {
667
+ child.kill("SIGTERM");
668
+ } catch {
669
+ }
670
+ const forceTimer = setTimeout(() => {
671
+ try {
672
+ child.kill("SIGKILL");
673
+ } catch {
674
+ }
675
+ }, 2500);
676
+ forceTimer.unref?.();
677
+ }, 5 * 6e4);
678
+ });
679
+ }
616
680
  async function runVerifiedUpdateTransaction(initial, dependencies, paths = resolveUpdatePaths()) {
617
681
  let state = initial;
618
682
  const target = assertExactUpdateTarget(initial.target_version);
@@ -632,27 +696,7 @@ async function runVerifiedUpdateTransaction(initial, dependencies, paths = resol
632
696
  npm_path: npmPath,
633
697
  ...prefixProbe.status === 0 && prefixProbe.stdout.trim() ? { npm_prefix: prefixProbe.stdout.trim() } : {}
634
698
  }, paths);
635
- const installer = dependencies.install ?? (async ({ npmPath: command, targetVersion, env: childEnv }) => {
636
- const result = runSync(
637
- command,
638
- [
639
- "install",
640
- "-g",
641
- `omnius@${targetVersion}`,
642
- "--prefer-online",
643
- "--no-audit",
644
- "--no-fund",
645
- "--no-progress"
646
- ],
647
- { env: childEnv, timeout: 5 * 6e4 }
648
- );
649
- if (result.stdout) process.stdout.write(result.stdout);
650
- if (result.stderr) process.stderr.write(result.stderr);
651
- return {
652
- exitCode: result.status ?? -1,
653
- ...result.error || result.status !== 0 ? { error: result.error?.message || result.stderr.trim() || `npm install exited with code ${result.status ?? -1}` } : {}
654
- };
655
- });
699
+ const installer = dependencies.install ?? installGlobalPackageStreaming;
656
700
  const installResult = await installer({ npmPath, targetVersion: target, env: env2 });
657
701
  state = transition(state, { installer_exit_code: installResult.exitCode }, paths);
658
702
  if (installResult.exitCode !== 0) {
@@ -721,6 +765,13 @@ function startDetachedGlobalUpdate(input, paths = resolveUpdatePaths()) {
721
765
  const operationId = randomUUID();
722
766
  const lockFd = acquireUpdateLock(operationId, paths);
723
767
  closeSync(lockFd);
768
+ const workerPath = fileURLToPath(new URL("./update-worker.js", import.meta.url));
769
+ if (!existsSync2(workerPath)) {
770
+ releaseUpdateLock(paths, operationId);
771
+ throw new Error(
772
+ `The coordinated update worker is missing at ${workerPath}. Reinstall Omnius from a package that ships dist/update-worker.js.`
773
+ );
774
+ }
724
775
  const now2 = (/* @__PURE__ */ new Date()).toISOString();
725
776
  let state = {
726
777
  schema_version: 1,
@@ -737,10 +788,15 @@ function startDetachedGlobalUpdate(input, paths = resolveUpdatePaths()) {
737
788
  tray_restarted: false
738
789
  };
739
790
  writeUpdateState(state, paths);
740
- const workerPath = fileURLToPath(new URL("./update-worker.js", import.meta.url));
741
791
  rotateUpdateLog(paths);
742
792
  const logFd = openSync(paths.logFile, "a", 384);
743
793
  try {
794
+ writeSync(
795
+ logFd,
796
+ `
797
+ [omnius update ${operationId}] ${input.fromVersion} -> ${targetVersion} ${now2}
798
+ `
799
+ );
744
800
  const child = spawn(
745
801
  process.execPath,
746
802
  [
@@ -805,6 +861,32 @@ function updateStatusSnapshot(paths = resolveUpdatePaths()) {
805
861
  log_tail: readUpdateLogTail(paths)
806
862
  };
807
863
  }
864
+ function readReconciledUpdateState(paths = resolveUpdatePaths()) {
865
+ const snapshot = updateStatusSnapshot(paths).state;
866
+ return snapshot.status === "idle" ? null : snapshot;
867
+ }
868
+ async function waitForUpdateTransaction(operationId, options2 = {}, paths = resolveUpdatePaths()) {
869
+ const pollIntervalMs = Math.max(25, options2.pollIntervalMs ?? 300);
870
+ const timeoutMs = Math.max(pollIntervalMs, options2.timeoutMs ?? 10 * 6e4);
871
+ const deadline = Date.now() + timeoutMs;
872
+ while (Date.now() <= deadline) {
873
+ const snapshot = updateStatusSnapshot(paths);
874
+ if (snapshot.state.status !== "idle") {
875
+ if (snapshot.state.operation_id !== operationId) {
876
+ throw new Error(
877
+ `Update operation changed from ${operationId} to ${snapshot.state.operation_id}`
878
+ );
879
+ }
880
+ options2.onProgress?.({
881
+ state: snapshot.state,
882
+ log_tail: snapshot.log_tail
883
+ });
884
+ if (snapshot.state.status !== "running") return snapshot.state;
885
+ }
886
+ await new Promise((resolve87) => setTimeout(resolve87, pollIntervalMs));
887
+ }
888
+ throw new Error(`Update operation ${operationId} did not finish within ${timeoutMs}ms`);
889
+ }
808
890
  function updateStateIsFreshRunning(state) {
809
891
  if (!state || state.status !== "running") return false;
810
892
  if (isPidAlive(state.pid)) return true;
@@ -679731,7 +679813,7 @@ async function runTrayForeground(explicitEndpoint) {
679731
679813
  let updateView = trayUpdatePresentation(
679732
679814
  health.version,
679733
679815
  availableUpdate?.latestVersion,
679734
- readUpdateState()
679816
+ readReconciledUpdateState()
679735
679817
  );
679736
679818
  const menuState = menuForHealth(health, endpoint, registered, updateView);
679737
679819
  const SysTray = await loadSysTray();
@@ -679773,7 +679855,7 @@ async function runTrayForeground(explicitEndpoint) {
679773
679855
  updateView = trayUpdatePresentation(
679774
679856
  health.version,
679775
679857
  availableUpdate?.latestVersion,
679776
- readUpdateState()
679858
+ readReconciledUpdateState()
679777
679859
  );
679778
679860
  menuState.updateItem.title = updateView.title;
679779
679861
  menuState.updateItem.tooltip = updateView.tooltip;
@@ -679842,7 +679924,7 @@ async function runTrayForeground(explicitEndpoint) {
679842
679924
  endpoint,
679843
679925
  trayWasRunning: true
679844
679926
  });
679845
- updateView = trayUpdatePresentation(currentVersion, targetVersion, readUpdateState());
679927
+ updateView = trayUpdatePresentation(currentVersion, targetVersion, readReconciledUpdateState());
679846
679928
  if (updateView.enabled) {
679847
679929
  updateView = {
679848
679930
  title: `Updating to v${targetVersion} — starting`,
@@ -679879,7 +679961,7 @@ async function runTrayForeground(explicitEndpoint) {
679879
679961
  updateView = trayUpdatePresentation(
679880
679962
  currentVersion,
679881
679963
  availableUpdate?.latestVersion,
679882
- readUpdateState()
679964
+ readReconciledUpdateState()
679883
679965
  );
679884
679966
  menuState.updateItem.title = updateView.title;
679885
679967
  menuState.updateItem.tooltip = updateView.tooltip;
@@ -747939,8 +748021,12 @@ async function handleUpdate(subcommand, ctx3) {
747939
748021
  renderInfo(`Omnius v${currentVersion} is already up to date.`);
747940
748022
  return;
747941
748023
  }
748024
+ const updateOverlay = startInstallOverlay(info.latestVersion);
748025
+ updateOverlay.setPhase("Preparing");
748026
+ updateOverlay.setProgress(0, 6);
748027
+ updateOverlay.setStatus("starting coordinated global update");
747942
748028
  try {
747943
- const [{ startDetachedGlobalUpdate: startDetachedGlobalUpdate2 }, { getTrayStatus: getTrayStatus2 }] = await Promise.all([
748029
+ const [{ startDetachedGlobalUpdate: startDetachedGlobalUpdate2, waitForUpdateTransaction: waitForUpdateTransaction2 }, { getTrayStatus: getTrayStatus2 }] = await Promise.all([
747944
748030
  Promise.resolve().then(() => (init_update_service(), update_service_exports)),
747945
748031
  Promise.resolve().then(() => (init_tray(), tray_exports))
747946
748032
  ]);
@@ -747951,11 +748037,52 @@ async function handleUpdate(subcommand, ctx3) {
747951
748037
  endpoint: trayStatus.endpoint,
747952
748038
  trayWasRunning: trayStatus.running
747953
748039
  });
748040
+ const phasePresentation = {
748041
+ queued: { label: "Preparing", progress: 0, status: "update worker queued" },
748042
+ installing: { label: "Package", progress: 1, status: `installing omnius@${info.latestVersion}` },
748043
+ package_verified: { label: "Verification", progress: 2, status: "global package and executable verified" },
748044
+ daemon_restarting: { label: "Daemon", progress: 3, status: "restarting shared daemon" },
748045
+ runtime_verified: { label: "Runtime", progress: 4, status: "daemon version and package hash verified" },
748046
+ tray_restarting: { label: "Indicator", progress: 5, status: "restarting system indicator" },
748047
+ completed: { label: "Complete", progress: 6, status: "global runtime update verified" },
748048
+ failed: { label: "Failed", progress: 6, status: "update verification failed" }
748049
+ };
748050
+ const marker = `[omnius update ${started.state.operation_id}]`;
748051
+ const terminal = await waitForUpdateTransaction2(
748052
+ started.state.operation_id,
748053
+ {
748054
+ pollIntervalMs: 250,
748055
+ timeoutMs: 10 * 6e4,
748056
+ onProgress(snapshot) {
748057
+ const view = phasePresentation[snapshot.state.phase];
748058
+ updateOverlay.setPhase(view.label);
748059
+ updateOverlay.setProgress(view.progress, 6);
748060
+ const operationLog = snapshot.log_tail.includes(marker) ? snapshot.log_tail.slice(snapshot.log_tail.lastIndexOf(marker) + marker.length) : snapshot.log_tail;
748061
+ const liveLine = operationLog.replace(/\x1B\][^\x07]*(?:\x07|\x1B\\)/g, "").replace(/\x1B\[[0-?]*[ -/]*[@-~]/g, "").split(/\r?\n/).map((line) => line.trim()).filter((line) => line && !/^npm notice/i.test(line)).at(-1);
748062
+ updateOverlay.setStatus((liveLine || view.status).slice(0, 100));
748063
+ }
748064
+ }
748065
+ );
748066
+ if (terminal.status === "failed") {
748067
+ updateOverlay.dismiss();
748068
+ renderError(`Update to v${info.latestVersion} failed: ${terminal.error || "unknown error"}`);
748069
+ if (terminal.remediation) renderWarning(terminal.remediation);
748070
+ return;
748071
+ }
748072
+ updateOverlay.stop("Package, daemon, and indicator verified");
748073
+ await new Promise((resolve87) => setTimeout(resolve87, 1e3));
748074
+ updateOverlay.dismiss();
747954
748075
  renderInfo(
747955
- `Global update to v${info.latestVersion} started (${started.state.operation_id.slice(0, 8)}). Track exact install/runtime verification at ${started.state.log_path}.`
748076
+ `Updated Omnius v${currentVersion} v${terminal.installed_version ?? info.latestVersion}; daemon v${terminal.daemon_version ?? info.latestVersion}${terminal.tray_was_running ? ", indicator restarted" : ""}.`
747956
748077
  );
748078
+ ctx3.contextSave?.();
748079
+ ctx3.savePendingTaskState?.();
748080
+ if (ctx3.hasActiveTask?.()) ctx3.abortActiveTask?.();
748081
+ ctx3.killEphemeral?.({ preserveInfrastructure: true });
748082
+ process.exit(120);
747957
748083
  } catch (error) {
747958
- renderError(`Update failed to start: ${error instanceof Error ? error.message : String(error)}`);
748084
+ updateOverlay.dismiss();
748085
+ renderError(`Update failed: ${error instanceof Error ? error.message : String(error)}`);
747959
748086
  }
747960
748087
  return;
747961
748088
  }