// — the key the chosen runtime runs on, set from here instead // of in the bao UI (`GET /api/review-classifier/keys`, `PUT .../keys/{runtime}`). // The page can say whether a key is stored and when it changed, never what it // is: the field takes a new key and is cleared once it's saved. A key is // saved apart from the settings, because it goes to the secret store and not // into the settings document. import { 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 { ActionRow } from "../../ui/action-row/ActionRow.js"; import { Button } from "../../ui/button/Button.js"; import { Hint } from "../../ui/hint/Hint.js"; import { TextField } from "../../ui/text-field/TextField.js"; import { keyOf, keyProblem, keyStatus, type KeysBody } from "./KeyTypes.js"; import type { Runtime } from "./SettingsTypes.js"; const KEYS = "/api/review-classifier/keys"; export function ClassifierKey({ runtime }: { runtime: Runtime }) { const [keys, setKeys] = useState(null); const [loadError, setLoadError] = useState(null); const [saveError, setSaveError] = useState(null); // The key as typed. Never kept after a save. const [text, setText] = useState(""); const [saving, setSaving] = useState(false); const [justSaved, setJustSaved] = useState(false); useEffect(() => { let cancelled = false; void (async () => { try { const res = await fetch(KEYS); if (cancelled) return; if (!res.ok) { setLoadError(await readApiError(res)); return; } const body = (await res.json()) as KeysBody; if (!cancelled) setKeys(body); } catch (e: unknown) { if (!cancelled) setLoadError({ detail: String(e) }); } })(); return () => { cancelled = true; }; }, []); // A key typed for one runtime must not be saved to the other. useEffect(() => { setText(""); setSaveError(null); setJustSaved(false); }, [runtime]); const problem = keyProblem(text); async function save(e: Event) { e.preventDefault(); if (problem !== null) return; setSaving(true); setSaveError(null); setJustSaved(false); try { const res = await fetch(`${KEYS}/${runtime}`, { method: "PUT", headers: { "content-type": "application/json" }, body: JSON.stringify({ api_key: text.trim() }), }); if (!res.ok) { setSaveError(await readApiError(res)); return; } setKeys((await res.json()) as KeysBody); setText(""); setJustSaved(true); } catch (err: unknown) { setSaveError({ detail: String(err) }); } finally { setSaving(false); } } const label = runtime === "acp" ? "provider key" : "Anthropic API key"; return (
void save(e)}> {loadError ? ( ) : null} { setText(t); setJustSaved(false); }} /> {saveError ? ( ) : null} {justSaved ? ( Saved. The next run uses it. ) : null} ); }