Skip to content
Merged
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
7 changes: 5 additions & 2 deletions packages/cli/src/commands/update.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,10 @@ const initializeI18n = vi.fn();
const resolveLanguageSetting = vi.fn((language?: string) => language || 'auto');

vi.mock('../config/settings.js', () => ({ loadSettings }));
vi.mock('../ui/utils/updateCheck.js', () => ({ checkForUpdatesDetailed }));
vi.mock('../ui/utils/updateCheck.js', async (importOriginal) => ({
...(await importOriginal<typeof import('../ui/utils/updateCheck.js')>()),
checkForUpdatesDetailed,
}));
vi.mock('../utils/installationInfo.js', () => ({
formatUpdateInstructions,
getInstallationInfo,
Expand Down Expand Up @@ -235,7 +238,7 @@ describe('update command', () => {
await updateCommand.handler(updateArgs);

expect(writeStderrLine).toHaveBeenCalledWith(
'Failed to check for updates. Please check your network or registry configuration.',
'Failed to check for updates (registry error). Please check your network or registry configuration.',
);
expect(process.exitCode).toBe(1);
expect(getInstallationInfo).not.toHaveBeenCalled();
Expand Down
5 changes: 3 additions & 2 deletions packages/cli/src/commands/update.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ export const updateCommand: CommandModule = {
handler: async () => {
const [
{ loadSettings },
{ checkForUpdatesDetailed },
{ checkForUpdatesDetailed, describeUpdateCheckFailure },
installationInfoModule,
standaloneUpdate,
stdioHelpers,
Expand Down Expand Up @@ -54,7 +54,8 @@ export const updateCommand: CommandModule = {
if (updateCheck.status === 'error') {
writeStderrLine(
t(
'Failed to check for updates. Please check your network or registry configuration.',
'Failed to check for updates ({{reason}}). Please check your network or registry configuration.',

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The report in #7049 is about the non-blocking startup check. This changes the explicit qwen update failure contract as well, and the same propagation appears in /update and the relaunch path. Could we leave those paths unchanged in this PR and keep the change at the startup boundary?

{ reason: describeUpdateCheckFailure(updateCheck.error) },
),
);
process.exitCode = 1;
Expand Down
10 changes: 8 additions & 2 deletions packages/cli/src/i18n/locales/ca.js
Original file line number Diff line number Diff line change
Expand Up @@ -2098,8 +2098,14 @@ export default {
'Hi ha una versió nova de Qwen Code disponible! {{current}} → {{latest}}',
'Qwen Code {{version}} is up to date!':
'Qwen Code {{version}} està actualitzat!',
'Failed to check for updates. Please check your network or registry configuration.':
"No s'han pogut comprovar les actualitzacions. Comproveu la xarxa o la configuració del registre.",
'Failed to check for updates ({{reason}}). Please check your network or registry configuration.':
"No s'han pogut comprovar les actualitzacions ({{reason}}). Comproveu la xarxa o la configuració del registre.",
'Update check skipped ({{reason}}) — run /update to retry.':
"S'ha omès la comprovació d'actualitzacions ({{reason}}) — executeu /update per tornar-ho a provar.",
'registry did not respond within {{seconds}}s':
'el registre no ha respost en {{seconds}}s',
'registry unreachable': 'no es pot accedir al registre',
'registry error': 'error del registre',
'Unable to check for updates: {{reason}}':
'No es poden comprovar les actualitzacions: {{reason}}',
'Update successful! The new version will be used on your next run.':
Expand Down
10 changes: 8 additions & 2 deletions packages/cli/src/i18n/locales/de.js
Original file line number Diff line number Diff line change
Expand Up @@ -2146,8 +2146,14 @@ export default {
'A new version of Qwen Code is available! {{current}} → {{latest}}':
'Eine neue Version von Qwen Code ist verfügbar! {{current}} → {{latest}}',
'Qwen Code {{version}} is up to date!': 'Qwen Code {{version}} ist aktuell!',
'Failed to check for updates. Please check your network or registry configuration.':
'Suche nach Updates fehlgeschlagen. Bitte Netzwerk- oder Registry-Konfiguration prüfen.',
'Failed to check for updates ({{reason}}). Please check your network or registry configuration.':
'Suche nach Updates fehlgeschlagen ({{reason}}). Bitte Netzwerk- oder Registry-Konfiguration prüfen.',
'Update check skipped ({{reason}}) — run /update to retry.':
'Update-Prüfung übersprungen ({{reason}}) — mit /update erneut versuchen.',
'registry did not respond within {{seconds}}s':
'Registry hat nicht innerhalb von {{seconds}}s geantwortet',
'registry unreachable': 'Registry nicht erreichbar',
'registry error': 'Registry-Fehler',
'Unable to check for updates: {{reason}}':
'Updates können nicht geprüft werden: {{reason}}',
'Update successful! The new version will be used on your next run.':
Expand Down
10 changes: 8 additions & 2 deletions packages/cli/src/i18n/locales/en.js
Original file line number Diff line number Diff line change
Expand Up @@ -2604,8 +2604,14 @@ export default {
'A new version of Qwen Code is available! {{current}} → {{latest}}',
'Qwen Code {{version}} is up to date!':
'Qwen Code {{version}} is up to date!',
'Failed to check for updates. Please check your network or registry configuration.':
'Failed to check for updates. Please check your network or registry configuration.',
'Failed to check for updates ({{reason}}). Please check your network or registry configuration.':
'Failed to check for updates ({{reason}}). Please check your network or registry configuration.',
'Update check skipped ({{reason}}) — run /update to retry.':
'Update check skipped ({{reason}}) — run /update to retry.',
'registry did not respond within {{seconds}}s':
'registry did not respond within {{seconds}}s',
'registry unreachable': 'registry unreachable',
'registry error': 'registry error',
'Unable to check for updates: {{reason}}':
'Unable to check for updates: {{reason}}',
'Update successful! The new version will be used on your next run.':
Expand Down
10 changes: 8 additions & 2 deletions packages/cli/src/i18n/locales/fr.js
Original file line number Diff line number Diff line change
Expand Up @@ -2148,8 +2148,14 @@ export default {
'A new version of Qwen Code is available! {{current}} → {{latest}}':
'Une nouvelle version de Qwen Code est disponible ! {{current}} → {{latest}}',
'Qwen Code {{version}} is up to date!': 'Qwen Code {{version}} est à jour !',
'Failed to check for updates. Please check your network or registry configuration.':
'Échec de la vérification des mises à jour. Vérifiez votre réseau ou la configuration du registre.',
'Failed to check for updates ({{reason}}). Please check your network or registry configuration.':
'Échec de la vérification des mises à jour ({{reason}}). Vérifiez votre réseau ou la configuration du registre.',
'Update check skipped ({{reason}}) — run /update to retry.':
'Vérification des mises à jour ignorée ({{reason}}) — exécutez /update pour réessayer.',
'registry did not respond within {{seconds}}s':
"le registre n'a pas répondu en {{seconds}}s",
'registry unreachable': 'registre inaccessible',
'registry error': 'erreur du registre',
'Unable to check for updates: {{reason}}':
'Impossible de vérifier les mises à jour : {{reason}}',
'Update successful! The new version will be used on your next run.':
Expand Down
10 changes: 8 additions & 2 deletions packages/cli/src/i18n/locales/ja.js
Original file line number Diff line number Diff line change
Expand Up @@ -1912,8 +1912,14 @@ export default {
'A new version of Qwen Code is available! {{current}} → {{latest}}':
'Qwen Code の新しいバージョンがあります!{{current}} → {{latest}}',
'Qwen Code {{version}} is up to date!': 'Qwen Code {{version}} は最新です!',
'Failed to check for updates. Please check your network or registry configuration.':
'アップデートの確認に失敗しました。ネットワークまたはレジストリ設定を確認してください。',
'Failed to check for updates ({{reason}}). Please check your network or registry configuration.':
'アップデートの確認に失敗しました({{reason}})。ネットワークまたはレジストリ設定を確認してください。',
'Update check skipped ({{reason}}) — run /update to retry.':
'アップデートの確認をスキップしました({{reason}})— /update で再試行できます。',
'registry did not respond within {{seconds}}s':
'レジストリが {{seconds}} 秒以内に応答しませんでした',
'registry unreachable': 'レジストリに接続できません',
'registry error': 'レジストリエラー',
'Unable to check for updates: {{reason}}':
'アップデートを確認できません: {{reason}}',
'Update successful! The new version will be used on your next run.':
Expand Down
10 changes: 8 additions & 2 deletions packages/cli/src/i18n/locales/pt.js
Original file line number Diff line number Diff line change
Expand Up @@ -2133,8 +2133,14 @@ export default {
'Uma nova versão do Qwen Code está disponível! {{current}} → {{latest}}',
'Qwen Code {{version}} is up to date!':
'Qwen Code {{version}} está atualizado!',
'Failed to check for updates. Please check your network or registry configuration.':
'Falha ao verificar atualizações. Verifique sua rede ou configuração do registro.',
'Failed to check for updates ({{reason}}). Please check your network or registry configuration.':
'Falha ao verificar atualizações ({{reason}}). Verifique sua rede ou configuração do registro.',
'Update check skipped ({{reason}}) — run /update to retry.':
'Verificação de atualização ignorada ({{reason}}) — execute /update para tentar novamente.',
'registry did not respond within {{seconds}}s':
'o registro não respondeu em {{seconds}}s',
'registry unreachable': 'registro inacessível',
'registry error': 'erro no registro',
'Unable to check for updates: {{reason}}':
'Não foi possível verificar atualizações: {{reason}}',
'Update successful! The new version will be used on your next run.':
Expand Down
10 changes: 8 additions & 2 deletions packages/cli/src/i18n/locales/ru.js
Original file line number Diff line number Diff line change
Expand Up @@ -2120,8 +2120,14 @@ export default {
'A new version of Qwen Code is available! {{current}} → {{latest}}':
'Доступна новая версия Qwen Code! {{current}} → {{latest}}',
'Qwen Code {{version}} is up to date!': 'Qwen Code {{version}} актуален!',
'Failed to check for updates. Please check your network or registry configuration.':
'Не удалось проверить обновления. Проверьте сеть или настройки registry.',
'Failed to check for updates ({{reason}}). Please check your network or registry configuration.':
'Не удалось проверить обновления ({{reason}}). Проверьте сеть или настройки registry.',
'Update check skipped ({{reason}}) — run /update to retry.':
'Проверка обновлений пропущена ({{reason}}) — выполните /update для повторной попытки.',
'registry did not respond within {{seconds}}s':
'registry не ответил за {{seconds}} с',
'registry unreachable': 'registry недоступен',
'registry error': 'ошибка registry',
'Unable to check for updates: {{reason}}':
'Невозможно проверить обновления: {{reason}}',
'Update successful! The new version will be used on your next run.':
Expand Down
10 changes: 8 additions & 2 deletions packages/cli/src/i18n/locales/zh-TW.js
Original file line number Diff line number Diff line change
Expand Up @@ -2197,8 +2197,14 @@ export default {
'A new version of Qwen Code is available! {{current}} → {{latest}}':
'Qwen Code 有新版本可用!{{current}} → {{latest}}',
'Qwen Code {{version}} is up to date!': 'Qwen Code {{version}} 已是最新!',
'Failed to check for updates. Please check your network or registry configuration.':
'檢查更新失敗。請檢查網路或 registry 設定。',
'Failed to check for updates ({{reason}}). Please check your network or registry configuration.':
'檢查更新失敗({{reason}})。請檢查網路或 registry 設定。',
'Update check skipped ({{reason}}) — run /update to retry.':
'已略過更新檢查({{reason}})— 可執行 /update 重試。',
'registry did not respond within {{seconds}}s':
'registry 在 {{seconds}} 秒內未回應',
'registry unreachable': 'registry 無法連線',
'registry error': 'registry 錯誤',
'Unable to check for updates: {{reason}}': '無法檢查更新:{{reason}}',
'Update successful! The new version will be used on your next run.':
'更新成功!新版本將在下次執行時生效。',
Expand Down
10 changes: 8 additions & 2 deletions packages/cli/src/i18n/locales/zh.js
Original file line number Diff line number Diff line change
Expand Up @@ -2399,8 +2399,14 @@ export default {
'A new version of Qwen Code is available! {{current}} → {{latest}}':
'Qwen Code 有新版本可用!{{current}} → {{latest}}',
'Qwen Code {{version}} is up to date!': 'Qwen Code {{version}} 已是最新!',
'Failed to check for updates. Please check your network or registry configuration.':
'检查更新失败。请检查网络或 registry 配置。',
'Failed to check for updates ({{reason}}). Please check your network or registry configuration.':
'检查更新失败({{reason}})。请检查网络或 registry 配置。',
'Update check skipped ({{reason}}) — run /update to retry.':
'已跳过更新检查({{reason}})— 可运行 /update 重试。',
'registry did not respond within {{seconds}}s':
'registry 在 {{seconds}} 秒内未响应',
'registry unreachable': 'registry 无法连接',
'registry error': 'registry 错误',
'Unable to check for updates: {{reason}}': '无法检查更新:{{reason}}',
'Update successful! The new version will be used on your next run.':
'更新成功!新版本将在下次运行时生效。',
Expand Down
60 changes: 53 additions & 7 deletions packages/cli/src/startup/startup-prefetch.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,8 @@ vi.mock('../utils/startupProfiler.js', () => ({
recordStartupEvent: (...args: unknown[]) => mockRecordStartupEvent(...args),
}));

vi.mock('../ui/utils/updateCheck.js', () => ({
vi.mock('../ui/utils/updateCheck.js', async (importOriginal) => ({
...(await importOriginal<typeof import('../ui/utils/updateCheck.js')>()),
checkForUpdatesDetailed: (...args: unknown[]) =>
mockCheckForUpdatesDetailed(...args),
}));
Expand All @@ -78,7 +79,12 @@ vi.mock('../utils/updateEventEmitter.js', () => ({
}));

vi.mock('../i18n/index.js', () => ({
t: (key: string) => key,
t: (key: string, params?: Record<string, string | number>) =>
params
? key.replace(/\{\{(\w+)\}\}/g, (match, name) =>
name in params ? String(params[name]) : match,
)
: key,
}));

vi.mock('../core/initializer.js', () => ({
Expand Down Expand Up @@ -415,7 +421,7 @@ describe('startupPrefetch', () => {
expect(mockConnectIdeForStartup).toHaveBeenCalledWith(config);
});

it('surfaces update check errors through the update event emitter', async () => {
it('surfaces update check errors as a warning through the update event emitter', async () => {
const config = makeConfig();
mockCheckForUpdatesDetailed.mockResolvedValue({
status: 'error',
Expand All @@ -427,12 +433,52 @@ describe('startupPrefetch', () => {
await vi.dynamicImportSettled();

expect(mockUpdateEventEmit).toHaveBeenCalledWith('update-failed', {
message:
'Failed to check for updates. Please check your network or registry configuration.',
message: 'Update check skipped (registry error) — run /update to retry.',
severity: 'warning',
});
expect(mockRequestUpdateOnExit).not.toHaveBeenCalled();
});

it('reports a timeout-specific reason when the update check times out', async () => {
const config = makeConfig();
const { UpdateCheckTimeoutError, FETCH_TIMEOUT_MS } = await import(
'../ui/utils/updateCheck.js'
);
mockCheckForUpdatesDetailed.mockResolvedValue({
status: 'error',
error: new UpdateCheckTimeoutError(FETCH_TIMEOUT_MS, 'latest'),
});

startPostRenderPrefetches(config, makeSettings());

await vi.dynamicImportSettled();

expect(mockUpdateEventEmit).toHaveBeenCalledWith('update-failed', {
message: `Update check skipped (registry did not respond within ${Math.round(FETCH_TIMEOUT_MS / 1000)}s) — run /update to retry.`,
severity: 'warning',
});
});

it('reports an offline-specific reason when the registry is unreachable', async () => {
const config = makeConfig();
const error = new Error('getaddrinfo failed') as NodeJS.ErrnoException;
error.code = 'ENOTFOUND';
mockCheckForUpdatesDetailed.mockResolvedValue({
status: 'error',
error,
});

startPostRenderPrefetches(config, makeSettings());

await vi.dynamicImportSettled();

expect(mockUpdateEventEmit).toHaveBeenCalledWith('update-failed', {
message:
'Update check skipped (registry unreachable) — run /update to retry.',
severity: 'warning',
});
});

it('requires connectIde option before connecting IDE', async () => {
const config = makeConfig();
const { statuses, stop } = captureIdeConnectionStatuses();
Expand Down Expand Up @@ -657,8 +703,8 @@ describe('startupPrefetch', () => {

expect(mockWarn).toHaveBeenCalledWith('update_check failed:', error);
expect(mockUpdateEventEmit).toHaveBeenCalledWith('update-failed', {
message:
'Failed to check for updates. Please check your network or registry configuration.',
message: 'Update check skipped (registry error) — run /update to retry.',
severity: 'warning',
});
});

Expand Down
18 changes: 12 additions & 6 deletions packages/cli/src/startup/startup-prefetch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,7 @@ export function startPostRenderPrefetches(
) {
runDeferredTask('update_check', async () => {
const [
{ checkForUpdatesDetailed },
{ checkForUpdatesDetailed, describeUpdateCheckFailure },
{ handleAutoUpdate },
{ getInstallationInfo },
{ updateEventEmitter },
Expand All @@ -171,9 +171,13 @@ export function startPostRenderPrefetches(
import('../utils/updateEventEmitter.js'),
import('../i18n/index.js'),
]);
const updateFailedMessage = t(
'Failed to check for updates. Please check your network or registry configuration.',
);
// The startup check is best-effort background work: surface failures as
// a soft warning with the concrete reason instead of an alarming error
// (#7049), while keeping the failure visible (#6857).
const updateCheckSkippedMessage = (error: unknown) =>
t('Update check skipped ({{reason}}) — run /update to retry.', {
reason: describeUpdateCheckFailure(error),
});
try {
const result = await checkForUpdatesDetailed();
if (result.status === 'update') {
Expand Down Expand Up @@ -222,12 +226,14 @@ export function startPostRenderPrefetches(
}
} else if (result.status === 'error') {
updateEventEmitter.emit('update-failed', {
message: updateFailedMessage,
message: updateCheckSkippedMessage(result.error),
severity: 'warning',
});
}
} catch (error) {
updateEventEmitter.emit('update-failed', {
message: updateFailedMessage,
message: updateCheckSkippedMessage(error),
severity: 'warning',
});
throw error;
}
Expand Down
7 changes: 5 additions & 2 deletions packages/cli/src/ui/commands/update-command.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,10 @@ const formatUpdateInstructions = vi.fn(
return ['Manual update required. Please reinstall Qwen Code.'];
},
);
vi.mock('../utils/updateCheck.js', () => ({ checkForUpdatesDetailed }));
vi.mock('../utils/updateCheck.js', async (importOriginal) => ({
...(await importOriginal<typeof import('../utils/updateCheck.js')>()),
checkForUpdatesDetailed,
}));
vi.mock('../../utils/processUtils.js', () => ({
CUSTOM_SANDBOX_IMAGE_ENV_VAR: 'QWEN_CODE_CUSTOM_SANDBOX_IMAGE',
HOST_UPDATE_RELAUNCH_ENV_VAR: 'QWEN_CODE_HOST_UPDATE_RELAUNCH',
Expand Down Expand Up @@ -376,7 +379,7 @@ describe('updateCommand', () => {
type: 'message',
messageType: 'error',
content:
'Failed to check for updates. Please check your network or registry configuration.',
'Failed to check for updates (registry error). Please check your network or registry configuration.',
});
expect(getInstallationInfo).not.toHaveBeenCalled();
});
Expand Down
5 changes: 3 additions & 2 deletions packages/cli/src/ui/commands/update-command.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ export const updateCommand: SlashCommand = {
supportedModes: ['interactive', 'non_interactive', 'acp'] as const,
action: async (context) => {
const [
{ checkForUpdatesDetailed },
{ checkForUpdatesDetailed, describeUpdateCheckFailure },
{
CUSTOM_SANDBOX_IMAGE_ENV_VAR,
HOST_UPDATE_RELAUNCH_ENV_VAR,
Expand Down Expand Up @@ -54,7 +54,8 @@ export const updateCommand: SlashCommand = {
type: 'message' as const,
messageType: 'error' as const,
content: t(
'Failed to check for updates. Please check your network or registry configuration.',
'Failed to check for updates ({{reason}}). Please check your network or registry configuration.',
{ reason: describeUpdateCheckFailure(updateCheck.error) },
),
};
}
Expand Down
Loading
Loading