@shipfox/api-integration-github 12.7.0 → 14.0.0

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.
Files changed (39) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/CHANGELOG.md +23 -0
  3. package/dist/api/client.d.ts +21 -3
  4. package/dist/api/client.d.ts.map +1 -1
  5. package/dist/api/client.js +115 -6
  6. package/dist/api/client.js.map +1 -1
  7. package/dist/api/installation-token-envelope.d.ts.map +1 -1
  8. package/dist/api/installation-token-envelope.js.map +1 -1
  9. package/dist/core/agent-tools.d.ts.map +1 -1
  10. package/dist/core/agent-tools.js +2 -6
  11. package/dist/core/agent-tools.js.map +1 -1
  12. package/dist/core/bot-identity.d.ts +4 -0
  13. package/dist/core/bot-identity.d.ts.map +1 -0
  14. package/dist/core/bot-identity.js +15 -0
  15. package/dist/core/bot-identity.js.map +1 -0
  16. package/dist/core/source-control.d.ts +4 -2
  17. package/dist/core/source-control.d.ts.map +1 -1
  18. package/dist/core/source-control.js +42 -11
  19. package/dist/core/source-control.js.map +1 -1
  20. package/dist/index.d.ts +78 -0
  21. package/dist/index.d.ts.map +1 -1
  22. package/dist/index.js +2 -0
  23. package/dist/index.js.map +1 -1
  24. package/dist/tsconfig.test.tsbuildinfo +1 -1
  25. package/package.json +3 -3
  26. package/src/api/client.test.ts +267 -9
  27. package/src/api/client.ts +184 -9
  28. package/src/api/installation-token-envelope.ts +3 -1
  29. package/src/core/agent-tools.test.ts +1 -0
  30. package/src/core/agent-tools.ts +2 -9
  31. package/src/core/bot-identity.test.ts +19 -0
  32. package/src/core/bot-identity.ts +19 -0
  33. package/src/core/install.test.ts +3 -0
  34. package/src/core/source-control.test.ts +180 -1
  35. package/src/core/source-control.ts +65 -11
  36. package/src/index.test.ts +3 -1
  37. package/src/index.ts +2 -0
  38. package/src/presentation/routes/install.test.ts +3 -0
  39. package/tsconfig.build.tsbuildinfo +1 -1
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/core/agent-tools.ts"],"sourcesContent":["import type {\n AgentToolCallInput,\n AgentToolCatalogEntry,\n AgentToolSelectionCatalog,\n AgentToolSession,\n AgentToolsProvider,\n IntegrationConnection,\n OpenAgentToolsSessionInput,\n} from '@shipfox/api-integration-spi';\nimport {Octokit} from 'octokit';\nimport {mapGithubError} from '#api/client.js';\nimport {\n createGithubInstallationTokenProvider,\n type GithubInstallationTokenProvider,\n} from '#api/installation-token-provider.js';\nimport {config, normalizedGithubApiBaseUrl} from '#config.js';\nimport type {GithubInstallation} from '#db/installations.js';\nimport {GithubIntegrationProviderError} from './errors.js';\nimport {\n type GithubAgentToolCatalogEntry,\n type GithubAgentToolId,\n type GithubAgentToolRequiredScope,\n githubAgentToolCatalog,\n githubAgentToolSelectionCatalog,\n} from './github-agent-tool-catalog.js';\n\nexport type {\n GithubAgentToolCatalogEntry,\n GithubAgentToolCategory,\n GithubAgentToolId,\n GithubAgentToolPermission,\n GithubAgentToolPermissionAccess,\n GithubAgentToolRequiredPermission,\n GithubAgentToolRequiredScope,\n GithubAgentToolSensitivity,\n} from './github-agent-tool-catalog.js';\nexport {\n buildGithubAgentToolSelectionCatalog,\n DEFAULT_JOB_LOG_TAIL_LINES,\n githubAgentToolCatalog,\n githubAgentToolSelectionCatalog,\n} from './github-agent-tool-catalog.js';\n\ntype GithubIntegrationConnection = IntegrationConnection<'github'>;\n\ntype GithubToolCallResult = {\n isError?: boolean | undefined;\n content: readonly {type: 'text'; text: string}[];\n structuredContent?: Record<string, unknown> | undefined;\n};\n\ntype GithubToolErrorCode =\n | 'invalid-request'\n | 'access-denied'\n | 'provider-rejected'\n | 'malformed-provider-response';\n\nconst GITHUB_GRAPHQL_ROUTE = 'POST /graphql';\nconst GITHUB_ARTIFACT_ARCHIVE_FORMAT = 'zip';\nconst GITHUB_ARTIFACT_DOWNLOAD_ROUTE = `GET /repos/{owner}/{repo}/actions/artifacts/{resource_id}/${GITHUB_ARTIFACT_ARCHIVE_FORMAT}`;\nconst GITHUB_ARTIFACT_DOWNLOAD_TIMEOUT_MS = 30_000;\nconst GITHUB_APP_BOT_SUFFIX = '[bot]';\nconst PENDING_REVIEW_PAGE_SIZE = 100;\nconst PENDING_REVIEW_MAX_PAGE_REQUESTS = 5;\nconst PENDING_REVIEW_LOOKUP_TIMEOUT_MS = 15_000;\nconst PENDING_REVIEW_PAGE_TIMEOUT_MS = 5_000;\nconst PENDING_REVIEW_PAGE_PATTERN = /[?&]page=(\\d+)/u;\nconst NO_PENDING_REVIEW_MESSAGE =\n 'No pending pull request review found for the authenticated GitHub user.';\n\nconst ADD_PENDING_REVIEW_COMMENT_MUTATION = `\n mutation AddCommentToPendingReview($input: AddPullRequestReviewThreadInput!) {\n addPullRequestReviewThread(input: $input) {\n thread {\n id\n }\n }\n }\n`;\n\nconst GET_PULL_REQUEST_REVIEW_THREADS_QUERY = `\n query GetPullRequestReviewThreads(\n $owner: String!\n $repo: String!\n $pullNumber: Int!\n $after: String\n ) {\n repository(owner: $owner, name: $repo) {\n pullRequest(number: $pullNumber) {\n reviewThreads(first: 100, after: $after) {\n nodes {\n id\n isResolved\n comments(first: 100) {\n nodes {\n id\n databaseId\n body\n author {\n login\n }\n path\n line\n side\n startLine\n startSide\n createdAt\n updatedAt\n url\n }\n pageInfo {\n hasNextPage\n endCursor\n }\n }\n }\n pageInfo {\n hasNextPage\n endCursor\n }\n }\n }\n }\n }\n`;\n\nconst RESOLVE_PULL_REQUEST_REVIEW_THREAD_MUTATION = `\n mutation ResolvePullRequestReviewThread($input: ResolveReviewThreadInput!) {\n resolveReviewThread(input: $input) {\n thread {\n id\n isResolved\n }\n }\n }\n`;\n\nexport class GithubAgentToolsProvider\n implements\n AgentToolsProvider<\n GithubIntegrationConnection,\n GithubAgentToolRequiredScope,\n unknown,\n GithubToolCallResult\n >\n{\n private readonly tokenProvider: GithubInstallationTokenProvider;\n\n constructor(private readonly options: GithubAgentToolsProviderOptions = {}) {\n this.tokenProvider = options.tokenProvider ?? createGithubInstallationTokenProvider();\n }\n\n catalog(): readonly GithubAgentToolCatalogEntry[] {\n return githubAgentToolCatalog;\n }\n\n selectionCatalog(): AgentToolSelectionCatalog {\n return githubAgentToolSelectionCatalog;\n }\n\n async openSession(\n input: OpenAgentToolsSessionInput<GithubIntegrationConnection, GithubAgentToolRequiredScope>,\n ): Promise<AgentToolSession<GithubToolCallResult>> {\n const installation = await this.options.getInstallationByConnectionId?.(input.connection.id);\n if (!installation) {\n throw new GithubIntegrationProviderError(\n 'installation-not-found',\n 'GitHub installation is not connected to this integration',\n );\n }\n const installationId = Number(installation.installationId);\n if (!Number.isSafeInteger(installationId) || installationId < 1) {\n throw new GithubIntegrationProviderError(\n 'malformed-provider-response',\n 'GitHub installation has an invalid installation ID',\n );\n }\n let tokenPromise:\n | ReturnType<GithubInstallationTokenProvider['getInstallationAccessToken']>\n | undefined;\n\n return {\n call: async (call) => {\n const tool = input.tools.find((candidate) => candidate.id === call.toolId);\n if (!tool) return githubToolError(`Unknown GitHub tool: ${call.toolId}`, 'invalid-request');\n const operation = resolveGithubOperation(tool, call);\n if (operation === undefined)\n return githubToolError('Unknown GitHub tool operation', 'invalid-request');\n const validationError = validateGithubToolArguments(tool, call.arguments);\n if (validationError) return githubToolError(validationError, 'invalid-request');\n tokenPromise ??= this.tokenProvider.getInstallationAccessToken(installationId);\n const token = await tokenPromise;\n if (!hasGrantedPermissions(token.permissions ?? {}, tool, call)) {\n return githubToolError(\n 'GitHub installation token is missing permission for this operation',\n 'access-denied',\n );\n }\n const client = (this.options.createClient ?? createOctokitClient)(token.token);\n const method =\n typeof call.arguments.method === 'string' ? call.arguments.method : undefined;\n\n if (operation.kind === 'graphql') {\n const data = await mapGithubError(() =>\n executeGithubGraphqlOperation(\n client,\n tool.id as GithubAgentToolId,\n method,\n operation.parameters,\n ),\n );\n if (data === undefined && tool.id === 'add_comment_to_pending_review') {\n return githubToolError(NO_PENDING_REVIEW_MESSAGE, 'provider-rejected');\n }\n return githubToolResult(tool.id as GithubAgentToolId, data);\n }\n\n const operationParameters = await mapGithubError(() =>\n resolvePendingReviewParameters(\n client,\n operation.parameters,\n tool.id as GithubAgentToolId,\n method,\n ),\n );\n if (operationParameters === undefined) {\n return githubToolError(NO_PENDING_REVIEW_MESSAGE, 'provider-rejected');\n }\n const response = await mapGithubError(() =>\n client.request(operation.route, operationParameters),\n );\n return githubToolResult(\n tool.id as GithubAgentToolId,\n response.data,\n response,\n operationParameters,\n operation.route,\n );\n },\n };\n }\n}\n\nexport interface GithubAgentToolsProviderOptions {\n getInstallationByConnectionId?:\n | ((connectionId: string) => Promise<GithubInstallation | undefined>)\n | undefined;\n tokenProvider?: GithubInstallationTokenProvider | undefined;\n createClient?: GithubToolClientFactory | undefined;\n}\n\nexport interface GithubToolResponse {\n data: unknown;\n headers?: Record<string, string | number | undefined> | undefined;\n status?: number | undefined;\n url?: string | undefined;\n}\n\nexport interface GithubToolClient {\n request(route: string, parameters: Record<string, unknown>): Promise<GithubToolResponse>;\n graphql?: ((query: string, variables: Record<string, unknown>) => Promise<unknown>) | undefined;\n}\n\nexport type GithubToolClientFactory = (token: string) => GithubToolClient;\n\ninterface GithubToolOperation {\n route: string;\n parameters: Record<string, unknown>;\n kind: 'rest' | 'graphql';\n}\n\nfunction createOctokitClient(token: string): GithubToolClient {\n const octokit = new Octokit({\n auth: token,\n baseUrl: normalizedGithubApiBaseUrl(),\n retry: {enabled: false},\n });\n return {\n request: async (route, parameters) => {\n if (route !== GITHUB_ARTIFACT_DOWNLOAD_ROUTE) {\n return await octokit.request(route, parameters);\n }\n\n const abortController = new AbortController();\n const timeout = setTimeout(\n () => abortController.abort(),\n GITHUB_ARTIFACT_DOWNLOAD_TIMEOUT_MS,\n );\n try {\n return await octokit.request(route, {\n ...parameters,\n request: {\n redirect: 'manual',\n parseSuccessResponseBody: false,\n signal: abortController.signal,\n },\n });\n } finally {\n clearTimeout(timeout);\n }\n },\n graphql: async (query, variables) => await octokit.graphql(query, variables),\n };\n}\n\nfunction resolveGithubOperation(\n tool: AgentToolCatalogEntry<GithubAgentToolRequiredScope>,\n call: AgentToolCallInput,\n): GithubToolOperation | undefined {\n const args = call.arguments;\n const method = typeof args.method === 'string' ? args.method : undefined;\n const params = {...args};\n delete params.method;\n\n if (tool.methods && !tool.methods.some((candidate) => candidate.id === method)) return undefined;\n\n const toolId = tool.id as GithubAgentToolId;\n const route = githubOperationRoute(toolId, method, params);\n return route === undefined\n ? undefined\n : {\n route,\n parameters: projectGithubOperationParameters(toolId, method, params),\n kind: route === GITHUB_GRAPHQL_ROUTE ? 'graphql' : 'rest',\n };\n}\n\nexport function githubOperationRoute(\n toolId: GithubAgentToolId,\n method: string | undefined,\n args: Record<string, unknown>,\n): string | undefined {\n const owner = '{owner}';\n const repo = '{repo}';\n const issue = '{issue_number}';\n const pull = '{pull_number}';\n const run = '{run_id}';\n const resource = '{resource_id}';\n const repoPath = `/repos/${owner}/${repo}`;\n\n switch (`${toolId}.${method ?? ''}`) {\n case 'issue_read.get':\n return `GET ${repoPath}/issues/${issue}`;\n case 'issue_read.get_comments':\n return `GET ${repoPath}/issues/${issue}/comments`;\n case 'issue_read.get_sub_issues':\n return `GET ${repoPath}/issues/${issue}/sub_issues`;\n case 'issue_read.get_parent':\n return `GET ${repoPath}/issues/${issue}/parent`;\n case 'issue_read.get_labels':\n return `GET ${repoPath}/issues/${issue}/labels`;\n case 'list_issue_types.':\n return args.repo === undefined\n ? 'GET /orgs/{owner}/issue-types'\n : `GET ${repoPath}/issue-types`;\n case 'list_issues.':\n return `GET ${repoPath}/issues`;\n case 'search_issues.':\n return 'GET /search/issues';\n case 'add_issue_comment.':\n if (args.comment_id !== undefined)\n return `POST ${repoPath}/issues/comments/{comment_id}/reactions`;\n return args.reaction !== undefined && args.body === undefined\n ? `POST ${repoPath}/issues/${issue}/reactions`\n : `POST ${repoPath}/issues/${issue}/comments`;\n case 'issue_write.create':\n return `POST ${repoPath}/issues`;\n case 'issue_write.update':\n return `PATCH ${repoPath}/issues/${issue}`;\n case 'sub_issue_write.add':\n return `POST ${repoPath}/issues/${issue}/sub_issues`;\n case 'sub_issue_write.remove':\n return `DELETE ${repoPath}/issues/${issue}/sub_issues/{sub_issue_id}`;\n case 'sub_issue_write.reprioritize':\n return `PATCH ${repoPath}/issues/${issue}/sub_issues/priority`;\n case 'pull_request_read.get':\n return `GET ${repoPath}/pulls/${pull}`;\n case 'pull_request_read.get_diff':\n return `GET ${repoPath}/pulls/${pull}`;\n case 'pull_request_read.get_status':\n return `GET ${repoPath}/commits/{ref}/status`;\n case 'pull_request_read.get_files':\n return `GET ${repoPath}/pulls/${pull}/files`;\n case 'pull_request_read.get_commits':\n return `GET ${repoPath}/pulls/${pull}/commits`;\n case 'pull_request_read.get_review_comments':\n return `GET ${repoPath}/pulls/${pull}/comments`;\n case 'pull_request_read.get_review_threads':\n return GITHUB_GRAPHQL_ROUTE;\n case 'pull_request_read.get_reviews':\n return `GET ${repoPath}/pulls/${pull}/reviews`;\n case 'pull_request_read.get_comments':\n return `GET ${repoPath}/issues/${pull}/comments`;\n case 'pull_request_read.get_check_runs':\n return `GET ${repoPath}/commits/{ref}/check-runs`;\n case 'list_pull_requests.':\n return `GET ${repoPath}/pulls`;\n case 'search_pull_requests.':\n return 'GET /search/issues';\n case 'create_pull_request.':\n return `POST ${repoPath}/pulls`;\n case 'update_pull_request.':\n return `PATCH ${repoPath}/pulls/${pull}`;\n case 'add_reply_to_pull_request_comment.':\n return args.reaction !== undefined && args.body === undefined\n ? `POST ${repoPath}/pulls/comments/{comment_id}/reactions`\n : `POST ${repoPath}/pulls/${pull}/comments/{comment_id}/replies`;\n case 'merge_pull_request.':\n return `PUT ${repoPath}/pulls/${pull}/merge`;\n case 'update_pull_request_branch.':\n return `PUT ${repoPath}/pulls/${pull}/update-branch`;\n case 'pull_request_review_write.create':\n return `POST ${repoPath}/pulls/${pull}/reviews`;\n case 'pull_request_review_write.submit_pending':\n return `POST ${repoPath}/pulls/${pull}/reviews/{review_id}/events`;\n case 'pull_request_review_write.delete_pending':\n return `DELETE ${repoPath}/pulls/${pull}/reviews/{review_id}`;\n case 'pull_request_review_thread_write.resolve':\n return GITHUB_GRAPHQL_ROUTE;\n case 'add_comment_to_pending_review.':\n return GITHUB_GRAPHQL_ROUTE;\n case 'actions_list.list_workflows':\n return `GET ${repoPath}/actions/workflows`;\n case 'actions_list.list_workflow_runs':\n return `GET ${repoPath}/actions/workflows/${resource}/runs`;\n case 'actions_list.list_workflow_jobs':\n return `GET ${repoPath}/actions/runs/${resource}/jobs`;\n case 'actions_list.list_workflow_run_artifacts':\n return `GET ${repoPath}/actions/runs/${resource}/artifacts`;\n case 'actions_get.get_workflow':\n return `GET ${repoPath}/actions/workflows/${resource}`;\n case 'actions_get.get_workflow_run':\n return `GET ${repoPath}/actions/runs/${resource}`;\n case 'actions_get.get_workflow_job':\n return `GET ${repoPath}/actions/jobs/${resource}`;\n case 'actions_get.download_workflow_run_artifact':\n return GITHUB_ARTIFACT_DOWNLOAD_ROUTE;\n case 'actions_get.get_workflow_run_usage':\n return `GET ${repoPath}/actions/runs/${resource}/timing`;\n case 'actions_get.get_workflow_run_logs_url':\n return `GET ${repoPath}/actions/runs/${resource}/logs`;\n case 'actions_run_trigger.run_workflow':\n return `POST ${repoPath}/actions/workflows/{workflow_id}/dispatches`;\n case 'actions_run_trigger.rerun_workflow_run':\n return `POST ${repoPath}/actions/runs/${run}/rerun`;\n case 'actions_run_trigger.rerun_failed_jobs':\n return `POST ${repoPath}/actions/runs/${run}/rerun-failed-jobs`;\n case 'actions_run_trigger.cancel_workflow_run':\n return `POST ${repoPath}/actions/runs/${run}/cancel`;\n case 'actions_run_trigger.delete_workflow_run_logs':\n return `DELETE ${repoPath}/actions/runs/${run}/logs`;\n case 'get_job_logs.':\n return `GET ${repoPath}/actions/jobs/{job_id}/logs`;\n default:\n return undefined;\n }\n}\n\nasync function addCommentToPendingReview(\n client: GithubToolClient,\n args: Record<string, unknown>,\n): Promise<unknown | undefined> {\n if (client.graphql === undefined) {\n throw new GithubIntegrationProviderError(\n 'malformed-provider-response',\n 'GitHub client does not support GraphQL operations',\n );\n }\n\n const review = await latestPendingReview(client, args, 'nodeId');\n if (review === undefined) return undefined;\n if (review.nodeId === undefined) {\n throw new GithubIntegrationProviderError(\n 'malformed-provider-response',\n 'GitHub pending pull request review did not include a node ID',\n );\n }\n\n const input: Record<string, unknown> = {\n pullRequestReviewId: review.nodeId,\n path: args.path,\n body: args.body,\n subjectType: args.subject_type,\n };\n if (args.line !== undefined) input.line = args.line;\n if (args.side !== undefined) input.side = args.side;\n if (args.start_line !== undefined) input.startLine = args.start_line;\n if (args.start_side !== undefined) input.startSide = args.start_side;\n\n return await client.graphql(ADD_PENDING_REVIEW_COMMENT_MUTATION, {input});\n}\n\nasync function executeGithubGraphqlOperation(\n client: GithubToolClient,\n toolId: GithubAgentToolId,\n method: string | undefined,\n parameters: Record<string, unknown>,\n): Promise<unknown | undefined> {\n if (client.graphql === undefined) {\n throw new GithubIntegrationProviderError(\n 'malformed-provider-response',\n 'GitHub client does not support GraphQL operations',\n );\n }\n\n switch (`${toolId}.${method ?? ''}`) {\n case 'pull_request_read.get_review_threads': {\n const variables: Record<string, unknown> = {\n owner: parameters.owner,\n repo: parameters.repo,\n pullNumber: parameters.pull_number,\n };\n if (typeof parameters.cursor === 'string') variables.after = parameters.cursor;\n return await client.graphql(GET_PULL_REQUEST_REVIEW_THREADS_QUERY, variables);\n }\n case 'pull_request_review_thread_write.resolve':\n return await client.graphql(RESOLVE_PULL_REQUEST_REVIEW_THREAD_MUTATION, {\n input: {threadId: parameters.thread_id},\n });\n case 'add_comment_to_pending_review.':\n return await addCommentToPendingReview(client, parameters);\n default:\n throw new GithubIntegrationProviderError(\n 'malformed-provider-response',\n 'GitHub operation does not support GraphQL operations',\n );\n }\n}\n\nexport function projectGithubOperationParameters(\n toolId: GithubAgentToolId,\n method: string | undefined,\n args: Record<string, unknown>,\n): Record<string, unknown> {\n const parameters = {...args};\n if (toolId === 'add_issue_comment' && parameters.reaction !== undefined) {\n parameters.content = parameters.reaction;\n delete parameters.reaction;\n if (parameters.body === undefined) delete parameters.body;\n }\n if (toolId === 'pull_request_read' && method === 'get_diff') {\n parameters.headers = {accept: 'application/vnd.github.diff'};\n }\n return parameters;\n}\n\nasync function resolvePendingReviewParameters(\n client: GithubToolClient,\n parameters: Record<string, unknown>,\n toolId: GithubAgentToolId,\n method: string | undefined,\n): Promise<Record<string, unknown> | undefined> {\n if (!isPendingReviewOperation(toolId, method)) return parameters;\n\n const review = await latestPendingReview(client, parameters, 'id');\n if (review === undefined) return undefined;\n if (review.id === undefined) {\n throw new GithubIntegrationProviderError(\n 'malformed-provider-response',\n 'GitHub pending pull request review did not include a numeric ID',\n );\n }\n return {...parameters, review_id: review.id};\n}\n\nfunction isPendingReviewOperation(toolId: GithubAgentToolId, method: string | undefined): boolean {\n return (\n toolId === 'pull_request_review_write' &&\n (method === 'submit_pending' || method === 'delete_pending')\n );\n}\n\ninterface PendingReviewReference {\n id?: number | undefined;\n nodeId?: string | undefined;\n}\n\ntype PendingReviewIdentifier = keyof PendingReviewReference;\n\ninterface PendingReviewPageResult {\n malformed: boolean;\n review?: PendingReviewReference | undefined;\n}\n\nasync function latestPendingReview(\n client: GithubToolClient,\n parameters: Record<string, unknown>,\n requiredIdentifier: PendingReviewIdentifier,\n): Promise<PendingReviewReference | undefined> {\n const lookupController = new AbortController();\n const lookupTimeout = setTimeout(\n () => lookupController.abort(),\n PENDING_REVIEW_LOOKUP_TIMEOUT_MS,\n );\n\n try {\n return await latestPendingReviewBeforeDeadline(\n client,\n parameters,\n requiredIdentifier,\n lookupController.signal,\n );\n } finally {\n clearTimeout(lookupTimeout);\n }\n}\n\nasync function latestPendingReviewBeforeDeadline(\n client: GithubToolClient,\n parameters: Record<string, unknown>,\n requiredIdentifier: PendingReviewIdentifier,\n lookupSignal: AbortSignal,\n): Promise<PendingReviewReference | undefined> {\n const firstPage = await requestPendingReviewPage(client, parameters, 1, lookupSignal);\n const lastPage = pendingReviewLastPage(firstPage.headers);\n let requests = 1;\n let malformed = false;\n\n for (let page = lastPage; page >= 1; page -= 1) {\n let response = firstPage;\n if (page !== 1) {\n if (requests >= PENDING_REVIEW_MAX_PAGE_REQUESTS) {\n throw new GithubIntegrationProviderError(\n 'content-too-large',\n 'GitHub pull request review history exceeded the pending review lookup limit',\n );\n }\n response = await requestPendingReviewPage(client, parameters, page, lookupSignal);\n requests += 1;\n }\n if (!Array.isArray(response.data)) {\n throw new GithubIntegrationProviderError(\n 'malformed-provider-response',\n 'GitHub pull request review list response was malformed',\n );\n }\n\n const result = latestPendingReviewOnPage(response.data, requiredIdentifier);\n if (result.review !== undefined) return result.review;\n malformed ||= result.malformed;\n }\n\n if (malformed) {\n throw new GithubIntegrationProviderError(\n 'malformed-provider-response',\n requiredIdentifier === 'nodeId'\n ? 'GitHub pending pull request review did not include a node ID'\n : 'GitHub pending pull request review did not include a numeric ID',\n );\n }\n return undefined;\n}\n\nasync function requestPendingReviewPage(\n client: GithubToolClient,\n parameters: Record<string, unknown>,\n page: number,\n lookupSignal: AbortSignal,\n): Promise<GithubToolResponse> {\n const pageController = new AbortController();\n const abortPage = () => pageController.abort();\n if (lookupSignal.aborted) abortPage();\n else lookupSignal.addEventListener('abort', abortPage, {once: true});\n const pageTimeout = setTimeout(abortPage, PENDING_REVIEW_PAGE_TIMEOUT_MS);\n\n try {\n return await client.request('GET /repos/{owner}/{repo}/pulls/{pull_number}/reviews', {\n owner: parameters.owner,\n repo: parameters.repo,\n pull_number: parameters.pull_number,\n per_page: PENDING_REVIEW_PAGE_SIZE,\n page,\n request: {signal: pageController.signal},\n });\n } finally {\n clearTimeout(pageTimeout);\n lookupSignal.removeEventListener('abort', abortPage);\n }\n}\n\nfunction pendingReviewLastPage(headers: GithubToolResponse['headers']): number {\n const link = headers?.link;\n if (typeof link !== 'string') return 1;\n const lastLink = link.split(',').find((part) => part.includes('rel=\"last\"'));\n if (lastLink === undefined) return 1;\n const match = PENDING_REVIEW_PAGE_PATTERN.exec(lastLink);\n const page = match?.[1] === undefined ? Number.NaN : Number.parseInt(match[1], 10);\n if (!Number.isSafeInteger(page) || page < 1) {\n throw new GithubIntegrationProviderError(\n 'malformed-provider-response',\n 'GitHub pull request review pagination response was malformed',\n );\n }\n return page;\n}\n\nfunction latestPendingReviewOnPage(\n data: readonly unknown[],\n requiredIdentifier: PendingReviewIdentifier,\n): PendingReviewPageResult {\n let malformed = false;\n const appLogin = githubAppBotLogin().toLowerCase();\n for (let index = data.length - 1; index >= 0; index -= 1) {\n const review = data[index];\n if (!isRecord(review) || review.state !== 'PENDING') continue;\n const userLogin = isRecord(review.user) ? review.user.login : undefined;\n if (typeof userLogin !== 'string' || userLogin.trim().length === 0) {\n malformed = true;\n continue;\n }\n if (userLogin.trim().toLowerCase() !== appLogin) continue;\n const id =\n typeof review.id === 'number' && Number.isSafeInteger(review.id) && review.id > 0\n ? review.id\n : undefined;\n const nodeId =\n typeof review.node_id === 'string' && review.node_id.trim().length > 0\n ? review.node_id.trim()\n : undefined;\n const reference = {id, nodeId};\n if (reference[requiredIdentifier] !== undefined) return {malformed, review: reference};\n malformed = true;\n }\n\n return {malformed};\n}\n\nfunction githubAppBotLogin(): string {\n const configuredUsername = config.GITHUB_APP_USERNAME?.trim() || config.GITHUB_APP_SLUG.trim();\n return configuredUsername.toLowerCase().endsWith(GITHUB_APP_BOT_SUFFIX)\n ? configuredUsername\n : `${configuredUsername}${GITHUB_APP_BOT_SUFFIX}`;\n}\n\nfunction githubToolResult(\n toolId: GithubAgentToolId,\n data: unknown,\n response?: GithubToolResponse,\n parameters?: Record<string, unknown>,\n route?: string,\n): GithubToolCallResult {\n const structuredContent = projectGithubToolOutput(toolId, data, response, parameters, route);\n if (structuredContent === undefined) {\n return githubToolError(\n 'GitHub artifact download did not return a download URL',\n 'malformed-provider-response',\n );\n }\n return {\n content: [{type: 'text', text: JSON.stringify(structuredContent)}],\n structuredContent,\n };\n}\n\nfunction projectGithubToolOutput(\n toolId: GithubAgentToolId,\n data: unknown,\n response?: GithubToolResponse,\n parameters?: Record<string, unknown>,\n route?: string,\n): Record<string, unknown> | undefined {\n if (route === GITHUB_ARTIFACT_DOWNLOAD_ROUTE) {\n return projectGithubArtifactDownloadOutput(response, parameters);\n }\n\n switch (toolId) {\n case 'list_issue_types':\n return {issue_types: data};\n case 'list_issues':\n return {issues: data};\n case 'search_issues':\n return {issues: githubSearchItems(data)};\n case 'list_pull_requests':\n return {pull_requests: data};\n case 'search_pull_requests':\n return {pull_requests: githubSearchItems(data)};\n case 'create_pull_request':\n case 'update_pull_request':\n return {pull_request: data};\n case 'merge_pull_request':\n return {merge: data};\n default:\n return isRecord(data) ? data : {result: data};\n }\n}\n\nfunction projectGithubArtifactDownloadOutput(\n response: GithubToolResponse | undefined,\n parameters: Record<string, unknown> | undefined,\n): Record<string, unknown> | undefined {\n if (response === undefined) return undefined;\n const downloadUrl = response.headers?.location;\n if (typeof downloadUrl !== 'string' || downloadUrl.length === 0) return undefined;\n\n const output: Record<string, unknown> = {\n archive_format: GITHUB_ARTIFACT_ARCHIVE_FORMAT,\n download_url: downloadUrl,\n };\n if (typeof parameters?.resource_id === 'string') output.artifact_id = parameters.resource_id;\n\n const contentType = response.headers?.['content-type'];\n if (typeof contentType === 'string') output.content_type = contentType;\n\n const contentLength = response.headers?.['content-length'];\n const sizeBytes = typeof contentLength === 'number' ? contentLength : Number(contentLength);\n if (Number.isSafeInteger(sizeBytes) && sizeBytes >= 0) output.size_bytes = sizeBytes;\n\n return output;\n}\n\nfunction githubSearchItems(data: unknown): unknown {\n return isRecord(data) ? data.items : data;\n}\n\nfunction githubToolError(message: string, code: GithubToolErrorCode): GithubToolCallResult {\n return {\n isError: true,\n content: [{type: 'text', text: message}],\n structuredContent: {code},\n };\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\nfunction validateGithubToolArguments(\n tool: AgentToolCatalogEntry<GithubAgentToolRequiredScope>,\n arguments_: Record<string, unknown>,\n): string | undefined {\n const required = Array.isArray(tool.inputSchema.required) ? tool.inputSchema.required : [];\n for (const name of required) {\n if (typeof name === 'string' && arguments_[name] === undefined) {\n return `Missing required parameter: ${name}`;\n }\n }\n\n const methodRequired = methodRequiredParameters(tool.inputSchema, arguments_);\n for (const name of methodRequired) {\n if (arguments_[name] === undefined) return `Missing required parameter: ${name}`;\n }\n\n const properties = tool.inputSchema.properties;\n if (typeof properties !== 'object' || properties === null || Array.isArray(properties)) {\n return undefined;\n }\n const propertySchemas = properties as Record<string, unknown>;\n for (const [name, value] of Object.entries(arguments_)) {\n const schema = propertySchemas[name];\n if (typeof schema !== 'object' || schema === null || Array.isArray(schema)) continue;\n const type = (schema as {type?: unknown}).type;\n if (type === 'integer' && (!Number.isInteger(value) || typeof value !== 'number')) {\n return `Parameter ${name} must be an integer`;\n }\n if (type === 'array' && !Array.isArray(value)) return `Parameter ${name} must be an array`;\n }\n return undefined;\n}\n\nfunction methodRequiredParameters(\n inputSchema: AgentToolCatalogEntry<GithubAgentToolRequiredScope>['inputSchema'],\n arguments_: Record<string, unknown>,\n): string[] {\n const method = arguments_.method;\n if (typeof method !== 'string' || !Array.isArray(inputSchema.oneOf)) return [];\n\n for (const candidate of inputSchema.oneOf) {\n if (!isRecord(candidate) || !isRecord(candidate.properties)) continue;\n const methodSchema = candidate.properties.method;\n if (!isRecord(methodSchema) || methodSchema.const !== method) continue;\n return Array.isArray(candidate.required)\n ? candidate.required.filter((name): name is string => typeof name === 'string')\n : [];\n }\n\n return [];\n}\n\nfunction hasGrantedPermissions(\n granted: Record<string, 'read' | 'write' | 'admin'>,\n tool: AgentToolCatalogEntry<GithubAgentToolRequiredScope>,\n call: AgentToolCallInput,\n): boolean {\n const method = typeof call.arguments.method === 'string' ? call.arguments.method : undefined;\n const required =\n tool.methods?.find((candidate) => candidate.id === method)?.requiredScope ?? tool.requiredScope;\n return required.every(({permission, access}) => {\n const actual = granted[permission];\n return actual === 'write' || actual === 'admin' || actual === access;\n });\n}\n"],"names":["Octokit","mapGithubError","createGithubInstallationTokenProvider","config","normalizedGithubApiBaseUrl","GithubIntegrationProviderError","githubAgentToolCatalog","githubAgentToolSelectionCatalog","buildGithubAgentToolSelectionCatalog","DEFAULT_JOB_LOG_TAIL_LINES","GITHUB_GRAPHQL_ROUTE","GITHUB_ARTIFACT_ARCHIVE_FORMAT","GITHUB_ARTIFACT_DOWNLOAD_ROUTE","GITHUB_ARTIFACT_DOWNLOAD_TIMEOUT_MS","GITHUB_APP_BOT_SUFFIX","PENDING_REVIEW_PAGE_SIZE","PENDING_REVIEW_MAX_PAGE_REQUESTS","PENDING_REVIEW_LOOKUP_TIMEOUT_MS","PENDING_REVIEW_PAGE_TIMEOUT_MS","PENDING_REVIEW_PAGE_PATTERN","NO_PENDING_REVIEW_MESSAGE","ADD_PENDING_REVIEW_COMMENT_MUTATION","GET_PULL_REQUEST_REVIEW_THREADS_QUERY","RESOLVE_PULL_REQUEST_REVIEW_THREAD_MUTATION","GithubAgentToolsProvider","options","tokenProvider","catalog","selectionCatalog","openSession","input","installation","getInstallationByConnectionId","connection","id","installationId","Number","isSafeInteger","tokenPromise","call","tool","tools","find","candidate","toolId","githubToolError","operation","resolveGithubOperation","undefined","validationError","validateGithubToolArguments","arguments","getInstallationAccessToken","token","hasGrantedPermissions","permissions","client","createClient","createOctokitClient","method","kind","data","executeGithubGraphqlOperation","parameters","githubToolResult","operationParameters","resolvePendingReviewParameters","response","request","route","octokit","auth","baseUrl","retry","enabled","abortController","AbortController","timeout","setTimeout","abort","redirect","parseSuccessResponseBody","signal","clearTimeout","graphql","query","variables","args","params","methods","some","githubOperationRoute","projectGithubOperationParameters","owner","repo","issue","pull","run","resource","repoPath","comment_id","reaction","body","addCommentToPendingReview","review","latestPendingReview","nodeId","pullRequestReviewId","path","subjectType","subject_type","line","side","start_line","startLine","start_side","startSide","pullNumber","pull_number","cursor","after","threadId","thread_id","content","headers","accept","isPendingReviewOperation","review_id","requiredIdentifier","lookupController","lookupTimeout","latestPendingReviewBeforeDeadline","lookupSignal","firstPage","requestPendingReviewPage","lastPage","pendingReviewLastPage","requests","malformed","page","Array","isArray","result","latestPendingReviewOnPage","pageController","abortPage","aborted","addEventListener","once","pageTimeout","per_page","removeEventListener","link","lastLink","split","part","includes","match","exec","NaN","parseInt","appLogin","githubAppBotLogin","toLowerCase","index","length","isRecord","state","userLogin","user","login","trim","node_id","reference","configuredUsername","GITHUB_APP_USERNAME","GITHUB_APP_SLUG","endsWith","structuredContent","projectGithubToolOutput","type","text","JSON","stringify","projectGithubArtifactDownloadOutput","issue_types","issues","githubSearchItems","pull_requests","pull_request","merge","downloadUrl","location","output","archive_format","download_url","resource_id","artifact_id","contentType","content_type","contentLength","sizeBytes","size_bytes","items","message","code","isError","value","arguments_","required","inputSchema","name","methodRequired","methodRequiredParameters","properties","propertySchemas","Object","entries","schema","isInteger","oneOf","methodSchema","const","filter","granted","requiredScope","every","permission","access","actual"],"mappings":"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"}
1
+ {"version":3,"sources":["../../src/core/agent-tools.ts"],"sourcesContent":["import type {\n AgentToolCallInput,\n AgentToolCatalogEntry,\n AgentToolSelectionCatalog,\n AgentToolSession,\n AgentToolsProvider,\n IntegrationConnection,\n OpenAgentToolsSessionInput,\n} from '@shipfox/api-integration-spi';\nimport {Octokit} from 'octokit';\nimport {mapGithubError} from '#api/client.js';\nimport {\n createGithubInstallationTokenProvider,\n type GithubInstallationTokenProvider,\n} from '#api/installation-token-provider.js';\nimport {normalizedGithubApiBaseUrl} from '#config.js';\nimport type {GithubInstallation} from '#db/installations.js';\nimport {githubAppBotLogin} from './bot-identity.js';\nimport {GithubIntegrationProviderError} from './errors.js';\nimport {\n type GithubAgentToolCatalogEntry,\n type GithubAgentToolId,\n type GithubAgentToolRequiredScope,\n githubAgentToolCatalog,\n githubAgentToolSelectionCatalog,\n} from './github-agent-tool-catalog.js';\n\nexport type {\n GithubAgentToolCatalogEntry,\n GithubAgentToolCategory,\n GithubAgentToolId,\n GithubAgentToolPermission,\n GithubAgentToolPermissionAccess,\n GithubAgentToolRequiredPermission,\n GithubAgentToolRequiredScope,\n GithubAgentToolSensitivity,\n} from './github-agent-tool-catalog.js';\nexport {\n buildGithubAgentToolSelectionCatalog,\n DEFAULT_JOB_LOG_TAIL_LINES,\n githubAgentToolCatalog,\n githubAgentToolSelectionCatalog,\n} from './github-agent-tool-catalog.js';\n\ntype GithubIntegrationConnection = IntegrationConnection<'github'>;\n\ntype GithubToolCallResult = {\n isError?: boolean | undefined;\n content: readonly {type: 'text'; text: string}[];\n structuredContent?: Record<string, unknown> | undefined;\n};\n\ntype GithubToolErrorCode =\n | 'invalid-request'\n | 'access-denied'\n | 'provider-rejected'\n | 'malformed-provider-response';\n\nconst GITHUB_GRAPHQL_ROUTE = 'POST /graphql';\nconst GITHUB_ARTIFACT_ARCHIVE_FORMAT = 'zip';\nconst GITHUB_ARTIFACT_DOWNLOAD_ROUTE = `GET /repos/{owner}/{repo}/actions/artifacts/{resource_id}/${GITHUB_ARTIFACT_ARCHIVE_FORMAT}`;\nconst GITHUB_ARTIFACT_DOWNLOAD_TIMEOUT_MS = 30_000;\nconst PENDING_REVIEW_PAGE_SIZE = 100;\nconst PENDING_REVIEW_MAX_PAGE_REQUESTS = 5;\nconst PENDING_REVIEW_LOOKUP_TIMEOUT_MS = 15_000;\nconst PENDING_REVIEW_PAGE_TIMEOUT_MS = 5_000;\nconst PENDING_REVIEW_PAGE_PATTERN = /[?&]page=(\\d+)/u;\nconst NO_PENDING_REVIEW_MESSAGE =\n 'No pending pull request review found for the authenticated GitHub user.';\n\nconst ADD_PENDING_REVIEW_COMMENT_MUTATION = `\n mutation AddCommentToPendingReview($input: AddPullRequestReviewThreadInput!) {\n addPullRequestReviewThread(input: $input) {\n thread {\n id\n }\n }\n }\n`;\n\nconst GET_PULL_REQUEST_REVIEW_THREADS_QUERY = `\n query GetPullRequestReviewThreads(\n $owner: String!\n $repo: String!\n $pullNumber: Int!\n $after: String\n ) {\n repository(owner: $owner, name: $repo) {\n pullRequest(number: $pullNumber) {\n reviewThreads(first: 100, after: $after) {\n nodes {\n id\n isResolved\n comments(first: 100) {\n nodes {\n id\n databaseId\n body\n author {\n login\n }\n path\n line\n side\n startLine\n startSide\n createdAt\n updatedAt\n url\n }\n pageInfo {\n hasNextPage\n endCursor\n }\n }\n }\n pageInfo {\n hasNextPage\n endCursor\n }\n }\n }\n }\n }\n`;\n\nconst RESOLVE_PULL_REQUEST_REVIEW_THREAD_MUTATION = `\n mutation ResolvePullRequestReviewThread($input: ResolveReviewThreadInput!) {\n resolveReviewThread(input: $input) {\n thread {\n id\n isResolved\n }\n }\n }\n`;\n\nexport class GithubAgentToolsProvider\n implements\n AgentToolsProvider<\n GithubIntegrationConnection,\n GithubAgentToolRequiredScope,\n unknown,\n GithubToolCallResult\n >\n{\n private readonly tokenProvider: GithubInstallationTokenProvider;\n\n constructor(private readonly options: GithubAgentToolsProviderOptions = {}) {\n this.tokenProvider = options.tokenProvider ?? createGithubInstallationTokenProvider();\n }\n\n catalog(): readonly GithubAgentToolCatalogEntry[] {\n return githubAgentToolCatalog;\n }\n\n selectionCatalog(): AgentToolSelectionCatalog {\n return githubAgentToolSelectionCatalog;\n }\n\n async openSession(\n input: OpenAgentToolsSessionInput<GithubIntegrationConnection, GithubAgentToolRequiredScope>,\n ): Promise<AgentToolSession<GithubToolCallResult>> {\n const installation = await this.options.getInstallationByConnectionId?.(input.connection.id);\n if (!installation) {\n throw new GithubIntegrationProviderError(\n 'installation-not-found',\n 'GitHub installation is not connected to this integration',\n );\n }\n const installationId = Number(installation.installationId);\n if (!Number.isSafeInteger(installationId) || installationId < 1) {\n throw new GithubIntegrationProviderError(\n 'malformed-provider-response',\n 'GitHub installation has an invalid installation ID',\n );\n }\n let tokenPromise:\n | ReturnType<GithubInstallationTokenProvider['getInstallationAccessToken']>\n | undefined;\n\n return {\n call: async (call) => {\n const tool = input.tools.find((candidate) => candidate.id === call.toolId);\n if (!tool) return githubToolError(`Unknown GitHub tool: ${call.toolId}`, 'invalid-request');\n const operation = resolveGithubOperation(tool, call);\n if (operation === undefined)\n return githubToolError('Unknown GitHub tool operation', 'invalid-request');\n const validationError = validateGithubToolArguments(tool, call.arguments);\n if (validationError) return githubToolError(validationError, 'invalid-request');\n tokenPromise ??= this.tokenProvider.getInstallationAccessToken(installationId);\n const token = await tokenPromise;\n if (!hasGrantedPermissions(token.permissions ?? {}, tool, call)) {\n return githubToolError(\n 'GitHub installation token is missing permission for this operation',\n 'access-denied',\n );\n }\n const client = (this.options.createClient ?? createOctokitClient)(token.token);\n const method =\n typeof call.arguments.method === 'string' ? call.arguments.method : undefined;\n\n if (operation.kind === 'graphql') {\n const data = await mapGithubError(() =>\n executeGithubGraphqlOperation(\n client,\n tool.id as GithubAgentToolId,\n method,\n operation.parameters,\n ),\n );\n if (data === undefined && tool.id === 'add_comment_to_pending_review') {\n return githubToolError(NO_PENDING_REVIEW_MESSAGE, 'provider-rejected');\n }\n return githubToolResult(tool.id as GithubAgentToolId, data);\n }\n\n const operationParameters = await mapGithubError(() =>\n resolvePendingReviewParameters(\n client,\n operation.parameters,\n tool.id as GithubAgentToolId,\n method,\n ),\n );\n if (operationParameters === undefined) {\n return githubToolError(NO_PENDING_REVIEW_MESSAGE, 'provider-rejected');\n }\n const response = await mapGithubError(() =>\n client.request(operation.route, operationParameters),\n );\n return githubToolResult(\n tool.id as GithubAgentToolId,\n response.data,\n response,\n operationParameters,\n operation.route,\n );\n },\n };\n }\n}\n\nexport interface GithubAgentToolsProviderOptions {\n getInstallationByConnectionId?:\n | ((connectionId: string) => Promise<GithubInstallation | undefined>)\n | undefined;\n tokenProvider?: GithubInstallationTokenProvider | undefined;\n createClient?: GithubToolClientFactory | undefined;\n}\n\nexport interface GithubToolResponse {\n data: unknown;\n headers?: Record<string, string | number | undefined> | undefined;\n status?: number | undefined;\n url?: string | undefined;\n}\n\nexport interface GithubToolClient {\n request(route: string, parameters: Record<string, unknown>): Promise<GithubToolResponse>;\n graphql?: ((query: string, variables: Record<string, unknown>) => Promise<unknown>) | undefined;\n}\n\nexport type GithubToolClientFactory = (token: string) => GithubToolClient;\n\ninterface GithubToolOperation {\n route: string;\n parameters: Record<string, unknown>;\n kind: 'rest' | 'graphql';\n}\n\nfunction createOctokitClient(token: string): GithubToolClient {\n const octokit = new Octokit({\n auth: token,\n baseUrl: normalizedGithubApiBaseUrl(),\n retry: {enabled: false},\n });\n return {\n request: async (route, parameters) => {\n if (route !== GITHUB_ARTIFACT_DOWNLOAD_ROUTE) {\n return await octokit.request(route, parameters);\n }\n\n const abortController = new AbortController();\n const timeout = setTimeout(\n () => abortController.abort(),\n GITHUB_ARTIFACT_DOWNLOAD_TIMEOUT_MS,\n );\n try {\n return await octokit.request(route, {\n ...parameters,\n request: {\n redirect: 'manual',\n parseSuccessResponseBody: false,\n signal: abortController.signal,\n },\n });\n } finally {\n clearTimeout(timeout);\n }\n },\n graphql: async (query, variables) => await octokit.graphql(query, variables),\n };\n}\n\nfunction resolveGithubOperation(\n tool: AgentToolCatalogEntry<GithubAgentToolRequiredScope>,\n call: AgentToolCallInput,\n): GithubToolOperation | undefined {\n const args = call.arguments;\n const method = typeof args.method === 'string' ? args.method : undefined;\n const params = {...args};\n delete params.method;\n\n if (tool.methods && !tool.methods.some((candidate) => candidate.id === method)) return undefined;\n\n const toolId = tool.id as GithubAgentToolId;\n const route = githubOperationRoute(toolId, method, params);\n return route === undefined\n ? undefined\n : {\n route,\n parameters: projectGithubOperationParameters(toolId, method, params),\n kind: route === GITHUB_GRAPHQL_ROUTE ? 'graphql' : 'rest',\n };\n}\n\nexport function githubOperationRoute(\n toolId: GithubAgentToolId,\n method: string | undefined,\n args: Record<string, unknown>,\n): string | undefined {\n const owner = '{owner}';\n const repo = '{repo}';\n const issue = '{issue_number}';\n const pull = '{pull_number}';\n const run = '{run_id}';\n const resource = '{resource_id}';\n const repoPath = `/repos/${owner}/${repo}`;\n\n switch (`${toolId}.${method ?? ''}`) {\n case 'issue_read.get':\n return `GET ${repoPath}/issues/${issue}`;\n case 'issue_read.get_comments':\n return `GET ${repoPath}/issues/${issue}/comments`;\n case 'issue_read.get_sub_issues':\n return `GET ${repoPath}/issues/${issue}/sub_issues`;\n case 'issue_read.get_parent':\n return `GET ${repoPath}/issues/${issue}/parent`;\n case 'issue_read.get_labels':\n return `GET ${repoPath}/issues/${issue}/labels`;\n case 'list_issue_types.':\n return args.repo === undefined\n ? 'GET /orgs/{owner}/issue-types'\n : `GET ${repoPath}/issue-types`;\n case 'list_issues.':\n return `GET ${repoPath}/issues`;\n case 'search_issues.':\n return 'GET /search/issues';\n case 'add_issue_comment.':\n if (args.comment_id !== undefined)\n return `POST ${repoPath}/issues/comments/{comment_id}/reactions`;\n return args.reaction !== undefined && args.body === undefined\n ? `POST ${repoPath}/issues/${issue}/reactions`\n : `POST ${repoPath}/issues/${issue}/comments`;\n case 'issue_write.create':\n return `POST ${repoPath}/issues`;\n case 'issue_write.update':\n return `PATCH ${repoPath}/issues/${issue}`;\n case 'sub_issue_write.add':\n return `POST ${repoPath}/issues/${issue}/sub_issues`;\n case 'sub_issue_write.remove':\n return `DELETE ${repoPath}/issues/${issue}/sub_issues/{sub_issue_id}`;\n case 'sub_issue_write.reprioritize':\n return `PATCH ${repoPath}/issues/${issue}/sub_issues/priority`;\n case 'pull_request_read.get':\n return `GET ${repoPath}/pulls/${pull}`;\n case 'pull_request_read.get_diff':\n return `GET ${repoPath}/pulls/${pull}`;\n case 'pull_request_read.get_status':\n return `GET ${repoPath}/commits/{ref}/status`;\n case 'pull_request_read.get_files':\n return `GET ${repoPath}/pulls/${pull}/files`;\n case 'pull_request_read.get_commits':\n return `GET ${repoPath}/pulls/${pull}/commits`;\n case 'pull_request_read.get_review_comments':\n return `GET ${repoPath}/pulls/${pull}/comments`;\n case 'pull_request_read.get_review_threads':\n return GITHUB_GRAPHQL_ROUTE;\n case 'pull_request_read.get_reviews':\n return `GET ${repoPath}/pulls/${pull}/reviews`;\n case 'pull_request_read.get_comments':\n return `GET ${repoPath}/issues/${pull}/comments`;\n case 'pull_request_read.get_check_runs':\n return `GET ${repoPath}/commits/{ref}/check-runs`;\n case 'list_pull_requests.':\n return `GET ${repoPath}/pulls`;\n case 'search_pull_requests.':\n return 'GET /search/issues';\n case 'create_pull_request.':\n return `POST ${repoPath}/pulls`;\n case 'update_pull_request.':\n return `PATCH ${repoPath}/pulls/${pull}`;\n case 'add_reply_to_pull_request_comment.':\n return args.reaction !== undefined && args.body === undefined\n ? `POST ${repoPath}/pulls/comments/{comment_id}/reactions`\n : `POST ${repoPath}/pulls/${pull}/comments/{comment_id}/replies`;\n case 'merge_pull_request.':\n return `PUT ${repoPath}/pulls/${pull}/merge`;\n case 'update_pull_request_branch.':\n return `PUT ${repoPath}/pulls/${pull}/update-branch`;\n case 'pull_request_review_write.create':\n return `POST ${repoPath}/pulls/${pull}/reviews`;\n case 'pull_request_review_write.submit_pending':\n return `POST ${repoPath}/pulls/${pull}/reviews/{review_id}/events`;\n case 'pull_request_review_write.delete_pending':\n return `DELETE ${repoPath}/pulls/${pull}/reviews/{review_id}`;\n case 'pull_request_review_thread_write.resolve':\n return GITHUB_GRAPHQL_ROUTE;\n case 'add_comment_to_pending_review.':\n return GITHUB_GRAPHQL_ROUTE;\n case 'actions_list.list_workflows':\n return `GET ${repoPath}/actions/workflows`;\n case 'actions_list.list_workflow_runs':\n return `GET ${repoPath}/actions/workflows/${resource}/runs`;\n case 'actions_list.list_workflow_jobs':\n return `GET ${repoPath}/actions/runs/${resource}/jobs`;\n case 'actions_list.list_workflow_run_artifacts':\n return `GET ${repoPath}/actions/runs/${resource}/artifacts`;\n case 'actions_get.get_workflow':\n return `GET ${repoPath}/actions/workflows/${resource}`;\n case 'actions_get.get_workflow_run':\n return `GET ${repoPath}/actions/runs/${resource}`;\n case 'actions_get.get_workflow_job':\n return `GET ${repoPath}/actions/jobs/${resource}`;\n case 'actions_get.download_workflow_run_artifact':\n return GITHUB_ARTIFACT_DOWNLOAD_ROUTE;\n case 'actions_get.get_workflow_run_usage':\n return `GET ${repoPath}/actions/runs/${resource}/timing`;\n case 'actions_get.get_workflow_run_logs_url':\n return `GET ${repoPath}/actions/runs/${resource}/logs`;\n case 'actions_run_trigger.run_workflow':\n return `POST ${repoPath}/actions/workflows/{workflow_id}/dispatches`;\n case 'actions_run_trigger.rerun_workflow_run':\n return `POST ${repoPath}/actions/runs/${run}/rerun`;\n case 'actions_run_trigger.rerun_failed_jobs':\n return `POST ${repoPath}/actions/runs/${run}/rerun-failed-jobs`;\n case 'actions_run_trigger.cancel_workflow_run':\n return `POST ${repoPath}/actions/runs/${run}/cancel`;\n case 'actions_run_trigger.delete_workflow_run_logs':\n return `DELETE ${repoPath}/actions/runs/${run}/logs`;\n case 'get_job_logs.':\n return `GET ${repoPath}/actions/jobs/{job_id}/logs`;\n default:\n return undefined;\n }\n}\n\nasync function addCommentToPendingReview(\n client: GithubToolClient,\n args: Record<string, unknown>,\n): Promise<unknown | undefined> {\n if (client.graphql === undefined) {\n throw new GithubIntegrationProviderError(\n 'malformed-provider-response',\n 'GitHub client does not support GraphQL operations',\n );\n }\n\n const review = await latestPendingReview(client, args, 'nodeId');\n if (review === undefined) return undefined;\n if (review.nodeId === undefined) {\n throw new GithubIntegrationProviderError(\n 'malformed-provider-response',\n 'GitHub pending pull request review did not include a node ID',\n );\n }\n\n const input: Record<string, unknown> = {\n pullRequestReviewId: review.nodeId,\n path: args.path,\n body: args.body,\n subjectType: args.subject_type,\n };\n if (args.line !== undefined) input.line = args.line;\n if (args.side !== undefined) input.side = args.side;\n if (args.start_line !== undefined) input.startLine = args.start_line;\n if (args.start_side !== undefined) input.startSide = args.start_side;\n\n return await client.graphql(ADD_PENDING_REVIEW_COMMENT_MUTATION, {input});\n}\n\nasync function executeGithubGraphqlOperation(\n client: GithubToolClient,\n toolId: GithubAgentToolId,\n method: string | undefined,\n parameters: Record<string, unknown>,\n): Promise<unknown | undefined> {\n if (client.graphql === undefined) {\n throw new GithubIntegrationProviderError(\n 'malformed-provider-response',\n 'GitHub client does not support GraphQL operations',\n );\n }\n\n switch (`${toolId}.${method ?? ''}`) {\n case 'pull_request_read.get_review_threads': {\n const variables: Record<string, unknown> = {\n owner: parameters.owner,\n repo: parameters.repo,\n pullNumber: parameters.pull_number,\n };\n if (typeof parameters.cursor === 'string') variables.after = parameters.cursor;\n return await client.graphql(GET_PULL_REQUEST_REVIEW_THREADS_QUERY, variables);\n }\n case 'pull_request_review_thread_write.resolve':\n return await client.graphql(RESOLVE_PULL_REQUEST_REVIEW_THREAD_MUTATION, {\n input: {threadId: parameters.thread_id},\n });\n case 'add_comment_to_pending_review.':\n return await addCommentToPendingReview(client, parameters);\n default:\n throw new GithubIntegrationProviderError(\n 'malformed-provider-response',\n 'GitHub operation does not support GraphQL operations',\n );\n }\n}\n\nexport function projectGithubOperationParameters(\n toolId: GithubAgentToolId,\n method: string | undefined,\n args: Record<string, unknown>,\n): Record<string, unknown> {\n const parameters = {...args};\n if (toolId === 'add_issue_comment' && parameters.reaction !== undefined) {\n parameters.content = parameters.reaction;\n delete parameters.reaction;\n if (parameters.body === undefined) delete parameters.body;\n }\n if (toolId === 'pull_request_read' && method === 'get_diff') {\n parameters.headers = {accept: 'application/vnd.github.diff'};\n }\n return parameters;\n}\n\nasync function resolvePendingReviewParameters(\n client: GithubToolClient,\n parameters: Record<string, unknown>,\n toolId: GithubAgentToolId,\n method: string | undefined,\n): Promise<Record<string, unknown> | undefined> {\n if (!isPendingReviewOperation(toolId, method)) return parameters;\n\n const review = await latestPendingReview(client, parameters, 'id');\n if (review === undefined) return undefined;\n if (review.id === undefined) {\n throw new GithubIntegrationProviderError(\n 'malformed-provider-response',\n 'GitHub pending pull request review did not include a numeric ID',\n );\n }\n return {...parameters, review_id: review.id};\n}\n\nfunction isPendingReviewOperation(toolId: GithubAgentToolId, method: string | undefined): boolean {\n return (\n toolId === 'pull_request_review_write' &&\n (method === 'submit_pending' || method === 'delete_pending')\n );\n}\n\ninterface PendingReviewReference {\n id?: number | undefined;\n nodeId?: string | undefined;\n}\n\ntype PendingReviewIdentifier = keyof PendingReviewReference;\n\ninterface PendingReviewPageResult {\n malformed: boolean;\n review?: PendingReviewReference | undefined;\n}\n\nasync function latestPendingReview(\n client: GithubToolClient,\n parameters: Record<string, unknown>,\n requiredIdentifier: PendingReviewIdentifier,\n): Promise<PendingReviewReference | undefined> {\n const lookupController = new AbortController();\n const lookupTimeout = setTimeout(\n () => lookupController.abort(),\n PENDING_REVIEW_LOOKUP_TIMEOUT_MS,\n );\n\n try {\n return await latestPendingReviewBeforeDeadline(\n client,\n parameters,\n requiredIdentifier,\n lookupController.signal,\n );\n } finally {\n clearTimeout(lookupTimeout);\n }\n}\n\nasync function latestPendingReviewBeforeDeadline(\n client: GithubToolClient,\n parameters: Record<string, unknown>,\n requiredIdentifier: PendingReviewIdentifier,\n lookupSignal: AbortSignal,\n): Promise<PendingReviewReference | undefined> {\n const firstPage = await requestPendingReviewPage(client, parameters, 1, lookupSignal);\n const lastPage = pendingReviewLastPage(firstPage.headers);\n let requests = 1;\n let malformed = false;\n\n for (let page = lastPage; page >= 1; page -= 1) {\n let response = firstPage;\n if (page !== 1) {\n if (requests >= PENDING_REVIEW_MAX_PAGE_REQUESTS) {\n throw new GithubIntegrationProviderError(\n 'content-too-large',\n 'GitHub pull request review history exceeded the pending review lookup limit',\n );\n }\n response = await requestPendingReviewPage(client, parameters, page, lookupSignal);\n requests += 1;\n }\n if (!Array.isArray(response.data)) {\n throw new GithubIntegrationProviderError(\n 'malformed-provider-response',\n 'GitHub pull request review list response was malformed',\n );\n }\n\n const result = latestPendingReviewOnPage(response.data, requiredIdentifier);\n if (result.review !== undefined) return result.review;\n malformed ||= result.malformed;\n }\n\n if (malformed) {\n throw new GithubIntegrationProviderError(\n 'malformed-provider-response',\n requiredIdentifier === 'nodeId'\n ? 'GitHub pending pull request review did not include a node ID'\n : 'GitHub pending pull request review did not include a numeric ID',\n );\n }\n return undefined;\n}\n\nasync function requestPendingReviewPage(\n client: GithubToolClient,\n parameters: Record<string, unknown>,\n page: number,\n lookupSignal: AbortSignal,\n): Promise<GithubToolResponse> {\n const pageController = new AbortController();\n const abortPage = () => pageController.abort();\n if (lookupSignal.aborted) abortPage();\n else lookupSignal.addEventListener('abort', abortPage, {once: true});\n const pageTimeout = setTimeout(abortPage, PENDING_REVIEW_PAGE_TIMEOUT_MS);\n\n try {\n return await client.request('GET /repos/{owner}/{repo}/pulls/{pull_number}/reviews', {\n owner: parameters.owner,\n repo: parameters.repo,\n pull_number: parameters.pull_number,\n per_page: PENDING_REVIEW_PAGE_SIZE,\n page,\n request: {signal: pageController.signal},\n });\n } finally {\n clearTimeout(pageTimeout);\n lookupSignal.removeEventListener('abort', abortPage);\n }\n}\n\nfunction pendingReviewLastPage(headers: GithubToolResponse['headers']): number {\n const link = headers?.link;\n if (typeof link !== 'string') return 1;\n const lastLink = link.split(',').find((part) => part.includes('rel=\"last\"'));\n if (lastLink === undefined) return 1;\n const match = PENDING_REVIEW_PAGE_PATTERN.exec(lastLink);\n const page = match?.[1] === undefined ? Number.NaN : Number.parseInt(match[1], 10);\n if (!Number.isSafeInteger(page) || page < 1) {\n throw new GithubIntegrationProviderError(\n 'malformed-provider-response',\n 'GitHub pull request review pagination response was malformed',\n );\n }\n return page;\n}\n\nfunction latestPendingReviewOnPage(\n data: readonly unknown[],\n requiredIdentifier: PendingReviewIdentifier,\n): PendingReviewPageResult {\n let malformed = false;\n const appLogin = githubAppBotLogin().toLowerCase();\n for (let index = data.length - 1; index >= 0; index -= 1) {\n const review = data[index];\n if (!isRecord(review) || review.state !== 'PENDING') continue;\n const userLogin = isRecord(review.user) ? review.user.login : undefined;\n if (typeof userLogin !== 'string' || userLogin.trim().length === 0) {\n malformed = true;\n continue;\n }\n if (userLogin.trim().toLowerCase() !== appLogin) continue;\n const id =\n typeof review.id === 'number' && Number.isSafeInteger(review.id) && review.id > 0\n ? review.id\n : undefined;\n const nodeId =\n typeof review.node_id === 'string' && review.node_id.trim().length > 0\n ? review.node_id.trim()\n : undefined;\n const reference = {id, nodeId};\n if (reference[requiredIdentifier] !== undefined) return {malformed, review: reference};\n malformed = true;\n }\n\n return {malformed};\n}\n\nfunction githubToolResult(\n toolId: GithubAgentToolId,\n data: unknown,\n response?: GithubToolResponse,\n parameters?: Record<string, unknown>,\n route?: string,\n): GithubToolCallResult {\n const structuredContent = projectGithubToolOutput(toolId, data, response, parameters, route);\n if (structuredContent === undefined) {\n return githubToolError(\n 'GitHub artifact download did not return a download URL',\n 'malformed-provider-response',\n );\n }\n return {\n content: [{type: 'text', text: JSON.stringify(structuredContent)}],\n structuredContent,\n };\n}\n\nfunction projectGithubToolOutput(\n toolId: GithubAgentToolId,\n data: unknown,\n response?: GithubToolResponse,\n parameters?: Record<string, unknown>,\n route?: string,\n): Record<string, unknown> | undefined {\n if (route === GITHUB_ARTIFACT_DOWNLOAD_ROUTE) {\n return projectGithubArtifactDownloadOutput(response, parameters);\n }\n\n switch (toolId) {\n case 'list_issue_types':\n return {issue_types: data};\n case 'list_issues':\n return {issues: data};\n case 'search_issues':\n return {issues: githubSearchItems(data)};\n case 'list_pull_requests':\n return {pull_requests: data};\n case 'search_pull_requests':\n return {pull_requests: githubSearchItems(data)};\n case 'create_pull_request':\n case 'update_pull_request':\n return {pull_request: data};\n case 'merge_pull_request':\n return {merge: data};\n default:\n return isRecord(data) ? data : {result: data};\n }\n}\n\nfunction projectGithubArtifactDownloadOutput(\n response: GithubToolResponse | undefined,\n parameters: Record<string, unknown> | undefined,\n): Record<string, unknown> | undefined {\n if (response === undefined) return undefined;\n const downloadUrl = response.headers?.location;\n if (typeof downloadUrl !== 'string' || downloadUrl.length === 0) return undefined;\n\n const output: Record<string, unknown> = {\n archive_format: GITHUB_ARTIFACT_ARCHIVE_FORMAT,\n download_url: downloadUrl,\n };\n if (typeof parameters?.resource_id === 'string') output.artifact_id = parameters.resource_id;\n\n const contentType = response.headers?.['content-type'];\n if (typeof contentType === 'string') output.content_type = contentType;\n\n const contentLength = response.headers?.['content-length'];\n const sizeBytes = typeof contentLength === 'number' ? contentLength : Number(contentLength);\n if (Number.isSafeInteger(sizeBytes) && sizeBytes >= 0) output.size_bytes = sizeBytes;\n\n return output;\n}\n\nfunction githubSearchItems(data: unknown): unknown {\n return isRecord(data) ? data.items : data;\n}\n\nfunction githubToolError(message: string, code: GithubToolErrorCode): GithubToolCallResult {\n return {\n isError: true,\n content: [{type: 'text', text: message}],\n structuredContent: {code},\n };\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\nfunction validateGithubToolArguments(\n tool: AgentToolCatalogEntry<GithubAgentToolRequiredScope>,\n arguments_: Record<string, unknown>,\n): string | undefined {\n const required = Array.isArray(tool.inputSchema.required) ? tool.inputSchema.required : [];\n for (const name of required) {\n if (typeof name === 'string' && arguments_[name] === undefined) {\n return `Missing required parameter: ${name}`;\n }\n }\n\n const methodRequired = methodRequiredParameters(tool.inputSchema, arguments_);\n for (const name of methodRequired) {\n if (arguments_[name] === undefined) return `Missing required parameter: ${name}`;\n }\n\n const properties = tool.inputSchema.properties;\n if (typeof properties !== 'object' || properties === null || Array.isArray(properties)) {\n return undefined;\n }\n const propertySchemas = properties as Record<string, unknown>;\n for (const [name, value] of Object.entries(arguments_)) {\n const schema = propertySchemas[name];\n if (typeof schema !== 'object' || schema === null || Array.isArray(schema)) continue;\n const type = (schema as {type?: unknown}).type;\n if (type === 'integer' && (!Number.isInteger(value) || typeof value !== 'number')) {\n return `Parameter ${name} must be an integer`;\n }\n if (type === 'array' && !Array.isArray(value)) return `Parameter ${name} must be an array`;\n }\n return undefined;\n}\n\nfunction methodRequiredParameters(\n inputSchema: AgentToolCatalogEntry<GithubAgentToolRequiredScope>['inputSchema'],\n arguments_: Record<string, unknown>,\n): string[] {\n const method = arguments_.method;\n if (typeof method !== 'string' || !Array.isArray(inputSchema.oneOf)) return [];\n\n for (const candidate of inputSchema.oneOf) {\n if (!isRecord(candidate) || !isRecord(candidate.properties)) continue;\n const methodSchema = candidate.properties.method;\n if (!isRecord(methodSchema) || methodSchema.const !== method) continue;\n return Array.isArray(candidate.required)\n ? candidate.required.filter((name): name is string => typeof name === 'string')\n : [];\n }\n\n return [];\n}\n\nfunction hasGrantedPermissions(\n granted: Record<string, 'read' | 'write' | 'admin'>,\n tool: AgentToolCatalogEntry<GithubAgentToolRequiredScope>,\n call: AgentToolCallInput,\n): boolean {\n const method = typeof call.arguments.method === 'string' ? call.arguments.method : undefined;\n const required =\n tool.methods?.find((candidate) => candidate.id === method)?.requiredScope ?? tool.requiredScope;\n return required.every(({permission, access}) => {\n const actual = granted[permission];\n return actual === 'write' || actual === 'admin' || actual === access;\n });\n}\n"],"names":["Octokit","mapGithubError","createGithubInstallationTokenProvider","normalizedGithubApiBaseUrl","githubAppBotLogin","GithubIntegrationProviderError","githubAgentToolCatalog","githubAgentToolSelectionCatalog","buildGithubAgentToolSelectionCatalog","DEFAULT_JOB_LOG_TAIL_LINES","GITHUB_GRAPHQL_ROUTE","GITHUB_ARTIFACT_ARCHIVE_FORMAT","GITHUB_ARTIFACT_DOWNLOAD_ROUTE","GITHUB_ARTIFACT_DOWNLOAD_TIMEOUT_MS","PENDING_REVIEW_PAGE_SIZE","PENDING_REVIEW_MAX_PAGE_REQUESTS","PENDING_REVIEW_LOOKUP_TIMEOUT_MS","PENDING_REVIEW_PAGE_TIMEOUT_MS","PENDING_REVIEW_PAGE_PATTERN","NO_PENDING_REVIEW_MESSAGE","ADD_PENDING_REVIEW_COMMENT_MUTATION","GET_PULL_REQUEST_REVIEW_THREADS_QUERY","RESOLVE_PULL_REQUEST_REVIEW_THREAD_MUTATION","GithubAgentToolsProvider","options","tokenProvider","catalog","selectionCatalog","openSession","input","installation","getInstallationByConnectionId","connection","id","installationId","Number","isSafeInteger","tokenPromise","call","tool","tools","find","candidate","toolId","githubToolError","operation","resolveGithubOperation","undefined","validationError","validateGithubToolArguments","arguments","getInstallationAccessToken","token","hasGrantedPermissions","permissions","client","createClient","createOctokitClient","method","kind","data","executeGithubGraphqlOperation","parameters","githubToolResult","operationParameters","resolvePendingReviewParameters","response","request","route","octokit","auth","baseUrl","retry","enabled","abortController","AbortController","timeout","setTimeout","abort","redirect","parseSuccessResponseBody","signal","clearTimeout","graphql","query","variables","args","params","methods","some","githubOperationRoute","projectGithubOperationParameters","owner","repo","issue","pull","run","resource","repoPath","comment_id","reaction","body","addCommentToPendingReview","review","latestPendingReview","nodeId","pullRequestReviewId","path","subjectType","subject_type","line","side","start_line","startLine","start_side","startSide","pullNumber","pull_number","cursor","after","threadId","thread_id","content","headers","accept","isPendingReviewOperation","review_id","requiredIdentifier","lookupController","lookupTimeout","latestPendingReviewBeforeDeadline","lookupSignal","firstPage","requestPendingReviewPage","lastPage","pendingReviewLastPage","requests","malformed","page","Array","isArray","result","latestPendingReviewOnPage","pageController","abortPage","aborted","addEventListener","once","pageTimeout","per_page","removeEventListener","link","lastLink","split","part","includes","match","exec","NaN","parseInt","appLogin","toLowerCase","index","length","isRecord","state","userLogin","user","login","trim","node_id","reference","structuredContent","projectGithubToolOutput","type","text","JSON","stringify","projectGithubArtifactDownloadOutput","issue_types","issues","githubSearchItems","pull_requests","pull_request","merge","downloadUrl","location","output","archive_format","download_url","resource_id","artifact_id","contentType","content_type","contentLength","sizeBytes","size_bytes","items","message","code","isError","value","arguments_","required","inputSchema","name","methodRequired","methodRequiredParameters","properties","propertySchemas","Object","entries","schema","isInteger","oneOf","methodSchema","const","filter","granted","requiredScope","every","permission","access","actual"],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ export declare function githubBotLogin(username: string): string;
2
+ export declare function configuredGithubAppBotLogin(): string | undefined;
3
+ export declare function githubAppBotLogin(): string;
4
+ //# sourceMappingURL=bot-identity.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bot-identity.d.ts","sourceRoot":"","sources":["../../src/core/bot-identity.ts"],"names":[],"mappings":"AAIA,wBAAgB,cAAc,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAKvD;AAED,wBAAgB,2BAA2B,IAAI,MAAM,GAAG,SAAS,CAGhE;AAED,wBAAgB,iBAAiB,IAAI,MAAM,CAE1C"}
@@ -0,0 +1,15 @@
1
+ import { config } from '#config.js';
2
+ const GITHUB_APP_BOT_SUFFIX = '[bot]';
3
+ export function githubBotLogin(username) {
4
+ const normalized = username.trim();
5
+ return normalized.toLowerCase().endsWith(GITHUB_APP_BOT_SUFFIX) ? normalized : `${normalized}${GITHUB_APP_BOT_SUFFIX}`;
6
+ }
7
+ export function configuredGithubAppBotLogin() {
8
+ const configuredUsername = config.GITHUB_APP_USERNAME?.trim();
9
+ return configuredUsername ? githubBotLogin(configuredUsername) : undefined;
10
+ }
11
+ export function githubAppBotLogin() {
12
+ return configuredGithubAppBotLogin() ?? githubBotLogin(config.GITHUB_APP_SLUG);
13
+ }
14
+
15
+ //# sourceMappingURL=bot-identity.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/core/bot-identity.ts"],"sourcesContent":["import {config} from '#config.js';\n\nconst GITHUB_APP_BOT_SUFFIX = '[bot]';\n\nexport function githubBotLogin(username: string): string {\n const normalized = username.trim();\n return normalized.toLowerCase().endsWith(GITHUB_APP_BOT_SUFFIX)\n ? normalized\n : `${normalized}${GITHUB_APP_BOT_SUFFIX}`;\n}\n\nexport function configuredGithubAppBotLogin(): string | undefined {\n const configuredUsername = config.GITHUB_APP_USERNAME?.trim();\n return configuredUsername ? githubBotLogin(configuredUsername) : undefined;\n}\n\nexport function githubAppBotLogin(): string {\n return configuredGithubAppBotLogin() ?? githubBotLogin(config.GITHUB_APP_SLUG);\n}\n"],"names":["config","GITHUB_APP_BOT_SUFFIX","githubBotLogin","username","normalized","trim","toLowerCase","endsWith","configuredGithubAppBotLogin","configuredUsername","GITHUB_APP_USERNAME","undefined","githubAppBotLogin","GITHUB_APP_SLUG"],"mappings":"AAAA,SAAQA,MAAM,QAAO,aAAa;AAElC,MAAMC,wBAAwB;AAE9B,OAAO,SAASC,eAAeC,QAAgB;IAC7C,MAAMC,aAAaD,SAASE,IAAI;IAChC,OAAOD,WAAWE,WAAW,GAAGC,QAAQ,CAACN,yBACrCG,aACA,GAAGA,aAAaH,uBAAuB;AAC7C;AAEA,OAAO,SAASO;IACd,MAAMC,qBAAqBT,OAAOU,mBAAmB,EAAEL;IACvD,OAAOI,qBAAqBP,eAAeO,sBAAsBE;AACnE;AAEA,OAAO,SAASC;IACd,OAAOJ,iCAAiCN,eAAeF,OAAOa,eAAe;AAC/E"}
@@ -1,14 +1,16 @@
1
- import { type CheckoutSpec, type CreateCheckoutSpecInput, type FetchFileInput, type FilePage, type FileSnapshot, type IntegrationConnection, type ListFilesInput, type ListRepositoriesInput, type RepositoryPage, type RepositorySnapshot, type ResolveRepositoryInput, type SourceControlProvider, type TriggerReference } from '@shipfox/api-integration-spi';
1
+ import { type CheckoutSpec, type CreateCheckoutSpecInput, type FetchFileInput, type FilePage, type FileSnapshot, type IntegrationConnection, type ListFilesInput, type ListRepositoriesInput, type RepositoryPage, type RepositorySnapshot, type ResolvedRef, type ResolveRefInput, type ResolveRepositoryInput, type SourceControlProvider, type TriggerReference } from '@shipfox/api-integration-spi';
2
2
  import type { GithubApiClient } from '#api/client.js';
3
3
  type GithubIntegrationConnection = IntegrationConnection<'github'>;
4
4
  export declare class GithubSourceControlProvider implements SourceControlProvider<GithubIntegrationConnection> {
5
5
  private readonly github;
6
- constructor(github: GithubApiClient);
6
+ private readonly appBotLogin;
7
+ constructor(github: GithubApiClient, appBotLogin?: () => string | undefined);
7
8
  listRepositories(input: ListRepositoriesInput<GithubIntegrationConnection>): Promise<RepositoryPage>;
8
9
  resolveRepository(input: ResolveRepositoryInput<GithubIntegrationConnection>): Promise<RepositorySnapshot>;
9
10
  listFiles(input: ListFilesInput<GithubIntegrationConnection>): Promise<FilePage>;
10
11
  fetchFile(input: FetchFileInput<GithubIntegrationConnection>): Promise<FileSnapshot>;
11
12
  resolveTriggerReference(payload: unknown): TriggerReference | null;
13
+ resolveRef(input: ResolveRefInput<GithubIntegrationConnection>): Promise<ResolvedRef>;
12
14
  createCheckoutSpec(input: CreateCheckoutSpecInput<GithubIntegrationConnection>): Promise<CheckoutSpec>;
13
15
  private installationId;
14
16
  }
@@ -1 +1 @@
1
- {"version":3,"file":"source-control.d.ts","sourceRoot":"","sources":["../../src/core/source-control.ts"],"names":[],"mappings":"AACA,OAAO,EAGL,KAAK,YAAY,EACjB,KAAK,uBAAuB,EAC5B,KAAK,cAAc,EACnB,KAAK,QAAQ,EACb,KAAK,YAAY,EACjB,KAAK,qBAAqB,EAI1B,KAAK,cAAc,EACnB,KAAK,qBAAqB,EAK1B,KAAK,cAAc,EACnB,KAAK,kBAAkB,EAEvB,KAAK,sBAAsB,EAC3B,KAAK,qBAAqB,EAC1B,KAAK,gBAAgB,EACtB,MAAM,8BAA8B,CAAC;AACtC,OAAO,KAAK,EAAC,eAAe,EAAmB,MAAM,gBAAgB,CAAC;AAKtE,KAAK,2BAA2B,GAAG,qBAAqB,CAAC,QAAQ,CAAC,CAAC;AAOnE,qBAAa,2BACX,YAAW,qBAAqB,CAAC,2BAA2B,CAAC;IAEjD,OAAO,CAAC,QAAQ,CAAC,MAAM;IAAnC,YAA6B,MAAM,EAAE,eAAe,EAAI;IAElD,gBAAgB,CACpB,KAAK,EAAE,qBAAqB,CAAC,2BAA2B,CAAC,GACxD,OAAO,CAAC,cAAc,CAAC,CA+CzB;IAEK,iBAAiB,CACrB,KAAK,EAAE,sBAAsB,CAAC,2BAA2B,CAAC,GACzD,OAAO,CAAC,kBAAkB,CAAC,CAS7B;IAEK,SAAS,CAAC,KAAK,EAAE,cAAc,CAAC,2BAA2B,CAAC,GAAG,OAAO,CAAC,QAAQ,CAAC,CAgBrF;IAEK,SAAS,CAAC,KAAK,EAAE,cAAc,CAAC,2BAA2B,CAAC,GAAG,OAAO,CAAC,YAAY,CAAC,CAyBzF;IAED,uBAAuB,CAAC,OAAO,EAAE,OAAO,GAAG,gBAAgB,GAAG,IAAI,CAwCjE;IAEK,kBAAkB,CACtB,KAAK,EAAE,uBAAuB,CAAC,2BAA2B,CAAC,GAC1D,OAAO,CAAC,YAAY,CAAC,CAkBvB;YAEa,cAAc;CAW7B"}
1
+ {"version":3,"file":"source-control.d.ts","sourceRoot":"","sources":["../../src/core/source-control.ts"],"names":[],"mappings":"AACA,OAAO,EAGL,KAAK,YAAY,EACjB,KAAK,uBAAuB,EAC5B,KAAK,cAAc,EACnB,KAAK,QAAQ,EACb,KAAK,YAAY,EACjB,KAAK,qBAAqB,EAK1B,KAAK,cAAc,EACnB,KAAK,qBAAqB,EAK1B,KAAK,cAAc,EACnB,KAAK,kBAAkB,EAEvB,KAAK,WAAW,EAChB,KAAK,eAAe,EACpB,KAAK,sBAAsB,EAC3B,KAAK,qBAAqB,EAC1B,KAAK,gBAAgB,EACtB,MAAM,8BAA8B,CAAC;AACtC,OAAO,KAAK,EAAC,eAAe,EAAmB,MAAM,gBAAgB,CAAC;AAKtE,KAAK,2BAA2B,GAAG,qBAAqB,CAAC,QAAQ,CAAC,CAAC;AAMnE,qBAAa,2BACX,YAAW,qBAAqB,CAAC,2BAA2B,CAAC;IAG3D,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,OAAO,CAAC,QAAQ,CAAC,WAAW;IAF9B,YACmB,MAAM,EAAE,eAAe,EACvB,WAAW,GAAE,MAAM,MAAM,GAAG,SAAuC,EAClF;IAEE,gBAAgB,CACpB,KAAK,EAAE,qBAAqB,CAAC,2BAA2B,CAAC,GACxD,OAAO,CAAC,cAAc,CAAC,CA+CzB;IAEK,iBAAiB,CACrB,KAAK,EAAE,sBAAsB,CAAC,2BAA2B,CAAC,GACzD,OAAO,CAAC,kBAAkB,CAAC,CAS7B;IAEK,SAAS,CAAC,KAAK,EAAE,cAAc,CAAC,2BAA2B,CAAC,GAAG,OAAO,CAAC,QAAQ,CAAC,CAgBrF;IAEK,SAAS,CAAC,KAAK,EAAE,cAAc,CAAC,2BAA2B,CAAC,GAAG,OAAO,CAAC,YAAY,CAAC,CAyBzF;IAED,uBAAuB,CAAC,OAAO,EAAE,OAAO,GAAG,gBAAgB,GAAG,IAAI,CAwCjE;IAEK,UAAU,CAAC,KAAK,EAAE,eAAe,CAAC,2BAA2B,CAAC,GAAG,OAAO,CAAC,WAAW,CAAC,CA6B1F;IAEK,kBAAkB,CACtB,KAAK,EAAE,uBAAuB,CAAC,2BAA2B,CAAC,GAC1D,OAAO,CAAC,YAAY,CAAC,CAsBvB;YAEa,cAAc;CAW7B"}
@@ -1,15 +1,15 @@
1
1
  import { Buffer } from 'node:buffer';
2
- import { asRecord, buildProviderRepositoryId, isRecord, isValidGitObjectId, isValidTriggerRef, MAX_REPOSITORY_FILE_BYTES, nonEmptyString, parseProviderRepositoryId, positiveInteger } from '@shipfox/api-integration-spi';
3
- import { config } from '#config.js';
2
+ import { asRecord, buildProviderRepositoryId, isRecord, isValidGitObjectId, isValidResolvableRef, isValidTriggerRef, MAX_REPOSITORY_FILE_BYTES, nonEmptyString, parseProviderRepositoryId, positiveInteger } from '@shipfox/api-integration-spi';
4
3
  import { getGithubInstallationByConnectionId } from '#db/installations.js';
4
+ import { configuredGithubAppBotLogin } from './bot-identity.js';
5
5
  import { GithubIntegrationProviderError } from './errors.js';
6
6
  const GITHUB_PROVIDER = 'github';
7
- const GITHUB_APP_BOT_SUFFIX = '[bot]';
8
7
  const SEARCH_PAGE_SIZE = 100;
9
8
  const SEARCH_MAX_PAGES_PER_REQUEST = 5;
10
9
  export class GithubSourceControlProvider {
11
- constructor(github){
10
+ constructor(github, appBotLogin = configuredGithubAppBotLogin){
12
11
  this.github = github;
12
+ this.appBotLogin = appBotLogin;
13
13
  }
14
14
  async listRepositories(input) {
15
15
  const installation = await getGithubInstallationByConnectionId(input.connection.id);
@@ -138,6 +138,29 @@ export class GithubSourceControlProvider {
138
138
  actor
139
139
  };
140
140
  }
141
+ async resolveRef(input) {
142
+ if (!isValidResolvableRef(input.ref)) {
143
+ throw new GithubIntegrationProviderError('ref-invalid', `GitHub ref ${formatRefForMessage(input.ref)} is not a resolvable branch or tag name`);
144
+ }
145
+ const installationId = await this.installationId(input.connection.id);
146
+ const { repositoryId } = parseGithubRepositoryLocator(input.externalRepositoryId);
147
+ const commits = await this.github.listRepositoryCommits({
148
+ installationId,
149
+ repositoryId,
150
+ ref: input.ref
151
+ });
152
+ const commit = commits[0]?.sha;
153
+ if (!commit) {
154
+ throw new GithubIntegrationProviderError('ref-not-found', `GitHub ref ${formatRefForMessage(input.ref)} does not resolve to a commit`);
155
+ }
156
+ if (!isValidGitObjectId(commit)) {
157
+ throw new GithubIntegrationProviderError('malformed-provider-response', `GitHub ref ${formatRefForMessage(input.ref)} resolved to an invalid commit`);
158
+ }
159
+ return {
160
+ ref: input.ref,
161
+ commit
162
+ };
163
+ }
141
164
  async createCheckoutSpec(input) {
142
165
  const installationId = await this.installationId(input.connection.id);
143
166
  const { repositoryId } = parseGithubRepositoryLocator(input.externalRepositoryId);
@@ -151,7 +174,8 @@ export class GithubSourceControlProvider {
151
174
  repositoryId,
152
175
  permissions: input.permissions
153
176
  });
154
- const gitAuthor = githubAppGitAuthor();
177
+ const botLogin = this.appBotLogin();
178
+ const gitAuthor = botLogin && input.permissions?.contents === 'write' ? await githubAppGitAuthor(this.github, token, botLogin) : undefined;
155
179
  return {
156
180
  repositoryUrl: repository.cloneUrl,
157
181
  ref,
@@ -189,13 +213,20 @@ function sameGithubRepository(first, second) {
189
213
  const secondName = nonEmptyString(second.full_name);
190
214
  return Boolean(firstName && secondName && firstName.toLowerCase() === secondName.toLowerCase());
191
215
  }
192
- function githubAppGitAuthor() {
193
- const appUsername = config.GITHUB_APP_USERNAME?.trim();
194
- if (!appUsername) return undefined;
195
- const name = appUsername.endsWith(GITHUB_APP_BOT_SUFFIX) ? appUsername : `${appUsername}${GITHUB_APP_BOT_SUFFIX}`;
216
+ function formatRefForMessage(ref) {
217
+ return JSON.stringify(ref);
218
+ }
219
+ async function githubAppGitAuthor(github, installationAccessToken, name) {
220
+ if (!github.getBotUser) {
221
+ throw new GithubIntegrationProviderError('provider-unavailable', 'GitHub bot identity resolution is unavailable');
222
+ }
223
+ const botUser = await github.getBotUser({
224
+ username: name,
225
+ installationAccessToken
226
+ });
196
227
  return {
197
- name,
198
- email: `${config.GITHUB_APP_ID}+${name}@users.noreply.github.com`
228
+ name: botUser.login,
229
+ email: `${botUser.id}+${botUser.login}@users.noreply.github.com`
199
230
  };
200
231
  }
201
232
  function toRepositorySnapshot(repository) {
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/core/source-control.ts"],"sourcesContent":["import {Buffer} from 'node:buffer';\nimport {\n asRecord,\n buildProviderRepositoryId,\n type CheckoutSpec,\n type CreateCheckoutSpecInput,\n type FetchFileInput,\n type FilePage,\n type FileSnapshot,\n type IntegrationConnection,\n isRecord,\n isValidGitObjectId,\n isValidTriggerRef,\n type ListFilesInput,\n type ListRepositoriesInput,\n MAX_REPOSITORY_FILE_BYTES,\n nonEmptyString,\n parseProviderRepositoryId,\n positiveInteger,\n type RepositoryPage,\n type RepositorySnapshot,\n type RepositoryVisibility,\n type ResolveRepositoryInput,\n type SourceControlProvider,\n type TriggerReference,\n} from '@shipfox/api-integration-spi';\nimport type {GithubApiClient, GithubRepository} from '#api/client.js';\nimport {config} from '#config.js';\nimport {getGithubInstallationByConnectionId} from '#db/installations.js';\nimport {GithubIntegrationProviderError} from './errors.js';\n\ntype GithubIntegrationConnection = IntegrationConnection<'github'>;\n\nconst GITHUB_PROVIDER = 'github';\nconst GITHUB_APP_BOT_SUFFIX = '[bot]';\nconst SEARCH_PAGE_SIZE = 100;\nconst SEARCH_MAX_PAGES_PER_REQUEST = 5;\n\nexport class GithubSourceControlProvider\n implements SourceControlProvider<GithubIntegrationConnection>\n{\n constructor(private readonly github: GithubApiClient) {}\n\n async listRepositories(\n input: ListRepositoriesInput<GithubIntegrationConnection>,\n ): Promise<RepositoryPage> {\n const installation = await getGithubInstallationByConnectionId(input.connection.id);\n if (!installation) {\n throw new GithubIntegrationProviderError(\n 'access-denied',\n 'GitHub installation details were not found for the connection',\n );\n }\n\n const installationId = Number.parseInt(installation.installationId, 10);\n const needle = input.search?.trim().toLowerCase();\n\n if (!needle) {\n const page = await this.github.listInstallationRepositories({\n installationId,\n limit: input.limit,\n cursor: input.cursor,\n });\n return {\n repositories: page.repositories.map(toRepositorySnapshot),\n nextCursor: page.nextCursor,\n };\n }\n\n const matches: RepositorySnapshot[] = [];\n let cursor = input.cursor;\n let pagesScanned = 0;\n while (matches.length < input.limit && pagesScanned < SEARCH_MAX_PAGES_PER_REQUEST) {\n const page = await this.github.listInstallationRepositories({\n installationId,\n limit: SEARCH_PAGE_SIZE,\n cursor,\n });\n pagesScanned += 1;\n for (const repo of page.repositories) {\n if (repo.fullName.toLowerCase().includes(needle)) {\n matches.push(toRepositorySnapshot(repo));\n }\n }\n cursor = page.nextCursor ?? undefined;\n if (!cursor) break;\n }\n\n return {\n repositories: matches.slice(0, input.limit),\n nextCursor: cursor ?? null,\n };\n }\n\n async resolveRepository(\n input: ResolveRepositoryInput<GithubIntegrationConnection>,\n ): Promise<RepositorySnapshot> {\n const installationId = await this.installationId(input.connection.id);\n const {repositoryId} = parseGithubRepositoryLocator(input.externalRepositoryId);\n const repository = await this.github.getRepository({\n installationId,\n repositoryId,\n });\n\n return toRepositorySnapshot(repository);\n }\n\n async listFiles(input: ListFilesInput<GithubIntegrationConnection>): Promise<FilePage> {\n const installationId = await this.installationId(input.connection.id);\n const {repositoryId} = parseGithubRepositoryLocator(input.externalRepositoryId);\n const page = await this.github.listRepositoryFiles({\n installationId,\n repositoryId,\n ref: input.ref,\n prefix: input.prefix,\n limit: input.limit,\n cursor: input.cursor,\n });\n\n return {\n files: page.files.map((file) => ({path: file.path, type: 'file', size: file.size})),\n nextCursor: page.nextCursor,\n };\n }\n\n async fetchFile(input: FetchFileInput<GithubIntegrationConnection>): Promise<FileSnapshot> {\n const installationId = await this.installationId(input.connection.id);\n const {repositoryId} = parseGithubRepositoryLocator(input.externalRepositoryId);\n const file = await this.github.fetchRepositoryFile({\n installationId,\n repositoryId,\n ref: input.ref,\n path: input.path,\n });\n\n if (\n file.size > MAX_REPOSITORY_FILE_BYTES ||\n Buffer.byteLength(file.content, 'utf8') > MAX_REPOSITORY_FILE_BYTES\n ) {\n throw new GithubIntegrationProviderError(\n 'content-too-large',\n 'GitHub file content is larger than the supported limit',\n );\n }\n\n return {\n path: file.path,\n ref: input.ref,\n content: file.content,\n };\n }\n\n resolveTriggerReference(payload: unknown): TriggerReference | null {\n if (!isRecord(payload)) return null;\n\n const actor = githubEventActor(payload);\n const pullRequest = asRecord(payload.pull_request);\n if (pullRequest) {\n const head = asRecord(pullRequest.head);\n const repository = asRecord(head?.repo);\n const base = asRecord(pullRequest.base);\n const baseRepository = asRecord(base?.repo) ?? asRecord(payload.repository);\n if (!repository || !baseRepository || !sameGithubRepository(repository, baseRepository)) {\n return null;\n }\n const repositoryId = githubRepositoryId(repository);\n const number = positiveInteger(pullRequest.number);\n const commit = nonEmptyString(head?.sha);\n const ref = number === null ? null : `refs/pull/${number}/head`;\n if (!repositoryId || !ref || !commit) return null;\n const hasValidReference = isValidGitObjectId(commit) && isValidTriggerRef(ref);\n if (!hasValidReference) return null;\n return {\n externalRepositoryId: buildProviderRepositoryId(GITHUB_PROVIDER, repositoryId),\n ref,\n commit,\n actor,\n };\n }\n\n const repositoryId = githubRepositoryId(asRecord(payload.repository));\n const ref = nonEmptyString(payload.ref);\n const commit = nonEmptyString(payload.after);\n if (!repositoryId || !ref || !commit) return null;\n const hasValidReference = isValidGitObjectId(commit) && isValidTriggerRef(ref);\n if (!hasValidReference) return null;\n return {\n externalRepositoryId: buildProviderRepositoryId(GITHUB_PROVIDER, repositoryId),\n ref,\n commit,\n actor,\n };\n }\n\n async createCheckoutSpec(\n input: CreateCheckoutSpecInput<GithubIntegrationConnection>,\n ): Promise<CheckoutSpec> {\n const installationId = await this.installationId(input.connection.id);\n const {repositoryId} = parseGithubRepositoryLocator(input.externalRepositoryId);\n const repository = await this.github.getRepository({installationId, repositoryId});\n const ref = input.ref?.trim() || repository.defaultBranch;\n const {token, expiresAt} = await this.github.createInstallationAccessToken({\n installationId,\n repositoryId,\n permissions: input.permissions,\n });\n const gitAuthor = githubAppGitAuthor();\n\n return {\n repositoryUrl: repository.cloneUrl,\n ref,\n credentials: {username: 'x-access-token', token, expiresAt},\n ...(gitAuthor ? {gitAuthor} : {}),\n };\n }\n\n private async installationId(connectionId: string): Promise<number> {\n const installation = await getGithubInstallationByConnectionId(connectionId);\n if (!installation) {\n throw new GithubIntegrationProviderError(\n 'access-denied',\n 'GitHub installation details were not found for the connection',\n );\n }\n\n return Number.parseInt(installation.installationId, 10);\n }\n}\n\nfunction githubRepositoryId(repository: Record<string, unknown> | null): string | null {\n return positiveInteger(repository?.id) === null ? null : String(repository?.id);\n}\n\n// `sender` is the one actor field every GitHub webhook event carries, so it stays correct\n// for pushes, pull requests, and anything else that reaches a trigger reference.\nfunction githubEventActor(payload: Record<string, unknown>): string | null {\n return nonEmptyString(asRecord(payload.sender)?.login);\n}\n\nfunction sameGithubRepository(\n first: Record<string, unknown>,\n second: Record<string, unknown>,\n): boolean {\n const firstId = githubRepositoryId(first);\n const secondId = githubRepositoryId(second);\n if (firstId && secondId) return firstId === secondId;\n\n const firstName = nonEmptyString(first.full_name);\n const secondName = nonEmptyString(second.full_name);\n return Boolean(firstName && secondName && firstName.toLowerCase() === secondName.toLowerCase());\n}\n\nfunction githubAppGitAuthor(): CheckoutSpec['gitAuthor'] {\n const appUsername = config.GITHUB_APP_USERNAME?.trim();\n if (!appUsername) return undefined;\n const name = appUsername.endsWith(GITHUB_APP_BOT_SUFFIX)\n ? appUsername\n : `${appUsername}${GITHUB_APP_BOT_SUFFIX}`;\n return {name, email: `${config.GITHUB_APP_ID}+${name}@users.noreply.github.com`};\n}\n\nfunction toRepositorySnapshot(repository: GithubRepository): RepositorySnapshot {\n return {\n externalRepositoryId: buildProviderRepositoryId(GITHUB_PROVIDER, String(repository.id)),\n owner: repository.ownerLogin,\n name: repository.name,\n fullName: repository.fullName,\n defaultBranch: repository.defaultBranch,\n visibility: toRepositoryVisibility(repository),\n cloneUrl: repository.cloneUrl,\n htmlUrl: repository.htmlUrl,\n };\n}\n\nfunction parseGithubRepositoryLocator(externalRepositoryId: string): {\n repositoryId: number;\n} {\n const value = parseProviderRepositoryId(externalRepositoryId, GITHUB_PROVIDER);\n const repositoryId = Number.parseInt(value, 10);\n if (!Number.isInteger(repositoryId) || repositoryId <= 0 || String(repositoryId) !== value) {\n throw new GithubIntegrationProviderError(\n 'repository-not-found',\n `GitHub repository id ${externalRepositoryId} must follow the form ${GITHUB_PROVIDER}:<numeric-id>`,\n );\n }\n\n return {repositoryId};\n}\n\nfunction toRepositoryVisibility(repository: GithubRepository): RepositoryVisibility {\n if (repository.visibility === 'public') return 'public';\n if (repository.visibility === 'private') return 'private';\n if (repository.visibility === 'internal') return 'internal';\n return repository.private ? 'private' : 'public';\n}\n"],"names":["Buffer","asRecord","buildProviderRepositoryId","isRecord","isValidGitObjectId","isValidTriggerRef","MAX_REPOSITORY_FILE_BYTES","nonEmptyString","parseProviderRepositoryId","positiveInteger","config","getGithubInstallationByConnectionId","GithubIntegrationProviderError","GITHUB_PROVIDER","GITHUB_APP_BOT_SUFFIX","SEARCH_PAGE_SIZE","SEARCH_MAX_PAGES_PER_REQUEST","GithubSourceControlProvider","github","listRepositories","input","installation","connection","id","installationId","Number","parseInt","needle","search","trim","toLowerCase","page","listInstallationRepositories","limit","cursor","repositories","map","toRepositorySnapshot","nextCursor","matches","pagesScanned","length","repo","fullName","includes","push","undefined","slice","resolveRepository","repositoryId","parseGithubRepositoryLocator","externalRepositoryId","repository","getRepository","listFiles","listRepositoryFiles","ref","prefix","files","file","path","type","size","fetchFile","fetchRepositoryFile","byteLength","content","resolveTriggerReference","payload","actor","githubEventActor","pullRequest","pull_request","head","base","baseRepository","sameGithubRepository","githubRepositoryId","number","commit","sha","hasValidReference","after","createCheckoutSpec","defaultBranch","token","expiresAt","createInstallationAccessToken","permissions","gitAuthor","githubAppGitAuthor","repositoryUrl","cloneUrl","credentials","username","connectionId","String","sender","login","first","second","firstId","secondId","firstName","full_name","secondName","Boolean","appUsername","GITHUB_APP_USERNAME","name","endsWith","email","GITHUB_APP_ID","owner","ownerLogin","visibility","toRepositoryVisibility","htmlUrl","value","isInteger","private"],"mappings":"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"}
1
+ {"version":3,"sources":["../../src/core/source-control.ts"],"sourcesContent":["import {Buffer} from 'node:buffer';\nimport {\n asRecord,\n buildProviderRepositoryId,\n type CheckoutSpec,\n type CreateCheckoutSpecInput,\n type FetchFileInput,\n type FilePage,\n type FileSnapshot,\n type IntegrationConnection,\n isRecord,\n isValidGitObjectId,\n isValidResolvableRef,\n isValidTriggerRef,\n type ListFilesInput,\n type ListRepositoriesInput,\n MAX_REPOSITORY_FILE_BYTES,\n nonEmptyString,\n parseProviderRepositoryId,\n positiveInteger,\n type RepositoryPage,\n type RepositorySnapshot,\n type RepositoryVisibility,\n type ResolvedRef,\n type ResolveRefInput,\n type ResolveRepositoryInput,\n type SourceControlProvider,\n type TriggerReference,\n} from '@shipfox/api-integration-spi';\nimport type {GithubApiClient, GithubRepository} from '#api/client.js';\nimport {getGithubInstallationByConnectionId} from '#db/installations.js';\nimport {configuredGithubAppBotLogin} from './bot-identity.js';\nimport {GithubIntegrationProviderError} from './errors.js';\n\ntype GithubIntegrationConnection = IntegrationConnection<'github'>;\n\nconst GITHUB_PROVIDER = 'github';\nconst SEARCH_PAGE_SIZE = 100;\nconst SEARCH_MAX_PAGES_PER_REQUEST = 5;\n\nexport class GithubSourceControlProvider\n implements SourceControlProvider<GithubIntegrationConnection>\n{\n constructor(\n private readonly github: GithubApiClient,\n private readonly appBotLogin: () => string | undefined = configuredGithubAppBotLogin,\n ) {}\n\n async listRepositories(\n input: ListRepositoriesInput<GithubIntegrationConnection>,\n ): Promise<RepositoryPage> {\n const installation = await getGithubInstallationByConnectionId(input.connection.id);\n if (!installation) {\n throw new GithubIntegrationProviderError(\n 'access-denied',\n 'GitHub installation details were not found for the connection',\n );\n }\n\n const installationId = Number.parseInt(installation.installationId, 10);\n const needle = input.search?.trim().toLowerCase();\n\n if (!needle) {\n const page = await this.github.listInstallationRepositories({\n installationId,\n limit: input.limit,\n cursor: input.cursor,\n });\n return {\n repositories: page.repositories.map(toRepositorySnapshot),\n nextCursor: page.nextCursor,\n };\n }\n\n const matches: RepositorySnapshot[] = [];\n let cursor = input.cursor;\n let pagesScanned = 0;\n while (matches.length < input.limit && pagesScanned < SEARCH_MAX_PAGES_PER_REQUEST) {\n const page = await this.github.listInstallationRepositories({\n installationId,\n limit: SEARCH_PAGE_SIZE,\n cursor,\n });\n pagesScanned += 1;\n for (const repo of page.repositories) {\n if (repo.fullName.toLowerCase().includes(needle)) {\n matches.push(toRepositorySnapshot(repo));\n }\n }\n cursor = page.nextCursor ?? undefined;\n if (!cursor) break;\n }\n\n return {\n repositories: matches.slice(0, input.limit),\n nextCursor: cursor ?? null,\n };\n }\n\n async resolveRepository(\n input: ResolveRepositoryInput<GithubIntegrationConnection>,\n ): Promise<RepositorySnapshot> {\n const installationId = await this.installationId(input.connection.id);\n const {repositoryId} = parseGithubRepositoryLocator(input.externalRepositoryId);\n const repository = await this.github.getRepository({\n installationId,\n repositoryId,\n });\n\n return toRepositorySnapshot(repository);\n }\n\n async listFiles(input: ListFilesInput<GithubIntegrationConnection>): Promise<FilePage> {\n const installationId = await this.installationId(input.connection.id);\n const {repositoryId} = parseGithubRepositoryLocator(input.externalRepositoryId);\n const page = await this.github.listRepositoryFiles({\n installationId,\n repositoryId,\n ref: input.ref,\n prefix: input.prefix,\n limit: input.limit,\n cursor: input.cursor,\n });\n\n return {\n files: page.files.map((file) => ({path: file.path, type: 'file', size: file.size})),\n nextCursor: page.nextCursor,\n };\n }\n\n async fetchFile(input: FetchFileInput<GithubIntegrationConnection>): Promise<FileSnapshot> {\n const installationId = await this.installationId(input.connection.id);\n const {repositoryId} = parseGithubRepositoryLocator(input.externalRepositoryId);\n const file = await this.github.fetchRepositoryFile({\n installationId,\n repositoryId,\n ref: input.ref,\n path: input.path,\n });\n\n if (\n file.size > MAX_REPOSITORY_FILE_BYTES ||\n Buffer.byteLength(file.content, 'utf8') > MAX_REPOSITORY_FILE_BYTES\n ) {\n throw new GithubIntegrationProviderError(\n 'content-too-large',\n 'GitHub file content is larger than the supported limit',\n );\n }\n\n return {\n path: file.path,\n ref: input.ref,\n content: file.content,\n };\n }\n\n resolveTriggerReference(payload: unknown): TriggerReference | null {\n if (!isRecord(payload)) return null;\n\n const actor = githubEventActor(payload);\n const pullRequest = asRecord(payload.pull_request);\n if (pullRequest) {\n const head = asRecord(pullRequest.head);\n const repository = asRecord(head?.repo);\n const base = asRecord(pullRequest.base);\n const baseRepository = asRecord(base?.repo) ?? asRecord(payload.repository);\n if (!repository || !baseRepository || !sameGithubRepository(repository, baseRepository)) {\n return null;\n }\n const repositoryId = githubRepositoryId(repository);\n const number = positiveInteger(pullRequest.number);\n const commit = nonEmptyString(head?.sha);\n const ref = number === null ? null : `refs/pull/${number}/head`;\n if (!repositoryId || !ref || !commit) return null;\n const hasValidReference = isValidGitObjectId(commit) && isValidTriggerRef(ref);\n if (!hasValidReference) return null;\n return {\n externalRepositoryId: buildProviderRepositoryId(GITHUB_PROVIDER, repositoryId),\n ref,\n commit,\n actor,\n };\n }\n\n const repositoryId = githubRepositoryId(asRecord(payload.repository));\n const ref = nonEmptyString(payload.ref);\n const commit = nonEmptyString(payload.after);\n if (!repositoryId || !ref || !commit) return null;\n const hasValidReference = isValidGitObjectId(commit) && isValidTriggerRef(ref);\n if (!hasValidReference) return null;\n return {\n externalRepositoryId: buildProviderRepositoryId(GITHUB_PROVIDER, repositoryId),\n ref,\n commit,\n actor,\n };\n }\n\n async resolveRef(input: ResolveRefInput<GithubIntegrationConnection>): Promise<ResolvedRef> {\n if (!isValidResolvableRef(input.ref)) {\n throw new GithubIntegrationProviderError(\n 'ref-invalid',\n `GitHub ref ${formatRefForMessage(input.ref)} is not a resolvable branch or tag name`,\n );\n }\n const installationId = await this.installationId(input.connection.id);\n const {repositoryId} = parseGithubRepositoryLocator(input.externalRepositoryId);\n const commits = await this.github.listRepositoryCommits({\n installationId,\n repositoryId,\n ref: input.ref,\n });\n const commit = commits[0]?.sha;\n if (!commit) {\n throw new GithubIntegrationProviderError(\n 'ref-not-found',\n `GitHub ref ${formatRefForMessage(input.ref)} does not resolve to a commit`,\n );\n }\n if (!isValidGitObjectId(commit)) {\n throw new GithubIntegrationProviderError(\n 'malformed-provider-response',\n `GitHub ref ${formatRefForMessage(input.ref)} resolved to an invalid commit`,\n );\n }\n\n return {ref: input.ref, commit};\n }\n\n async createCheckoutSpec(\n input: CreateCheckoutSpecInput<GithubIntegrationConnection>,\n ): Promise<CheckoutSpec> {\n const installationId = await this.installationId(input.connection.id);\n const {repositoryId} = parseGithubRepositoryLocator(input.externalRepositoryId);\n const repository = await this.github.getRepository({installationId, repositoryId});\n const ref = input.ref?.trim() || repository.defaultBranch;\n const {token, expiresAt} = await this.github.createInstallationAccessToken({\n installationId,\n repositoryId,\n permissions: input.permissions,\n });\n const botLogin = this.appBotLogin();\n const gitAuthor =\n botLogin && input.permissions?.contents === 'write'\n ? await githubAppGitAuthor(this.github, token, botLogin)\n : undefined;\n\n return {\n repositoryUrl: repository.cloneUrl,\n ref,\n credentials: {username: 'x-access-token', token, expiresAt},\n ...(gitAuthor ? {gitAuthor} : {}),\n };\n }\n\n private async installationId(connectionId: string): Promise<number> {\n const installation = await getGithubInstallationByConnectionId(connectionId);\n if (!installation) {\n throw new GithubIntegrationProviderError(\n 'access-denied',\n 'GitHub installation details were not found for the connection',\n );\n }\n\n return Number.parseInt(installation.installationId, 10);\n }\n}\n\nfunction githubRepositoryId(repository: Record<string, unknown> | null): string | null {\n return positiveInteger(repository?.id) === null ? null : String(repository?.id);\n}\n\n// `sender` is the one actor field every GitHub webhook event carries, so it stays correct\n// for pushes, pull requests, and anything else that reaches a trigger reference.\nfunction githubEventActor(payload: Record<string, unknown>): string | null {\n return nonEmptyString(asRecord(payload.sender)?.login);\n}\n\nfunction sameGithubRepository(\n first: Record<string, unknown>,\n second: Record<string, unknown>,\n): boolean {\n const firstId = githubRepositoryId(first);\n const secondId = githubRepositoryId(second);\n if (firstId && secondId) return firstId === secondId;\n\n const firstName = nonEmptyString(first.full_name);\n const secondName = nonEmptyString(second.full_name);\n return Boolean(firstName && secondName && firstName.toLowerCase() === secondName.toLowerCase());\n}\n\nfunction formatRefForMessage(ref: string): string {\n return JSON.stringify(ref);\n}\n\nasync function githubAppGitAuthor(\n github: GithubApiClient,\n installationAccessToken: string,\n name: string,\n): Promise<CheckoutSpec['gitAuthor']> {\n if (!github.getBotUser) {\n throw new GithubIntegrationProviderError(\n 'provider-unavailable',\n 'GitHub bot identity resolution is unavailable',\n );\n }\n\n const botUser = await github.getBotUser({username: name, installationAccessToken});\n return {\n name: botUser.login,\n email: `${botUser.id}+${botUser.login}@users.noreply.github.com`,\n };\n}\n\nfunction toRepositorySnapshot(repository: GithubRepository): RepositorySnapshot {\n return {\n externalRepositoryId: buildProviderRepositoryId(GITHUB_PROVIDER, String(repository.id)),\n owner: repository.ownerLogin,\n name: repository.name,\n fullName: repository.fullName,\n defaultBranch: repository.defaultBranch,\n visibility: toRepositoryVisibility(repository),\n cloneUrl: repository.cloneUrl,\n htmlUrl: repository.htmlUrl,\n };\n}\n\nfunction parseGithubRepositoryLocator(externalRepositoryId: string): {\n repositoryId: number;\n} {\n const value = parseProviderRepositoryId(externalRepositoryId, GITHUB_PROVIDER);\n const repositoryId = Number.parseInt(value, 10);\n if (!Number.isInteger(repositoryId) || repositoryId <= 0 || String(repositoryId) !== value) {\n throw new GithubIntegrationProviderError(\n 'repository-not-found',\n `GitHub repository id ${externalRepositoryId} must follow the form ${GITHUB_PROVIDER}:<numeric-id>`,\n );\n }\n\n return {repositoryId};\n}\n\nfunction toRepositoryVisibility(repository: GithubRepository): RepositoryVisibility {\n if (repository.visibility === 'public') return 'public';\n if (repository.visibility === 'private') return 'private';\n if (repository.visibility === 'internal') return 'internal';\n return repository.private ? 'private' : 'public';\n}\n"],"names":["Buffer","asRecord","buildProviderRepositoryId","isRecord","isValidGitObjectId","isValidResolvableRef","isValidTriggerRef","MAX_REPOSITORY_FILE_BYTES","nonEmptyString","parseProviderRepositoryId","positiveInteger","getGithubInstallationByConnectionId","configuredGithubAppBotLogin","GithubIntegrationProviderError","GITHUB_PROVIDER","SEARCH_PAGE_SIZE","SEARCH_MAX_PAGES_PER_REQUEST","GithubSourceControlProvider","github","appBotLogin","listRepositories","input","installation","connection","id","installationId","Number","parseInt","needle","search","trim","toLowerCase","page","listInstallationRepositories","limit","cursor","repositories","map","toRepositorySnapshot","nextCursor","matches","pagesScanned","length","repo","fullName","includes","push","undefined","slice","resolveRepository","repositoryId","parseGithubRepositoryLocator","externalRepositoryId","repository","getRepository","listFiles","listRepositoryFiles","ref","prefix","files","file","path","type","size","fetchFile","fetchRepositoryFile","byteLength","content","resolveTriggerReference","payload","actor","githubEventActor","pullRequest","pull_request","head","base","baseRepository","sameGithubRepository","githubRepositoryId","number","commit","sha","hasValidReference","after","resolveRef","formatRefForMessage","commits","listRepositoryCommits","createCheckoutSpec","defaultBranch","token","expiresAt","createInstallationAccessToken","permissions","botLogin","gitAuthor","contents","githubAppGitAuthor","repositoryUrl","cloneUrl","credentials","username","connectionId","String","sender","login","first","second","firstId","secondId","firstName","full_name","secondName","Boolean","JSON","stringify","installationAccessToken","name","getBotUser","botUser","email","owner","ownerLogin","visibility","toRepositoryVisibility","htmlUrl","value","isInteger","private"],"mappings":"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"}
package/dist/index.d.ts CHANGED
@@ -44,6 +44,84 @@ export interface CreateGithubIntegrationProviderOptions extends Omit<CreateGithu
44
44
  export declare function createGithubIntegrationProvider(options: CreateGithubIntegrationProviderOptions): {
45
45
  provider: 'github';
46
46
  displayName: string;
47
+ eventCatalog: {
48
+ readonly provider: 'GitHub';
49
+ readonly passthrough: true;
50
+ readonly upstreamEventsDocUrl: "https://docs.github.com/en/webhooks/webhook-events-and-payloads";
51
+ readonly events: readonly [{
52
+ readonly name: 'push';
53
+ readonly summary: 'A Git reference receives one or more commits.';
54
+ readonly emittedWhen: 'GitHub sends a push webhook to the connected GitHub App.';
55
+ readonly payloadKind: 'raw-provider';
56
+ readonly payloadDocUrl: "https://docs.github.com/en/webhooks/webhook-events-and-payloads";
57
+ }, {
58
+ readonly name: 'pull_request.opened';
59
+ readonly summary: 'A pull request opens.';
60
+ readonly emittedWhen: 'GitHub sends a pull_request webhook with the opened action.';
61
+ readonly payloadKind: 'raw-provider';
62
+ readonly payloadDocUrl: "https://docs.github.com/en/webhooks/webhook-events-and-payloads";
63
+ }, {
64
+ readonly name: 'pull_request.closed';
65
+ readonly summary: 'A pull request closes or merges.';
66
+ readonly emittedWhen: 'GitHub sends a pull_request webhook with the closed action.';
67
+ readonly payloadKind: 'raw-provider';
68
+ readonly payloadDocUrl: "https://docs.github.com/en/webhooks/webhook-events-and-payloads";
69
+ }, {
70
+ readonly name: 'pull_request.synchronize';
71
+ readonly summary: 'A pull request head branch receives commits.';
72
+ readonly emittedWhen: 'GitHub sends a pull_request webhook with the synchronize action.';
73
+ readonly payloadKind: 'raw-provider';
74
+ readonly payloadDocUrl: "https://docs.github.com/en/webhooks/webhook-events-and-payloads";
75
+ }, {
76
+ readonly name: 'pull_request.reopened';
77
+ readonly summary: 'A closed pull request reopens.';
78
+ readonly emittedWhen: 'GitHub sends a pull_request webhook with the reopened action.';
79
+ readonly payloadKind: 'raw-provider';
80
+ readonly payloadDocUrl: "https://docs.github.com/en/webhooks/webhook-events-and-payloads";
81
+ }, {
82
+ readonly name: 'issues.opened';
83
+ readonly summary: 'An issue opens.';
84
+ readonly emittedWhen: 'GitHub sends an issues webhook with the opened action.';
85
+ readonly payloadKind: 'raw-provider';
86
+ readonly payloadDocUrl: "https://docs.github.com/en/webhooks/webhook-events-and-payloads";
87
+ }, {
88
+ readonly name: 'issues.closed';
89
+ readonly summary: 'An issue closes.';
90
+ readonly emittedWhen: 'GitHub sends an issues webhook with the closed action.';
91
+ readonly payloadKind: 'raw-provider';
92
+ readonly payloadDocUrl: "https://docs.github.com/en/webhooks/webhook-events-and-payloads";
93
+ }, {
94
+ readonly name: 'issues.reopened';
95
+ readonly summary: 'A closed issue reopens.';
96
+ readonly emittedWhen: 'GitHub sends an issues webhook with the reopened action.';
97
+ readonly payloadKind: 'raw-provider';
98
+ readonly payloadDocUrl: "https://docs.github.com/en/webhooks/webhook-events-and-payloads";
99
+ }, {
100
+ readonly name: 'issues.edited';
101
+ readonly summary: 'An issue title or body changes.';
102
+ readonly emittedWhen: 'GitHub sends an issues webhook with the edited action.';
103
+ readonly payloadKind: 'raw-provider';
104
+ readonly payloadDocUrl: "https://docs.github.com/en/webhooks/webhook-events-and-payloads";
105
+ }, {
106
+ readonly name: 'issues.labeled';
107
+ readonly summary: 'A label is added to an issue.';
108
+ readonly emittedWhen: 'GitHub sends an issues webhook with the labeled action.';
109
+ readonly payloadKind: 'raw-provider';
110
+ readonly payloadDocUrl: "https://docs.github.com/en/webhooks/webhook-events-and-payloads";
111
+ }, {
112
+ readonly name: 'issues.unlabeled';
113
+ readonly summary: 'A label is removed from an issue.';
114
+ readonly emittedWhen: 'GitHub sends an issues webhook with the unlabeled action.';
115
+ readonly payloadKind: 'raw-provider';
116
+ readonly payloadDocUrl: "https://docs.github.com/en/webhooks/webhook-events-and-payloads";
117
+ }, {
118
+ readonly name: 'release.published';
119
+ readonly summary: 'A release is published.';
120
+ readonly emittedWhen: 'GitHub sends a release webhook with the published action.';
121
+ readonly payloadKind: 'raw-provider';
122
+ readonly payloadDocUrl: "https://docs.github.com/en/webhooks/webhook-events-and-payloads";
123
+ }];
124
+ };
47
125
  adapters: {
48
126
  source_control: GithubSourceControlProvider;
49
127
  agent_tools: GithubAgentToolsProvider;