fix(coding-agent): repair self-update detection

Fixes #3942
Fixes #3980
Fixes #3922
This commit is contained in:
Armin Ronacher
2026-04-30 19:10:06 +02:00
parent def47ece92
commit ade08de14c
6 changed files with 349 additions and 117 deletions

View File

@@ -7,10 +7,12 @@ import {
getSelfUpdateCommand,
getSelfUpdateUnavailableInstruction,
PACKAGE_NAME,
type SelfUpdateCommand,
VERSION,
} from "./config.js";
import { DefaultPackageManager } from "./core/package-manager.js";
import { SettingsManager } from "./core/settings-manager.js";
import { shouldUseWindowsShell } from "./utils/child-process.js";
import { getLatestPiVersion, isNewerPackageVersion } from "./utils/version-check.js";
export type PackageCommand = "install" | "remove" | "update" | "list";
@@ -271,13 +273,9 @@ function updateTargetIncludesExtensions(target: UpdateTarget): boolean {
return target.type === "all" || target.type === "extensions";
}
function canSelfUpdate(): boolean {
return getSelfUpdateCommand(PACKAGE_NAME) !== undefined;
}
function printSelfUpdateUnavailable(): void {
function printSelfUpdateUnavailable(npmCommand?: string[]): void {
console.error(`error: ${APP_NAME} cannot self-update this installation.`);
console.error(getSelfUpdateUnavailableInstruction(PACKAGE_NAME));
console.error(getSelfUpdateUnavailableInstruction(PACKAGE_NAME, npmCommand));
const entrypoint = process.argv[1];
if (entrypoint) {
@@ -286,9 +284,7 @@ function printSelfUpdateUnavailable(): void {
}
}
function printSelfUpdateFallback(): void {
const command = getSelfUpdateCommand(PACKAGE_NAME);
if (!command) return;
function printSelfUpdateFallback(command: SelfUpdateCommand): void {
console.error(chalk.dim(`If this keeps failing, run this command yourself: ${command.display}`));
}
@@ -312,19 +308,14 @@ async function shouldRunSelfUpdate(force: boolean): Promise<boolean> {
return false;
}
async function runSelfUpdate(): Promise<void> {
const command = getSelfUpdateCommand(PACKAGE_NAME);
if (!command) {
throw new Error(
`${APP_NAME} cannot self-update this installation. ${getSelfUpdateUnavailableInstruction(PACKAGE_NAME)}`,
);
}
async function runSelfUpdate(command: SelfUpdateCommand): Promise<void> {
console.log(chalk.dim(`Updating ${APP_NAME} with ${command.display}...`));
await new Promise<void>((resolve, reject) => {
// Windows package managers are commonly .cmd shims. Use the shell so Node can execute them;
// command and args come from getSelfUpdateCommandForMethod(), not user input.
const child = spawn(command.command, command.args, { stdio: "inherit", shell: process.platform === "win32" });
// Windows package managers are commonly .cmd shims. Use the shell so Node can execute them.
const child = spawn(command.command, command.args, {
stdio: "inherit",
shell: shouldUseWindowsShell(command.command),
});
child.on("error", (error) => {
reject(error);
});
@@ -409,16 +400,12 @@ export async function handlePackageCommand(args: string[]): Promise<boolean> {
return true;
}
if (options.command === "update" && options.updateTarget?.type === "self" && !canSelfUpdate()) {
printSelfUpdateUnavailable();
process.exitCode = 1;
return true;
}
const cwd = process.cwd();
const agentDir = getAgentDir();
const settingsManager = SettingsManager.create(cwd, agentDir);
reportSettingsErrors(settingsManager, "package command");
const selfUpdateNpmCommand = settingsManager.getGlobalSettings().npmCommand;
const packageManager = new DefaultPackageManager({ cwd, agentDir, settingsManager });
packageManager.setProgressCallback((event) => {
@@ -493,24 +480,25 @@ export async function handlePackageCommand(args: string[]): Promise<boolean> {
}
}
if (updateTargetIncludesSelf(target)) {
if (canSelfUpdate()) {
if (!(await shouldRunSelfUpdate(options.force))) {
return true;
}
try {
await runSelfUpdate();
} catch (error: unknown) {
const message = error instanceof Error ? error.message : "Unknown package command error";
console.error(chalk.red(`Error: ${message}`));
printSelfUpdateFallback();
process.exitCode = 1;
return true;
}
console.log(chalk.green(`Updated ${APP_NAME}`));
} else {
printSelfUpdateUnavailable();
const selfUpdateCommand = getSelfUpdateCommand(PACKAGE_NAME, selfUpdateNpmCommand);
if (!selfUpdateCommand) {
printSelfUpdateUnavailable(selfUpdateNpmCommand);
process.exitCode = 1;
return true;
}
if (!(await shouldRunSelfUpdate(options.force))) {
return true;
}
try {
await runSelfUpdate(selfUpdateCommand);
} catch (error: unknown) {
const message = error instanceof Error ? error.message : "Unknown package command error";
console.error(chalk.red(`Error: ${message}`));
printSelfUpdateFallback(selfUpdateCommand);
process.exitCode = 1;
return true;
}
console.log(chalk.green(`Updated ${APP_NAME}`));
}
return true;
}