| 1 | import type { Provider } from "./integrations"; |
| 2 | |
| 3 | /** |
| 4 | * The connector catalog: every tool g1t connects to, or will, in one list. |
| 5 | * The workspace's Integrations page and your own Integrations settings are |
| 6 | * both drawn from it, so a new connector is added here, once. |
| 7 | * |
| 8 | * A connector is `available` when g1t can set it up today (its `href` is |
| 9 | * where), and `soon` while it is planned. `scopes` says who connects it: |
| 10 | * |
| 11 | * - `workspace`: an owner connects it once, for everyone in the workspace. |
| 12 | * - `personal`: each person connects their own account, and it follows them |
| 13 | * into every workspace they belong to (a calendar, a personal sign-in). |
| 14 | * |
| 15 | * A connector with both scopes can say something different for each, in |
| 16 | * `personal`: its own description, status, capabilities or setup page. |
| 17 | * `connectorView` resolves that into what one context shows. |
| 18 | * |
| 19 | * Paths in `href` are g1t's own pages. `:workspace` stands for the |
| 20 | * workspace's slug. |
| 21 | */ |
| 22 | |
| 23 | export type ConnectorScope = "workspace" | "personal"; |
| 24 | export type ConnectorStatus = "available" | "soon"; |
| 25 | |
| 26 | export type ConnectorCategory = |
| 27 | | "code" |
| 28 | | "issues" |
| 29 | | "chat" |
| 30 | | "calendar" |
| 31 | | "docs" |
| 32 | | "monitoring" |
| 33 | | "cloud" |
| 34 | | "ai" |
| 35 | | "data" |
| 36 | | "crm" |
| 37 | | "design" |
| 38 | | "security" |
| 39 | | "files"; |
| 40 | |
| 41 | /** What a connector lets g1t do, as the small tags on its card. */ |
| 42 | export type ConnectorCapability = |
| 43 | | "Agents can read" |
| 44 | | "Agents can act" |
| 45 | | "Opens issues" |
| 46 | | "Writes back" |
| 47 | | "Imports repositories" |
| 48 | | "Signs you in" |
| 49 | | "Sends events" |
| 50 | | "Alerts into chat" |
| 51 | | "Sets your status" |
| 52 | | "Deploys" |
| 53 | | "Syncs people"; |
| 54 | |
| 55 | export type Connector = { |
| 56 | /** Stable and unique: used in links and requests. */ |
| 57 | id: string; |
| 58 | name: string; |
| 59 | category: ConnectorCategory; |
| 60 | /** One plain line of what it does in g1t, for a workspace (or for you, when it is only personal). */ |
| 61 | description: string; |
| 62 | scopes: ConnectorScope[]; |
| 63 | status: ConnectorStatus; |
| 64 | /** Where an available connector is set up, for each scope it has. */ |
| 65 | href?: Partial<Record<ConnectorScope, string>>; |
| 66 | capabilities?: ConnectorCapability[]; |
| 67 | /** The integration provider behind it, whose connections show it as connected. */ |
| 68 | provider?: Provider; |
| 69 | /** What differs when you connect it for yourself. */ |
| 70 | personal?: { description?: string; status?: ConnectorStatus; capabilities?: ConnectorCapability[] }; |
| 71 | /** More words search should find it by. */ |
| 72 | keywords?: string[]; |
| 73 | }; |
| 74 | |
| 75 | /** A connector as one context (a workspace, or you) shows it. */ |
| 76 | export type ConnectorView = { |
| 77 | id: string; |
| 78 | name: string; |
| 79 | category: ConnectorCategory; |
| 80 | description: string; |
| 81 | status: ConnectorStatus; |
| 82 | href: string | null; |
| 83 | capabilities: ConnectorCapability[]; |
| 84 | provider: Provider | null; |
| 85 | keywords: string[]; |
| 86 | /** The other scope it can also be connected in. */ |
| 87 | alsoIn: ConnectorScope | null; |
| 88 | }; |
| 89 | |
| 90 | /** The categories, in the order the directory lists them. */ |
| 91 | export const CONNECTOR_CATEGORIES: { id: ConnectorCategory; title: string }[] = [ |
| 92 | { id: "code", title: "Code & CI" }, |
| 93 | { id: "issues", title: "Issues & projects" }, |
| 94 | { id: "chat", title: "Chat & meetings" }, |
| 95 | { id: "calendar", title: "Calendar & email" }, |
| 96 | { id: "docs", title: "Docs & knowledge" }, |
| 97 | { id: "monitoring", title: "Monitoring & incidents" }, |
| 98 | { id: "cloud", title: "Cloud & deploy" }, |
| 99 | { id: "ai", title: "AI & agents" }, |
| 100 | { id: "data", title: "Data & analytics" }, |
| 101 | { id: "crm", title: "CRM & support" }, |
| 102 | { id: "design", title: "Design" }, |
| 103 | { id: "security", title: "Security & identity" }, |
| 104 | { id: "files", title: "Files & storage" }, |
| 105 | ]; |
| 106 | |
| 107 | const W: ConnectorScope[] = ["workspace"]; |
| 108 | const P: ConnectorScope[] = ["personal"]; |
| 109 | const BOTH: ConnectorScope[] = ["workspace", "personal"]; |
| 110 | |
| 111 | /** A planned connector. */ |
| 112 | function soon( |
| 113 | id: string, |
| 114 | name: string, |
| 115 | category: ConnectorCategory, |
| 116 | scopes: ConnectorScope[], |
| 117 | description: string, |
| 118 | capabilities: ConnectorCapability[] = [], |
| 119 | personal?: Connector["personal"], |
| 120 | ): Connector { |
| 121 | return { id, name, category, description, scopes, status: "soon", capabilities, ...(personal ? { personal } : {}) }; |
| 122 | } |
| 123 | |
| 124 | export const CONNECTORS: Connector[] = [ |
| 125 | // Code & CI ------------------------------------------------------------- |
| 126 | { |
| 127 | id: "github", |
| 128 | name: "GitHub", |
| 129 | category: "code", |
| 130 | description: "Import repositories, with their branches, tags and issues, through g1t's GitHub App.", |
| 131 | scopes: BOTH, |
| 132 | status: "available", |
| 133 | href: { workspace: "/new/github?workspace=:workspace", personal: "/settings/github" }, |
| 134 | capabilities: ["Imports repositories"], |
| 135 | personal: { |
| 136 | description: "Sign in with your GitHub account, and reach the repositories you can see there to import them.", |
| 137 | capabilities: ["Signs you in", "Imports repositories"], |
| 138 | }, |
| 139 | keywords: ["git", "import", "sign in"], |
| 140 | }, |
| 141 | { |
| 142 | id: "webhooks", |
| 143 | name: "Webhooks", |
| 144 | category: "code", |
| 145 | description: "Every repository's events, sent to your own addresses as they happen, signed.", |
| 146 | scopes: W, |
| 147 | status: "available", |
| 148 | href: { workspace: "/:workspace/-/webhooks" }, |
| 149 | capabilities: ["Sends events"], |
| 150 | keywords: ["http", "events", "outgoing"], |
| 151 | }, |
| 152 | soon("gitlab", "GitLab", "code", BOTH, "Import projects, with their branches, tags and issues.", ["Imports repositories"], { |
| 153 | description: "Sign in with GitLab, and reach your own projects to import them.", |
| 154 | capabilities: ["Signs you in", "Imports repositories"], |
| 155 | }), |
| 156 | soon("bitbucket", "Bitbucket", "code", BOTH, "Import repositories, with their branches and tags.", ["Imports repositories"], { |
| 157 | description: "Reach your own Bitbucket repositories to import them.", |
| 158 | capabilities: ["Imports repositories"], |
| 159 | }), |
| 160 | soon("circleci", "CircleCI", "code", W, "Pipeline results as checks on pull requests, which agents read when a build fails.", ["Agents can read"]), |
| 161 | soon("buildkite", "Buildkite", "code", W, "Build results as checks on pull requests, with the failing step for agents to fix.", ["Agents can read"]), |
| 162 | soon("docker-hub", "Docker Hub", "code", W, "Push images your workflows build, and pull private base images.", ["Deploys"]), |
| 163 | |
| 164 | // Issues & projects ----------------------------------------------------- |
| 165 | { |
| 166 | id: "linear", |
| 167 | name: "Linear", |
| 168 | category: "issues", |
| 169 | description: "Agents read ENG-42 when work mentions it; import issues, and they hear back when the work lands.", |
| 170 | scopes: BOTH, |
| 171 | status: "available", |
| 172 | href: { workspace: "/:workspace/-/integrations/trackers?add=linear#add" }, |
| 173 | capabilities: ["Agents can read", "Writes back"], |
| 174 | provider: "linear", |
| 175 | personal: { |
| 176 | description: "Issues assigned to you in Linear, in your g1t inbox beside everything else.", |
| 177 | status: "soon", |
| 178 | capabilities: ["Agents can read"], |
| 179 | }, |
| 180 | keywords: ["tracker", "tickets"], |
| 181 | }, |
| 182 | { |
| 183 | id: "jira", |
| 184 | name: "Jira", |
| 185 | category: "issues", |
| 186 | description: "Agents read TECH-1234 when work mentions it; import tickets, and they hear back when the work lands.", |
| 187 | scopes: W, |
| 188 | status: "available", |
| 189 | href: { workspace: "/:workspace/-/integrations/trackers?add=jira#add" }, |
| 190 | capabilities: ["Agents can read", "Writes back"], |
| 191 | provider: "jira", |
| 192 | keywords: ["atlassian", "tracker", "tickets"], |
| 193 | }, |
| 194 | soon("shortcut", "Shortcut", "issues", W, "Stories agents read when work mentions them, updated when the work lands.", ["Agents can read", "Writes back"]), |
| 195 | soon("clickup", "ClickUp", "issues", W, "Tasks agents can read and import, kept in step with their pull requests.", ["Agents can read", "Writes back"]), |
| 196 | soon("trello", "Trello", "issues", W, "Cards become issues, and move along their board as the work does.", ["Opens issues", "Writes back"]), |
| 197 | |
| 198 | // Chat & meetings ------------------------------------------------------- |
| 199 | soon("slack", "Slack", "chat", BOTH, "Alerts and updates in the channels your team already reads.", ["Alerts into chat"], { |
| 200 | description: "Your status in g1t follows the one you set in Slack, and the other way round.", |
| 201 | capabilities: ["Sets your status"], |
| 202 | }), |
| 203 | soon("microsoft-teams", "Microsoft Teams", "chat", BOTH, "Alerts and updates posted to Teams channels.", ["Alerts into chat"], { |
| 204 | description: "Your status in g1t follows your Teams presence.", |
| 205 | capabilities: ["Sets your status"], |
| 206 | }), |
| 207 | soon("discord", "Discord", "chat", W, "Releases, alerts and updates posted to a Discord server.", ["Alerts into chat"]), |
| 208 | soon("zoom", "Zoom", "chat", BOTH, "Meeting notes and recordings agents can read when work refers to them.", ["Agents can read"], { |
| 209 | description: "Sets your status to On a call while you are in a Zoom meeting.", |
| 210 | capabilities: ["Sets your status"], |
| 211 | }), |
| 212 | soon("google-meet", "Google Meet", "chat", P, "Sets your status to On a call while you are in a meeting, and starts a call from any channel.", ["Sets your status"]), |
| 213 | |
| 214 | // Calendar & email ------------------------------------------------------ |
| 215 | soon("google-calendar", "Google Calendar", "calendar", P, "Sets your status to In a meeting while you are in one.", ["Sets your status"]), |
| 216 | soon("outlook-calendar", "Outlook Calendar", "calendar", P, "Sets your status to In a meeting while you are in one.", ["Sets your status"]), |
| 217 | soon("gmail", "Gmail", "calendar", P, "Turn an email into an issue, from your own inbox.", ["Opens issues"]), |
| 218 | soon("outlook-mail", "Outlook Mail", "calendar", P, "Turn an email into an issue, from your own inbox.", ["Opens issues"]), |
| 219 | |
| 220 | // Docs & knowledge ------------------------------------------------------ |
| 221 | soon("google-docs", "Google Docs", "docs", BOTH, "Agents read the specs and plans linked from issues and chat.", ["Agents can read"], { |
| 222 | description: "Docs you share in chat open for agents as well as people.", |
| 223 | capabilities: ["Agents can read"], |
| 224 | }), |
| 225 | soon("coda", "Coda", "docs", W, "Agents read the docs and tables linked from issues and chat.", ["Agents can read"]), |
| 226 | |
| 227 | // Monitoring & incidents ------------------------------------------------ |
| 228 | { |
| 229 | id: "sentry", |
| 230 | name: "Sentry", |
| 231 | category: "monitoring", |
| 232 | description: "New errors open issues with the stack trace, and resolve in Sentry when the fix merges.", |
| 233 | scopes: W, |
| 234 | status: "available", |
| 235 | href: { workspace: "/:workspace/-/integrations/alerts?add=sentry#add" }, |
| 236 | capabilities: ["Opens issues", "Agents can read", "Writes back"], |
| 237 | provider: "sentry", |
| 238 | keywords: ["errors", "alerts", "exceptions"], |
| 239 | }, |
| 240 | { |
| 241 | id: "datadog", |
| 242 | name: "Datadog", |
| 243 | category: "monitoring", |
| 244 | description: "Monitors that trigger open issues, and recoveries are noted on them.", |
| 245 | scopes: W, |
| 246 | status: "available", |
| 247 | href: { workspace: "/:workspace/-/integrations/alerts?add=datadog#add" }, |
| 248 | capabilities: ["Opens issues"], |
| 249 | provider: "datadog", |
| 250 | keywords: ["monitors", "alerts", "apm"], |
| 251 | }, |
| 252 | { |
| 253 | id: "alerts-webhook", |
| 254 | name: "Alerts webhook", |
| 255 | category: "monitoring", |
| 256 | description: "Anything that can send signed JSON opens issues: your own scripts, or any monitoring tool.", |
| 257 | scopes: W, |
| 258 | status: "available", |
| 259 | href: { workspace: "/:workspace/-/integrations/alerts?add=webhook#add" }, |
| 260 | capabilities: ["Opens issues"], |
| 261 | provider: "webhook", |
| 262 | keywords: ["incoming", "http", "alerts", "pagerduty", "grafana"], |
| 263 | }, |
| 264 | soon("pagerduty", "PagerDuty", "monitoring", BOTH, "Incidents open issues, and resolve when the fix lands.", ["Opens issues", "Writes back"], { |
| 265 | description: "Sets your status to On call while you are on call.", |
| 266 | capabilities: ["Sets your status"], |
| 267 | }), |
| 268 | soon("opsgenie", "Opsgenie", "monitoring", W, "Alerts open issues, and close when the fix lands.", ["Opens issues", "Writes back"]), |
| 269 | soon("incident-io", "incident.io", "monitoring", W, "Incidents get a channel in g1t, with what changed alongside.", ["Opens issues", "Alerts into chat"]), |
| 270 | soon("grafana", "Grafana", "monitoring", W, "Alert rules open issues, and agents read the dashboards they link to.", ["Opens issues", "Agents can read"]), |
| 271 | soon("new-relic", "New Relic", "monitoring", W, "Alerts open issues, with the traces agents need to find the cause.", ["Opens issues", "Agents can read"]), |
| 272 | soon("honeycomb", "Honeycomb", "monitoring", W, "Triggers open issues, and agents query traces while they work.", ["Opens issues", "Agents can read"]), |
| 273 | soon("better-stack", "Better Stack", "monitoring", W, "Uptime incidents and log alerts open issues.", ["Opens issues"]), |
| 274 | |
| 275 | // Cloud & deploy -------------------------------------------------------- |
| 276 | soon("aws", "Amazon Web Services", "cloud", W, "Workflows and deployments reach AWS with short-lived credentials, no stored keys.", ["Deploys"]), |
| 277 | soon("google-cloud", "Google Cloud", "cloud", W, "Workflows and deployments reach Google Cloud with short-lived credentials.", ["Deploys"]), |
| 278 | soon("azure", "Microsoft Azure", "cloud", W, "Workflows and deployments reach Azure with short-lived credentials.", ["Deploys"]), |
| 279 | soon("netlify", "Netlify", "cloud", W, "Deploy previews as checks on pull requests.", ["Deploys", "Agents can read"]), |
| 280 | soon("fly-io", "Fly.io", "cloud", W, "Deploy apps from workflows, and roll back from the deployment's page.", ["Deploys"]), |
| 281 | soon("kubernetes", "Kubernetes", "cloud", W, "Roll out to your own clusters from workflows, with each rollout's status on g1t.", ["Deploys"]), |
| 282 | |
| 283 | // AI & agents. AI providers are not connectors: they are each workspace's |
| 284 | // own AI settings, at /:workspace/-/ai. |
| 285 | { |
| 286 | id: "mcp", |
| 287 | name: "MCP clients", |
| 288 | category: "ai", |
| 289 | description: "Your own agent, connected to g1t's MCP server, works in g1t as you.", |
| 290 | scopes: P, |
| 291 | status: "available", |
| 292 | href: { personal: "/settings/applications" }, |
| 293 | capabilities: ["Agents can act"], |
| 294 | keywords: ["model context protocol", "agent", "applications", "oauth"], |
| 295 | }, |
| 296 | |
| 297 | // Data & analytics ------------------------------------------------------ |
| 298 | soon("postgresql", "PostgreSQL", "data", W, "Agents read your schema, and run read-only queries while they investigate.", ["Agents can read"]), |
| 299 | soon("snowflake", "Snowflake", "data", W, "Agents answer questions from your warehouse with read-only queries.", ["Agents can read"]), |
| 300 | soon("bigquery", "BigQuery", "data", W, "Agents answer questions from your datasets with read-only queries.", ["Agents can read"]), |
| 301 | soon("amplitude", "Amplitude", "data", W, "Agents read the charts that work refers to.", ["Agents can read"]), |
| 302 | soon("segment", "Segment", "data", W, "Agents read your tracking plan, and check new events against it.", ["Agents can read"]), |
| 303 | |
| 304 | // CRM & support --------------------------------------------------------- |
| 305 | soon("zendesk", "Zendesk", "crm", W, "Turn a ticket into an issue, and tell the customer when the fix ships.", ["Opens issues", "Writes back"]), |
| 306 | soon("salesforce", "Salesforce", "crm", W, "Agents read the account and case behind a request.", ["Agents can read"]), |
| 307 | |
| 308 | // Design ---------------------------------------------------------------- |
| 309 | soon("miro", "Miro", "design", W, "Agents read the boards linked from issues and chat.", ["Agents can read"]), |
| 310 | soon("storybook", "Storybook", "design", W, "Each pull request's stories as a check, with what changed.", ["Agents can read"]), |
| 311 | |
| 312 | // Security & identity --------------------------------------------------- |
| 313 | soon("okta", "Okta", "security", W, "Single sign-on, and people added and removed with your directory.", ["Signs you in", "Syncs people"]), |
| 314 | soon("entra-id", "Microsoft Entra ID", "security", W, "Single sign-on, and people added and removed with your directory.", ["Signs you in", "Syncs people"]), |
| 315 | soon("google-workspace", "Google Workspace", "security", BOTH, "Single sign-on, and people added and removed with your directory.", ["Signs you in", "Syncs people"], { |
| 316 | description: "Sign in to g1t with your Google account.", |
| 317 | capabilities: ["Signs you in"], |
| 318 | }), |
| 319 | soon("1password", "1Password", "security", W, "Secrets from a vault, read by workflows and deployments when they run.", ["Deploys"]), |
| 320 | soon("snyk", "Snyk", "security", W, "Findings open issues, and an agent opens the fix.", ["Opens issues"]), |
| 321 | |
| 322 | // Files & storage ------------------------------------------------------- |
| 323 | soon("google-drive", "Google Drive", "files", BOTH, "Agents read the files linked from issues and chat.", ["Agents can read"], { |
| 324 | description: "Attach files from your Drive in chat, without downloading them first.", |
| 325 | capabilities: ["Agents can read"], |
| 326 | }), |
| 327 | soon("dropbox", "Dropbox", "files", BOTH, "Agents read the files linked from issues and chat.", ["Agents can read"], { |
| 328 | description: "Attach files from your Dropbox in chat.", |
| 329 | capabilities: ["Agents can read"], |
| 330 | }), |
| 331 | soon("onedrive", "OneDrive and SharePoint", "files", BOTH, "Agents read the files linked from issues and chat.", ["Agents can read"], { |
| 332 | description: "Attach files from your OneDrive in chat.", |
| 333 | capabilities: ["Agents can read"], |
| 334 | }), |
| 335 | soon("box", "Box", "files", W, "Agents read the files linked from issues and chat.", ["Agents can read"]), |
| 336 | ]; |
| 337 | |
| 338 | /** The connector by its id. */ |
| 339 | export function connectorById(id: string): Connector | undefined { |
| 340 | return CONNECTORS.find((connector) => connector.id === id); |
| 341 | } |
| 342 | |
| 343 | /** A connector as `scope` shows it, or null when it cannot be connected there. */ |
| 344 | export function connectorView(connector: Connector, scope: ConnectorScope): ConnectorView | null { |
| 345 | if (!connector.scopes.includes(scope)) return null; |
| 346 | const own = scope === "personal" ? connector.personal : undefined; |
| 347 | const status = own?.status ?? connector.status; |
| 348 | const other: ConnectorScope = scope === "workspace" ? "personal" : "workspace"; |
| 349 | return { |
| 350 | id: connector.id, |
| 351 | name: connector.name, |
| 352 | category: connector.category, |
| 353 | description: own?.description ?? connector.description, |
| 354 | status, |
| 355 | href: status === "available" ? (connector.href?.[scope] ?? null) : null, |
| 356 | capabilities: own?.capabilities ?? connector.capabilities ?? [], |
| 357 | provider: connector.provider ?? null, |
| 358 | keywords: connector.keywords ?? [], |
| 359 | alsoIn: connector.scopes.includes(other) ? other : null, |
| 360 | }; |
| 361 | } |
| 362 | |
| 363 | /** Every connector `scope` can show, in catalog order. */ |
| 364 | export function connectorsFor(scope: ConnectorScope): ConnectorView[] { |
| 365 | return CONNECTORS.map((connector) => connectorView(connector, scope)).filter((view): view is ConnectorView => view != null); |
| 366 | } |
| 367 | |
| 368 | /** A setup path with its `:workspace` filled in. */ |
| 369 | export function connectorPath(path: string, workspace: string): string { |
| 370 | return path.replace(/:workspace\b/g, encodeURIComponent(workspace)); |
| 371 | } |