@pi-unipi/updater 3.0.0-alpha.39 → 3.0.0-alpha.40

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi-unipi/updater",
3
- "version": "3.0.0-alpha.39",
3
+ "version": "3.0.0-alpha.40",
4
4
  "description": "Auto-updater, changelog browser, and readme browser for Unipi — checks npm registry, renders CHANGELOG.md and README.md files in TUI overlays",
5
5
  "type": "module",
6
6
  "main": "index.ts",
@@ -34,7 +34,7 @@
34
34
  "access": "public"
35
35
  },
36
36
  "dependencies": {
37
- "@pi-unipi/core": "3.0.0-alpha.39"
37
+ "@pi-unipi/core": "3.0.0-alpha.40"
38
38
  },
39
39
  "peerDependencies": {
40
40
  "@earendil-works/pi-coding-agent": "^0.87.1",
package/src/channel.ts CHANGED
@@ -81,6 +81,19 @@ export function pickChannelVersion(distTags: Record<string, string> | undefined,
81
81
  return candidates.reduce((best, v) => (compareVersions(v, best) > 0 ? v : best));
82
82
  }
83
83
 
84
+ /**
85
+ * UNI-272: which dist-tag the picked version came from. An alpha install
86
+ * offered 3.0.0 from `latest` (graduation) must install `latest` — installing
87
+ * `@alpha` again would reinstall the alpha it already has, and keep the
88
+ * install on the alpha channel forever. Ties go to the install's own channel.
89
+ */
90
+ export function pickInstallTag(distTags: Record<string, string> | undefined, channel: string): string {
91
+ const picked = pickChannelVersion(distTags, channel);
92
+ if (!picked || !distTags) return channel;
93
+ if (distTags[channel] === picked) return channel;
94
+ return distTags.latest === picked ? "latest" : channel;
95
+ }
96
+
84
97
  /** The `pi install` source for this channel. */
85
98
  export function installSpec(channel: string): string {
86
99
  return channel === "latest" ? `npm:${UNIPI_PACKAGE}` : `npm:${UNIPI_PACKAGE}@${channel}`;
package/src/checker.ts CHANGED
@@ -12,7 +12,7 @@ import { getInstalledPackageVersion } from "@pi-unipi/core";
12
12
  import { loadConfig } from "./settings.js";
13
13
  import { readLastCheck, writeLastCheck, isCheckDue } from "./cache.js";
14
14
  import { compareVersions, isNewerVersion } from "./version.js";
15
- import { pickChannelVersion, readUnipiPackageSpec, resolveChannel, UNIPI_PACKAGE } from "./channel.js";
15
+ import { pickChannelVersion, pickInstallTag, readUnipiPackageSpec, resolveChannel, UNIPI_PACKAGE } from "./channel.js";
16
16
  import type { UpdateCheckResult } from "../types.js";
17
17
 
18
18
  /** npm registry base; `UNIPI_UPDATER_REGISTRY` overrides it (tests / mirrors). */
@@ -34,12 +34,13 @@ export function currentChannel(currentVersion = getInstalledVersion()): string {
34
34
  }
35
35
 
36
36
  /** Build an update result without ever reporting downgrades as updates. */
37
- function toUpdateResult(latestVersion: string, currentVersion: string, channel: string): UpdateCheckResult {
37
+ function toUpdateResult(latestVersion: string, currentVersion: string, channel: string, installTag?: string): UpdateCheckResult {
38
38
  return {
39
39
  updateAvailable: isNewerVersion(latestVersion, currentVersion),
40
40
  latestVersion,
41
41
  currentVersion,
42
42
  channel,
43
+ installTag: installTag ?? channel,
43
44
  };
44
45
  }
45
46
 
@@ -69,7 +70,7 @@ export async function checkForUpdates(opts: CheckOptions = {}): Promise<UpdateCh
69
70
  const cache = readLastCheck();
70
71
  if (!opts.force && cache && (cache.channel ?? "latest") === channel && !isCheckDue(config.checkIntervalMs)) {
71
72
  if (compareVersions(cache.latestVersion, currentVersion) >= 0) {
72
- return toUpdateResult(cache.latestVersion, currentVersion, channel);
73
+ return toUpdateResult(cache.latestVersion, currentVersion, channel, cache.installTag);
73
74
  }
74
75
  }
75
76
 
@@ -87,6 +88,7 @@ export async function checkForUpdates(opts: CheckOptions = {}): Promise<UpdateCh
87
88
  // The dist-tags endpoint returns the map itself; the full packument nests it.
88
89
  const tags = (body["dist-tags"] ?? body) as Record<string, string>;
89
90
  const latestVersion = pickChannelVersion(tags, channel);
91
+ const installTag = pickInstallTag(tags, channel);
90
92
 
91
93
  if (!latestVersion) {
92
94
  throw new Error(`No dist-tag "${channel}" in npm response`);
@@ -96,10 +98,11 @@ export async function checkForUpdates(opts: CheckOptions = {}): Promise<UpdateCh
96
98
  lastCheck: new Date().toISOString(),
97
99
  latestVersion,
98
100
  channel,
101
+ installTag,
99
102
  skippedVersion: cache?.skippedVersion,
100
103
  });
101
104
 
102
- return toUpdateResult(latestVersion, currentVersion, channel);
105
+ return toUpdateResult(latestVersion, currentVersion, channel, installTag);
103
106
  } catch (err: unknown) {
104
107
  // Network error — reuse cached info for this channel only, and never
105
108
  // suggest a downgrade from a stale cache.
@@ -110,6 +113,7 @@ export async function checkForUpdates(opts: CheckOptions = {}): Promise<UpdateCh
110
113
  latestVersion: usable?.latestVersion ?? "",
111
114
  currentVersion,
112
115
  channel,
116
+ installTag: usable?.installTag ?? channel,
113
117
  error: err instanceof Error ? err.message : String(err) || "Unknown error",
114
118
  };
115
119
  }
@@ -93,7 +93,8 @@ export function renderUpdateOverlay(checkResult: UpdateCheckResult, providedNewe
93
93
  state.installing = true;
94
94
  tui.requestRender();
95
95
 
96
- const result = await installUpdate(state.result.channel ?? "latest");
96
+ // UNI-272: the tag the offered version came from (alpha → 3.0.0 stable installs `latest`).
97
+ const result = await installUpdate(state.result.installTag ?? state.result.channel ?? "latest");
97
98
  if (result.success) {
98
99
  done({ updated: true });
99
100
  } else {
package/types.ts CHANGED
@@ -22,6 +22,8 @@ export interface LastCheckCache {
22
22
  skippedVersion?: string;
23
23
  /** npm dist-tag the check followed (absent in pre-UNI-262 caches = "latest") */
24
24
  channel?: string;
25
+ /** UNI-272: dist-tag to install the offered version from (≠ channel on graduation). */
26
+ installTag?: string;
25
27
  }
26
28
 
27
29
  /** A parsed changelog version entry */
@@ -58,6 +60,9 @@ export interface UpdateCheckResult {
58
60
  currentVersion: string;
59
61
  /** npm dist-tag this install follows ("alpha", "latest", …) */
60
62
  channel?: string;
63
+ /** UNI-272: the dist-tag the offered version comes from — install from this
64
+ * (an alpha install offered 3.0.0 stable installs `latest`, leaving alpha). */
65
+ installTag?: string;
61
66
  /** Error message if check failed */
62
67
  error?: string;
63
68
  }