@mastra/factory 0.5.0 → 0.6.0-alpha.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +42 -0
- package/dist/auth.d.ts +2 -0
- package/dist/auth.d.ts.map +1 -1
- package/dist/auth.js +2 -0
- package/dist/auth.js.map +1 -1
- package/dist/capabilities/intake.d.ts +1 -0
- package/dist/capabilities/intake.d.ts.map +1 -1
- package/dist/capabilities/version-control.d.ts +2 -0
- package/dist/capabilities/version-control.d.ts.map +1 -1
- package/dist/factory.d.ts.map +1 -1
- package/dist/factory.js +2 -1
- package/dist/factory.js.map +1 -1
- package/dist/integrations/base.d.ts +6 -0
- package/dist/integrations/base.d.ts.map +1 -1
- package/dist/integrations/github/integration.d.ts +1 -0
- package/dist/integrations/github/integration.d.ts.map +1 -1
- package/dist/integrations/github/integration.js +11 -1
- package/dist/integrations/github/integration.js.map +1 -1
- package/dist/integrations/github/routes.d.ts.map +1 -1
- package/dist/integrations/github/routes.js +20 -46
- package/dist/integrations/github/routes.js.map +1 -1
- package/dist/integrations/github/rules.d.ts +4 -0
- package/dist/integrations/github/rules.d.ts.map +1 -1
- package/dist/integrations/github/rules.js +68 -10
- package/dist/integrations/github/rules.js.map +1 -1
- package/dist/integrations/github/sandbox-release.d.ts +16 -1
- package/dist/integrations/github/sandbox-release.d.ts.map +1 -1
- package/dist/integrations/github/sandbox-release.js +42 -1
- package/dist/integrations/github/sandbox-release.js.map +1 -1
- package/dist/integrations/linear/integration.d.ts +2 -0
- package/dist/integrations/linear/integration.d.ts.map +1 -1
- package/dist/integrations/linear/integration.js +5 -1
- package/dist/integrations/linear/integration.js.map +1 -1
- package/dist/integrations/linear/routes.d.ts.map +1 -1
- package/dist/integrations/linear/routes.js +1 -0
- package/dist/integrations/linear/routes.js.map +1 -1
- package/dist/integrations/linear/rules.d.ts +2 -0
- package/dist/integrations/linear/rules.d.ts.map +1 -1
- package/dist/integrations/linear/rules.js.map +1 -1
- package/dist/integrations/platform/github/integration.d.ts.map +1 -1
- package/dist/integrations/platform/github/integration.js +7 -0
- package/dist/integrations/platform/github/integration.js.map +1 -1
- package/dist/integrations/slack/integration.d.ts.map +1 -1
- package/dist/integrations/slack/integration.js +3 -2
- package/dist/integrations/slack/integration.js.map +1 -1
- package/dist/integrations/slack/slack.d.ts +37 -110
- package/dist/integrations/slack/slack.d.ts.map +1 -1
- package/dist/integrations/slack/slack.js +49 -44
- package/dist/integrations/slack/slack.js.map +1 -1
- package/dist/routes/surface.d.ts +11 -1
- package/dist/routes/surface.d.ts.map +1 -1
- package/dist/routes/surface.js +18 -8
- package/dist/routes/surface.js.map +1 -1
- package/dist/rules/defaults.d.ts.map +1 -1
- package/dist/rules/defaults.js +35 -4
- package/dist/rules/defaults.js.map +1 -1
- package/dist/rules/start-coordinator.d.ts.map +1 -1
- package/dist/rules/start-coordinator.js +7 -26
- package/dist/rules/start-coordinator.js.map +1 -1
- package/dist/rules/types.d.ts +10 -0
- package/dist/rules/types.d.ts.map +1 -1
- package/dist/rules/types.js.map +1 -1
- package/dist/session/factory-session.d.ts +116 -0
- package/dist/session/factory-session.d.ts.map +1 -0
- package/dist/session/factory-session.js +162 -0
- package/dist/session/factory-session.js.map +1 -0
- package/dist/storage/domains/audit/domain.d.ts +9 -2
- package/dist/storage/domains/audit/domain.d.ts.map +1 -1
- package/dist/storage/domains/audit/domain.js +106 -4
- package/dist/storage/domains/audit/domain.js.map +1 -1
- package/dist/storage/domains/work-items/base.d.ts.map +1 -1
- package/dist/storage/domains/work-items/base.js +11 -0
- package/dist/storage/domains/work-items/base.js.map +1 -1
- package/package.json +7 -7
- package/dist/integrations/github/factory-session.d.ts +0 -12
- package/dist/integrations/github/factory-session.d.ts.map +0 -1
- package/dist/integrations/github/factory-session.js +0 -37
- package/dist/integrations/github/factory-session.js.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"integration.js","names":["#client","#endpointHost","#slug","#pollingEnabled","#pollingIntervalMs","#reconcileEnabled","#installationReposCache","#repositoryAccessCache","#runIssueTriage","#resolveIntakeDispatch","#listIssues","#listPullRequests","#getIssue","#createIssueComment","#updateIntakeIssue","#storage","#getPullRequest","#createPullRequest","#updatePullRequest","#mergePullRequest","#listComments","#createComment","#updateComment","#deleteComment","#listReviews","#getReview","#createReview","#updateReview","#submitReview","#dismissReview","#deletePendingReview","#listReviewComments","#createReviewComment","#updateReviewComment","#deleteReviewComment","#requestedReviewers","#integrationStorage","#statusRoute","#connectRoute","#connectUserRoute","#syncInstallations","#fetchUserConnection"],"sources":["../../../../src/integrations/platform/github/integration.ts"],"sourcesContent":["import type { RequestContext } from '@mastra/core/request-context';\nimport type { ApiRoute } from '@mastra/core/server';\nimport { registerApiRoute } from '@mastra/core/server';\nimport type { Context } from 'hono';\n\nimport type { IntegrationConnection } from '../../../capabilities/connection.js';\nimport type {\n CreateIntakeCommentInput,\n Intake,\n IntakeIssue,\n IntakeIssueDetail,\n UpdateIntakeIssueInput,\n} from '../../../capabilities/intake.js';\nimport type {\n CreatePullRequestCommentInput,\n CreatePullRequestInput,\n CreateReviewCommentInput,\n CreateReviewInput,\n DeletePullRequestCommentInput,\n DismissReviewInput,\n ListPullRequestCommentsInput,\n ListPullRequestsInput,\n ListReviewCommentsInput,\n ListReviewsInput,\n MergePullRequestInput,\n PullRequest,\n PullRequestComment,\n PullRequestRef,\n RepositoryAccess,\n Review,\n ReviewComment,\n ReviewRef,\n SubmitReviewInput,\n UpdatePullRequestCommentInput,\n UpdatePullRequestInput,\n UpdateReviewInput,\n UpdateReviewersInput,\n VersionControl,\n} from '../../../capabilities/version-control.js';\nimport type { IntegrationStorageHandle } from '../../../storage/domains/integrations/base.js';\nimport type {\n SourceControlInstallation,\n SourceControlStorageHandle,\n} from '../../../storage/domains/source-control/base.js';\nimport type { FactoryIntegration, IntegrationContext, IntegrationTools } from '../../base.js';\nimport type { GithubIntegration, GithubRepositoryPermission, RepoSummary } from '../../github/integration.js';\nimport type { GithubIssueTriageInput, GithubIssueTriageResult } from '../../github/issue-triage.js';\nimport { runGithubIssueTriage } from '../../github/issue-triage.js';\nimport { buildGithubRoutes } from '../../github/routes.js';\nimport { attachGithubReconciler, attachGithubRules } from '../../github/rules.js';\nimport type { ReconcilePullRequestState } from '../../github/rules.js';\nimport {\n createGithubSubscriptionTools,\n parseCreatedPullRequest,\n subscribeCurrentSessionToPullRequest,\n} from '../../github/session-subscriptions.js';\nimport type { GithubSubscriptionStorage } from '../../github/subscriptions.js';\nimport {\n logPlatformInfo,\n logPlatformWarn,\n PlatformApiClient,\n PlatformApiError,\n platformApiClientConfigFromEnv,\n} from '../api-client.js';\nimport { PlatformGithubEventWorker } from './event-worker.js';\nimport type { PlatformGithubEventStorage } from './event-worker.js';\n\ntype GithubActor = { login: string; avatarUrl: string | null; htmlUrl: string | null } | null;\n\ntype PlatformGithubInstallation = {\n installationId: number;\n accountLogin: string;\n accountType: string;\n suspendedAt: string | null;\n usable: boolean;\n};\n\ntype PlatformGithubUserConnection = {\n connected: boolean;\n githubUsername: string | null;\n reason?: 'token-invalid' | 'no-accessible-installation' | 'missing-permissions' | 'verification-unavailable' | null;\n};\n\ntype GithubIssue = {\n number: number;\n state: 'open' | 'closed';\n title: string;\n body: string | null;\n htmlUrl: string;\n labels: string[];\n assignees: string[];\n commentCount: number;\n user: GithubActor;\n createdAt: string;\n updatedAt: string;\n};\n\ntype GithubComment = {\n id: number;\n body: string;\n htmlUrl: string;\n user: GithubActor;\n createdAt: string;\n updatedAt: string;\n};\n\ntype GithubPullRequest = {\n number: number;\n title: string;\n body: string | null;\n state: 'open' | 'closed';\n htmlUrl: string;\n merged: boolean;\n mergeable: boolean | null;\n draft: boolean;\n head: { ref: string; sha: string };\n base: { ref: string; repo: { id: number; fullName: string } };\n user: GithubActor;\n createdAt: string;\n updatedAt: string;\n};\n\ntype GithubReview = {\n id: number;\n htmlUrl: string | null;\n body: string | null;\n state: 'PENDING' | 'COMMENTED' | 'APPROVED' | 'CHANGES_REQUESTED' | 'DISMISSED';\n commitId: string | null;\n submittedAt: string | null;\n user: GithubActor;\n};\n\ntype GithubReviewComment = GithubComment & {\n path: string;\n line: number | null;\n side: 'LEFT' | 'RIGHT' | null;\n commitId: string;\n replyToId: number | null;\n};\n\nconst PAGE_SIZE = 30;\nconst API_PREFIX = '/v1/server';\n/**\n * How long an installation's repository listing may be reused. The repos\n * route and token minting both call it — often several times within one UI\n * interaction — and each call is a full Platform round trip.\n */\nconst INSTALLATION_REPOS_CACHE_TTL_MS = 30_000;\n/**\n * How long a minted repository-scoped token may be reused. GitHub\n * installation tokens live ~60 minutes; 5 minutes keeps a wide validity\n * margin while collapsing the per-session-materialization mint round trip.\n */\nconst REPOSITORY_ACCESS_CACHE_TTL_MS = 5 * 60_000;\n/**\n * Upper bound for both TTL caches. Entries expire lazily on re-access, so\n * without a hard cap keys that stop being queried would accumulate for the\n * integration's (long) lifetime. Insertion-order eviction — matches the\n * bounded verification cache in `@mastra/auth-studio`.\n */\nconst MAX_CACHE_ENTRIES = 1000;\n\nfunction setBounded<K, V>(cache: Map<K, V>, key: K, value: V): void {\n cache.delete(key);\n cache.set(key, value);\n if (cache.size > MAX_CACHE_ENTRIES) {\n const oldest = cache.keys().next().value;\n if (oldest !== undefined) cache.delete(oldest);\n }\n}\n\nconst REPOSITORY_TOKEN_PERMISSIONS = {\n contents: 'write',\n issues: 'write',\n pull_requests: 'write',\n} as const;\n\nfunction loose(c: unknown): Context {\n return c as Context;\n}\n\nfunction routeBaseUrl(ctx: IntegrationContext, requestUrl: string): string {\n return (ctx.baseUrl || new URL(requestUrl).origin).replace(/\\/+$/, '');\n}\n\nexport class PlatformGithubIntegration implements FactoryIntegration {\n readonly id = 'github';\n readonly #client: PlatformApiClient;\n readonly #endpointHost: string;\n readonly #slug: string | undefined;\n readonly #pollingEnabled: boolean;\n readonly #pollingIntervalMs: number | undefined;\n readonly #reconcileEnabled: boolean;\n #storage: SourceControlStorageHandle | undefined;\n #integrationStorage: GithubSubscriptionStorage | undefined;\n /** installationId → cached repository listing (TTL-bounded). */\n readonly #installationReposCache = new Map<number, { repos: RepoSummary[]; expiresAt: number }>();\n /** `orgId:repositoryId` → cached repository access (TTL-bounded). */\n readonly #repositoryAccessCache = new Map<string, { access: RepositoryAccess; expiresAt: number }>();\n readonly #runIssueTriage?: (input: GithubIssueTriageInput) => Promise<GithubIssueTriageResult>;\n\n readonly intake: Intake = {\n resolveIntakeDispatch: input => this.#resolveIntakeDispatch(input),\n listSources: async ({ orgId, userId }) => {\n const installations = await this.#client.request<{\n installations: Array<{\n installationId: number;\n accountLogin: string;\n accountType: string;\n suspendedAt: string | null;\n usable: boolean;\n }>;\n }>('GET', `${API_PREFIX}/github-app/installations`);\n const usable = installations.installations.filter(\n installation => installation.usable && !installation.suspendedAt,\n );\n const repositories = await Promise.all(\n usable.map(async installation => {\n const storedInstallation = await this.versionControl.registerInstallation({\n orgId,\n userId,\n installation: {\n externalId: String(installation.installationId),\n accountName: installation.accountLogin,\n accountType: installation.accountType,\n },\n });\n const result = await this.#client.request<{\n repositories: Array<{\n id: number;\n fullName: string;\n private: boolean;\n defaultBranch: string;\n htmlUrl: string;\n }>;\n }>('GET', `${API_PREFIX}/github-app/installations/${installation.installationId}/repositories`);\n await this.versionControl.registerRepositories({\n orgId,\n installationId: storedInstallation.id,\n repositories: result.repositories.map(repository => ({\n externalId: String(repository.id),\n slug: repository.fullName,\n defaultBranch: repository.defaultBranch,\n metadata: { private: repository.private, url: repository.htmlUrl },\n })),\n });\n return result.repositories.map(repository => ({ repository, installation }));\n }),\n );\n return repositories.flat().map(({ repository, installation }) => ({\n id: repository.fullName,\n name: repository.fullName,\n type: 'repository',\n metadata: {\n installationId: installation.installationId,\n accountLogin: installation.accountLogin,\n accountType: installation.accountType,\n repositoryId: repository.id,\n defaultBranch: repository.defaultBranch,\n private: repository.private,\n url: repository.htmlUrl,\n },\n }));\n },\n listItems: async ({ sourceIds, cursor }) => {\n const page = parsePositiveCursor(cursor);\n const pages = await Promise.all(\n sourceIds.map(async sourceId => {\n const connection = { type: 'app-installation' as const, installationId: 1 };\n const [issues, pullRequests] = await Promise.all([\n this.#listIssues(connection, sourceId, page),\n this.#listPullRequests({ connection, sourceId, includeDrafts: false, cursor: String(page) }),\n ]);\n return {\n items: [\n ...issues.issues.map(issue => ({\n source: { type: 'issue', externalId: `${sourceId}:${issue.id}`, url: issue.url },\n sourceId,\n title: issue.title,\n status: issue.state ?? undefined,\n labels: issue.labels,\n assignee: issue.assignee,\n createdAt: issue.createdAt,\n updatedAt: issue.updatedAt,\n metadata: { repository: sourceId, number: Number(issue.id), author: issue.author },\n })),\n ...pullRequests.pullRequests.map(pullRequest => ({\n source: { type: 'pull-request', externalId: `${sourceId}:${pullRequest.id}`, url: pullRequest.url },\n sourceId,\n title: pullRequest.title,\n status: pullRequest.state,\n createdAt: pullRequest.createdAt,\n updatedAt: pullRequest.updatedAt,\n metadata: {\n repository: sourceId,\n number: Number(pullRequest.id),\n author: pullRequest.author,\n baseBranch: pullRequest.baseBranch,\n headBranch: pullRequest.headBranch,\n },\n })),\n ],\n hasNextPage: issues.nextCursor !== null || pullRequests.nextCursor !== null,\n };\n }),\n );\n return {\n items: pages.flatMap(result => result.items),\n nextCursor: pages.some(result => result.hasNextPage) ? String(page + 1) : null,\n };\n },\n listIssues: async input => {\n requireGithubConnection(input.connection);\n const sourceId = requireSingleSource(input.sourceIds, 'GitHub Intake requires exactly one repository source.');\n return this.#listIssues(input.connection, sourceId, parsePositiveCursor(input.cursor), input.labels);\n },\n getIssue: input => this.#getIssue(input.connection, input.sourceId, input.issueId),\n createComment: input => this.#createIssueComment(input),\n updateIssue: input => this.#updateIntakeIssue(input),\n };\n\n readonly versionControl: VersionControl = {\n initialize: ({ storage }) => {\n this.#storage = storage;\n },\n registerInstallation: ({ orgId, userId, installation }) =>\n this.storage.installations.upsert({\n orgId,\n connectedByUserId: userId,\n externalId: installation.externalId,\n accountName: installation.accountName,\n accountType: installation.accountType,\n providerMetadata: installation.metadata,\n }),\n registerRepositories: ({ orgId, installationId, repositories }) =>\n Promise.all(\n repositories.map(repository =>\n this.storage.repositories.upsert({\n orgId,\n input: {\n installationId,\n externalId: repository.externalId,\n slug: repository.slug,\n defaultBranch: repository.defaultBranch,\n providerMetadata: repository.metadata,\n },\n }),\n ),\n ),\n getRepositoryAccess: async ({ orgId, repositoryId }) => {\n // Every session materialization requests access; reuse a recent grant\n // instead of re-minting through the Platform each time. The TTL keeps\n // a wide margin under GitHub's ~60min installation-token lifetime.\n const cacheKey = `${orgId}:${repositoryId}`;\n const cached = this.#repositoryAccessCache.get(cacheKey);\n if (cached && cached.expiresAt > Date.now()) return cached.access;\n this.#repositoryAccessCache.delete(cacheKey);\n\n const repository = await this.storage.repositories.get({ orgId, id: repositoryId });\n if (!repository) throw new Error('Version-control repository not found.');\n const cloneUrl = `https://github.com/${repository.slug}.git`;\n const installation = await this.storage.installations.get({ orgId, id: repository.installationId });\n if (!installation) throw new Error('Version-control installation not found.');\n const installationId = parsePositiveInteger(installation.externalId);\n if (installationId === null) throw new Error('GitHub installation id is invalid.');\n const repositoryName = splitRepository(repository.slug).repo;\n\n try {\n const token = await this.#client.request<{ token: string }>(\n 'POST',\n `${API_PREFIX}/github-app/installations/${installationId}/token`,\n { repositories: [repositoryName], permissions: REPOSITORY_TOKEN_PERMISSIONS },\n );\n const access: RepositoryAccess = {\n cloneUrl,\n authorization: { scheme: 'bearer', token: token.token },\n };\n setBounded(this.#repositoryAccessCache, cacheKey, {\n access,\n expiresAt: Date.now() + REPOSITORY_ACCESS_CACHE_TTL_MS,\n });\n return access;\n } catch (err) {\n // Recover from stale installation: when a GitHub App is uninstalled and reinstalled,\n // GitHub assigns a new installation ID. Platform creates a new installation row,\n // and Factory's intake.listSources creates a new local installation row with the\n // new externalId. Try to find the new installation by accountName.\n if (!isDeadInstallation(err) || !installation.accountName) throw err;\n const installations = await this.storage.installations.list({ orgId });\n const newInstallation = installations.find(\n inst => inst.accountName === installation.accountName && inst.id !== installation.id,\n );\n if (!newInstallation) throw err;\n const newInstallationId = parsePositiveInteger(newInstallation.externalId);\n if (newInstallationId === null) throw err;\n\n // Persist the migration so we don't hit the recovery path on every call.\n await this.storage.repositories.migrateInstallation({\n orgId,\n id: repositoryId,\n newInstallationId: newInstallation.id,\n });\n\n const token = await this.#client.request<{ token: string }>(\n 'POST',\n `${API_PREFIX}/github-app/installations/${newInstallationId}/token`,\n { repositories: [repositoryName], permissions: REPOSITORY_TOKEN_PERMISSIONS },\n );\n const access: RepositoryAccess = {\n cloneUrl,\n authorization: { scheme: 'bearer', token: token.token },\n };\n setBounded(this.#repositoryAccessCache, cacheKey, {\n access,\n expiresAt: Date.now() + REPOSITORY_ACCESS_CACHE_TTL_MS,\n });\n return access;\n }\n },\n listPullRequests: input => this.#listPullRequests(input),\n getPullRequest: input => this.#getPullRequest(input),\n createPullRequest: input => this.#createPullRequest(input),\n updatePullRequest: input => this.#updatePullRequest(input),\n closePullRequest: input => this.#updatePullRequest({ ...input, state: 'closed' }),\n mergePullRequest: input => this.#mergePullRequest(input),\n listComments: input => this.#listComments(input),\n createComment: input => this.#createComment(input),\n updateComment: input => this.#updateComment(input),\n deleteComment: input => this.#deleteComment(input),\n listReviews: input => this.#listReviews(input),\n getReview: input => this.#getReview(input),\n createReview: input => this.#createReview(input),\n updateReview: input => this.#updateReview(input),\n submitReview: input => this.#submitReview(input),\n dismissReview: input => this.#dismissReview(input),\n deletePendingReview: input => this.#deletePendingReview(input),\n listReviewComments: input => this.#listReviewComments(input),\n createReviewComment: input => this.#createReviewComment(input),\n updateReviewComment: input => this.#updateReviewComment(input),\n deleteReviewComment: input => this.#deleteReviewComment(input),\n listRequestedReviewers: input => this.#requestedReviewers('GET', input),\n requestReviewers: input => this.#requestedReviewers('POST', input),\n removeRequestedReviewers: input => this.#requestedReviewers('DELETE', input),\n };\n\n constructor(\n options: {\n runIssueTriage?: (input: GithubIssueTriageInput) => Promise<GithubIssueTriageResult>;\n /** GitHub App slug used to recognize Factory's own webhook writes. */\n slug?: string;\n } = {},\n ) {\n const config = platformApiClientConfigFromEnv();\n this.#client = new PlatformApiClient(config);\n this.#endpointHost = new URL(config.baseUrl).host;\n this.#slug = options.slug;\n this.#pollingEnabled = process.env.MASTRA_PLATFORM_GITHUB_POLLING_ENABLED?.trim().toLowerCase() !== 'false';\n this.#pollingIntervalMs = optionalPositiveIntegerEnv('MASTRA_PLATFORM_GITHUB_POLLING_INTERVAL_MS');\n this.#reconcileEnabled = process.env.MASTRA_PLATFORM_GITHUB_RECONCILE_ENABLED?.trim().toLowerCase() !== 'false';\n this.#runIssueTriage = options.runIssueTriage;\n }\n\n /** GitHub App slug when the deployment explicitly provides it. */\n get slug(): string | undefined {\n return this.#slug;\n }\n\n get storage(): SourceControlStorageHandle {\n if (!this.#storage) throw new Error('PlatformGithubIntegration source-control storage has not been initialized.');\n return this.#storage;\n }\n\n get sourceControlStorage(): SourceControlStorageHandle {\n return this.storage;\n }\n\n get integrationStorage(): GithubSubscriptionStorage {\n if (!this.#integrationStorage) {\n throw new Error('PlatformGithubIntegration generic storage has not been initialized.');\n }\n return this.#integrationStorage;\n }\n\n /** Resolve a stored GitHub locator without scanning installations or repositories. */\n async #resolveIntakeDispatch({\n orgId,\n externalSource,\n }: {\n orgId: string;\n externalSource: { type: string; externalId: string };\n }): Promise<{ connection: IntegrationConnection; sourceId: string; issueId: string } | null> {\n const target = parseGithubExternalTarget(externalSource.externalId);\n if (!target) return null;\n const repository = target.repository.includes('/')\n ? target.repository\n : (await this.storage.repositories.findByExternalId({ orgId, externalId: target.repository }))?.slug;\n if (!repository) return null;\n return {\n connection: { type: 'app-installation', installationId: 1 },\n sourceId: repository,\n issueId: target.issueId,\n };\n }\n\n initialize({ storage }: { storage: IntegrationStorageHandle }): void {\n this.#integrationStorage = storage as unknown as GithubSubscriptionStorage;\n logPlatformInfo('Platform GitHub integration initialized', {\n endpointHost: this.#endpointHost,\n pollingEnabled: this.#pollingEnabled,\n pollingIntervalMs: this.#pollingIntervalMs,\n reconcileEnabled: this.#reconcileEnabled,\n });\n }\n\n routes(ctx: IntegrationContext): ApiRoute[] {\n const ingestFactoryEvent = attachGithubRules(this, ctx);\n return [\n this.#statusRoute(ctx),\n this.#connectRoute(ctx),\n this.#connectUserRoute(ctx),\n ...buildGithubRoutes({\n auth: ctx.auth,\n fleet: ctx.fleet,\n storage: ctx.factoryStorage,\n github: this as unknown as GithubIntegration,\n stateSigner: ctx.stateSigner,\n baseUrl: ctx.baseUrl,\n controller: ctx.controller,\n projects: ctx.storage.projects,\n emitAudit: ctx.hooks?.emitAudit,\n ingestFactoryEvent,\n runIssueTriage:\n this.#runIssueTriage ??\n (ctx.controller ? input => runGithubIssueTriage({ controller: ctx.controller!, input }) : undefined),\n }).filter(\n route =>\n route.path !== '/web/github/status' &&\n route.path !== '/web/github/webhook' &&\n !route.path.startsWith('/auth/github/'),\n ),\n ];\n }\n\n #statusRoute(ctx: IntegrationContext): ApiRoute {\n return registerApiRoute('/web/github/status', {\n method: 'GET',\n requiresAuth: false,\n handler: async c => {\n await ctx.auth.ensureUser(loose(c));\n const tenant = ctx.auth.tenant(loose(c));\n if (!tenant) return c.json({ error: 'unauthorized', reason: 'auth_required' }, 401);\n if (!tenant.orgId) {\n return c.json({\n enabled: true,\n sandboxEnabled: ctx.fleet.enabled,\n organizationRequired: true,\n connected: false,\n installations: [],\n userConnected: false,\n userGithubUsername: null,\n reason: 'organization_required',\n diagnostics: this.diagnostics(),\n });\n }\n\n const [installations, userConnection] = await Promise.all([\n this.#syncInstallations(tenant.orgId, tenant.userId),\n this.#fetchUserConnection(tenant.userId),\n ]);\n return c.json({\n enabled: true,\n sandboxEnabled: ctx.fleet.enabled,\n connected: installations.length > 0,\n installations: installations.map(installation => ({\n installationId: Number(installation.externalId),\n accountLogin: installation.accountName,\n accountType: installation.accountType,\n })),\n userConnected: userConnection.connected,\n userGithubUsername: userConnection.githubUsername,\n reason: installations.length > 0 ? 'ready' : 'not_connected',\n diagnostics: this.diagnostics(),\n });\n },\n });\n }\n\n #connectRoute(ctx: IntegrationContext): ApiRoute {\n return registerApiRoute('/auth/github/connect', {\n method: 'GET',\n requiresAuth: false,\n handler: async c => {\n await ctx.auth.ensureUser(loose(c));\n const tenant = ctx.auth.tenant(loose(c));\n if (!tenant?.orgId) return c.json({ error: 'unauthorized' }, 401);\n\n const redirectTo = c.req.query('redirectTo') || c.req.query('return_to') || '/';\n const originator = routeBaseUrl(ctx, c.req.url);\n logPlatformInfo('Starting Platform GitHub connect flow', {\n orgId: tenant.orgId,\n redirectTo,\n originator,\n });\n const query = new URLSearchParams({\n action: 'install',\n redirectTo,\n originator,\n });\n const { url } = await this.#client.request<{ url: string }>(\n 'GET',\n `${API_PREFIX}/github-app/install-url?${query}`,\n );\n return c.redirect(url);\n },\n });\n }\n\n #connectUserRoute(ctx: IntegrationContext): ApiRoute {\n return registerApiRoute('/auth/github/connect-user', {\n method: 'GET',\n requiresAuth: false,\n handler: async c => {\n await ctx.auth.ensureUser(loose(c));\n const tenant = ctx.auth.tenant(loose(c));\n if (!tenant?.orgId) return c.json({ error: 'unauthorized' }, 401);\n\n const redirectTo = c.req.query('redirectTo') || c.req.query('return_to') || '/';\n const originator = routeBaseUrl(ctx, c.req.url);\n logPlatformInfo('Starting Platform GitHub user authorization flow', {\n orgId: tenant.orgId,\n redirectTo,\n originator,\n });\n const query = new URLSearchParams({\n userId: tenant.userId,\n redirectTo,\n originator,\n });\n const { url } = await this.#client.request<{ url: string }>(\n 'GET',\n `${API_PREFIX}/github-app/authenticate?${query}`,\n );\n return c.redirect(url);\n },\n });\n }\n\n /**\n * Personal GitHub connection status for the acting user. Returns\n * not-connected when the platform predates the user-connection endpoint.\n */\n async #fetchUserConnection(userId: string): Promise<PlatformGithubUserConnection> {\n try {\n const connection = await this.#client.request<PlatformGithubUserConnection>(\n 'GET',\n `${API_PREFIX}/github-app/user-connection?${new URLSearchParams({ userId })}`,\n );\n if (!connection.connected && connection.reason) {\n logPlatformWarn('Platform GitHub user connection verification failed', {\n userId,\n reason: connection.reason,\n });\n }\n return connection;\n } catch {\n return { connected: false, githubUsername: null };\n }\n }\n\n async #syncInstallations(orgId: string, userId: string): Promise<SourceControlInstallation[]> {\n const result = await this.#client.request<{ installations: PlatformGithubInstallation[] }>(\n 'GET',\n `${API_PREFIX}/github-app/installations`,\n );\n const usableInstallations = result.installations.filter(\n installation => installation.usable && !installation.suspendedAt,\n );\n return Promise.all(\n usableInstallations.map(installation =>\n this.versionControl.registerInstallation({\n orgId,\n userId,\n installation: {\n externalId: String(installation.installationId),\n accountName: installation.accountLogin,\n accountType: installation.accountType,\n },\n }),\n ),\n );\n }\n\n workers(ctx: IntegrationContext): PlatformGithubEventWorker[] {\n if (!this.#pollingEnabled && !this.#reconcileEnabled) return [];\n if (!ctx.controller) {\n throw new Error('Platform GitHub event polling requires the mounted Mastra Code controller.');\n }\n return [\n new PlatformGithubEventWorker({\n client: this.#client,\n controller: ctx.controller,\n github: this,\n storage: ctx.storage.generic as unknown as PlatformGithubEventStorage,\n ingestFactoryEvent: attachGithubRules(this, ctx),\n reconcileFactoryState: this.#reconcileEnabled\n ? attachGithubReconciler(this, ctx, input => this.fetchPullRequestState(input))\n : undefined,\n pollEventsEnabled: this.#pollingEnabled,\n intervalMs: this.#pollingIntervalMs,\n }),\n ];\n }\n\n /**\n * Reads live PR state through the Platform GitHub proxy for the merge\n * reconciler. Returns undefined when the PR cannot be resolved (missing,\n * proxy error) so a sweep never fabricates a merge.\n */\n async fetchPullRequestState(input: {\n installationId: number;\n repository: string;\n number: number;\n }): Promise<ReconcilePullRequestState | undefined> {\n let repository: { owner: string; repo: string };\n try {\n repository = splitRepository(input.repository);\n } catch {\n return undefined;\n }\n try {\n const result = await this.#client.request<{\n title?: string;\n html_url?: string;\n state?: string;\n merged?: boolean;\n created_at?: string;\n merged_by?: { login?: string } | null;\n head?: { ref?: string };\n base?: { ref?: string };\n }>(\n 'GET',\n `${API_PREFIX}/github/repos/${encodeURIComponent(repository.owner)}/${encodeURIComponent(repository.repo)}/pulls/${input.number}`,\n );\n return {\n title: result.title ?? `PR ${input.number}`,\n url: result.html_url ?? `https://github.com/${input.repository}/pull/${input.number}`,\n state: result.state === 'closed' ? 'closed' : 'open',\n merged: result.merged === true,\n headBranch: result.head?.ref ?? '',\n baseBranch: result.base?.ref ?? '',\n ...(result.created_at ? { createdAt: result.created_at } : {}),\n ...(result.merged_by?.login ? { mergedBy: result.merged_by.login } : {}),\n };\n } catch {\n return undefined;\n }\n }\n\n sessionTools({ requestContext }: { requestContext: RequestContext }): IntegrationTools {\n return createGithubSubscriptionTools(requestContext, this as unknown as GithubIntegration);\n }\n\n async postToolObserver({\n toolContext,\n requestContext,\n }: Parameters<NonNullable<FactoryIntegration['postToolObserver']>>[0]): Promise<void> {\n const pullRequestUrl = parseCreatedPullRequest(toolContext);\n if (!pullRequestUrl || !requestContext) return;\n await subscribeCurrentSessionToPullRequest(\n requestContext,\n pullRequestUrl,\n 'auto-gh-pr-create',\n this as unknown as GithubIntegration,\n );\n }\n\n diagnostics(): Record<string, unknown> {\n return {\n mode: 'platform',\n endpointHost: this.#endpointHost,\n polling: {\n enabled: this.#pollingEnabled,\n ...(this.#pollingIntervalMs === undefined ? {} : { intervalMs: this.#pollingIntervalMs }),\n },\n reconcile: { enabled: this.#reconcileEnabled },\n };\n }\n\n async getRepositoryCollaboratorPermission(\n _installationId: number,\n repoFullName: string,\n username: string,\n signal?: AbortSignal,\n ): Promise<GithubRepositoryPermission | undefined> {\n let repository: { owner: string; repo: string };\n try {\n repository = splitRepository(repoFullName);\n } catch {\n return undefined;\n }\n try {\n const result = await this.#client.request<{ permission: GithubRepositoryPermission }>(\n 'GET',\n `${API_PREFIX}/github/repos/${encodeURIComponent(repository.owner)}/${encodeURIComponent(repository.repo)}/collaborators/${encodeURIComponent(username)}/permission`,\n undefined,\n { signal },\n );\n return result.permission;\n } catch {\n return undefined;\n }\n }\n\n async listInstallationRepos(installationId: number): Promise<RepoSummary[]> {\n // Hot in two places — the repos route and token minting (which re-lists\n // only to validate the 1–10 repo rule). A short TTL collapses repeat\n // Platform round trips within one UI interaction.\n const cached = this.#installationReposCache.get(installationId);\n if (cached && cached.expiresAt > Date.now()) return cached.repos;\n this.#installationReposCache.delete(installationId);\n\n const result = await this.#client.request<{\n repositories: Array<{\n id: number;\n owner: string;\n name: string;\n fullName: string;\n private: boolean;\n defaultBranch: string;\n }>;\n }>('GET', `${API_PREFIX}/github-app/installations/${installationId}/repositories`);\n const repos = result.repositories.map(repository => ({ ...repository, installationId }));\n setBounded(this.#installationReposCache, installationId, {\n repos,\n expiresAt: Date.now() + INSTALLATION_REPOS_CACHE_TTL_MS,\n });\n return repos;\n }\n\n async mintInstallationToken(installationId: number): Promise<string> {\n const repositories = await this.listInstallationRepos(installationId);\n if (repositories.length === 0 || repositories.length > 10) {\n throw new Error('Platform GitHub token minting requires between one and ten installation repositories.');\n }\n const result = await this.#client.request<{ token: string }>(\n 'POST',\n `${API_PREFIX}/github-app/installations/${installationId}/token`,\n { repositories: repositories.map(repository => repository.name), permissions: REPOSITORY_TOKEN_PERMISSIONS },\n );\n return result.token;\n }\n\n async addIssueLabels(\n _installationId: number,\n sourceId: string,\n issueNumber: number,\n labels: string[],\n ): Promise<string[]> {\n const result = await this.#client.request<{ labels: string[] }>(\n 'POST',\n repositoryPath(sourceId, `issues/${issueNumber}/labels`),\n { labels },\n );\n return result.labels;\n }\n\n getInstallationOctokit(_installationId: number): ReturnType<GithubIntegration['getInstallationOctokit']> {\n return {\n pulls: {\n get: async ({ owner, repo, pull_number }: { owner: string; repo: string; pull_number: number }) => {\n const data = await this.#client.request<GithubPullRequest>(\n 'GET',\n repositoryPath(`${owner}/${repo}`, `pulls/${pull_number}`),\n );\n return { data: { base: { repo: { id: data.base.repo.id } } } };\n },\n },\n } as unknown as ReturnType<GithubIntegration['getInstallationOctokit']>;\n }\n\n async #listIssues(connection: IntegrationConnection, sourceId: string, page: number, labels?: string[]) {\n requireGithubConnection(connection);\n const path = repositoryPath(sourceId, 'issues');\n const query = new URLSearchParams({ state: 'open', page: String(page), per_page: String(PAGE_SIZE) });\n const normalizedLabels = normalizeLabels(labels);\n if (normalizedLabels.length > 0) query.set('label', normalizedLabels.join(','));\n const result = await this.#client.request<{ issues: GithubIssue[] }>('GET', `${path}?${query}`);\n return {\n issues: result.issues.map(issue => parseIntakeIssue(sourceId, issue)),\n nextCursor: result.issues.length > 0 ? String(page + 1) : null,\n };\n }\n\n async #getIssue(connection: IntegrationConnection, sourceId: string | undefined, issueId: string) {\n requireGithubConnection(connection);\n const repository = requireSource(sourceId, 'GitHub Intake requires a repository source.');\n const issueNumber = requirePositiveId(issueId, 'issue');\n try {\n const [issue, comments] = await Promise.all([\n this.#client.request<GithubIssue>('GET', repositoryPath(repository, `issues/${issueNumber}`)),\n this.#client.request<{ comments: GithubComment[] }>(\n 'GET',\n `${repositoryPath(repository, `issues/${issueNumber}/comments`)}?per_page=100`,\n ),\n ]);\n return parseIntakeIssueDetail(repository, issue, comments.comments);\n } catch (error) {\n if (isNotFound(error)) return null;\n throw error;\n }\n }\n\n async #updateIntakeIssue(input: UpdateIntakeIssueInput): Promise<IntakeIssue | null> {\n requireGithubConnection(input.connection);\n const repository = requireSource(input.sourceId, 'GitHub Intake requires a repository source.');\n const issueNumber = requirePositiveId(input.issueId, 'issue');\n if (input.state.kind === 'byName') {\n logPlatformWarn(`Platform GitHub: updateIssue byName is not supported (name=${input.state.name}); ignoring.`);\n return null;\n }\n const targetState: 'open' | 'closed' =\n input.state.stateType === 'unstarted' || input.state.stateType === 'started' ? 'open' : 'closed';\n const stateReason: 'completed' | 'not_planned' | null =\n targetState === 'closed' ? (input.state.stateType === 'canceled' ? 'not_planned' : 'completed') : null;\n // Reject PR targets: probe the pulls endpoint. A 200 means the number is a\n // pull request, not an issue. Factory does not close PRs via updateIssue —\n // PR merges/closes go through the version-control pipeline.\n try {\n await this.#client.request<GithubPullRequest>('GET', repositoryPath(repository, `pulls/${issueNumber}`));\n logPlatformWarn(`Platform GitHub: updateIssue rejected — target ${repository}#${issueNumber} is a pull request.`);\n return null;\n } catch (error) {\n if (!isNotFound(error)) throw error;\n // 404 on pulls means it's an issue (or nothing) — fall through to PATCH.\n }\n try {\n const issue = await this.#client.request<GithubIssue>(\n 'PATCH',\n repositoryPath(repository, `issues/${issueNumber}`),\n {\n state: targetState,\n ...(stateReason ? { state_reason: stateReason } : {}),\n },\n { actingUserId: input.actingUserId },\n );\n return parseIntakeIssue(repository, issue);\n } catch (error) {\n if (isNotFound(error)) return null;\n throw error;\n }\n }\n\n async #createIssueComment(input: CreateIntakeCommentInput) {\n requireGithubConnection(input.connection);\n const repository = requireSource(input.sourceId, 'GitHub Intake requires a repository source.');\n const issueNumber = requirePositiveId(input.issueId, 'issue');\n try {\n const comment = await this.#client.request<GithubComment>(\n 'POST',\n repositoryPath(repository, `issues/${issueNumber}/comments`),\n { body: input.body },\n { actingUserId: input.actingUserId },\n );\n return { id: String(comment.id), url: comment.htmlUrl };\n } catch (error) {\n if (isNotFound(error)) return null;\n throw error;\n }\n }\n\n async #listPullRequests(input: ListPullRequestsInput) {\n requireGithubConnection(input.connection);\n const page = parsePositiveCursor(input.cursor);\n const query = new URLSearchParams({\n state: input.state ?? 'open',\n page: String(page),\n per_page: String(PAGE_SIZE),\n });\n const result = await this.#client.request<{ pullRequests: GithubPullRequest[] }>(\n 'GET',\n `${repositoryPath(input.sourceId, 'pulls')}?${query}`,\n );\n return {\n pullRequests: result.pullRequests\n .filter(pullRequest => input.includeDrafts !== false || !pullRequest.draft)\n .map(parsePullRequest),\n nextCursor: result.pullRequests.length === PAGE_SIZE ? String(page + 1) : null,\n };\n }\n\n async #getPullRequest(input: PullRequestRef) {\n try {\n return parsePullRequest(\n await this.#client.request<GithubPullRequest>('GET', pullRequestPath(input, input.pullRequestId)),\n );\n } catch (error) {\n if (isNotFound(error)) return null;\n throw error;\n }\n }\n\n async #createPullRequest(input: CreatePullRequestInput) {\n requireGithubConnection(input.connection);\n const result = await this.#client.request<GithubPullRequest>(\n 'POST',\n repositoryPath(input.sourceId, 'pulls'),\n {\n head: input.headBranch,\n base: input.baseBranch,\n title: input.title,\n body: input.body,\n draft: input.draft,\n },\n { actingUserId: input.actingUserId },\n );\n return parsePullRequest(result);\n }\n\n async #updatePullRequest(input: UpdatePullRequestInput) {\n const result = await this.#client.request<GithubPullRequest>(\n 'PATCH',\n pullRequestPath(input, input.pullRequestId),\n {\n title: input.title,\n body: input.body === null ? '' : input.body,\n base: input.baseBranch,\n state: input.state,\n },\n { actingUserId: input.actingUserId },\n );\n return parsePullRequest(result);\n }\n\n #mergePullRequest(input: MergePullRequestInput) {\n return this.#client.request<{ merged: boolean; message: string; sha: string | null }>(\n 'PUT',\n `${pullRequestPath(input, input.pullRequestId)}/merge`,\n { commitTitle: input.commitTitle, commitMessage: input.commitMessage, method: input.method },\n { actingUserId: input.actingUserId },\n );\n }\n\n async #listComments(input: ListPullRequestCommentsInput) {\n const page = parsePositiveCursor(input.cursor);\n const result = await this.#client.request<{ comments: GithubComment[] }>(\n 'GET',\n `${repositoryPath(input.sourceId, `issues/${requirePositiveId(input.pullRequestId, 'pull request')}/comments`)}?page=${page}&per_page=${PAGE_SIZE}`,\n );\n return {\n comments: result.comments.map(parseComment),\n nextCursor: result.comments.length === PAGE_SIZE ? String(page + 1) : null,\n };\n }\n\n async #createComment(input: CreatePullRequestCommentInput) {\n const comment = await this.#client.request<GithubComment>(\n 'POST',\n repositoryPath(input.sourceId, `issues/${requirePositiveId(input.pullRequestId, 'pull request')}/comments`),\n { body: input.body },\n { actingUserId: input.actingUserId },\n );\n return parseComment(comment);\n }\n\n async #updateComment(input: UpdatePullRequestCommentInput) {\n requireGithubConnection(input.connection);\n const comment = await this.#client.request<GithubComment>(\n 'PATCH',\n repositoryPath(input.sourceId, `issues/comments/${requirePositiveId(input.commentId, 'comment')}`),\n { body: input.body },\n { actingUserId: input.actingUserId },\n );\n return parseComment(comment);\n }\n\n async #deleteComment(input: DeletePullRequestCommentInput) {\n requireGithubConnection(input.connection);\n await this.#client.request<void>(\n 'DELETE',\n repositoryPath(input.sourceId, `issues/comments/${requirePositiveId(input.commentId, 'comment')}`),\n undefined,\n { actingUserId: input.actingUserId },\n );\n }\n\n async #listReviews(input: ListReviewsInput) {\n const page = parsePositiveCursor(input.cursor);\n const result = await this.#client.request<{ reviews: GithubReview[] }>(\n 'GET',\n `${pullRequestPath(input, input.pullRequestId)}/reviews?page=${page}&per_page=${PAGE_SIZE}`,\n );\n return {\n reviews: result.reviews.map(parseReview),\n nextCursor: result.reviews.length === PAGE_SIZE ? String(page + 1) : null,\n };\n }\n\n async #getReview(input: ReviewRef) {\n try {\n return parseReview(\n await this.#client.request<GithubReview>(\n 'GET',\n `${pullRequestPath(input, input.pullRequestId)}/reviews/${requirePositiveId(input.reviewId, 'review')}`,\n ),\n );\n } catch (error) {\n if (isNotFound(error)) return null;\n throw error;\n }\n }\n\n async #createReview(input: CreateReviewInput) {\n const review = await this.#client.request<GithubReview>(\n 'POST',\n `${pullRequestPath(input, input.pullRequestId)}/reviews`,\n { body: input.body, commitId: input.commitId, event: input.event ? reviewEvent(input.event) : undefined },\n { actingUserId: input.actingUserId },\n );\n return parseReview(review);\n }\n\n async #updateReview(input: UpdateReviewInput) {\n const review = await this.#client.request<GithubReview>(\n 'PUT',\n `${pullRequestPath(input, input.pullRequestId)}/reviews/${requirePositiveId(input.reviewId, 'review')}`,\n { body: input.body },\n { actingUserId: input.actingUserId },\n );\n return parseReview(review);\n }\n\n async #submitReview(input: SubmitReviewInput) {\n const review = await this.#client.request<GithubReview>(\n 'POST',\n `${pullRequestPath(input, input.pullRequestId)}/reviews/${requirePositiveId(input.reviewId, 'review')}/events`,\n { body: input.body, event: reviewEvent(input.event) },\n { actingUserId: input.actingUserId },\n );\n return parseReview(review);\n }\n\n async #dismissReview(input: DismissReviewInput) {\n const review = await this.#client.request<GithubReview>(\n 'PUT',\n `${pullRequestPath(input, input.pullRequestId)}/reviews/${requirePositiveId(input.reviewId, 'review')}/dismissals`,\n { message: input.message },\n { actingUserId: input.actingUserId },\n );\n return parseReview(review);\n }\n\n async #deletePendingReview(input: ReviewRef) {\n await this.#client.request<void>(\n 'DELETE',\n `${pullRequestPath(input, input.pullRequestId)}/reviews/${requirePositiveId(input.reviewId, 'review')}`,\n undefined,\n { actingUserId: input.actingUserId },\n );\n }\n\n async #listReviewComments(input: ListReviewCommentsInput) {\n const page = parsePositiveCursor(input.cursor);\n const result = await this.#client.request<{ comments: GithubReviewComment[] }>(\n 'GET',\n `${pullRequestPath(input, input.pullRequestId)}/comments?page=${page}&per_page=${PAGE_SIZE}`,\n );\n return {\n comments: result.comments.map(parseReviewComment),\n nextCursor: result.comments.length === PAGE_SIZE ? String(page + 1) : null,\n };\n }\n\n async #createReviewComment(input: CreateReviewCommentInput) {\n let body: Record<string, unknown>;\n if (input.replyToId !== undefined) {\n body = { body: input.body, replyToId: requirePositiveId(input.replyToId, 'review comment') };\n } else {\n if (!input.commitId || !input.path || input.line === undefined || !input.side) {\n throw new Error('A review comment requires commitId, path, line, and side unless it is a reply.');\n }\n body = {\n body: input.body,\n commitId: input.commitId,\n path: input.path,\n line: input.line,\n side: input.side.toUpperCase(),\n startLine: input.startLine,\n startSide: input.startSide?.toUpperCase(),\n };\n }\n return parseReviewComment(\n await this.#client.request<GithubReviewComment>(\n 'POST',\n `${pullRequestPath(input, input.pullRequestId)}/comments`,\n body,\n { actingUserId: input.actingUserId },\n ),\n );\n }\n\n async #updateReviewComment(input: UpdatePullRequestCommentInput) {\n requireGithubConnection(input.connection);\n return parseReviewComment(\n await this.#client.request<GithubReviewComment>(\n 'PATCH',\n repositoryPath(input.sourceId, `pulls/comments/${requirePositiveId(input.commentId, 'review comment')}`),\n { body: input.body },\n { actingUserId: input.actingUserId },\n ),\n );\n }\n\n async #deleteReviewComment(input: DeletePullRequestCommentInput) {\n requireGithubConnection(input.connection);\n await this.#client.request<void>(\n 'DELETE',\n repositoryPath(input.sourceId, `pulls/comments/${requirePositiveId(input.commentId, 'review comment')}`),\n undefined,\n { actingUserId: input.actingUserId },\n );\n }\n\n #requestedReviewers(method: 'GET' | 'POST' | 'DELETE', input: UpdateReviewersInput) {\n requireGithubConnection(input.connection);\n return this.#client.request<{ users: string[]; teams: string[] }>(\n method,\n `${pullRequestPath(input, input.pullRequestId)}/requested-reviewers`,\n method === 'GET' ? undefined : { users: input.users, teams: input.teams },\n method === 'GET' ? undefined : { actingUserId: input.actingUserId },\n );\n }\n}\n\nfunction repositoryPath(sourceId: string, suffix: string): string {\n const { owner, repo } = splitRepository(sourceId);\n return `${API_PREFIX}/github/repos/${encodeURIComponent(owner)}/${encodeURIComponent(repo)}/${suffix}`;\n}\n\nfunction pullRequestPath(\n input: { connection: IntegrationConnection; sourceId: string },\n pullRequestId: string,\n): string {\n requireGithubConnection(input.connection);\n return repositoryPath(input.sourceId, `pulls/${requirePositiveId(pullRequestId, 'pull request')}`);\n}\n\nfunction splitRepository(sourceId: string): { owner: string; repo: string } {\n const slash = sourceId.indexOf('/');\n if (slash <= 0 || slash === sourceId.length - 1) {\n throw new Error('GitHub capabilities require an owner/repository source.');\n }\n return { owner: sourceId.slice(0, slash), repo: sourceId.slice(slash + 1) };\n}\n\nfunction parseIntakeIssue(sourceId: string, issue: GithubIssue): IntakeIssue {\n return {\n id: String(issue.number),\n identifier: `#${issue.number}`,\n title: issue.title,\n url: issue.htmlUrl,\n author: issue.user?.login ?? null,\n state: issue.state,\n stateType: issue.state,\n priority: null,\n assignee: issue.assignees[0] ?? null,\n source: sourceId,\n labels: issue.labels,\n commentCount: issue.commentCount,\n createdAt: issue.createdAt,\n updatedAt: issue.updatedAt,\n };\n}\n\nfunction parseIntakeIssueDetail(sourceId: string, issue: GithubIssue, comments: GithubComment[]): IntakeIssueDetail {\n return {\n ...parseIntakeIssue(sourceId, issue),\n description: issue.body?.trim() ? issue.body : null,\n comments: comments.map(comment => ({\n author: comment.user?.login ?? null,\n body: comment.body,\n createdAt: comment.createdAt,\n })),\n };\n}\n\nfunction parsePullRequest(pullRequest: GithubPullRequest): PullRequest {\n return {\n id: String(pullRequest.number),\n title: pullRequest.title,\n url: pullRequest.htmlUrl,\n author: pullRequest.user?.login ?? null,\n body: pullRequest.body?.trim() ? pullRequest.body : null,\n state: pullRequest.state,\n draft: pullRequest.draft,\n merged: pullRequest.merged,\n mergeable: pullRequest.mergeable,\n baseBranch: pullRequest.base.ref,\n headBranch: pullRequest.head.ref,\n headSha: pullRequest.head.sha,\n createdAt: pullRequest.createdAt,\n updatedAt: pullRequest.updatedAt,\n };\n}\n\nfunction parseComment(comment: GithubComment): PullRequestComment {\n return {\n id: String(comment.id),\n url: comment.htmlUrl,\n author: comment.user?.login ?? null,\n body: comment.body,\n createdAt: comment.createdAt,\n updatedAt: comment.updatedAt,\n };\n}\n\nfunction parseReview(review: GithubReview): Review {\n const states: Record<GithubReview['state'], Review['state']> = {\n PENDING: 'pending',\n COMMENTED: 'commented',\n APPROVED: 'approved',\n CHANGES_REQUESTED: 'changes-requested',\n DISMISSED: 'dismissed',\n };\n return {\n id: String(review.id),\n url: review.htmlUrl,\n author: review.user?.login ?? null,\n body: review.body?.trim() ? review.body : null,\n state: states[review.state],\n commitId: review.commitId,\n submittedAt: review.submittedAt,\n };\n}\n\nfunction parseReviewComment(comment: GithubReviewComment): ReviewComment {\n return {\n ...parseComment(comment),\n path: comment.path,\n line: comment.line,\n side: comment.side?.toLowerCase() as 'left' | 'right' | null,\n commitId: comment.commitId,\n replyToId: comment.replyToId === null ? null : String(comment.replyToId),\n };\n}\n\nfunction requireGithubConnection(connection: IntegrationConnection): void {\n if (connection.type !== 'app-installation' && connection.type !== 'oauth') {\n throw new Error('GitHub capabilities require a GitHub connection.');\n }\n}\n\nfunction requireSingleSource(sourceIds: string[], message: string): string {\n if (sourceIds.length !== 1) throw new Error(message);\n return sourceIds[0]!;\n}\n\nfunction requireSource(sourceId: string | undefined, message: string): string {\n if (!sourceId) throw new Error(message);\n return sourceId;\n}\n\nfunction normalizeLabels(labels: string[] | undefined): string[] {\n return [...new Set((labels ?? []).map(label => label.trim()).filter(Boolean))];\n}\n\nfunction parsePositiveCursor(cursor: string | undefined): number {\n if (cursor === undefined) return 1;\n const parsed = parsePositiveInteger(cursor);\n if (parsed === null) throw new Error('GitHub cursor must be a positive page number.');\n return parsed;\n}\n\nfunction parsePositiveInteger(value: string): number | null {\n if (!/^\\d+$/.test(value)) return null;\n const parsed = Number(value);\n return Number.isSafeInteger(parsed) && parsed > 0 ? parsed : null;\n}\n\nfunction parseGithubExternalTarget(externalId: string): { repository: string; issueId: string } | null {\n const match =\n externalId.match(/^(.+\\/.+):(\\d+)$/) ??\n externalId.match(/^github:(\\d+):(?:issue|pull-request):(\\d+)$/) ??\n externalId.match(/^(\\d+):(\\d+)$/);\n if (!match?.[1] || !match[2] || parsePositiveInteger(match[2]) === null) return null;\n return { repository: match[1], issueId: match[2] };\n}\n\nfunction optionalPositiveIntegerEnv(name: 'MASTRA_PLATFORM_GITHUB_POLLING_INTERVAL_MS'): number | undefined {\n const value = process.env[name]?.trim();\n if (!value) return undefined;\n const parsed = parsePositiveInteger(value);\n if (parsed === null) throw new Error(`${name} must be a positive integer.`);\n return parsed;\n}\n\nfunction requirePositiveId(value: string, resource: string): number {\n const parsed = parsePositiveInteger(value);\n if (parsed === null) throw new Error(`GitHub ${resource} id must be a positive integer.`);\n return parsed;\n}\n\nfunction reviewEvent(event: 'approve' | 'request-changes' | 'comment') {\n if (event === 'approve') return 'APPROVE' as const;\n if (event === 'request-changes') return 'REQUEST_CHANGES' as const;\n return 'COMMENT' as const;\n}\n\nfunction isNotFound(error: unknown): boolean {\n return error instanceof PlatformApiError && error.status === 404;\n}\n\n// Platform answers 404 when the installation row is gone, 409 when it is suspended or soft-deleted.\n// A 502 also covers a dead installation but is indistinguishable from a transient GitHub outage.\nfunction isDeadInstallation(error: unknown): boolean {\n return error instanceof PlatformApiError && (error.status === 404 || error.status === 409);\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"integration.js","names":["#client","#endpointHost","#slug","#pollingEnabled","#pollingIntervalMs","#reconcileEnabled","#installationReposCache","#repositoryAccessCache","#runIssueTriage","#resolveIntakeDispatch","#listIssues","#listPullRequests","#getIssue","#createIssueComment","#updateIntakeIssue","#storage","#getPullRequest","#createPullRequest","#updatePullRequest","#mergePullRequest","#listComments","#createComment","#updateComment","#deleteComment","#listReviews","#getReview","#createReview","#updateReview","#submitReview","#dismissReview","#deletePendingReview","#listReviewComments","#createReviewComment","#updateReviewComment","#deleteReviewComment","#requestedReviewers","#integrationStorage","#statusRoute","#connectRoute","#connectUserRoute","#syncInstallations","#fetchUserConnection"],"sources":["../../../../src/integrations/platform/github/integration.ts"],"sourcesContent":["import type { RequestContext } from '@mastra/core/request-context';\nimport type { ApiRoute } from '@mastra/core/server';\nimport { registerApiRoute } from '@mastra/core/server';\nimport type { Context } from 'hono';\n\nimport type { IntegrationConnection } from '../../../capabilities/connection.js';\nimport type {\n CreateIntakeCommentInput,\n Intake,\n IntakeIssue,\n IntakeIssueDetail,\n UpdateIntakeIssueInput,\n} from '../../../capabilities/intake.js';\nimport type {\n CreatePullRequestCommentInput,\n CreatePullRequestInput,\n CreateReviewCommentInput,\n CreateReviewInput,\n DeletePullRequestCommentInput,\n DismissReviewInput,\n ListPullRequestCommentsInput,\n ListPullRequestsInput,\n ListReviewCommentsInput,\n ListReviewsInput,\n MergePullRequestInput,\n PullRequest,\n PullRequestComment,\n PullRequestRef,\n RepositoryAccess,\n Review,\n ReviewComment,\n ReviewRef,\n SubmitReviewInput,\n UpdatePullRequestCommentInput,\n UpdatePullRequestInput,\n UpdateReviewInput,\n UpdateReviewersInput,\n VersionControl,\n} from '../../../capabilities/version-control.js';\nimport type { IntegrationStorageHandle } from '../../../storage/domains/integrations/base.js';\nimport type {\n SourceControlInstallation,\n SourceControlStorageHandle,\n} from '../../../storage/domains/source-control/base.js';\nimport type { FactoryIntegration, IntegrationContext, IntegrationTools } from '../../base.js';\nimport type { GithubIntegration, GithubRepositoryPermission, RepoSummary } from '../../github/integration.js';\nimport type { GithubIssueTriageInput, GithubIssueTriageResult } from '../../github/issue-triage.js';\nimport { runGithubIssueTriage } from '../../github/issue-triage.js';\nimport { buildGithubRoutes } from '../../github/routes.js';\nimport { attachGithubReconciler, attachGithubRules } from '../../github/rules.js';\nimport type { ReconcilePullRequestState } from '../../github/rules.js';\nimport {\n createGithubSubscriptionTools,\n parseCreatedPullRequest,\n subscribeCurrentSessionToPullRequest,\n} from '../../github/session-subscriptions.js';\nimport type { GithubSubscriptionStorage } from '../../github/subscriptions.js';\nimport {\n logPlatformInfo,\n logPlatformWarn,\n PlatformApiClient,\n PlatformApiError,\n platformApiClientConfigFromEnv,\n} from '../api-client.js';\nimport { PlatformGithubEventWorker } from './event-worker.js';\nimport type { PlatformGithubEventStorage } from './event-worker.js';\n\ntype GithubActor = { login: string; avatarUrl: string | null; htmlUrl: string | null } | null;\n\ntype PlatformGithubInstallation = {\n installationId: number;\n accountLogin: string;\n accountType: string;\n suspendedAt: string | null;\n usable: boolean;\n};\n\ntype PlatformGithubUserConnection = {\n connected: boolean;\n githubUsername: string | null;\n reason?: 'token-invalid' | 'no-accessible-installation' | 'missing-permissions' | 'verification-unavailable' | null;\n};\n\ntype GithubIssue = {\n number: number;\n state: 'open' | 'closed';\n title: string;\n body: string | null;\n htmlUrl: string;\n labels: string[];\n assignees: string[];\n commentCount: number;\n user: GithubActor;\n createdAt: string;\n updatedAt: string;\n};\n\ntype GithubComment = {\n id: number;\n body: string;\n htmlUrl: string;\n user: GithubActor;\n createdAt: string;\n updatedAt: string;\n};\n\ntype GithubPullRequest = {\n number: number;\n title: string;\n body: string | null;\n state: 'open' | 'closed';\n htmlUrl: string;\n merged: boolean;\n mergeable: boolean | null;\n draft: boolean;\n head: { ref: string; sha: string };\n base: { ref: string; repo: { id: number; fullName: string } };\n user: GithubActor;\n assignees?: string[];\n requestedReviewers?: string[];\n createdAt: string;\n updatedAt: string;\n};\n\ntype GithubReview = {\n id: number;\n htmlUrl: string | null;\n body: string | null;\n state: 'PENDING' | 'COMMENTED' | 'APPROVED' | 'CHANGES_REQUESTED' | 'DISMISSED';\n commitId: string | null;\n submittedAt: string | null;\n user: GithubActor;\n};\n\ntype GithubReviewComment = GithubComment & {\n path: string;\n line: number | null;\n side: 'LEFT' | 'RIGHT' | null;\n commitId: string;\n replyToId: number | null;\n};\n\nconst PAGE_SIZE = 30;\nconst API_PREFIX = '/v1/server';\n/**\n * How long an installation's repository listing may be reused. The repos\n * route and token minting both call it — often several times within one UI\n * interaction — and each call is a full Platform round trip.\n */\nconst INSTALLATION_REPOS_CACHE_TTL_MS = 30_000;\n/**\n * How long a minted repository-scoped token may be reused. GitHub\n * installation tokens live ~60 minutes; 5 minutes keeps a wide validity\n * margin while collapsing the per-session-materialization mint round trip.\n */\nconst REPOSITORY_ACCESS_CACHE_TTL_MS = 5 * 60_000;\n/**\n * Upper bound for both TTL caches. Entries expire lazily on re-access, so\n * without a hard cap keys that stop being queried would accumulate for the\n * integration's (long) lifetime. Insertion-order eviction — matches the\n * bounded verification cache in `@mastra/auth-studio`.\n */\nconst MAX_CACHE_ENTRIES = 1000;\n\nfunction setBounded<K, V>(cache: Map<K, V>, key: K, value: V): void {\n cache.delete(key);\n cache.set(key, value);\n if (cache.size > MAX_CACHE_ENTRIES) {\n const oldest = cache.keys().next().value;\n if (oldest !== undefined) cache.delete(oldest);\n }\n}\n\nconst REPOSITORY_TOKEN_PERMISSIONS = {\n contents: 'write',\n issues: 'write',\n pull_requests: 'write',\n} as const;\n\nfunction loose(c: unknown): Context {\n return c as Context;\n}\n\nfunction routeBaseUrl(ctx: IntegrationContext, requestUrl: string): string {\n return (ctx.baseUrl || new URL(requestUrl).origin).replace(/\\/+$/, '');\n}\n\nexport class PlatformGithubIntegration implements FactoryIntegration {\n readonly id = 'github';\n readonly #client: PlatformApiClient;\n readonly #endpointHost: string;\n readonly #slug: string | undefined;\n readonly #pollingEnabled: boolean;\n readonly #pollingIntervalMs: number | undefined;\n readonly #reconcileEnabled: boolean;\n #storage: SourceControlStorageHandle | undefined;\n #integrationStorage: GithubSubscriptionStorage | undefined;\n /** installationId → cached repository listing (TTL-bounded). */\n readonly #installationReposCache = new Map<number, { repos: RepoSummary[]; expiresAt: number }>();\n /** `orgId:repositoryId` → cached repository access (TTL-bounded). */\n readonly #repositoryAccessCache = new Map<string, { access: RepositoryAccess; expiresAt: number }>();\n readonly #runIssueTriage?: (input: GithubIssueTriageInput) => Promise<GithubIssueTriageResult>;\n\n readonly intake: Intake = {\n resolveIntakeDispatch: input => this.#resolveIntakeDispatch(input),\n listSources: async ({ orgId, userId }) => {\n const installations = await this.#client.request<{\n installations: Array<{\n installationId: number;\n accountLogin: string;\n accountType: string;\n suspendedAt: string | null;\n usable: boolean;\n }>;\n }>('GET', `${API_PREFIX}/github-app/installations`);\n const usable = installations.installations.filter(\n installation => installation.usable && !installation.suspendedAt,\n );\n const repositories = await Promise.all(\n usable.map(async installation => {\n const storedInstallation = await this.versionControl.registerInstallation({\n orgId,\n userId,\n installation: {\n externalId: String(installation.installationId),\n accountName: installation.accountLogin,\n accountType: installation.accountType,\n },\n });\n const result = await this.#client.request<{\n repositories: Array<{\n id: number;\n fullName: string;\n private: boolean;\n defaultBranch: string;\n htmlUrl: string;\n }>;\n }>('GET', `${API_PREFIX}/github-app/installations/${installation.installationId}/repositories`);\n await this.versionControl.registerRepositories({\n orgId,\n installationId: storedInstallation.id,\n repositories: result.repositories.map(repository => ({\n externalId: String(repository.id),\n slug: repository.fullName,\n defaultBranch: repository.defaultBranch,\n metadata: { private: repository.private, url: repository.htmlUrl },\n })),\n });\n return result.repositories.map(repository => ({ repository, installation }));\n }),\n );\n return repositories.flat().map(({ repository, installation }) => ({\n id: repository.fullName,\n name: repository.fullName,\n type: 'repository',\n metadata: {\n installationId: installation.installationId,\n accountLogin: installation.accountLogin,\n accountType: installation.accountType,\n repositoryId: repository.id,\n defaultBranch: repository.defaultBranch,\n private: repository.private,\n url: repository.htmlUrl,\n },\n }));\n },\n listItems: async ({ sourceIds, cursor }) => {\n const page = parsePositiveCursor(cursor);\n const pages = await Promise.all(\n sourceIds.map(async sourceId => {\n const connection = { type: 'app-installation' as const, installationId: 1 };\n const [issues, pullRequests] = await Promise.all([\n this.#listIssues(connection, sourceId, page),\n this.#listPullRequests({ connection, sourceId, includeDrafts: false, cursor: String(page) }),\n ]);\n return {\n items: [\n ...issues.issues.map(issue => ({\n source: { type: 'issue', externalId: `${sourceId}:${issue.id}`, url: issue.url },\n sourceId,\n title: issue.title,\n status: issue.state ?? undefined,\n labels: issue.labels,\n assignee: issue.assignee,\n createdAt: issue.createdAt,\n updatedAt: issue.updatedAt,\n metadata: { repository: sourceId, number: Number(issue.id), author: issue.author },\n })),\n ...pullRequests.pullRequests.map(pullRequest => ({\n source: { type: 'pull-request', externalId: `${sourceId}:${pullRequest.id}`, url: pullRequest.url },\n sourceId,\n title: pullRequest.title,\n status: pullRequest.state,\n createdAt: pullRequest.createdAt,\n updatedAt: pullRequest.updatedAt,\n metadata: {\n repository: sourceId,\n number: Number(pullRequest.id),\n author: pullRequest.author,\n baseBranch: pullRequest.baseBranch,\n headBranch: pullRequest.headBranch,\n },\n })),\n ],\n hasNextPage: issues.nextCursor !== null || pullRequests.nextCursor !== null,\n };\n }),\n );\n return {\n items: pages.flatMap(result => result.items),\n nextCursor: pages.some(result => result.hasNextPage) ? String(page + 1) : null,\n };\n },\n listIssues: async input => {\n requireGithubConnection(input.connection);\n const sourceId = requireSingleSource(input.sourceIds, 'GitHub Intake requires exactly one repository source.');\n return this.#listIssues(input.connection, sourceId, parsePositiveCursor(input.cursor), input.labels);\n },\n getIssue: input => this.#getIssue(input.connection, input.sourceId, input.issueId),\n createComment: input => this.#createIssueComment(input),\n updateIssue: input => this.#updateIntakeIssue(input),\n };\n\n readonly versionControl: VersionControl = {\n initialize: ({ storage }) => {\n this.#storage = storage;\n },\n registerInstallation: ({ orgId, userId, installation }) =>\n this.storage.installations.upsert({\n orgId,\n connectedByUserId: userId,\n externalId: installation.externalId,\n accountName: installation.accountName,\n accountType: installation.accountType,\n providerMetadata: installation.metadata,\n }),\n registerRepositories: ({ orgId, installationId, repositories }) =>\n Promise.all(\n repositories.map(repository =>\n this.storage.repositories.upsert({\n orgId,\n input: {\n installationId,\n externalId: repository.externalId,\n slug: repository.slug,\n defaultBranch: repository.defaultBranch,\n providerMetadata: repository.metadata,\n },\n }),\n ),\n ),\n getRepositoryAccess: async ({ orgId, repositoryId }) => {\n // Every session materialization requests access; reuse a recent grant\n // instead of re-minting through the Platform each time. The TTL keeps\n // a wide margin under GitHub's ~60min installation-token lifetime.\n const cacheKey = `${orgId}:${repositoryId}`;\n const cached = this.#repositoryAccessCache.get(cacheKey);\n if (cached && cached.expiresAt > Date.now()) return cached.access;\n this.#repositoryAccessCache.delete(cacheKey);\n\n const repository = await this.storage.repositories.get({ orgId, id: repositoryId });\n if (!repository) throw new Error('Version-control repository not found.');\n const cloneUrl = `https://github.com/${repository.slug}.git`;\n const installation = await this.storage.installations.get({ orgId, id: repository.installationId });\n if (!installation) throw new Error('Version-control installation not found.');\n const installationId = parsePositiveInteger(installation.externalId);\n if (installationId === null) throw new Error('GitHub installation id is invalid.');\n const repositoryName = splitRepository(repository.slug).repo;\n\n try {\n const token = await this.#client.request<{ token: string }>(\n 'POST',\n `${API_PREFIX}/github-app/installations/${installationId}/token`,\n { repositories: [repositoryName], permissions: REPOSITORY_TOKEN_PERMISSIONS },\n );\n const access: RepositoryAccess = {\n cloneUrl,\n authorization: { scheme: 'bearer', token: token.token },\n };\n setBounded(this.#repositoryAccessCache, cacheKey, {\n access,\n expiresAt: Date.now() + REPOSITORY_ACCESS_CACHE_TTL_MS,\n });\n return access;\n } catch (err) {\n // Recover from stale installation: when a GitHub App is uninstalled and reinstalled,\n // GitHub assigns a new installation ID. Platform creates a new installation row,\n // and Factory's intake.listSources creates a new local installation row with the\n // new externalId. Try to find the new installation by accountName.\n if (!isDeadInstallation(err) || !installation.accountName) throw err;\n const installations = await this.storage.installations.list({ orgId });\n const newInstallation = installations.find(\n inst => inst.accountName === installation.accountName && inst.id !== installation.id,\n );\n if (!newInstallation) throw err;\n const newInstallationId = parsePositiveInteger(newInstallation.externalId);\n if (newInstallationId === null) throw err;\n\n // Persist the migration so we don't hit the recovery path on every call.\n await this.storage.repositories.migrateInstallation({\n orgId,\n id: repositoryId,\n newInstallationId: newInstallation.id,\n });\n\n const token = await this.#client.request<{ token: string }>(\n 'POST',\n `${API_PREFIX}/github-app/installations/${newInstallationId}/token`,\n { repositories: [repositoryName], permissions: REPOSITORY_TOKEN_PERMISSIONS },\n );\n const access: RepositoryAccess = {\n cloneUrl,\n authorization: { scheme: 'bearer', token: token.token },\n };\n setBounded(this.#repositoryAccessCache, cacheKey, {\n access,\n expiresAt: Date.now() + REPOSITORY_ACCESS_CACHE_TTL_MS,\n });\n return access;\n }\n },\n listPullRequests: input => this.#listPullRequests(input),\n getPullRequest: input => this.#getPullRequest(input),\n createPullRequest: input => this.#createPullRequest(input),\n updatePullRequest: input => this.#updatePullRequest(input),\n closePullRequest: input => this.#updatePullRequest({ ...input, state: 'closed' }),\n mergePullRequest: input => this.#mergePullRequest(input),\n listComments: input => this.#listComments(input),\n createComment: input => this.#createComment(input),\n updateComment: input => this.#updateComment(input),\n deleteComment: input => this.#deleteComment(input),\n listReviews: input => this.#listReviews(input),\n getReview: input => this.#getReview(input),\n createReview: input => this.#createReview(input),\n updateReview: input => this.#updateReview(input),\n submitReview: input => this.#submitReview(input),\n dismissReview: input => this.#dismissReview(input),\n deletePendingReview: input => this.#deletePendingReview(input),\n listReviewComments: input => this.#listReviewComments(input),\n createReviewComment: input => this.#createReviewComment(input),\n updateReviewComment: input => this.#updateReviewComment(input),\n deleteReviewComment: input => this.#deleteReviewComment(input),\n listRequestedReviewers: input => this.#requestedReviewers('GET', input),\n requestReviewers: input => this.#requestedReviewers('POST', input),\n removeRequestedReviewers: input => this.#requestedReviewers('DELETE', input),\n };\n\n constructor(\n options: {\n runIssueTriage?: (input: GithubIssueTriageInput) => Promise<GithubIssueTriageResult>;\n /** GitHub App slug used to recognize Factory's own webhook writes. */\n slug?: string;\n } = {},\n ) {\n const config = platformApiClientConfigFromEnv();\n this.#client = new PlatformApiClient(config);\n this.#endpointHost = new URL(config.baseUrl).host;\n this.#slug = options.slug;\n this.#pollingEnabled = process.env.MASTRA_PLATFORM_GITHUB_POLLING_ENABLED?.trim().toLowerCase() !== 'false';\n this.#pollingIntervalMs = optionalPositiveIntegerEnv('MASTRA_PLATFORM_GITHUB_POLLING_INTERVAL_MS');\n this.#reconcileEnabled = process.env.MASTRA_PLATFORM_GITHUB_RECONCILE_ENABLED?.trim().toLowerCase() !== 'false';\n this.#runIssueTriage = options.runIssueTriage;\n }\n\n /** GitHub App slug when the deployment explicitly provides it. */\n get slug(): string | undefined {\n return this.#slug;\n }\n\n get storage(): SourceControlStorageHandle {\n if (!this.#storage) throw new Error('PlatformGithubIntegration source-control storage has not been initialized.');\n return this.#storage;\n }\n\n get sourceControlStorage(): SourceControlStorageHandle {\n return this.storage;\n }\n\n get integrationStorage(): GithubSubscriptionStorage {\n if (!this.#integrationStorage) {\n throw new Error('PlatformGithubIntegration generic storage has not been initialized.');\n }\n return this.#integrationStorage;\n }\n\n /** Resolve a stored GitHub locator without scanning installations or repositories. */\n async #resolveIntakeDispatch({\n orgId,\n externalSource,\n }: {\n orgId: string;\n externalSource: { type: string; externalId: string };\n }): Promise<{ connection: IntegrationConnection; sourceId: string; issueId: string } | null> {\n const target = parseGithubExternalTarget(externalSource.externalId);\n if (!target) return null;\n const repository = target.repository.includes('/')\n ? target.repository\n : (await this.storage.repositories.findByExternalId({ orgId, externalId: target.repository }))?.slug;\n if (!repository) return null;\n return {\n connection: { type: 'app-installation', installationId: 1 },\n sourceId: repository,\n issueId: target.issueId,\n };\n }\n\n initialize({ storage }: { storage: IntegrationStorageHandle }): void {\n this.#integrationStorage = storage as unknown as GithubSubscriptionStorage;\n logPlatformInfo('Platform GitHub integration initialized', {\n endpointHost: this.#endpointHost,\n pollingEnabled: this.#pollingEnabled,\n pollingIntervalMs: this.#pollingIntervalMs,\n reconcileEnabled: this.#reconcileEnabled,\n });\n }\n\n routes(ctx: IntegrationContext): ApiRoute[] {\n const ingestFactoryEvent = attachGithubRules(this, ctx);\n return [\n this.#statusRoute(ctx),\n this.#connectRoute(ctx),\n this.#connectUserRoute(ctx),\n ...buildGithubRoutes({\n auth: ctx.auth,\n fleet: ctx.fleet,\n storage: ctx.factoryStorage,\n github: this as unknown as GithubIntegration,\n stateSigner: ctx.stateSigner,\n baseUrl: ctx.baseUrl,\n controller: ctx.controller,\n projects: ctx.storage.projects,\n emitAudit: ctx.hooks?.emitAudit,\n ingestFactoryEvent,\n runIssueTriage:\n this.#runIssueTriage ??\n (ctx.controller ? input => runGithubIssueTriage({ controller: ctx.controller!, input }) : undefined),\n }).filter(\n route =>\n route.path !== '/web/github/status' &&\n route.path !== '/web/github/webhook' &&\n !route.path.startsWith('/auth/github/'),\n ),\n ];\n }\n\n #statusRoute(ctx: IntegrationContext): ApiRoute {\n return registerApiRoute('/web/github/status', {\n method: 'GET',\n requiresAuth: false,\n handler: async c => {\n await ctx.auth.ensureUser(loose(c));\n const tenant = ctx.auth.tenant(loose(c));\n if (!tenant) return c.json({ error: 'unauthorized', reason: 'auth_required' }, 401);\n if (!tenant.orgId) {\n return c.json({\n enabled: true,\n sandboxEnabled: ctx.fleet.enabled,\n organizationRequired: true,\n connected: false,\n installations: [],\n userConnected: false,\n userGithubUsername: null,\n reason: 'organization_required',\n diagnostics: this.diagnostics(),\n });\n }\n\n const [installations, userConnection] = await Promise.all([\n this.#syncInstallations(tenant.orgId, tenant.userId),\n this.#fetchUserConnection(tenant.userId),\n ]);\n return c.json({\n enabled: true,\n sandboxEnabled: ctx.fleet.enabled,\n connected: installations.length > 0,\n installations: installations.map(installation => ({\n installationId: Number(installation.externalId),\n accountLogin: installation.accountName,\n accountType: installation.accountType,\n })),\n userConnected: userConnection.connected,\n userGithubUsername: userConnection.githubUsername,\n reason: installations.length > 0 ? 'ready' : 'not_connected',\n diagnostics: this.diagnostics(),\n });\n },\n });\n }\n\n #connectRoute(ctx: IntegrationContext): ApiRoute {\n return registerApiRoute('/auth/github/connect', {\n method: 'GET',\n requiresAuth: false,\n handler: async c => {\n await ctx.auth.ensureUser(loose(c));\n const tenant = ctx.auth.tenant(loose(c));\n if (!tenant?.orgId) return c.json({ error: 'unauthorized' }, 401);\n\n const redirectTo = c.req.query('redirectTo') || c.req.query('return_to') || '/';\n const originator = routeBaseUrl(ctx, c.req.url);\n logPlatformInfo('Starting Platform GitHub connect flow', {\n orgId: tenant.orgId,\n redirectTo,\n originator,\n });\n const query = new URLSearchParams({\n action: 'install',\n redirectTo,\n originator,\n });\n const { url } = await this.#client.request<{ url: string }>(\n 'GET',\n `${API_PREFIX}/github-app/install-url?${query}`,\n );\n return c.redirect(url);\n },\n });\n }\n\n #connectUserRoute(ctx: IntegrationContext): ApiRoute {\n return registerApiRoute('/auth/github/connect-user', {\n method: 'GET',\n requiresAuth: false,\n handler: async c => {\n await ctx.auth.ensureUser(loose(c));\n const tenant = ctx.auth.tenant(loose(c));\n if (!tenant?.orgId) return c.json({ error: 'unauthorized' }, 401);\n\n const redirectTo = c.req.query('redirectTo') || c.req.query('return_to') || '/';\n const originator = routeBaseUrl(ctx, c.req.url);\n logPlatformInfo('Starting Platform GitHub user authorization flow', {\n orgId: tenant.orgId,\n redirectTo,\n originator,\n });\n const query = new URLSearchParams({\n userId: tenant.userId,\n redirectTo,\n originator,\n });\n const { url } = await this.#client.request<{ url: string }>(\n 'GET',\n `${API_PREFIX}/github-app/authenticate?${query}`,\n );\n return c.redirect(url);\n },\n });\n }\n\n /**\n * Personal GitHub connection status for the acting user. Returns\n * not-connected when the platform predates the user-connection endpoint.\n */\n async #fetchUserConnection(userId: string): Promise<PlatformGithubUserConnection> {\n try {\n const connection = await this.#client.request<PlatformGithubUserConnection>(\n 'GET',\n `${API_PREFIX}/github-app/user-connection?${new URLSearchParams({ userId })}`,\n );\n if (!connection.connected && connection.reason) {\n logPlatformWarn('Platform GitHub user connection verification failed', {\n userId,\n reason: connection.reason,\n });\n }\n return connection;\n } catch {\n return { connected: false, githubUsername: null };\n }\n }\n\n async #syncInstallations(orgId: string, userId: string): Promise<SourceControlInstallation[]> {\n const result = await this.#client.request<{ installations: PlatformGithubInstallation[] }>(\n 'GET',\n `${API_PREFIX}/github-app/installations`,\n );\n const usableInstallations = result.installations.filter(\n installation => installation.usable && !installation.suspendedAt,\n );\n return Promise.all(\n usableInstallations.map(installation =>\n this.versionControl.registerInstallation({\n orgId,\n userId,\n installation: {\n externalId: String(installation.installationId),\n accountName: installation.accountLogin,\n accountType: installation.accountType,\n },\n }),\n ),\n );\n }\n\n workers(ctx: IntegrationContext): PlatformGithubEventWorker[] {\n if (!this.#pollingEnabled && !this.#reconcileEnabled) return [];\n if (!ctx.controller) {\n throw new Error('Platform GitHub event polling requires the mounted Mastra Code controller.');\n }\n return [\n new PlatformGithubEventWorker({\n client: this.#client,\n controller: ctx.controller,\n github: this,\n storage: ctx.storage.generic as unknown as PlatformGithubEventStorage,\n ingestFactoryEvent: attachGithubRules(this, ctx),\n reconcileFactoryState: this.#reconcileEnabled\n ? attachGithubReconciler(this, ctx, input => this.fetchPullRequestState(input))\n : undefined,\n pollEventsEnabled: this.#pollingEnabled,\n intervalMs: this.#pollingIntervalMs,\n }),\n ];\n }\n\n /**\n * Reads live PR state through the Platform GitHub proxy for the merge\n * reconciler. Returns undefined when the PR cannot be resolved (missing,\n * proxy error) so a sweep never fabricates a merge.\n */\n async fetchPullRequestState(input: {\n installationId: number;\n repository: string;\n number: number;\n }): Promise<ReconcilePullRequestState | undefined> {\n let repository: { owner: string; repo: string };\n try {\n repository = splitRepository(input.repository);\n } catch {\n return undefined;\n }\n try {\n const result = await this.#client.request<{\n title?: string;\n html_url?: string;\n state?: string;\n draft?: boolean;\n merged?: boolean;\n created_at?: string;\n user?: { login?: string } | null;\n assignees?: Array<{ login?: string }> | null;\n requested_reviewers?: Array<{ login?: string }> | null;\n merged_by?: { login?: string } | null;\n head?: { ref?: string };\n base?: { ref?: string };\n }>(\n 'GET',\n `${API_PREFIX}/github/repos/${encodeURIComponent(repository.owner)}/${encodeURIComponent(repository.repo)}/pulls/${input.number}`,\n );\n return {\n title: result.title ?? `PR ${input.number}`,\n url: result.html_url ?? `https://github.com/${input.repository}/pull/${input.number}`,\n state: result.state === 'closed' ? 'closed' : 'open',\n draft: result.draft === true,\n merged: result.merged === true,\n assignees: (result.assignees ?? []).flatMap(assignee => (assignee.login ? [assignee.login] : [])),\n requestedReviewers: (result.requested_reviewers ?? []).flatMap(reviewer =>\n reviewer.login ? [reviewer.login] : [],\n ),\n headBranch: result.head?.ref ?? '',\n baseBranch: result.base?.ref ?? '',\n ...(result.user?.login ? { author: result.user.login } : {}),\n ...(result.created_at ? { createdAt: result.created_at } : {}),\n ...(result.merged_by?.login ? { mergedBy: result.merged_by.login } : {}),\n };\n } catch {\n return undefined;\n }\n }\n\n sessionTools({ requestContext }: { requestContext: RequestContext }): IntegrationTools {\n return createGithubSubscriptionTools(requestContext, this as unknown as GithubIntegration);\n }\n\n async postToolObserver({\n toolContext,\n requestContext,\n }: Parameters<NonNullable<FactoryIntegration['postToolObserver']>>[0]): Promise<void> {\n const pullRequestUrl = parseCreatedPullRequest(toolContext);\n if (!pullRequestUrl || !requestContext) return;\n await subscribeCurrentSessionToPullRequest(\n requestContext,\n pullRequestUrl,\n 'auto-gh-pr-create',\n this as unknown as GithubIntegration,\n );\n }\n\n diagnostics(): Record<string, unknown> {\n return {\n mode: 'platform',\n endpointHost: this.#endpointHost,\n polling: {\n enabled: this.#pollingEnabled,\n ...(this.#pollingIntervalMs === undefined ? {} : { intervalMs: this.#pollingIntervalMs }),\n },\n reconcile: { enabled: this.#reconcileEnabled },\n };\n }\n\n async getRepositoryCollaboratorPermission(\n _installationId: number,\n repoFullName: string,\n username: string,\n signal?: AbortSignal,\n ): Promise<GithubRepositoryPermission | undefined> {\n let repository: { owner: string; repo: string };\n try {\n repository = splitRepository(repoFullName);\n } catch {\n return undefined;\n }\n try {\n const result = await this.#client.request<{ permission: GithubRepositoryPermission }>(\n 'GET',\n `${API_PREFIX}/github/repos/${encodeURIComponent(repository.owner)}/${encodeURIComponent(repository.repo)}/collaborators/${encodeURIComponent(username)}/permission`,\n undefined,\n { signal },\n );\n return result.permission;\n } catch {\n return undefined;\n }\n }\n\n async listInstallationRepos(installationId: number): Promise<RepoSummary[]> {\n // Hot in two places — the repos route and token minting (which re-lists\n // only to validate the 1–10 repo rule). A short TTL collapses repeat\n // Platform round trips within one UI interaction.\n const cached = this.#installationReposCache.get(installationId);\n if (cached && cached.expiresAt > Date.now()) return cached.repos;\n this.#installationReposCache.delete(installationId);\n\n const result = await this.#client.request<{\n repositories: Array<{\n id: number;\n owner: string;\n name: string;\n fullName: string;\n private: boolean;\n defaultBranch: string;\n }>;\n }>('GET', `${API_PREFIX}/github-app/installations/${installationId}/repositories`);\n const repos = result.repositories.map(repository => ({ ...repository, installationId }));\n setBounded(this.#installationReposCache, installationId, {\n repos,\n expiresAt: Date.now() + INSTALLATION_REPOS_CACHE_TTL_MS,\n });\n return repos;\n }\n\n async mintInstallationToken(installationId: number): Promise<string> {\n const repositories = await this.listInstallationRepos(installationId);\n if (repositories.length === 0 || repositories.length > 10) {\n throw new Error('Platform GitHub token minting requires between one and ten installation repositories.');\n }\n const result = await this.#client.request<{ token: string }>(\n 'POST',\n `${API_PREFIX}/github-app/installations/${installationId}/token`,\n { repositories: repositories.map(repository => repository.name), permissions: REPOSITORY_TOKEN_PERMISSIONS },\n );\n return result.token;\n }\n\n async addIssueLabels(\n _installationId: number,\n sourceId: string,\n issueNumber: number,\n labels: string[],\n ): Promise<string[]> {\n const result = await this.#client.request<{ labels: string[] }>(\n 'POST',\n repositoryPath(sourceId, `issues/${issueNumber}/labels`),\n { labels },\n );\n return result.labels;\n }\n\n getInstallationOctokit(_installationId: number): ReturnType<GithubIntegration['getInstallationOctokit']> {\n return {\n pulls: {\n get: async ({ owner, repo, pull_number }: { owner: string; repo: string; pull_number: number }) => {\n const data = await this.#client.request<GithubPullRequest>(\n 'GET',\n repositoryPath(`${owner}/${repo}`, `pulls/${pull_number}`),\n );\n return { data: { base: { repo: { id: data.base.repo.id } } } };\n },\n },\n } as unknown as ReturnType<GithubIntegration['getInstallationOctokit']>;\n }\n\n async #listIssues(connection: IntegrationConnection, sourceId: string, page: number, labels?: string[]) {\n requireGithubConnection(connection);\n const path = repositoryPath(sourceId, 'issues');\n const query = new URLSearchParams({ state: 'open', page: String(page), per_page: String(PAGE_SIZE) });\n const normalizedLabels = normalizeLabels(labels);\n if (normalizedLabels.length > 0) query.set('label', normalizedLabels.join(','));\n const result = await this.#client.request<{ issues: GithubIssue[] }>('GET', `${path}?${query}`);\n return {\n issues: result.issues.map(issue => parseIntakeIssue(sourceId, issue)),\n nextCursor: result.issues.length > 0 ? String(page + 1) : null,\n };\n }\n\n async #getIssue(connection: IntegrationConnection, sourceId: string | undefined, issueId: string) {\n requireGithubConnection(connection);\n const repository = requireSource(sourceId, 'GitHub Intake requires a repository source.');\n const issueNumber = requirePositiveId(issueId, 'issue');\n try {\n const [issue, comments] = await Promise.all([\n this.#client.request<GithubIssue>('GET', repositoryPath(repository, `issues/${issueNumber}`)),\n this.#client.request<{ comments: GithubComment[] }>(\n 'GET',\n `${repositoryPath(repository, `issues/${issueNumber}/comments`)}?per_page=100`,\n ),\n ]);\n return parseIntakeIssueDetail(repository, issue, comments.comments);\n } catch (error) {\n if (isNotFound(error)) return null;\n throw error;\n }\n }\n\n async #updateIntakeIssue(input: UpdateIntakeIssueInput): Promise<IntakeIssue | null> {\n requireGithubConnection(input.connection);\n const repository = requireSource(input.sourceId, 'GitHub Intake requires a repository source.');\n const issueNumber = requirePositiveId(input.issueId, 'issue');\n if (input.state.kind === 'byName') {\n logPlatformWarn(`Platform GitHub: updateIssue byName is not supported (name=${input.state.name}); ignoring.`);\n return null;\n }\n const targetState: 'open' | 'closed' =\n input.state.stateType === 'unstarted' || input.state.stateType === 'started' ? 'open' : 'closed';\n const stateReason: 'completed' | 'not_planned' | null =\n targetState === 'closed' ? (input.state.stateType === 'canceled' ? 'not_planned' : 'completed') : null;\n // Reject PR targets: probe the pulls endpoint. A 200 means the number is a\n // pull request, not an issue. Factory does not close PRs via updateIssue —\n // PR merges/closes go through the version-control pipeline.\n try {\n await this.#client.request<GithubPullRequest>('GET', repositoryPath(repository, `pulls/${issueNumber}`));\n logPlatformWarn(`Platform GitHub: updateIssue rejected — target ${repository}#${issueNumber} is a pull request.`);\n return null;\n } catch (error) {\n if (!isNotFound(error)) throw error;\n // 404 on pulls means it's an issue (or nothing) — fall through to PATCH.\n }\n try {\n const issue = await this.#client.request<GithubIssue>(\n 'PATCH',\n repositoryPath(repository, `issues/${issueNumber}`),\n {\n state: targetState,\n ...(stateReason ? { state_reason: stateReason } : {}),\n },\n { actingUserId: input.actingUserId },\n );\n return parseIntakeIssue(repository, issue);\n } catch (error) {\n if (isNotFound(error)) return null;\n throw error;\n }\n }\n\n async #createIssueComment(input: CreateIntakeCommentInput) {\n requireGithubConnection(input.connection);\n const repository = requireSource(input.sourceId, 'GitHub Intake requires a repository source.');\n const issueNumber = requirePositiveId(input.issueId, 'issue');\n try {\n const comment = await this.#client.request<GithubComment>(\n 'POST',\n repositoryPath(repository, `issues/${issueNumber}/comments`),\n { body: input.body },\n { actingUserId: input.actingUserId },\n );\n return { id: String(comment.id), url: comment.htmlUrl };\n } catch (error) {\n if (isNotFound(error)) return null;\n throw error;\n }\n }\n\n async #listPullRequests(input: ListPullRequestsInput) {\n requireGithubConnection(input.connection);\n const page = parsePositiveCursor(input.cursor);\n const query = new URLSearchParams({\n state: input.state ?? 'open',\n page: String(page),\n per_page: String(PAGE_SIZE),\n });\n const result = await this.#client.request<{ pullRequests: GithubPullRequest[] }>(\n 'GET',\n `${repositoryPath(input.sourceId, 'pulls')}?${query}`,\n );\n return {\n pullRequests: result.pullRequests\n .filter(pullRequest => input.includeDrafts !== false || !pullRequest.draft)\n .map(parsePullRequest),\n nextCursor: result.pullRequests.length === PAGE_SIZE ? String(page + 1) : null,\n };\n }\n\n async #getPullRequest(input: PullRequestRef) {\n try {\n return parsePullRequest(\n await this.#client.request<GithubPullRequest>('GET', pullRequestPath(input, input.pullRequestId)),\n );\n } catch (error) {\n if (isNotFound(error)) return null;\n throw error;\n }\n }\n\n async #createPullRequest(input: CreatePullRequestInput) {\n requireGithubConnection(input.connection);\n const result = await this.#client.request<GithubPullRequest>(\n 'POST',\n repositoryPath(input.sourceId, 'pulls'),\n {\n head: input.headBranch,\n base: input.baseBranch,\n title: input.title,\n body: input.body,\n draft: input.draft,\n },\n { actingUserId: input.actingUserId },\n );\n return parsePullRequest(result);\n }\n\n async #updatePullRequest(input: UpdatePullRequestInput) {\n const result = await this.#client.request<GithubPullRequest>(\n 'PATCH',\n pullRequestPath(input, input.pullRequestId),\n {\n title: input.title,\n body: input.body === null ? '' : input.body,\n base: input.baseBranch,\n state: input.state,\n },\n { actingUserId: input.actingUserId },\n );\n return parsePullRequest(result);\n }\n\n #mergePullRequest(input: MergePullRequestInput) {\n return this.#client.request<{ merged: boolean; message: string; sha: string | null }>(\n 'PUT',\n `${pullRequestPath(input, input.pullRequestId)}/merge`,\n { commitTitle: input.commitTitle, commitMessage: input.commitMessage, method: input.method },\n { actingUserId: input.actingUserId },\n );\n }\n\n async #listComments(input: ListPullRequestCommentsInput) {\n const page = parsePositiveCursor(input.cursor);\n const result = await this.#client.request<{ comments: GithubComment[] }>(\n 'GET',\n `${repositoryPath(input.sourceId, `issues/${requirePositiveId(input.pullRequestId, 'pull request')}/comments`)}?page=${page}&per_page=${PAGE_SIZE}`,\n );\n return {\n comments: result.comments.map(parseComment),\n nextCursor: result.comments.length === PAGE_SIZE ? String(page + 1) : null,\n };\n }\n\n async #createComment(input: CreatePullRequestCommentInput) {\n const comment = await this.#client.request<GithubComment>(\n 'POST',\n repositoryPath(input.sourceId, `issues/${requirePositiveId(input.pullRequestId, 'pull request')}/comments`),\n { body: input.body },\n { actingUserId: input.actingUserId },\n );\n return parseComment(comment);\n }\n\n async #updateComment(input: UpdatePullRequestCommentInput) {\n requireGithubConnection(input.connection);\n const comment = await this.#client.request<GithubComment>(\n 'PATCH',\n repositoryPath(input.sourceId, `issues/comments/${requirePositiveId(input.commentId, 'comment')}`),\n { body: input.body },\n { actingUserId: input.actingUserId },\n );\n return parseComment(comment);\n }\n\n async #deleteComment(input: DeletePullRequestCommentInput) {\n requireGithubConnection(input.connection);\n await this.#client.request<void>(\n 'DELETE',\n repositoryPath(input.sourceId, `issues/comments/${requirePositiveId(input.commentId, 'comment')}`),\n undefined,\n { actingUserId: input.actingUserId },\n );\n }\n\n async #listReviews(input: ListReviewsInput) {\n const page = parsePositiveCursor(input.cursor);\n const result = await this.#client.request<{ reviews: GithubReview[] }>(\n 'GET',\n `${pullRequestPath(input, input.pullRequestId)}/reviews?page=${page}&per_page=${PAGE_SIZE}`,\n );\n return {\n reviews: result.reviews.map(parseReview),\n nextCursor: result.reviews.length === PAGE_SIZE ? String(page + 1) : null,\n };\n }\n\n async #getReview(input: ReviewRef) {\n try {\n return parseReview(\n await this.#client.request<GithubReview>(\n 'GET',\n `${pullRequestPath(input, input.pullRequestId)}/reviews/${requirePositiveId(input.reviewId, 'review')}`,\n ),\n );\n } catch (error) {\n if (isNotFound(error)) return null;\n throw error;\n }\n }\n\n async #createReview(input: CreateReviewInput) {\n const review = await this.#client.request<GithubReview>(\n 'POST',\n `${pullRequestPath(input, input.pullRequestId)}/reviews`,\n { body: input.body, commitId: input.commitId, event: input.event ? reviewEvent(input.event) : undefined },\n { actingUserId: input.actingUserId },\n );\n return parseReview(review);\n }\n\n async #updateReview(input: UpdateReviewInput) {\n const review = await this.#client.request<GithubReview>(\n 'PUT',\n `${pullRequestPath(input, input.pullRequestId)}/reviews/${requirePositiveId(input.reviewId, 'review')}`,\n { body: input.body },\n { actingUserId: input.actingUserId },\n );\n return parseReview(review);\n }\n\n async #submitReview(input: SubmitReviewInput) {\n const review = await this.#client.request<GithubReview>(\n 'POST',\n `${pullRequestPath(input, input.pullRequestId)}/reviews/${requirePositiveId(input.reviewId, 'review')}/events`,\n { body: input.body, event: reviewEvent(input.event) },\n { actingUserId: input.actingUserId },\n );\n return parseReview(review);\n }\n\n async #dismissReview(input: DismissReviewInput) {\n const review = await this.#client.request<GithubReview>(\n 'PUT',\n `${pullRequestPath(input, input.pullRequestId)}/reviews/${requirePositiveId(input.reviewId, 'review')}/dismissals`,\n { message: input.message },\n { actingUserId: input.actingUserId },\n );\n return parseReview(review);\n }\n\n async #deletePendingReview(input: ReviewRef) {\n await this.#client.request<void>(\n 'DELETE',\n `${pullRequestPath(input, input.pullRequestId)}/reviews/${requirePositiveId(input.reviewId, 'review')}`,\n undefined,\n { actingUserId: input.actingUserId },\n );\n }\n\n async #listReviewComments(input: ListReviewCommentsInput) {\n const page = parsePositiveCursor(input.cursor);\n const result = await this.#client.request<{ comments: GithubReviewComment[] }>(\n 'GET',\n `${pullRequestPath(input, input.pullRequestId)}/comments?page=${page}&per_page=${PAGE_SIZE}`,\n );\n return {\n comments: result.comments.map(parseReviewComment),\n nextCursor: result.comments.length === PAGE_SIZE ? String(page + 1) : null,\n };\n }\n\n async #createReviewComment(input: CreateReviewCommentInput) {\n let body: Record<string, unknown>;\n if (input.replyToId !== undefined) {\n body = { body: input.body, replyToId: requirePositiveId(input.replyToId, 'review comment') };\n } else {\n if (!input.commitId || !input.path || input.line === undefined || !input.side) {\n throw new Error('A review comment requires commitId, path, line, and side unless it is a reply.');\n }\n body = {\n body: input.body,\n commitId: input.commitId,\n path: input.path,\n line: input.line,\n side: input.side.toUpperCase(),\n startLine: input.startLine,\n startSide: input.startSide?.toUpperCase(),\n };\n }\n return parseReviewComment(\n await this.#client.request<GithubReviewComment>(\n 'POST',\n `${pullRequestPath(input, input.pullRequestId)}/comments`,\n body,\n { actingUserId: input.actingUserId },\n ),\n );\n }\n\n async #updateReviewComment(input: UpdatePullRequestCommentInput) {\n requireGithubConnection(input.connection);\n return parseReviewComment(\n await this.#client.request<GithubReviewComment>(\n 'PATCH',\n repositoryPath(input.sourceId, `pulls/comments/${requirePositiveId(input.commentId, 'review comment')}`),\n { body: input.body },\n { actingUserId: input.actingUserId },\n ),\n );\n }\n\n async #deleteReviewComment(input: DeletePullRequestCommentInput) {\n requireGithubConnection(input.connection);\n await this.#client.request<void>(\n 'DELETE',\n repositoryPath(input.sourceId, `pulls/comments/${requirePositiveId(input.commentId, 'review comment')}`),\n undefined,\n { actingUserId: input.actingUserId },\n );\n }\n\n #requestedReviewers(method: 'GET' | 'POST' | 'DELETE', input: UpdateReviewersInput) {\n requireGithubConnection(input.connection);\n return this.#client.request<{ users: string[]; teams: string[] }>(\n method,\n `${pullRequestPath(input, input.pullRequestId)}/requested-reviewers`,\n method === 'GET' ? undefined : { users: input.users, teams: input.teams },\n method === 'GET' ? undefined : { actingUserId: input.actingUserId },\n );\n }\n}\n\nfunction repositoryPath(sourceId: string, suffix: string): string {\n const { owner, repo } = splitRepository(sourceId);\n return `${API_PREFIX}/github/repos/${encodeURIComponent(owner)}/${encodeURIComponent(repo)}/${suffix}`;\n}\n\nfunction pullRequestPath(\n input: { connection: IntegrationConnection; sourceId: string },\n pullRequestId: string,\n): string {\n requireGithubConnection(input.connection);\n return repositoryPath(input.sourceId, `pulls/${requirePositiveId(pullRequestId, 'pull request')}`);\n}\n\nfunction splitRepository(sourceId: string): { owner: string; repo: string } {\n const slash = sourceId.indexOf('/');\n if (slash <= 0 || slash === sourceId.length - 1) {\n throw new Error('GitHub capabilities require an owner/repository source.');\n }\n return { owner: sourceId.slice(0, slash), repo: sourceId.slice(slash + 1) };\n}\n\nfunction parseIntakeIssue(sourceId: string, issue: GithubIssue): IntakeIssue {\n return {\n id: String(issue.number),\n identifier: `#${issue.number}`,\n title: issue.title,\n url: issue.htmlUrl,\n author: issue.user?.login ?? null,\n state: issue.state,\n stateType: issue.state,\n priority: null,\n assignee: issue.assignees[0] ?? null,\n assignees: issue.assignees,\n source: sourceId,\n labels: issue.labels,\n commentCount: issue.commentCount,\n createdAt: issue.createdAt,\n updatedAt: issue.updatedAt,\n };\n}\n\nfunction parseIntakeIssueDetail(sourceId: string, issue: GithubIssue, comments: GithubComment[]): IntakeIssueDetail {\n return {\n ...parseIntakeIssue(sourceId, issue),\n description: issue.body?.trim() ? issue.body : null,\n comments: comments.map(comment => ({\n author: comment.user?.login ?? null,\n body: comment.body,\n createdAt: comment.createdAt,\n })),\n };\n}\n\nfunction parsePullRequest(pullRequest: GithubPullRequest): PullRequest {\n return {\n id: String(pullRequest.number),\n title: pullRequest.title,\n url: pullRequest.htmlUrl,\n author: pullRequest.user?.login ?? null,\n assignees: pullRequest.assignees ?? [],\n requestedReviewers: pullRequest.requestedReviewers ?? [],\n body: pullRequest.body?.trim() ? pullRequest.body : null,\n state: pullRequest.state,\n draft: pullRequest.draft,\n merged: pullRequest.merged,\n mergeable: pullRequest.mergeable,\n baseBranch: pullRequest.base.ref,\n headBranch: pullRequest.head.ref,\n headSha: pullRequest.head.sha,\n createdAt: pullRequest.createdAt,\n updatedAt: pullRequest.updatedAt,\n };\n}\n\nfunction parseComment(comment: GithubComment): PullRequestComment {\n return {\n id: String(comment.id),\n url: comment.htmlUrl,\n author: comment.user?.login ?? null,\n body: comment.body,\n createdAt: comment.createdAt,\n updatedAt: comment.updatedAt,\n };\n}\n\nfunction parseReview(review: GithubReview): Review {\n const states: Record<GithubReview['state'], Review['state']> = {\n PENDING: 'pending',\n COMMENTED: 'commented',\n APPROVED: 'approved',\n CHANGES_REQUESTED: 'changes-requested',\n DISMISSED: 'dismissed',\n };\n return {\n id: String(review.id),\n url: review.htmlUrl,\n author: review.user?.login ?? null,\n body: review.body?.trim() ? review.body : null,\n state: states[review.state],\n commitId: review.commitId,\n submittedAt: review.submittedAt,\n };\n}\n\nfunction parseReviewComment(comment: GithubReviewComment): ReviewComment {\n return {\n ...parseComment(comment),\n path: comment.path,\n line: comment.line,\n side: comment.side?.toLowerCase() as 'left' | 'right' | null,\n commitId: comment.commitId,\n replyToId: comment.replyToId === null ? null : String(comment.replyToId),\n };\n}\n\nfunction requireGithubConnection(connection: IntegrationConnection): void {\n if (connection.type !== 'app-installation' && connection.type !== 'oauth') {\n throw new Error('GitHub capabilities require a GitHub connection.');\n }\n}\n\nfunction requireSingleSource(sourceIds: string[], message: string): string {\n if (sourceIds.length !== 1) throw new Error(message);\n return sourceIds[0]!;\n}\n\nfunction requireSource(sourceId: string | undefined, message: string): string {\n if (!sourceId) throw new Error(message);\n return sourceId;\n}\n\nfunction normalizeLabels(labels: string[] | undefined): string[] {\n return [...new Set((labels ?? []).map(label => label.trim()).filter(Boolean))];\n}\n\nfunction parsePositiveCursor(cursor: string | undefined): number {\n if (cursor === undefined) return 1;\n const parsed = parsePositiveInteger(cursor);\n if (parsed === null) throw new Error('GitHub cursor must be a positive page number.');\n return parsed;\n}\n\nfunction parsePositiveInteger(value: string): number | null {\n if (!/^\\d+$/.test(value)) return null;\n const parsed = Number(value);\n return Number.isSafeInteger(parsed) && parsed > 0 ? parsed : null;\n}\n\nfunction parseGithubExternalTarget(externalId: string): { repository: string; issueId: string } | null {\n const match =\n externalId.match(/^(.+\\/.+):(\\d+)$/) ??\n externalId.match(/^github:(\\d+):(?:issue|pull-request):(\\d+)$/) ??\n externalId.match(/^(\\d+):(\\d+)$/);\n if (!match?.[1] || !match[2] || parsePositiveInteger(match[2]) === null) return null;\n return { repository: match[1], issueId: match[2] };\n}\n\nfunction optionalPositiveIntegerEnv(name: 'MASTRA_PLATFORM_GITHUB_POLLING_INTERVAL_MS'): number | undefined {\n const value = process.env[name]?.trim();\n if (!value) return undefined;\n const parsed = parsePositiveInteger(value);\n if (parsed === null) throw new Error(`${name} must be a positive integer.`);\n return parsed;\n}\n\nfunction requirePositiveId(value: string, resource: string): number {\n const parsed = parsePositiveInteger(value);\n if (parsed === null) throw new Error(`GitHub ${resource} id must be a positive integer.`);\n return parsed;\n}\n\nfunction reviewEvent(event: 'approve' | 'request-changes' | 'comment') {\n if (event === 'approve') return 'APPROVE' as const;\n if (event === 'request-changes') return 'REQUEST_CHANGES' as const;\n return 'COMMENT' as const;\n}\n\nfunction isNotFound(error: unknown): boolean {\n return error instanceof PlatformApiError && error.status === 404;\n}\n\n// Platform answers 404 when the installation row is gone, 409 when it is suspended or soft-deleted.\n// A 502 also covers a dead installation but is indistinguishable from a transient GitHub outage.\nfunction isDeadInstallation(error: unknown): boolean {\n return error instanceof PlatformApiError && (error.status === 404 || error.status === 409);\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"integration.d.ts","sourceRoot":"","sources":["../../../src/integrations/slack/integration.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AAEpD,OAAO,KAAK,EAAE,qBAAqB,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAKhG;;;;;GAKG;AACH,MAAM,WAAW,sBAAsB;IACrC,2DAA2D;IAC3D,aAAa,EAAE,MAAM,CAAC;IACtB,0DAA0D;IAC1D,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB;;;;OAIG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB;;;OAGG;IACH,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,uDAAuD;IACvD,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,qBAAa,gBAAiB,YAAW,kBAAkB;;IACzD,QAAQ,CAAC,EAAE,WAAW;IACtB;;;;OAIG;IACH,QAAQ,CAAC,yBAAyB,QAAQ;gBAU9B,MAAM,EAAE,sBAAsB;IAS1C,QAAQ,CAAC,GAAG,EAAE,kBAAkB,GAAG,qBAAqB;
|
|
1
|
+
{"version":3,"file":"integration.d.ts","sourceRoot":"","sources":["../../../src/integrations/slack/integration.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AAEpD,OAAO,KAAK,EAAE,qBAAqB,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAKhG;;;;;GAKG;AACH,MAAM,WAAW,sBAAsB;IACrC,2DAA2D;IAC3D,aAAa,EAAE,MAAM,CAAC;IACtB,0DAA0D;IAC1D,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB;;;;OAIG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB;;;OAGG;IACH,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,uDAAuD;IACvD,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,qBAAa,gBAAiB,YAAW,kBAAkB;;IACzD,QAAQ,CAAC,EAAE,WAAW;IACtB;;;;OAIG;IACH,QAAQ,CAAC,yBAAyB,QAAQ;gBAU9B,MAAM,EAAE,sBAAsB;IAS1C,QAAQ,CAAC,GAAG,EAAE,kBAAkB,GAAG,qBAAqB;IAoBxD,MAAM,CAAC,GAAG,EAAE,kBAAkB,GAAG,QAAQ,EAAE;IAc3C,WAAW,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;CASvC"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { createSlackConnectRoutes } from "./connect-route.js";
|
|
2
|
-
import {
|
|
2
|
+
import { createSlackChannelsConfig } from "./slack.js";
|
|
3
3
|
//#region src/integrations/slack/integration.ts
|
|
4
4
|
var SlackIntegration = class {
|
|
5
5
|
id = "slack";
|
|
@@ -32,7 +32,8 @@ var SlackIntegration = class {
|
|
|
32
32
|
},
|
|
33
33
|
accountLinks: ctx.storage.channelIdentity,
|
|
34
34
|
projects: ctx.storage.projects,
|
|
35
|
-
sourceControl: sourceControlOwner
|
|
35
|
+
sourceControl: sourceControlOwner,
|
|
36
|
+
memorySettings: ctx.storage.memorySettings,
|
|
36
37
|
workItems: ctx.rules?.workItems
|
|
37
38
|
});
|
|
38
39
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"integration.js","names":["#config","#repoBackedSessions"],"sources":["../../../src/integrations/slack/integration.ts"],"sourcesContent":["/**\n * `SlackIntegration` — Slack as a `FactoryIntegration`.\n *\n * Slack contributes two things to a factory: the chat channels that carry\n * inbound messages into agent runs (`channels()`), and the browser-facing\n * account-link routes that bind a Slack sender to a Mastra tenant (`routes()`).\n * Both used to be assembled by hand in the deploy entry, which had to reach\n * into the prepared agent controller by string key, resolve storage domains\n * itself, mint its own state signer, and splice routes onto the factory's\n * assembled server config. Implementing the integration interface instead means\n * the factory does all of that the same way it already does for GitHub and\n * Linear, and the entry's job shrinks to reading Slack's env vars once.\n *\n * Slack ships as a factory built-in alongside GitHub and Linear (it originally\n * lived in `mastracode/web` to keep `@mastra/slack`/`chat` out of this package;\n * the team reversed that so `create-factory` consumers get Slack channels out\n * of the box). The integration interface remains open — third parties still add\n * capabilities by implementing `FactoryIntegration` from outside the package.\n */\n\nimport type { ApiRoute } from '@mastra/core/server';\n\nimport type { FactoryChannelsConfig, FactoryIntegration, IntegrationContext } from '../base.js';\n\nimport { createSlackConnectRoutes } from './connect-route.js';\nimport { createSlackChannelsConfig
|
|
1
|
+
{"version":3,"file":"integration.js","names":["#config","#repoBackedSessions"],"sources":["../../../src/integrations/slack/integration.ts"],"sourcesContent":["/**\n * `SlackIntegration` — Slack as a `FactoryIntegration`.\n *\n * Slack contributes two things to a factory: the chat channels that carry\n * inbound messages into agent runs (`channels()`), and the browser-facing\n * account-link routes that bind a Slack sender to a Mastra tenant (`routes()`).\n * Both used to be assembled by hand in the deploy entry, which had to reach\n * into the prepared agent controller by string key, resolve storage domains\n * itself, mint its own state signer, and splice routes onto the factory's\n * assembled server config. Implementing the integration interface instead means\n * the factory does all of that the same way it already does for GitHub and\n * Linear, and the entry's job shrinks to reading Slack's env vars once.\n *\n * Slack ships as a factory built-in alongside GitHub and Linear (it originally\n * lived in `mastracode/web` to keep `@mastra/slack`/`chat` out of this package;\n * the team reversed that so `create-factory` consumers get Slack channels out\n * of the box). The integration interface remains open — third parties still add\n * capabilities by implementing `FactoryIntegration` from outside the package.\n */\n\nimport type { ApiRoute } from '@mastra/core/server';\n\nimport type { FactoryChannelsConfig, FactoryIntegration, IntegrationContext } from '../base.js';\n\nimport { createSlackConnectRoutes } from './connect-route.js';\nimport { createSlackChannelsConfig } from './slack.js';\n\n/**\n * Slack app credentials, read from env ONCE by the deploy entry. `signingSecret`\n * is required because the Slack adapter validates it at construction — an\n * integration constructed without it would throw during `prepare()` rather than\n * reporting itself unconfigured.\n */\nexport interface SlackIntegrationConfig {\n /** Verifies inbound Slack request signatures. Required. */\n signingSecret: string;\n /** Bot token used to post replies and ephemeral cards. */\n botToken?: string;\n /**\n * OAuth client credentials. Required for account linking: \"Sign in with\n * Slack\" (OIDC) is the only flow that writes a link, so without these the\n * settings surface reports `canConnect: false` and no sender can link.\n */\n clientId?: string;\n clientSecret?: string;\n /**\n * HTTPS origin Slack redirects back to. Slack requires HTTPS, so locally this\n * is the tunnel origin rather than the app's own public URL.\n */\n oidcRedirectBaseUrl?: string;\n /** SPA origin the post-connect redirect returns to. */\n uiOrigin?: string;\n}\n\nexport class SlackIntegration implements FactoryIntegration {\n readonly id = 'slack';\n /**\n * The OIDC connect flow round-trips a signed `state` through Slack, so the\n * replica handling the callback must be able to verify a state a different\n * replica signed.\n */\n readonly requiresStableStateSigner = true;\n\n readonly #config: SlackIntegrationConfig;\n /**\n * Whether `channels()` found a source-control owner on the context and wired\n * repo-backed sessions. Set at the channels() attach path, which runs once at\n * boot before diagnostics are served.\n */\n #repoBackedSessions = false;\n\n constructor(config: SlackIntegrationConfig) {\n if (!config.signingSecret) {\n throw new Error(\n \"SlackIntegration: 'signingSecret' is required — Slack cannot verify inbound requests without it.\",\n );\n }\n this.#config = config;\n }\n\n channels(ctx: IntegrationContext): FactoryChannelsConfig {\n // Repo-backed sessions come from the factory's source-control owner\n // (GitHub, when registered) — no config-level wiring by the entry.\n const sourceControlOwner = ctx.storage.sourceControlOwner;\n this.#repoBackedSessions = Boolean(sourceControlOwner);\n return createSlackChannelsConfig({\n slack: {\n clientId: this.#config.clientId,\n clientSecret: this.#config.clientSecret,\n signingSecret: this.#config.signingSecret,\n botToken: this.#config.botToken,\n },\n accountLinks: ctx.storage.channelIdentity,\n projects: ctx.storage.projects,\n sourceControl: sourceControlOwner,\n memorySettings: ctx.storage.memorySettings,\n workItems: ctx.rules?.workItems,\n });\n }\n\n routes(ctx: IntegrationContext): ApiRoute[] {\n const { clientId, clientSecret, oidcRedirectBaseUrl, uiOrigin } = this.#config;\n return createSlackConnectRoutes({\n auth: ctx.auth,\n accountLinks: ctx.storage.channelIdentity,\n tenantStateSigner: ctx.stateSigner,\n oidc:\n clientId && clientSecret && oidcRedirectBaseUrl\n ? { clientId, clientSecret, redirectBaseUrl: oidcRedirectBaseUrl, uiOrigin }\n : undefined,\n projects: ctx.storage.projects,\n });\n }\n\n diagnostics(): Record<string, unknown> {\n const { clientId, clientSecret, botToken, oidcRedirectBaseUrl } = this.#config;\n return {\n configured: true,\n botTokenConfigured: Boolean(botToken),\n oidcConfigured: Boolean(clientId && clientSecret && oidcRedirectBaseUrl),\n repoBackedSessions: this.#repoBackedSessions,\n };\n }\n}\n"],"mappings":";;;AAsDA,IAAa,mBAAb,MAA4D;CAC1D,KAAc;;;;;;CAMd,4BAAqC;CAErC;;;;;;CAMA,sBAAsB;CAEtB,YAAY,QAAgC;EAC1C,IAAI,CAAC,OAAO,eACV,MAAM,IAAI,MACR,kGACF;EAEF,KAAKA,UAAU;CACjB;CAEA,SAAS,KAAgD;EAGvD,MAAM,qBAAqB,IAAI,QAAQ;EACvC,KAAKC,sBAAsB,QAAQ,kBAAkB;EACrD,OAAO,0BAA0B;GAC/B,OAAO;IACL,UAAU,KAAKD,QAAQ;IACvB,cAAc,KAAKA,QAAQ;IAC3B,eAAe,KAAKA,QAAQ;IAC5B,UAAU,KAAKA,QAAQ;GACzB;GACA,cAAc,IAAI,QAAQ;GAC1B,UAAU,IAAI,QAAQ;GACtB,eAAe;GACf,gBAAgB,IAAI,QAAQ;GAC5B,WAAW,IAAI,OAAO;EACxB,CAAC;CACH;CAEA,OAAO,KAAqC;EAC1C,MAAM,EAAE,UAAU,cAAc,qBAAqB,aAAa,KAAKA;EACvE,OAAO,yBAAyB;GAC9B,MAAM,IAAI;GACV,cAAc,IAAI,QAAQ;GAC1B,mBAAmB,IAAI;GACvB,MACE,YAAY,gBAAgB,sBACxB;IAAE;IAAU;IAAc,iBAAiB;IAAqB;GAAS,IACzE,KAAA;GACN,UAAU,IAAI,QAAQ;EACxB,CAAC;CACH;CAEA,cAAuC;EACrC,MAAM,EAAE,UAAU,cAAc,UAAU,wBAAwB,KAAKA;EACvE,OAAO;GACL,YAAY;GACZ,oBAAoB,QAAQ,QAAQ;GACpC,gBAAgB,QAAQ,YAAY,gBAAgB,mBAAmB;GACvE,oBAAoB,KAAKC;EAC3B;CACF;AACF"}
|