From 9f7b96a434ca7888f624b1e642beca4cefffac48 Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Sun, 2 Aug 2026 17:25:25 -0700 Subject: [PATCH] fix: secure sessions and clarify Keychain retry --- CHANGELOG.md | 2 + .../Ollama/OllamaUIErrorMapper.swift | 3 +- .../Resources/ar.lproj/Localizable.strings | 2 +- .../Resources/ca.lproj/Localizable.strings | 2 +- .../Resources/de.lproj/Localizable.strings | 2 +- .../Resources/en.lproj/Localizable.strings | 2 +- .../Resources/es.lproj/Localizable.strings | 2 +- .../Resources/fa.lproj/Localizable.strings | 2 +- .../Resources/fr.lproj/Localizable.strings | 2 +- .../Resources/gl.lproj/Localizable.strings | 2 +- .../Resources/id.lproj/Localizable.strings | 2 +- .../Resources/it.lproj/Localizable.strings | 2 +- .../Resources/ja.lproj/Localizable.strings | 2 +- .../Resources/ko.lproj/Localizable.strings | 2 +- .../Resources/nl.lproj/Localizable.strings | 2 +- .../Resources/pl.lproj/Localizable.strings | 2 +- .../Resources/pt-BR.lproj/Localizable.strings | 2 +- .../Resources/ru.lproj/Localizable.strings | 2 +- .../Resources/sv.lproj/Localizable.strings | 2 +- .../Resources/th.lproj/Localizable.strings | 2 +- .../Resources/tr.lproj/Localizable.strings | 2 +- .../Resources/uk.lproj/Localizable.strings | 2 +- .../Resources/vi.lproj/Localizable.strings | 2 +- .../zh-Hans.lproj/Localizable.strings | 2 +- .../zh-Hant.lproj/Localizable.strings | 2 +- Sources/CodexBarCLI/CLICookieCommand.swift | 2 +- .../Augment/AugmentStatusProbe.swift | 17 +++- .../Providers/Ollama/OllamaUsageFetcher.swift | 3 +- .../AugmentSessionStoreTests.swift | 91 +++++++++++++++++++ .../CodexBarTests/CLICookieRefreshTests.swift | 3 +- .../OllamaUIErrorMapperTests.swift | 5 + 31 files changed, 142 insertions(+), 30 deletions(-) create mode 100644 Tests/CodexBarTests/AugmentSessionStoreTests.swift diff --git a/CHANGELOG.md b/CHANGELOG.md index e46481f7ea..a1b8c68d70 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,8 @@ - Menu bar: Session/Weekly/Auto pace layout tokens that render the signed pace delta (`+11%`, `-8%`, `0%`), restoring the pre-0.45 "Both" display in the layout editor (#2540, fixes #2534). Thanks @kratocz! ### Fixed +- Augment: store session cookies owner-only (0600), atomically publish updates, and repair permissions on legacy files (#2567). +- Ollama: direct declined Chrome Keychain access recovery to the provider card's Refresh (⌘R) action instead of the ambiguous manual-cookie path (#2072). - Codex: persist and budget fork-parent discovery so missing parents quiesce between inventory changes instead of sweeping every rollout on each refresh (#2525, #2538). Thanks @xx205, and @Helmi and @kiranmagic7 for the investigation! - Claude: Auto cold boot with Keychain disabled loads without manual refresh (#2494, fixes #2493). Thanks @gmkbenjamin! - Menu: no more stray floating "Refresh" tooltip beside the menu when switching tabs with the cursor over the actions area. diff --git a/Sources/CodexBar/Providers/Ollama/OllamaUIErrorMapper.swift b/Sources/CodexBar/Providers/Ollama/OllamaUIErrorMapper.swift index 702cc9bd59..3034cbfa7e 100644 --- a/Sources/CodexBar/Providers/Ollama/OllamaUIErrorMapper.swift +++ b/Sources/CodexBar/Providers/Ollama/OllamaUIErrorMapper.swift @@ -15,7 +15,8 @@ struct OllamaUIErrorMapper { } if let browserName = self.browserName( in: trimmed, - suffix: " cookie decryption was declined in Keychain; retry with a manual refresh.") + suffix: " cookie decryption was declined in Keychain. " + + "Open the provider card and click Refresh (⌘R) to request Keychain access again.") { return String(format: localize("ollama_browser_cookie_decryption_denied"), browserName) } diff --git a/Sources/CodexBar/Resources/ar.lproj/Localizable.strings b/Sources/CodexBar/Resources/ar.lproj/Localizable.strings index 804e96f759..e34abdf162 100644 --- a/Sources/CodexBar/Resources/ar.lproj/Localizable.strings +++ b/Sources/CodexBar/Resources/ar.lproj/Localizable.strings @@ -1,7 +1,7 @@ /* Arabic localization for CodexBar */ "ollama_safari_cookie_access_hint" = "تحتاج ملفات تعريف ارتباط Safari إلى وصول كامل إلى القرص لتطبيق CodexBar (إعدادات النظام > الخصوصية والأمان)."; -"ollama_browser_cookie_decryption_denied" = "تم رفض فك تشفير ملفات تعريف ارتباط %@ في سلسلة المفاتيح؛ أعد المحاولة بتحديث يدوي."; +"ollama_browser_cookie_decryption_denied" = "تم رفض فك تشفير ملفات تعريف ارتباط %@ في سلسلة المفاتيح. افتح بطاقة موفر الخدمة وانقر على تحديث (⌘R) لطلب الوصول إلى سلسلة المفاتيح مرة أخرى."; "ollama_browser_cookie_decryption_disabled" = "فك تشفير ملفات تعريف ارتباط %@ معطل في CodexBar؛ فعّل الوصول إلى سلسلة المفاتيح ثم حدّث."; " providers" = " providers"; diff --git a/Sources/CodexBar/Resources/ca.lproj/Localizable.strings b/Sources/CodexBar/Resources/ca.lproj/Localizable.strings index 6c64f10588..90dc7f77a4 100644 --- a/Sources/CodexBar/Resources/ca.lproj/Localizable.strings +++ b/Sources/CodexBar/Resources/ca.lproj/Localizable.strings @@ -1,7 +1,7 @@ /* Catalan localization for CodexBar */ "ollama_safari_cookie_access_hint" = "Les galetes de Safari necessiten accés complet al disc per a CodexBar (Configuració del Sistema > Privadesa i seguretat)."; -"ollama_browser_cookie_decryption_denied" = "S'ha denegat el desxifratge de les galetes de %@ al Clauer; torneu-ho a provar amb una actualització manual."; +"ollama_browser_cookie_decryption_denied" = "S'ha denegat el desxifratge de les galetes de %@ al Clauer. Obriu la targeta del proveïdor i feu clic a Actualitza (⌘R) per tornar a sol·licitar accés al Clauer."; "ollama_browser_cookie_decryption_disabled" = "El desxifratge de les galetes de %@ està desactivat a CodexBar; activeu l'accés al Clauer i actualitzeu."; " providers" = " proveïdors"; diff --git a/Sources/CodexBar/Resources/de.lproj/Localizable.strings b/Sources/CodexBar/Resources/de.lproj/Localizable.strings index 444e9a0383..986baa2975 100644 --- a/Sources/CodexBar/Resources/de.lproj/Localizable.strings +++ b/Sources/CodexBar/Resources/de.lproj/Localizable.strings @@ -21,7 +21,7 @@ "hooks_delete_argument" = "Argument löschen"; "ollama_safari_cookie_access_hint" = "Safari-Cookies benötigen vollen Festplattenzugriff für CodexBar (Systemeinstellungen > Datenschutz & Sicherheit)."; -"ollama_browser_cookie_decryption_denied" = "Die Entschlüsselung der %@-Cookies wurde im Schlüsselbund abgelehnt; versuchen Sie es mit einer manuellen Aktualisierung erneut."; +"ollama_browser_cookie_decryption_denied" = "Die Entschlüsselung der %@-Cookies wurde im Schlüsselbund abgelehnt. Öffne die Anbieterkarte und klicke auf „Aktualisieren“ (⌘R), um erneut Schlüsselbundzugriff anzufordern."; "ollama_browser_cookie_decryption_disabled" = "Die Entschlüsselung der %@-Cookies ist in CodexBar deaktiviert; aktivieren Sie den Schlüsselbundzugriff und aktualisieren Sie."; " providers" = "Anbieter"; diff --git a/Sources/CodexBar/Resources/en.lproj/Localizable.strings b/Sources/CodexBar/Resources/en.lproj/Localizable.strings index 9a615eb87d..d218b6c7ab 100644 --- a/Sources/CodexBar/Resources/en.lproj/Localizable.strings +++ b/Sources/CodexBar/Resources/en.lproj/Localizable.strings @@ -1,7 +1,7 @@ /* English localization for CodexBar (base/fallback) */ "ollama_safari_cookie_access_hint" = "Safari cookies need Full Disk Access for CodexBar (System Settings > Privacy & Security)."; -"ollama_browser_cookie_decryption_denied" = "%@ cookie decryption was declined in Keychain; retry with a manual refresh."; +"ollama_browser_cookie_decryption_denied" = "%@ cookie decryption was declined in Keychain. Open the provider card and click Refresh (⌘R) to request Keychain access again."; "ollama_browser_cookie_decryption_disabled" = "%@ cookie decryption is disabled in CodexBar; enable Keychain access and refresh."; " providers" = " providers"; diff --git a/Sources/CodexBar/Resources/es.lproj/Localizable.strings b/Sources/CodexBar/Resources/es.lproj/Localizable.strings index eaf258d0cb..35eba0ecd9 100644 --- a/Sources/CodexBar/Resources/es.lproj/Localizable.strings +++ b/Sources/CodexBar/Resources/es.lproj/Localizable.strings @@ -21,7 +21,7 @@ "hooks_delete_argument" = "Eliminar argumento"; "ollama_safari_cookie_access_hint" = "Las cookies de Safari necesitan acceso total al disco para CodexBar (Ajustes del Sistema > Privacidad y seguridad)."; -"ollama_browser_cookie_decryption_denied" = "Se rechazó en el Llavero el descifrado de las cookies de %@; vuelve a intentarlo con una actualización manual."; +"ollama_browser_cookie_decryption_denied" = "Se rechazó en el Llavero el descifrado de las cookies de %@. Abre la tarjeta del proveedor y haz clic en Actualizar (⌘R) para volver a solicitar acceso al Llavero."; "ollama_browser_cookie_decryption_disabled" = "El descifrado de las cookies de %@ está desactivado en CodexBar; activa el acceso al Llavero y actualiza."; " providers" = " proveedores"; diff --git a/Sources/CodexBar/Resources/fa.lproj/Localizable.strings b/Sources/CodexBar/Resources/fa.lproj/Localizable.strings index d37c3dbea9..9488b425cf 100644 --- a/Sources/CodexBar/Resources/fa.lproj/Localizable.strings +++ b/Sources/CodexBar/Resources/fa.lproj/Localizable.strings @@ -1,7 +1,7 @@ /* Persian localization for CodexBar */ "ollama_safari_cookie_access_hint" = "کوکی‌های Safari برای CodexBar به دسترسی کامل دیسک نیاز دارند (تنظیمات سیستم > حریم خصوصی و امنیت)."; -"ollama_browser_cookie_decryption_denied" = "رمزگشایی کوکی‌های %@ در Keychain رد شد؛ با یک تازه‌سازی دستی دوباره تلاش کنید."; +"ollama_browser_cookie_decryption_denied" = "رمزگشایی کوکی‌های %@ در Keychain رد شد. کارت ارائه‌دهنده را باز کنید و روی تازه‌سازی (⌘R) کلیک کنید تا دوباره دسترسی به Keychain درخواست شود."; "ollama_browser_cookie_decryption_disabled" = "رمزگشایی کوکی‌های %@ در CodexBar غیرفعال است؛ دسترسی Keychain را فعال و تازه‌سازی کنید."; " providers" = " providers"; diff --git a/Sources/CodexBar/Resources/fr.lproj/Localizable.strings b/Sources/CodexBar/Resources/fr.lproj/Localizable.strings index 8eceffcfef..e848e1eb87 100644 --- a/Sources/CodexBar/Resources/fr.lproj/Localizable.strings +++ b/Sources/CodexBar/Resources/fr.lproj/Localizable.strings @@ -21,7 +21,7 @@ "hooks_delete_argument" = "Supprimer l’argument"; "ollama_safari_cookie_access_hint" = "Les cookies Safari nécessitent l’accès complet au disque pour CodexBar (Réglages Système > Confidentialité et sécurité)."; -"ollama_browser_cookie_decryption_denied" = "Le déchiffrement des cookies %@ a été refusé dans le Trousseau ; réessayez avec une actualisation manuelle."; +"ollama_browser_cookie_decryption_denied" = "Le déchiffrement des cookies %@ a été refusé dans le Trousseau. Ouvrez la fiche du fournisseur et cliquez sur Actualiser (⌘R) pour redemander l’accès au Trousseau."; "ollama_browser_cookie_decryption_disabled" = "Le déchiffrement des cookies %@ est désactivé dans CodexBar ; activez l’accès au Trousseau et actualisez."; " providers" = " fournisseurs"; diff --git a/Sources/CodexBar/Resources/gl.lproj/Localizable.strings b/Sources/CodexBar/Resources/gl.lproj/Localizable.strings index 0a47456788..44955d81dd 100644 --- a/Sources/CodexBar/Resources/gl.lproj/Localizable.strings +++ b/Sources/CodexBar/Resources/gl.lproj/Localizable.strings @@ -1,7 +1,7 @@ /* Galician localization for CodexBar */ "ollama_safari_cookie_access_hint" = "As cookies de Safari precisan acceso total ao disco para CodexBar (Axustes do Sistema > Privacidade e seguridade)."; -"ollama_browser_cookie_decryption_denied" = "Rexeitouse no Chaveiro o descifrado das cookies de %@; téntao de novo cunha actualización manual."; +"ollama_browser_cookie_decryption_denied" = "Rexeitouse no Chaveiro o descifrado das cookies de %@. Abre a tarxeta do provedor e preme Actualizar (⌘R) para solicitar de novo acceso ao Chaveiro."; "ollama_browser_cookie_decryption_disabled" = "O descifrado das cookies de %@ está desactivado en CodexBar; activa o acceso ao Chaveiro e actualiza."; " providers" = " provedores"; diff --git a/Sources/CodexBar/Resources/id.lproj/Localizable.strings b/Sources/CodexBar/Resources/id.lproj/Localizable.strings index 750ece8541..9192176165 100644 --- a/Sources/CodexBar/Resources/id.lproj/Localizable.strings +++ b/Sources/CodexBar/Resources/id.lproj/Localizable.strings @@ -1,7 +1,7 @@ /* Indonesian localization for CodexBar */ "ollama_safari_cookie_access_hint" = "Cookie Safari memerlukan Akses Disk Penuh untuk CodexBar (Pengaturan Sistem > Privasi & Keamanan)."; -"ollama_browser_cookie_decryption_denied" = "Dekripsi cookie %@ ditolak di Rantai Kunci; coba lagi dengan penyegaran manual."; +"ollama_browser_cookie_decryption_denied" = "Dekripsi cookie %@ ditolak di Rantai Kunci. Buka kartu penyedia dan klik Segarkan (⌘R) untuk meminta akses Rantai Kunci lagi."; "ollama_browser_cookie_decryption_disabled" = "Dekripsi cookie %@ dinonaktifkan di CodexBar; aktifkan akses Rantai Kunci lalu segarkan."; " providers" = " penyedia"; diff --git a/Sources/CodexBar/Resources/it.lproj/Localizable.strings b/Sources/CodexBar/Resources/it.lproj/Localizable.strings index 81bea3b7dd..e4ce5bf1ff 100644 --- a/Sources/CodexBar/Resources/it.lproj/Localizable.strings +++ b/Sources/CodexBar/Resources/it.lproj/Localizable.strings @@ -1,7 +1,7 @@ /* Italian localization for CodexBar */ "ollama_safari_cookie_access_hint" = "I cookie di Safari richiedono l’accesso completo al disco per CodexBar (Impostazioni di Sistema > Privacy e sicurezza)."; -"ollama_browser_cookie_decryption_denied" = "La decrittografia dei cookie di %@ è stata rifiutata nel Portachiavi; riprova con un aggiornamento manuale."; +"ollama_browser_cookie_decryption_denied" = "La decrittografia dei cookie di %@ è stata rifiutata nel Portachiavi. Apri la scheda del provider e fai clic su Aggiorna (⌘R) per richiedere di nuovo l’accesso al Portachiavi."; "ollama_browser_cookie_decryption_disabled" = "La decrittografia dei cookie di %@ è disabilitata in CodexBar; abilita l’accesso al Portachiavi e aggiorna."; " providers" = " provider"; diff --git a/Sources/CodexBar/Resources/ja.lproj/Localizable.strings b/Sources/CodexBar/Resources/ja.lproj/Localizable.strings index 75adf471ad..f8ad58360b 100644 --- a/Sources/CodexBar/Resources/ja.lproj/Localizable.strings +++ b/Sources/CodexBar/Resources/ja.lproj/Localizable.strings @@ -21,7 +21,7 @@ "hooks_delete_argument" = "引数を削除"; "ollama_safari_cookie_access_hint" = "Safari Cookie を読み込むには、CodexBar にフルディスクアクセスが必要です(システム設定 > プライバシーとセキュリティ)。"; -"ollama_browser_cookie_decryption_denied" = "%@ Cookie の復号がキーチェーンで拒否されました。手動で更新して再試行してください。"; +"ollama_browser_cookie_decryption_denied" = "%@ Cookie の復号がキーチェーンで拒否されました。プロバイダーカードを開き、更新(⌘R)をクリックしてキーチェーンへのアクセスを再度要求してください。"; "ollama_browser_cookie_decryption_disabled" = "%@ Cookie の復号が CodexBar で無効です。キーチェーンへのアクセスを有効にして更新してください。"; " providers" = " 件のプロバイダ"; diff --git a/Sources/CodexBar/Resources/ko.lproj/Localizable.strings b/Sources/CodexBar/Resources/ko.lproj/Localizable.strings index 87fd2a6a5f..a67ca8b324 100644 --- a/Sources/CodexBar/Resources/ko.lproj/Localizable.strings +++ b/Sources/CodexBar/Resources/ko.lproj/Localizable.strings @@ -21,7 +21,7 @@ "hooks_delete_argument" = "인수 삭제"; "ollama_safari_cookie_access_hint" = "Safari 쿠키를 읽으려면 CodexBar에 전체 디스크 접근 권한이 필요합니다(시스템 설정 > 개인정보 보호 및 보안)."; -"ollama_browser_cookie_decryption_denied" = "%@ 쿠키 복호화가 키체인에서 거부되었습니다. 수동 새로 고침으로 다시 시도하세요."; +"ollama_browser_cookie_decryption_denied" = "%@ 쿠키 복호화가 키체인에서 거부되었습니다. 제공자 카드를 열고 새로 고침(⌘R)을 클릭하여 키체인 접근을 다시 요청하세요."; "ollama_browser_cookie_decryption_disabled" = "%@ 쿠키 복호화가 CodexBar에서 비활성화되어 있습니다. 키체인 접근을 활성화하고 새로 고침하세요."; " providers" = " 공급자"; diff --git a/Sources/CodexBar/Resources/nl.lproj/Localizable.strings b/Sources/CodexBar/Resources/nl.lproj/Localizable.strings index 6860b45551..70864f1bee 100644 --- a/Sources/CodexBar/Resources/nl.lproj/Localizable.strings +++ b/Sources/CodexBar/Resources/nl.lproj/Localizable.strings @@ -21,7 +21,7 @@ "hooks_delete_argument" = "Argument verwijderen"; "ollama_safari_cookie_access_hint" = "Safari-cookies vereisen volledige schijftoegang voor CodexBar (Systeeminstellingen > Privacy en beveiliging)."; -"ollama_browser_cookie_decryption_denied" = "Het ontsleutelen van %@-cookies is geweigerd in Sleutelhanger; probeer opnieuw met handmatig vernieuwen."; +"ollama_browser_cookie_decryption_denied" = "Het ontsleutelen van %@-cookies is geweigerd in Sleutelhanger. Open de providerkaart en klik op Vernieuw (⌘R) om opnieuw toegang tot Sleutelhanger te vragen."; "ollama_browser_cookie_decryption_disabled" = "Het ontsleutelen van %@-cookies is uitgeschakeld in CodexBar; schakel Sleutelhangertoegang in en vernieuw."; " providers" = " providers"; diff --git a/Sources/CodexBar/Resources/pl.lproj/Localizable.strings b/Sources/CodexBar/Resources/pl.lproj/Localizable.strings index cfaba75496..3c213bbbe1 100644 --- a/Sources/CodexBar/Resources/pl.lproj/Localizable.strings +++ b/Sources/CodexBar/Resources/pl.lproj/Localizable.strings @@ -1,7 +1,7 @@ /* English localization for CodexBar (base/fallback) */ "ollama_safari_cookie_access_hint" = "Pliki cookie Safari wymagają pełnego dostępu do dysku dla CodexBar (Ustawienia systemowe > Prywatność i ochrona)."; -"ollama_browser_cookie_decryption_denied" = "Odszyfrowanie plików cookie %@ zostało odrzucone w Pęku kluczy; spróbuj ponownie przez ręczne odświeżenie."; +"ollama_browser_cookie_decryption_denied" = "Odszyfrowanie plików cookie %@ zostało odrzucone w Pęku kluczy. Otwórz kartę dostawcy i kliknij Odśwież (⌘R), aby ponownie poprosić o dostęp do Pęku kluczy."; "ollama_browser_cookie_decryption_disabled" = "Odszyfrowanie plików cookie %@ jest wyłączone w CodexBar; włącz dostęp do Pęku kluczy i odśwież."; " providers" = " providers"; diff --git a/Sources/CodexBar/Resources/pt-BR.lproj/Localizable.strings b/Sources/CodexBar/Resources/pt-BR.lproj/Localizable.strings index 7a28025269..6d7a0f3628 100644 --- a/Sources/CodexBar/Resources/pt-BR.lproj/Localizable.strings +++ b/Sources/CodexBar/Resources/pt-BR.lproj/Localizable.strings @@ -21,7 +21,7 @@ "hooks_delete_argument" = "Excluir argumento"; "ollama_safari_cookie_access_hint" = "Os cookies do Safari precisam de Acesso Total ao Disco para o CodexBar (Ajustes do Sistema > Privacidade e Segurança)."; -"ollama_browser_cookie_decryption_denied" = "A descriptografia dos cookies do %@ foi recusada nas Chaves; tente novamente com uma atualização manual."; +"ollama_browser_cookie_decryption_denied" = "A descriptografia dos cookies do %@ foi recusada nas Chaves. Abra o cartão do provedor e clique em Atualizar (⌘R) para solicitar novamente o acesso às Chaves."; "ollama_browser_cookie_decryption_disabled" = "A descriptografia dos cookies do %@ está desativada no CodexBar; ative o acesso às Chaves e atualize."; " providers" = " provedores"; diff --git a/Sources/CodexBar/Resources/ru.lproj/Localizable.strings b/Sources/CodexBar/Resources/ru.lproj/Localizable.strings index d1d1f273aa..f736ff0a02 100644 --- a/Sources/CodexBar/Resources/ru.lproj/Localizable.strings +++ b/Sources/CodexBar/Resources/ru.lproj/Localizable.strings @@ -21,7 +21,7 @@ "hooks_delete_argument" = "Удалить аргумент"; "ollama_safari_cookie_access_hint" = "Для файлов cookie Safari приложению CodexBar нужен полный доступ к диску (Системные настройки > Конфиденциальность и безопасность)."; -"ollama_browser_cookie_decryption_denied" = "Расшифровка файлов cookie %@ была отклонена в Связке ключей; повторите попытку с помощью ручного обновления."; +"ollama_browser_cookie_decryption_denied" = "Расшифровка файлов cookie %@ была отклонена в Связке ключей. Откройте карточку провайдера и нажмите «Обновить» (⌘R), чтобы снова запросить доступ к Связке ключей."; "ollama_browser_cookie_decryption_disabled" = "Расшифровка файлов cookie %@ отключена в CodexBar; включите доступ к Связке ключей и обновите."; " providers" = " провайдеров"; diff --git a/Sources/CodexBar/Resources/sv.lproj/Localizable.strings b/Sources/CodexBar/Resources/sv.lproj/Localizable.strings index f5b2b25e96..c0bc5b23e7 100644 --- a/Sources/CodexBar/Resources/sv.lproj/Localizable.strings +++ b/Sources/CodexBar/Resources/sv.lproj/Localizable.strings @@ -21,7 +21,7 @@ "hooks_delete_argument" = "Ta bort argument"; "ollama_safari_cookie_access_hint" = "Safari-cookies kräver full skivåtkomst för CodexBar (Systeminställningar > Integritet och säkerhet)."; -"ollama_browser_cookie_decryption_denied" = "Dekryptering av %@-cookies nekades i Nyckelhanteraren; försök igen med en manuell uppdatering."; +"ollama_browser_cookie_decryption_denied" = "Dekryptering av %@-cookies nekades i Nyckelhanteraren. Öppna leverantörskortet och klicka på Uppdatera (⌘R) för att begära åtkomst till Nyckelhanteraren igen."; "ollama_browser_cookie_decryption_disabled" = "Dekryptering av %@-cookies är inaktiverad i CodexBar; aktivera åtkomst till Nyckelhanteraren och uppdatera."; " providers" = " leverantörer"; diff --git a/Sources/CodexBar/Resources/th.lproj/Localizable.strings b/Sources/CodexBar/Resources/th.lproj/Localizable.strings index 1ea1793bd8..5844b8b8bf 100644 --- a/Sources/CodexBar/Resources/th.lproj/Localizable.strings +++ b/Sources/CodexBar/Resources/th.lproj/Localizable.strings @@ -1,7 +1,7 @@ /* Thai localization for CodexBar */ "ollama_safari_cookie_access_hint" = "คุกกี้ Safari ต้องใช้สิทธิ์เข้าถึงดิสก์แบบเต็มสำหรับ CodexBar (การตั้งค่าระบบ > ความเป็นส่วนตัวและความปลอดภัย)"; -"ollama_browser_cookie_decryption_denied" = "การถอดรหัสคุกกี้ %@ ถูกปฏิเสธในพวงกุญแจ โปรดลองอีกครั้งด้วยการรีเฟรชด้วยตนเอง"; +"ollama_browser_cookie_decryption_denied" = "การถอดรหัสคุกกี้ %@ ถูกปฏิเสธในพวงกุญแจ เปิดการ์ดผู้ให้บริการแล้วคลิก รีเฟรช (⌘R) เพื่อขอสิทธิ์เข้าถึงพวงกุญแจอีกครั้ง"; "ollama_browser_cookie_decryption_disabled" = "การถอดรหัสคุกกี้ %@ ถูกปิดใช้งานใน CodexBar ให้เปิดการเข้าถึงพวงกุญแจแล้วรีเฟรช"; " providers" = "ผู้ให้บริการ "; diff --git a/Sources/CodexBar/Resources/tr.lproj/Localizable.strings b/Sources/CodexBar/Resources/tr.lproj/Localizable.strings index 88d936e179..99f4304336 100644 --- a/Sources/CodexBar/Resources/tr.lproj/Localizable.strings +++ b/Sources/CodexBar/Resources/tr.lproj/Localizable.strings @@ -1,7 +1,7 @@ /* Turkish localization for CodexBar */ "ollama_safari_cookie_access_hint" = "Safari çerezleri için CodexBar’a Tam Disk Erişimi gerekir (Sistem Ayarları > Gizlilik ve Güvenlik)."; -"ollama_browser_cookie_decryption_denied" = "%@ çerezlerinin şifresini çözme Anahtar Zinciri’nde reddedildi; manuel yenilemeyle tekrar deneyin."; +"ollama_browser_cookie_decryption_denied" = "%@ çerezlerinin şifresini çözme Anahtar Zinciri’nde reddedildi. Sağlayıcı kartını açın ve Anahtar Zinciri erişimini yeniden istemek için Yenile’ye (⌘R) tıklayın."; "ollama_browser_cookie_decryption_disabled" = "%@ çerezlerinin şifresini çözme CodexBar’da devre dışı; Anahtar Zinciri erişimini etkinleştirip yenileyin."; " providers" = " sağlayıcı"; diff --git a/Sources/CodexBar/Resources/uk.lproj/Localizable.strings b/Sources/CodexBar/Resources/uk.lproj/Localizable.strings index c3083386ac..d04f93c5f4 100644 --- a/Sources/CodexBar/Resources/uk.lproj/Localizable.strings +++ b/Sources/CodexBar/Resources/uk.lproj/Localizable.strings @@ -21,7 +21,7 @@ "hooks_delete_argument" = "Видалити аргумент"; "ollama_safari_cookie_access_hint" = "Для файлів cookie Safari програмі CodexBar потрібен повний доступ до диска (Системні параметри > Конфіденційність і безпека)."; -"ollama_browser_cookie_decryption_denied" = "Розшифрування файлів cookie %@ було відхилено у В’язці ключів; повторіть спробу за допомогою ручного оновлення."; +"ollama_browser_cookie_decryption_denied" = "Розшифрування файлів cookie %@ було відхилено у В’язці ключів. Відкрийте картку провайдера й натисніть «Оновити» (⌘R), щоб знову запитати доступ до В’язки ключів."; "ollama_browser_cookie_decryption_disabled" = "Розшифрування файлів cookie %@ вимкнено в CodexBar; увімкніть доступ до В’язки ключів і оновіть."; " providers" = "провайдерів"; diff --git a/Sources/CodexBar/Resources/vi.lproj/Localizable.strings b/Sources/CodexBar/Resources/vi.lproj/Localizable.strings index d0a274842e..852ea0c786 100644 --- a/Sources/CodexBar/Resources/vi.lproj/Localizable.strings +++ b/Sources/CodexBar/Resources/vi.lproj/Localizable.strings @@ -21,7 +21,7 @@ "hooks_delete_argument" = "Xóa đối số"; "ollama_safari_cookie_access_hint" = "Cookie Safari cần Quyền truy cập toàn bộ ổ đĩa cho CodexBar (Cài đặt hệ thống > Quyền riêng tư & Bảo mật)."; -"ollama_browser_cookie_decryption_denied" = "Việc giải mã cookie %@ đã bị từ chối trong Chuỗi khóa; hãy thử lại bằng cách làm mới thủ công."; +"ollama_browser_cookie_decryption_denied" = "Việc giải mã cookie %@ đã bị từ chối trong Chuỗi khóa. Mở thẻ nhà cung cấp và bấm Làm mới (⌘R) để yêu cầu lại quyền truy cập Chuỗi khóa."; "ollama_browser_cookie_decryption_disabled" = "Việc giải mã cookie %@ bị tắt trong CodexBar; hãy bật quyền truy cập Chuỗi khóa rồi làm mới."; " providers" = "nhà cung cấp"; diff --git a/Sources/CodexBar/Resources/zh-Hans.lproj/Localizable.strings b/Sources/CodexBar/Resources/zh-Hans.lproj/Localizable.strings index b6952c621b..8a7e0d83b8 100644 --- a/Sources/CodexBar/Resources/zh-Hans.lproj/Localizable.strings +++ b/Sources/CodexBar/Resources/zh-Hans.lproj/Localizable.strings @@ -21,7 +21,7 @@ "hooks_delete_argument" = "删除参数"; "ollama_safari_cookie_access_hint" = "CodexBar 需要“完全磁盘访问权限”才能读取 Safari Cookie(系统设置 > 隐私与安全性)。"; -"ollama_browser_cookie_decryption_denied" = "钥匙串拒绝解密 %@ Cookie;请通过手动刷新重试。"; +"ollama_browser_cookie_decryption_denied" = "钥匙串拒绝解密 %@ Cookie。打开提供商卡片并点按“刷新”(⌘R),再次请求钥匙串访问权限。"; "ollama_browser_cookie_decryption_disabled" = "CodexBar 中已停用 %@ Cookie 解密;请启用钥匙串访问权限并刷新。"; " providers" = " 提供商"; diff --git a/Sources/CodexBar/Resources/zh-Hant.lproj/Localizable.strings b/Sources/CodexBar/Resources/zh-Hant.lproj/Localizable.strings index cd90cdcc55..f86f17382a 100644 --- a/Sources/CodexBar/Resources/zh-Hant.lproj/Localizable.strings +++ b/Sources/CodexBar/Resources/zh-Hant.lproj/Localizable.strings @@ -21,7 +21,7 @@ "hooks_delete_argument" = "刪除引數"; "ollama_safari_cookie_access_hint" = "CodexBar 需要「完整磁碟存取權」才能讀取 Safari Cookie(系統設定 > 隱私權與安全性)。"; -"ollama_browser_cookie_decryption_denied" = "鑰匙圈拒絕解密 %@ Cookie;請透過手動重新整理再試一次。"; +"ollama_browser_cookie_decryption_denied" = "鑰匙圈拒絕解密 %@ Cookie。開啟供應商卡片並按一下「重新整理」(⌘R),再次要求鑰匙圈存取權限。"; "ollama_browser_cookie_decryption_disabled" = "CodexBar 中已停用 %@ Cookie 解密;請啟用鑰匙圈存取權並重新整理。"; " providers" = " 提供者"; diff --git a/Sources/CodexBarCLI/CLICookieCommand.swift b/Sources/CodexBarCLI/CLICookieCommand.swift index 26d14a6f7b..1b5ce1a7f2 100644 --- a/Sources/CodexBarCLI/CLICookieCommand.swift +++ b/Sources/CodexBarCLI/CLICookieCommand.swift @@ -152,7 +152,7 @@ extension CodexBarCLI { provider: descriptor.cli.name, status: .failed, message: "\(browser.displayName) cookie decryption was declined in Keychain; " + - "retry with --allow-keychain-prompt.") + "rerun with --allow-keychain-prompt to request Keychain access again.") } return CookieRefreshResult( provider: descriptor.cli.name, diff --git a/Sources/CodexBarCore/Providers/Augment/AugmentStatusProbe.swift b/Sources/CodexBarCore/Providers/Augment/AugmentStatusProbe.swift index 52ae33aebd..d8d97228ee 100644 --- a/Sources/CodexBarCore/Providers/Augment/AugmentStatusProbe.swift +++ b/Sources/CodexBarCore/Providers/Augment/AugmentStatusProbe.swift @@ -281,6 +281,12 @@ public actor AugmentSessionStore { Task { await self.loadFromDiskIfNeeded() } } + #if DEBUG + init(fileURL: URL) { + self.fileURL = fileURL + } + #endif + public func setCookies(_ cookies: [HTTPCookie]) { self.hasLoadedFromDisk = true self.sessionCookies = cookies @@ -316,6 +322,7 @@ public actor AugmentSessionStore { private func loadFromDiskIfNeeded() { guard !self.hasLoadedFromDisk else { return } self.hasLoadedFromDisk = true + CredentialFileWriter.repairPermissions(at: self.fileURL) self.loadFromDisk() } @@ -349,7 +356,7 @@ public actor AugmentSessionStore { else { return } - try? data.write(to: self.fileURL) + try? CredentialFileWriter.writePrivate(data, to: self.fileURL) } private func loadFromDisk() { @@ -362,7 +369,9 @@ public actor AugmentSessionStore { var cookieProps: [HTTPCookiePropertyKey: Any] = [:] for (key, value) in props { // Skip marker keys - if key.hasSuffix("_isDate") || key.hasSuffix("_isURL") { continue } + if key.hasSuffix("_isDate") || key.hasSuffix("_isURL") { + continue + } let propKey = HTTPCookiePropertyKey(key) @@ -642,7 +651,9 @@ public struct AugmentStatusProbe: Sendable { @MainActor private static var recentDumps: [String] = [] @MainActor private static func recordDump(_ text: String) { - if self.recentDumps.count >= 5 { self.recentDumps.removeFirst() } + if self.recentDumps.count >= 5 { + self.recentDumps.removeFirst() + } self.recentDumps.append(text) } diff --git a/Sources/CodexBarCore/Providers/Ollama/OllamaUsageFetcher.swift b/Sources/CodexBarCore/Providers/Ollama/OllamaUsageFetcher.swift index 5774766e8b..19813fc558 100644 --- a/Sources/CodexBarCore/Providers/Ollama/OllamaUsageFetcher.swift +++ b/Sources/CodexBarCore/Providers/Ollama/OllamaUsageFetcher.swift @@ -134,7 +134,8 @@ public enum OllamaUsageError: LocalizedError, Sendable { case .safariCookieAccessDenied: "Safari cookies need Full Disk Access for CodexBar (System Settings > Privacy & Security)." case let .browserCookieDecryptionDenied(browserName): - "\(browserName) cookie decryption was declined in Keychain; retry with a manual refresh." + "\(browserName) cookie decryption was declined in Keychain. " + + "Open the provider card and click Refresh (⌘R) to request Keychain access again." case let .browserCookieDecryptionDisabled(browserName): "\(browserName) cookie decryption is disabled in CodexBar; enable Keychain access and refresh." } diff --git a/Tests/CodexBarTests/AugmentSessionStoreTests.swift b/Tests/CodexBarTests/AugmentSessionStoreTests.swift new file mode 100644 index 0000000000..2c68af19d6 --- /dev/null +++ b/Tests/CodexBarTests/AugmentSessionStoreTests.swift @@ -0,0 +1,91 @@ +import Foundation +import Testing +@testable import CodexBarCore + +struct AugmentSessionStoreTests { + private enum PublishProbeError: Error { + case stop + } + + @Test + func `new session files are owner only`() async throws { + let (directory, fileURL) = try Self.makeSessionLocation() + defer { try? FileManager.default.removeItem(at: directory) } + let store = AugmentSessionStore(fileURL: fileURL) + + try await store.setCookies([Self.makeCookie(value: "new-session")]) + + let attributes = try FileManager.default.attributesOfItem(atPath: fileURL.path) + let permissions = try #require(attributes[.posixPermissions] as? NSNumber) + #expect(permissions.intValue & 0o777 == 0o600) + } + + @Test + func `loading repairs legacy session file permissions`() async throws { + let (directory, fileURL) = try Self.makeSessionLocation() + defer { try? FileManager.default.removeItem(at: directory) } + let writer = AugmentSessionStore(fileURL: fileURL) + try await writer.setCookies([Self.makeCookie(value: "legacy-session")]) + try FileManager.default.setAttributes([.posixPermissions: 0o644], ofItemAtPath: fileURL.path) + let reader = AugmentSessionStore(fileURL: fileURL) + + let cookies = await reader.getCookies() + + #expect(cookies.map(\.value) == ["legacy-session"]) + let attributes = try FileManager.default.attributesOfItem(atPath: fileURL.path) + let permissions = try #require(attributes[.posixPermissions] as? NSNumber) + #expect(permissions.intValue & 0o777 == 0o600) + } + + @Test + func `session cookies round trip through disk`() async throws { + let (directory, fileURL) = try Self.makeSessionLocation() + defer { try? FileManager.default.removeItem(at: directory) } + let writer = AugmentSessionStore(fileURL: fileURL) + try await writer.setCookies([Self.makeCookie(value: "round-trip")]) + let reader = AugmentSessionStore(fileURL: fileURL) + + let cookies = await reader.getCookies() + + #expect(cookies.count == 1) + #expect(cookies.first?.name == "augment_session") + #expect(cookies.first?.value == "round-trip") + #expect(cookies.first?.domain == "app.augmentcode.com") + } + + @Test + func `failed staged write publishes nothing`() throws { + let (directory, fileURL) = try Self.makeSessionLocation() + defer { try? FileManager.default.removeItem(at: directory) } + + #expect(throws: PublishProbeError.stop) { + try CredentialFileWriter.writePrivate(Data("session-cookie".utf8), to: fileURL) { stagedURL in + let attributes = try FileManager.default.attributesOfItem(atPath: stagedURL.path) + let permissions = try #require(attributes[.posixPermissions] as? NSNumber) + #expect(permissions.intValue & 0o777 == 0o600) + throw PublishProbeError.stop + } + } + + #expect(!FileManager.default.fileExists(atPath: fileURL.path)) + #expect(try FileManager.default.contentsOfDirectory(atPath: directory.path).isEmpty) + } + + private static func makeSessionLocation() throws -> (URL, URL) { + let directory = FileManager.default.temporaryDirectory + .appendingPathComponent("codexbar-augment-session-\(UUID().uuidString)", isDirectory: true) + try FileManager.default.createDirectory(at: directory, withIntermediateDirectories: true) + return (directory, directory.appendingPathComponent("augment-session.json")) + } + + private static func makeCookie(value: String) throws -> HTTPCookie { + try #require(HTTPCookie(properties: [ + .name: "augment_session", + .value: value, + .domain: "app.augmentcode.com", + .path: "/", + .expires: Date(timeIntervalSince1970: 1_900_000_000), + .secure: true, + ])) + } +} diff --git a/Tests/CodexBarTests/CLICookieRefreshTests.swift b/Tests/CodexBarTests/CLICookieRefreshTests.swift index 606a7cf4c2..933f8d556c 100644 --- a/Tests/CodexBarTests/CLICookieRefreshTests.swift +++ b/Tests/CodexBarTests/CLICookieRefreshTests.swift @@ -310,7 +310,8 @@ struct CLICookieRefreshTests { error: NSError(domain: "opaque-test-marker", code: 1)) #expect(result.message == - "Chrome cookie decryption was declined in Keychain; retry with --allow-keychain-prompt.") + "Chrome cookie decryption was declined in Keychain; " + + "rerun with --allow-keychain-prompt to request Keychain access again.") #expect(!result.message.contains("opaque-test-marker")) } } diff --git a/Tests/CodexBarTests/OllamaUIErrorMapperTests.swift b/Tests/CodexBarTests/OllamaUIErrorMapperTests.swift index bd02b52efc..f8499a5093 100644 --- a/Tests/CodexBarTests/OllamaUIErrorMapperTests.swift +++ b/Tests/CodexBarTests/OllamaUIErrorMapperTests.swift @@ -14,6 +14,11 @@ struct OllamaUIErrorMapperTests { @Test func `maps Brave decryption denial with browser name`() { + #expect( + OllamaUsageError.browserCookieDecryptionDenied("Brave").localizedDescription == + "Brave cookie decryption was declined in Keychain. " + + "Open the provider card and click Refresh (⌘R) to request Keychain access again.") + let message = OllamaUIErrorMapper.userFacingMessage( OllamaUsageError.browserCookieDecryptionDenied("Brave").localizedDescription, localize: { key in