claudeup 4.27.0 → 4.27.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/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "claudeup",
|
|
3
|
-
"version": "4.27.
|
|
3
|
+
"version": "4.27.1",
|
|
4
4
|
"description": "TUI tool for managing Claude Code plugins, MCPs, and configuration",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "src/main.tsx",
|
|
@@ -64,8 +64,8 @@
|
|
|
64
64
|
"typescript": "^5.6.3"
|
|
65
65
|
},
|
|
66
66
|
"optionalDependencies": {
|
|
67
|
-
"claudeup-darwin-arm64": "4.27.
|
|
68
|
-
"claudeup-darwin-x64": "4.27.
|
|
69
|
-
"claudeup-linux-x64": "4.27.
|
|
67
|
+
"claudeup-darwin-arm64": "4.27.1",
|
|
68
|
+
"claudeup-darwin-x64": "4.27.1",
|
|
69
|
+
"claudeup-linux-x64": "4.27.1"
|
|
70
70
|
}
|
|
71
71
|
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Guards the update-check refresh fix. checkForUpdates caches within a session
|
|
3
|
+
* (so we don't hammer the registry), but `force` bypasses that cache so a
|
|
4
|
+
* release landing mid-session is actually seen — the 4.25-vs-4.26 banner
|
|
5
|
+
* staleness. (The fetch itself also sets `cache: "no-store"` so a CDN-stale
|
|
6
|
+
* response can't mask a fresh release.)
|
|
7
|
+
*/
|
|
8
|
+
import { afterEach, describe, expect, it } from "bun:test";
|
|
9
|
+
import { checkForUpdates } from "../services/version-check.js";
|
|
10
|
+
|
|
11
|
+
describe("checkForUpdates — session cache vs forced re-check", () => {
|
|
12
|
+
const realFetch = globalThis.fetch;
|
|
13
|
+
afterEach(() => {
|
|
14
|
+
globalThis.fetch = realFetch;
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
it("serves the session cache by default and re-fetches only when forced", async () => {
|
|
18
|
+
let calls = 0;
|
|
19
|
+
let latest = "9.9.9";
|
|
20
|
+
globalThis.fetch = (async () => {
|
|
21
|
+
calls++;
|
|
22
|
+
return new Response(JSON.stringify({ version: latest }), {
|
|
23
|
+
status: 200,
|
|
24
|
+
headers: { "content-type": "application/json" },
|
|
25
|
+
});
|
|
26
|
+
}) as typeof fetch;
|
|
27
|
+
|
|
28
|
+
// Force the first call so the test is independent of any cache another
|
|
29
|
+
// test in the same process may have primed.
|
|
30
|
+
const first = await checkForUpdates(true);
|
|
31
|
+
expect(calls).toBe(1);
|
|
32
|
+
expect(first.latestVersion).toBe("9.9.9");
|
|
33
|
+
|
|
34
|
+
// Default call is served from the session cache — no new fetch.
|
|
35
|
+
const cached = await checkForUpdates();
|
|
36
|
+
expect(calls).toBe(1);
|
|
37
|
+
expect(cached.latestVersion).toBe("9.9.9");
|
|
38
|
+
|
|
39
|
+
// A release lands; a forced re-check bypasses the cache and sees it.
|
|
40
|
+
latest = "9.9.10";
|
|
41
|
+
const forced = await checkForUpdates(true);
|
|
42
|
+
expect(calls).toBe(2);
|
|
43
|
+
expect(forced.latestVersion).toBe("9.9.10");
|
|
44
|
+
});
|
|
45
|
+
});
|
|
@@ -33,6 +33,9 @@ async function fetchLatestVersion(): Promise<string | null> {
|
|
|
33
33
|
signal: controller.signal,
|
|
34
34
|
headers: {
|
|
35
35
|
Accept: "application/json",
|
|
36
|
+
// Always revalidate — a CDN/HTTP-cached response serves a stale
|
|
37
|
+
// "latest" right after a release (the 4.25-vs-4.26 flicker).
|
|
38
|
+
"Cache-Control": "no-cache",
|
|
36
39
|
},
|
|
37
40
|
});
|
|
38
41
|
|
|
@@ -70,12 +73,15 @@ function getUpdateType(
|
|
|
70
73
|
}
|
|
71
74
|
|
|
72
75
|
/**
|
|
73
|
-
* Check if a new version is available
|
|
74
|
-
* Returns cached result
|
|
76
|
+
* Check if a new version is available.
|
|
77
|
+
* Returns the session-cached result unless `force` requests a fresh check.
|
|
75
78
|
*/
|
|
76
|
-
export async function checkForUpdates(
|
|
77
|
-
|
|
78
|
-
|
|
79
|
+
export async function checkForUpdates(
|
|
80
|
+
force = false,
|
|
81
|
+
): Promise<VersionCheckResult> {
|
|
82
|
+
// Return the session-cached result unless a fresh check is forced — a periodic
|
|
83
|
+
// re-check passes force:true so a release landing mid-session updates the banner.
|
|
84
|
+
if (!force && cachedResult) {
|
|
79
85
|
return cachedResult;
|
|
80
86
|
}
|
|
81
87
|
|
package/src/ui/App.tsx
CHANGED
|
@@ -348,11 +348,22 @@ function AppContent({ onExit }: AppContentProps) {
|
|
|
348
348
|
} | null>(null);
|
|
349
349
|
const gitignoreModal = useGitignoreModal();
|
|
350
350
|
|
|
351
|
-
// Check for updates on startup (
|
|
351
|
+
// Check for updates on startup, then re-check periodically (force-bypassing
|
|
352
|
+
// the session cache) so the banner reflects a release that lands while
|
|
353
|
+
// claudeup is left open — otherwise it shows the stale startup value.
|
|
352
354
|
useEffect(() => {
|
|
353
355
|
checkForUpdates()
|
|
354
356
|
.then(setUpdateInfo)
|
|
355
357
|
.catch(() => {});
|
|
358
|
+
const id = setInterval(
|
|
359
|
+
() => {
|
|
360
|
+
checkForUpdates(true)
|
|
361
|
+
.then(setUpdateInfo)
|
|
362
|
+
.catch(() => {});
|
|
363
|
+
},
|
|
364
|
+
15 * 60 * 1000, // 15 min
|
|
365
|
+
);
|
|
366
|
+
return () => clearInterval(id);
|
|
356
367
|
}, []);
|
|
357
368
|
|
|
358
369
|
// Auto-dismiss recovery banner after 5 seconds
|