fix(desktop): i18n the gateway auth error summary + clean regex char classes

Follow-up to the salvaged PR #39439:
- Replace the hardcoded English gateway-auth summary with a
  notifications.errors.gatewayAuthFailed i18n key (en/ja/zh/zh-hant + types)
- Fix the malformed ['"'] regex character classes (duplicate quote) in
  notifications.ts error matchers
- Add regression test: gateway_auth_failed maps to the gateway auth
  summary, provider invalid_api_key still maps to the OpenAI summary (#39365)
This commit is contained in:
Teknium 2026-07-23 08:29:16 -07:00
parent 32f7c5afaf
commit 25b3cd2ced
7 changed files with 42 additions and 3 deletions

View file

@ -132,6 +132,7 @@ export const en: Translations = {
errors: {
elevenLabsNeedsKey: 'ElevenLabs STT needs ELEVENLABS_API_KEY.',
elevenLabsRejectedKey: 'ElevenLabs rejected the API key (401).',
gatewayAuthFailed: 'Gateway authentication failed — check your API_SERVER_KEY.',
methodNotAllowed:
'The desktop backend rejected that request (405 Method Not Allowed). Try restarting Hermes Desktop.',
microphonePermission: 'Microphone permission was denied.',

View file

@ -133,6 +133,7 @@ export const ja = defineLocale({
errors: {
elevenLabsNeedsKey: 'ElevenLabs STT には ELEVENLABS_API_KEY が必要です。',
elevenLabsRejectedKey: 'ElevenLabs が API キーを拒否しました (401)。',
gatewayAuthFailed: 'ゲートウェイ認証に失敗しました — API_SERVER_KEY を確認してください。',
methodNotAllowed:
'デスクトップバックエンドがそのリクエストを拒否しました (405 Method Not Allowed)。Hermes Desktop を再起動してください。',
microphonePermission: 'マイクのアクセス許可が拒否されました。',

View file

@ -173,6 +173,7 @@ export interface Translations {
errors: {
elevenLabsNeedsKey: string
elevenLabsRejectedKey: string
gatewayAuthFailed: string
methodNotAllowed: string
microphonePermission: string
openaiRejectedApiKey: string

View file

@ -129,6 +129,7 @@ export const zhHant = defineLocale({
errors: {
elevenLabsNeedsKey: 'ElevenLabs STT 需要 ELEVENLABS_API_KEY。',
elevenLabsRejectedKey: 'ElevenLabs 拒絕了該 API 金鑰 (401)。',
gatewayAuthFailed: '閘道認證失敗 — 請檢查你的 API_SERVER_KEY。',
methodNotAllowed: '桌面後端拒絕了該請求 (405 Method Not Allowed)。請嘗試重新啟動 Hermes Desktop。',
microphonePermission: '麥克風權限已被拒絕。',
openaiRejectedApiKey: 'OpenAI 拒絕了該 API 金鑰。',

View file

@ -129,6 +129,7 @@ export const zh: Translations = {
errors: {
elevenLabsNeedsKey: 'ElevenLabs STT 需要 ELEVENLABS_API_KEY。',
elevenLabsRejectedKey: 'ElevenLabs 拒绝了该 API key (401)。',
gatewayAuthFailed: '网关认证失败 — 请检查你的 API_SERVER_KEY。',
methodNotAllowed: '桌面后端拒绝了该请求 (405 Method Not Allowed)。请尝试重启 Hermes Desktop。',
microphonePermission: '麦克风权限已被拒绝。',
openaiRejectedApiKey: 'OpenAI 拒绝了该 API key。',

View file

@ -0,0 +1,34 @@
import { beforeEach, expect, test } from 'vitest'
import { $notifications, clearNotifications, notifyError } from './notifications'
beforeEach(() => {
clearNotifications()
})
function lastMessage(): string {
return $notifications.get()[0]?.message ?? ''
}
// Regression for #39365: a gateway auth 401 (bad API_SERVER_KEY) must not be
// summarized as a provider (OpenAI/OpenRouter) API key problem.
test('gateway_auth_failed error is summarized as gateway auth, not provider key', () => {
notifyError(
new Error(
'401 {"error": {"message": "Invalid gateway API key (API_SERVER_KEY)", "type": "gateway_auth_error", "code": "gateway_auth_failed"}}'
),
'Request failed'
)
expect(lastMessage()).toContain('API_SERVER_KEY')
expect(lastMessage()).not.toMatch(/OpenAI/i)
})
test('provider invalid_api_key error still maps to the OpenAI summary', () => {
notifyError(
new Error('401 {"error": {"message": "Incorrect API key provided", "code": "invalid_api_key"}}'),
'Request failed'
)
expect(lastMessage()).toMatch(/OpenAI rejected the API key/i)
})

View file

@ -78,11 +78,11 @@ function cleanErrorText(value: string) {
const ERROR_SUMMARIES: { test: (msg: string) => boolean; summarize: (msg: string) => string }[] = [
{
test: msg => /['"']code['"']\s*:\s*['"']gateway_auth_failed['"']/i.test(msg),
summarize: () => 'Gateway authentication failed — check your API_SERVER_KEY.'
test: msg => /['"]code['"]\s*:\s*['"]gateway_auth_failed['"]/i.test(msg),
summarize: () => translateNow('notifications.errors.gatewayAuthFailed')
},
{
test: msg => /incorrect api key provided/i.test(msg) || /['"']code['"']\s*:\s*['"']invalid_api_key['"']/i.test(msg),
test: msg => /incorrect api key provided/i.test(msg) || /['"]code['"]\s*:\s*['"]invalid_api_key['"]/i.test(msg),
summarize: msg => {
const status = msg.match(/(?:error code|status(?:Code)?)[^\d]*(\d{3})/i)?.[1]