import { createFileRoute } from "@tanstack/react-router"; import { useState } from "react"; import { createAgentIdentity, createAgentToken, deleteAgentIdentity, listAgentIdentities } from "~/fns/agents"; import { useAction } from "~/hooks/use-action"; import { Button } from "~/ui/Button"; import { CheckboxField, SelectField, TextAreaField, TextField } from "~/ui/Field"; import { ErrorBanner } from "~/ui/Callout"; import { EmptyState } from "~/ui/EmptyState"; import { Panel } from "~/ui/Panel"; import { RelativeTime } from "~/ui/RelativeTime"; import { SecretReveal } from "~/ui/Callout"; const AREAS = [ { id: "contents", label: "Contents" }, { id: "issues", label: "Issues" }, { id: "pulls", label: "Pull requests" }, { id: "actions", label: "Actions" }, { id: "admin", label: "Administration" }, ] as const; const ACCESS = [ { value: "", label: "No access" }, { value: "read", label: "Read-only" }, { value: "write", label: "Read and write" }, ] as const; const EXPIRY = [ { value: "7", label: "7 days" }, { value: "30", label: "30 days" }, { value: "90", label: "90 days" }, { value: "366", label: "1 year" }, ] as const; export const Route = createFileRoute("/settings/agents")({ loader: () => listAgentIdentities(), component: AgentsPage, }); function accessValue(value: FormDataEntryValue | null): "read" | "write" | undefined { return value === "read" || value === "write" ? value : undefined; } function AgentTokenForm({ agentId, onCreated }: { agentId: string; onCreated: (token: string) => void }) { const mint = useAction(createAgentToken); const [allRepos, setAllRepos] = useState(false); const [forgeAccess, setForgeAccess] = useState(false); return (
{ event.preventDefault(); const form = new FormData(event.currentTarget); const repos = String(form.get("repos") ?? "") .split(/[\s,]+/) .filter(Boolean); const result = await mint.run({ data: { id: agentId, repos: allRepos ? [] : repos, allRepos, permissions: Object.fromEntries(AREAS.map((area) => [area.id, accessValue(form.get(area.id))])), expiresInDays: Number(form.get("expires") ?? "30"), forgeAccess, }, }); if (result) onCreated(result.token); }} > setAllRepos(event.currentTarget.checked)} /> {!allRepos && ( )}
{AREAS.map((area) => ( ))}
setForgeAccess(event.currentTarget.checked)} /> ); } function AgentsPage() { const agents = Route.useLoaderData(); const create = useAction(createAgentIdentity); const remove = useAction(deleteAgentIdentity); const [created, setCreated] = useState(null); const [minting, setMinting] = useState(null); return (
{created && }
{ event.preventDefault(); const formElement = event.currentTarget; const form = new FormData(formElement); const result = await create.run({ data: { name: String(form.get("name") ?? ""), description: String(form.get("description") ?? "") }, }); if (result) formElement.reset(); }} >

Commits, pull requests and audit entries made with an agent's tokens name the agent and you. Tokens that aren't bound to an agent are shown as an unregistered agent acting for you.

{agents.length === 0 ? ( ) : (
    {agents.map((agent) => (
  • {agent.name}
    {agent.description &&
    {agent.description}
    }
    {agent.tokens} active token{agent.tokens === 1 ? "" : "s"} ยท registered{" "}
    {minting === agent.id && ( { setCreated(token); setMinting(null); }} /> )}
  • ))}
)}
); }