@pi-unipi/updater 3.0.0-alpha.36 → 3.0.0-alpha.38
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 +2 -2
- package/src/channel.ts +87 -0
- package/src/checker.ts +56 -31
- package/src/index.ts +2 -1
- package/src/installer.ts +10 -5
- package/src/remote-changelog.ts +19 -6
- package/src/tui/changelog-overlay.ts +4 -8
- package/src/tui/update-overlay.ts +1 -1
- package/types.ts +4 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pi-unipi/updater",
|
|
3
|
-
"version": "3.0.0-alpha.
|
|
3
|
+
"version": "3.0.0-alpha.38",
|
|
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.
|
|
37
|
+
"@pi-unipi/core": "3.0.0-alpha.38"
|
|
38
38
|
},
|
|
39
39
|
"peerDependencies": {
|
|
40
40
|
"@earendil-works/pi-coding-agent": "^0.87.1",
|
package/src/channel.ts
ADDED
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @pi-unipi/updater — release channel resolution (UNI-262)
|
|
3
|
+
*
|
|
4
|
+
* UniPi 3 ships on the npm `alpha` dist-tag while `latest` still points at the
|
|
5
|
+
* 2.x line. Comparing an alpha install against `latest` offered a downgrade.
|
|
6
|
+
* The channel to follow comes from, in order:
|
|
7
|
+
* 1. the pi settings package spec (`npm:@pi-unipi/unipi@alpha`) when its
|
|
8
|
+
* version part is a dist-tag (not a pinned semver),
|
|
9
|
+
* 2. the installed version's prerelease id (`3.0.0-alpha.36` → `alpha`),
|
|
10
|
+
* 3. `latest`.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
14
|
+
import { homedir } from "node:os";
|
|
15
|
+
import { join } from "node:path";
|
|
16
|
+
import { compareVersions } from "@pi-unipi/core";
|
|
17
|
+
|
|
18
|
+
export const UNIPI_PACKAGE = "@pi-unipi/unipi";
|
|
19
|
+
|
|
20
|
+
/** `3.0.0-alpha.36` → `alpha`; stable → null. */
|
|
21
|
+
export function prereleaseChannel(version: string): string | null {
|
|
22
|
+
const m = /^v?\d+\.\d+\.\d+-([0-9A-Za-z-]+)/.exec(version.trim());
|
|
23
|
+
if (!m) return null;
|
|
24
|
+
const id = m[1]!;
|
|
25
|
+
return /^\d+$/.test(id) ? null : id;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/** `npm:@pi-unipi/unipi@alpha` → `alpha`; pinned semver / no tag / other package → null. */
|
|
29
|
+
export function channelFromSpec(spec: string): string | null {
|
|
30
|
+
const s = spec.trim().replace(/^npm:/, "");
|
|
31
|
+
if (!s.startsWith(`${UNIPI_PACKAGE}@`)) return null;
|
|
32
|
+
const tag = s.slice(UNIPI_PACKAGE.length + 1).trim();
|
|
33
|
+
if (!tag) return null;
|
|
34
|
+
// A pinned or ranged version is not a channel.
|
|
35
|
+
if (/^[\^~<>=v]?\d/.test(tag) || tag === "*") return null;
|
|
36
|
+
return tag;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/** pi's agent dir (honours PI_CODING_AGENT_DIR like pi itself). */
|
|
40
|
+
export function piAgentDir(env: NodeJS.ProcessEnv = process.env): string {
|
|
41
|
+
const dir = env.PI_CODING_AGENT_DIR;
|
|
42
|
+
if (dir) return dir.startsWith("~") ? join(homedir(), dir.slice(1)) : dir;
|
|
43
|
+
return join(homedir(), ".pi", "agent");
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/** The UniPi package spec from pi settings (project first, then global), if any. */
|
|
47
|
+
export function readUnipiPackageSpec(cwd = process.cwd(), env: NodeJS.ProcessEnv = process.env): string | null {
|
|
48
|
+
const files = [join(cwd, ".pi", "settings.json"), join(piAgentDir(env), "settings.json")];
|
|
49
|
+
for (const file of files) {
|
|
50
|
+
try {
|
|
51
|
+
if (!existsSync(file)) continue;
|
|
52
|
+
const settings = JSON.parse(readFileSync(file, "utf8")) as { packages?: Array<string | { source?: string }> };
|
|
53
|
+
for (const pkg of settings.packages ?? []) {
|
|
54
|
+
const source = typeof pkg === "string" ? pkg : pkg?.source;
|
|
55
|
+
if (typeof source !== "string") continue;
|
|
56
|
+
const bare = source.trim().replace(/^npm:/, "");
|
|
57
|
+
if (bare === UNIPI_PACKAGE || bare.startsWith(`${UNIPI_PACKAGE}@`)) return source.trim();
|
|
58
|
+
}
|
|
59
|
+
} catch {
|
|
60
|
+
// Unreadable settings — try the next file.
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
return null;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/** The dist-tag this install follows. */
|
|
67
|
+
export function resolveChannel(installedVersion: string, spec: string | null): string {
|
|
68
|
+
return (spec ? channelFromSpec(spec) : null) ?? prereleaseChannel(installedVersion) ?? "latest";
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Pick the version to offer from a dist-tags map. A prerelease channel also
|
|
73
|
+
* considers `latest` so a graduation (3.0.0 stable) is still offered; a stable
|
|
74
|
+
* install never sees prerelease tags. Returns null if nothing usable.
|
|
75
|
+
*/
|
|
76
|
+
export function pickChannelVersion(distTags: Record<string, string> | undefined, channel: string): string | null {
|
|
77
|
+
if (!distTags) return null;
|
|
78
|
+
const candidates = [distTags[channel], channel !== "latest" ? distTags.latest : undefined]
|
|
79
|
+
.filter((v): v is string => typeof v === "string" && v.length > 0);
|
|
80
|
+
if (candidates.length === 0) return null;
|
|
81
|
+
return candidates.reduce((best, v) => (compareVersions(v, best) > 0 ? v : best));
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/** The `pi install` source for this channel. */
|
|
85
|
+
export function installSpec(channel: string): string {
|
|
86
|
+
return channel === "latest" ? `npm:${UNIPI_PACKAGE}` : `npm:${UNIPI_PACKAGE}@${channel}`;
|
|
87
|
+
}
|
package/src/checker.ts
CHANGED
|
@@ -1,90 +1,115 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @pi-unipi/updater — NPM registry checker
|
|
3
3
|
*
|
|
4
|
-
* Fetches
|
|
5
|
-
*
|
|
4
|
+
* Fetches the dist-tag of the install's release channel (UNI-262: an alpha
|
|
5
|
+
* install follows `alpha`, never `latest`), compares with the installed
|
|
6
|
+
* version, never offers a lower version, and respects the check interval.
|
|
6
7
|
*/
|
|
7
8
|
|
|
9
|
+
import { dirname } from "node:path";
|
|
10
|
+
import { fileURLToPath } from "node:url";
|
|
8
11
|
import { getInstalledPackageVersion } from "@pi-unipi/core";
|
|
9
12
|
import { loadConfig } from "./settings.js";
|
|
10
13
|
import { readLastCheck, writeLastCheck, isCheckDue } from "./cache.js";
|
|
11
14
|
import { compareVersions, isNewerVersion } from "./version.js";
|
|
15
|
+
import { pickChannelVersion, readUnipiPackageSpec, resolveChannel, UNIPI_PACKAGE } from "./channel.js";
|
|
12
16
|
import type { UpdateCheckResult } from "../types.js";
|
|
13
17
|
|
|
14
|
-
/**
|
|
15
|
-
|
|
18
|
+
/** npm registry base; `UNIPI_UPDATER_REGISTRY` overrides it (tests / mirrors). */
|
|
19
|
+
export function registryUrl(env: NodeJS.ProcessEnv = process.env): string {
|
|
20
|
+
const base = (env.UNIPI_UPDATER_REGISTRY || "https://registry.npmjs.org").replace(/\/+$/, "");
|
|
21
|
+
return `${base}/-/package/${UNIPI_PACKAGE}/dist-tags`;
|
|
22
|
+
}
|
|
16
23
|
|
|
17
24
|
/** Resolve the installed version of @pi-unipi/unipi */
|
|
18
|
-
function getInstalledVersion(): string {
|
|
19
|
-
//
|
|
20
|
-
const dir =
|
|
21
|
-
return getInstalledPackageVersion(dir,
|
|
25
|
+
export function getInstalledVersion(): string {
|
|
26
|
+
// fileURLToPath, not URL.pathname: on Windows the latter is "/C:/…".
|
|
27
|
+
const dir = dirname(dirname(fileURLToPath(import.meta.url)));
|
|
28
|
+
return getInstalledPackageVersion(dir, UNIPI_PACKAGE);
|
|
22
29
|
}
|
|
23
30
|
|
|
31
|
+
/** The channel this install follows. */
|
|
32
|
+
export function currentChannel(currentVersion = getInstalledVersion()): string {
|
|
33
|
+
return resolveChannel(currentVersion, readUnipiPackageSpec());
|
|
34
|
+
}
|
|
24
35
|
|
|
25
36
|
/** Build an update result without ever reporting downgrades as updates. */
|
|
26
|
-
function toUpdateResult(latestVersion: string, currentVersion: string): UpdateCheckResult {
|
|
37
|
+
function toUpdateResult(latestVersion: string, currentVersion: string, channel: string): UpdateCheckResult {
|
|
27
38
|
return {
|
|
28
39
|
updateAvailable: isNewerVersion(latestVersion, currentVersion),
|
|
29
40
|
latestVersion,
|
|
30
41
|
currentVersion,
|
|
42
|
+
channel,
|
|
31
43
|
};
|
|
32
44
|
}
|
|
33
45
|
|
|
46
|
+
export interface CheckOptions {
|
|
47
|
+
fetchImpl?: typeof fetch;
|
|
48
|
+
currentVersion?: string;
|
|
49
|
+
channel?: string;
|
|
50
|
+
/** Ignore the interval cache. */
|
|
51
|
+
force?: boolean;
|
|
52
|
+
}
|
|
53
|
+
|
|
34
54
|
/**
|
|
35
|
-
* Check for updates
|
|
55
|
+
* Check for updates on this install's channel.
|
|
36
56
|
* Respects check interval — skips if last check was recent.
|
|
37
|
-
* Returns update status and version info.
|
|
38
57
|
*/
|
|
39
|
-
export async function checkForUpdates(): Promise<UpdateCheckResult> {
|
|
40
|
-
const currentVersion = getInstalledVersion();
|
|
58
|
+
export async function checkForUpdates(opts: CheckOptions = {}): Promise<UpdateCheckResult> {
|
|
59
|
+
const currentVersion = opts.currentVersion ?? getInstalledVersion();
|
|
60
|
+
const channel = opts.channel ?? currentChannel(currentVersion);
|
|
41
61
|
|
|
42
62
|
try {
|
|
43
63
|
const config = loadConfig();
|
|
44
64
|
|
|
45
|
-
//
|
|
46
|
-
//
|
|
47
|
-
// refresh:
|
|
48
|
-
//
|
|
65
|
+
// A cache is only reused for the same channel (an old cache written by
|
|
66
|
+
// the pre-UNI-262 checker holds `latest`). If the cached version is older
|
|
67
|
+
// than the installed one, refresh: right after a release the cache has
|
|
68
|
+
// not seen the new dist-tag yet.
|
|
49
69
|
const cache = readLastCheck();
|
|
50
|
-
if (cache && !isCheckDue(config.checkIntervalMs)) {
|
|
70
|
+
if (!opts.force && cache && (cache.channel ?? "latest") === channel && !isCheckDue(config.checkIntervalMs)) {
|
|
51
71
|
if (compareVersions(cache.latestVersion, currentVersion) >= 0) {
|
|
52
|
-
return toUpdateResult(cache.latestVersion, currentVersion);
|
|
72
|
+
return toUpdateResult(cache.latestVersion, currentVersion, channel);
|
|
53
73
|
}
|
|
54
74
|
}
|
|
55
75
|
|
|
56
|
-
|
|
57
|
-
const response = await
|
|
76
|
+
const fetchImpl = opts.fetchImpl ?? fetch;
|
|
77
|
+
const response = await fetchImpl(registryUrl(), {
|
|
58
78
|
headers: { Accept: "application/json" },
|
|
59
|
-
signal: AbortSignal.timeout(10000),
|
|
79
|
+
signal: AbortSignal.timeout(10000),
|
|
60
80
|
});
|
|
61
81
|
|
|
62
82
|
if (!response.ok) {
|
|
63
83
|
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
|
64
84
|
}
|
|
65
85
|
|
|
66
|
-
const
|
|
67
|
-
|
|
86
|
+
const body = await response.json() as Record<string, unknown>;
|
|
87
|
+
// The dist-tags endpoint returns the map itself; the full packument nests it.
|
|
88
|
+
const tags = (body["dist-tags"] ?? body) as Record<string, string>;
|
|
89
|
+
const latestVersion = pickChannelVersion(tags, channel);
|
|
68
90
|
|
|
69
91
|
if (!latestVersion) {
|
|
70
|
-
throw new Error(
|
|
92
|
+
throw new Error(`No dist-tag "${channel}" in npm response`);
|
|
71
93
|
}
|
|
72
94
|
|
|
73
|
-
// Write cache
|
|
74
95
|
writeLastCheck({
|
|
75
96
|
lastCheck: new Date().toISOString(),
|
|
76
97
|
latestVersion,
|
|
98
|
+
channel,
|
|
99
|
+
skippedVersion: cache?.skippedVersion,
|
|
77
100
|
});
|
|
78
101
|
|
|
79
|
-
return toUpdateResult(latestVersion, currentVersion);
|
|
102
|
+
return toUpdateResult(latestVersion, currentVersion, channel);
|
|
80
103
|
} catch (err: unknown) {
|
|
81
|
-
// Network error —
|
|
82
|
-
// downgrade from a stale cache.
|
|
104
|
+
// Network error — reuse cached info for this channel only, and never
|
|
105
|
+
// suggest a downgrade from a stale cache.
|
|
83
106
|
const cache = readLastCheck();
|
|
107
|
+
const usable = cache && (cache.channel ?? "latest") === channel ? cache : null;
|
|
84
108
|
return {
|
|
85
|
-
updateAvailable:
|
|
86
|
-
latestVersion:
|
|
109
|
+
updateAvailable: usable ? isNewerVersion(usable.latestVersion, currentVersion) : false,
|
|
110
|
+
latestVersion: usable?.latestVersion ?? "",
|
|
87
111
|
currentVersion,
|
|
112
|
+
channel,
|
|
88
113
|
error: err instanceof Error ? err.message : String(err) || "Unknown error",
|
|
89
114
|
};
|
|
90
115
|
}
|
package/src/index.ts
CHANGED
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
* /unipi:readme and /unipi:changelog.
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
|
+
import { fileURLToPath } from "node:url";
|
|
11
12
|
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
12
13
|
import {
|
|
13
14
|
bus,
|
|
@@ -25,7 +26,7 @@ import { renderUpdateOverlay } from "./tui/update-overlay.js";
|
|
|
25
26
|
import { loadUpdateChangelog } from "./remote-changelog.js";
|
|
26
27
|
|
|
27
28
|
/** Package version */
|
|
28
|
-
const VERSION = getPackageVersion(new URL("..", import.meta.url)
|
|
29
|
+
const VERSION = getPackageVersion(fileURLToPath(new URL("..", import.meta.url)));
|
|
29
30
|
|
|
30
31
|
export default function updaterExtension(pi: ExtensionAPI): void {
|
|
31
32
|
|
package/src/installer.ts
CHANGED
|
@@ -6,7 +6,10 @@
|
|
|
6
6
|
|
|
7
7
|
import { exec } from "child_process";
|
|
8
8
|
import { promisify } from "util";
|
|
9
|
+
import { dirname } from "node:path";
|
|
10
|
+
import { fileURLToPath } from "node:url";
|
|
9
11
|
import { getInstalledPackageVersion } from "@pi-unipi/core";
|
|
12
|
+
import { installSpec } from "./channel.js";
|
|
10
13
|
import type { InstallResult } from "../types.js";
|
|
11
14
|
|
|
12
15
|
const execAsync = promisify(exec);
|
|
@@ -15,16 +18,18 @@ const execAsync = promisify(exec);
|
|
|
15
18
|
const INSTALL_TIMEOUT_MS = 60000;
|
|
16
19
|
|
|
17
20
|
/**
|
|
18
|
-
* Install the
|
|
19
|
-
* Uses pi CLI: `pi install npm:@pi-unipi/unipi`
|
|
21
|
+
* Install the newest @pi-unipi/unipi on `channel`.
|
|
22
|
+
* Uses pi CLI: `pi install npm:@pi-unipi/unipi[@<channel>]`
|
|
20
23
|
* Returns structured result with success/failure info.
|
|
21
24
|
*/
|
|
22
|
-
export async function installUpdate(): Promise<InstallResult> {
|
|
23
|
-
const thisDir =
|
|
25
|
+
export async function installUpdate(channel = "latest"): Promise<InstallResult> {
|
|
26
|
+
const thisDir = dirname(dirname(fileURLToPath(import.meta.url)));
|
|
24
27
|
|
|
25
28
|
try {
|
|
29
|
+
// Install from the same channel the check used — a bare spec would pull
|
|
30
|
+
// `latest` and downgrade an alpha install (UNI-262).
|
|
26
31
|
await execAsync(
|
|
27
|
-
|
|
32
|
+
`pi install ${installSpec(channel)}`,
|
|
28
33
|
{
|
|
29
34
|
timeout: INSTALL_TIMEOUT_MS,
|
|
30
35
|
env: { ...process.env },
|
package/src/remote-changelog.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
2
2
|
import { homedir } from "node:os";
|
|
3
3
|
import { join } from "node:path";
|
|
4
|
-
import { UPDATER_DIRS } from "@pi-unipi/core";
|
|
4
|
+
import { UPDATER_DIRS, compareVersions } from "@pi-unipi/core";
|
|
5
5
|
import type { ChangelogEntry } from "../types.js";
|
|
6
6
|
import { getNewerVersions, parseChangelogContent, parseChangelog, resolveChangelogPath } from "./changelog.js";
|
|
7
7
|
|
|
@@ -17,6 +17,16 @@ function cacheDirectory(opts?: RemoteChangelogOptions): string {
|
|
|
17
17
|
return (opts?.cacheDir ?? UPDATER_DIRS.CACHE).replace("~", homedir());
|
|
18
18
|
}
|
|
19
19
|
|
|
20
|
+
/**
|
|
21
|
+
* Branches to try when the release tag is missing. Prereleases are pushed to
|
|
22
|
+
* a channel branch (`3.0.0-alpha.36` → `v3.0.0-alpha`); `main` holds the
|
|
23
|
+
* stable line only, so it would show the wrong changelog to an alpha install.
|
|
24
|
+
*/
|
|
25
|
+
export function fallbackRefs(version: string): string[] {
|
|
26
|
+
const m = /^v?(\d+\.\d+\.\d+-[0-9A-Za-z-]+)\.[0-9A-Za-z.-]+$/.exec(version);
|
|
27
|
+
return m ? [`v${m[1]}`, "main"] : ["main"];
|
|
28
|
+
}
|
|
29
|
+
|
|
20
30
|
export async function fetchRemoteChangelog(version: string, opts: RemoteChangelogOptions = {}): Promise<string | null> {
|
|
21
31
|
const cacheDir = cacheDirectory(opts);
|
|
22
32
|
const cachePath = join(cacheDir, `changelog-${version}.md`);
|
|
@@ -31,10 +41,11 @@ export async function fetchRemoteChangelog(version: string, opts: RemoteChangelo
|
|
|
31
41
|
try {
|
|
32
42
|
let response = await fetchImpl(`${CHANGELOG_RAW_BASE}/v${version}/CHANGELOG.md`, { signal });
|
|
33
43
|
// The release tag is immutable, so its changelog is safe to cache forever.
|
|
34
|
-
// The
|
|
44
|
+
// The branch fallbacks (tag not pushed yet) are not: never cache them.
|
|
35
45
|
const cacheable = response.ok;
|
|
36
|
-
|
|
37
|
-
|
|
46
|
+
for (const ref of fallbackRefs(version)) {
|
|
47
|
+
if (response.status !== 404) break;
|
|
48
|
+
response = await fetchImpl(`${CHANGELOG_RAW_BASE}/${ref}/CHANGELOG.md`, { signal });
|
|
38
49
|
}
|
|
39
50
|
if (!response.ok) return null;
|
|
40
51
|
const content = await response.text();
|
|
@@ -55,7 +66,9 @@ export async function loadUpdateChangelog(
|
|
|
55
66
|
): Promise<ChangelogEntry[]> {
|
|
56
67
|
const remote = await fetchRemoteChangelog(latestVersion, opts);
|
|
57
68
|
const entries = remote === null ? [] : parseChangelogContent(remote);
|
|
58
|
-
const
|
|
69
|
+
const upTo = (list: ChangelogEntry[]) =>
|
|
70
|
+
list.filter((e) => e.version === "Unreleased" || compareVersions(e.version, latestVersion) <= 0);
|
|
71
|
+
const newer = remote === null ? [] : upTo(getNewerVersions(entries, currentVersion));
|
|
59
72
|
if (newer.length > 0) return newer;
|
|
60
|
-
return getNewerVersions(parseChangelog(resolveChangelogPath()), currentVersion);
|
|
73
|
+
return upTo(getNewerVersions(parseChangelog(resolveChangelogPath()), currentVersion));
|
|
61
74
|
}
|
|
@@ -5,10 +5,11 @@
|
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
import { existsSync } from "fs";
|
|
8
|
+
import { fileURLToPath } from "node:url";
|
|
8
9
|
import type { Theme } from "@earendil-works/pi-coding-agent";
|
|
9
10
|
import { parseChangelog, resolveChangelogPath } from "../changelog.js";
|
|
10
11
|
import { renderMarkdown } from "../markdown.js";
|
|
11
|
-
import { getInstalledPackageVersion } from "@pi-unipi/core";
|
|
12
|
+
import { getInstalledPackageVersion, compareVersions } from "@pi-unipi/core";
|
|
12
13
|
import { createListDetailOverlay } from "./list-detail-overlay.js";
|
|
13
14
|
import type { ChangelogEntry } from "../../types.js";
|
|
14
15
|
|
|
@@ -17,7 +18,7 @@ import type { ChangelogEntry } from "../../types.js";
|
|
|
17
18
|
*/
|
|
18
19
|
export function renderChangelogOverlay() {
|
|
19
20
|
const installedVersion = getInstalledPackageVersion(
|
|
20
|
-
new URL("..", import.meta.url)
|
|
21
|
+
fileURLToPath(new URL("..", import.meta.url)),
|
|
21
22
|
"@pi-unipi/unipi",
|
|
22
23
|
);
|
|
23
24
|
|
|
@@ -39,12 +40,7 @@ export function renderChangelogOverlay() {
|
|
|
39
40
|
} else if (entry.version === installedVersion) {
|
|
40
41
|
label = theme.fg("success", "✓ Current");
|
|
41
42
|
} else {
|
|
42
|
-
const
|
|
43
|
-
const pb = installedVersion.split(".").map(Number);
|
|
44
|
-
const isNewer =
|
|
45
|
-
pa[0]! > pb[0]! ||
|
|
46
|
-
(pa[0] === pb[0] && pa[1]! > pb[1]!) ||
|
|
47
|
-
(pa[0] === pb[0] && pa[1] === pb[1] && pa[2]! > pb[2]!);
|
|
43
|
+
const isNewer = compareVersions(entry.version, installedVersion) > 0;
|
|
48
44
|
label = isNewer ? theme.fg("warning", "↑ New") : "";
|
|
49
45
|
}
|
|
50
46
|
|
|
@@ -93,7 +93,7 @@ export function renderUpdateOverlay(checkResult: UpdateCheckResult, providedNewe
|
|
|
93
93
|
state.installing = true;
|
|
94
94
|
tui.requestRender();
|
|
95
95
|
|
|
96
|
-
const result = await installUpdate();
|
|
96
|
+
const result = await installUpdate(state.result.channel ?? "latest");
|
|
97
97
|
if (result.success) {
|
|
98
98
|
done({ updated: true });
|
|
99
99
|
} else {
|
package/types.ts
CHANGED
|
@@ -20,6 +20,8 @@ export interface LastCheckCache {
|
|
|
20
20
|
latestVersion: string;
|
|
21
21
|
/** Version the user chose to skip */
|
|
22
22
|
skippedVersion?: string;
|
|
23
|
+
/** npm dist-tag the check followed (absent in pre-UNI-262 caches = "latest") */
|
|
24
|
+
channel?: string;
|
|
23
25
|
}
|
|
24
26
|
|
|
25
27
|
/** A parsed changelog version entry */
|
|
@@ -54,6 +56,8 @@ export interface UpdateCheckResult {
|
|
|
54
56
|
latestVersion: string;
|
|
55
57
|
/** Currently installed version */
|
|
56
58
|
currentVersion: string;
|
|
59
|
+
/** npm dist-tag this install follows ("alpha", "latest", …) */
|
|
60
|
+
channel?: string;
|
|
57
61
|
/** Error message if check failed */
|
|
58
62
|
error?: string;
|
|
59
63
|
}
|