Watch
0
0
Fork
You've already forked hyperhive
0

swarm ui: a log in button in the claude login row of an agent's settings

An agent with no stored login, or one to log in again, can be logged in from
the settings panel instead of waiting for its turns to fail with a login needed
badge first. The button opens the same dialog as the card's.
This commit is contained in:
iris 2026-11-15 15:11:41 +01:00 • committed by mara
commit e9ddfd61ec

View file

@ -1,13 +1,15 @@
// <AgentClaudeLogin> — the swarm's stored claude login for one agent: when it
// was written (`GET …/claude-login`, read from the secret store's metadata),
// or that none is, and a delete that removes every version of it
// (`DELETE …/claude-login/stored`) after a confirmation.
// or that none is, a button that logs the agent in (or in again) without
// waiting for its turns to fail first, and a delete that removes every version
// of it (`DELETE …/claude-login/stored`) after a confirmation.
//
// Its state belongs to one agent: callers key it by agent name.
import { useCallback, useEffect, useState } from "preact/hooks";
import { ApiErrorPanel } from "@hive/shared/api-error-panel.js";
import { readApiError, type ProblemDetails } from "@hive/shared/api-error.js";
import { Badge } from "@hive/shared/badge.js";
import { ClaudeLoginDialog } from "../claude-login/ClaudeLoginDialog.js";
import { ConfirmDialog } from "../../ui/confirm-dialog/ConfirmDialog.js";
import { Hint } from "../../ui/hint/Hint.js";
import { RelativeTime } from "../../ui/relative-time/RelativeTime.js";
@ -26,6 +28,7 @@ export function AgentClaudeLogin({ agent }: { agent: string }) {
const [confirming, setConfirming] = useState(false);
const [deleting, setDeleting] = useState(false);
const [deleteError, setDeleteError] = useState<ProblemDetails | null>(null);
const [loggingIn, setLoggingIn] = useState(false);
const load = useCallback(async () => {
try {
@ -75,17 +78,37 @@ export function AgentClaudeLogin({ agent }: { agent: string }) {
return errorPanel ?? <Hint as="span">…</Hint>;
}
const at = storedAtMs(login);
const loginBadge = (
<Badge
variant="quiet"
value={login.stored_at === null ? "log in" : "log in again"}
title={`log ${agent} in to claude from here`}
onClick={() => setLoggingIn(true)}
/>
);
return (
<>
{errorPanel}
{loggingIn ? (
<ClaudeLoginDialog
agent={agent}
onClose={() => {
setLoggingIn(false);
void load();
}}
/>
) : null}
{login.stored_at === null ? (
<Hint as="span">none stored</Hint>
<>
<Hint as="span">none stored</Hint> {loginBadge}
</>
) : (
<>
<span title={login.stored_at}>
stored{" "}
{at === null ? login.stored_at : <RelativeTime epochMs={at} />}
</span>{" "}
{loginBadge}{" "}
<Badge
variant="quiet"
value="delete login"