From 835993760f84b31c2e1da46e178a6af31ad170cc Mon Sep 17 00:00:00 2001 From: Steve Korshakov Date: Sun, 8 Nov 2026 19:58:33 -0800 Subject: [PATCH 1/2] android: Claude on Happy Cloud is a setup token only Servers no longer offer Claude's browser sign-in (claudeSignIn is false), so the Claude page is now just the setup-token form. Drop the Open Claude / pasted-code branch, the route's token flag, the claudeSignIn capability, and their strings. The chooser entry reads "Use a Claude setup token" and shows when the server takes one. --- .../com/slopus/happy/agent/CloudProviders.kt | 3 - .../happy/ui/connection/CloudProviders.kt | 118 +++--------------- .../strings_cloud_providers.xml | 8 +- .../strings_cloud_providers.xml | 8 +- .../res/values-ca/strings_cloud_providers.xml | 8 +- .../res/values-es/strings_cloud_providers.xml | 8 +- .../res/values-it/strings_cloud_providers.xml | 8 +- .../res/values-ja/strings_cloud_providers.xml | 8 +- .../res/values-pl/strings_cloud_providers.xml | 8 +- .../res/values-pt/strings_cloud_providers.xml | 8 +- .../res/values-ru/strings_cloud_providers.xml | 8 +- .../res/values/strings_cloud_providers.xml | 8 +- .../slopus/happy/agent/CloudProvidersTest.kt | 4 +- .../ui/connection/CloudProvidersScreenTest.kt | 10 +- 14 files changed, 32 insertions(+), 183 deletions(-) diff --git a/packages/happy-android/app/src/main/kotlin/com/slopus/happy/agent/CloudProviders.kt b/packages/happy-android/app/src/main/kotlin/com/slopus/happy/agent/CloudProviders.kt index 504b4a7d60..86462c6c94 100644 --- a/packages/happy-android/app/src/main/kotlin/com/slopus/happy/agent/CloudProviders.kt +++ b/packages/happy-android/app/src/main/kotlin/com/slopus/happy/agent/CloudProviders.kt @@ -145,7 +145,6 @@ data class CloudCapabilities( val apiKeys: Boolean, val claudeSetupToken: Boolean, val codexSignIn: Boolean, - val claudeSignIn: Boolean, ) { companion object { fun parse(json: JsonObject?): CloudCapabilities? = json?.let { @@ -153,7 +152,6 @@ data class CloudCapabilities( apiKeys = it["apiKeys"].bool == true, claudeSetupToken = it["claudeSetupToken"].bool == true, codexSignIn = it["codexSignIn"].bool == true, - claudeSignIn = it["claudeSignIn"].bool == true, ) } @@ -162,7 +160,6 @@ data class CloudCapabilities( apiKeys = true, claudeSetupToken = false, codexSignIn = CloudProviders.CODEX in signIns, - claudeSignIn = CloudProviders.CLAUDE in signIns, ) } } diff --git a/packages/happy-android/app/src/main/kotlin/com/slopus/happy/ui/connection/CloudProviders.kt b/packages/happy-android/app/src/main/kotlin/com/slopus/happy/ui/connection/CloudProviders.kt index ad86263954..74d9c4102b 100644 --- a/packages/happy-android/app/src/main/kotlin/com/slopus/happy/ui/connection/CloudProviders.kt +++ b/packages/happy-android/app/src/main/kotlin/com/slopus/happy/ui/connection/CloudProviders.kt @@ -14,7 +14,6 @@ import androidx.compose.runtime.DisposableEffect import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.produceState import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.saveable.rememberSaveable @@ -76,7 +75,7 @@ import kotlinx.serialization.Serializable @Serializable data class CloudChatGptRoute(val connectionId: String, val to: String? = null) /** Claude's sign-in, or its setup token ([token]). */ -@Serializable data class CloudClaudeRoute(val connectionId: String, val to: String? = null, val token: Boolean = false) +@Serializable data class CloudClaudeRoute(val connectionId: String, val to: String? = null) /** An OpenAI or Anthropic API key; [vendor] picks the vendor first. */ @Serializable data class CloudApiKeyRoute(val connectionId: String, val vendor: String? = null, val to: String? = null) @@ -115,7 +114,7 @@ fun NavGraphBuilder.cloudProviderRoutes(graph: AppGraph, nav: NavHostController) } page { entry -> val route = entry.toRoute() - CloudClaudePage(graph, nav, route.connectionId, CloudDestination.of(route.to), route.token) + CloudClaudePage(graph, nav, route.connectionId, CloudDestination.of(route.to)) } page { entry -> val route = entry.toRoute() @@ -167,7 +166,7 @@ private fun CloudProvidersPage(graph: AppGraph, nav: NavHostController, connecti choosing = false when (flow) { CloudProviderFlow.ChatGpt -> nav.navigate(CloudChatGptRoute(connectionId, to)) - CloudProviderFlow.Claude -> nav.navigate(CloudClaudeRoute(connectionId, to, token = capabilities?.claudeSignIn != true)) + CloudProviderFlow.Claude -> nav.navigate(CloudClaudeRoute(connectionId, to)) is CloudProviderFlow.ApiKey -> nav.navigate(CloudApiKeyRoute(connectionId, flow.vendor, to)) CloudProviderFlow.Choose -> Unit } @@ -305,9 +304,9 @@ internal fun CloudAddChooser(capabilities: CloudCapabilities?, onPick: (CloudPro onClick = { pick(CloudProviderFlow.ChatGpt) }, ) } - if (capabilities?.claudeSignIn == true || capabilities?.claudeSetupToken == true) { + if (capabilities?.claudeSetupToken == true) { Item( - title = stringResource(if (capabilities.claudeSignIn) R.string.cloud_providers_add_claude else R.string.cloud_providers_add_setup_token), + title = stringResource(R.string.cloud_providers_add_setup_token), icon = { ProviderIcon(ProviderIconKind.Claude, size = 24.dp) }, showChevron = false, onClick = { pick(CloudProviderFlow.Claude) }, @@ -450,114 +449,27 @@ private fun Outcome(title: String, message: String?, onRetry: (() -> Unit)?) { private fun failureTitle(code: String?): String = stringResource(if (code == "expired") R.string.cloud_providers_expired else R.string.cloud_providers_failed) -// ---- Claude (pasted code, or a setup token) -------------------------------------------------- +// ---- Claude (a setup token) -------------------------------------------------------------------- @Composable -private fun CloudClaudePage(graph: AppGraph, nav: NavHostController, connectionId: String, asked: CloudDestination?, startWithToken: Boolean) { +private fun CloudClaudePage(graph: AppGraph, nav: NavHostController, connectionId: String, asked: CloudDestination?) { val session = remember(connectionId) { graph.agentSessions.session(connectionId) } ?: return val allowed = remember(session) { destinations(session) } var destination by rememberSaveable { mutableStateOf(destinationFor(allowed, asked)) } - var token by rememberSaveable { mutableStateOf(startWithToken) } - val capabilities by produceState(null, session) { - value = try { - CloudProviders.capabilities(session.client) - } catch (e: CancellationException) { - throw e - } catch (_: Exception) { - null - } - } - // A link to Claude where only its setup token is offered opens that. - LaunchedEffect(capabilities) { - if (capabilities?.let { !it.claudeSignIn && it.claudeSetupToken } == true) token = true - } - val scope = rememberCoroutineScope() - val context = LocalContext.current - val dark = HappyTheme.colors.dark - if (token) { - CloudSecretFlow( - title = stringResource(R.string.cloud_providers_add_setup_token), - field = stringResource(R.string.cloud_providers_setup_token), - hint = stringResource(R.string.cloud_providers_setup_token_hint), - done = stringResource(R.string.cloud_providers_claude_done), - allowed = allowed, - destination = destination, - onDestination = { destination = it }, - vendors = emptyList(), - add = { _, secret, to -> CloudProviders.addCredential(session.client, CloudProviders.container(to, CloudProviders.CLAUDE_CODE), CloudProviders.CLAUDE_CODE, "oauth_token", secret) }, - nav = nav, - ) - return - } - val run = remember(session, destination) { CloudSignInRun(session.client, scope, CloudProviders.ANTHROPIC, destination).also { it.start() } } - DisposableEffect(run) { onDispose { run.close() } } - val state by run.current.collectAsStateWithLifecycle() - LeaveWhenDone(state == CloudSignInRun.State.Done, nav) - CloudClaudeScreen( - state = state, + CloudSecretFlow( + title = stringResource(R.string.cloud_providers_add_setup_token), + field = stringResource(R.string.cloud_providers_setup_token), + hint = stringResource(R.string.cloud_providers_setup_token_hint), + done = stringResource(R.string.cloud_providers_claude_done), allowed = allowed, destination = destination, onDestination = { destination = it }, - onOpen = { url -> openSignInPage(context, url, dark) }, - onCode = run::sendCode, - onUseToken = { token = true }.takeIf { capabilities?.claudeSetupToken == true }, - onRetry = run::start, - onBack = { nav.popBackStack() }, + vendors = emptyList(), + add = { _, secret, to -> CloudProviders.addCredential(session.client, CloudProviders.container(to, CloudProviders.CLAUDE_CODE), CloudProviders.CLAUDE_CODE, "oauth_token", secret) }, + nav = nav, ) } -/** - * Claude's sign-in: Open Claude (a Custom Tab), then the code its page shows - * pasted here and sent to the server, which exchanges it. Or a setup token - * instead, where the server takes them ([onUseToken]). The code is never - * kept on the phone. - */ -@Composable -internal fun CloudClaudeScreen( - state: CloudSignInRun.State, - allowed: List, - destination: CloudDestination, - onDestination: (CloudDestination) -> Unit, - onOpen: (String) -> Unit, - onCode: (String) -> Unit, - onUseToken: (() -> Unit)?, - onRetry: () -> Unit, - onBack: () -> Unit, -) { - var code by remember { mutableStateOf("") } - Screen(header = { StackHeader(stringResource(R.string.cloud_providers_add_claude), onBack) }) { - ItemList { - SaveTo(allowed, destination, enabled = state !is CloudSignInRun.State.Done, onPick = onDestination) - when (state) { - CloudSignInRun.State.Starting -> ItemGroup { Item(title = stringResource(R.string.cloud_providers_add_claude), loading = true, showChevron = false) } - is CloudSignInRun.State.Waiting -> ItemGroup { - FlowColumn { - Text(stringResource(R.string.cloud_providers_claude_body), style = happyTextStyle(fontSize = 15, lineHeight = 21), color = HappyTheme.colors.textSecondary, textAlign = TextAlign.Center) - state.url?.let { url -> - RoundButton(title = stringResource(R.string.cloud_providers_open_claude), onClick = { onOpen(url) }, modifier = Modifier.testTag(CloudProvidersTags.OPEN)) - } - SecretField(code, { code = it }, stringResource(R.string.cloud_providers_code), enabled = !state.sendingCode, hidden = false) - RoundButton( - title = stringResource(R.string.cloud_providers_connect), - onClick = { onCode(code) }, - enabled = code.isNotBlank(), - loading = state.sendingCode, - modifier = Modifier.testTag(CloudProvidersTags.SUBMIT), - ) - } - } - CloudSignInRun.State.Done -> Outcome(stringResource(R.string.cloud_providers_claude_done), null, null) - is CloudSignInRun.State.Failed -> Outcome(failureTitle(state.code), state.message ?: stringResource(R.string.cloud_failed), onRetry) - } - if (state !is CloudSignInRun.State.Done && onUseToken != null) { - ItemGroup { - Item(title = stringResource(R.string.cloud_providers_use_token), titleColor = HappyTheme.colors.textLink, showChevron = false, onClick = onUseToken) - } - } - } - } -} - @Composable private fun SecretField(value: String, onChange: (String) -> Unit, placeholder: String, enabled: Boolean, hidden: Boolean) { HappyTextField( diff --git a/packages/happy-android/app/src/main/res/values-b+zh+Hans/strings_cloud_providers.xml b/packages/happy-android/app/src/main/res/values-b+zh+Hans/strings_cloud_providers.xml index a5a13b3b44..04fabf2ce4 100644 --- a/packages/happy-android/app/src/main/res/values-b+zh+Hans/strings_cloud_providers.xml +++ b/packages/happy-android/app/src/main/res/values-b+zh+Hans/strings_cloud_providers.xml @@ -10,18 +10,12 @@ 尚未添加任何内容 添加 使用 ChatGPT 登录 - 使用 Claude 登录 - 添加 Claude 设置令牌 + 使用 Claude 设置令牌 添加 API 密钥 保存到 在 OpenAI 输入此代码 打开 OpenAI 正在等待你完成… - 在浏览器中登录 Claude,然后粘贴显示的代码。 - 打开 Claude - 代码 - 连接 - 改用设置令牌 设置令牌 在你的电脑上运行 `claude setup-token` 获取。 API 密钥 diff --git a/packages/happy-android/app/src/main/res/values-b+zh+Hant/strings_cloud_providers.xml b/packages/happy-android/app/src/main/res/values-b+zh+Hant/strings_cloud_providers.xml index b13f4f19bd..289e0a047b 100644 --- a/packages/happy-android/app/src/main/res/values-b+zh+Hant/strings_cloud_providers.xml +++ b/packages/happy-android/app/src/main/res/values-b+zh+Hant/strings_cloud_providers.xml @@ -10,18 +10,12 @@ 尚未新增任何內容 新增 使用 ChatGPT 登入 - 使用 Claude 登入 - 新增 Claude 設定權杖 + 使用 Claude 設定權杖 新增 API 金鑰 儲存到 在 OpenAI 輸入此代碼 開啟 OpenAI 正在等待你完成… - 在瀏覽器中登入 Claude,然後貼上顯示的代碼。 - 開啟 Claude - 代碼 - 連線 - 改用設定權杖 設定權杖 在你的電腦上執行 `claude setup-token` 取得。 API 金鑰 diff --git a/packages/happy-android/app/src/main/res/values-ca/strings_cloud_providers.xml b/packages/happy-android/app/src/main/res/values-ca/strings_cloud_providers.xml index dc0f78143a..bfc8cf7e8e 100644 --- a/packages/happy-android/app/src/main/res/values-ca/strings_cloud_providers.xml +++ b/packages/happy-android/app/src/main/res/values-ca/strings_cloud_providers.xml @@ -10,18 +10,12 @@ Encara no hi ha res Afegeix Inicia sessió amb ChatGPT - Inicia sessió amb Claude - Afegeix un token de Claude + Fes servir un token de configuració de Claude Afegeix una clau d\'API Desa a Introdueix aquest codi a OpenAI Obre OpenAI Esperant que acabis… - Inicia sessió a Claude al navegador i enganxa el codi que mostra. - Obre Claude - Codi - Connecta - Fes servir un token de configuració Token de configuració De `claude setup-token` al teu ordinador. Clau d\'API diff --git a/packages/happy-android/app/src/main/res/values-es/strings_cloud_providers.xml b/packages/happy-android/app/src/main/res/values-es/strings_cloud_providers.xml index e897b3a603..8b54240a97 100644 --- a/packages/happy-android/app/src/main/res/values-es/strings_cloud_providers.xml +++ b/packages/happy-android/app/src/main/res/values-es/strings_cloud_providers.xml @@ -10,18 +10,12 @@ Aún no hay nada Añadir Iniciar sesión con ChatGPT - Iniciar sesión con Claude - Añadir token de Claude + Usar un token de configuración de Claude Añadir clave de API Guardar en Introduce este código en OpenAI Abrir OpenAI Esperando a que termines… - Inicia sesión en Claude en el navegador y pega el código que aparece. - Abrir Claude - Código - Conectar - Usar un token de configuración Token de configuración De `claude setup-token` en tu ordenador. Clave de API diff --git a/packages/happy-android/app/src/main/res/values-it/strings_cloud_providers.xml b/packages/happy-android/app/src/main/res/values-it/strings_cloud_providers.xml index e47a9d6888..035b2c31ae 100644 --- a/packages/happy-android/app/src/main/res/values-it/strings_cloud_providers.xml +++ b/packages/happy-android/app/src/main/res/values-it/strings_cloud_providers.xml @@ -10,18 +10,12 @@ Ancora nulla Aggiungi Accedi con ChatGPT - Accedi con Claude - Aggiungi token di Claude + Usa un token di configurazione di Claude Aggiungi chiave API Salva in Inserisci questo codice su OpenAI Apri OpenAI In attesa che tu finisca… - Accedi a Claude nel browser, poi incolla il codice mostrato. - Apri Claude - Codice - Collega - Usa un token di configurazione Token di configurazione Da `claude setup-token` sul tuo computer. Chiave API diff --git a/packages/happy-android/app/src/main/res/values-ja/strings_cloud_providers.xml b/packages/happy-android/app/src/main/res/values-ja/strings_cloud_providers.xml index b0e9945c77..18ebca050d 100644 --- a/packages/happy-android/app/src/main/res/values-ja/strings_cloud_providers.xml +++ b/packages/happy-android/app/src/main/res/values-ja/strings_cloud_providers.xml @@ -10,18 +10,12 @@ まだ何も追加されていません 追加 ChatGPT でサインイン - Claude でサインイン - Claude のセットアップトークンを追加 + Claude のセットアップトークンを使う API キーを追加 保存先 このコードを OpenAI で入力してください OpenAI を開く 完了を待っています… - ブラウザで Claude にサインインし、表示されたコードを貼り付けてください。 - Claude を開く - コード - 接続 - セットアップトークンを使う セットアップトークン コンピュータで `claude setup-token` を実行して取得します。 API キー diff --git a/packages/happy-android/app/src/main/res/values-pl/strings_cloud_providers.xml b/packages/happy-android/app/src/main/res/values-pl/strings_cloud_providers.xml index c0db5ece53..3dd0d65cf1 100644 --- a/packages/happy-android/app/src/main/res/values-pl/strings_cloud_providers.xml +++ b/packages/happy-android/app/src/main/res/values-pl/strings_cloud_providers.xml @@ -10,18 +10,12 @@ Nic jeszcze nie dodano Dodaj Zaloguj się przez ChatGPT - Zaloguj się przez Claude - Dodaj token Claude + Użyj tokenu konfiguracji Claude Dodaj klucz API Zapisz w Wpisz ten kod na stronie OpenAI Otwórz OpenAI Czekamy, aż skończysz… - Zaloguj się do Claude w przeglądarce, a potem wklej pokazany kod. - Otwórz Claude - Kod - Połącz - Użyj tokenu konfiguracji Token konfiguracji Z `claude setup-token` na Twoim komputerze. Klucz API diff --git a/packages/happy-android/app/src/main/res/values-pt/strings_cloud_providers.xml b/packages/happy-android/app/src/main/res/values-pt/strings_cloud_providers.xml index 2f6de7ed75..b114ff222a 100644 --- a/packages/happy-android/app/src/main/res/values-pt/strings_cloud_providers.xml +++ b/packages/happy-android/app/src/main/res/values-pt/strings_cloud_providers.xml @@ -10,18 +10,12 @@ Nada adicionado ainda Adicionar Entrar com ChatGPT - Entrar com Claude - Adicionar token do Claude + Usar um token de configuração do Claude Adicionar chave de API Salvar em Digite este código na OpenAI Abrir OpenAI Aguardando você terminar… - Entre no Claude no navegador e cole o código exibido. - Abrir Claude - Código - Conectar - Usar um token de configuração Token de configuração De `claude setup-token` no seu computador. Chave de API diff --git a/packages/happy-android/app/src/main/res/values-ru/strings_cloud_providers.xml b/packages/happy-android/app/src/main/res/values-ru/strings_cloud_providers.xml index 43b1f31300..f22ad92cce 100644 --- a/packages/happy-android/app/src/main/res/values-ru/strings_cloud_providers.xml +++ b/packages/happy-android/app/src/main/res/values-ru/strings_cloud_providers.xml @@ -10,18 +10,12 @@ Пока ничего не добавлено Добавить Войти через ChatGPT - Войти через Claude - Добавить токен Claude + Использовать токен настройки Claude Добавить API-ключ Сохранить в Введите этот код на сайте OpenAI Открыть OpenAI Ждём, пока вы закончите… - Войдите в Claude в браузере, затем вставьте показанный код. - Открыть Claude - Код - Подключить - Использовать токен настройки Токен настройки Из `claude setup-token` на вашем компьютере. API-ключ diff --git a/packages/happy-android/app/src/main/res/values/strings_cloud_providers.xml b/packages/happy-android/app/src/main/res/values/strings_cloud_providers.xml index 26de85dcbc..98504f4b84 100644 --- a/packages/happy-android/app/src/main/res/values/strings_cloud_providers.xml +++ b/packages/happy-android/app/src/main/res/values/strings_cloud_providers.xml @@ -15,18 +15,12 @@ Nothing added yet Add Sign in with ChatGPT - Sign in with Claude - Add Claude setup token + Use a Claude setup token Add API key Save to Enter this code at OpenAI Open OpenAI Waiting for you to finish… - Sign in to Claude in the browser, then paste the code it shows. - Open Claude - Code - Connect - Use a setup token instead Setup token From `claude setup-token` on your computer. API key diff --git a/packages/happy-android/app/src/test/kotlin/com/slopus/happy/agent/CloudProvidersTest.kt b/packages/happy-android/app/src/test/kotlin/com/slopus/happy/agent/CloudProvidersTest.kt index f3757807fe..d28d0b0e02 100644 --- a/packages/happy-android/app/src/test/kotlin/com/slopus/happy/agent/CloudProvidersTest.kt +++ b/packages/happy-android/app/src/test/kotlin/com/slopus/happy/agent/CloudProvidersTest.kt @@ -86,7 +86,7 @@ class CloudProvidersTest { @Test fun whatMayBeAddedIsTheServersWordElseTheListedSignIns() = runBlocking { val said = AgentClient(Server(mapOf("GET /v0/cloud/providers" to listOf(200 to """{"providers":[],"capabilities":{"apiKeys":true,"claudeSetupToken":true,"codexSignIn":false,"claudeSignIn":true}}"""))), "t") - assertEquals(CloudCapabilities(apiKeys = true, claudeSetupToken = true, codexSignIn = false, claudeSignIn = true), CloudProviders.capabilities(said)) + assertEquals(CloudCapabilities(apiKeys = true, claudeSetupToken = true, codexSignIn = false), CloudProviders.capabilities(said)) val silent = AgentClient( Server( mapOf( @@ -96,7 +96,7 @@ class CloudProvidersTest { ), "t", ) - assertEquals(CloudCapabilities(apiKeys = true, claudeSetupToken = false, codexSignIn = true, claudeSignIn = true), CloudProviders.capabilities(silent)) + assertEquals(CloudCapabilities(apiKeys = true, claudeSetupToken = false, codexSignIn = true), CloudProviders.capabilities(silent)) } @Test diff --git a/packages/happy-android/app/src/test/kotlin/com/slopus/happy/ui/connection/CloudProvidersScreenTest.kt b/packages/happy-android/app/src/test/kotlin/com/slopus/happy/ui/connection/CloudProvidersScreenTest.kt index 25ec6df802..2f0ac58212 100644 --- a/packages/happy-android/app/src/test/kotlin/com/slopus/happy/ui/connection/CloudProvidersScreenTest.kt +++ b/packages/happy-android/app/src/test/kotlin/com/slopus/happy/ui/connection/CloudProvidersScreenTest.kt @@ -67,15 +67,15 @@ class CloudProvidersScreenTest { // Before `capabilities`: the listed sign-ins and keys, never a setup token. rule.setHappyContent { CloudAddChooser(CloudCapabilities.listed(setOf("codex")), onPick = {}, onDismiss = {}) } rule.onNodeWithText("Sign in with ChatGPT").assertIsDisplayed() - assertEquals(0, rule.onAllNodesWithText("Sign in with Claude").fetchSemanticsNodes().size) - assertEquals(0, rule.onAllNodesWithText("Add Claude setup token").fetchSemanticsNodes().size) + assertEquals(0, rule.onAllNodesWithText("Use a Claude setup token").fetchSemanticsNodes().size) rule.onNodeWithText("Add API key").assertIsDisplayed() } @Test - fun aSetupTokenStandsInForClaudesSignInWhereTheServerTakesOne() { - rule.setHappyContent { CloudAddChooser(CloudCapabilities(apiKeys = false, claudeSetupToken = true, codexSignIn = false, claudeSignIn = false), onPick = {}, onDismiss = {}) } - rule.onNodeWithText("Add Claude setup token").assertIsDisplayed() + fun claudeIsOfferedOnlyAsASetupToken() { + rule.setHappyContent { CloudAddChooser(CloudCapabilities(apiKeys = false, claudeSetupToken = true, codexSignIn = false), onPick = {}, onDismiss = {}) } + rule.onNodeWithText("Use a Claude setup token").assertIsDisplayed() + assertEquals(0, rule.onAllNodesWithText("Sign in with Claude").fetchSemanticsNodes().size) assertEquals(0, rule.onAllNodesWithText("Sign in with ChatGPT").fetchSemanticsNodes().size) assertEquals(0, rule.onAllNodesWithText("Add API key").fetchSemanticsNodes().size) } From a1a3a24ae56ddd11615c62c9e4cf753f4488861f Mon Sep 17 00:00:00 2001 From: Steve Korshakov Date: Sun, 8 Nov 2026 19:59:40 -0800 Subject: [PATCH 2/2] android: fix the Claude route's comment --- .../kotlin/com/slopus/happy/ui/connection/CloudProviders.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/happy-android/app/src/main/kotlin/com/slopus/happy/ui/connection/CloudProviders.kt b/packages/happy-android/app/src/main/kotlin/com/slopus/happy/ui/connection/CloudProviders.kt index 74d9c4102b..19e671710c 100644 --- a/packages/happy-android/app/src/main/kotlin/com/slopus/happy/ui/connection/CloudProviders.kt +++ b/packages/happy-android/app/src/main/kotlin/com/slopus/happy/ui/connection/CloudProviders.kt @@ -74,7 +74,7 @@ import kotlinx.serialization.Serializable /** ChatGPT's device sign-in, kept in [to] (a [CloudDestination] id) when it says. */ @Serializable data class CloudChatGptRoute(val connectionId: String, val to: String? = null) -/** Claude's sign-in, or its setup token ([token]). */ +/** Claude's setup token, kept in [to] when it says. */ @Serializable data class CloudClaudeRoute(val connectionId: String, val to: String? = null) /** An OpenAI or Anthropic API key; [vendor] picks the vendor first. */