* Detect the installed Claude Desktop version. * On macOS, reads CFBundleShortVersionString from the app plist. * On Windows, finds the highest app-X.Y.Z directory in the Squirrel install. * Returns null if version cannot be determined.
()
| 87 | * Returns null if version cannot be determined. |
| 88 | */ |
| 89 | async function getDesktopVersion(): Promise<string | null> { |
| 90 | const platform = process.platform |
| 91 | |
| 92 | if (platform === 'darwin') { |
| 93 | const { code, stdout } = await execFileNoThrow('defaults', [ |
| 94 | 'read', |
| 95 | '/Applications/Claude.app/Contents/Info.plist', |
| 96 | 'CFBundleShortVersionString', |
| 97 | ]) |
| 98 | if (code !== 0) { |
| 99 | return null |
| 100 | } |
| 101 | const version = stdout.trim() |
| 102 | return version.length > 0 ? version : null |
| 103 | } else if (platform === 'win32') { |
| 104 | const localAppData = process.env.LOCALAPPDATA |
| 105 | if (!localAppData) { |
| 106 | return null |
| 107 | } |
| 108 | const installDir = join(localAppData, 'AnthropicClaude') |
| 109 | try { |
| 110 | const entries = await readdir(installDir) |
| 111 | const versions = entries |
| 112 | .filter(e => e.startsWith('app-')) |
| 113 | .map(e => e.slice(4)) |
| 114 | .filter(v => semverCoerce(v) !== null) |
| 115 | .sort((a, b) => { |
| 116 | const ca = semverCoerce(a)! |
| 117 | const cb = semverCoerce(b)! |
| 118 | return ca.compare(cb) |
| 119 | }) |
| 120 | return versions.length > 0 ? versions[versions.length - 1]! : null |
| 121 | } catch { |
| 122 | return null |
| 123 | } |
| 124 | } |
| 125 | |
| 126 | return null |
| 127 | } |
| 128 | |
| 129 | export type DesktopInstallStatus = |
| 130 | | { status: 'not-installed' } |
no test coverage detected