diff --git a/packages/coding-agent/CHANGELOG.md b/packages/coding-agent/CHANGELOG.md index e9d1df5f6..6ded373e8 100644 --- a/packages/coding-agent/CHANGELOG.md +++ b/packages/coding-agent/CHANGELOG.md @@ -21,6 +21,7 @@ - Fixed `omp --resume ` crashing with an uncaught exception when an interactive user declined the cross-project fork prompt. `createSessionManager` now returns `undefined` for that cancellation, while non-interactive invocations still fail with a diagnostic when they cannot answer the fork prompt ([#1668](https://github.com/can1357/oh-my-pi/issues/1668)). - Fixed `history.db` never recording the originating session id: the `session_id` column documented for 15.6.0 was missing from the shipped storage layer, so the column was never created/populated on the write path and every prompt row had `session_id` `NULL`. Restored the `session_id` column, schema migration (`ALTER TABLE history ADD COLUMN session_id` for pre-existing databases), and `HistoryEntry.sessionId`; wired interactive mode to register `setSessionResolver(...)` so prompts are stamped with the session active at submission time (tracking fork/resume switches); and re-enabled prompt-history ranking in the `--resume` and in-session session pickers via `HistoryStorage.matchingSessionIds()`. - Fixed `/quit` shutdown leaving the parent shell prompt at the top of the viewport after the final TUI teardown render on Linux terminals ([#1620](https://github.com/can1357/oh-my-pi/issues/1620)). +- Fixed `omp update` failing with `No version matching "X" found for specifier "@oh-my-pi/pi-coding-agent" (but package exists)` when bun saw an older catalog than the update check did. The version is resolved by querying `https://registry.npmjs.org/` directly, but `bun install -g` would then consult its on-disk manifest snapshot or a configured npm mirror (corporate proxy, Taobao, …) that hadn't replicated the release. The bun install step now runs with `--no-cache --registry=https://registry.npmjs.org/` so it hits exactly the registry the version check used ([#1686](https://github.com/can1357/oh-my-pi/issues/1686)). ## [15.7.6] - 2026-06-01 ### Added diff --git a/packages/coding-agent/src/cli/update-cli.ts b/packages/coding-agent/src/cli/update-cli.ts index 60f82e980..3a04d1558 100644 --- a/packages/coding-agent/src/cli/update-cli.ts +++ b/packages/coding-agent/src/cli/update-cli.ts @@ -14,6 +14,18 @@ import { theme } from "../modes/theme/theme"; const REPO = "can1357/oh-my-pi"; const PACKAGE = "@oh-my-pi/pi-coding-agent"; +/** + * Official npm registry origin. + * + * Pinned across both the version check and the bun install step so the two + * agree on which catalog they are talking to. A user's bun may be pointed at + * an unofficial mirror (corporate proxy, Taobao, etc.) that lags the upstream + * registry by minutes-to-hours, in which case `getLatestRelease` would resolve + * a version the mirror has not yet replicated and the install would fail with + * `No version matching "X" found for specifier "" (but package exists)`. + * See #1686. + */ +const NPM_REGISTRY = "https://registry.npmjs.org/"; interface ReleaseInfo { tag: string; @@ -130,7 +142,7 @@ async function resolveUpdateTarget(): Promise { * Uses npm instead of GitHub API to avoid unauthenticated rate limiting. */ async function getLatestRelease(): Promise { - const response = await fetch(`https://registry.npmjs.org/${PACKAGE}/latest`); + const response = await fetch(`${NPM_REGISTRY}${PACKAGE}/latest`); if (!response.ok) { throw new Error(`Failed to fetch release info: ${response.statusText}`); } @@ -292,12 +304,39 @@ export async function replaceBinaryForUpdate(options: BinaryReplacementOptions): } } +/** + * Build the bun argv used to globally install a specific omp version. + * + * The version is selected by hitting {@link NPM_REGISTRY} directly in + * {@link getLatestRelease}, so the install MUST observe the same catalog: + * + * - `--registry=${NPM_REGISTRY}` pins the install to the official registry + * regardless of the user's bunfig/`.npmrc`. A mirror (corporate proxy, + * Taobao, …) that hasn't yet replicated the release would otherwise reject + * a version the upstream registry already advertises. + * - `--no-cache` tells bun to ignore its on-disk manifest snapshot so it + * re-fetches metadata from that registry on every invocation. + * + * Together these two flags make `omp update` produce exactly the registry + * lookup the version check just performed. See #1686. + */ +export function buildBunInstallArgs(expectedVersion: string): string[] { + return [ + "install", + "-g", + "--no-cache", + `--registry=${NPM_REGISTRY}`, + `${PACKAGE}@${expectedVersion}`, + ]; +} + /** * Update via bun package manager. */ async function updateViaBun(expectedVersion: string): Promise { console.log(chalk.dim("Updating via bun...")); - const result = await $`bun install -g ${PACKAGE}@${expectedVersion}`.nothrow(); + const args = buildBunInstallArgs(expectedVersion); + const result = await $`bun ${args}`.nothrow(); if (result.exitCode !== 0) { throw new Error(`bun install failed with exit code ${result.exitCode}`); } diff --git a/packages/coding-agent/test/update-cli.test.ts b/packages/coding-agent/test/update-cli.test.ts index 90965536f..038fcba15 100644 --- a/packages/coding-agent/test/update-cli.test.ts +++ b/packages/coding-agent/test/update-cli.test.ts @@ -2,7 +2,7 @@ import { afterEach, describe, expect, it } from "bun:test"; import * as fs from "node:fs/promises"; import * as os from "node:os"; import * as path from "node:path"; -import { replaceBinaryForUpdate, resolveUpdateMethodForTest } from "../src/cli/update-cli"; +import { buildBunInstallArgs, replaceBinaryForUpdate, resolveUpdateMethodForTest } from "../src/cli/update-cli"; const tempDirs: string[] = []; @@ -35,6 +35,26 @@ describe("update-cli install target detection", () => { }); }); +describe("update-cli bun install command", () => { + it("pins the official npm registry and bypasses the manifest cache so a stale mirror or snapshot cannot mask a freshly published version", () => { + // Regression: omp queries https://registry.npmjs.org//latest directly. + // The install MUST hit the same registry, otherwise: + // - a lagging mirror (corp proxy, Taobao, …) rejects the version with + // `No version matching "X" (but package exists)`, + // - or bun's local manifest snapshot does the same when the user's bun + // is already pointed at the official registry but its cache predates + // the release. + // See https://github.com/can1357/oh-my-pi/issues/1686. + expect(buildBunInstallArgs("15.7.6")).toEqual([ + "install", + "-g", + "--no-cache", + "--registry=https://registry.npmjs.org/", + "@oh-my-pi/pi-coding-agent@15.7.6", + ]); + }); +}); + describe("update-cli binary replacement", () => { it("restores the previous binary when the replacement fails verification", async () => { const dir = await makeTempDir();