Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 16 additions & 15 deletions src/cli/update.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ export async function update() {
// from the first-party distribution bucket, which would silently replace the
// OpenClaude build (with the OpenAI shim) with the upstream Claude Code
// binary (without it).
if (getAPIProvider() !== 'firstParty') {
if (getAPIProvider() !== 'firstParty' && MACRO.PACKAGE_URL !== '@gitlawb/openclaude') {
writeToStdout(
chalk.yellow('Auto-update is not available for third-party provider builds.\n') +
'To update, pull the latest source from the repository and rebuild:\n' +
Expand All @@ -43,7 +43,8 @@ export async function update() {
}

logEvent('tengu_update_check', {})
writeToStdout(`Current version: ${MACRO.VERSION}\n`)
const currentVersion = typeof MACRO !== 'undefined' ? (MACRO.DISPLAY_VERSION ?? MACRO.VERSION) : '0.0.0';
writeToStdout(`Current version: ${currentVersion}\n`)

const channel = getInitialSettings()?.autoUpdatesChannel ?? 'latest'
writeToStdout(`Checking for updates to ${channel} version...\n`)
Expand Down Expand Up @@ -136,8 +137,8 @@ export async function update() {
if (packageManager === 'homebrew') {
writeToStdout('Claude is managed by Homebrew.\n')
const latest = await getLatestVersion(channel)
if (latest && !gte(MACRO.VERSION, latest)) {
writeToStdout(`Update available: ${MACRO.VERSION} → ${latest}\n`)
if (latest && !gte(currentVersion, latest)) {
writeToStdout(`Update available: ${currentVersion} → ${latest}\n`)
writeToStdout('\n')
writeToStdout('To update, run:\n')
writeToStdout(chalk.bold(' brew upgrade claude-code') + '\n')
Expand All @@ -147,8 +148,8 @@ export async function update() {
} else if (packageManager === 'winget') {
writeToStdout('Claude is managed by winget.\n')
const latest = await getLatestVersion(channel)
if (latest && !gte(MACRO.VERSION, latest)) {
writeToStdout(`Update available: ${MACRO.VERSION} → ${latest}\n`)
if (latest && !gte(currentVersion, latest)) {
writeToStdout(`Update available: ${currentVersion} → ${latest}\n`)
writeToStdout('\n')
writeToStdout('To update, run:\n')
writeToStdout(
Expand All @@ -160,8 +161,8 @@ export async function update() {
} else if (packageManager === 'apk') {
writeToStdout('Claude is managed by apk.\n')
const latest = await getLatestVersion(channel)
if (latest && !gte(MACRO.VERSION, latest)) {
writeToStdout(`Update available: ${MACRO.VERSION} → ${latest}\n`)
if (latest && !gte(currentVersion, latest)) {
writeToStdout(`Update available: ${currentVersion} → ${latest}\n`)
writeToStdout('\n')
writeToStdout('To update, run:\n')
writeToStdout(chalk.bold(' apk upgrade claude-code') + '\n')
Expand Down Expand Up @@ -250,14 +251,14 @@ export async function update() {
await gracefulShutdown(1)
}

if (result.latestVersion === MACRO.VERSION) {
if (result.latestVersion === currentVersion) {
writeToStdout(
chalk.green(`Claude Code is up to date (${MACRO.VERSION})`) + '\n',
chalk.green(`Claude Code is up to date (${currentVersion})`) + '\n',
)
} else {
writeToStdout(
chalk.green(
`Successfully updated from ${MACRO.VERSION} to version ${result.latestVersion}`,
`Successfully updated from ${currentVersion} to version ${result.latestVersion}`,
) + '\n',
)
await regenerateCompletionCache()
Expand Down Expand Up @@ -320,15 +321,15 @@ export async function update() {
}

// Check if versions match exactly, including any build metadata (like SHA)
if (latestVersion === MACRO.VERSION) {
if (latestVersion === currentVersion) {
writeToStdout(
chalk.green(`Claude Code is up to date (${MACRO.VERSION})`) + '\n',
chalk.green(`Claude Code is up to date (${currentVersion})`) + '\n',
)
await gracefulShutdown(0)
}

writeToStdout(
`New version available: ${latestVersion} (current: ${MACRO.VERSION})\n`,
`New version available: ${latestVersion} (current: ${currentVersion})\n`,
)
writeToStdout('Installing update...\n')

Expand Down Expand Up @@ -388,7 +389,7 @@ export async function update() {
case 'success':
writeToStdout(
chalk.green(
`Successfully updated from ${MACRO.VERSION} to version ${latestVersion}`,
`Successfully updated from ${currentVersion} to version ${latestVersion}`,
) + '\n',
)
await regenerateCompletionCache()
Expand Down
2 changes: 1 addition & 1 deletion src/components/AutoUpdater.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ export function AutoUpdater({
logForDebugging('AutoUpdater: Skipping update check in test/dev environment');
return;
}
const currentVersion = MACRO.VERSION;
const currentVersion = typeof MACRO !== 'undefined' ? (MACRO.DISPLAY_VERSION ?? MACRO.VERSION) : '0.0.0';
const channel = getInitialSettings()?.autoUpdatesChannel ?? 'latest';
let latestVersion = await getLatestVersion(channel);
const isDisabled = isAutoUpdaterDisabled();
Expand Down
6 changes: 3 additions & 3 deletions src/components/LogoV2/LogoV2.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ export function LogoV2() {
if ($[2] === Symbol.for("react.memo_cache_sentinel")) {
t2 = () => {
const currentConfig = getGlobalConfig();
if (currentConfig.lastReleaseNotesSeen === MACRO.VERSION) {
if (currentConfig.lastReleaseNotesSeen === (typeof MACRO !== 'undefined' ? (MACRO.DISPLAY_VERSION ?? MACRO.VERSION) : '0.0.0')) {
return;
}
saveGlobalConfig(_temp3);
Expand Down Expand Up @@ -528,12 +528,12 @@ export function LogoV2() {
return t41;
}
function _temp3(current) {
if (current.lastReleaseNotesSeen === MACRO.VERSION) {
if (current.lastReleaseNotesSeen === (typeof MACRO !== 'undefined' ? (MACRO.DISPLAY_VERSION ?? MACRO.VERSION) : '0.0.0')) {
return current;
}
return {
...current,
lastReleaseNotesSeen: MACRO.VERSION
lastReleaseNotesSeen: (typeof MACRO !== 'undefined' ? (MACRO.DISPLAY_VERSION ?? MACRO.VERSION) : '0.0.0')
};
}
function _temp2(s_0) {
Expand Down
4 changes: 2 additions & 2 deletions src/components/NativeAutoUpdater.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -87,14 +87,14 @@ export function NativeAutoUpdater({
// Log the start of an auto-update check for funnel analysis
logEvent('tengu_native_auto_updater_start', {});
try {
const currentVersion = typeof MACRO !== 'undefined' ? (MACRO.DISPLAY_VERSION ?? MACRO.VERSION) : '0.0.0';
// Check if current version is above the max allowed version
const maxVersion = await getMaxVersion();
if (maxVersion && gt(MACRO.VERSION, maxVersion)) {
if (maxVersion && gt(currentVersion, maxVersion)) {
const msg = await getMaxVersionMessage();
setMaxVersionIssue(msg ?? 'affects your version');
}
const result = await installLatest(channel);
const currentVersion = MACRO.VERSION;
const latencyMs = Date.now() - startTime;

// Handle lock contention gracefully - just return without treating as error
Expand Down
11 changes: 6 additions & 5 deletions src/components/PackageManagerAutoUpdater.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,23 +31,24 @@ export function PackageManagerAutoUpdater(t0) {
if (isAutoUpdaterDisabled()) {
return;
}
const currentVersion = typeof MACRO !== 'undefined' ? (MACRO.DISPLAY_VERSION ?? MACRO.VERSION) : '0.0.0';
const [channel, pm] = await Promise.all([Promise.resolve(getInitialSettings()?.autoUpdatesChannel ?? "latest"), getPackageManager()]);
setPackageManager(pm);
let latest = await getLatestVersionFromGcs(channel);
const maxVersion = await getMaxVersion();
if (maxVersion && latest && gt(latest, maxVersion)) {
logForDebugging(`PackageManagerAutoUpdater: maxVersion ${maxVersion} is set, capping update from ${latest} to ${maxVersion}`);
if (gte(MACRO.VERSION, maxVersion)) {
logForDebugging(`PackageManagerAutoUpdater: current version ${MACRO.VERSION} is already at or above maxVersion ${maxVersion}, skipping update`);
if (gte(currentVersion, maxVersion)) {
logForDebugging(`PackageManagerAutoUpdater: current version ${currentVersion} is already at or above maxVersion ${maxVersion}, skipping update`);
setUpdateAvailable(false);
return;
}
latest = maxVersion;
}
const hasUpdate = latest && !gte(MACRO.VERSION, latest) && !shouldSkipVersion(latest);
const hasUpdate = latest && !gte(currentVersion, latest) && !shouldSkipVersion(latest);
setUpdateAvailable(!!hasUpdate);
if (hasUpdate) {
logForDebugging(`PackageManagerAutoUpdater: Update available ${MACRO.VERSION} -> ${latest}`);
logForDebugging(`PackageManagerAutoUpdater: Update available ${currentVersion} -> ${latest}`);
}
};
$[0] = t1;
Expand Down Expand Up @@ -76,7 +77,7 @@ export function PackageManagerAutoUpdater(t0) {
const updateCommand = packageManager === "homebrew" ? "brew upgrade claude-code" : packageManager === "winget" ? "winget upgrade Anthropic.ClaudeCode" : packageManager === "apk" ? "apk upgrade claude-code" : "your package manager update command";
let t4;
if ($[3] !== verbose) {
t4 = verbose && <Text dimColor={true} wrap="truncate">currentVersion: {MACRO.VERSION}</Text>;
t4 = verbose && <Text dimColor={true} wrap="truncate">currentVersion: {typeof MACRO !== 'undefined' ? (MACRO.DISPLAY_VERSION ?? MACRO.VERSION) : '0.0.0'}</Text>;
$[3] = verbose;
$[4] = t4;
} else {
Expand Down
4 changes: 2 additions & 2 deletions src/components/Settings/Config.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -1642,7 +1642,7 @@ export function Config({
channel: channel as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS
});
}} />}
</Dialog> : showSubmenu === 'ChannelDowngrade' ? <ChannelDowngradeDialog currentVersion={MACRO.VERSION} onChoice={(choice: ChannelDowngradeChoice) => {
</Dialog> : showSubmenu === 'ChannelDowngrade' ? <ChannelDowngradeDialog currentVersion={(typeof MACRO !== 'undefined' ? (MACRO.DISPLAY_VERSION ?? MACRO.VERSION) : '0.0.0')} onChoice={(choice: ChannelDowngradeChoice) => {
setShowSubmenu(null);
setTabsHidden(false);
if (choice === 'cancel') {
Expand All @@ -1659,7 +1659,7 @@ export function Config({
};
if (choice === 'stay') {
// User wants to stay on current version until stable catches up
newSettings.minimumVersion = MACRO.VERSION;
newSettings.minimumVersion = (typeof MACRO !== 'undefined' ? (MACRO.DISPLAY_VERSION ?? MACRO.VERSION) : '0.0.0');
}
updateSettingsForSource('userSettings', newSettings);
setSettingsData(prev_27 => ({
Expand Down
2 changes: 1 addition & 1 deletion src/hooks/useUpdateNotification.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ export function shouldShowUpdateNotification(

export function useUpdateNotification(
updatedVersion: string | null | undefined,
initialVersion: string = MACRO.VERSION,
initialVersion: string = typeof MACRO !== 'undefined' ? (MACRO.DISPLAY_VERSION ?? MACRO.VERSION) : '0.0.0',
): string | null {
const [lastNotifiedSemver, setLastNotifiedSemver] = useState<string | null>(
() => getSemverPart(initialVersion),
Expand Down
2 changes: 2 additions & 0 deletions src/main.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@ import { getActiveAgentsFromList, getAgentDefinitionsWithOverrides, isBuiltInAge
import type { LogOption } from './types/logs.js';
import type { Message as MessageType } from './types/message.js';
import { assertMinVersion } from './utils/autoUpdater.js';
import { notifyUpdates } from './utils/updateNotifier.js';
import { CLAUDE_IN_CHROME_SKILL_HINT, CLAUDE_IN_CHROME_SKILL_HINT_WITH_WEBBROWSER } from './utils/claudeInChrome/prompt.js';
import { setupClaudeInChrome, shouldAutoEnableClaudeInChrome, shouldEnableClaudeInChrome } from './utils/claudeInChrome/setup.js';
import { getContextWindowForModel } from './utils/context.js';
Expand Down Expand Up @@ -1770,6 +1771,7 @@ async function run(): Promise<CommanderCommand> {
console.error(warning);
});
void assertMinVersion();
void notifyUpdates();

// claude.ai config fetch: -p mode only (interactive uses useManageMCPConnections
// two-phase loading). Kicked off here to overlap with setup(); awaited
Expand Down
1 change: 1 addition & 0 deletions src/utils/autoUpdater.ts
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,7 @@ This will ensure you have access to the latest features and improvements.
}
}


/**
* Returns the maximum allowed version for the current user type.
* For ants, returns the `ant` field (dev version format).
Expand Down
7 changes: 4 additions & 3 deletions src/utils/nativeInstaller/installer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -507,6 +507,7 @@ async function updateLatest(

logForDebugging(`Checking for native installer update to version ${version}`)

const currentVersion = typeof MACRO !== 'undefined' ? (MACRO.DISPLAY_VERSION ?? MACRO.VERSION) : '0.0.0';
// Check if max version is set (server-side kill switch for auto-updates)
if (!forceReinstall) {
const maxVersion = await getMaxVersion()
Expand All @@ -515,9 +516,9 @@ async function updateLatest(
`Native installer: maxVersion ${maxVersion} is set, capping update from ${version} to ${maxVersion}`,
)
// If we're already at or above maxVersion, skip the update entirely
if (gte(MACRO.VERSION, maxVersion)) {
if (gte(currentVersion, maxVersion)) {
logForDebugging(
`Native installer: current version ${MACRO.VERSION} is already at or above maxVersion ${maxVersion}, skipping update`,
`Native installer: current version ${currentVersion} is already at or above maxVersion ${maxVersion}, skipping update`,
)
logEvent('tengu_native_update_skipped_max_version', {
latency_ms: Date.now() - startTime,
Expand All @@ -537,7 +538,7 @@ async function updateLatest(
// is invalid (e.g., empty/corrupted from a failed install), or we're running via npx.
if (
!forceReinstall &&
version === MACRO.VERSION &&
version === currentVersion &&
(await versionIsAvailable(version)) &&
(await isPossibleClaudeBinary(executablePath))
) {
Expand Down
42 changes: 42 additions & 0 deletions src/utils/updateNotifier.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import chalk from 'chalk'
import { getLatestVersion } from './autoUpdater.js'
import { logForDebugging } from './debug.js'
import { lt } from './semver.js'
import { getInitialSettings } from './settings/settings.js'

/**
* Checks for a newer version on npm and suggests the user to update.
* Does not block startup.
*/
export async function notifyUpdates(): Promise<void> {
if (
process.env.NODE_ENV === 'test' ||
process.env.NODE_ENV === 'development' ||
process.env.OPENCLAUDE_SKIP_UPDATE_CHECK === 'true'
) {
return
}

try {
const channel = getInitialSettings()?.autoUpdatesChannel ?? 'latest'
const latestVersion = await getLatestVersion(channel)

const currentVersion =
typeof MACRO !== 'undefined'
? (MACRO.DISPLAY_VERSION ?? MACRO.VERSION)
: '0.0.0'

if (latestVersion && lt(currentVersion, latestVersion)) {
// biome-ignore lint/suspicious/noConsole:: intentional console output
console.log(
chalk.green(`
A new version of Open Claude is available: ${latestVersion} (current: ${currentVersion})
To update, please run:
npm install -g ${MACRO.PACKAGE_URL}
`),
)
}
} catch (error) {
logForDebugging(`notifyUpdates failed: ${error}`)
}
}
Loading