import { useCallback, useEffect, useMemo, useState } from "react"; import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"; import { AlertCircle, AlertTriangle, ArchiveRestore, Archive, Ban, CheckCircle2, ChevronLeft, ChevronRight, Cloud, CornerLeftUp, Copy, Database, Edit3, ExternalLink, Folder, FolderOpen, KeyRound, Link2, Lock, Loader2, MoreHorizontal, Plus, RefreshCw, Search, ShieldCheck, Star, Trash2, X, Filter, Info, Pencil, UserRound, Users, } from "lucide-react"; import { Link, useSearchParams } from "react-router-dom"; import type { CompanySecret, CompanySecretUsageBinding, CompanySecretProviderConfig, SecretProviderConfigDiscoveryCandidate, SecretProviderConfigDiscoveryPreviewResult, SecretAccessEvent, SecretManagedMode, SecretProvider, SecretProviderConfigStatus, SecretProviderDescriptor, SecretStatus, UserSecretCoverageSummary, UserSecretDefinition, } from "@paperclipai/shared"; import { hidesCompanySection } from "@paperclipai/shared"; import { useCompany } from "../context/CompanyContext"; import { useHiddenSettings } from "../hooks/useHiddenSettings"; import { useBreadcrumbs } from "../context/BreadcrumbContext"; import { useToastActions } from "../context/ToastContext"; import { secretsApi, type CreateSecretInput, type CreateSecretProviderConfigInput, type SecretProviderHealthResponse, type UpdateSecretProviderConfigInput, } from "../api/secrets"; import { ApiError } from "../api/client"; import { accessApi, type CompanyUserDirectoryEntry } from "../api/access"; import { agentsApi } from "../api/agents"; import { envKeyFromSecretName } from "../components/environment-variables-editor/model"; import { AGENT_ACCESS_CONFIG_PATH_PREFIX, aliasFromConfigPath, consumerTypeLabel, deliveryModeForConfigPath, deliveryModeLabel, } from "../lib/secret-delivery"; import { queryKeys } from "../lib/queryKeys"; import { EmptyState } from "../components/EmptyState"; import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; import { Textarea } from "@/components/ui/textarea"; import { Checkbox } from "@/components/ui/checkbox"; import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover"; import { Sheet, SheetContent, SheetHeader, SheetTitle, SheetDescription, } from "@/components/ui/sheet"; import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, } from "@/components/ui/dialog"; import { Tabs, TabsList, TabsTrigger, TabsContent } from "@/components/ui/tabs"; import { Badge } from "@/components/ui/badge"; import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuSeparator, DropdownMenuTrigger, } from "@/components/ui/dropdown-menu"; import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/components/ui/tooltip"; import { cn } from "../lib/utils"; import { copyTextToClipboard } from "../lib/clipboard"; import { useCopyAction } from "../lib/use-copy-action"; import { PageTabBar } from "../components/PageTabBar"; import { AgentSelect } from "../components/AgentMultiSelect"; import { ImportFromVaultDialog } from "./secrets/ImportFromVaultDialog"; import { MyUserSecretsTab } from "./secrets/MyUserSecretsTab"; import { ProposalsTab } from "./secrets/ProposalsTab"; import { SecretPathName } from "./secrets/SecretPathName"; import { buildSecretPathBreadcrumbs, buildSecretPathListing, getSecretPathRowName, normalizeSecretPath, validateSecretFolderSegment, type SecretPathFolder, } from "./secrets/secret-path"; import { SetMyUserSecretDialog } from "./secrets/SetMyUserSecretDialog"; import { coverageSummaryLabel, UserSecretChip, } from "./secrets/user-secret-presentation"; import type { MyUserSecretEntry } from "../api/secrets"; type CreateMode = "managed" | "external"; // "value" writes a new secret value (for external references: through to the // provider); "reference" re-points an external reference without writing. type RotateMode = "value" | "reference"; type SecretValueProvider = "company" | "user"; type ProvidedByFilter = "all" | SecretValueProvider; type SecretsTab = "secrets" | "my-secrets" | "vaults" | "proposals"; type SecretsViewMode = "folders" | "flat"; const SECRETS_VIEW_MODE_STORAGE_KEY = "paperclip.secrets.viewMode"; function readStoredViewMode(): SecretsViewMode | null { try { const stored = window.localStorage.getItem(SECRETS_VIEW_MODE_STORAGE_KEY); return stored === "folders" || stored === "flat" ? stored : null; } catch { return null; } } /** "12 secrets · 3 folders" — folder part omitted when there are no subfolders. */ function formatSecretPathCounts(secretCount: number, folderCount: number): string { const parts = [`${secretCount} ${secretCount === 1 ? "secret" : "secrets"}`]; if (folderCount > 0) { parts.push(`${folderCount} ${folderCount === 1 ? "folder" : "folders"}`); } return parts.join(" · "); } type UnifiedSecretRow = | { id: string; kind: "company"; secret: CompanySecret } | { id: string; kind: "user"; definition: UserSecretDefinition }; type ProviderVaultForm = { provider: SecretProvider; displayName: string; status: SecretProviderConfigStatus; isDefault: boolean; backupReminderAcknowledged: boolean; region: string; namespace: string; secretNamePrefix: string; kmsKeyId: string; ownerTag: string; environmentTag: string; projectId: string; location: string; address: string; mountPath: string; secretPathPrefix: string; }; type SafeProviderErrorDetails = { code?: string; provider?: string; operation?: string; providerConfigId?: string; providerVaultContext?: string; region?: string; credentialPath?: string; requiredCapability?: string; actionableMessage?: string; safeAlternative?: string; }; const EMPTY_SECRETS: CompanySecret[] = []; const EMPTY_USER_SECRET_DEFINITIONS: UserSecretDefinition[] = []; const EMPTY_MY_USER_SECRETS: MyUserSecretEntry[] = []; const EMPTY_SECRET_PROVIDERS: SecretProviderDescriptor[] = []; const EMPTY_PROVIDER_CONFIGS: CompanySecretProviderConfig[] = []; const PROVIDER_ORDER: SecretProvider[] = [ "local_encrypted", "aws_secrets_manager", "gcp_secret_manager", "vault", ]; function defaultProviderVaultStatus(provider: SecretProvider): SecretProviderConfigStatus { return provider === "gcp_secret_manager" || provider === "vault" ? "coming_soon" : "ready"; } function emptyProviderVaultForm(provider: SecretProvider = "local_encrypted"): ProviderVaultForm { return { provider, displayName: "", status: defaultProviderVaultStatus(provider), isDefault: false, backupReminderAcknowledged: false, region: "", namespace: "", secretNamePrefix: "", kmsKeyId: "", ownerTag: "", environmentTag: "", projectId: "", location: "", address: "", mountPath: "", secretPathPrefix: "", }; } function providerConfigValue(config: CompanySecretProviderConfig["config"], key: string) { if (!config || typeof config !== "object" || Array.isArray(config)) return ""; const value = (config as Record)[key]; return typeof value === "string" ? value : ""; } function apiErrorDetails(error: unknown): SafeProviderErrorDetails | null { if (!(error instanceof ApiError)) return null; const body = error.body; if (!body || typeof body !== "object") return null; const details = (body as Record).details; if (!details || typeof details !== "object" || Array.isArray(details)) return null; return details as SafeProviderErrorDetails; } function apiErrorCode(error: unknown): string | null { return apiErrorDetails(error)?.code ?? null; } function isAwsDiscoveryAccessDenied(error: unknown): boolean { const details = apiErrorDetails(error); if (details?.provider === "aws_secrets_manager" && details.operation === "secret_provider_config.discovery.preview") { return details.code === "access_denied"; } if (!(error instanceof ApiError)) return false; return apiErrorCode(error) === "access_denied"; } function readableErrorMessage(error: unknown): string { if (error instanceof ApiError) return error.message || `Request failed: ${error.status}`; if (error instanceof Error) return error.message; return "Unexpected error"; } function providerVaultFormFromConfig(config: CompanySecretProviderConfig): ProviderVaultForm { return { ...emptyProviderVaultForm(config.provider), displayName: config.displayName, status: config.status, isDefault: config.isDefault, backupReminderAcknowledged: Boolean((config.config as Record | undefined)?.backupReminderAcknowledged), region: providerConfigValue(config.config, "region"), namespace: providerConfigValue(config.config, "namespace"), secretNamePrefix: providerConfigValue(config.config, "secretNamePrefix"), kmsKeyId: providerConfigValue(config.config, "kmsKeyId"), ownerTag: providerConfigValue(config.config, "ownerTag"), environmentTag: providerConfigValue(config.config, "environmentTag"), projectId: providerConfigValue(config.config, "projectId"), location: providerConfigValue(config.config, "location"), address: providerConfigValue(config.config, "address"), mountPath: providerConfigValue(config.config, "mountPath"), secretPathPrefix: providerConfigValue(config.config, "secretPathPrefix"), }; } function formatRelative(value: Date | string | null | undefined): string { if (!value) return "—"; const date = typeof value === "string" ? new Date(value) : value; if (Number.isNaN(date.getTime())) return "—"; const diff = Date.now() - date.getTime(); if (diff < 0) return date.toLocaleString(); const seconds = Math.floor(diff / 1000); if (seconds < 60) return `${seconds}s ago`; const minutes = Math.floor(seconds / 60); if (minutes < 60) return `${minutes}m ago`; const hours = Math.floor(minutes / 60); if (hours < 48) return `${hours}h ago`; const days = Math.floor(hours / 24); if (days < 30) return `${days}d ago`; return date.toLocaleDateString(); } function statusTextTone(status: SecretStatus) { switch (status) { case "active": return "text-emerald-700 dark:text-emerald-300"; case "disabled": return "text-amber-700 dark:text-amber-300"; case "archived": return "text-muted-foreground"; case "deleted": return "text-destructive"; default: return "text-muted-foreground"; } } function providerLabel(providers: SecretProviderDescriptor[] | undefined, id: SecretProvider) { return providers?.find((p) => p.id === id)?.label ?? id.replaceAll("_", " "); } function normalizeSecretKeyForPreview(input: string) { return input .trim() .toLowerCase() .replace(/[^a-z0-9_.-]+/g, "-") .replace(/^-+|-+$/g, "") .slice(0, 120); } function normalizeUserSecretKeyForPreview(input: string) { return input .trim() .toUpperCase() .replace(/[^A-Z0-9_]+/g, "_") .replace(/^_+|_+$/g, "") .slice(0, 120); } function modeLabel(managedMode: SecretManagedMode) { return managedMode === "paperclip_managed" ? "Paperclip-managed" : "Linked external"; } function modeDescription(managedMode: SecretManagedMode, canWriteExternalValue = false) { if (managedMode === "paperclip_managed") { return "Paperclip owns create and rotation writes for this provider secret."; } return canWriteExternalValue ? "Paperclip resolves this provider reference and can write new values to it via Update value." : "Paperclip resolves this provider reference but does not rotate the provider value."; } function statusLabel(status: SecretStatus) { return status.charAt(0).toUpperCase() + status.slice(1); } function statusDotTone(status: SecretStatus) { switch (status) { case "active": return "bg-emerald-500"; case "disabled": return "bg-amber-500"; case "archived": return "bg-muted-foreground"; case "deleted": return "bg-destructive"; default: return "bg-muted-foreground"; } } function StatusBadge({ status }: { status: SecretStatus }) { return ( ); } function MetaChip({ children }: { children: React.ReactNode }) { return ( {children} ); } function providerIndicatorLabel( secret: CompanySecret, providers: SecretProviderDescriptor[], providerConfigs: CompanySecretProviderConfig[], ) { const provider = providerLabel(providers, secret.provider); const vault = providerVaultLabel(providerConfigs, secret.providerConfigId); const custody = modeLabel(secret.managedMode); return [ `${custody} · ${provider}`, vault ? `Vault: ${vault}` : null, secret.externalRef ? `Reference: ${secret.externalRef}` : null, ] .filter(Boolean) .join("\n"); } function SecretProviderIndicator({ secret, providers, providerConfigs, }: { secret: CompanySecret; providers: SecretProviderDescriptor[]; providerConfigs: CompanySecretProviderConfig[]; }) { const label = providerIndicatorLabel(secret, providers, providerConfigs); const Icon = secret.managedMode === "external_reference" ? ExternalLink : Lock; return ( {label} ); } function UpdatedWithTooltip({ updatedAt, tooltip, }: { updatedAt: Date | string | null | undefined; tooltip: string; }) { return ( {formatRelative(updatedAt)} {tooltip} ); } function healthEntryForProvider( health: SecretProviderHealthResponse | null, providerId: SecretProvider, ) { return health?.providers.find((entry) => entry.provider === providerId) ?? null; } export function getCreateProviderBlockReason( provider: SecretProviderDescriptor | null | undefined, mode: CreateMode, health: SecretProviderHealthResponse | null, providerConfig?: CompanySecretProviderConfig | null, ) { if (!provider) return "Select a provider."; if (mode === "managed" && provider.supportsManagedValues === false) { return `${provider.label} does not support Paperclip-managed secret values.`; } if (mode === "external" && provider.supportsExternalReferences === false) { return `${provider.label} does not support linked external references.`; } const selectedProviderConfigBlockReason = providerConfig?.provider === provider.id ? getProviderConfigBlockReason(providerConfig) : null; const selectedProviderConfigReady = providerConfig?.provider === provider.id && !selectedProviderConfigBlockReason; if (provider.configured === false) { if (selectedProviderConfigReady) return null; if (selectedProviderConfigBlockReason) return selectedProviderConfigBlockReason; const healthEntry = healthEntryForProvider(health, provider.id); const deploymentMessage = `Deployment default ${provider.label} is not configured.`; const nextStep = " Select a ready provider vault or configure the deployment default."; return healthEntry?.message ? `${deploymentMessage}${nextStep} ${healthEntry.message}` : `${deploymentMessage}${nextStep}`; } const healthEntry = healthEntryForProvider(health, provider.id); if (healthEntry?.status === "error") { return `${provider.label} health check failed: ${healthEntry.message}`; } return null; } function providerHealthText( provider: SecretProviderDescriptor | null | undefined, health: SecretProviderHealthResponse | null, providerConfig?: CompanySecretProviderConfig | null, ) { if (!provider) return null; if ( provider.configured === false && providerConfig?.provider === provider.id && !getProviderConfigBlockReason(providerConfig) ) { return `Using selected provider vault. Deployment default ${provider.label} is not configured.`; } const entry = healthEntryForProvider(health, provider.id); if (!entry) return null; const warnings = entry.warnings?.join(" "); return [entry.message, warnings].filter(Boolean).join(" "); } function detailString(details: Record | undefined, key: string) { const value = details?.[key]; return typeof value === "string" && value.trim() ? value.trim() : null; } export function getProviderConfigBlockReason( config: CompanySecretProviderConfig | null | undefined, ) { if (!config) return null; if (config.status === "disabled") return "This provider vault is disabled."; if (config.status === "coming_soon") return "This provider vault is saved as draft metadata only."; if (config.healthStatus === "error") { return config.healthMessage ?? "This provider vault health check failed."; } return null; } export function getSelectableProviderConfig( configs: CompanySecretProviderConfig[], provider: SecretProvider, ) { const providerConfigs = configs.filter((config) => config.provider === provider); return ( providerConfigs.find((config) => config.isDefault && !getProviderConfigBlockReason(config)) ?? providerConfigs.find((config) => !getProviderConfigBlockReason(config)) ?? null ); } export function getDefaultProviderConfigId( configs: CompanySecretProviderConfig[], provider: SecretProvider, ) { const selected = getSelectableProviderConfig(configs, provider); const providerConfigs = configs.filter((config) => config.provider === provider); return ( selected?.id ?? providerConfigs.find((config) => config.isDefault)?.id ?? "" ); } export function findCreateProviderReplacement({ providers, providerConfigs, currentProvider, mode, health, }: { providers: SecretProviderDescriptor[]; providerConfigs: CompanySecretProviderConfig[]; currentProvider: SecretProvider; mode: CreateMode; health: SecretProviderHealthResponse | null; }) { return ( providers.find((provider) => { const selectedConfig = provider.id === currentProvider ? providerConfigs.find( (config) => config.provider === provider.id && !getProviderConfigBlockReason(config), ) ?? null : getSelectableProviderConfig(providerConfigs, provider.id); return !getCreateProviderBlockReason(provider, mode, health, selectedConfig); }) ?? null ); } function providerVaultLabel(configs: CompanySecretProviderConfig[], id: string | null | undefined) { if (!id) return "Deployment default"; return configs.find((config) => config.id === id)?.displayName ?? "Unknown vault"; } function buildProviderVaultConfig(form: ProviderVaultForm): Record { const compact = (value: string) => value.trim() || null; switch (form.provider) { case "local_encrypted": return { backupReminderAcknowledged: form.backupReminderAcknowledged }; case "aws_secrets_manager": return { region: form.region.trim(), namespace: compact(form.namespace), secretNamePrefix: compact(form.secretNamePrefix), kmsKeyId: compact(form.kmsKeyId), ownerTag: compact(form.ownerTag), environmentTag: compact(form.environmentTag), }; case "gcp_secret_manager": return { projectId: compact(form.projectId), location: compact(form.location), namespace: compact(form.namespace), secretNamePrefix: compact(form.secretNamePrefix), }; case "vault": return { address: compact(form.address), namespace: compact(form.namespace), mountPath: compact(form.mountPath), secretPathPrefix: compact(form.secretPathPrefix), }; default: return {}; } } function getAwsProviderVaultDiscoveryQuery(form: ProviderVaultForm): string | null { return ( form.secretNamePrefix.trim() || form.namespace.trim() || form.environmentTag.trim() || form.ownerTag.trim() || null ); } export function getAwsManagedPathPreview(input: { provider: SecretProviderDescriptor | null | undefined; health: SecretProviderHealthResponse | null; companyId: string; secretKeySource: string; }) { if (input.provider?.id !== "aws_secrets_manager") return null; const healthEntry = healthEntryForProvider(input.health, "aws_secrets_manager"); const prefix = detailString(healthEntry?.details, "prefix") ?? "paperclip"; const deploymentId = detailString(healthEntry?.details, "deploymentId") ?? "{deploymentId}"; const secretKey = normalizeSecretKeyForPreview(input.secretKeySource) || "{secretKey}"; return `${prefix}/${deploymentId}/${input.companyId}/${secretKey}`; } export function Secrets() { const queryClient = useQueryClient(); const { selectedCompanyId } = useCompany(); const { setBreadcrumbs } = useBreadcrumbs(); const { pushToast } = useToastActions(); const [activeTab, setActiveTab] = useState("secrets"); // Operator-hidden sub-tabs (UI-only; the secrets APIs stay live for agents). const { hidden: hiddenSettings } = useHiddenSettings(); const hideVaultsTab = hidesCompanySection(hiddenSettings, "company.secrets.vaults"); const hideProposalsTab = hidesCompanySection(hiddenSettings, "company.secrets.proposals"); useEffect(() => { if ((activeTab === "vaults" && hideVaultsTab) || (activeTab === "proposals" && hideProposalsTab)) { setActiveTab("secrets"); } }, [activeTab, hideVaultsTab, hideProposalsTab]); const [secretDetailTab, setSecretDetailTab] = useState("details"); const [search, setSearch] = useState(""); const [statusFilter, setStatusFilter] = useState("active"); const [providerFilter, setProviderFilter] = useState("all"); const [providedByFilter, setProvidedByFilter] = useState("all"); const [searchParams, setSearchParams] = useSearchParams(); // The detail sheet is deep-linkable: `?secret=` / // `?definition=` are the source of truth for the current selection, so // every open secret has a shareable URL and Back closes the sheet. const selectedSecretId = searchParams.get("secret"); const selectedDefinitionId = searchParams.get("definition"); const setDetailSelection = useCallback( (secretId: string | null, definitionId: string | null = null) => { setSearchParams((prev) => { const next = new URLSearchParams(prev); if (secretId) next.set("secret", secretId); else next.delete("secret"); if (definitionId) next.set("definition", definitionId); else next.delete("definition"); return next; }); }, [setSearchParams], ); const [usageDialogSecretId, setUsageDialogSecretId] = useState(null); const [createOpen, setCreateOpen] = useState(false); const [importOpen, setImportOpen] = useState(false); const [importInitialVaultId, setImportInitialVaultId] = useState(null); const [secretValueProvider, setSecretValueProvider] = useState("company"); const [createMode, setCreateMode] = useState("managed"); const [editingDefinition, setEditingDefinition] = useState(null); const [createNamePrefix, setCreateNamePrefix] = useState(null); const [createKeyDirty, setCreateKeyDirty] = useState(false); const [createKeyEditable, setCreateKeyEditable] = useState(false); const [createForm, setCreateForm] = useState({ name: "", key: "", value: "", description: "", usageGuidance: "", externalRef: "", provider: "local_encrypted" as SecretProvider, providerConfigId: "", }); const [createError, setCreateError] = useState(null); const [rotateOpen, setRotateOpen] = useState(false); const [rotateMode, setRotateMode] = useState("value"); const [rotateValue, setRotateValue] = useState(""); const [rotateExternalRef, setRotateExternalRef] = useState(""); const [rotateProviderConfigId, setRotateProviderConfigId] = useState(""); const [rotateError, setRotateError] = useState(null); const [deleteConfirm, setDeleteConfirm] = useState(null); const [definitionDeleteConfirm, setDefinitionDeleteConfirm] = useState(null); const [setMyValueFor, setSetMyValueFor] = useState(null); const [vaultDialogOpen, setVaultDialogOpen] = useState(false); const [editingVault, setEditingVault] = useState(null); const [removeVaultConfirm, setRemoveVaultConfirm] = useState(null); const [vaultForm, setVaultForm] = useState(() => emptyProviderVaultForm()); const [vaultError, setVaultError] = useState(null); const [vaultDiscovery, setVaultDiscovery] = useState(null); const [vaultDiscoveryError, setVaultDiscoveryError] = useState(null); const [newFolderOpen, setNewFolderOpen] = useState(false); const [newFolderName, setNewFolderName] = useState(""); const [newFolderError, setNewFolderError] = useState(null); useEffect(() => { setBreadcrumbs([{ label: "Secrets" }]); }, [setBreadcrumbs]); const secretsQuery = useQuery({ queryKey: selectedCompanyId ? queryKeys.secrets.list(selectedCompanyId) : ["secrets", "__disabled__"], queryFn: () => secretsApi.list(selectedCompanyId!), enabled: Boolean(selectedCompanyId), }); const userDefinitionsQuery = useQuery({ queryKey: selectedCompanyId ? queryKeys.secrets.userDefinitions(selectedCompanyId) : ["user-secret-definitions", "__disabled__"], queryFn: () => secretsApi.listUserSecretDefinitions(selectedCompanyId!), enabled: Boolean(selectedCompanyId), }); const myUserSecretsQuery = useQuery({ queryKey: selectedCompanyId ? queryKeys.secrets.myUserSecrets(selectedCompanyId) : ["my-user-secrets", "__disabled__"], queryFn: () => secretsApi.listMyUserSecrets(selectedCompanyId!), enabled: Boolean(selectedCompanyId), }); const providersQuery = useQuery({ queryKey: selectedCompanyId ? queryKeys.secrets.providers(selectedCompanyId) : ["secret-providers", "__disabled__"], queryFn: () => secretsApi.providers(selectedCompanyId!), enabled: Boolean(selectedCompanyId), staleTime: 5 * 60_000, }); const providerHealthQuery = useQuery({ queryKey: selectedCompanyId ? ["secret-provider-health", selectedCompanyId] : ["secret-provider-health", "__disabled__"], queryFn: () => secretsApi.providerHealth(selectedCompanyId!), enabled: Boolean(selectedCompanyId), refetchInterval: 60_000, retry: false, }); const providerConfigsQuery = useQuery({ queryKey: selectedCompanyId ? queryKeys.secrets.providerConfigs(selectedCompanyId) : ["secret-provider-configs", "__disabled__"], queryFn: () => secretsApi.providerConfigs(selectedCompanyId!), enabled: Boolean(selectedCompanyId), retry: false, }); const proposalsQuery = useQuery({ queryKey: selectedCompanyId ? queryKeys.secrets.proposals(selectedCompanyId, "pending") : ["secret-proposals", "__disabled__"], queryFn: () => secretsApi.listProposals(selectedCompanyId!, "pending"), enabled: Boolean(selectedCompanyId) && !hideProposalsTab, }); const secrets = secretsQuery.data ?? EMPTY_SECRETS; const userDefinitions = userDefinitionsQuery.data ?? EMPTY_USER_SECRET_DEFINITIONS; const pendingProposalCount = proposalsQuery.data?.length ?? 0; const myUserSecrets = myUserSecretsQuery.data ?? EMPTY_MY_USER_SECRETS; const providers = providersQuery.data ?? EMPTY_SECRET_PROVIDERS; const providerConfigs = providerConfigsQuery.data ?? EMPTY_PROVIDER_CONFIGS; const selectedSecret = useMemo( () => secrets.find((secret) => secret.id === selectedSecretId) ?? null, [secrets, selectedSecretId], ); const selectedDefinition = useMemo( () => userDefinitions.find((definition) => definition.id === selectedDefinitionId) ?? null, [selectedDefinitionId, userDefinitions], ); const selectedSecretAccessReference = useMemo( () => selectedSecret ? { kind: "company", secret: selectedSecret } : null, [selectedSecret], ); const selectedDefinitionAccessReference = useMemo( () => selectedDefinition ? { kind: "user", definition: selectedDefinition } : null, [selectedDefinition], ); const selectedDefinitionMyEntry = useMemo(() => { if (!selectedDefinition) return null; return myUserSecrets.find((entry) => entry.definition.id === selectedDefinition.id) ?? { definition: selectedDefinition, secret: null, }; }, [myUserSecrets, selectedDefinition]); const usageDialogSecret = useMemo( () => secrets.find((secret) => secret.id === usageDialogSecretId) ?? null, [secrets, usageDialogSecretId], ); const selectedCreateProvider = useMemo( () => providers.find((provider) => provider.id === createForm.provider) ?? null, [providers, createForm.provider], ); const createProviderConfigs = useMemo( () => providerConfigs.filter((config) => config.provider === createForm.provider), [createForm.provider, providerConfigs], ); const selectedCreateProviderConfig = useMemo( () => providerConfigs.find((config) => config.id === createForm.providerConfigId) ?? null, [createForm.providerConfigId, providerConfigs], ); const selectedRotateProviderConfigs = useMemo( () => providerConfigs.filter((config) => config.provider === selectedSecret?.provider), [providerConfigs, selectedSecret?.provider], ); const selectedRotateProviderConfig = useMemo( () => providerConfigs.find((config) => config.id === rotateProviderConfigId) ?? null, [providerConfigs, rotateProviderConfigId], ); const createProviderBlockReason = getCreateProviderBlockReason( selectedCreateProvider, createMode, providerHealthQuery.data ?? null, selectedCreateProviderConfig, ) ?? getProviderConfigBlockReason(selectedCreateProviderConfig); const rotateProviderBlockReason = getProviderConfigBlockReason(selectedRotateProviderConfig); const createProviderHealthText = providerHealthText( selectedCreateProvider, providerHealthQuery.data ?? null, selectedCreateProviderConfig, ); const awsManagedPathPreview = getAwsManagedPathPreview({ provider: selectedCreateProvider, health: providerHealthQuery.data ?? null, companyId: selectedCompanyId ?? "{companyId}", secretKeySource: createForm.key.trim() || createForm.name, }); const unifiedRows = useMemo( () => [ ...secrets.map((secret) => ({ id: `company:${secret.id}`, kind: "company" as const, secret })), ...userDefinitions.map((definition) => ({ id: `user:${definition.id}`, kind: "user" as const, definition, })), ], [secrets, userDefinitions], ); const filteredRows = useMemo(() => { const needle = search.trim().toLowerCase(); return unifiedRows.filter((row) => { const providedBy: SecretValueProvider = row.kind === "company" ? "company" : "user"; const status = row.kind === "company" ? row.secret.status : row.definition.status; if (providedByFilter !== "all" && providedBy !== providedByFilter) return false; if (statusFilter !== "all" && status !== statusFilter) return false; if (providerFilter !== "all" && row.kind === "company" && row.secret.provider !== providerFilter) { return false; } if (!needle) return true; if (row.kind === "company") { return ( row.secret.name.toLowerCase().includes(needle) || row.secret.key.toLowerCase().includes(needle) || (row.secret.description?.toLowerCase().includes(needle) ?? false) || (row.secret.externalRef?.toLowerCase().includes(needle) ?? false) ); } return ( row.definition.name.toLowerCase().includes(needle) || row.definition.key.toLowerCase().includes(needle) || (row.definition.description?.toLowerCase().includes(needle) ?? false) || (row.definition.usageGuidance?.toLowerCase().includes(needle) ?? false) ); }); }, [providedByFilter, providerFilter, search, statusFilter, unifiedRows]); const activeSecretFilterCount = (statusFilter === "active" ? 0 : 1) + (providerFilter === "all" ? 0 : 1) + (providedByFilter === "all" ? 0 : 1); // --- Folder view (PAP-14698) -------------------------------------------- // Folders are derived purely from slash-delimited secret names; there is no // server-side folder record. `?path=` holds the normalized current folder // and is only meaningful on the main Secrets tab (inert on the others). const pathParam = normalizeSecretPath(searchParams.get("path") ?? ""); const folderPath = activeTab === "secrets" ? pathParam : ""; const searching = search.trim().length > 0; const [storedViewMode, setStoredViewMode] = useState(readStoredViewMode); const hasSlashNames = useMemo( () => unifiedRows.some((row) => getSecretPathRowName(row).includes("/")), [unifiedRows], ); // No explicit preference → default to Folders once any name has a slash. const resolvedViewMode: SecretsViewMode = storedViewMode ?? (hasSlashNames ? "folders" : "flat"); // A `?path=` deep link forces folder view for the visit even if the stored // preference is Flat. Search always renders a flat global result set. const effectiveViewMode: SecretsViewMode = folderPath ? "folders" : resolvedViewMode; const showFolderView = effectiveViewMode === "folders" && !searching; const goToFolder = useCallback( (path: string) => { setSearchParams( (prev) => { const next = new URLSearchParams(prev); const normalized = normalizeSecretPath(path); if (normalized) next.set("path", normalized); else next.delete("path"); return next; }, { replace: false }, ); }, [setSearchParams], ); function closeNewFolder() { setNewFolderOpen(false); setNewFolderName(""); setNewFolderError(null); } function stageNewFolder() { const segment = newFolderName.trim(); const error = validateSecretFolderSegment(segment); if (error) { setNewFolderError(error); return; } goToFolder(folderPath ? `${folderPath}/${segment}` : segment); closeNewFolder(); } const setViewMode = useCallback( (mode: SecretsViewMode) => { setStoredViewMode(mode); try { window.localStorage.setItem(SECRETS_VIEW_MODE_STORAGE_KEY, mode); } catch { // Ignore storage failures (private mode / disabled); view still works. } // Flat has no notion of a current folder — leaving it out of the URL. if (mode === "flat") goToFolder(""); }, [goToFolder], ); const folderListing = useMemo( () => buildSecretPathListing(filteredRows, folderPath), [filteredRows, folderPath], ); const breadcrumbs = useMemo(() => buildSecretPathBreadcrumbs(folderPath), [folderPath]); const parentFolderPath = useMemo(() => { const segments = folderPath ? folderPath.split("/") : []; return segments.slice(0, -1).join("/"); }, [folderPath]); const currentFolderSecretCount = folderListing.secrets.length + folderListing.folders.reduce((total, folder) => total + folder.secretCount, 0); const folderRows = showFolderView ? folderListing.folders : []; const secretRows = showFolderView ? folderListing.secrets : filteredRows; const showUpRow = showFolderView && folderPath.length > 0; const usageQuery = useQuery({ queryKey: selectedSecret ? queryKeys.secrets.usage(selectedSecret.id) : ["secrets", "usage", "__disabled__"], queryFn: () => secretsApi.usage(selectedSecret!.id), enabled: Boolean(selectedSecret), }); const eventsQuery = useQuery({ queryKey: selectedSecret ? queryKeys.secrets.accessEvents(selectedSecret.id) : ["secrets", "access-events", "__disabled__"], queryFn: () => secretsApi.accessEvents(selectedSecret!.id), enabled: Boolean(selectedSecret), }); const usageDialogQuery = useQuery({ queryKey: usageDialogSecret ? queryKeys.secrets.usage(usageDialogSecret.id) : ["secrets", "usage-dialog", "__disabled__"], queryFn: () => secretsApi.usage(usageDialogSecret!.id), enabled: Boolean(usageDialogSecret), }); function invalidateAll(extraIds: string[] = []) { if (!selectedCompanyId) return; queryClient.invalidateQueries({ queryKey: queryKeys.secrets.list(selectedCompanyId) }); queryClient.invalidateQueries({ queryKey: queryKeys.secrets.userDefinitions(selectedCompanyId) }); queryClient.invalidateQueries({ queryKey: queryKeys.secrets.myUserSecrets(selectedCompanyId) }); queryClient.invalidateQueries({ queryKey: queryKeys.secrets.providerConfigs(selectedCompanyId) }); for (const id of extraIds) { queryClient.invalidateQueries({ queryKey: queryKeys.secrets.usage(id) }); queryClient.invalidateQueries({ queryKey: queryKeys.secrets.accessEvents(id) }); queryClient.invalidateQueries({ queryKey: queryKeys.secrets.userDefinitionCoverage(selectedCompanyId, id) }); } } function openCreateSecret() { const prefix = folderPath ? `${folderPath}/` : null; setEditingDefinition(null); setCreateNamePrefix(prefix); setSecretValueProvider("company"); setCreateMode("managed"); setCreateKeyDirty(false); setCreateKeyEditable(false); setCreateError(null); setCreateForm({ name: prefix ?? "", key: "", value: "", description: "", usageGuidance: "", externalRef: "", provider: "local_encrypted", providerConfigId: getDefaultProviderConfigId(providerConfigs, "local_encrypted"), }); setCreateOpen(true); } function openEditDefinition(definition: UserSecretDefinition) { setEditingDefinition(definition); setCreateNamePrefix(null); setSecretValueProvider("user"); setCreateMode("managed"); setCreateKeyDirty(true); setCreateKeyEditable(false); setCreateError(null); setCreateForm({ name: definition.name, key: definition.key, value: "", description: definition.description ?? "", usageGuidance: definition.usageGuidance ?? "", externalRef: "", provider: "local_encrypted", providerConfigId: "", }); setCreateOpen(true); } const createMutation = useMutation({ mutationFn: async () => { const sharedDefinitionPayload = { name: createForm.name.trim(), description: createForm.description.trim() || null, usageGuidance: createForm.usageGuidance.trim() || null, }; if (editingDefinition) { const definition = await secretsApi.updateUserSecretDefinition( selectedCompanyId!, editingDefinition.id, sharedDefinitionPayload, ); return { kind: "user" as const, item: definition, action: "updated" as const }; } if (secretValueProvider === "user") { const definition = await secretsApi.createUserSecretDefinition(selectedCompanyId!, { ...sharedDefinitionPayload, key: createForm.key.trim(), status: "active", }); return { kind: "user" as const, item: definition, action: "created" as const }; } const input: CreateSecretInput = { name: createForm.name.trim(), provider: createForm.provider, providerConfigId: createForm.providerConfigId || null, managedMode: createMode === "external" ? "external_reference" : "paperclip_managed", description: createForm.description.trim() || null, }; if (createForm.key.trim()) input.key = createForm.key.trim(); if (createMode === "managed") { input.value = createForm.value; } else { input.externalRef = createForm.externalRef.trim(); } const secret = await secretsApi.create(selectedCompanyId!, input); return { kind: "company" as const, item: secret, action: "created" as const }; }, onSuccess: (result) => { pushToast({ title: result.kind === "company" ? "Secret created" : result.action === "updated" ? "User-provided secret updated" : "User-provided secret created", body: result.item.name, tone: "success", }); setCreateOpen(false); setEditingDefinition(null); setCreateNamePrefix(null); setSecretValueProvider("company"); setCreateKeyDirty(false); setCreateKeyEditable(false); setCreateForm({ name: "", key: "", value: "", description: "", usageGuidance: "", externalRef: "", provider: createForm.provider, providerConfigId: getDefaultProviderConfigId(providerConfigs, createForm.provider), }); setCreateError(null); if (result.kind === "company") { setDetailSelection(result.item.id); invalidateAll([result.item.id]); } else { setDetailSelection(null, result.item.id); invalidateAll([result.item.id]); } }, onError: (error) => { setCreateError(error); }, }); const rotateMutation = useMutation({ mutationFn: () => { if (!selectedSecret) throw new Error("Select a secret first"); if (selectedSecret.managedMode === "external_reference" && rotateMode === "reference") { return secretsApi.rotate(selectedSecret.id, { externalRef: rotateExternalRef.trim() || selectedSecret.externalRef || undefined, providerConfigId: rotateProviderConfigId || null, }); } return secretsApi.rotate(selectedSecret.id, { value: rotateValue, providerConfigId: rotateProviderConfigId || null, }); }, onSuccess: (updated) => { pushToast({ title: "Rotated", body: `${updated.name} → v${updated.latestVersion}`, tone: "success" }); setRotateOpen(false); setRotateValue(""); setRotateExternalRef(""); setRotateProviderConfigId(""); setRotateError(null); invalidateAll([updated.id]); }, onError: (error) => { setRotateError(error instanceof Error ? error.message : "Rotate failed"); }, }); const statusMutation = useMutation({ mutationFn: ({ id, status }: { id: string; status: SecretStatus }) => { switch (status) { case "active": return secretsApi.enable(id); case "disabled": return secretsApi.disable(id); case "archived": return secretsApi.archive(id); default: return secretsApi.update(id, { status }); } }, onSuccess: (updated) => { pushToast({ title: `Secret ${updated.status}`, body: updated.name, tone: "info" }); invalidateAll([updated.id]); }, onError: (error) => { pushToast({ title: "Status update failed", body: error instanceof Error ? error.message : "Try again", tone: "error", }); }, }); const definitionStatusMutation = useMutation({ mutationFn: ({ definition, status }: { definition: UserSecretDefinition; status: SecretStatus }) => secretsApi.updateUserSecretDefinition(selectedCompanyId!, definition.id, { status }), onSuccess: (updated) => { pushToast({ title: `User-provided secret ${updated.status}`, body: updated.name, tone: "info" }); invalidateAll([updated.id]); }, onError: (error) => { pushToast({ title: "Status update failed", body: error instanceof Error ? error.message : "Try again", tone: "error", }); }, }); const deleteMutation = useMutation({ mutationFn: (id: string) => secretsApi.remove(id), onSuccess: (_response, id) => { pushToast({ title: "Secret deleted", tone: "info" }); setDeleteConfirm(null); if (selectedSecretId === id) setDetailSelection(null); invalidateAll([id]); }, onError: (error) => { pushToast({ title: "Delete failed", body: error instanceof Error ? error.message : "Try again", tone: "error", }); }, }); const deleteDefinitionMutation = useMutation({ mutationFn: (definition: UserSecretDefinition) => secretsApi.removeUserSecretDefinition(selectedCompanyId!, definition.id), onSuccess: (_response, definition) => { pushToast({ title: "User-provided secret removed", body: definition.name, tone: "info" }); setDefinitionDeleteConfirm(null); if (selectedDefinitionId === definition.id) setDetailSelection(null); invalidateAll([definition.id]); }, onError: (error) => { pushToast({ title: "Delete failed", body: error instanceof Error ? error.message : "Try again", tone: "error", }); }, }); const saveVaultMutation = useMutation({ mutationFn: () => { const data: CreateSecretProviderConfigInput | UpdateSecretProviderConfigInput = { displayName: vaultForm.displayName.trim(), status: vaultForm.status, isDefault: vaultForm.isDefault, config: buildProviderVaultConfig(vaultForm), }; if (editingVault) { return secretsApi.updateProviderConfig(editingVault.id, data); } return secretsApi.createProviderConfig(selectedCompanyId!, { ...(data as UpdateSecretProviderConfigInput), provider: vaultForm.provider, } as CreateSecretProviderConfigInput); }, onSuccess: (saved) => { pushToast({ title: editingVault ? "Provider vault updated" : "Provider vault created", body: saved.displayName, tone: "success" }); setVaultDialogOpen(false); setEditingVault(null); setVaultForm(emptyProviderVaultForm()); setVaultError(null); invalidateAll(); }, onError: (error) => { setVaultError(error instanceof ApiError ? error.message : (error as Error).message); }, }); const discoverVaultMutation = useMutation({ mutationFn: () => secretsApi.providerConfigDiscoveryPreview(selectedCompanyId!, { provider: "aws_secrets_manager", config: buildProviderVaultConfig(vaultForm), query: getAwsProviderVaultDiscoveryQuery(vaultForm), pageSize: 25, }), onSuccess: (preview) => { setVaultDiscovery(preview); setVaultDiscoveryError(null); }, onError: (error) => { setVaultDiscovery(null); setVaultDiscoveryError(error); }, }); const disableVaultMutation = useMutation({ mutationFn: (id: string) => secretsApi.disableProviderConfig(id), onSuccess: (updated) => { pushToast({ title: "Provider vault disabled", body: updated.displayName, tone: "info" }); invalidateAll(); }, onError: (error) => { pushToast({ title: "Disable failed", body: error instanceof Error ? error.message : "Try again", tone: "error", }); }, }); const removeVaultMutation = useMutation({ mutationFn: (id: string) => secretsApi.removeProviderConfig(id), onSuccess: (removed) => { pushToast({ title: "Provider vault removed", body: `${removed.displayName} was removed from Paperclip only.`, tone: "info", }); setRemoveVaultConfirm(null); invalidateAll(); }, onError: (error) => { pushToast({ title: "Remove failed", body: error instanceof Error ? error.message : "Try again", tone: "error", }); }, }); const defaultVaultMutation = useMutation({ mutationFn: (id: string) => secretsApi.setDefaultProviderConfig(id), onSuccess: (updated) => { pushToast({ title: "Default vault set", body: updated.displayName, tone: "success" }); invalidateAll(); }, onError: (error) => { pushToast({ title: "Default update failed", body: error instanceof Error ? error.message : "Try again", tone: "error", }); }, }); const healthVaultMutation = useMutation({ mutationFn: (id: string) => secretsApi.checkProviderConfigHealth(id), onSuccess: (health) => { pushToast({ title: "Health checked", body: health.message, tone: health.status === "error" ? "error" : "info" }); invalidateAll(); }, onError: (error) => { pushToast({ title: "Health check failed", body: error instanceof Error ? error.message : "Try again", tone: "error", }); }, }); useEffect(() => { if (!createOpen || providers.length === 0) return; const currentBlockReason = getCreateProviderBlockReason( providers.find((provider) => provider.id === createForm.provider) ?? null, createMode, providerHealthQuery.data ?? null, providerConfigs.find((config) => config.id === createForm.providerConfigId) ?? null, ); if (!currentBlockReason) return; const replacement = findCreateProviderReplacement({ providers, providerConfigs, currentProvider: createForm.provider, mode: createMode, health: providerHealthQuery.data ?? null, }); if (replacement && replacement.id !== createForm.provider) { setCreateForm((current) => ({ ...current, provider: replacement.id, providerConfigId: getDefaultProviderConfigId(providerConfigs, replacement.id), })); } }, [createForm.provider, createMode, createOpen, providerConfigs, providerHealthQuery.data, providers]); useEffect(() => { if (!createOpen) return; const current = providerConfigs.find((config) => config.id === createForm.providerConfigId); if (current?.provider === createForm.provider) return; const nextProviderConfigId = getDefaultProviderConfigId(providerConfigs, createForm.provider); if (nextProviderConfigId === createForm.providerConfigId) return; setCreateForm((form) => ({ ...form, providerConfigId: nextProviderConfigId, })); }, [createForm.provider, createForm.providerConfigId, createOpen, providerConfigs]); useEffect(() => { if (!rotateOpen || !selectedSecret) return; setRotateProviderConfigId( selectedSecret.providerConfigId ?? getDefaultProviderConfigId(providerConfigs, selectedSecret.provider), ); }, [providerConfigs, rotateOpen, selectedSecret]); function openCreateVault(provider: SecretProvider = "local_encrypted") { setEditingVault(null); setVaultForm(emptyProviderVaultForm(provider)); setVaultError(null); setVaultDiscovery(null); setVaultDiscoveryError(null); setVaultDialogOpen(true); } function openEditVault(config: CompanySecretProviderConfig) { setEditingVault(config); setVaultForm(providerVaultFormFromConfig(config)); setVaultError(null); setVaultDiscovery(null); setVaultDiscoveryError(null); setVaultDialogOpen(true); } function openImportFromVault(config?: CompanySecretProviderConfig | null) { setImportInitialVaultId(config?.id ?? null); setImportOpen(true); } function applyVaultDiscoveryCandidate(candidate: SecretProviderConfigDiscoveryCandidate) { if (candidate.provider !== "aws_secrets_manager") return; const config = candidate.config as Record; setVaultForm((current) => ({ ...current, displayName: current.displayName.trim() ? current.displayName : candidate.displayName, region: providerConfigValue(config, "region"), namespace: providerConfigValue(config, "namespace"), secretNamePrefix: providerConfigValue(config, "secretNamePrefix"), kmsKeyId: providerConfigValue(config, "kmsKeyId"), ownerTag: providerConfigValue(config, "ownerTag"), environmentTag: providerConfigValue(config, "environmentTag"), })); } function openCompanySecret(secret: CompanySecret) { setSecretDetailTab("details"); setDetailSelection(secret.id); } function openUserDefinition(definition: UserSecretDefinition) { setSecretDetailTab("details"); setDetailSelection(null, definition.id); } function secretSupportsExternalValueWrite(secret: CompanySecret) { return ( secret.managedMode === "external_reference" && Boolean(secret.externalRef) && Boolean(providers.find((provider) => provider.id === secret.provider)?.supportsExternalValueWrites) ); } function rotateActionLabel(secret: CompanySecret) { return secret.managedMode === "external_reference" && !secretSupportsExternalValueWrite(secret) ? "Update reference" : "Update value"; } function openRotateSecret(secret: CompanySecret) { openCompanySecret(secret); setRotateOpen(true); setRotateMode( secret.managedMode === "external_reference" && !secretSupportsExternalValueWrite(secret) ? "reference" : "value", ); setRotateValue(""); setRotateExternalRef(""); setRotateProviderConfigId( secret.providerConfigId ?? getDefaultProviderConfigId(providerConfigs, secret.provider), ); setRotateError(null); } function copyDetailLink() { void copyTextToClipboard(window.location.href) .then(() => pushToast({ title: "Link copied", body: "Deep link to this secret", tone: "success" })) .catch((error) => pushToast({ title: "Copy failed", body: error instanceof Error ? error.message : "Unable to copy link", tone: "error", }), ); } function copySecretKey(key: string) { void copyTextToClipboard(key) .then(() => pushToast({ title: "Secret key copied", body: key, tone: "success" })) .catch((error) => pushToast({ title: "Copy failed", body: error instanceof Error ? error.message : "Unable to copy secret key", tone: "error", }), ); } function renderRowActions(row: UnifiedSecretRow) { const name = row.kind === "company" ? row.secret.name : row.definition.name; return ( { if (row.kind === "company") openCompanySecret(row.secret); else openUserDefinition(row.definition); }} > View details {row.kind === "company" ? ( <> setUsageDialogSecretId(row.secret.id)}> View references ({row.secret.referenceCount ?? 0}) openRotateSecret(row.secret)}> {rotateActionLabel(row.secret)} statusMutation.mutate({ id: row.secret.id, status: row.secret.status === "active" ? "disabled" : "active", }) } > {row.secret.status === "active" ? : } {row.secret.status === "active" ? "Disable" : "Activate"} statusMutation.mutate({ id: row.secret.id, status: row.secret.status === "archived" ? "active" : "archived", }) } > {row.secret.status === "archived" ? ( ) : ( )} {row.secret.status === "archived" ? "Unarchive" : "Archive"} setDeleteConfirm(row.secret)}> Delete secret ) : ( <> setSetMyValueFor( myUserSecrets.find((entry) => entry.definition.id === row.definition.id) ?? { definition: row.definition, secret: null, }, ) } > {myUserSecrets.find((entry) => entry.definition.id === row.definition.id)?.secret ? "Update my value" : "Set my value"} openEditDefinition(row.definition)}> Edit definition definitionStatusMutation.mutate({ definition: row.definition, status: row.definition.status === "active" ? "disabled" : "active", }) } > {row.definition.status === "active" ? ( ) : ( )} {row.definition.status === "active" ? "Disable" : "Activate"} definitionStatusMutation.mutate({ definition: row.definition, status: row.definition.status === "archived" ? "active" : "archived", }) } > {row.definition.status === "archived" ? ( ) : ( )} {row.definition.status === "archived" ? "Unarchive" : "Archive"} setDefinitionDeleteConfirm(row.definition)}> Delete definition )} ); } function folderLinkTo(path: string) { const params = new URLSearchParams(searchParams); const normalized = normalizeSecretPath(path); if (normalized) params.set("path", normalized); else params.delete("path"); const qs = params.toString(); return { search: qs ? `?${qs}` : "" }; } /** Secret-name treatment: raw in flat view, muted-path/bold-leaf otherwise. */ function renderSecretName(name: string) { if (searching) return ; if (showFolderView) return ; return {name}; } function renderFolderTableRow(folder: SecretPathFolder) { return (
{folder.name}
{formatSecretPathCounts(folder.secretCount, folder.folderCount)}