136 lines · 5.0 KB
import { useState } from "react";
import { Badge } from "~/ui/Badge";
import { EmptyState } from "~/ui/EmptyState";
import { Panel } from "~/ui/Panel";
import { RelativeTime } from "~/ui/RelativeTime";
import { Button } from "~/ui/Button";
import { SelectField, TextField } from "~/ui/Field";
import { ErrorBanner } from "~/ui/Callout";
import type { RunnerSummary } from "~/server/ci/runner-registration";
import { Callout } from "~/ui/Callout";

export type RunnerIsolation = "container" | "none";
type Minted = { token: string; expiresAt: number };
type MintInput = { labels: string[]; isolation: RunnerIsolation; scope: string };

/** Mints a single-use registration token; the admin, not the runner, declares its labels and isolation. */
export function RegisterRunnerPanel({
  serverUrl,
  scopes,
  pending,
  error,
  onMint,
}: {
  serverUrl: string;
  scopes?: Array<{ value: string; label: string }>;
  pending: boolean;
  error: string | null;
  onMint: (input: MintInput) => Promise<Minted | undefined>;
}) {
  const [minted, setMinted] = useState<Minted | null>(null);
  return (
    <Panel title="Register a self-hosted runner">
      <form
        className="space-y-3 p-4"
        onSubmit={async (event) => {
          event.preventDefault();
          const form = new FormData(event.currentTarget);
          const labels = String(form.get("labels") ?? "")
            .split(",")
            .map((label) => label.trim())