import { useState, Suspense } from "react"; import { useAtomValue, useAtomSet, Result } from "@effect-atom/atom-react"; import { secretsAtom, setSecret, removeSecret } from "../api/atoms"; import { secretWriteKeys } from "../api/reactivity-keys"; import type { SecretProviderPlugin } from "../plugins/secret-provider-plugin"; import { SecretId } from "@executor/sdk"; import { useScope } from "../hooks/use-scope"; import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogDescription, DialogFooter, DialogClose, } from "../components/dialog"; import { Button } from "../components/button"; import { Input } from "../components/input"; import { Label } from "../components/label"; import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger, } from "../components/dropdown-menu"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from "../components/select"; import { CardStack, CardStackContent, CardStackEntry, CardStackEntryActions, CardStackEntryContent, CardStackEntryDescription, CardStackEntryTitle, CardStackHeader, } from "../components/card-stack"; import { Badge } from "../components/badge"; type SecretStorageOption = { readonly label: string; readonly value: string; }; const defaultStorageOptions: readonly SecretStorageOption[] = [ { value: "auto", label: "Auto" }, { value: "keychain", label: "Keychain" }, { value: "file", label: "File" }, ]; // --------------------------------------------------------------------------- // Add secret dialog // --------------------------------------------------------------------------- function AddSecretDialog(props: { open: boolean; onOpenChange: (v: boolean) => void; description: string; storageOptions: readonly SecretStorageOption[]; }) { const initialProvider = props.storageOptions[0]?.value ?? "auto"; const [id, setId] = useState(""); const [name, setName] = useState(""); const [value, setValue] = useState(""); const [provider, setProvider] = useState(initialProvider); const [saving, setSaving] = useState(false); const [error, setError] = useState(null); const scopeId = useScope(); const doSet = useAtomSet(setSecret, { mode: "promise" }); const reset = () => { setId(""); setName(""); setValue(""); setProvider(initialProvider); setError(null); setSaving(false); }; const handleSave = async () => { if (!id.trim() || !name.trim() || !value.trim()) return; setSaving(true); setError(null); try { await doSet({ path: { scopeId }, payload: { id: SecretId.make(id.trim()), name: name.trim(), value: value.trim(), provider: provider === "auto" ? undefined : provider, }, reactivityKeys: secretWriteKeys, }); reset(); props.onOpenChange(false); } catch (e) { setError(e instanceof Error ? e.message : "Failed to save secret"); setSaving(false); } }; return ( { if (!v) reset(); props.onOpenChange(v); }} > New secret {props.description}
setId((e.target as HTMLInputElement).value)} className="font-mono text-xs h-9" />
setName((e.target as HTMLInputElement).value)} className="text-sm h-9" />
setValue((e.target as HTMLInputElement).value)} className="font-mono text-xs h-9" />
{props.storageOptions.length > 1 && (
)}
{error && (

{error}

)}
); } // --------------------------------------------------------------------------- // Secret row // --------------------------------------------------------------------------- function SecretRow(props: { showProvider: boolean; secret: { id: string; name: string; provider?: string }; onRemove: () => void; }) { const { secret, showProvider } = props; return ( {secret.name} {showProvider && secret.provider && {secret.provider}} Remove secret ); } // --------------------------------------------------------------------------- // Page // --------------------------------------------------------------------------- export function SecretsPage(props: { addSecretDescription?: string; showProviderInfo?: boolean; secretProviderPlugins: readonly SecretProviderPlugin[]; storageOptions?: readonly SecretStorageOption[]; }) { const storageOptions = props.storageOptions ?? defaultStorageOptions; const showProviderInfo = props.showProviderInfo ?? true; const addSecretDescription = props.addSecretDescription ?? "Store a credential or API key. Values are kept in your system keychain when available, with a local encrypted file fallback."; const { secretProviderPlugins } = props; const [addOpen, setAddOpen] = useState(false); const scopeId = useScope(); const secrets = useAtomValue(secretsAtom(scopeId)); const doRemove = useAtomSet(removeSecret, { mode: "promise" }); const handleRemove = async (secretId: string) => { try { await doRemove({ path: { scopeId, secretId: SecretId.make(secretId), }, reactivityKeys: secretWriteKeys, }); } catch { // TODO: toast } }; return (
{/* Header */}

Secrets

Credentials and API keys used by your connected sources.

{/* Provider plugins */} {showProviderInfo && secretProviderPlugins.length > 0 && (
Providers {secretProviderPlugins.map((plugin) => (
} >
))}
)} {/* Secrets list */} {Result.match(secrets, { onInitial: () => (

Loading secrets…

), onFailure: () => (

Failed to load secrets

), onSuccess: ({ value }) => ( Secrets {value.length === 0 ? ( Add API keys and credentials to authenticate your sources. ) : ( value.map((s) => ( handleRemove(s.id)} /> )) )} ), })}
); }