From 59ec8003db83013890d31849d82dcebd16fb1234 Mon Sep 17 00:00:00 2001 From: Armin Ronacher Date: Wed, 27 May 2026 00:59:09 +0200 Subject: [PATCH] fix(coding-agent): bypass age gates for self-update closes #4929 --- packages/coding-agent/CHANGELOG.md | 1 + packages/coding-agent/src/config.ts | 17 +++++- packages/coding-agent/test/config.test.ts | 65 +++++++++++++++-------- 3 files changed, 58 insertions(+), 25 deletions(-) diff --git a/packages/coding-agent/CHANGELOG.md b/packages/coding-agent/CHANGELOG.md index a5a0fd8d..35412c12 100644 --- a/packages/coding-agent/CHANGELOG.md +++ b/packages/coding-agent/CHANGELOG.md @@ -4,6 +4,7 @@ ### Fixed +- Fixed self-update commands to bypass npm, pnpm, and Bun minimum release age gates for explicit `pi update` runs ([#4929](https://github.com/earendil-works/pi/issues/4929)). - Fixed context token estimates to count user image attachments consistently with tool result images ([#4983](https://github.com/earendil-works/pi/issues/4983)). - Fixed `RpcClient` to reject pending requests and consume stdin pipe errors when the child process exits unexpectedly ([#4764](https://github.com/earendil-works/pi/issues/4764)). - Fixed managed npm extension updates to avoid package managers installing or resolving pi host packages as peer dependencies ([#4907](https://github.com/earendil-works/pi/issues/4907)). diff --git a/packages/coding-agent/src/config.ts b/packages/coding-agent/src/config.ts index dd091804..01ec8f8e 100644 --- a/packages/coding-agent/src/config.ts +++ b/packages/coding-agent/src/config.ts @@ -111,7 +111,13 @@ function getSelfUpdateCommandForMethod( return undefined; case "pnpm": return makeSelfUpdateCommand( - makeSelfUpdateCommandStep("pnpm", ["install", "-g", "--ignore-scripts", updatePackageName]), + makeSelfUpdateCommandStep("pnpm", [ + "install", + "-g", + "--ignore-scripts", + "--config.minimumReleaseAge=0", + updatePackageName, + ]), updatePackageName === installedPackageName ? undefined : makeSelfUpdateCommandStep("pnpm", ["remove", "-g", installedPackageName]), @@ -125,7 +131,13 @@ function getSelfUpdateCommandForMethod( ); case "bun": return makeSelfUpdateCommand( - makeSelfUpdateCommandStep("bun", ["install", "-g", "--ignore-scripts", updatePackageName]), + makeSelfUpdateCommandStep("bun", [ + "install", + "-g", + "--ignore-scripts", + "--minimum-release-age=0", + updatePackageName, + ]), updatePackageName === installedPackageName ? undefined : makeSelfUpdateCommandStep("bun", ["uninstall", "-g", installedPackageName]), @@ -139,6 +151,7 @@ function getSelfUpdateCommandForMethod( "install", "-g", "--ignore-scripts", + "--min-release-age=0", updatePackageName, ]); const uninstallStep = diff --git a/packages/coding-agent/test/config.test.ts b/packages/coding-agent/test/config.test.ts index 6b90778a..cbfa044e 100644 --- a/packages/coding-agent/test/config.test.ts +++ b/packages/coding-agent/test/config.test.ts @@ -153,7 +153,7 @@ describe("detectInstallMethod", () => { expect(detectInstallMethod()).toBe("pnpm"); expect(getUpdateInstruction("@earendil-works/pi-coding-agent")).toBe( - "Run: pnpm install -g --ignore-scripts @earendil-works/pi-coding-agent", + "Run: pnpm install -g --ignore-scripts --config.minimumReleaseAge=0 @earendil-works/pi-coding-agent", ); }); @@ -175,8 +175,16 @@ describe("detectInstallMethod", () => { expect(detectInstallMethod()).toBe("npm"); expect(command).toEqual({ command: "npm", - args: ["--prefix", prefix, "install", "-g", "--ignore-scripts", "@earendil-works/pi-coding-agent"], - display: `npm --prefix ${prefix} install -g --ignore-scripts @earendil-works/pi-coding-agent`, + args: [ + "--prefix", + prefix, + "install", + "-g", + "--ignore-scripts", + "--min-release-age=0", + "@earendil-works/pi-coding-agent", + ], + display: `npm --prefix ${prefix} install -g --ignore-scripts --min-release-age=0 @earendil-works/pi-coding-agent`, }); }); @@ -187,8 +195,8 @@ describe("detectInstallMethod", () => { expect(command).toEqual({ command: "npm", - args: ["--prefix", prefix, "install", "-g", "--ignore-scripts", "@new-scope/pi"], - display: `npm --prefix ${prefix} uninstall -g @mariozechner/pi-coding-agent && npm --prefix ${prefix} install -g --ignore-scripts @new-scope/pi`, + args: ["--prefix", prefix, "install", "-g", "--ignore-scripts", "--min-release-age=0", "@new-scope/pi"], + display: `npm --prefix ${prefix} uninstall -g @mariozechner/pi-coding-agent && npm --prefix ${prefix} install -g --ignore-scripts --min-release-age=0 @new-scope/pi`, steps: [ { command: "npm", @@ -197,8 +205,8 @@ describe("detectInstallMethod", () => { }, { command: "npm", - args: ["--prefix", prefix, "install", "-g", "--ignore-scripts", "@new-scope/pi"], - display: `npm --prefix ${prefix} install -g --ignore-scripts @new-scope/pi`, + args: ["--prefix", prefix, "install", "-g", "--ignore-scripts", "--min-release-age=0", "@new-scope/pi"], + display: `npm --prefix ${prefix} install -g --ignore-scripts --min-release-age=0 @new-scope/pi`, }, ], }); @@ -211,8 +219,16 @@ describe("detectInstallMethod", () => { expect(command).toEqual({ command: "npm", - args: ["--prefix", prefix, "install", "-g", "--ignore-scripts", "@earendil-works/pi-coding-agent"], - display: `npm --prefix ${prefix} install -g --ignore-scripts @earendil-works/pi-coding-agent`, + args: [ + "--prefix", + prefix, + "install", + "-g", + "--ignore-scripts", + "--min-release-age=0", + "@earendil-works/pi-coding-agent", + ], + display: `npm --prefix ${prefix} install -g --ignore-scripts --min-release-age=0 @earendil-works/pi-coding-agent`, }); }); @@ -227,6 +243,7 @@ describe("detectInstallMethod", () => { "install", "-g", "--ignore-scripts", + "--min-release-age=0", "@earendil-works/pi-coding-agent", ]); }); @@ -237,7 +254,7 @@ describe("detectInstallMethod", () => { const command = getSelfUpdateCommand("@earendil-works/pi-coding-agent"); expect(command?.display).toBe( - `npm --prefix "${prefix}" install -g --ignore-scripts @earendil-works/pi-coding-agent`, + `npm --prefix "${prefix}" install -g --ignore-scripts --min-release-age=0 @earendil-works/pi-coding-agent`, ); }); @@ -248,7 +265,7 @@ describe("detectInstallMethod", () => { expect(detectInstallMethod()).toBe("npm"); expect(getUpdateInstruction("@earendil-works/pi-coding-agent")).toBe( - "Run: npm install -g --ignore-scripts @earendil-works/pi-coding-agent", + "Run: npm install -g --ignore-scripts --min-release-age=0 @earendil-works/pi-coding-agent", ); }); @@ -260,8 +277,8 @@ describe("detectInstallMethod", () => { expect(detectInstallMethod()).toBe("bun"); expect(command).toEqual({ command: "bun", - args: ["install", "-g", "--ignore-scripts", "@earendil-works/pi-coding-agent"], - display: "bun install -g --ignore-scripts @earendil-works/pi-coding-agent", + args: ["install", "-g", "--ignore-scripts", "--minimum-release-age=0", "@earendil-works/pi-coding-agent"], + display: "bun install -g --ignore-scripts --minimum-release-age=0 @earendil-works/pi-coding-agent", }); }); @@ -273,8 +290,9 @@ describe("detectInstallMethod", () => { expect(detectInstallMethod()).toBe("pnpm"); expect(command).toEqual({ command: "pnpm", - args: ["install", "-g", "--ignore-scripts", "@new-scope/pi"], - display: "pnpm remove -g @mariozechner/pi-coding-agent && pnpm install -g --ignore-scripts @new-scope/pi", + args: ["install", "-g", "--ignore-scripts", "--config.minimumReleaseAge=0", "@new-scope/pi"], + display: + "pnpm remove -g @mariozechner/pi-coding-agent && pnpm install -g --ignore-scripts --config.minimumReleaseAge=0 @new-scope/pi", steps: [ { command: "pnpm", @@ -283,8 +301,8 @@ describe("detectInstallMethod", () => { }, { command: "pnpm", - args: ["install", "-g", "--ignore-scripts", "@new-scope/pi"], - display: "pnpm install -g --ignore-scripts @new-scope/pi", + args: ["install", "-g", "--ignore-scripts", "--config.minimumReleaseAge=0", "@new-scope/pi"], + display: "pnpm install -g --ignore-scripts --config.minimumReleaseAge=0 @new-scope/pi", }, ], }); @@ -328,8 +346,8 @@ describe("detectInstallMethod", () => { expect(detectInstallMethod()).toBe("pnpm"); expect(command).toEqual({ command: "pnpm", - args: ["install", "-g", "--ignore-scripts", packageName], - display: `pnpm install -g --ignore-scripts ${packageName}`, + args: ["install", "-g", "--ignore-scripts", "--config.minimumReleaseAge=0", packageName], + display: `pnpm install -g --ignore-scripts --config.minimumReleaseAge=0 ${packageName}`, }); }); @@ -366,8 +384,9 @@ describe("detectInstallMethod", () => { expect(detectInstallMethod()).toBe("bun"); expect(command).toEqual({ command: "bun", - args: ["install", "-g", "--ignore-scripts", "@new-scope/pi"], - display: "bun uninstall -g @mariozechner/pi-coding-agent && bun install -g --ignore-scripts @new-scope/pi", + args: ["install", "-g", "--ignore-scripts", "--minimum-release-age=0", "@new-scope/pi"], + display: + "bun uninstall -g @mariozechner/pi-coding-agent && bun install -g --ignore-scripts --minimum-release-age=0 @new-scope/pi", steps: [ { command: "bun", @@ -376,8 +395,8 @@ describe("detectInstallMethod", () => { }, { command: "bun", - args: ["install", "-g", "--ignore-scripts", "@new-scope/pi"], - display: "bun install -g --ignore-scripts @new-scope/pi", + args: ["install", "-g", "--ignore-scripts", "--minimum-release-age=0", "@new-scope/pi"], + display: "bun install -g --ignore-scripts --minimum-release-age=0 @new-scope/pi", }, ], });