// src/specs/types.ts
function specAuthToConnectorAuth(auth) {
  if (auth.mode === "api_key") return "api_key";
  if (auth.mode === "oauth2") return "oauth2";
  if (auth.mode === "none") return "none";
  return "custom";
}

// src/specs/families.ts
var INTEGRATION_FAMILIES = {
  google: {
    id: "google",
    title: "Google OAuth",
    authMode: "oauth2",
    consoleUrl: "https://console.cloud.google.com/apis/credentials",
    authorizationUrl: "https://accounts.google.com/o/oauth2/v2/auth",
    tokenUrl: "https://oauth2.googleapis.com/token",
    redirectUriTemplate: "https://{host}/api/integrations/oauth/google/callback",
    credentialFields: [
      { label: "Client ID", env: "GOOGLE_OAUTH_CLIENT_ID", description: "Google OAuth client ID.", example: "1234567890-abc.apps.googleusercontent.com", regex: "^[0-9]+-[a-zA-Z0-9_-]+\\.apps\\.googleusercontent\\.com$", secret: false },
      { label: "Client Secret", env: "GOOGLE_OAUTH_CLIENT_SECRET", description: "Google OAuth client secret.", example: "GOCSPX-...", secret: true }
    ],
    consoleSteps: [
      { id: "project", title: "Select project", detail: "Open Google Cloud Console and select the project that owns the OAuth client." },
      { id: "consent", title: "Configure consent screen", detail: "Configure OAuth consent, app name, support email, and publishing status appropriate for the deployment." },
      { id: "client", title: "Create web client", detail: "Create an OAuth client of type Web application." },
      { id: "redirect", title: "Add redirect URI", detail: "Add {redirectUri} as an authorized redirect URI.", copyValue: "{redirectUri}" },
      { id: "scopes", title: "Add scopes", detail: "Add the provider scopes listed in this spec." }
    ],
    knownQuirks: [
      { id: "offline-access", severity: "warning", message: "Use access_type=offline and prompt=consent when refresh tokens are required." },
      { id: "verification", severity: "warning", message: "Sensitive or restricted scopes may require Google verification before broad external use." }
    ],
    lifecycle: { supportsRefresh: true, supportsRevoke: true, supportsIncrementalAuth: true, recommendedHealthcheckIntervalHours: 24 }
  },
  "microsoft-graph": {
    id: "microsoft-graph",
    title: "Microsoft Graph OAuth",
    authMode: "oauth2",
    consoleUrl: "https://entra.microsoft.com/#view/Microsoft_AAD_RegisteredApps/ApplicationsListBlade",
    authorizationUrl: "https://login.microsoftonline.com/common/oauth2/v2.0/authorize",
    tokenUrl: "https://login.microsoftonline.com/common/oauth2/v2.0/token",
    redirectUriTemplate: "https://{host}/api/integrations/oauth/microsoft/callback",
    credentialFields: [
      { label: "Client ID", env: "MS_OAUTH_CLIENT_ID", description: "Microsoft Entra application client ID.", example: "00000000-0000-0000-0000-000000000000", regex: "^[0-9a-fA-F-]{36}$", secret: false },
      { label: "Client Secret", env: "MS_OAUTH_CLIENT_SECRET", description: "Microsoft Entra client secret value.", secret: true }
    ],
    consoleSteps: [
      { id: "app", title: "Register app", detail: "Create or open an app registration in Microsoft Entra." },
      { id: "redirect", title: "Add redirect URI", detail: "Add {redirectUri} as a Web redirect URI.", copyValue: "{redirectUri}" },
      { id: "secret", title: "Create secret", detail: "Create a client secret and store the secret value, not the secret ID." },
      { id: "permissions", title: "Add Graph permissions", detail: "Add the delegated Graph scopes listed in this spec and grant admin consent where required." }
    ],
    knownQuirks: [
      { id: "tenant-common", severity: "info", message: "The common tenant supports multi-tenant OAuth; single-tenant deployments should override the tenant segment." },
      { id: "admin-consent", severity: "warning", message: "Some Graph scopes require tenant admin consent." }
    ],
    lifecycle: { supportsRefresh: true, supportsRevoke: true, supportsIncrementalAuth: true, recommendedHealthcheckIntervalHours: 24 }
  },
  atlassian: {
    id: "atlassian",
    title: "Atlassian OAuth",
    authMode: "oauth2",
    consoleUrl: "https://developer.atlassian.com/console/myapps/",
    authorizationUrl: "https://auth.atlassian.com/authorize",
    tokenUrl: "https://auth.atlassian.com/oauth/token",
    redirectUriTemplate: "https://{host}/api/integrations/oauth/atlassian/callback",
    credentialFields: [
      { label: "Client ID", description: "Atlassian OAuth client ID.", secret: false },
      { label: "Client Secret", description: "Atlassian OAuth client secret.", secret: true }
    ],
    consoleSteps: [
      { id: "app", title: "Create OAuth app", detail: "Create an OAuth 2.0 app in the Atlassian developer console." },
      { id: "redirect", title: "Add callback URL", detail: "Add {redirectUri} as the callback URL.", copyValue: "{redirectUri}" },
      { id: "apis", title: "Enable APIs", detail: "Enable the Jira or Confluence APIs required by this connector." }
    ],
    lifecycle: { supportsRefresh: true, supportsRevoke: false, supportsIncrementalAuth: false, recommendedHealthcheckIntervalHours: 24 }
  },
  salesforce: {
    id: "salesforce",
    title: "Salesforce OAuth",
    authMode: "oauth2",
    consoleUrl: "https://login.salesforce.com",
    authorizationUrl: "https://login.salesforce.com/services/oauth2/authorize",
    tokenUrl: "https://login.salesforce.com/services/oauth2/token",
    redirectUriTemplate: "https://{host}/api/integrations/oauth/salesforce/callback",
    credentialFields: [
      { label: "Client ID", env: "SALESFORCE_OAUTH_CLIENT_ID", description: "Salesforce connected app consumer key.", secret: false },
      { label: "Client Secret", env: "SALESFORCE_OAUTH_CLIENT_SECRET", description: "Salesforce connected app consumer secret.", secret: true }
    ],
    consoleSteps: [
      { id: "connected-app", title: "Create connected app", detail: "Create a Salesforce connected app with OAuth enabled." },
      { id: "callback", title: "Add callback URL", detail: "Add {redirectUri} as the callback URL.", copyValue: "{redirectUri}" },
      { id: "scopes", title: "Select scopes", detail: "Add api and refresh_token/offline_access, plus any connector-specific scopes." }
    ],
    knownQuirks: [
      { id: "instance-url", severity: "critical", message: "Runtime calls must use the instance_url returned by the token response." }
    ],
    lifecycle: { supportsRefresh: true, supportsRevoke: true, supportsIncrementalAuth: false, recommendedHealthcheckIntervalHours: 24 }
  },
  hubspot: {
    id: "hubspot",
    title: "HubSpot OAuth",
    authMode: "oauth2",
    consoleUrl: "https://developers.hubspot.com/",
    authorizationUrl: "https://app.hubspot.com/oauth/authorize",
    tokenUrl: "https://api.hubapi.com/oauth/v1/token",
    redirectUriTemplate: "https://{host}/api/integrations/oauth/hubspot/callback",
    credentialFields: [
      { label: "Client ID", env: "HUBSPOT_OAUTH_CLIENT_ID", description: "HubSpot app client ID.", secret: false },
      { label: "Client Secret", env: "HUBSPOT_OAUTH_CLIENT_SECRET", description: "HubSpot app client secret.", secret: true }
    ],
    consoleSteps: [
      { id: "app", title: "Create private/public app", detail: "Create a HubSpot app and configure OAuth." },
      { id: "redirect", title: "Add redirect URL", detail: "Add {redirectUri} to the app redirect URLs.", copyValue: "{redirectUri}" },
      { id: "scopes", title: "Add CRM scopes", detail: "Add the CRM object scopes listed in this spec." }
    ],
    lifecycle: { supportsRefresh: true, supportsRevoke: true, supportsIncrementalAuth: false, recommendedHealthcheckIntervalHours: 24 }
  },
  slack: {
    id: "slack",
    title: "Slack OAuth",
    authMode: "oauth2",
    consoleUrl: "https://api.slack.com/apps",
    authorizationUrl: "https://slack.com/oauth/v2/authorize",
    tokenUrl: "https://slack.com/api/oauth.v2.access",
    redirectUriTemplate: "https://{host}/api/integrations/oauth/slack/callback",
    credentialFields: [
      { label: "Client ID", env: "SLACK_OAUTH_CLIENT_ID", description: "Slack app client ID.", secret: false },
      { label: "Client Secret", env: "SLACK_OAUTH_CLIENT_SECRET", description: "Slack app client secret.", secret: true }
    ],
    consoleSteps: [
      { id: "app", title: "Create Slack app", detail: "Create or open a Slack app." },
      { id: "redirect", title: "Add redirect URL", detail: "Add {redirectUri} under OAuth & Permissions.", copyValue: "{redirectUri}" },
      { id: "scopes", title: "Add bot scopes", detail: "Add the bot token scopes listed in this spec and reinstall the app." }
    ],
    knownQuirks: [
      { id: "bot-token", severity: "info", message: "Slack usually returns a bot access token; refresh tokens require token rotation." }
    ],
    lifecycle: { supportsRefresh: false, supportsRevoke: true, supportsIncrementalAuth: false, recommendedHealthcheckIntervalHours: 24 }
  },
  notion: {
    id: "notion",
    title: "Notion OAuth",
    authMode: "oauth2",
    consoleUrl: "https://www.notion.so/my-integrations",
    authorizationUrl: "https://api.notion.com/v1/oauth/authorize",
    tokenUrl: "https://api.notion.com/v1/oauth/token",
    redirectUriTemplate: "https://{host}/api/integrations/oauth/notion/callback",
    credentialFields: [
      { label: "Client ID", env: "NOTION_OAUTH_CLIENT_ID", description: "Notion integration OAuth client ID.", secret: false },
      { label: "Client Secret", env: "NOTION_OAUTH_CLIENT_SECRET", description: "Notion integration OAuth client secret.", secret: true }
    ],
    consoleSteps: [
      { id: "integration", title: "Create integration", detail: "Create a Notion public integration." },
      { id: "redirect", title: "Add redirect URI", detail: "Add {redirectUri} as the redirect URI.", copyValue: "{redirectUri}" },
      { id: "capabilities", title: "Select capabilities", detail: "Enable read/update/insert capabilities matching this connector." }
    ],
    lifecycle: { supportsRefresh: true, supportsRevoke: true, supportsIncrementalAuth: false, recommendedHealthcheckIntervalHours: 24 }
  },
  "standard-oauth2": {
    id: "standard-oauth2",
    title: "Standard OAuth 2.0",
    authMode: "oauth2",
    redirectUriTemplate: "https://{host}/api/integrations/oauth/{kind}/callback",
    credentialFields: [
      { label: "Client ID", description: "OAuth client ID.", secret: false },
      { label: "Client Secret", description: "OAuth client secret.", secret: true }
    ],
    consoleSteps: [
      { id: "app", title: "Create OAuth app", detail: "Create an OAuth app in the provider console." },
      { id: "redirect", title: "Add redirect URI", detail: "Add {redirectUri} as an allowed redirect URI.", copyValue: "{redirectUri}" },
      { id: "scopes", title: "Add scopes", detail: "Add the scopes listed in this spec." }
    ],
    lifecycle: { supportsRefresh: true, supportsRevoke: false, supportsIncrementalAuth: false, recommendedHealthcheckIntervalHours: 24 }
  },
  "api-key": {
    id: "api-key",
    title: "API key",
    authMode: "api_key",
    credentialFields: [
      { label: "API Key", description: "Provider API key or token.", example: "sk_...", secret: true }
    ],
    consoleSteps: [
      { id: "token", title: "Create token", detail: "Create an API key/token in the provider console with the minimum required permissions." }
    ],
    lifecycle: { supportsRefresh: false, supportsRevoke: true, supportsIncrementalAuth: false, recommendedHealthcheckIntervalHours: 24 }
  },
  hmac: {
    id: "hmac",
    title: "HMAC secret",
    authMode: "hmac",
    credentialFields: [
      { label: "Signing Secret", description: "Webhook signing secret.", secret: true }
    ],
    consoleSteps: [
      { id: "secret", title: "Configure signing secret", detail: "Configure the shared signing secret in the sender and receiver." }
    ],
    lifecycle: { supportsRefresh: false, supportsRevoke: true, supportsIncrementalAuth: false, recommendedHealthcheckIntervalHours: 24 }
  },
  none: {
    id: "none",
    title: "No authentication",
    authMode: "none",
    credentialFields: [],
    consoleSteps: [
      { id: "configure", title: "Configure endpoint", detail: "No provider credentials are required." }
    ],
    lifecycle: { supportsRefresh: false, supportsRevoke: false, supportsIncrementalAuth: false }
  }
};
function getIntegrationFamily(id) {
  return INTEGRATION_FAMILIES[id];
}

// src/coverage-catalog.ts
var DEFAULT_PROVIDER_KINDS = ["first_party", "nango", "pipedream", "tangle_catalog", "custom"];
var COVERAGE_SPECS = [
  ["gmail", "Gmail", "email", "email", "tier_0", "email,google,workspace,inbox"],
  ["outlook-mail", "Outlook Mail", "email", "email", "tier_0", "email,microsoft,office,inbox"],
  ["google-calendar", "Google Calendar", "calendar", "calendar", "tier_0", "calendar,google,workspace,scheduling"],
  ["outlook-calendar", "Outlook Calendar", "calendar", "calendar", "tier_0", "calendar,microsoft,office,scheduling"],
  ["slack", "Slack", "chat", "chat", "tier_0", "chat,collaboration,internal-comms"],
  ["microsoft-teams", "Microsoft Teams", "chat", "chat", "tier_0", "chat,microsoft,collaboration"],
  ["google-drive", "Google Drive", "storage", "storage", "tier_0", "files,google,workspace,storage"],
  ["onedrive", "OneDrive", "storage", "storage", "tier_0", "files,microsoft,office,storage"],
  ["dropbox", "Dropbox", "storage", "storage", "tier_1", "files,storage"],
  ["box", "Box", "storage", "storage", "tier_1", "files,enterprise,storage"],
  ["google-docs", "Google Docs", "docs", "docs", "tier_0", "docs,google,workspace"],
  ["google-sheets", "Google Sheets", "database", "database", "tier_0", "sheets,spreadsheet,google,database"],
  ["microsoft-excel", "Microsoft Excel", "database", "database", "tier_0", "sheets,spreadsheet,microsoft,database"],
  ["notion", "Notion", "docs", "docs", "tier_0", "docs,wiki,knowledge"],
  ["airtable", "Airtable", "database", "database", "tier_0", "database,spreadsheet,ops"],
  ["coda", "Coda", "docs", "docs", "tier_1", "docs,wiki,ops"],
  ["confluence", "Confluence", "docs", "docs", "tier_1", "docs,wiki,atlassian"],
  ["sharepoint", "SharePoint", "storage", "storage", "tier_1", "files,microsoft,enterprise"],
  ["hubspot", "HubSpot", "crm", "crm", "tier_0", "crm,sales,marketing"],
  ["salesforce", "Salesforce", "crm", "crm", "tier_0", "crm,sales,enterprise"],
  ["pipedrive", "Pipedrive", "crm", "crm", "tier_1", "crm,sales"],
  ["zoho-crm", "Zoho CRM", "crm", "crm", "tier_1", "crm,sales"],
  ["close", "Close", "crm", "crm", "tier_1", "crm,sales"],
  ["attio", "Attio", "crm", "crm", "tier_1", "crm,sales,startups"],
  ["linear", "Linear", "workflow", "project", "tier_0", "project,engineering,tickets"],
  ["jira", "Jira", "workflow", "project", "tier_0", "project,engineering,tickets,atlassian"],
  ["github", "GitHub", "workflow", "dev", "tier_0", "code,dev,issues,git"],
  ["gitlab", "GitLab", "workflow", "dev", "tier_1", "code,dev,issues,git"],
  ["bitbucket", "Bitbucket", "workflow", "dev", "tier_2", "code,dev,git,atlassian"],
  ["asana", "Asana", "workflow", "project", "tier_1", "project,tasks"],
  ["trello", "Trello", "workflow", "project", "tier_1", "project,tasks,atlassian"],
  ["monday", "monday.com", "workflow", "project", "tier_1", "project,tasks,ops"],
  ["clickup", "ClickUp", "workflow", "project", "tier_1", "project,tasks,ops"],
  ["basecamp", "Basecamp", "workflow", "project", "tier_2", "project,tasks"],
  ["zendesk", "Zendesk", "crm", "support", "tier_0", "support,tickets,customer-success"],
  ["intercom", "Intercom", "crm", "support", "tier_0", "support,chat,customer-success"],
  ["freshdesk", "Freshdesk", "crm", "support", "tier_1", "support,tickets"],
  ["helpscout", "Help Scout", "crm", "support", "tier_1", "support,tickets"],
  ["front", "Front", "email", "support", "tier_1", "support,email,shared-inbox"],
  ["gorgias", "Gorgias", "crm", "support", "tier_1", "support,ecommerce"],
  ["stripe", "Stripe", "workflow", "finance", "tier_0", "payments,billing,finance"],
  ["quickbooks", "QuickBooks", "workflow", "finance", "tier_0", "accounting,finance"],
  ["xero", "Xero", "workflow", "finance", "tier_1", "accounting,finance"],
  ["netsuite", "NetSuite", "workflow", "finance", "tier_1", "erp,finance,enterprise"],
  ["sage", "Sage", "workflow", "finance", "tier_2", "accounting,finance"],
  ["plaid", "Plaid", "workflow", "finance", "tier_1", "banking,finance"],
  ["shopify", "Shopify", "workflow", "commerce", "tier_0", "ecommerce,orders,commerce"],
  ["woocommerce", "WooCommerce", "workflow", "commerce", "tier_1", "ecommerce,orders,wordpress"],
  ["bigcommerce", "BigCommerce", "workflow", "commerce", "tier_1", "ecommerce,orders"],
  ["amazon-seller-central", "Amazon Seller Central", "workflow", "commerce", "tier_1", "marketplace,ecommerce"],
  ["ebay", "eBay", "workflow", "commerce", "tier_2", "marketplace,ecommerce"],
  ["etsy", "Etsy", "workflow", "commerce", "tier_2", "marketplace,ecommerce"],
  ["mailchimp", "Mailchimp", "workflow", "marketing", "tier_0", "email-marketing,marketing"],
  ["klaviyo", "Klaviyo", "workflow", "marketing", "tier_0", "email-marketing,ecommerce,marketing"],
  ["marketo", "Marketo", "workflow", "marketing", "tier_1", "marketing,enterprise"],
  ["braze", "Braze", "workflow", "marketing", "tier_1", "marketing,lifecycle"],
  ["customer-io", "Customer.io", "workflow", "marketing", "tier_1", "marketing,lifecycle"],
  ["sendgrid", "SendGrid", "email", "email", "tier_1", "email,transactional"],
  ["postmark", "Postmark", "email", "email", "tier_1", "email,transactional"],
  ["twilio", "Twilio", "chat", "chat", "tier_0", "sms,voice,communications"],
  ["discord", "Discord", "chat", "chat", "tier_1", "chat,community"],
  ["telegram", "Telegram", "chat", "chat", "tier_1", "chat,community"],
  ["whatsapp-business", "WhatsApp Business", "chat", "chat", "tier_1", "chat,meta,customer-comms"],
  ["facebook-pages", "Facebook Pages", "workflow", "marketing", "tier_1", "social,meta,marketing"],
  ["instagram-business", "Instagram Business", "workflow", "marketing", "tier_1", "social,meta,marketing"],
  ["linkedin", "LinkedIn", "workflow", "sales", "tier_1", "social,sales,gtm"],
  ["x-twitter", "X / Twitter", "workflow", "marketing", "tier_1", "social,marketing"],
  ["youtube", "YouTube", "storage", "storage", "tier_1", "video,content"],
  ["tiktok", "TikTok", "workflow", "marketing", "tier_2", "social,video,marketing"],
  ["google-analytics", "Google Analytics", "database", "analytics", "tier_0", "analytics,web,marketing"],
  ["mixpanel", "Mixpanel", "database", "analytics", "tier_1", "analytics,product"],
  ["amplitude", "Amplitude", "database", "analytics", "tier_1", "analytics,product"],
  ["segment", "Segment", "database", "analytics", "tier_1", "analytics,cdp"],
  ["snowflake", "Snowflake", "database", "database", "tier_0", "warehouse,data"],
  ["bigquery", "BigQuery", "database", "database", "tier_0", "warehouse,google,data"],
  ["redshift", "Redshift", "database", "database", "tier_1", "warehouse,aws,data"],
  ["postgres", "Postgres", "database", "database", "tier_0", "database,sql"],
  ["mysql", "MySQL", "database", "database", "tier_1", "database,sql"],
  ["mongodb", "MongoDB", "database", "database", "tier_1", "database,nosql"],
  ["supabase", "Supabase", "database", "database", "tier_1", "database,postgres"],
  ["firebase", "Firebase", "database", "database", "tier_1", "database,google,app"],
  ["redis", "Redis", "database", "database", "tier_2", "database,cache"],
  ["aws-s3", "Amazon S3", "storage", "storage", "tier_0", "files,aws,storage"],
  ["aws-lambda", "AWS Lambda", "workflow", "dev", "tier_1", "aws,serverless,dev"],
  ["aws-cloudwatch", "AWS CloudWatch", "database", "analytics", "tier_1", "aws,logs,observability"],
  ["google-cloud-storage", "Google Cloud Storage", "storage", "storage", "tier_1", "files,gcp,storage"],
  ["azure-blob-storage", "Azure Blob Storage", "storage", "storage", "tier_1", "files,azure,storage"],
  ["vercel", "Vercel", "workflow", "dev", "tier_1", "deployments,dev"],
  ["netlify", "Netlify", "workflow", "dev", "tier_2", "deployments,dev"],
  ["cloudflare", "Cloudflare", "workflow", "dev", "tier_1", "edge,dev,dns"],
  ["sentry", "Sentry", "workflow", "dev", "tier_1", "errors,observability,dev"],
  ["datadog", "Datadog", "database", "analytics", "tier_1", "observability,logs,metrics"],
  ["new-relic", "New Relic", "database", "analytics", "tier_2", "observability,logs,metrics"],
  ["pagerduty", "PagerDuty", "workflow", "project", "tier_1", "incident,on-call"],
  ["opsgenie", "Opsgenie", "workflow", "project", "tier_2", "incident,on-call,atlassian"],
  ["okta", "Okta", "internal", "workflow", "tier_1", "identity,security"],
  ["auth0", "Auth0", "internal", "workflow", "tier_1", "identity,security"],
  ["workday", "Workday", "workflow", "hr", "tier_1", "hr,finance,enterprise"],
  ["bamboohr", "BambooHR", "workflow", "hr", "tier_1", "hr,people"],
  ["greenhouse", "Greenhouse", "workflow", "hr", "tier_1", "recruiting,hr"],
  ["lever", "Lever", "workflow", "hr", "tier_1", "recruiting,hr"],
  ["gusto", "Gusto", "workflow", "hr", "tier_1", "payroll,hr"],
  ["rippling", "Rippling", "workflow", "hr", "tier_1", "hr,it,identity"],
  ["docusign", "DocuSign", "docs", "docs", "tier_1", "contracts,signature,legal"],
  ["pandadoc", "PandaDoc", "docs", "docs", "tier_1", "contracts,signature,sales"],
  ["hellosign", "Dropbox Sign", "docs", "docs", "tier_2", "contracts,signature"],
  ["clio", "Clio", "workflow", "project", "tier_1", "legal,practice-management"],
  ["ironclad", "Ironclad", "docs", "docs", "tier_1", "legal,contracts"],
  ["lexisnexis", "LexisNexis", "docs", "docs", "tier_2", "legal,research"],
  ["calendly", "Calendly", "calendar", "calendar", "tier_0", "scheduling,calendar"],
  ["cal-com", "Cal.com", "calendar", "calendar", "tier_1", "scheduling,calendar"],
  ["zoom", "Zoom", "calendar", "calendar", "tier_0", "meetings,video,calendar"],
  ["google-meet", "Google Meet", "calendar", "calendar", "tier_1", "meetings,google,video"],
  ["microsoft-graph", "Microsoft Graph", "internal", "workflow", "tier_0", "microsoft,enterprise,identity"],
  ["openai", "OpenAI", "workflow", "ai", "tier_0", "ai,llm"],
  ["anthropic", "Anthropic", "workflow", "ai", "tier_1", "ai,llm"],
  ["gemini", "Google Gemini", "workflow", "ai", "tier_1", "ai,llm,google"],
  ["huggingface", "Hugging Face", "workflow", "ai", "tier_1", "ai,models"],
  ["pinecone", "Pinecone", "database", "database", "tier_1", "vector,database,ai"],
  ["weaviate", "Weaviate", "database", "database", "tier_1", "vector,database,ai"],
  ["qdrant", "Qdrant", "database", "database", "tier_1", "vector,database,ai"],
  ["zapier", "Zapier", "workflow", "workflow", "tier_1", "automation,workflow"],
  ["make", "Make", "workflow", "workflow", "tier_1", "automation,workflow"],
  ["nango", "Nango", "workflow", "workflow", "tier_1", "integration-platform,oauth"],
  ["pipedream", "Pipedream", "workflow", "workflow", "tier_1", "integration-platform,workflow"],
  ["open-automation-catalog", "Open Automation Catalog", "workflow", "workflow", "tier_1", "automation,workflow,open-source"],
  ["webhook", "Generic Webhook", "webhook", "webhook", "tier_0", "webhook,http,events", "none"],
  ["http", "HTTP Request", "workflow", "webhook", "tier_0", "http,api,webhook", "none"],
  ["rss", "RSS", "webhook", "webhook", "tier_1", "feeds,content", "none"],
  ["zapier-transfer", "Zapier Transfer", "workflow", "workflow", "long_tail", "automation,migration"],
  ["typeform", "Typeform", "workflow", "marketing", "tier_1", "forms,marketing"],
  ["google-forms", "Google Forms", "workflow", "marketing", "tier_1", "forms,google"],
  ["jotform", "Jotform", "workflow", "marketing", "tier_2", "forms"],
  ["webflow", "Webflow", "workflow", "marketing", "tier_1", "cms,website"],
  ["wordpress", "WordPress", "workflow", "marketing", "tier_1", "cms,website"],
  ["contentful", "Contentful", "docs", "docs", "tier_1", "cms,content"],
  ["sanity", "Sanity", "docs", "docs", "tier_1", "cms,content"],
  ["figma", "Figma", "docs", "docs", "tier_0", "design,creative"],
  ["canva", "Canva", "docs", "docs", "tier_1", "design,creative"],
  ["adobe-creative-cloud", "Adobe Creative Cloud", "storage", "storage", "tier_1", "design,creative,files"],
  ["miro", "Miro", "docs", "docs", "tier_1", "whiteboard,collaboration"],
  ["figjam", "FigJam", "docs", "docs", "tier_2", "whiteboard,design"]
];
function listIntegrationCoverageSpecs() {
  return COVERAGE_SPECS.map(([id, title, category, actionPack2, priority, domains, auth = "oauth2"]) => ({
    id,
    title,
    category,
    actionPack: actionPack2,
    priority,
    auth,
    providerKinds: providerKindsFor(auth),
    domains: domains.split(",").map((domain) => domain.trim()).filter(Boolean),
    scopes: scopesFor(id, actionPack2)
  }));
}
function buildIntegrationCoverageConnectors(options = {}) {
  const providerId = options.providerId ?? "coverage";
  return listIntegrationCoverageSpecs().filter((spec) => !options.priorities || options.priorities.includes(spec.priority)).filter((spec) => !options.categories || options.categories.includes(spec.category)).filter((spec) => !options.actionPacks || options.actionPacks.includes(spec.actionPack)).map((spec) => specToConnector(spec, providerId));
}
function integrationCoverageChecklistMarkdown() {
  const specs = listIntegrationCoverageSpecs();
  const lines = [
    "# Agent Integrations Coverage Checklist",
    "",
    "Generated from `listIntegrationCoverageSpecs()`. Catalog presence means the product can plan/request/connect the integration; native adapters and runtime backends execute behind the same provider contract.",
    "",
    "## Summary",
    "",
    `- Total cataloged integrations: ${specs.length}`,
    `- Tier 0: ${specs.filter((spec) => spec.priority === "tier_0").length}`,
    `- Tier 1: ${specs.filter((spec) => spec.priority === "tier_1").length}`,
    `- Tier 2: ${specs.filter((spec) => spec.priority === "tier_2").length}`,
    `- Long tail: ${specs.filter((spec) => spec.priority === "long_tail").length}`,
    "",
    "## Checklist",
    ""
  ];
  for (const spec of specs) {
    lines.push(`- [ ] ${spec.priority} / ${spec.category} / ${spec.title} (${spec.id}) - ${spec.domains.join(", ")}`);
  }
  return `${lines.join("\n")}
`;
}
function specToConnector(spec, providerId) {
  const actions = actionPack(spec.actionPack, spec.scopes ?? []);
  return {
    id: spec.id,
    providerId,
    title: spec.title,
    category: spec.category,
    auth: spec.auth,
    scopes: spec.scopes ?? [],
    actions,
    triggers: triggersFor(spec.actionPack, spec.scopes ?? []),
    metadata: {
      source: "coverage-catalog",
      priority: spec.priority,
      domains: spec.domains,
      providerKinds: spec.providerKinds,
      executable: false
    }
  };
}
function actionPack(pack, scopes) {
  const readScope = scopes.find((scope2) => scope2.endsWith(".read")) ?? scopes[0];
  const writeScope = scopes.find((scope2) => scope2.endsWith(".write")) ?? scopes[1] ?? readScope;
  const scope = (value) => value ? [value] : [];
  const read = (id, title, description) => ({
    id,
    title,
    description,
    risk: "read",
    requiredScopes: scope(readScope),
    dataClass: dataClassFor(pack),
    inputSchema: objectSchema()
  });
  const write = (id, title, description) => ({
    id,
    title,
    description,
    risk: "write",
    requiredScopes: scope(writeScope),
    dataClass: dataClassFor(pack),
    approvalRequired: true,
    inputSchema: objectSchema()
  });
  const destructive = (id, title, description) => ({
    id,
    title,
    description,
    risk: "destructive",
    requiredScopes: scope(writeScope),
    dataClass: dataClassFor(pack),
    approvalRequired: true,
    inputSchema: objectSchema()
  });
  switch (pack) {
    case "email":
      return [read("messages.search", "Search messages", "Search messages and threads."), read("messages.read", "Read message", "Read a message by id."), write("drafts.create", "Create draft", "Create an email draft."), write("messages.send", "Send message", "Send or reply to an email message.")];
    case "calendar":
      return [read("events.search", "Search events", "Search calendar events."), read("availability.read", "Read availability", "Read availability windows."), write("events.create", "Create event", "Create a calendar event."), write("events.update", "Update event", "Update a calendar event."), destructive("events.cancel", "Cancel event", "Cancel a calendar event.")];
    case "chat":
      return [read("messages.search", "Search messages", "Search channel or direct messages."), read("channels.list", "List channels", "List channels or rooms."), write("messages.post", "Send message", "Send a message to a channel or direct message."), write("threads.reply", "Reply in thread", "Reply to a thread or conversation.")];
    case "crm":
      return [read("records.search", "Search records", "Search contacts, companies, and deals."), read("records.read", "Read record", "Read a CRM record."), write("records.upsert", "Upsert record", "Create or update a CRM record."), write("notes.create", "Create note", "Add a note or activity.")];
    case "storage":
      return [read("files.search", "Search files", "Search files and folders."), read("files.read", "Read file", "Read file metadata or content."), write("files.upload", "Upload file", "Upload a file."), write("files.update", "Update file", "Update file metadata or content.")];
    case "docs":
      return [read("documents.search", "Search documents", "Search documents or pages."), read("documents.read", "Read document", "Read a document."), write("documents.create", "Create document", "Create a document or page."), write("documents.update", "Update document", "Update a document or page.")];
    case "database":
      return [read("records.query", "Query records", "Query rows, records, or objects."), read("records.read", "Read record", "Read one row, record, or object."), write("records.upsert", "Upsert record", "Create or update a row, record, or object."), destructive("records.delete", "Delete record", "Delete a row, record, or object.")];
    case "project":
      return [read("tasks.search", "Search tasks", "Search tasks, tickets, or issues."), read("tasks.read", "Read task", "Read a task, ticket, or issue."), write("tasks.create", "Create task", "Create a task, ticket, or issue."), write("tasks.update", "Update task", "Update a task, ticket, or issue.")];
    case "support":
      return [read("tickets.search", "Search tickets", "Search support tickets or conversations."), read("customers.read", "Read customer", "Read a customer profile."), write("tickets.reply", "Reply to ticket", "Reply to a support ticket."), write("tickets.update", "Update ticket", "Update ticket status, tags, or assignee.")];
    case "marketing":
      return [read("contacts.search", "Search contacts", "Search marketing contacts or audiences."), read("campaigns.read", "Read campaign", "Read campaign metadata and performance."), write("contacts.upsert", "Upsert contact", "Create or update a contact."), write("campaigns.create", "Create campaign", "Create a campaign draft.")];
    case "sales":
      return [read("prospects.search", "Search prospects", "Search prospects, leads, or accounts."), read("activities.read", "Read activities", "Read sales activity history."), write("prospects.upsert", "Upsert prospect", "Create or update a prospect."), write("sequence.enqueue", "Enroll in sequence", "Enroll a prospect in a sales sequence.")];
    case "commerce":
      return [read("orders.search", "Search orders", "Search orders."), read("customers.read", "Read customer", "Read customer and purchase history."), write("orders.update", "Update order", "Update order metadata or fulfillment state."), write("products.update", "Update product", "Update product metadata.")];
    case "finance":
      return [read("transactions.search", "Search transactions", "Search transactions, invoices, or payments."), read("accounts.read", "Read account", "Read account or customer financial record."), write("invoices.create", "Create invoice", "Create an invoice or payment object."), write("records.sync", "Sync record", "Sync a finance or accounting record.")];
    case "hr":
      return [read("people.search", "Search people", "Search employees, candidates, or contractors."), read("people.read", "Read person", "Read a person profile."), write("people.update", "Update person", "Update a person profile."), write("events.create", "Create HR event", "Create a recruiting or HR event.")];
    case "dev":
      return [read("resources.search", "Search resources", "Search issues, repos, deployments, logs, or incidents."), read("resources.read", "Read resource", "Read a developer resource."), write("resources.create", "Create resource", "Create an issue, deployment, incident, or config."), write("resources.update", "Update resource", "Update a developer resource.")];
    case "ai":
      return [read("models.list", "List models", "List available models or endpoints."), write("responses.create", "Create response", "Create an AI response or job."), write("embeddings.create", "Create embeddings", "Create embeddings or vector jobs."), read("usage.read", "Read usage", "Read usage metadata.")];
    case "analytics":
      return [read("reports.query", "Query reports", "Query analytics reports."), read("events.search", "Search events", "Search analytics events."), write("events.track", "Track event", "Track an analytics event."), write("audiences.sync", "Sync audience", "Sync an audience or cohort.")];
    case "workflow":
      return [read("runs.search", "Search runs", "Search workflow runs or jobs."), read("templates.list", "List templates", "List workflow templates."), write("runs.start", "Start run", "Start a workflow run."), write("webhooks.dispatch", "Dispatch webhook", "Dispatch a workflow webhook.")];
    case "webhook":
      return [write("requests.send", "Send request", "Send an HTTP request or webhook event."), read("events.search", "Search events", "Search received webhook events."), write("subscriptions.create", "Create subscription", "Create a webhook subscription."), destructive("subscriptions.delete", "Delete subscription", "Delete a webhook subscription.")];
  }
}
function triggersFor(pack, scopes) {
  const readScope = scopes.find((scope) => scope.endsWith(".read")) ?? scopes[0];
  const requiredScopes = readScope ? [readScope] : [];
  if (pack === "email") return [{ id: "message.received", title: "Message received", requiredScopes, dataClass: "private" }];
  if (pack === "calendar") return [{ id: "event.changed", title: "Event changed", requiredScopes, dataClass: "private" }];
  if (pack === "chat") return [{ id: "message.posted", title: "Message posted", requiredScopes, dataClass: "private" }];
  if (pack === "crm") return [{ id: "record.changed", title: "Record changed", requiredScopes, dataClass: "private" }];
  if (pack === "support") return [{ id: "ticket.changed", title: "Ticket changed", requiredScopes, dataClass: "private" }];
  if (pack === "commerce") return [{ id: "order.changed", title: "Order changed", requiredScopes, dataClass: "sensitive" }];
  if (pack === "finance") return [{ id: "transaction.changed", title: "Transaction changed", requiredScopes, dataClass: "sensitive" }];
  if (pack === "workflow" || pack === "webhook") return [{ id: "event.received", title: "Event received", requiredScopes, dataClass: "internal" }];
  return void 0;
}
function scopesFor(id, pack) {
  if (pack === "webhook") return [];
  return [`${id}.read`, `${id}.write`];
}
function providerKindsFor(auth) {
  if (auth === "none") return ["first_party", "pipedream", "tangle_catalog", "custom"];
  return DEFAULT_PROVIDER_KINDS;
}
function dataClassFor(pack) {
  if (pack === "finance" || pack === "commerce" || pack === "hr") return "sensitive";
  if (pack === "workflow" || pack === "webhook" || pack === "dev" || pack === "analytics") return "internal";
  return "private";
}
function objectSchema() {
  return { type: "object", additionalProperties: true, properties: {} };
}

// src/specs/overrides.ts
var INTEGRATION_OVERRIDES = {
  // ── Stripe pack ────────────────────────────────────────────────────
  // Stripe issues two key types: secret keys (sk_*) and restricted keys
  // (rk_*). For voice-agent workloads, restricted keys are the right call
  // — least-privilege scoped to the specific resources the agent can
  // touch. The hint nudges operators toward that path.
  "stripe-pack": {
    consoleUrl: "https://dashboard.stripe.com/apikeys",
    credentialFields: [
      {
        label: "Stripe secret key",
        description: "Restricted key recommended. Dashboard \u2192 Developers \u2192 API keys \u2192 Create restricted key. Grant write access on Customers, Invoices, and Checkout Sessions.",
        example: "sk_live_\u2026 or rk_live_\u2026 (use sk_test_\u2026 / rk_test_\u2026 for staging)",
        regex: "^(sk|rk)_(live|test)_[A-Za-z0-9]+$",
        secret: true
      }
    ],
    consoleSteps: [
      {
        id: "open-keys",
        title: "Open Stripe API keys",
        detail: "Visit https://dashboard.stripe.com/apikeys",
        copyValue: "https://dashboard.stripe.com/apikeys"
      },
      {
        id: "create-restricted",
        title: "Create a restricted key",
        detail: 'Click "Create restricted key". Name it something descriptive (e.g. "ph0ny voice agent \u2014 prod"). Grant WRITE on Customers, Invoices, and Checkout Sessions. Leave everything else NONE.'
      },
      {
        id: "paste",
        title: "Paste the key",
        detail: "Copy the key Stripe shows once (rk_live_\u2026 or sk_live_\u2026). Paste it into ph0ny. The key is sealed before persistence."
      }
    ]
  },
  // ── Twilio SMS ─────────────────────────────────────────────────────
  // Twilio's REST API uses Basic auth with two parts: Account SID
  // (public-ish, AC…) + Auth Token (secret). The default api-key family
  // only exposes one field, which doesn't fit. Providing both fields
  // explicitly lets the consumer's UI render two inputs.
  "twilio-sms": {
    consoleUrl: "https://console.twilio.com/",
    credentialFields: [
      {
        label: "Account SID",
        description: "Your Twilio Account SID. Console \u2192 Account \u2192 API keys & tokens.",
        example: "AC\u2026 (34 hex chars)",
        regex: "^AC[a-f0-9]{32}$",
        secret: false
      },
      {
        label: "Auth Token",
        description: "Your Twilio Auth Token (or Standard API Key secret). Use a non-primary auth token in production so rotating it won't break other Twilio integrations.",
        secret: true
      }
    ],
    consoleSteps: [
      {
        id: "open",
        title: "Open Twilio console",
        detail: "Visit https://console.twilio.com/",
        copyValue: "https://console.twilio.com/"
      },
      {
        id: "find",
        title: "Find your Account SID + Auth Token",
        detail: "Account info is on the dashboard home. For better security, create a Standard API Key (Account \u2192 API keys & tokens \u2192 Create API Key) and use the SID + Secret pair instead of the primary auth token."
      },
      {
        id: "paste",
        title: "Paste both values",
        detail: "Account SID is non-secret; Auth Token is sealed before persistence."
      }
    ],
    knownQuirks: [
      {
        id: "subaccount-tokens",
        severity: "info",
        message: "If you use Twilio subaccounts, paste the SID/Token of the subaccount that owns the phone numbers your agent calls \u2014 not the master account."
      }
    ]
  }
};
function getIntegrationOverride(kind) {
  return INTEGRATION_OVERRIDES[kind];
}

// src/specs/registry.ts
var EXECUTABLE_KINDS = /* @__PURE__ */ new Set([
  "google-calendar",
  "google-sheets",
  "outlook-calendar",
  "microsoft-calendar",
  "slack",
  "hubspot",
  "notion",
  "notion-database",
  "salesforce",
  "github",
  "gitlab",
  "airtable",
  "asana",
  "stripe",
  "stripe-pack",
  "twilio",
  "twilio-sms",
  "webhook"
]);
var KIND_ALIASES = {
  "outlook-calendar": "microsoft-calendar",
  notion: "notion-database",
  stripe: "stripe-pack",
  twilio: "twilio-sms"
};
function listIntegrationSpecs() {
  const connectors = new Map(buildIntegrationCoverageConnectors({ providerId: "spec" }).map((c) => [c.id, c]));
  return listIntegrationCoverageSpecs().map((coverage) => {
    const connector = connectors.get(coverage.id);
    if (!connector) throw new Error(`missing coverage connector for ${coverage.id}`);
    return specFromCoverage(coverage, connector);
  });
}
function getIntegrationSpec(kind) {
  const canonical = KIND_ALIASES[kind] ?? kind;
  return listIntegrationSpecs().find((spec) => spec.kind === canonical || KIND_ALIASES[spec.kind] === canonical);
}
function resolveConnectorAuthSpec(kind) {
  const spec = getIntegrationSpec(kind);
  if (!spec) return void 0;
  const auth = spec.auth;
  if (auth.mode === "oauth2") {
    return {
      kind: spec.kind,
      authKind: "oauth2",
      requestedScopes: auth.scopes.map((scope) => scope.providerScope).filter(Boolean),
      authorizationUrl: auth.authorizationUrl,
      tokenUrl: auth.tokenUrl,
      pkce: auth.pkce,
      redirectUriTemplate: auth.redirectUriTemplate,
      clientIdEnv: auth.clientIdEnv,
      clientSecretEnv: auth.clientSecretEnv,
      extraAuthParams: auth.extraAuthParams
    };
  }
  if (auth.mode === "api_key") {
    return { kind: spec.kind, authKind: "api_key", requestedScopes: [] };
  }
  if (auth.mode === "none") {
    return { kind: spec.kind, authKind: "none", requestedScopes: [] };
  }
  return { kind: spec.kind, authKind: "custom", requestedScopes: [] };
}
function listExecutableIntegrationSpecs() {
  return listIntegrationSpecs().filter((spec) => spec.status === "executable");
}
function integrationSpecToConnector(spec, providerId = "spec") {
  return {
    id: spec.kind,
    providerId,
    title: spec.title,
    category: spec.category,
    auth: spec.auth.mode === "api_key" ? "api_key" : spec.auth.mode === "oauth2" ? "oauth2" : spec.auth.mode === "none" ? "none" : "custom",
    scopes: spec.permissions.flatMap((permission) => permission.providerScopes),
    actions: spec.actions,
    triggers: spec.triggers,
    metadata: {
      ...spec.metadata ?? {},
      source: "integration-spec",
      status: spec.status,
      family: spec.family,
      plannerHints: spec.plannerHints
    }
  };
}
function specFromCoverage(coverage, connector) {
  const kind = KIND_ALIASES[coverage.id] ?? coverage.id;
  const family = familyFor(coverage);
  const familySpec = getIntegrationFamily(family);
  const permissions = permissionsFor(coverage, connector.actions);
  const auth = authFor(coverage, family, permissions);
  const status = statusFor(kind);
  const override = getIntegrationOverride(kind) ?? getIntegrationOverride(coverage.id);
  const knownQuirks = override?.knownQuirks ? [...familySpec.knownQuirks ?? [], ...override.knownQuirks] : familySpec.knownQuirks;
  return {
    kind,
    title: connector.title,
    category: connector.category,
    status,
    family,
    auth,
    permissions,
    actions: connector.actions,
    triggers: connector.triggers,
    setup: {
      consoleUrl: override?.consoleUrl ?? familySpec.consoleUrl,
      consoleSteps: override?.consoleSteps ?? familySpec.consoleSteps,
      credentialFields: override?.credentialFields ?? credentialFieldsFor(auth),
      redirectUriTemplate: auth.mode === "oauth2" ? auth.redirectUriTemplate : familySpec.redirectUriTemplate,
      knownQuirks,
      postSetup: override?.postSetup,
      healthcheck: override?.healthcheck ?? healthcheckFor(kind, status, auth)
    },
    lifecycle: familySpec.lifecycle,
    plannerHints: plannerHintsFor(coverage, connector.actions),
    metadata: { priority: coverage.priority, domains: coverage.domains }
  };
}
function familyFor(spec) {
  if (hmacKinds.has(spec.id)) return "hmac";
  if (spec.auth === "none") return "none";
  if (spec.id.startsWith("google-") || spec.domains.includes("google")) return "google";
  if (spec.id.startsWith("microsoft-") || ["outlook-mail", "outlook-calendar", "onedrive", "sharepoint"].includes(spec.id)) return "microsoft-graph";
  if (["jira", "confluence", "trello", "bitbucket"].includes(spec.id)) return "atlassian";
  if (spec.id === "salesforce") return "salesforce";
  if (spec.id === "hubspot") return "hubspot";
  if (spec.id === "slack") return "slack";
  if (spec.id === "notion") return "notion";
  if (apiKeyKinds.has(spec.id)) return "api-key";
  return "standard-oauth2";
}
var apiKeyKinds = /* @__PURE__ */ new Set(["github", "gitlab", "airtable", "asana", "stripe", "twilio", "sendgrid", "postmark"]);
var hmacKinds = /* @__PURE__ */ new Set(["webhook"]);
function authFor(spec, family, permissions) {
  const f = INTEGRATION_FAMILIES[family];
  if (family === "none") return { mode: "none" };
  if (family === "hmac") {
    return { mode: "hmac", credential: f.credentialFields[0], signatureHeader: `${spec.id}-signature` };
  }
  if (family === "api-key") {
    return { mode: "api_key", credential: apiKeyFieldFor(spec.id), placement: apiKeyPlacementFor(spec.id) };
  }
  const scopes = permissions.flatMap(
    (permission) => permission.providerScopes.map((providerScope) => ({
      normalized: permission.normalized,
      providerScope,
      title: permission.title,
      reason: permission.reason,
      risk: permission.risk,
      dataClass: permission.dataClass
    }))
  );
  return {
    mode: "oauth2",
    authorizationUrl: f.authorizationUrl ?? `https://example.invalid/${spec.id}/authorize`,
    tokenUrl: f.tokenUrl ?? `https://example.invalid/${spec.id}/token`,
    clientIdEnv: f.credentialFields.find((field) => !field.secret)?.env,
    clientSecretEnv: f.credentialFields.find((field) => field.secret)?.env,
    scopes,
    extraAuthParams: extraAuthParamsFor(family),
    redirectUriTemplate: (f.redirectUriTemplate ?? "https://{host}/api/integrations/oauth/{kind}/callback").replace("{kind}", spec.id),
    pkce: family === "google" || family === "microsoft-graph" ? "supported" : "unsupported"
  };
}
function credentialFieldsFor(auth) {
  if (auth.mode === "api_key" || auth.mode === "hmac") return [auth.credential];
  if (auth.mode === "oauth2") {
    return [
      { label: "Client ID", env: auth.clientIdEnv, description: "OAuth client ID.", secret: false },
      { label: "Client Secret", env: auth.clientSecretEnv, description: "OAuth client secret.", secret: true }
    ];
  }
  return [];
}
function permissionsFor(spec, actions) {
  const dataClass = dataClassFor2(actions);
  const readScope = providerScopeFor(spec, "read");
  const writeScope = providerScopeFor(spec, "write");
  const permissions = [
    {
      normalized: `${spec.actionPack}.read`,
      providerScopes: readScope ? [readScope] : [],
      title: `${spec.title} read`,
      risk: "read",
      dataClass,
      reason: `Read ${spec.title} data for user-authorized agent workflows.`
    }
  ];
  if (actions.some((a) => a.risk !== "read")) {
    permissions.push({
      normalized: `${spec.actionPack}.write`,
      providerScopes: writeScope ? [writeScope] : [],
      title: `${spec.title} write`,
      risk: "write",
      dataClass,
      reason: `Create or update ${spec.title} resources after policy approval.`
    });
  }
  return permissions;
}
function providerScopeFor(spec, mode) {
  const explicit = explicitScopes[spec.id]?.[mode];
  if (explicit) return explicit;
  if (spec.auth === "none") return "";
  return `${spec.id}.${mode}`;
}
var explicitScopes = {
  gmail: { read: "https://www.googleapis.com/auth/gmail.readonly", write: "https://www.googleapis.com/auth/gmail.modify" },
  "google-calendar": { read: "https://www.googleapis.com/auth/calendar.readonly", write: "https://www.googleapis.com/auth/calendar" },
  "google-sheets": { read: "https://www.googleapis.com/auth/spreadsheets.readonly", write: "https://www.googleapis.com/auth/spreadsheets" },
  "google-drive": { read: "https://www.googleapis.com/auth/drive.readonly", write: "https://www.googleapis.com/auth/drive.file" },
  "google-docs": { read: "https://www.googleapis.com/auth/documents.readonly", write: "https://www.googleapis.com/auth/documents" },
  "outlook-mail": { read: "Mail.Read", write: "Mail.Send" },
  "outlook-calendar": { read: "Calendars.Read", write: "Calendars.ReadWrite" },
  "microsoft-teams": { read: "ChannelMessage.Read.All", write: "ChannelMessage.Send" },
  onedrive: { read: "Files.Read", write: "Files.ReadWrite" },
  sharepoint: { read: "Sites.Read.All", write: "Sites.ReadWrite.All" },
  slack: { read: "channels:read", write: "chat:write" },
  hubspot: { read: "crm.objects.contacts.read", write: "crm.objects.contacts.write" },
  salesforce: { read: "api", write: "api" },
  notion: { read: "", write: "" },
  github: { read: "repo:read", write: "repo" },
  gitlab: { read: "read_api", write: "api" },
  airtable: { read: "data.records:read", write: "data.records:write" },
  asana: { read: "default", write: "default" },
  stripe: { read: "read_only", write: "standard" },
  twilio: { read: "api_key", write: "api_key" }
};
function plannerHintsFor(spec, actions) {
  return {
    useFor: spec.domains.map((domain) => domain.replace(/-/g, " ")),
    dataFreshness: ["calendar", "chat", "commerce", "finance", "support"].includes(spec.actionPack) ? "near_realtime" : "eventual",
    writeRisk: actions.some((a) => a.risk === "destructive") ? "high" : actions.some((a) => a.risk === "write") ? "medium" : "low"
  };
}
function healthcheckFor(kind, status, auth) {
  if (status !== "executable") {
    return { id: `${kind}.static`, level: "static", description: "Catalog-only integration; no executable connector healthcheck is available yet." };
  }
  if (auth.mode === "oauth2") {
    return { id: `${kind}.connection`, level: "connection", description: "Validate a user connection by calling the connector test endpoint." };
  }
  if (auth.mode === "api_key") {
    return { id: `${kind}.connection`, level: "connection", description: "Validate API credentials by calling the connector test endpoint." };
  }
  if (auth.mode === "hmac") {
    return { id: `${kind}.webhook`, level: "webhook", description: "Validate webhook signing configuration with a signed test payload." };
  }
  return { id: `${kind}.static`, level: "static", description: "No credentials are required." };
}
function statusFor(kind) {
  return EXECUTABLE_KINDS.has(kind) ? "executable" : "catalog";
}
function dataClassFor2(actions) {
  if (actions.some((a) => a.dataClass === "secret")) return "secret";
  if (actions.some((a) => a.dataClass === "sensitive")) return "sensitive";
  if (actions.some((a) => a.dataClass === "private")) return "private";
  if (actions.some((a) => a.dataClass === "internal")) return "internal";
  return "public";
}
function apiKeyFieldFor(kind) {
  return {
    label: `${kind} API key`,
    description: `API key or token for ${kind}.`,
    example: kind === "stripe" ? "sk_live_..." : void 0,
    secret: true
  };
}
function apiKeyPlacementFor(kind) {
  if (kind === "gitlab") return "header";
  return "bearer";
}
function extraAuthParamsFor(family) {
  if (family === "google") return { access_type: "offline", prompt: "consent", include_granted_scopes: "true" };
  if (family === "notion") return { owner: "user" };
  return void 0;
}

// src/specs/renderers.ts
function renderConsoleSteps(spec, options) {
  const redirectUri = renderRedirectUri(spec, options);
  return spec.setup.consoleSteps.map((step) => ({
    ...step,
    detail: renderTemplate(step.detail, spec, options, redirectUri),
    copyValue: step.copyValue ? renderTemplate(step.copyValue, spec, options, redirectUri) : void 0
  }));
}
function renderRunbookMarkdown(spec, options) {
  const steps = renderConsoleSteps(spec, options);
  const lines = [
    `# ${spec.title} Integration Setup`,
    "",
    `- Kind: \`${spec.kind}\``,
    `- Status: \`${spec.status}\``,
    `- Auth: \`${spec.auth.mode}\``,
    `- Family: \`${spec.family}\``
  ];
  if (spec.setup.consoleUrl) lines.push(`- Console: ${spec.setup.consoleUrl}`);
  if (spec.setup.redirectUriTemplate) lines.push(`- Redirect URI: \`${renderRedirectUri(spec, options)}\``);
  lines.push("", "## Credentials", "");
  for (const field of spec.setup.credentialFields) {
    lines.push(`- ${field.secret ? "[secret] " : ""}${field.label}${field.env ? ` (\`${field.env}\`)` : ""}: ${field.description}`);
  }
  lines.push("", "## Permissions", "");
  for (const permission of spec.permissions) {
    lines.push(`- \`${permission.normalized}\`: ${permission.providerScopes.length ? permission.providerScopes.map((scope) => `\`${scope}\``).join(", ") : "no provider scope"} - ${permission.reason}`);
  }
  lines.push("", "## Console Steps", "");
  for (const [i, step] of steps.entries()) {
    lines.push(`${i + 1}. ${step.title}: ${step.detail}`);
  }
  if (spec.setup.knownQuirks?.length) {
    lines.push("", "## Known Quirks", "");
    for (const quirk of spec.setup.knownQuirks) lines.push(`- ${quirk.severity}: ${quirk.message}`);
  }
  return `${lines.join("\n")}
`;
}
function renderAgentToolDescription(spec) {
  const hints = spec.plannerHints;
  const useFor = hints?.useFor?.length ? `Use for ${hints.useFor.join(", ")}.` : `Use for ${spec.title} workflows.`;
  const risk = hints ? `Freshness: ${hints.dataFreshness}. Write risk: ${hints.writeRisk}.` : "";
  return `${spec.title} (${spec.kind}). ${useFor} ${risk}`.trim();
}
function buildHealthcheckPlan(spec) {
  const healthcheck = spec.setup.healthcheck ?? { id: `${spec.kind}.static`, level: "static", description: "No healthcheck defined." };
  const requires = [];
  if (healthcheck.level === "connection") requires.push("connection_credentials");
  if (healthcheck.level === "client_config" && spec.auth.mode === "oauth2") requires.push("client_id", "client_secret");
  if (spec.auth.mode === "api_key") requires.push("api_key");
  if (spec.auth.mode === "hmac") requires.push("hmac_secret");
  return {
    kind: spec.kind,
    healthcheck,
    requires,
    message: healthcheck.description
  };
}
function renderTemplate(template, spec, options, redirectUri) {
  return template.replaceAll("{host}", options.host).replaceAll("{kind}", spec.kind).replaceAll("{redirectUri}", redirectUri ?? renderRedirectUri(spec, options));
}
function renderRedirectUri(spec, options) {
  return (options.callbackPath ?? spec.setup.redirectUriTemplate ?? "").replaceAll("{host}", options.host).replaceAll("{kind}", spec.kind);
}
function consoleStepsToText(steps) {
  return steps.map((step, index) => `${index + 1}. ${step.title}: ${step.detail}`).join("\n");
}

// src/specs/validation.ts
function validateIntegrationSpec(spec) {
  const issues = [];
  if (!spec.kind.trim()) issues.push({ path: "kind", message: "kind is required" });
  if (!spec.title.trim()) issues.push({ path: "title", message: "title is required" });
  if (!spec.actions.length) issues.push({ path: "actions", message: "at least one action is required" });
  if (!spec.permissions.length) issues.push({ path: "permissions", message: "at least one permission is required" });
  if (spec.auth.mode === "oauth2") {
    if (!spec.auth.authorizationUrl) issues.push({ path: "auth.authorizationUrl", message: "authorizationUrl is required" });
    if (!spec.auth.tokenUrl) issues.push({ path: "auth.tokenUrl", message: "tokenUrl is required" });
    if (!spec.auth.redirectUriTemplate) issues.push({ path: "auth.redirectUriTemplate", message: "redirectUriTemplate is required" });
  }
  const actionIds = /* @__PURE__ */ new Set();
  for (const [index, action] of spec.actions.entries()) {
    if (actionIds.has(action.id)) issues.push({ path: `actions[${index}].id`, message: `duplicate action id ${action.id}` });
    actionIds.add(action.id);
  }
  return { ok: issues.length === 0, issues };
}
function assertValidIntegrationSpec(spec) {
  const result = validateIntegrationSpec(spec);
  if (!result.ok) {
    throw new Error(`Invalid integration spec ${spec.kind}: ${result.issues.map((i) => `${i.path}: ${i.message}`).join("; ")}`);
  }
}
function validateCredentialFormat(field, value) {
  if (!value.trim()) return { ok: false, field: field.label, message: `${field.label} is required` };
  if (field.regex && !new RegExp(field.regex).test(value)) {
    return { ok: false, field: field.label, message: `${field.label} does not match expected format` };
  }
  return { ok: true, field: field.label };
}
function validateCredentialSet(spec, values) {
  return spec.setup.credentialFields.map((field) => {
    const key = field.env ?? field.label;
    return validateCredentialFormat(field, values[key] ?? "");
  });
}

export {
  listIntegrationCoverageSpecs,
  buildIntegrationCoverageConnectors,
  integrationCoverageChecklistMarkdown,
  INTEGRATION_FAMILIES,
  getIntegrationFamily,
  listIntegrationSpecs,
  getIntegrationSpec,
  resolveConnectorAuthSpec,
  listExecutableIntegrationSpecs,
  integrationSpecToConnector,
  specAuthToConnectorAuth,
  renderConsoleSteps,
  renderRunbookMarkdown,
  renderAgentToolDescription,
  buildHealthcheckPlan,
  consoleStepsToText,
  validateIntegrationSpec,
  assertValidIntegrationSpec,
  validateCredentialFormat,
  validateCredentialSet
};
//# sourceMappingURL=chunk-VVC7U7W7.js.map