diff --git a/frontend/packages/swarm-ui/src/agent/claude-login/StoredLogin.ts b/frontend/packages/swarm-ui/src/agent/claude-login/StoredLogin.ts index 359dce4aa..052d082d4 100644 --- a/frontend/packages/swarm-ui/src/agent/claude-login/StoredLogin.ts +++ b/frontend/packages/swarm-ui/src/agent/claude-login/StoredLogin.ts @@ -24,6 +24,13 @@ export function storedAtMs(login: StoredLogin): number | null { return Number.isNaN(ms) ? null : ms; } +// The settings row's login button: `log in` when the swarm holds no login for +// the agent, `log in again` when it holds one, so a forced relogin reads as +// what it is. +export function loginButtonLabel(login: StoredLogin): string { + return login.stored_at === null ? "log in" : "log in again"; +} + // What the delete's confirmation says: it removes the swarm's copy, and the // agent reads the store every 5 minutes and logs out when the copy is gone. export function deleteMessage(agent: string): string { diff --git a/frontend/packages/swarm-ui/src/agent/detail/AgentClaudeLogin.tsx b/frontend/packages/swarm-ui/src/agent/detail/AgentClaudeLogin.tsx index b15735b8a..5c66781ae 100644 --- a/frontend/packages/swarm-ui/src/agent/detail/AgentClaudeLogin.tsx +++ b/frontend/packages/swarm-ui/src/agent/detail/AgentClaudeLogin.tsx @@ -17,6 +17,7 @@ import { showToast } from "../../ui/toast/toasts.js"; import { deleteMessage, deleteStoredLoginPath, + loginButtonLabel, storedAtMs, storedLoginPath, type StoredLogin, @@ -81,7 +82,7 @@ export function AgentClaudeLogin({ agent }: { agent: string }) { const loginBadge = ( setLoggingIn(true)} /> diff --git a/frontend/packages/swarm-ui/test/stored-login.test.mjs b/frontend/packages/swarm-ui/test/stored-login.test.mjs index ae872e147..c79d0b1b7 100644 --- a/frontend/packages/swarm-ui/test/stored-login.test.mjs +++ b/frontend/packages/swarm-ui/test/stored-login.test.mjs @@ -6,6 +6,7 @@ import { test } from "node:test"; import { deleteMessage, deleteStoredLoginPath, + loginButtonLabel, storedAtMs, storedLoginPath, } from "../src/agent/claude-login/StoredLogin.ts"; @@ -32,3 +33,11 @@ test("the delete's confirmation names the agent", () => { assert.match(deleteMessage("atlas"), /stored claude login for atlas/); assert.match(deleteMessage("atlas"), /logged out within 5 minutes/); }); + +test("the login button says log in until a login is stored, then log in again", () => { + assert.equal(loginButtonLabel({ stored_at: null }), "log in"); + assert.equal( + loginButtonLabel({ stored_at: "2026-11-10T12:00:00Z" }), + "log in again", + ); +});