pi-updater 0.2.7 → 0.2.9
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/CHANGELOG.md +10 -0
- package/README.md +30 -4
- package/index.ts +95 -28
- package/package.json +6 -2
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## 0.2.9 - 2026-03-16
|
|
4
|
+
|
|
5
|
+
- Keep startup checks cache-first and non-blocking.
|
|
6
|
+
- Add a one-time background live check per run.
|
|
7
|
+
- Show update prompt in the same session when the background check finds a newer version.
|
|
8
|
+
- Respect `PI_SKIP_VERSION_CHECK` and `PI_OFFLINE` for automatic checks.
|
|
9
|
+
- Avoid duplicate automatic prompts for the same version in one run.
|
|
10
|
+
- `/update` now warns and exits early when `PI_OFFLINE` is set.
|
package/README.md
CHANGED
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
# pi-updater
|
|
2
2
|
|
|
3
|
-
A Codex-style auto-updater for
|
|
3
|
+
A lightweight, Codex-style auto-updater for pi with fast, cache-first startup checks.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
- npm: https://www.npmjs.com/package/pi-updater
|
|
6
|
+
- repo: https://github.com/tonze/pi-updater
|
|
7
|
+
|
|
8
|
+
> **Note:** Automatic installation currently supports npm-based pi installs only.
|
|
6
9
|
|
|
7
10
|
<img width="800" height="482" alt="Screenshot 2026-02-28 at 09 01 37" src="https://github.com/user-attachments/assets/89df2dad-8d91-464b-b3cb-dfd15bce1c06" />
|
|
8
11
|
|
|
@@ -13,9 +16,18 @@ A Codex-style auto-updater for Pi.
|
|
|
13
16
|
- **Skip** — dismiss until next session
|
|
14
17
|
- **Skip this version** — don't ask again until a newer version appears
|
|
15
18
|
|
|
16
|
-
|
|
19
|
+
**In the background (once per run):** performs one live npm check and can show the prompt in the same session when a new release is detected.
|
|
20
|
+
|
|
21
|
+
**`/update`:** manually check for updates (always fetches fresh from npm, unless `PI_OFFLINE` is set).
|
|
22
|
+
|
|
23
|
+
## How version checks work
|
|
24
|
+
|
|
25
|
+
pi-updater uses a cache-first approach to keep startup fast:
|
|
17
26
|
|
|
18
|
-
|
|
27
|
+
1. On startup, cached version data is checked instantly.
|
|
28
|
+
2. One background live fetch refreshes the cache.
|
|
29
|
+
3. If the background fetch finds a newer version, pi-updater can prompt in the same session.
|
|
30
|
+
4. Automatic checks are skipped when `PI_SKIP_VERSION_CHECK` or `PI_OFFLINE` is set.
|
|
19
31
|
|
|
20
32
|
## Install
|
|
21
33
|
|
|
@@ -33,6 +45,20 @@ pi install git:github.com/tonze/pi-updater
|
|
|
33
45
|
|
|
34
46
|
Use `/update` inside pi to manually check for updates and install them.
|
|
35
47
|
|
|
48
|
+
## Environment flags
|
|
49
|
+
|
|
50
|
+
Disable automatic version checks:
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
export PI_SKIP_VERSION_CHECK=1
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
Or run in offline mode (also disables automatic checks):
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
export PI_OFFLINE=1
|
|
60
|
+
```
|
|
61
|
+
|
|
36
62
|
## Updating this package
|
|
37
63
|
|
|
38
64
|
```bash
|
package/index.ts
CHANGED
|
@@ -11,9 +11,13 @@ const PACKAGE_NAME = "@mariozechner/pi-coding-agent";
|
|
|
11
11
|
const REGISTRY_URL = `https://registry.npmjs.org/${PACKAGE_NAME}/latest`;
|
|
12
12
|
const CACHE_FILE = join(homedir(), ".pi", "agent", "update-cache.json");
|
|
13
13
|
|
|
14
|
+
const ENV_SKIP_VERSION_CHECK = "PI_SKIP_VERSION_CHECK";
|
|
15
|
+
const ENV_OFFLINE = "PI_OFFLINE";
|
|
16
|
+
|
|
14
17
|
interface VersionCache {
|
|
15
18
|
latestVersion: string;
|
|
16
19
|
dismissedVersion?: string;
|
|
20
|
+
checkedAt?: string;
|
|
17
21
|
}
|
|
18
22
|
|
|
19
23
|
function readCache(): VersionCache | undefined {
|
|
@@ -48,6 +52,27 @@ function isNewer(latest: string, current: string): boolean {
|
|
|
48
52
|
return l[2] > c[2];
|
|
49
53
|
}
|
|
50
54
|
|
|
55
|
+
function isEnvSet(name: string): boolean {
|
|
56
|
+
return Boolean(process.env[name]);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function shouldSkipAutoChecks(): boolean {
|
|
60
|
+
return isEnvSet(ENV_SKIP_VERSION_CHECK) || isEnvSet(ENV_OFFLINE);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function isOffline(): boolean {
|
|
64
|
+
return isEnvSet(ENV_OFFLINE);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function saveLatestToCache(latest: string) {
|
|
68
|
+
const prev = readCache();
|
|
69
|
+
writeCache({
|
|
70
|
+
latestVersion: latest,
|
|
71
|
+
dismissedVersion: prev?.dismissedVersion,
|
|
72
|
+
checkedAt: new Date().toISOString(),
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
|
|
51
76
|
async function fetchLatestVersion(): Promise<string | undefined> {
|
|
52
77
|
try {
|
|
53
78
|
const res = await fetch(REGISTRY_URL, {
|
|
@@ -60,33 +85,30 @@ async function fetchLatestVersion(): Promise<string | undefined> {
|
|
|
60
85
|
}
|
|
61
86
|
}
|
|
62
87
|
|
|
63
|
-
/**
|
|
64
|
-
|
|
65
|
-
* Always kicks off a background fetch to refresh the cache for the next run.
|
|
66
|
-
*/
|
|
67
|
-
function getUpgradeVersion(): string | undefined {
|
|
88
|
+
/** Returns a cached upgrade if available and not dismissed. */
|
|
89
|
+
function getCachedUpgradeVersion(): string | undefined {
|
|
68
90
|
const cache = readCache();
|
|
69
|
-
|
|
70
|
-
void fetchLatestVersion().then((latest) => {
|
|
71
|
-
if (!latest) return;
|
|
72
|
-
// Re-read cache to avoid overwriting a dismissal that happened during the fetch
|
|
73
|
-
writeCache({
|
|
74
|
-
latestVersion: latest,
|
|
75
|
-
dismissedVersion: readCache()?.dismissedVersion,
|
|
76
|
-
});
|
|
77
|
-
});
|
|
78
|
-
|
|
79
91
|
if (!cache) return undefined;
|
|
80
92
|
if (!isNewer(cache.latestVersion, VERSION)) return undefined;
|
|
81
93
|
if (cache.dismissedVersion === cache.latestVersion) return undefined;
|
|
82
94
|
return cache.latestVersion;
|
|
83
95
|
}
|
|
84
96
|
|
|
97
|
+
/** Fetch latest from npm and refresh cache. */
|
|
98
|
+
async function refreshLatestVersionInCache(): Promise<string | undefined> {
|
|
99
|
+
const latest = await fetchLatestVersion();
|
|
100
|
+
if (!latest) return undefined;
|
|
101
|
+
saveLatestToCache(latest);
|
|
102
|
+
return latest;
|
|
103
|
+
}
|
|
104
|
+
|
|
85
105
|
function dismissVersion(version: string) {
|
|
86
106
|
const cache = readCache();
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
107
|
+
writeCache({
|
|
108
|
+
latestVersion: cache?.latestVersion ?? version,
|
|
109
|
+
dismissedVersion: version,
|
|
110
|
+
checkedAt: cache?.checkedAt,
|
|
111
|
+
});
|
|
90
112
|
}
|
|
91
113
|
|
|
92
114
|
function getInstallCommand(version: string): { program: string; args: string[] } {
|
|
@@ -101,6 +123,10 @@ function fmtCmd(cmd: { program: string; args: string[] }): string {
|
|
|
101
123
|
}
|
|
102
124
|
|
|
103
125
|
export default function (pi: ExtensionAPI) {
|
|
126
|
+
let promptOpen = false;
|
|
127
|
+
const promptedVersions = new Set<string>();
|
|
128
|
+
let liveCheckStarted = false;
|
|
129
|
+
|
|
104
130
|
async function doInstall(
|
|
105
131
|
ctx: ExtensionContext,
|
|
106
132
|
latest: string,
|
|
@@ -159,21 +185,64 @@ export default function (pi: ExtensionAPI) {
|
|
|
159
185
|
await doInstall(ctx, latest, cmd);
|
|
160
186
|
}
|
|
161
187
|
|
|
162
|
-
|
|
188
|
+
function canAutoPromptVersion(latest: string): boolean {
|
|
189
|
+
if (!isNewer(latest, VERSION)) return false;
|
|
190
|
+
if (promptedVersions.has(latest)) return false;
|
|
191
|
+
if (readCache()?.dismissedVersion === latest) return false;
|
|
192
|
+
return true;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
async function maybeShowAutoPrompt(ctx: ExtensionContext, latest: string) {
|
|
163
196
|
if (!ctx.hasUI) return;
|
|
164
|
-
|
|
165
|
-
if (latest)
|
|
197
|
+
if (promptOpen) return;
|
|
198
|
+
if (!canAutoPromptVersion(latest)) return;
|
|
199
|
+
|
|
200
|
+
promptOpen = true;
|
|
201
|
+
promptedVersions.add(latest);
|
|
202
|
+
try {
|
|
203
|
+
await showUpdatePrompt(ctx, latest);
|
|
204
|
+
} finally {
|
|
205
|
+
promptOpen = false;
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
function runAutoChecks(ctx: ExtensionContext) {
|
|
210
|
+
if (!ctx.hasUI) return;
|
|
211
|
+
if (shouldSkipAutoChecks()) return;
|
|
212
|
+
|
|
213
|
+
const cached = getCachedUpgradeVersion();
|
|
214
|
+
if (cached) void maybeShowAutoPrompt(ctx, cached);
|
|
215
|
+
|
|
216
|
+
if (liveCheckStarted) return;
|
|
217
|
+
liveCheckStarted = true;
|
|
218
|
+
|
|
219
|
+
void refreshLatestVersionInCache()
|
|
220
|
+
.then((latest) => {
|
|
221
|
+
if (!latest) return;
|
|
222
|
+
void maybeShowAutoPrompt(ctx, latest);
|
|
223
|
+
})
|
|
224
|
+
.catch(() => {});
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
pi.on("session_start", async (_event, ctx) => {
|
|
228
|
+
runAutoChecks(ctx);
|
|
166
229
|
});
|
|
167
230
|
|
|
168
231
|
pi.on("session_switch", async (_event, ctx) => {
|
|
169
|
-
|
|
170
|
-
const latest = getUpgradeVersion();
|
|
171
|
-
if (latest) void showUpdatePrompt(ctx, latest);
|
|
232
|
+
runAutoChecks(ctx);
|
|
172
233
|
});
|
|
173
234
|
|
|
174
235
|
pi.registerCommand("update", {
|
|
175
236
|
description: "Check for pi updates and install",
|
|
176
237
|
handler: async (_args, ctx) => {
|
|
238
|
+
if (isOffline()) {
|
|
239
|
+
ctx.ui.notify(
|
|
240
|
+
"PI_OFFLINE is set. Disable it to check for updates.",
|
|
241
|
+
"warning",
|
|
242
|
+
);
|
|
243
|
+
return;
|
|
244
|
+
}
|
|
245
|
+
|
|
177
246
|
const latest = await ctx.ui.custom<string | null>(
|
|
178
247
|
(tui, theme, _kb, done) => {
|
|
179
248
|
const loader = new BorderedLoader(
|
|
@@ -194,16 +263,14 @@ export default function (pi: ExtensionAPI) {
|
|
|
194
263
|
return;
|
|
195
264
|
}
|
|
196
265
|
|
|
197
|
-
|
|
198
|
-
latestVersion: latest,
|
|
199
|
-
dismissedVersion: readCache()?.dismissedVersion,
|
|
200
|
-
});
|
|
266
|
+
saveLatestToCache(latest);
|
|
201
267
|
|
|
202
268
|
if (!isNewer(latest, VERSION)) {
|
|
203
269
|
ctx.ui.notify(`Already on latest version (${VERSION}).`, "info");
|
|
204
270
|
return;
|
|
205
271
|
}
|
|
206
272
|
|
|
273
|
+
promptedVersions.add(latest);
|
|
207
274
|
await showUpdatePrompt(ctx, latest);
|
|
208
275
|
},
|
|
209
276
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pi-updater",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.9",
|
|
4
4
|
"description": "Codex-style auto-updater for pi. Checks for new versions on startup and prompts to install.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -22,8 +22,12 @@
|
|
|
22
22
|
},
|
|
23
23
|
"files": [
|
|
24
24
|
"index.ts",
|
|
25
|
-
"README.md"
|
|
25
|
+
"README.md",
|
|
26
|
+
"CHANGELOG.md"
|
|
26
27
|
],
|
|
28
|
+
"peerDependencies": {
|
|
29
|
+
"@mariozechner/pi-coding-agent": "*"
|
|
30
|
+
},
|
|
27
31
|
"devDependencies": {
|
|
28
32
|
"@mariozechner/pi-coding-agent": "^0.55.1",
|
|
29
33
|
"@types/node": "^25.3.2",
|