import type { IntegrationChip } from "../components/capabilities/integration-view-model.js"; import type { CapabilityCatalogItem, CapabilityKind, CapabilitySource, ConnectionMetadata, ConnectionOwnership, CreateCapabilityInput, SocialConnection } from "../types.js"; export type CapabilityFilter = "all" | CapabilityKind; /** Singular human label for a capability kind ("MCP server", "API"…). */ export declare function capabilityKindLabel(kind: CapabilityKind): string; export declare function capabilityItemKindLabel(item: CapabilityCatalogItem): string; /** Human label for where a catalog item came from. */ export declare function capabilitySourceLabel(source: CapabilitySource | string): string; export type CuratedSkillProvenance = { libraryId: string | null; version: string | null; contentSha256: string | null; sourceCommit: string | null; provenance: string | null; sourceUrl: string | null; license: string | null; documentationUrl: string | null; artifactPath: string | null; status: "enabled" | "not_enabled"; effectiveSelection: string; }; /** * Return the public, immutable identity and effective selection state for a * curated skill. Credentials and other runtime configuration are deliberately * not part of this projection. */ export declare function curatedSkillProvenance(item: CapabilityCatalogItem): CuratedSkillProvenance | null; export type CapabilityFormState = { kind: "mcp"; name: string; description: string; category: string; tags: string; endpointUrl: string; homepageUrl: string; installUrl: string; enableAfterAdd: boolean; }; export declare function emptyCapabilityForm(): CapabilityFormState; export declare function isConnectorCatalogItem(item: CapabilityCatalogItem): boolean; export declare function filterCapabilityCatalogItems(items: CapabilityCatalogItem[], filter: CapabilityFilter, query: string): CapabilityCatalogItem[]; export declare function capabilityErrorToast(error: unknown, fallbackTitle: string): { title: string; description: string; }; /** * The enable-path 422 raised when a credentialed MCP is enabled without a * connection ref or headers. The connect sheet catches this to route the user * into the credential form instead of showing the raw API string. */ export declare function isMissingCredentialsError(error: unknown): boolean; export declare function cleanApiErrorMessage(message: string): string; export type RequiredHeaderField = { /** The wire header name the broker injects (e.g. "X-API-Key"). */ name: string; /** Human label for the input — never the word "headers". */ label: string; }; export type CapabilityConnectPlan = { mode: "setup_required"; } | { mode: "enable"; } | { mode: "dedicated"; } | { mode: "social_oauth"; provider: "x" | "reddit"; } | { mode: "fiken_api_token"; } | { mode: "oauth"; providerDomain: string; mcpUrl: string | null; requestedScopes: string[]; } | { mode: "api_key"; providerDomain: string; fields: RequiredHeaderField[]; }; export declare function capabilityConnectPlan(item: CapabilityCatalogItem): CapabilityConnectPlan; /** * Workspace-shared is the default for a new connection; the explicit * "Connect only for me" choice (and every personal-only connector) overrides it. */ export declare const DEFAULT_CONNECTION_OWNERSHIP: ConnectionOwnership; /** * The one-click / one-dialog connect fast path offered by a row or tile icon, * derived purely from the catalog item so it can be asserted without React. * * `null` means "no fast path": the full detail sheet owns it. That deliberately * covers dedicated lifecycles (Skills, Plugins, Fiken, social) AND any api-key * connector declaring more than one required header, which a single-field * dialog would silently half-connect. * * Ownership is never assumed: a personal-only connector (official Gmail, * Slack's hosted MCP) resolves to `personal` here exactly as the detail sheet * does, so the fast path can never start a workspace-owned binding for one. */ export type CapabilityQuickConnectPlan = { mode: "enable"; } | { mode: "oauth"; ownership: ConnectionOwnership; providerDomain: string; mcpUrl: string | null; /** True when the connector is not curated-official, so one confirming dialog is shown first. */ confirm: boolean; } | { mode: "api_key"; ownership: ConnectionOwnership; providerDomain: string; field: RequiredHeaderField; }; export declare function capabilityQuickConnectPlan(item: CapabilityCatalogItem): CapabilityQuickConnectPlan | null; /** * The stored credential for an api-key connect. Keyed by the field's WIRE * header name (what the broker injects), never by its human label - "API key" * is not even a legal HTTP header token, so keying by the label silently * stores an unusable credential that only fails later as a 401. */ export declare function apiKeyCredential(field: RequiredHeaderField, value: string): { headers: Record; }; export declare function defaultCapabilityConnectionOwnership(item: CapabilityCatalogItem): ConnectionOwnership; /** * Curation facts written by the catalog import from `data/catalog/curated.json`. * `curated` records that a row has a reviewed presentation overlay; `featured` * means we chose to promote it. `official` is the separate evidence-backed * claim that the provider publishes the server on its own domain. None is a * security review and must never be rendered as "reviewed" or "verified". */ export declare function capabilityCuration(item: Pick): { curated: boolean; featured: boolean; official: boolean; }; /** Stable order: featured first, everything else in its existing order. */ export declare function sortFeaturedFirst>(items: readonly T[]): T[]; /** Human label for a catalog category slug; unknown slugs are title-cased. */ export declare function capabilityCategoryLabel(category: string | null | undefined): string | null; /** Short auth hint for a tile ("OAuth" / "API key"), or null when none applies. */ export declare function capabilityAuthHint(item: CapabilityCatalogItem): string | null; /** * The same four-state chip Integrations rows use, derived for a Connectors * catalog item so both surfaces render the identical compact state indicator. * `health` is optional: tiles that only ever show not-yet-enabled items (the * Browse grid, registry results) can omit it since every item there is * necessarily "Not connected". */ export declare function capabilityStateChip(item: Pick, health?: ConnectionHealth): IntegrationChip; export declare function preferredSocialConnection(connections: SocialConnection[], provider: "x" | "reddit"): SocialConnection | null; export declare function socialConnectionsForOwnership(connections: SocialConnection[], ownership?: ConnectionOwnership): SocialConnection[]; export declare function domainFromUrl(url: string | null): string | null; /** * The connection ref the API records on an enabled catalog item's installation * — the authoritative link between the installation and its connection row. * `null` means the item was enabled without a connection (headers-enabled or * credential-free), which is healthy. */ export declare function installedConnectionRef(item: CapabilityCatalogItem): CapabilityCatalogItem["connectionRef"]; /** * The detail sheet's selection: the id it's bound to, whether it came from the * public registry (drives persist-on-connect), whether the snapshot is an * authoritative fallback, and the snapshot taken at open time. * * `snapshotFallback` is true when the id may legitimately be absent from the live * `items` list even though the row exists — a registry result not yet persisted, * OR a just-created custom item opened before (or across a failed) refresh. For * those the snapshot renders until the live row appears; for a normal catalog * selection it stays false so a vanished row closes the sheet instead of ghosting. */ export type SheetSelection = { id: string; registry: boolean; snapshotFallback: boolean; snapshot: CapabilityCatalogItem; }; /** * The item the detail sheet should render, derived from the LIVE catalog by id so * a mutation elsewhere (disable, refresh) re-derives it instead of leaving a stale * snapshot. A snapshot-fallback selection (registry result, or freshly created and * not yet in `items`) falls back to its snapshot; any other selection absent from * the catalog resolves to null so the caller closes the sheet rather than ghost. */ export declare function resolveSheetItem(selected: SheetSelection | null, items: CapabilityCatalogItem[]): CapabilityCatalogItem | null; export type ConnectionHealth = { state: "none"; } | { state: "unverified"; } | { state: "connected"; connection: ConnectionMetadata; } | { state: "attention"; connection: ConnectionMetadata | null; }; /** * Health of an enabled item. Workspace bindings resolve by their exact id; * subject bindings intentionally store no id and resolve only among the current * caller's visible personal rows by provider and kind. `loaded` distinguishes a * failed connection-list read from a genuinely missing or inactive row. * Host bindings are not represented in that native list and therefore remain * enabled without a native health or repair action. * * The `loaded` gate matters because listConnections needs a distinct * `connections:read` scope: a grant with catalog access but not that sctracking-403s, * and a failed load must not read as "every connection was deleted" and paint * healthy integrations amber. */ /** * The workspace-shared Fiken connection the first-party fiken tools resolve: * usable status first, then newest update. Mirrors the server-side selection. */ export declare function fikenWorkspaceConnection(connections: ConnectionMetadata[]): ConnectionMetadata | null; export declare function connectionHealth(item: CapabilityCatalogItem, connections: ConnectionMetadata[], loaded: boolean): ConnectionHealth; /** * How to repair an enabled item whose connection needs attention, driven by the * installation's OWN connectionRef.kind — NOT the current catalog plan, which can * drift (an item enabled via a connection may later read as plain "enable" in the * catalog). Returns null when there's nothing to repair. `connectionId` is the * surviving row to reuse, or null when it was deleted (repair mints a fresh one). */ export type ReconnectPlan = { kind: "oauth"; connectionId: string | null; ownership: ConnectionOwnership; } | { kind: "api_key"; connectionId: string | null; ownership: ConnectionOwnership; }; export declare function capabilityReconnectPlan(item: CapabilityCatalogItem, health: ConnectionHealth): ReconnectPlan | null; /** The generic single credential field for an api-key reconnect when the catalog * no longer supplies requiredHeaders (drift). The wire name defaults to the most * common bearer header; the label never leaks the word "headers". */ export declare const GENERIC_API_KEY_FIELD: RequiredHeaderField; /** * The workspace-shared connection (subjectId null) for a provider domain, used * to reuse an existing row on an API-key connect retry instead of creating a * duplicate. Both sides normalized (the catalog domain is raw; the row's is * API-canonicalized) so the match holds. */ export declare function workspaceConnectionForDomain(connections: ConnectionMetadata[], providerDomain: string): ConnectionMetadata | null; /** * The existing workspace connection id to reuse on an API-key connect/retry * instead of creating a duplicate: the installation's own ref first, then a * workspace-shared row for the domain. `null` → none exists, so mint a new one. * Keeps a retry after a create-then-enable failure from piling up dead rows. */ export declare function connectionToReuseForApiKey(item: CapabilityCatalogItem, connections: ConnectionMetadata[], providerDomain: string, ownership?: ConnectionOwnership): string | null; /** * Public-registry results to show for the CURRENT query. Registry hits are held * in state after an explicit search; once the user edits the query they no * longer describe what's on screen, so gate them on the searched term matching * the live (trimmed) query — otherwise a stale search renders against a new one. */ export declare function registryResultsForQuery(query: string, searched: string | null, results: CapabilityCatalogItem[]): CapabilityCatalogItem[]; /** * Client mirror of the API's `canonicalProviderDomain` (apps/api oauth-client): * trim, lowercase, strip a single leading "www.". Used only to match/dedup rows * client-side — connectionRefs sent to the API are always built from the * connection row the API returns, never from a domain the client canonicalized. */ export declare function normalizeProviderDomain(domain: string): string; /** * What to do when an OAuth round-trip returns, once the item has been resolved * from a FRESH catalog fetch (a just-created registry item may be absent from * the pre-redirect snapshot). Pure so the decision is unit-testable: * - missing: success but the item is no longer in the catalog → can't enable. * - no_connection: success but no connection id came back → can't enable. * - reconnect: already enabled and OAuth refreshed the SAME connection the * installation already references → nothing to re-enable. * - enable: a fresh connect, OR a reconnect whose old row was gone so OAuth * minted a new one → (re-)enable to point the installation at it. */ export type OAuthResumeAction = "missing" | "no_connection" | "reconnect" | "enable"; export declare function oauthResumeAction(item: CapabilityCatalogItem | null, connectionId: string | null): OAuthResumeAction; /** Generic per-subject OAuth binding; never persist the returned personal row id. */ export declare function subjectOAuthConnectionRef(providerDomain: string): { providerDomain: string; kind: "oauth2"; subjectScope: "subject"; }; /** New catalog connects opt into all eligible accounts; reconnects preserve * existing exact refs and retain an explicitly installed selector. */ export declare function catalogConnectionAccountSelection(item: Pick): "all_eligible" | undefined; /** Build the capability binding that matches the OAuth row's explicit ownership. */ export declare function oauthConnectionRef(ownership: ConnectionOwnership, connectionId: string, providerDomain: string, accountSelection?: "all_eligible"): ReturnType | { providerDomain: string; kind: "oauth2"; subjectScope: "workspace" | "subject"; accountSelection: "all_eligible"; } | { connectionId: string; providerDomain: string; kind: "oauth2"; subjectScope: "workspace"; }; export declare function apiKeyConnectionRef(ownership: ConnectionOwnership, connectionId: string, providerDomain: string, accountSelection?: "all_eligible"): { providerDomain: string; kind: "api_key"; subjectScope: "subject"; } | { providerDomain: string; kind: "api_key"; subjectScope: "workspace" | "subject"; accountSelection: "all_eligible"; } | { connectionId: string; providerDomain: string; kind: "api_key"; subjectScope: "workspace"; }; export declare function oauthConnectionOwnership(value: string | null): ConnectionOwnership | null; /** First one or two initials for the logo monogram fallback. */ export declare function capabilityMonogram(name: string): string; export declare function scheduleSummaryForMetadata(value: unknown): string; export declare function capabilityCounts(items: CapabilityCatalogItem[]): Record; export declare function capabilityFilterLabel(kind: CapabilityFilter): string; export declare function createInputFromCatalogItem(item: CapabilityCatalogItem): CreateCapabilityInput; /** Validate the MCP-only "Add custom" dialog. */ export declare function capabilityFormError(form: CapabilityFormState): string | null; export declare function capabilityInputFromForm(form: CapabilityFormState): CreateCapabilityInput | null;