import { useState } from "react";
import { AlertTriangle, CircleHelp, ExternalLink, Loader2 } from "lucide-react";
import type { ChatEndpoint } from "@/api/chatEndpoints";
import { chatEndpointsApi } from "@/api/chatEndpoints";
import { CopyText } from "@/components/CopyText";
import { SetupWizardFooter } from "@/components/SetupWizard";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip";
import { Link } from "@/lib/router";
import { gitHubAppSettingsUrl } from "./GitHubAppIdentity";
export function GitHubConnectionRecoveryNotice({ endpoint }: { endpoint: ChatEndpoint }) {
if (!["attention", "revoked"].includes(endpoint.status)) return null;
return (
Reconnect this bot to restore GitHub reviews and @mentions. Start with the App already saved in Paperclip. {endpoint.lastError ?? endpoint.healthMessage}{endpoint.status === "revoked" ? "GitHub bot is disconnected" : "GitHub connection needs repair"}
Connection details
Restore {appName} using its saved credentials. Paperclip will check the App and its installation, then repair event delivery.
Your agent, repository choices, review settings, and task history stay attached to this connection. You don’t need to create another GitHub App.
{rotationAlreadyCommitted ? "The rotated secret is saved. Reconnect using that value; Paperclip will check GitHub’s signed delivery." : <>Generate a new secret, then replace the webhook secret in GitHub App settings. GitHub must verify the new secret before you reconnect.>}
Generating immediately invalidates the previous secret. This value is shown only here; keep this page open until you have copied it.
{rotationUncertain &&The active secret is not confirmed. Check its saved status before using an earlier value.
} {rotationUncertain && rotationSecret && } {rotationSecret && !rotationUncertain &&{rotateWebhookSecret ? "Could not finish secret rotation" : replace ? "Could not reconnect this App" : "Could not reconnect the saved App"}
{error}
{rotateWebhookSecret ? "Use the newest secret in GitHub App settings, then retry reconnecting after GitHub verifies delivery." : "Try again if the service is temporarily unavailable. If GitHub rejected the saved key, replace the credentials for this same App below."}
Open GitHub App settings
If it’s owned by an organization, open that organization’s Settings → Developer settings → GitHub Apps → Edit. You’ll need permission to manage the App.
{credentialFields &&Enter the requested credentials below. Other saved credentials stay in place.
}Copy App ID from the App’s settings page. {reconnect && "The saved ID is filled in when available."}
setCredentials({ ...credentials, appId: event.target.value })} /> {appIdInvalid &&Use the numeric App ID, not the Client ID.
}You can reuse a .pem file you already saved. GitHub cannot show an old private key again. Creating a new key for the same App keeps its installation.
{ const file = event.target.files?.[0]; if (!file) return; setFileError(""); try { const privateKey = await file.text(); setCredentials(current => ({ ...current, privateKey })); setPrivateKeyDraft(""); } catch { setFileError("Could not read this file. Try again or paste the key below."); } }} /> { event.preventDefault(); setCredentials({ ...credentials, privateKey: event.clipboardData.getData("text") }); setPrivateKeyDraft(""); setFileError(""); }} onChange={event => { setPrivateKeyDraft(event.target.value); setCredentials({ ...credentials, privateKey: event.target.value }); }} /> {keyLoaded &&Private key loaded. Paste or upload another key to replace it.
} {keyInvalid &&Use a PEM private key, including its BEGIN and END lines. A token or client secret won’t work here.
} {fileError &&{fileError}
} GitHub’s private key instructionsUse the secret saved when you configured this App’s webhook. If you no longer have it, change the webhook secret in GitHub App settings and enter the same value here.
setCredentials({ ...credentials, webhookSecret: event.target.value })} />