diff --git a/Dockerfile b/Dockerfile index a34fae653..b41f0b864 100644 --- a/Dockerfile +++ b/Dockerfile @@ -110,6 +110,14 @@ RUN sed -i 's/\r$//' /usr/local/bin/docker-entrypoint-core.sh \ # The entrypoint runs as root so it can chown the mounted volume, then execs # gosu to drop to the openhuman user before starting the binary. +# +# CAUTION: because the image default user is root, `docker exec ...` lands +# as root and does NOT run the entrypoint — so running `openhuman-core` that way +# creates a root-owned `config.toml` (the core writes it at mode 0600), which +# uid 10001 then cannot read on the next start. Use +# `docker exec -u openhuman openhuman-core ...` for any CLI poking around. +# The entrypoint heals a workspace already in that state, but prevention is +# cheaper than a restart loop. USER root # Default workspace directory diff --git a/app/src/components/oauth/__tests__/OAuthProviderButton.test.tsx b/app/src/components/oauth/__tests__/OAuthProviderButton.test.tsx index 554a8db0c..d5f9973fb 100644 --- a/app/src/components/oauth/__tests__/OAuthProviderButton.test.tsx +++ b/app/src/components/oauth/__tests__/OAuthProviderButton.test.tsx @@ -67,6 +67,7 @@ describe('OAuthProviderButton', () => { vi.mocked(getDeepLinkAuthState).mockReturnValue({ isProcessing: false, errorMessage: null, + errorMessageKey: null, requiresAppDataReset: false, }); }); @@ -123,6 +124,7 @@ describe('OAuthProviderButton', () => { vi.mocked(getDeepLinkAuthState).mockReturnValue({ isProcessing: true, errorMessage: null, + errorMessageKey: null, requiresAppDataReset: false, }); diff --git a/app/src/lib/i18n/ar.ts b/app/src/lib/i18n/ar.ts index df02e8678..e22e91c39 100644 --- a/app/src/lib/i18n/ar.ts +++ b/app/src/lib/i18n/ar.ts @@ -3205,6 +3205,9 @@ const messages: TranslationMap = { 'welcome.continueLocally': 'المتابعة محليًا', 'welcome.continueLocallyExperimental': 'المتابعة محليًا (تجريبي)', 'welcome.localSessionStarting': 'بدء الجلسة المحلية...', + 'welcome.coreConfigUnreadable': + 'تعذّر على بيئة التشغيل قراءة ملف الإعدادات الخاص بها. قد يكون الملف config.toml مملوكًا لحساب مستخدم آخر، أو غير متاح لعملية التشغيل لسبب آخر. أعد تشغيل بيئة التشغيل، وإن لم يُجدِ ذلك فأصلح ملكية مجلد العمل أو أعد إنشاء وحدة التخزين الخاصة به.', + 'welcome.localSessionErrorFallback': 'تعذّر بدء جلسة محلية.', 'welcome.localSessionDesc': 'يستخدم ملف تعريف محلي غير متصل ويتخطى TinyHumans OAuth.', 'chat.agentChatDesc': 'فتح جلسة محادثة مباشرة مع الوكيل.', 'chat.modelPlaceholder': 'gpt-4o', diff --git a/app/src/lib/i18n/bn.ts b/app/src/lib/i18n/bn.ts index 37d9429ad..a29cd1289 100644 --- a/app/src/lib/i18n/bn.ts +++ b/app/src/lib/i18n/bn.ts @@ -3280,6 +3280,9 @@ const messages: TranslationMap = { 'welcome.continueLocally': 'স্থানীয়ভাবে চালিয়ে যান', 'welcome.continueLocallyExperimental': 'লোকালি চালিয়ে যান (প্রায়োগিক)', 'welcome.localSessionStarting': 'স্থানীয় অধিবেশন শুরু করা হচ্ছে...', + 'welcome.coreConfigUnreadable': + 'রানটাইম তার কনফিগারেশন ফাইল পড়তে পারেনি। config.toml অন্য কোনো ব্যবহারকারী অ্যাকাউন্টের হতে পারে, অথবা অন্য কোনো কারণে রানটাইম প্রক্রিয়ার জন্য দুর্গম হতে পারে। রানটাইম পুনরায় চালু করুন, আর তাতে কাজ না হলে ওয়ার্কস্পেসের মালিকানা ঠিক করুন বা এর ভলিউম নতুন করে তৈরি করুন।', + 'welcome.localSessionErrorFallback': 'স্থানীয় সেশন শুরু করা যায়নি।', 'welcome.localSessionDesc': 'একটি অফলাইন skips__ BR__Ans স্থানীয় প্রোফাইল ব্যবহার করে।', 'chat.agentChatDesc': 'এজেন্টের সাথে সরাসরি চ্যাট সেশন খুলুন।', 'chat.modelPlaceholder': 'gpt-4o', diff --git a/app/src/lib/i18n/de.ts b/app/src/lib/i18n/de.ts index 2da5bad10..8e0ea2587 100644 --- a/app/src/lib/i18n/de.ts +++ b/app/src/lib/i18n/de.ts @@ -3371,6 +3371,9 @@ const messages: TranslationMap = { 'welcome.continueLocally': 'Lokal fortfahren', 'welcome.continueLocallyExperimental': 'Lokal fortfahren (Experimentell)', 'welcome.localSessionStarting': 'Lokal starten Sitzung...', + 'welcome.coreConfigUnreadable': + 'Die Laufzeitumgebung konnte ihre Konfigurationsdatei nicht lesen. config.toml gehört möglicherweise einem anderen Benutzerkonto oder ist für den Laufzeitprozess aus einem anderen Grund nicht zugänglich. Starten Sie die Laufzeitumgebung neu, und reparieren Sie andernfalls die Besitzrechte des Arbeitsverzeichnisses oder erstellen Sie dessen Volume neu.', + 'welcome.localSessionErrorFallback': 'Lokale Sitzung konnte nicht gestartet werden.', 'welcome.localSessionDesc': 'Verwendet ein lokales Offline-Profil und überspringt TinyHumans OAuth.', 'chat.agentChatDesc': 'Öffne eine direkte Chat-Sitzung mit dem Agenten.', diff --git a/app/src/lib/i18n/en.ts b/app/src/lib/i18n/en.ts index ac4420545..18a98459f 100644 --- a/app/src/lib/i18n/en.ts +++ b/app/src/lib/i18n/en.ts @@ -3533,6 +3533,9 @@ const en: TranslationMap = { 'welcome.continueLocally': 'Continue locally', 'welcome.continueLocallyExperimental': 'Continue Locally (Experimental)', 'welcome.localSessionStarting': 'Starting local session...', + 'welcome.coreConfigUnreadable': + 'The runtime could not read its configuration file. config.toml may belong to a different user account, or be inaccessible to the runtime process for another reason. Restart the runtime, and if that does not help, repair the workspace ownership or re-create its volume.', + 'welcome.localSessionErrorFallback': 'Could not start a local session.', 'welcome.localSessionDesc': 'Uses an offline local profile and skips TinyHumans OAuth.', // Chat: Agent chat panel description diff --git a/app/src/lib/i18n/es.ts b/app/src/lib/i18n/es.ts index 123e57fa1..d7579fc06 100644 --- a/app/src/lib/i18n/es.ts +++ b/app/src/lib/i18n/es.ts @@ -3341,6 +3341,9 @@ const messages: TranslationMap = { 'welcome.continueLocally': 'Continuar localmente', 'welcome.continueLocallyExperimental': 'Continuar localmente (Experimental)', 'welcome.localSessionStarting': 'Iniciando sesión local...', + 'welcome.coreConfigUnreadable': + 'El entorno de ejecución no pudo leer su archivo de configuración. Es posible que config.toml pertenezca a otra cuenta de usuario o que resulte inaccesible para el proceso por otro motivo. Reinicia el entorno de ejecución y, si eso no ayuda, repara la propiedad del espacio de trabajo o vuelve a crear su volumen.', + 'welcome.localSessionErrorFallback': 'No se pudo iniciar una sesión local.', 'welcome.localSessionDesc': 'Utiliza un perfil local sin conexión y omite TinyHumans OAuth.', 'chat.agentChatDesc': 'Abre una sesión de chat directo con el agente.', 'chat.modelPlaceholder': 'gpt-4o', diff --git a/app/src/lib/i18n/fr.ts b/app/src/lib/i18n/fr.ts index e25e05c02..622f66058 100644 --- a/app/src/lib/i18n/fr.ts +++ b/app/src/lib/i18n/fr.ts @@ -3365,6 +3365,9 @@ const messages: TranslationMap = { 'welcome.continueLocally': 'Continuer localement', 'welcome.continueLocallyExperimental': 'Continuer en local (Expérimental)', 'welcome.localSessionStarting': 'Démarrage de la session locale...', + 'welcome.coreConfigUnreadable': + "L'environnement d'exécution n'a pas pu lire son fichier de configuration. config.toml appartient peut-être à un autre compte utilisateur, ou reste inaccessible au processus pour une autre raison. Redémarrez l'environnement d'exécution puis, si cela ne suffit pas, corrigez les droits du répertoire de travail ou recréez son volume.", + 'welcome.localSessionErrorFallback': 'Impossible de démarrer une session locale.', 'welcome.localSessionDesc': 'Utilise un profil local hors ligne et ignore TinyHumans OAuth.', 'chat.agentChatDesc': "Ouvrir une session de chat direct avec l'agent.", 'chat.modelPlaceholder': 'gpt-4o', diff --git a/app/src/lib/i18n/hi.ts b/app/src/lib/i18n/hi.ts index f4bc2491d..cdd10b790 100644 --- a/app/src/lib/i18n/hi.ts +++ b/app/src/lib/i18n/hi.ts @@ -3276,6 +3276,9 @@ const messages: TranslationMap = { 'welcome.continueLocally': 'स्थानीय स्तर पर जारी रखें', 'welcome.continueLocallyExperimental': 'लोकल रूप से जारी रखें (प्रायोगिक)', 'welcome.localSessionStarting': 'स्थानीय सत्र प्रारंभ हो रहा है...', + 'welcome.coreConfigUnreadable': + 'रनटाइम अपनी कॉन्फ़िगरेशन फ़ाइल नहीं पढ़ सका। हो सकता है config.toml किसी दूसरे उपयोगकर्ता खाते का हो, या किसी अन्य कारण से रनटाइम प्रक्रिया के लिए दुर्गम हो। रनटाइम को पुनः आरंभ करें, और यदि इससे मदद न मिले तो वर्कस्पेस का स्वामित्व ठीक करें या उसका वॉल्यूम दोबारा बनाएँ।', + 'welcome.localSessionErrorFallback': 'स्थानीय सत्र शुरू नहीं किया जा सका।', 'welcome.localSessionDesc': 'ऑफ़लाइन स्थानीय प्रोफ़ाइल का उपयोग करता है और TinyHumans OAuth को छोड़ देता है।', 'chat.agentChatDesc': 'एजेंट के साथ डायरेक्ट चैट सेशन खोलें।', diff --git a/app/src/lib/i18n/id.ts b/app/src/lib/i18n/id.ts index e765f9bfe..8ca438427 100644 --- a/app/src/lib/i18n/id.ts +++ b/app/src/lib/i18n/id.ts @@ -3290,6 +3290,9 @@ const messages: TranslationMap = { 'welcome.continueLocally': 'Lanjutkan secara lokal', 'welcome.continueLocallyExperimental': 'Lanjutkan Secara Lokal (Eksperimental)', 'welcome.localSessionStarting': 'Memulai sesi lokal...', + 'welcome.coreConfigUnreadable': + 'Runtime tidak dapat membaca berkas konfigurasinya. config.toml mungkin milik akun pengguna lain, atau tidak dapat diakses oleh proses runtime karena alasan lain. Mulai ulang runtime, dan jika belum teratasi, perbaiki kepemilikan direktori kerja atau buat ulang volumenya.', + 'welcome.localSessionErrorFallback': 'Tidak dapat memulai sesi lokal.', 'welcome.localSessionDesc': 'Menggunakan profil lokal offline dan melewati TinyHumans OAuth.', 'chat.agentChatDesc': 'Buka sesi chat langsung dengan agen.', 'chat.modelPlaceholder': 'gpt-4o', diff --git a/app/src/lib/i18n/it.ts b/app/src/lib/i18n/it.ts index a4274d990..79161cede 100644 --- a/app/src/lib/i18n/it.ts +++ b/app/src/lib/i18n/it.ts @@ -3339,6 +3339,9 @@ const messages: TranslationMap = { 'welcome.continueLocally': 'Continua localmente', 'welcome.continueLocallyExperimental': 'Continua Localmente (Sperimentale)', 'welcome.localSessionStarting': 'Avvio sessione locale...', + 'welcome.coreConfigUnreadable': + 'Il runtime non è riuscito a leggere il proprio file di configurazione. config.toml potrebbe appartenere a un altro account utente oppure essere inaccessibile al processo per un altro motivo. Riavvia il runtime e, se non basta, correggi la proprietà della cartella di lavoro o ricrea il suo volume.', + 'welcome.localSessionErrorFallback': 'Impossibile avviare una sessione locale.', 'welcome.localSessionDesc': 'Utilizza un profilo locale offline e salta TinyHumans OAuth.', 'chat.agentChatDesc': "Apri una sessione di chat diretta con l'agente.", 'chat.modelPlaceholder': 'gpt-4o', diff --git a/app/src/lib/i18n/ko.ts b/app/src/lib/i18n/ko.ts index 387adefd6..638702741 100644 --- a/app/src/lib/i18n/ko.ts +++ b/app/src/lib/i18n/ko.ts @@ -3243,6 +3243,9 @@ const messages: TranslationMap = { 'welcome.continueLocally': '로컬에서 계속', 'welcome.continueLocallyExperimental': '로컬에서 계속(실험적)', 'welcome.localSessionStarting': '로컬 세션 시작 중...', + 'welcome.coreConfigUnreadable': + '런타임이 자체 구성 파일을 읽지 못했습니다. config.toml이 다른 사용자 계정 소유이거나 다른 이유로 런타임 프로세스가 접근할 수 없는 상태일 수 있습니다. 런타임을 다시 시작하고, 그래도 해결되지 않으면 작업 디렉터리의 소유권을 복구하거나 볼륨을 다시 만드세요.', + 'welcome.localSessionErrorFallback': '로컬 세션을 시작할 수 없습니다.', 'welcome.localSessionDesc': '오프라인 로컬 프로필을 사용하고 TinyHumans를 건너뜁니다. OAuth.', 'chat.agentChatDesc': '에이전트와 직접 채팅 세션을 엽니다.', 'chat.modelPlaceholder': 'gpt-4o', diff --git a/app/src/lib/i18n/pl.ts b/app/src/lib/i18n/pl.ts index 3e0cc45bb..6b4b519bf 100644 --- a/app/src/lib/i18n/pl.ts +++ b/app/src/lib/i18n/pl.ts @@ -3316,6 +3316,9 @@ const messages: TranslationMap = { 'welcome.continueLocally': 'Kontynuuj lokalnie', 'welcome.continueLocallyExperimental': 'Kontynuuj lokalnie (Eksperymentalne)', 'welcome.localSessionStarting': 'Rozpoczynanie sesji lokalnej...', + 'welcome.coreConfigUnreadable': + 'Środowisko uruchomieniowe nie mogło odczytać swojego pliku konfiguracyjnego. Plik config.toml może należeć do innego konta użytkownika lub być niedostępny dla procesu z innego powodu. Uruchom ponownie środowisko, a jeśli to nie pomoże, napraw właściciela katalogu roboczego lub utwórz jego wolumin od nowa.', + 'welcome.localSessionErrorFallback': 'Nie udało się rozpocząć sesji lokalnej.', 'welcome.localSessionDesc': 'Używa lokalnego profilu offline i pomija logowanie OAuth TinyHumans.', 'chat.agentChatDesc': 'Otwórz bezpośrednią sesję czatu z agentem.', diff --git a/app/src/lib/i18n/pt.ts b/app/src/lib/i18n/pt.ts index 783b0ba32..6e110468f 100644 --- a/app/src/lib/i18n/pt.ts +++ b/app/src/lib/i18n/pt.ts @@ -3333,6 +3333,9 @@ const messages: TranslationMap = { 'welcome.continueLocally': 'Continuar localmente', 'welcome.continueLocallyExperimental': 'Continuar Localmente (Experimental)', 'welcome.localSessionStarting': 'Iniciando sessão local...', + 'welcome.coreConfigUnreadable': + 'O runtime não conseguiu ler o seu ficheiro de configuração. O config.toml pode pertencer a outra conta de utilizador ou estar inacessível ao processo por outro motivo. Reinicie o runtime e, se isso não resolver, corrija a propriedade da pasta de trabalho ou recrie o seu volume.', + 'welcome.localSessionErrorFallback': 'Não foi possível iniciar uma sessão local.', 'welcome.localSessionDesc': 'Usa um perfil local offline e ignora TinyHumans OAuth.', 'chat.agentChatDesc': 'Abrir uma sessão de chat direto com o agente.', 'chat.modelPlaceholder': 'gpt-4o', diff --git a/app/src/lib/i18n/ru.ts b/app/src/lib/i18n/ru.ts index be1680cc7..e05673276 100644 --- a/app/src/lib/i18n/ru.ts +++ b/app/src/lib/i18n/ru.ts @@ -3305,6 +3305,9 @@ const messages: TranslationMap = { 'welcome.continueLocally': 'Продолжить локально', 'welcome.continueLocallyExperimental': 'Продолжить локально (Экспериментально)', 'welcome.localSessionStarting': 'Запуск локального сеанса...', + 'welcome.coreConfigUnreadable': + 'Среде выполнения не удалось прочитать свой файл конфигурации. Возможно, config.toml принадлежит другой учётной записи или недоступен процессу по иной причине. Перезапустите среду выполнения, а если это не поможет, исправьте владельца рабочего каталога или создайте его том заново.', + 'welcome.localSessionErrorFallback': 'Не удалось запустить локальный сеанс.', 'welcome.localSessionDesc': 'Использует автономный локальный профиль и пропускает TinyHumans OAuth.', 'chat.agentChatDesc': 'Открыть прямой чат с агентом.', diff --git a/app/src/lib/i18n/zh-CN.ts b/app/src/lib/i18n/zh-CN.ts index ce08b5017..dce9338c5 100644 --- a/app/src/lib/i18n/zh-CN.ts +++ b/app/src/lib/i18n/zh-CN.ts @@ -3105,6 +3105,9 @@ const messages: TranslationMap = { 'welcome.continueLocally': '本地继续', 'welcome.continueLocallyExperimental': '本地继续(实验性)', 'welcome.localSessionStarting': '正在启动本地会话...', + 'welcome.coreConfigUnreadable': + '运行时无法读取自己的配置文件。config.toml 可能归属于其他用户账户,或因其他原因无法被运行时进程访问。请重启运行时;若仍未解决,请修复工作目录的归属权或重新创建其数据卷。', + 'welcome.localSessionErrorFallback': '无法启动本地会话。', 'welcome.localSessionDesc': '使用离线本地配置文件,跳过 TinyHumans OAuth。', 'chat.agentChatDesc': '与智能体进行直接对话。', 'chat.modelPlaceholder': 'gpt-4o', diff --git a/app/src/pages/Welcome.tsx b/app/src/pages/Welcome.tsx index e7a56e25c..6eb80749f 100644 --- a/app/src/pages/Welcome.tsx +++ b/app/src/pages/Welcome.tsx @@ -15,6 +15,10 @@ import { useAppDispatch, useAppSelector } from '../store/hooks'; import { resolveTheme, setThemeMode, type ThemeMode } from '../store/themeSlice'; import { clearAllAppData } from '../utils/clearAllAppData'; import { clearStoredCoreMode, clearStoredCoreToken, storeRpcUrl } from '../utils/configPersistence'; +import { + CORE_CONFIG_UNREADABLE_I18N_KEY, + isCoreConfigUnreadableError, +} from '../utils/coreConfigFailure'; import { PRIVACY_POLICY_URL, TERMS_OF_USE_URL } from '../utils/links'; import { createLocalSessionToken, LOCAL_SESSION_USER } from '../utils/localSession'; import { openUrl } from '../utils/openUrl'; @@ -26,7 +30,12 @@ const Welcome = () => { const navigate = useNavigate(); const dispatch = useAppDispatch(); const { storeSessionToken } = useCoreState(); - const { isProcessing, errorMessage, requiresAppDataReset } = useDeepLinkAuthState(); + const { isProcessing, errorMessage, errorMessageKey, requiresAppDataReset } = + useDeepLinkAuthState(); + // Deep-link auth runs outside React and cannot translate its own copy, so it + // hands over a key for the failures whose copy is localized. Everything else + // still carries a literal message. + const deepLinkError = errorMessageKey ? t(errorMessageKey) : errorMessage; const themeMode = useAppSelector(state => state.theme?.mode ?? 'system') as ThemeMode; const resolvedTheme = resolveTheme(themeMode); const isDark = resolvedTheme === 'dark'; @@ -72,7 +81,14 @@ const Welcome = () => { } catch (err) { const message = err instanceof Error ? err.message : String(err); log('[welcome] local session login failed: %s', message); - setLocalLoginError(message || 'Could not start a local session.'); + // A config-read denial is permanent and host-side; showing the raw + // anyhow chain (absolute path + `os error 13`) gives the user nothing to + // act on. Unrecognized failures keep their original message. + setLocalLoginError( + isCoreConfigUnreadableError(message) + ? t(CORE_CONFIG_UNREADABLE_I18N_KEY) + : message || t('welcome.localSessionErrorFallback') + ); setIsLocalSigningIn(false); } }; @@ -140,11 +156,11 @@ const Welcome = () => { {t('welcome.subtitle')}

- {errorMessage ? ( + {deepLinkError ? (
-

{errorMessage}

+

{deepLinkError}

{requiresAppDataReset ? (