import { useQuery } from "@tanstack/react-query"; import { MAINTAINER_HEARTBEAT_DEFAULT_INTERVAL_SECONDS, MAINTAINER_HEARTBEAT_MIN_INTERVAL_SECONDS } from "@vtit-agent-coding/shared"; import { useEffect, useState } from "react"; import { toast } from "sonner"; import { useCreateBoardMaintainer, useUpdateBoardMaintainer } from "../hooks/useBoard"; import { api } from "../lib/api"; import { Button } from "./ui/button"; import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle } from "./ui/dialog"; import { Input } from "./ui/input"; import { Label } from "./ui/label"; import { Switch } from "./ui/switch"; interface BoardMaintainer { id: string; agent_id?: string; interval_seconds: number; heartbeat_enabled?: boolean; } interface MaintainerAgent { id: string; name?: string; username?: string; role?: string; } interface BoardMaintainerDialogProps { boardId: string; maintainer?: BoardMaintainer; open: boolean; onOpenChange: (open: boolean) => void; } export function BoardMaintainerDialog({ boardId, maintainer, open, onOpenChange }: BoardMaintainerDialogProps) { const isEditing = !!maintainer; const createMaintainer = useCreateBoardMaintainer(boardId); const updateMaintainer = useUpdateBoardMaintainer(boardId); const agentQuery = useQuery({ queryKey: ["agents", { kind: "worker", maintainer: true }], queryFn: () => api.agents.list({ kind: "worker", maintainer: "true" }), enabled: open && !isEditing, }); const [agentId, setAgentId] = useState(""); const [intervalSeconds, setIntervalSeconds] = useState(String(MAINTAINER_HEARTBEAT_DEFAULT_INTERVAL_SECONDS)); const [heartbeatEnabled, setHeartbeatEnabled] = useState(true); useEffect(() => { if (!open) return; setAgentId(maintainer?.agent_id ?? ""); setIntervalSeconds(String(maintainer?.interval_seconds ?? MAINTAINER_HEARTBEAT_DEFAULT_INTERVAL_SECONDS)); setHeartbeatEnabled(maintainer?.heartbeat_enabled ?? true); }, [open, maintainer?.id]); const agents = (agentQuery.data ?? []) as MaintainerAgent[]; const pending = createMaintainer.isPending || updateMaintainer.isPending; async function save() { const seconds = Number.parseInt(intervalSeconds, 10); if (!Number.isInteger(seconds) || seconds < MAINTAINER_HEARTBEAT_MIN_INTERVAL_SECONDS) { toast.error("Interval must be at least 3600 seconds"); return; } try { if (maintainer) { await updateMaintainer.mutateAsync({ maintainerId: maintainer.id, body: { interval_seconds: seconds, heartbeat_enabled: heartbeatEnabled }, }); toast.success("Maintainer updated"); } else { if (!agentId) { toast.error("Agent is required"); return; } await createMaintainer.mutateAsync({ agent_id: agentId, interval_seconds: seconds, heartbeat_enabled: heartbeatEnabled, }); toast.success("Maintainer created"); } onOpenChange(false); } catch (err: any) { toast.error(err.message ?? "Failed to save maintainer"); } } return ( {isEditing ? "Edit maintainer" : "Add maintainer"} Maintainers run on a schedule and use AK commands to inspect and operate this board.
{!isEditing && (
)}
setIntervalSeconds(event.target.value)} inputMode="numeric" />

Minimum {MAINTAINER_HEARTBEAT_MIN_INTERVAL_SECONDS} seconds.

GitHub events stay active when this is off.

); }