fix(auto-update): sync cache package.json to opencode.json intent

When users switch from pinned version to tag in opencode.json (e.g.,
3.10.0 -> @latest), the cache package.json still contains the resolved
version. This causes bun install to reinstall the old version instead
of resolving the new tag.

This adds syncCachePackageJsonToIntent() which updates the cache
package.json to match user intent before running bun install. Uses
atomic writes (temp file + rename) with UUID-based temp names for
concurrent safety.

Critical changes:
- Treat all sync errors as abort conditions (file_not_found,
  plugin_not_in_deps, parse_error, write_error) to prevent corrupting
  a bad cache state further
- Remove dead code (unreachable revert branch for pinned versions)
- Add tests for all error paths and atomic write cleanup
This commit is contained in:
acamq
2026-03-11 07:42:08 -06:00
parent 6e82edcf02
commit 1fb636dbbc
6 changed files with 560 additions and 11 deletions
@@ -11,9 +11,7 @@ export interface PluginEntryInfo {
configPath: string
}
function isExplicitVersionPin(pinnedVersion: string): boolean {
return /^\d+\.\d+\.\d+/.test(pinnedVersion)
}
const EXACT_SEMVER_REGEX = /^\d+\.\d+\.\d+(-[\w.]+)?$/
export function findPluginEntry(directory: string): PluginEntryInfo | null {
for (const configPath of getConfigPaths(directory)) {
@@ -29,7 +27,7 @@ export function findPluginEntry(directory: string): PluginEntryInfo | null {
}
if (entry.startsWith(`${PACKAGE_NAME}@`)) {
const pinnedVersion = entry.slice(PACKAGE_NAME.length + 1)
const isPinned = isExplicitVersionPin(pinnedVersion)
const isPinned = EXACT_SEMVER_REGEX.test(pinnedVersion.trim())
return { entry, isPinned, pinnedVersion, configPath }
}
}