/** * `GithubIntegration` — the injectable GitHub App integration for the web * factory. * * The deploy entry (`src/mastra/index.ts`) reads the `GITHUB_APP_*` env vars * once, constructs an instance, and passes it to `MastraFactory` (the same * dependency-injection pattern as the auth adapter and the sandbox machine). * The factory seeds it into the runtime-config registry for the feature gate * (`./config.ts`) and hands the instance to every consumer explicitly — route * handlers receive it through `routes()`, the webhook fan-out and session * subscription tools through their deps. * * The class owns: * - the GitHub App credentials (validated + PEM-normalized at construction), * - every GitHub API operation the web surface performs (Octokit factories, * token minting, installation/repo/issue/PR listings, OAuth exchange), * - the webhook secret used to verify GitHub webhook deliveries, * - its own HTTP surface: `routes()` returns the `/web/github/*` + * `/auth/github/*` Mastra `apiRoutes`. * * A custom integration (different app per tenant, custom Octokit config) can * subclass this and override individual methods — everything downstream only * talks to the instance. */ import type { RequestContext } from '@mastra/core/request-context'; import type { ApiRoute } from '@mastra/core/server'; import type { MastraWorker } from '@mastra/core/worker'; import { Octokit } from '@octokit/rest'; import type { Intake } from '../../capabilities/intake.js'; import type { VersionControl } from '../../capabilities/version-control.js'; import type { FactoryIntegration, IntegrationContext, IntegrationTools } from '../base.js'; import type { GithubEventRules, GithubRuleOverrides } from './default-rules.js'; import type { ReconcileIssueState, ReconcilePullRequestState } from './rules.js'; import type { GithubSubscriptionStorage } from './subscriptions.js'; /** * Normalize a PEM private key supplied via env. Env tooling tends to mangle * multi-line PEMs, so two single-line forms are supported: * - `\n`-escaped: literal `\n` sequences become real newlines * - fully flattened: newlines stripped entirely — the PEM is rebuilt by * re-wrapping the base64 body (Node's decoder rejects header/body/footer * on one line with `error:1E08010C:DECODER routines::unsupported`) */ export declare function normalizePrivateKey(raw: string): string; export interface UserInstallation { installationId: number; accountLogin: string | null; accountType: string | null; } export interface RepoSummary { id: number; fullName: string; name: string; owner: string; defaultBranch: string; private: boolean; installationId: number; } export interface GithubTriageCommentUpsertInput { installationId: number; repository: string; issueNumber: number; body: string; } export interface GithubTriageCommentUpsertResult { action: 'created' | 'updated'; commentId: string; url: string; } export type GithubRepositoryPermission = 'admin' | 'maintain' | 'write' | 'triage' | 'read' | 'none'; export interface IssueSummary { number: number; title: string; url: string; author: string | null; assignees: string[]; labels: string[]; comments: number; createdAt: string; updatedAt: string; } /** Page size for issue/PR listings; one GitHub API call per page. */ export declare const LIST_PAGE_SIZE = 30; export interface IssuePage { issues: IssueSummary[]; /** Next page number to request, or `null` when this was the last page. */ nextPage: number | null; } export interface ListRepoOpenIssuesOptions { label?: string; } export interface GithubIntegrationConfig { /** Replace an event handler, or disable it with null; omitted events keep defaults. */ rules?: GithubRuleOverrides; /** GitHub App id (the numeric id, as a string). */ appId: string; /** * App private key (PEM). Multi-line, `\n`-escaped, and fully flattened * single-line forms are all accepted (normalized at construction). */ privateKey: string; /** OAuth client id of the GitHub App. */ clientId: string; /** OAuth client secret of the GitHub App. */ clientSecret: string; /** App slug — the URL name used to build the install URL. */ slug: string; /** * Secret GitHub uses to sign webhook deliveries. Omitted → webhook * signature verification rejects all deliveries. Also serves as the default * replica-stable OAuth/install `state` secret (see `./config.ts`). */ webhookSecret?: string; /** * Extra bot logins authorized to trigger author-gated PR notifications * (reviews and comments). Merged over the built-in defaults, matched * case-insensitively. Deployments running their own reviewer bots opt them * in here; every other bot stays gated. */ authorizedBots?: string[]; } export declare class GithubIntegration implements FactoryIntegration { #private; /** Stable integration identifier (see `../factory-integration.ts`). */ readonly id = "github"; get rules(): GithubEventRules; readonly intake: Intake; readonly versionControl: VersionControl; /** * The OAuth/install flow round-trips a signed `state` through GitHub, so a * multi-replica deploy needs a deployment-stable state secret. */ readonly requiresStableStateSigner = true; constructor(config: GithubIntegrationConfig); /** App slug — the URL name used to build the install URL. */ get slug(): string; /** Whether a GitHub login belongs to this integration's App. */ isFactoryCommentAuthor(login: string | null | undefined): boolean; /** Extra bot logins authorized to trigger author-gated PR notifications. */ get authorizedBots(): readonly string[]; get genericStorage(): IntegrationContext['storage']['generic']; get sourceControlStorage(): IntegrationContext['storage']['sourceControl']; get integrationStorage(): GithubSubscriptionStorage; /** Secret GitHub uses to sign webhook deliveries, when configured. */ get webhookSecret(): string | undefined; /** * Octokit authenticated as the GitHub App itself (app JWT). Used for * app-level operations and to mint installation tokens. */ getAppOctokit(): Octokit; /** * Octokit authenticated as a specific installation (installation access * token). Used to list repos and to operate on a repo on the user's behalf. */ getInstallationOctokit(installationId: number): Octokit; /** Octokit authenticated as a user via their OAuth token (the identify step). */ getUserOctokit(userToken: string): Octokit; /** * Mint a short-lived installation access token. Returned token is used only * server-side / inside the sandbox clone URL and never sent to the browser. */ mintInstallationToken(installationId: number): Promise; /** * List the installations the authenticated user can access, via their OAuth * token (`GET /user/installations`). */ listUserInstallations(userToken: string): Promise; /** List repos accessible to an installation (paginated). */ listInstallationRepos(installationId: number): Promise; /** * The authenticated user's permission level on a repo, through the * installation. `undefined` when the repo/user is not accessible. */ getRepositoryCollaboratorPermission(installationId: number, repoFullName: string, username: string, signal?: AbortSignal): Promise; /** * Fetch a single repo's metadata through an installation token and confirm * the installation actually has access to it. Returns `null` when the repo * is not accessible to the installation (so a client can't create a project * for an arbitrary repo under an installation id it merely owns). */ getInstallationRepo(installationId: number, repoFullName: string): Promise; /** Add labels to an issue (deduplicated; no-op on empty/malformed input). */ addIssueLabels(installationId: number, repoFullName: string, issueNumber: number, labels: string[]): Promise; /** Remove one label from an issue. */ removeIssueLabel(installationId: number, repoFullName: string, issueNumber: number, label: string): Promise; /** * List one page of a repo's open issues through an installation token. The * issues API also returns pull requests, so those are filtered out (the * filter can make a non-final page shorter than the page size — `nextPage` * is derived from the raw response length, not the filtered one). */ listRepoOpenIssues(installationId: number, repoFullName: string, page: number, options?: ListRepoOpenIssuesOptions): Promise; /** * Build the GitHub App install URL. `state` is carried through the install * flow and validated on callback. */ buildInstallUrl(state: string): string; /** * Build the OAuth identify URL (authorize) used to confirm the user's * identity and obtain a user token for listing their installations. */ buildOAuthIdentifyUrl(state: string, redirectUri: string): string; /** Exchange an OAuth `code` for a user access token. */ exchangeOAuthCode(code: string, redirectUri: string): Promise; /** * The integration's HTTP surface: the `/web/github/*` + `/auth/github/*` * Mastra `apiRoutes` (webhook handler, install/OAuth flow, project + * worktree + session operations). The factory folds these into the server's * `apiRoutes` when the feature is ready. Handlers operate on this instance. */ routes(ctx: IntegrationContext): ApiRoute[]; /** * Pull-request and issue sweeps share a worker and lease, but can be enabled * and paced independently. The legacy combined controls remain the fallback. */ workers(ctx: IntegrationContext): MastraWorker[]; /** * Reads live PR state for the merge reconciler. Returns undefined when the PR * cannot be resolved so a sweep never fabricates a merge. */ fetchPullRequestState(input: { installationId: number; repository: string; number: number; }): Promise; /** * Reads live issue state for the reconciler. Returns undefined when the * issue cannot be resolved (missing, is actually a PR, API error) so a * sweep never fabricates a close. */ fetchIssueState(input: { installationId: number; repository: string; number: number; }): Promise; /** * Session-scoped agent tools for token refresh and PR subscriptions in * sessions bound to a GitHub-backed project. Empty elsewhere. */ upsertFactoryTriageComment(input: GithubTriageCommentUpsertInput): Promise; sessionTools({ requestContext }: { requestContext: RequestContext; }): IntegrationTools; postToolObserver({ toolContext, requestContext, }: Parameters>[0]): Promise; /** Non-secret config snapshot for system diagnostics/startup logs. */ diagnostics(): Record; } //# sourceMappingURL=integration.d.ts.map