diff --git a/ui/src/pages/apps/AppDetail.test.tsx b/ui/src/pages/apps/AppDetail.test.tsx index 6a9f427424..08e86194c0 100644 --- a/ui/src/pages/apps/AppDetail.test.tsx +++ b/ui/src/pages/apps/AppDetail.test.tsx @@ -262,7 +262,10 @@ function personalGrant(overrides: Record = {}) { }; } -function dedicatedGitHubGrant(overrides: Record = {}) { +function dedicatedGitHubGrant( + overrides: Record = {}, + githubOverrides: Record = {}, +) { return organizationGrant({ id: "grant-agent", kind: "agent", @@ -282,6 +285,7 @@ function dedicatedGitHubGrant(overrides: Record = {}) { webhookHealth: "pending", lastWebhookAt: null, lastAccessRefreshAt: "2026-09-05T12:00:00.000Z", + ...githubOverrides, }, }, ...overrides, @@ -1467,7 +1471,7 @@ describe("AppDetail", () => { await renderAppDetail(); - expect(container.querySelector('a[href="/agents/agent-1"]')?.textContent).toContain("Used only by Coder"); + expect(container.querySelector('a[href="/agents/coder"]')?.textContent).toContain("Used only by Coder"); expect(container.textContent).toContain("Repositories"); expect(container.textContent).toContain("1 selected repositories"); expect(container.querySelector( @@ -1489,19 +1493,9 @@ describe("AppDetail", () => { })); listConnectionGrantsMock.mockResolvedValue({ connection: { id: "conn-1", uid: "conn-1" }, - grants: [dedicatedGitHubGrant({ - providerTenant: { - github: { - userId: "123", - login: "dottabot", - installationCount: 1, - repositoryCount: 0, - repositorySelection: "all", - installationIds: ["456"], - installationOwnerLogins: ["paperclipai"], - managementUrl: "https://github.com/settings/installations/456", - }, - }, + grants: [dedicatedGitHubGrant({}, { + repositoryCount: 0, + repositorySelection: "all", })], capabilities: fullCapabilities(), currentUserId: "user-1", @@ -1514,6 +1508,32 @@ describe("AppDetail", () => { expect(container.textContent).not.toContain("selected repositories"); }); + it.each([ + ["mixed", "Mixed access; scope varies by installation"], + ["none", "No repositories selected"], + ] as const)("labels %s GitHub repository access explicitly", async (repositorySelection, expected) => { + mockParams.tab = "permissions"; + getConnectionMock.mockResolvedValue(connection({ + credentialPolicy: "per_agent", + authKind: "oauth", + })); + listConnectionGrantsMock.mockResolvedValue({ + connection: { id: "conn-1", uid: "conn-1" }, + grants: [dedicatedGitHubGrant({}, { + repositoryCount: 0, + repositorySelection, + })], + capabilities: fullCapabilities(), + currentUserId: "user-1", + members: [], + }); + + await renderAppDetail(); + + expect(container.textContent).toContain(expected); + expect(container.textContent).not.toContain("selected repositories"); + }); + it("persists an empty audience as all organization members", async () => { mockParams.tab = "permissions"; getConnectionMock.mockResolvedValue(connection({ createdByUserId: "user-1" })); diff --git a/ui/src/pages/apps/app-detail/IdentitiesSection.tsx b/ui/src/pages/apps/app-detail/IdentitiesSection.tsx index 1e04918ef8..b2b3a5cd62 100644 --- a/ui/src/pages/apps/app-detail/IdentitiesSection.tsx +++ b/ui/src/pages/apps/app-detail/IdentitiesSection.tsx @@ -31,8 +31,8 @@ import { AlertDialogTitle, } from "@/components/ui/alert-dialog"; import { Link } from "@/lib/router"; -import { brandChipBadge } from "@/lib/status-colors"; -import { cn } from "@/lib/utils"; +import { brandBanner, brandChipBadge } from "@/lib/status-colors"; +import { agentUrl, cn } from "@/lib/utils"; import { audienceUserIds, grantAccountLabel, @@ -99,7 +99,7 @@ export function IdentitiesSection({ credentialPolicy: ToolConnectionCredentialPolicy; ownerUserId: string | null; connectedUser: { label: string; image: string | null } | null; - dedicatedAgent: { id: string; name: string } | null; + dedicatedAgent: { id: string; name: string; urlKey?: string | null } | null; grantsQuery: ConnectionGrantsResponse | undefined; loading: boolean; error: boolean; @@ -181,7 +181,7 @@ export function IdentitiesSection({ status={agentGrant?.status ?? null} detail={dedicatedAgent ? ( Used only by {dedicatedAgent.name} @@ -296,19 +296,32 @@ function GitHubConnectionSummary({ }) { const github = grant.providerTenant?.github; if (!github) return null; - const allRepositories = github.repositorySelection === "all"; + const repositoryWarning = github.repositorySelection === "all" + ? "All current and future repositories" + : github.repositorySelection === "mixed" + ? "Mixed access; scope varies by installation" + : null; + const repositorySummary = github.repositorySelection === "none" + ? "No repositories selected" + : `${github.repositoryCount} selected repositories`; return (
Repositories
- {allRepositories ? ( -
- - All current and future repositories + {repositoryWarning ? ( +
+
) : ( -
{github.repositoryCount} selected repositories
+
{repositorySummary}
)}
{github.managementUrl ? (