@remodex/rmx 1.0.2 → 1.0.3

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/src/update/job.ts CHANGED
@@ -212,7 +212,7 @@ async function waitForGhostListenClear(
212
212
  return { ok: false, accessDenied };
213
213
  }
214
214
 
215
- function packageLauncherPath(): string {
215
+ export function packageLauncherPath(): string {
216
216
  // This module lives at src/update/job.ts — the launcher is <pkg-root>/bin/ocx.mjs.
217
217
  // After `npm install -g`, import.meta.url can still point at npm's renamed temp
218
218
  // tree (`@remodex/.rmx-*` or the legacy `@bitkyc08/.opencodex-*`). Prefer the
@@ -374,8 +374,8 @@ function renderSafeCommand(value: string): string {
374
374
  part === "$" || part === "<path>"
375
375
  || /^(?:npm|bun|pnpm|yarn|node)$/.test(part)
376
376
  || /^-{1,2}[\w-]+$/.test(part)
377
- || /^(?:install|add|update|i)$/.test(part)
378
- || /^opencodex(?:@[\w.\-]+)?$/.test(part)
377
+ || /^(?:install|add|update|i|__exact-update)$/.test(part)
378
+ || /^(?:@remodex\/rmx|opencodex)(?:@[\w.\-]+)?$/.test(part)
379
379
  || /^(?:latest|preview|next|beta)$/.test(part)
380
380
  || /^\d[\w.\-]*$/.test(part));
381
381
  if (allowed) return rendered.join(" ");
@@ -445,9 +445,12 @@ export function updateExecutionCommand(
445
445
  ): { bin: string; args: string[]; display: string } {
446
446
  if (installer === "npm") {
447
447
  const bin = nodeBin();
448
- const args = [launcher, "update", "--tag", channel];
449
- // The Node launcher self-update re-resolves the tag at its own time — a residual
450
- // divergence window this path cannot close (documented, not claimed immutable).
448
+ const args = resolvedVersion
449
+ ? [launcher, "__exact-update", resolvedVersion]
450
+ : [launcher, "update", "--tag", channel];
451
+ // A scheduled update passes a registry-resolved version through the hidden exact
452
+ // launcher path. Interactive/dashboard updates without a resolved target retain
453
+ // the historical tag-based launcher behavior.
451
454
  return { bin, args, display: formatCommand(bin, args) };
452
455
  }
453
456
  if (installer === "bun") {
@@ -1742,6 +1745,12 @@ export interface GuiUpdateWorkerIo {
1742
1745
  checkForUpdateFn?: (channel: Channel) => ReturnType<typeof checkForUpdate>;
1743
1746
  /** Bypass the registry integrity probe, which runs before the cache gate and needs network. */
1744
1747
  integrityFn?: (version: string | null) => ReturnType<typeof checkUpdatePackageIntegrity>;
1748
+ /**
1749
+ * Immutable target selected by a caller that already resolved a registry version.
1750
+ * npm uses the hidden Node-launcher path so the package manager cannot re-resolve a
1751
+ * moving dist-tag between the check and install.
1752
+ */
1753
+ exactVersion?: string;
1745
1754
  runCommandFn?: (
1746
1755
  job: UpdateJobState,
1747
1756
  bin: string,
@@ -1811,7 +1820,9 @@ export async function runGuiUpdateWorker(
1811
1820
  ? `Integrity pre-flight skipped: ${integrity.reason}. Proceeding best-effort.`
1812
1821
  : `Verified ${PKG}@${check.latestVersion} integrity metadata ${integrity.integrity.slice(0, 24)}…`;
1813
1822
 
1814
- const cmd = updateExecutionCommand(check.installer, channel, undefined, check.latestVersion);
1823
+ const cmd = io.exactVersion === undefined
1824
+ ? updateExecutionCommand(check.installer, channel, undefined, check.latestVersion)
1825
+ : updateExecutionCommand(check.installer, channel, undefined, io.exactVersion);
1815
1826
  job = updateJob(job, {
1816
1827
  currentVersion: check.currentVersion,
1817
1828
  latestVersion: check.latestVersion,