From 6baa4fe35b62cd677a9ce0deeef052c1c4ea81d0 Mon Sep 17 00:00:00 2001 From: Tim Nunamaker Date: Thu, 6 Aug 2026 18:24:29 -0500 Subject: [PATCH 01/87] fix(console): unify manifest-driven source setup Project manifest setup metadata into shared owner-facing form contracts and remove misleading provider setup CTAs. Assisted-by: AI Signed-off-by: Tim Nunamaker --- .../components/source-setup-catalog.tsx | 46 ++++++-- .../browser-session/[connectorId]/page.tsx | 62 +++++++---- .../browser-session/page.invariants.test.ts | 15 ++- .../manual-upload-page.invariants.test.ts | 2 +- .../[connectorId]/manual-upload-form.tsx | 21 ++-- .../static-secret-page.invariants.test.ts | 12 +- .../static-secret/[connectorId]/actions.ts | 23 +++- .../static-secret/[connectorId]/page.tsx | 32 ++++-- .../(console)/lib/connection-catalog.test.ts | 15 +++ .../app/(console)/lib/connection-catalog.ts | 50 +++++++++ .../src/app/(console)/lib/ref-client.ts | 1 + .../lib/source-copy-negative.test.ts | 7 +- .../lib/source-setup-form-contract.test.ts | 100 +++++++++++++++++ .../lib/source-setup-form-contract.ts | 103 ++++++++++++++++++ .../lib/source-setup-presentation.ts | 23 +++- .../sources/add/add-source-demo-data.ts | 20 ++++ .../ref-static-secret-draft-connection.ts | 89 ++++++++++++++- ...atic-secret-draft-connection-route.test.ts | 42 ++++++- 18 files changed, 584 insertions(+), 79 deletions(-) create mode 100644 apps/console/src/app/(console)/lib/source-setup-form-contract.test.ts create mode 100644 apps/console/src/app/(console)/lib/source-setup-form-contract.ts diff --git a/apps/console/src/app/(console)/components/source-setup-catalog.tsx b/apps/console/src/app/(console)/components/source-setup-catalog.tsx index 8a0f38584..629ea6ec7 100644 --- a/apps/console/src/app/(console)/components/source-setup-catalog.tsx +++ b/apps/console/src/app/(console)/components/source-setup-catalog.tsx @@ -9,6 +9,7 @@ import type { RefCountState } from "../lib/ref-client.ts"; import { sourceSetupAction, sourceSetupAvailability, + sourceSetupContext, sourceSetupGuidance, sourceSetupRank, sourceSetupSecondaryAction, @@ -117,7 +118,7 @@ function SourceAcquisitionPaths({ paths }: { paths: readonly ConnectorAcquisitio function sourceMethodLine(entry: ConnectorCatalogEntry, existingSourceCount: number): string { if (entry.modality === "browser_bound" && entry.setupModality === "static_secret") { - return "Connect in a secure browser, with optional encrypted sign-in details for repair."; + return "Connect in a secure browser; interactive sign-in is valid, with optional saved details for repair."; } switch (entry.disposition) { case "local_collector_enroll": @@ -129,7 +130,7 @@ function sourceMethodLine(entry: ConnectorCatalogEntry, existingSourceCount: num ? `${existingSourceCount} existing ${existingSourceCount === 1 ? "source" : "sources"} can receive another export; choose on the import page.` : "Owner-exported file import."; case "provider_auth_deployment_blocked": - return "Server provider settings are required before account setup."; + return "Provider authorization is separate from file import; server provider settings are required before account setup."; case "browser_collector_manual": case "browser_bound_runbook": return entry.disposition === "browser_collector_manual" @@ -140,6 +141,21 @@ function sourceMethodLine(entry: ConnectorCatalogEntry, existingSourceCount: num } } +function SourceSetupContext({ entry }: { entry: ConnectorCatalogEntry }) { + const context = sourceSetupContext(entry); + if (!context) { + return null; + } + return ( +

+ {context} +

+ ); +} + function sourceDetailHref(connectorKey: string, connectionId: string): string { const params = new URLSearchParams({ connection_id: connectionId }); return `/sources/${encodeURIComponent(connectorKey)}?${params.toString()}`; @@ -256,6 +272,7 @@ function SourceSetupCard({

{sourceMethodLine(entry, existingSources.length)}

+ @@ -296,18 +313,33 @@ function ServerSetupSummary({ entries }: { entries: readonly ConnectorCatalogEnt

- These sources need provider app settings on this instance before an account can be added. + These sources need provider app settings on the instance before an account can be added. This dashboard shows + the missing requirements but does not edit provider applications here.

    {entries.map((entry) => ( -
  • +
  • {entry.displayName}

    {sourceMethodLine(entry, 0)}

    +
    - - Open server settings - + {entry.externalDocs.length > 0 ? ( +
    + Provider documentation: + {entry.externalDocs.map((doc) => ( + + {doc.label} + + ))} +
    + ) : null}
  • ))}
diff --git a/apps/console/src/app/(console)/connect/browser-session/[connectorId]/page.tsx b/apps/console/src/app/(console)/connect/browser-session/[connectorId]/page.tsx index f9d96ed60..8d392456c 100644 --- a/apps/console/src/app/(console)/connect/browser-session/[connectorId]/page.tsx +++ b/apps/console/src/app/(console)/connect/browser-session/[connectorId]/page.tsx @@ -30,7 +30,13 @@ import Link from "next/link"; import { notFound } from "next/navigation"; import { RecordroomShellWithPalette } from "@/app/(console)/components/recordroom-shell-with-palette.tsx"; import { isBrowserBoundConnector, isSupportedBrowserCollectorConnector } from "../../../lib/connection-modality.ts"; -import { getStaticSecretSetup, type StaticSecretSetup, type StaticSecretSetupField } from "../../../lib/ref-client.ts"; +import { getStaticSecretSetup, type StaticSecretSetupField } from "../../../lib/ref-client.ts"; +import { + type BrowserOptionalCredentialContract, + browserSessionFormContract, + connectionNameFieldContract, + optionalCredentialFieldLabel, +} from "../../../lib/source-setup-form-contract.ts"; export const dynamic = "force-dynamic"; @@ -62,38 +68,33 @@ function inputType(field: StaticSecretSetupField): "email" | "password" | "text" } function OptionalStoredCredentialFields({ + credentials, searchParams, - setup, }: { + credentials: BrowserOptionalCredentialContract; searchParams: Record; - setup: StaticSecretSetup; }) { return (
- - Remember my sign-in details for automatic reconnection (optional) - -

- These manifest-defined details are encrypted and may help with initial sign-in or repair. CAPTCHA, OTP, - passkeys, and other human steps always stay in the secure browser; unattended reconnection is not guaranteed. -

+ {credentials.title} +

{credentials.description}

- {setup.credential_capture.fields.map((field) => ( + {credentials.fields.map((field) => (