pi-updater 0.2.9 → 0.3.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/CHANGELOG.md +15 -0
- package/README.md +5 -1
- package/index.ts +112 -32
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,20 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.3.1 - 2026-04-04
|
|
4
|
+
|
|
5
|
+
- Compatibility with pi 0.65+: use `session_start` instead of legacy `session_switch` for automatic checks. See [pi-mono v0.65.0](https://github.com/badlogic/pi-mono/releases/tag/v0.65.0).
|
|
6
|
+
- Store cache and dismissed-version state in pi's configured agent directory.
|
|
7
|
+
- Preserve `--no-session` mode when restarting after an update and show the correct manual restart hint.
|
|
8
|
+
|
|
9
|
+
## Unreleased
|
|
10
|
+
|
|
11
|
+
## 0.3.0 - 2026-03-23
|
|
12
|
+
|
|
13
|
+
- Auto-restart pi after a successful update. Asks to restart, then seamlessly relaunches on the current session.
|
|
14
|
+
- Falls back to manual restart message in non-interactive modes or if restart fails.
|
|
15
|
+
- Cross-platform: uses `shell: true` on Windows to handle `.cmd` shims.
|
|
16
|
+
- `/update --test` to simulate the full update flow without a real install.
|
|
17
|
+
|
|
3
18
|
## 0.2.9 - 2026-03-16
|
|
4
19
|
|
|
5
20
|
- Keep startup checks cache-first and non-blocking.
|
package/README.md
CHANGED
|
@@ -12,10 +12,12 @@ A lightweight, Codex-style auto-updater for pi with fast, cache-first startup ch
|
|
|
12
12
|
## What it does
|
|
13
13
|
|
|
14
14
|
**On startup:** if a newer version is available, shows a prompt:
|
|
15
|
-
- **Update now** — install with npm, then restart pi
|
|
15
|
+
- **Update now** — install with npm, then auto-restart pi on the current session
|
|
16
16
|
- **Skip** — dismiss until next session
|
|
17
17
|
- **Skip this version** — don't ask again until a newer version appears
|
|
18
18
|
|
|
19
|
+
After a successful update, pi-updater asks whether to restart immediately. If confirmed, pi relaunches seamlessly on the current session. In non-interactive modes or if auto-restart fails, it falls back to a manual restart message. Ephemeral `--no-session` runs stay ephemeral on restart.
|
|
20
|
+
|
|
19
21
|
**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
22
|
|
|
21
23
|
**`/update`:** manually check for updates (always fetches fresh from npm, unless `PI_OFFLINE` is set).
|
|
@@ -45,6 +47,8 @@ pi install git:github.com/tonze/pi-updater
|
|
|
45
47
|
|
|
46
48
|
Use `/update` inside pi to manually check for updates and install them.
|
|
47
49
|
|
|
50
|
+
Cache and dismissed-version state are stored in pi's configured agent directory and respect `PI_CODING_AGENT_DIR`.
|
|
51
|
+
|
|
48
52
|
## Environment flags
|
|
49
53
|
|
|
50
54
|
Disable automatic version checks:
|
package/index.ts
CHANGED
|
@@ -2,14 +2,14 @@ import type {
|
|
|
2
2
|
ExtensionAPI,
|
|
3
3
|
ExtensionContext,
|
|
4
4
|
} from "@mariozechner/pi-coding-agent";
|
|
5
|
-
import { VERSION, BorderedLoader } from "@mariozechner/pi-coding-agent";
|
|
5
|
+
import { VERSION, BorderedLoader, getAgentDir } from "@mariozechner/pi-coding-agent";
|
|
6
|
+
import { spawnSync } from "node:child_process";
|
|
6
7
|
import { readFileSync, writeFileSync, mkdirSync } from "node:fs";
|
|
7
8
|
import { join, dirname } from "node:path";
|
|
8
|
-
import { homedir } from "node:os";
|
|
9
9
|
|
|
10
10
|
const PACKAGE_NAME = "@mariozechner/pi-coding-agent";
|
|
11
11
|
const REGISTRY_URL = `https://registry.npmjs.org/${PACKAGE_NAME}/latest`;
|
|
12
|
-
const CACHE_FILE = join(
|
|
12
|
+
const CACHE_FILE = join(getAgentDir(), "update-cache.json");
|
|
13
13
|
|
|
14
14
|
const ENV_SKIP_VERSION_CHECK = "PI_SKIP_VERSION_CHECK";
|
|
15
15
|
const ENV_OFFLINE = "PI_OFFLINE";
|
|
@@ -127,6 +127,40 @@ export default function (pi: ExtensionAPI) {
|
|
|
127
127
|
const promptedVersions = new Set<string>();
|
|
128
128
|
let liveCheckStarted = false;
|
|
129
129
|
|
|
130
|
+
async function findPiBinary(): Promise<string> {
|
|
131
|
+
const cmd = process.platform === "win32" ? "where" : "which";
|
|
132
|
+
const result = await pi.exec(cmd, ["pi"]);
|
|
133
|
+
if (result.code === 0 && result.stdout?.trim()) {
|
|
134
|
+
return result.stdout.trim().split(/\r?\n/)[0];
|
|
135
|
+
}
|
|
136
|
+
return "pi";
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
function canAutoRestart(ctx: ExtensionContext): boolean {
|
|
140
|
+
return ctx.hasUI && !!process.stdin.isTTY && !!process.stdout.isTTY;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
async function restartPi(ctx: ExtensionContext): Promise<boolean> {
|
|
144
|
+
const piBinary = await findPiBinary();
|
|
145
|
+
const sessionFile = ctx.sessionManager.getSessionFile();
|
|
146
|
+
const restartArgs = sessionFile ? ["--session", sessionFile] : ["--no-session"];
|
|
147
|
+
|
|
148
|
+
return ctx.ui.custom<boolean>((tui, _theme, _kb, done) => {
|
|
149
|
+
tui.stop();
|
|
150
|
+
const result = spawnSync(piBinary, restartArgs, {
|
|
151
|
+
cwd: ctx.cwd,
|
|
152
|
+
env: process.env,
|
|
153
|
+
stdio: "inherit",
|
|
154
|
+
shell: process.platform === "win32",
|
|
155
|
+
windowsHide: false,
|
|
156
|
+
});
|
|
157
|
+
tui.start();
|
|
158
|
+
tui.requestRender(true);
|
|
159
|
+
done(!result.error && (result.status === null || result.status === 0));
|
|
160
|
+
return { render: () => [], invalidate: () => {} };
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
|
|
130
164
|
async function doInstall(
|
|
131
165
|
ctx: ExtensionContext,
|
|
132
166
|
latest: string,
|
|
@@ -136,37 +170,54 @@ export default function (pi: ExtensionAPI) {
|
|
|
136
170
|
const loader = new BorderedLoader(tui, theme, `Installing ${latest}...`);
|
|
137
171
|
loader.onAbort = () => done(false);
|
|
138
172
|
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
);
|
|
152
|
-
return false;
|
|
153
|
-
}
|
|
154
|
-
return true;
|
|
155
|
-
};
|
|
156
|
-
|
|
157
|
-
run()
|
|
158
|
-
.then(done)
|
|
173
|
+
pi.exec(cmd.program, cmd.args, { timeout: 120_000 })
|
|
174
|
+
.then((result) => {
|
|
175
|
+
if (result.code !== 0) {
|
|
176
|
+
ctx.ui.notify(
|
|
177
|
+
`Update failed (exit ${result.code}): ${result.stderr || result.stdout}`,
|
|
178
|
+
"error",
|
|
179
|
+
);
|
|
180
|
+
done(false);
|
|
181
|
+
} else {
|
|
182
|
+
done(true);
|
|
183
|
+
}
|
|
184
|
+
})
|
|
159
185
|
.catch(() => done(false));
|
|
186
|
+
|
|
160
187
|
return loader;
|
|
161
188
|
});
|
|
162
189
|
|
|
163
190
|
if (!success) return;
|
|
164
191
|
|
|
165
|
-
const
|
|
192
|
+
const restartTip = ctx.sessionManager.getSessionFile()
|
|
193
|
+
? "Tip: run `pi -c` to continue this session."
|
|
194
|
+
: "Tip: run `pi --no-session` to continue without a saved session.";
|
|
195
|
+
|
|
196
|
+
if (!canAutoRestart(ctx)) {
|
|
197
|
+
ctx.ui.notify(
|
|
198
|
+
`Updated to ${latest}! Please restart pi.\n${restartTip}`,
|
|
199
|
+
"info",
|
|
200
|
+
);
|
|
201
|
+
return;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
const restart = await ctx.ui.confirm(
|
|
166
205
|
`Updated to ${latest}!`,
|
|
167
|
-
"
|
|
206
|
+
"Restart now?",
|
|
207
|
+
);
|
|
208
|
+
|
|
209
|
+
if (!restart) return;
|
|
210
|
+
|
|
211
|
+
const ok = await restartPi(ctx);
|
|
212
|
+
if (ok) {
|
|
213
|
+
ctx.shutdown();
|
|
214
|
+
return;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
ctx.ui.notify(
|
|
218
|
+
`Updated to ${latest}! Auto-restart failed. Please restart pi manually.\n${restartTip}`,
|
|
219
|
+
"error",
|
|
168
220
|
);
|
|
169
|
-
if (ok) ctx.shutdown();
|
|
170
221
|
}
|
|
171
222
|
|
|
172
223
|
async function showUpdatePrompt(ctx: ExtensionContext, latest: string) {
|
|
@@ -224,17 +275,46 @@ export default function (pi: ExtensionAPI) {
|
|
|
224
275
|
.catch(() => {});
|
|
225
276
|
}
|
|
226
277
|
|
|
227
|
-
pi.on("session_start", async (
|
|
228
|
-
|
|
229
|
-
});
|
|
230
|
-
|
|
231
|
-
pi.on("session_switch", async (_event, ctx) => {
|
|
278
|
+
pi.on("session_start", async (event, ctx) => {
|
|
279
|
+
if (event.reason === "reload" || event.reason === "fork") return;
|
|
232
280
|
runAutoChecks(ctx);
|
|
233
281
|
});
|
|
234
282
|
|
|
235
283
|
pi.registerCommand("update", {
|
|
236
284
|
description: "Check for pi updates and install",
|
|
237
|
-
handler: async (
|
|
285
|
+
handler: async (rawArgs, ctx) => {
|
|
286
|
+
// /update --test — simulate the full UI flow without a real install
|
|
287
|
+
if (rawArgs?.trim() === "--test") {
|
|
288
|
+
const fakeLatest = "99.0.0";
|
|
289
|
+
const cmd = getInstallCommand(fakeLatest);
|
|
290
|
+
const choice = await ctx.ui.select(`Update ${VERSION} → ${fakeLatest}`, [
|
|
291
|
+
`Update now (${fmtCmd(cmd)})`,
|
|
292
|
+
"Skip",
|
|
293
|
+
"Skip this version",
|
|
294
|
+
]);
|
|
295
|
+
if (!choice || choice === "Skip" || choice === "Skip this version") return;
|
|
296
|
+
|
|
297
|
+
await ctx.ui.custom<void>((tui, theme, _kb, done) => {
|
|
298
|
+
const loader = new BorderedLoader(tui, theme, `Installing ${fakeLatest}...`);
|
|
299
|
+
loader.onAbort = () => done();
|
|
300
|
+
setTimeout(() => done(), 1500);
|
|
301
|
+
return loader;
|
|
302
|
+
});
|
|
303
|
+
|
|
304
|
+
if (!canAutoRestart(ctx)) {
|
|
305
|
+
ctx.ui.notify(`Updated to ${fakeLatest}! Please restart pi.`, "info");
|
|
306
|
+
return;
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
const restart = await ctx.ui.confirm(`Updated to ${fakeLatest}!`, "Restart now?");
|
|
310
|
+
if (!restart) return;
|
|
311
|
+
|
|
312
|
+
const ok = await restartPi(ctx);
|
|
313
|
+
if (ok) { ctx.shutdown(); return; }
|
|
314
|
+
ctx.ui.notify("Test restart failed.", "error");
|
|
315
|
+
return;
|
|
316
|
+
}
|
|
317
|
+
|
|
238
318
|
if (isOffline()) {
|
|
239
319
|
ctx.ui.notify(
|
|
240
320
|
"PI_OFFLINE is set. Disable it to check for updates.",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pi-updater",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.1",
|
|
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",
|
|
@@ -29,7 +29,7 @@
|
|
|
29
29
|
"@mariozechner/pi-coding-agent": "*"
|
|
30
30
|
},
|
|
31
31
|
"devDependencies": {
|
|
32
|
-
"@mariozechner/pi-coding-agent": "^0.
|
|
32
|
+
"@mariozechner/pi-coding-agent": "^0.65.0",
|
|
33
33
|
"@types/node": "^25.3.2",
|
|
34
34
|
"typescript": "^5.9.3"
|
|
35
35
|
}
|