@shipfox/api-integration-github 12.5.0 → 12.7.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.
- package/.turbo/turbo-build.log +1 -1
- package/CHANGELOG.md +14 -0
- package/dist/api/client.d.ts.map +1 -1
- package/dist/api/client.js +8 -5
- package/dist/api/client.js.map +1 -1
- package/dist/api/github-octokit.d.ts +7 -0
- package/dist/api/github-octokit.d.ts.map +1 -0
- package/dist/api/github-octokit.js +32 -0
- package/dist/api/github-octokit.js.map +1 -0
- package/dist/api/installation-token-provider.d.ts.map +1 -1
- package/dist/api/installation-token-provider.js +4 -2
- package/dist/api/installation-token-provider.js.map +1 -1
- package/dist/config.d.ts +1 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +8 -0
- package/dist/config.js.map +1 -1
- package/dist/core/agent-tools.d.ts.map +1 -1
- package/dist/core/agent-tools.js +203 -68
- package/dist/core/agent-tools.js.map +1 -1
- package/dist/core/github-agent-tool-catalog.d.ts +1 -1
- package/dist/core/github-agent-tool-catalog.d.ts.map +1 -1
- package/dist/core/github-agent-tool-catalog.js +19 -0
- package/dist/core/github-agent-tool-catalog.js.map +1 -1
- package/dist/metrics/instance.d.ts +1 -0
- package/dist/metrics/instance.d.ts.map +1 -1
- package/dist/metrics/instance.js +19 -0
- package/dist/metrics/instance.js.map +1 -1
- package/dist/tsconfig.test.tsbuildinfo +1 -1
- package/package.json +1 -1
- package/src/api/client.test.ts +25 -5
- package/src/api/client.ts +22 -5
- package/src/api/github-octokit.test.ts +115 -0
- package/src/api/github-octokit.ts +49 -0
- package/src/api/installation-token-provider.test.ts +44 -5
- package/src/api/installation-token-provider.ts +5 -2
- package/src/config.ts +5 -0
- package/src/core/agent-tools.test.ts +395 -74
- package/src/core/agent-tools.ts +284 -69
- package/src/core/github-agent-tool-catalog.ts +35 -0
- package/src/metrics/instance.ts +25 -0
- package/test/env.ts +1 -0
- package/test/fixtures/github-installation-token.ts +8 -0
- package/test/index.ts +4 -0
- package/tsconfig.build.tsbuildinfo +1 -1
package/.turbo/turbo-build.log
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
$ shipfox-swc
|
|
2
|
-
Successfully compiled:
|
|
2
|
+
Successfully compiled: 31 files with swc (417.05ms)
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,19 @@
|
|
|
1
1
|
# @shipfox/api-integration-github
|
|
2
2
|
|
|
3
|
+
## 12.7.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 4639dd9: Add GitHub pull request review-thread inspection and resolution tools.
|
|
8
|
+
|
|
9
|
+
## 12.6.0
|
|
10
|
+
|
|
11
|
+
### Patch Changes
|
|
12
|
+
|
|
13
|
+
- 8aaed9e: Expose bounded error codes for deterministic GitHub agent tool failures.
|
|
14
|
+
- 4848ad7: Fix pending GitHub review comments by resolving the caller's pending review through the REST API.
|
|
15
|
+
- 594cdbe: Support both stateless and stateful GitHub App installation token formats.
|
|
16
|
+
|
|
3
17
|
## 12.5.0
|
|
4
18
|
|
|
5
19
|
### Patch Changes
|
package/dist/api/client.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/api/client.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/api/client.ts"],"names":[],"mappings":"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"}
|
package/dist/api/client.js
CHANGED
|
@@ -4,6 +4,8 @@ import ky, { HTTPError, TimeoutError } from 'ky';
|
|
|
4
4
|
import { App, Octokit, RequestError } from 'octokit';
|
|
5
5
|
import { config, normalizedGithubApiBaseUrl, normalizedGithubPrivateKey } from '#config.js';
|
|
6
6
|
import { GithubIntegrationProviderError } from '#core/errors.js';
|
|
7
|
+
import { recordInstallationTokenFormat } from '#metrics/index.js';
|
|
8
|
+
import { getGithubInstallationOctokit, githubInstallationTokenFormatPlugin } from './github-octokit.js';
|
|
7
9
|
const NEXT_PAGE_RE = /[?&]page=(\d+)/;
|
|
8
10
|
const TRAILING_SLASHES_RE = /\/+$/;
|
|
9
11
|
const MAX_TREE_WALK_DEPTH = 10;
|
|
@@ -66,7 +68,7 @@ let OctokitGithubApiClient = class OctokitGithubApiClient {
|
|
|
66
68
|
};
|
|
67
69
|
}
|
|
68
70
|
async listInstallationRepositories(input) {
|
|
69
|
-
const octokit = await this.getApp()
|
|
71
|
+
const octokit = await mapGithubError(()=>getGithubInstallationOctokit(this.getApp(), input.installationId));
|
|
70
72
|
const page = cursorToPage(input.cursor);
|
|
71
73
|
const response = await mapGithubError(()=>octokit.rest.apps.listReposAccessibleToInstallation({
|
|
72
74
|
per_page: input.limit,
|
|
@@ -78,14 +80,14 @@ let OctokitGithubApiClient = class OctokitGithubApiClient {
|
|
|
78
80
|
};
|
|
79
81
|
}
|
|
80
82
|
async getRepository(input) {
|
|
81
|
-
const octokit = await this.getApp()
|
|
83
|
+
const octokit = await mapGithubError(()=>getGithubInstallationOctokit(this.getApp(), input.installationId));
|
|
82
84
|
const response = await mapGithubError(()=>octokit.request('GET /repositories/{repository_id}', {
|
|
83
85
|
repository_id: input.repositoryId
|
|
84
86
|
}));
|
|
85
87
|
return toGithubRepository(response.data);
|
|
86
88
|
}
|
|
87
89
|
async listRepositoryFiles(input) {
|
|
88
|
-
const octokit = await this.getApp()
|
|
90
|
+
const octokit = await mapGithubError(()=>getGithubInstallationOctokit(this.getApp(), input.installationId));
|
|
89
91
|
const repository = await this.getRepository({
|
|
90
92
|
installationId: input.installationId,
|
|
91
93
|
repositoryId: input.repositoryId
|
|
@@ -151,7 +153,7 @@ let OctokitGithubApiClient = class OctokitGithubApiClient {
|
|
|
151
153
|
};
|
|
152
154
|
}
|
|
153
155
|
async fetchRepositoryFile(input) {
|
|
154
|
-
const octokit = await this.getApp()
|
|
156
|
+
const octokit = await mapGithubError(()=>getGithubInstallationOctokit(this.getApp(), input.installationId));
|
|
155
157
|
const repository = await this.getRepository({
|
|
156
158
|
installationId: input.installationId,
|
|
157
159
|
repositoryId: input.repositoryId
|
|
@@ -191,6 +193,7 @@ let OctokitGithubApiClient = class OctokitGithubApiClient {
|
|
|
191
193
|
if (typeof response.data.token !== 'string') {
|
|
192
194
|
throw new GithubIntegrationProviderError('malformed-provider-response', 'GitHub installation access token response did not include a token');
|
|
193
195
|
}
|
|
196
|
+
recordInstallationTokenFormat(response.data.token);
|
|
194
197
|
const expiresAt = new Date(response.data.expires_at);
|
|
195
198
|
if (Number.isNaN(expiresAt.getTime())) {
|
|
196
199
|
throw new GithubIntegrationProviderError('malformed-provider-response', 'GitHub installation access token response did not include a valid expiry');
|
|
@@ -208,7 +211,7 @@ let OctokitGithubApiClient = class OctokitGithubApiClient {
|
|
|
208
211
|
this.app = new App({
|
|
209
212
|
appId: config.GITHUB_APP_ID,
|
|
210
213
|
privateKey: normalizedGithubPrivateKey(),
|
|
211
|
-
Octokit: Octokit.defaults({
|
|
214
|
+
Octokit: Octokit.plugin(githubInstallationTokenFormatPlugin).defaults({
|
|
212
215
|
baseUrl: normalizedGithubApiBaseUrl()
|
|
213
216
|
})
|
|
214
217
|
});
|
package/dist/api/client.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/api/client.ts"],"sourcesContent":["import {Buffer} from 'node:buffer';\nimport {MAX_REPOSITORY_FILE_BYTES} from '@shipfox/api-integration-spi';\nimport ky, {HTTPError, TimeoutError} from 'ky';\nimport {App, Octokit, RequestError} from 'octokit';\nimport {config, normalizedGithubApiBaseUrl, normalizedGithubPrivateKey} from '#config.js';\nimport {GithubIntegrationProviderError} from '#core/errors.js';\n\nconst NEXT_PAGE_RE = /[?&]page=(\\d+)/;\nconst TRAILING_SLASHES_RE = /\\/+$/;\nconst MAX_TREE_WALK_DEPTH = 10;\n\nexport interface GithubAccount {\n login: string;\n type: string;\n}\n\nexport interface GithubInstallationDetails {\n id: number;\n account: GithubAccount;\n repositorySelection: string;\n suspendedAt: Date | null;\n htmlUrl: string;\n raw: Record<string, unknown>;\n}\n\nexport interface GithubRepository {\n id: number;\n ownerLogin: string;\n name: string;\n fullName: string;\n defaultBranch: string;\n private: boolean;\n visibility?: string | undefined;\n cloneUrl: string;\n htmlUrl: string;\n}\n\nexport interface GithubRepositoryPage {\n repositories: GithubRepository[];\n nextCursor: string | null;\n}\n\nexport interface GithubFileEntry {\n path: string;\n size: number | null;\n}\n\nexport interface GithubFilePage {\n files: GithubFileEntry[];\n nextCursor: string | null;\n}\n\nexport interface GithubFileContent {\n path: string;\n content: string;\n size: number;\n}\n\nexport interface GithubUserInstallationPage {\n installationIds: number[];\n nextCursor: string | null;\n}\n\nexport interface GithubApiClient {\n exchangeOAuthCode(code: string): Promise<string>;\n listUserInstallations(input: {\n userAccessToken: string;\n cursor?: string | undefined;\n }): Promise<GithubUserInstallationPage>;\n getInstallation(installationId: number): Promise<GithubInstallationDetails>;\n listInstallationRepositories(input: {\n installationId: number;\n limit: number;\n cursor?: string | undefined;\n }): Promise<GithubRepositoryPage>;\n getRepository(input: {installationId: number; repositoryId: number}): Promise<GithubRepository>;\n listRepositoryFiles(input: {\n installationId: number;\n repositoryId: number;\n ref: string;\n prefix: string;\n limit: number;\n cursor?: string | undefined;\n }): Promise<GithubFilePage>;\n fetchRepositoryFile(input: {\n installationId: number;\n repositoryId: number;\n ref: string;\n path: string;\n }): Promise<GithubFileContent>;\n createInstallationAccessToken(input: {\n installationId: number;\n repositoryId: number;\n permissions?: {contents: 'read' | 'write'} | undefined;\n }): Promise<GithubInstallationAccessToken>;\n}\n\nexport interface GithubInstallationAccessToken {\n token: string;\n expiresAt: Date;\n permissions?: Record<string, 'read' | 'write' | 'admin'> | undefined;\n}\n\nexport function createGithubApiClient(): GithubApiClient {\n return new OctokitGithubApiClient();\n}\n\nclass OctokitGithubApiClient implements GithubApiClient {\n private app: App | undefined;\n\n async exchangeOAuthCode(code: string): Promise<string> {\n const body = await mapGithubOAuthError(() =>\n ky\n .post('https://github.com/login/oauth/access_token', {\n headers: {accept: 'application/json'},\n json: {\n client_id: config.GITHUB_APP_CLIENT_ID,\n client_secret: config.GITHUB_APP_CLIENT_SECRET,\n code,\n },\n })\n .json<{access_token?: unknown}>(),\n );\n\n if (typeof body.access_token !== 'string') {\n throw new GithubIntegrationProviderError(\n 'malformed-provider-response',\n 'GitHub OAuth response did not include an access token',\n );\n }\n return body.access_token;\n }\n\n async listUserInstallations(input: {\n userAccessToken: string;\n cursor?: string | undefined;\n }): Promise<GithubUserInstallationPage> {\n const octokit = new Octokit({\n auth: input.userAccessToken,\n baseUrl: normalizedGithubApiBaseUrl(),\n });\n const page = cursorToPage(input.cursor);\n const response = await mapGithubError(() =>\n octokit.request('GET /user/installations', {\n per_page: 100,\n page,\n }),\n );\n\n const installations = response.data.installations ?? [];\n return {\n installationIds: installations.map((installation) => installation.id),\n nextCursor: nextCursorFromLink(response.headers.link),\n };\n }\n\n async getInstallation(installationId: number): Promise<GithubInstallationDetails> {\n const response = await mapGithubError(() =>\n this.getApp().octokit.rest.apps.getInstallation({installation_id: installationId}),\n );\n const data = response.data;\n const account = data.account;\n if (!account) {\n throw new GithubIntegrationProviderError(\n 'malformed-provider-response',\n 'GitHub installation response did not include an account',\n );\n }\n\n const login = 'login' in account ? account.login : account.slug;\n return {\n id: data.id,\n account: {\n login,\n type: 'type' in account ? account.type : 'Enterprise',\n },\n repositorySelection: data.repository_selection,\n suspendedAt: data.suspended_at ? new Date(data.suspended_at) : null,\n htmlUrl: data.html_url,\n raw: data as unknown as Record<string, unknown>,\n };\n }\n\n async listInstallationRepositories(input: {\n installationId: number;\n limit: number;\n cursor?: string | undefined;\n }): Promise<GithubRepositoryPage> {\n const octokit = await this.getApp().getInstallationOctokit(input.installationId);\n const page = cursorToPage(input.cursor);\n const response = await mapGithubError(() =>\n octokit.rest.apps.listReposAccessibleToInstallation({\n per_page: input.limit,\n page,\n }),\n );\n\n return {\n repositories: response.data.repositories.map(toGithubRepository),\n nextCursor: nextCursorFromLink(response.headers.link),\n };\n }\n\n async getRepository(input: {\n installationId: number;\n repositoryId: number;\n }): Promise<GithubRepository> {\n const octokit = await this.getApp().getInstallationOctokit(input.installationId);\n const response = await mapGithubError(() =>\n octokit.request('GET /repositories/{repository_id}', {\n repository_id: input.repositoryId,\n }),\n );\n\n return toGithubRepository(response.data);\n }\n\n async listRepositoryFiles(input: {\n installationId: number;\n repositoryId: number;\n ref: string;\n prefix: string;\n limit: number;\n cursor?: string | undefined;\n }): Promise<GithubFilePage> {\n const octokit = await this.getApp().getInstallationOctokit(input.installationId);\n const repository = await this.getRepository({\n installationId: input.installationId,\n repositoryId: input.repositoryId,\n });\n const owner = repository.ownerLogin;\n const repo = repository.name;\n const startPath = input.prefix.replace(TRAILING_SLASHES_RE, '');\n const offset = input.cursor ? Number.parseInt(input.cursor, 10) : 0;\n const start = Number.isNaN(offset) || offset < 0 ? 0 : offset;\n const collected: GithubFileEntry[] = [];\n const overflowLimit = start + input.limit + 1;\n\n type GetContentData = Awaited<ReturnType<typeof octokit.rest.repos.getContent>>['data'];\n\n const walk = async (path: string, depth: number): Promise<void> => {\n if (collected.length >= overflowLimit) return;\n if (depth > MAX_TREE_WALK_DEPTH) return;\n\n let data: GetContentData;\n try {\n const response = await mapGithubError(\n () =>\n octokit.rest.repos.getContent({\n owner,\n repo,\n path,\n ref: input.ref,\n }),\n 'file-not-found',\n );\n data = response.data;\n } catch (error) {\n if (error instanceof GithubIntegrationProviderError && error.reason === 'file-not-found') {\n return;\n }\n throw error;\n }\n\n if (!Array.isArray(data)) {\n if (data.type === 'file' && data.path) {\n collected.push({path: data.path, size: typeof data.size === 'number' ? data.size : null});\n }\n return;\n }\n\n const entries = [...data].sort((a, b) => (a.path ?? '').localeCompare(b.path ?? ''));\n for (const entry of entries) {\n if (collected.length >= overflowLimit) return;\n if (!entry.path) continue;\n if (entry.type === 'file') {\n collected.push({\n path: entry.path,\n size: typeof entry.size === 'number' ? entry.size : null,\n });\n } else if (entry.type === 'dir') {\n await walk(entry.path, depth + 1);\n }\n }\n };\n\n await walk(startPath, 0);\n\n const sorted = collected.sort((a, b) => a.path.localeCompare(b.path));\n const page = sorted.slice(start, start + input.limit);\n const consumed = start + page.length;\n const hasMore = consumed < sorted.length;\n\n return {\n files: page,\n nextCursor: hasMore ? String(consumed) : null,\n };\n }\n\n async fetchRepositoryFile(input: {\n installationId: number;\n repositoryId: number;\n ref: string;\n path: string;\n }): Promise<GithubFileContent> {\n const octokit = await this.getApp().getInstallationOctokit(input.installationId);\n const repository = await this.getRepository({\n installationId: input.installationId,\n repositoryId: input.repositoryId,\n });\n const response = await mapGithubError(\n () =>\n octokit.rest.repos.getContent({\n owner: repository.ownerLogin,\n repo: repository.name,\n path: input.path,\n ref: input.ref,\n }),\n 'file-not-found',\n );\n const data = response.data;\n\n if (Array.isArray(data) || data.type !== 'file') {\n throw new GithubIntegrationProviderError('file-not-found', 'GitHub path is not a file');\n }\n if (data.size > MAX_REPOSITORY_FILE_BYTES) {\n throw new GithubIntegrationProviderError(\n 'content-too-large',\n 'GitHub file content is larger than the supported limit',\n );\n }\n if (typeof data.content !== 'string' || data.encoding !== 'base64') {\n throw new GithubIntegrationProviderError(\n 'malformed-provider-response',\n 'GitHub file response did not include base64 content',\n );\n }\n\n return {\n path: data.path,\n size: data.size,\n content: Buffer.from(data.content, 'base64').toString('utf8'),\n };\n }\n\n async createInstallationAccessToken(input: {\n installationId: number;\n repositoryId: number;\n permissions?: {contents: 'read' | 'write'} | undefined;\n }): Promise<GithubInstallationAccessToken> {\n const response = await mapGithubError(() =>\n this.getApp().octokit.rest.apps.createInstallationAccessToken({\n installation_id: input.installationId,\n repository_ids: [input.repositoryId],\n permissions: input.permissions ?? {contents: 'read'},\n }),\n );\n\n if (typeof response.data.token !== 'string') {\n throw new GithubIntegrationProviderError(\n 'malformed-provider-response',\n 'GitHub installation access token response did not include a token',\n );\n }\n\n const expiresAt = new Date(response.data.expires_at);\n if (Number.isNaN(expiresAt.getTime())) {\n throw new GithubIntegrationProviderError(\n 'malformed-provider-response',\n 'GitHub installation access token response did not include a valid expiry',\n );\n }\n\n return {\n token: response.data.token,\n expiresAt,\n ...(response.data.permissions === undefined ? {} : {permissions: response.data.permissions}),\n };\n }\n\n private getApp(): App {\n if (!this.app) {\n this.app = new App({\n appId: config.GITHUB_APP_ID,\n privateKey: normalizedGithubPrivateKey(),\n Octokit: Octokit.defaults({baseUrl: normalizedGithubApiBaseUrl()}),\n });\n }\n return this.app;\n }\n}\n\nasync function mapGithubOAuthError<T>(operation: () => Promise<T>): Promise<T> {\n try {\n return await operation();\n } catch (error) {\n if (error instanceof HTTPError) {\n if (error.response.status === 429) {\n throw new GithubIntegrationProviderError(\n 'rate-limited',\n 'GitHub OAuth code exchange failed',\n retryAfterSecondsFromHeaders(error.response.headers),\n );\n }\n if (error.response.status >= 500) {\n throw new GithubIntegrationProviderError(\n 'provider-unavailable',\n 'GitHub OAuth code exchange failed',\n );\n }\n throw new GithubIntegrationProviderError(\n 'access-denied',\n 'GitHub OAuth code exchange failed',\n );\n }\n if (error instanceof TimeoutError) {\n throw new GithubIntegrationProviderError('timeout', 'GitHub OAuth request timed out');\n }\n throw error;\n }\n}\n\nexport async function mapGithubError<T>(\n operation: () => Promise<T>,\n notFoundReason:\n | 'repository-not-found'\n | 'installation-not-found'\n | 'file-not-found' = 'repository-not-found',\n): Promise<T> {\n try {\n return await operation();\n } catch (error) {\n if (error instanceof GithubIntegrationProviderError) throw error;\n if (error instanceof RequestError) {\n if (error.status === 404) {\n throw new GithubIntegrationProviderError(\n notFoundReason,\n error.message,\n undefined,\n error.status,\n );\n }\n if (error.status === 429 || isGithubRateLimitError(error)) {\n throw new GithubIntegrationProviderError(\n 'rate-limited',\n error.message,\n retryAfterSeconds(error),\n error.status,\n );\n }\n if (error.status === 401 || error.status === 403) {\n throw new GithubIntegrationProviderError(\n 'access-denied',\n error.message,\n retryAfterSeconds(error),\n error.status,\n );\n }\n if (error.status >= 500) {\n throw new GithubIntegrationProviderError(\n 'provider-unavailable',\n error.message,\n undefined,\n error.status,\n );\n }\n if (error.status >= 400) {\n throw new GithubIntegrationProviderError(\n 'provider-rejected',\n error.message,\n undefined,\n error.status,\n );\n }\n }\n if (error instanceof Error && error.name === 'AbortError') {\n throw new GithubIntegrationProviderError('timeout', 'GitHub request timed out');\n }\n throw error;\n }\n}\n\nfunction isGithubRateLimitError(error: RequestError): boolean {\n return error.status === 403 && error.response?.headers['x-ratelimit-remaining'] === '0';\n}\n\nfunction retryAfterSeconds(error: RequestError): number | undefined {\n return parseRetryAfterSeconds(error.response?.headers['retry-after']);\n}\n\nfunction retryAfterSecondsFromHeaders(headers: Headers): number | undefined {\n return parseRetryAfterSeconds(headers.get('retry-after'));\n}\n\nfunction parseRetryAfterSeconds(\n retryAfter: string | number | null | undefined,\n): number | undefined {\n if (!retryAfter) return undefined;\n const parsed = Number.parseInt(String(retryAfter), 10);\n return Number.isNaN(parsed) ? undefined : parsed;\n}\n\nfunction cursorToPage(cursor: string | undefined): number {\n if (!cursor) return 1;\n const page = Number.parseInt(cursor, 10);\n return Number.isNaN(page) || page < 1 ? 1 : page;\n}\n\nfunction nextCursorFromLink(link: string | undefined): string | null {\n if (!link) return null;\n const next = link.split(',').find((part) => part.includes('rel=\"next\"'));\n if (!next) return null;\n const match = next.match(NEXT_PAGE_RE);\n return match?.[1] ?? null;\n}\n\nfunction toGithubRepository(repository: {\n id: number;\n owner: {login: string};\n name: string;\n full_name: string;\n default_branch?: string | null | undefined;\n private: boolean;\n visibility?: string | undefined;\n clone_url?: string | null | undefined;\n html_url?: string | null | undefined;\n}): GithubRepository {\n if (!repository.default_branch || !repository.clone_url || !repository.html_url) {\n throw new GithubIntegrationProviderError(\n 'malformed-provider-response',\n 'GitHub repository response is missing required fields',\n );\n }\n return {\n id: repository.id,\n ownerLogin: repository.owner.login,\n name: repository.name,\n fullName: repository.full_name,\n defaultBranch: repository.default_branch,\n private: repository.private,\n visibility: repository.visibility,\n cloneUrl: repository.clone_url,\n htmlUrl: repository.html_url,\n };\n}\n"],"names":["Buffer","MAX_REPOSITORY_FILE_BYTES","ky","HTTPError","TimeoutError","App","Octokit","RequestError","config","normalizedGithubApiBaseUrl","normalizedGithubPrivateKey","GithubIntegrationProviderError","NEXT_PAGE_RE","TRAILING_SLASHES_RE","MAX_TREE_WALK_DEPTH","createGithubApiClient","OctokitGithubApiClient","exchangeOAuthCode","code","body","mapGithubOAuthError","post","headers","accept","json","client_id","GITHUB_APP_CLIENT_ID","client_secret","GITHUB_APP_CLIENT_SECRET","access_token","listUserInstallations","input","octokit","auth","userAccessToken","baseUrl","page","cursorToPage","cursor","response","mapGithubError","request","per_page","installations","data","installationIds","map","installation","id","nextCursor","nextCursorFromLink","link","getInstallation","installationId","getApp","rest","apps","installation_id","account","login","slug","type","repositorySelection","repository_selection","suspendedAt","suspended_at","Date","htmlUrl","html_url","raw","listInstallationRepositories","getInstallationOctokit","listReposAccessibleToInstallation","limit","repositories","toGithubRepository","getRepository","repository_id","repositoryId","listRepositoryFiles","repository","owner","ownerLogin","repo","name","startPath","prefix","replace","offset","Number","parseInt","start","isNaN","collected","overflowLimit","walk","path","depth","length","repos","getContent","ref","error","reason","Array","isArray","push","size","entries","sort","a","b","localeCompare","entry","sorted","slice","consumed","hasMore","files","String","fetchRepositoryFile","content","encoding","from","toString","createInstallationAccessToken","repository_ids","permissions","contents","token","expiresAt","expires_at","getTime","undefined","app","appId","GITHUB_APP_ID","privateKey","defaults","operation","status","retryAfterSecondsFromHeaders","notFoundReason","message","isGithubRateLimitError","retryAfterSeconds","Error","parseRetryAfterSeconds","get","retryAfter","parsed","next","split","find","part","includes","match","default_branch","clone_url","fullName","full_name","defaultBranch","private","visibility","cloneUrl"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../src/api/client.ts"],"sourcesContent":["import {Buffer} from 'node:buffer';\nimport {MAX_REPOSITORY_FILE_BYTES} from '@shipfox/api-integration-spi';\nimport ky, {HTTPError, TimeoutError} from 'ky';\nimport {App, Octokit, RequestError} from 'octokit';\nimport {config, normalizedGithubApiBaseUrl, normalizedGithubPrivateKey} from '#config.js';\nimport {GithubIntegrationProviderError} from '#core/errors.js';\nimport {recordInstallationTokenFormat} from '#metrics/index.js';\nimport {\n getGithubInstallationOctokit,\n githubInstallationTokenFormatPlugin,\n} from './github-octokit.js';\n\nconst NEXT_PAGE_RE = /[?&]page=(\\d+)/;\nconst TRAILING_SLASHES_RE = /\\/+$/;\nconst MAX_TREE_WALK_DEPTH = 10;\n\nexport interface GithubAccount {\n login: string;\n type: string;\n}\n\nexport interface GithubInstallationDetails {\n id: number;\n account: GithubAccount;\n repositorySelection: string;\n suspendedAt: Date | null;\n htmlUrl: string;\n raw: Record<string, unknown>;\n}\n\nexport interface GithubRepository {\n id: number;\n ownerLogin: string;\n name: string;\n fullName: string;\n defaultBranch: string;\n private: boolean;\n visibility?: string | undefined;\n cloneUrl: string;\n htmlUrl: string;\n}\n\nexport interface GithubRepositoryPage {\n repositories: GithubRepository[];\n nextCursor: string | null;\n}\n\nexport interface GithubFileEntry {\n path: string;\n size: number | null;\n}\n\nexport interface GithubFilePage {\n files: GithubFileEntry[];\n nextCursor: string | null;\n}\n\nexport interface GithubFileContent {\n path: string;\n content: string;\n size: number;\n}\n\nexport interface GithubUserInstallationPage {\n installationIds: number[];\n nextCursor: string | null;\n}\n\nexport interface GithubApiClient {\n exchangeOAuthCode(code: string): Promise<string>;\n listUserInstallations(input: {\n userAccessToken: string;\n cursor?: string | undefined;\n }): Promise<GithubUserInstallationPage>;\n getInstallation(installationId: number): Promise<GithubInstallationDetails>;\n listInstallationRepositories(input: {\n installationId: number;\n limit: number;\n cursor?: string | undefined;\n }): Promise<GithubRepositoryPage>;\n getRepository(input: {installationId: number; repositoryId: number}): Promise<GithubRepository>;\n listRepositoryFiles(input: {\n installationId: number;\n repositoryId: number;\n ref: string;\n prefix: string;\n limit: number;\n cursor?: string | undefined;\n }): Promise<GithubFilePage>;\n fetchRepositoryFile(input: {\n installationId: number;\n repositoryId: number;\n ref: string;\n path: string;\n }): Promise<GithubFileContent>;\n createInstallationAccessToken(input: {\n installationId: number;\n repositoryId: number;\n permissions?: {contents: 'read' | 'write'} | undefined;\n }): Promise<GithubInstallationAccessToken>;\n}\n\nexport interface GithubInstallationAccessToken {\n token: string;\n expiresAt: Date;\n permissions?: Record<string, 'read' | 'write' | 'admin'> | undefined;\n}\n\nexport function createGithubApiClient(): GithubApiClient {\n return new OctokitGithubApiClient();\n}\n\nclass OctokitGithubApiClient implements GithubApiClient {\n private app: App | undefined;\n\n async exchangeOAuthCode(code: string): Promise<string> {\n const body = await mapGithubOAuthError(() =>\n ky\n .post('https://github.com/login/oauth/access_token', {\n headers: {accept: 'application/json'},\n json: {\n client_id: config.GITHUB_APP_CLIENT_ID,\n client_secret: config.GITHUB_APP_CLIENT_SECRET,\n code,\n },\n })\n .json<{access_token?: unknown}>(),\n );\n\n if (typeof body.access_token !== 'string') {\n throw new GithubIntegrationProviderError(\n 'malformed-provider-response',\n 'GitHub OAuth response did not include an access token',\n );\n }\n return body.access_token;\n }\n\n async listUserInstallations(input: {\n userAccessToken: string;\n cursor?: string | undefined;\n }): Promise<GithubUserInstallationPage> {\n const octokit = new Octokit({\n auth: input.userAccessToken,\n baseUrl: normalizedGithubApiBaseUrl(),\n });\n const page = cursorToPage(input.cursor);\n const response = await mapGithubError(() =>\n octokit.request('GET /user/installations', {\n per_page: 100,\n page,\n }),\n );\n\n const installations = response.data.installations ?? [];\n return {\n installationIds: installations.map((installation) => installation.id),\n nextCursor: nextCursorFromLink(response.headers.link),\n };\n }\n\n async getInstallation(installationId: number): Promise<GithubInstallationDetails> {\n const response = await mapGithubError(() =>\n this.getApp().octokit.rest.apps.getInstallation({installation_id: installationId}),\n );\n const data = response.data;\n const account = data.account;\n if (!account) {\n throw new GithubIntegrationProviderError(\n 'malformed-provider-response',\n 'GitHub installation response did not include an account',\n );\n }\n\n const login = 'login' in account ? account.login : account.slug;\n return {\n id: data.id,\n account: {\n login,\n type: 'type' in account ? account.type : 'Enterprise',\n },\n repositorySelection: data.repository_selection,\n suspendedAt: data.suspended_at ? new Date(data.suspended_at) : null,\n htmlUrl: data.html_url,\n raw: data as unknown as Record<string, unknown>,\n };\n }\n\n async listInstallationRepositories(input: {\n installationId: number;\n limit: number;\n cursor?: string | undefined;\n }): Promise<GithubRepositoryPage> {\n const octokit = await mapGithubError(() =>\n getGithubInstallationOctokit(this.getApp(), input.installationId),\n );\n const page = cursorToPage(input.cursor);\n const response = await mapGithubError(() =>\n octokit.rest.apps.listReposAccessibleToInstallation({\n per_page: input.limit,\n page,\n }),\n );\n\n return {\n repositories: response.data.repositories.map(toGithubRepository),\n nextCursor: nextCursorFromLink(response.headers.link),\n };\n }\n\n async getRepository(input: {\n installationId: number;\n repositoryId: number;\n }): Promise<GithubRepository> {\n const octokit = await mapGithubError(() =>\n getGithubInstallationOctokit(this.getApp(), input.installationId),\n );\n const response = await mapGithubError(() =>\n octokit.request('GET /repositories/{repository_id}', {\n repository_id: input.repositoryId,\n }),\n );\n\n return toGithubRepository(response.data);\n }\n\n async listRepositoryFiles(input: {\n installationId: number;\n repositoryId: number;\n ref: string;\n prefix: string;\n limit: number;\n cursor?: string | undefined;\n }): Promise<GithubFilePage> {\n const octokit = await mapGithubError(() =>\n getGithubInstallationOctokit(this.getApp(), input.installationId),\n );\n const repository = await this.getRepository({\n installationId: input.installationId,\n repositoryId: input.repositoryId,\n });\n const owner = repository.ownerLogin;\n const repo = repository.name;\n const startPath = input.prefix.replace(TRAILING_SLASHES_RE, '');\n const offset = input.cursor ? Number.parseInt(input.cursor, 10) : 0;\n const start = Number.isNaN(offset) || offset < 0 ? 0 : offset;\n const collected: GithubFileEntry[] = [];\n const overflowLimit = start + input.limit + 1;\n\n type GetContentData = Awaited<ReturnType<typeof octokit.rest.repos.getContent>>['data'];\n\n const walk = async (path: string, depth: number): Promise<void> => {\n if (collected.length >= overflowLimit) return;\n if (depth > MAX_TREE_WALK_DEPTH) return;\n\n let data: GetContentData;\n try {\n const response = await mapGithubError(\n () =>\n octokit.rest.repos.getContent({\n owner,\n repo,\n path,\n ref: input.ref,\n }),\n 'file-not-found',\n );\n data = response.data;\n } catch (error) {\n if (error instanceof GithubIntegrationProviderError && error.reason === 'file-not-found') {\n return;\n }\n throw error;\n }\n\n if (!Array.isArray(data)) {\n if (data.type === 'file' && data.path) {\n collected.push({path: data.path, size: typeof data.size === 'number' ? data.size : null});\n }\n return;\n }\n\n const entries = [...data].sort((a, b) => (a.path ?? '').localeCompare(b.path ?? ''));\n for (const entry of entries) {\n if (collected.length >= overflowLimit) return;\n if (!entry.path) continue;\n if (entry.type === 'file') {\n collected.push({\n path: entry.path,\n size: typeof entry.size === 'number' ? entry.size : null,\n });\n } else if (entry.type === 'dir') {\n await walk(entry.path, depth + 1);\n }\n }\n };\n\n await walk(startPath, 0);\n\n const sorted = collected.sort((a, b) => a.path.localeCompare(b.path));\n const page = sorted.slice(start, start + input.limit);\n const consumed = start + page.length;\n const hasMore = consumed < sorted.length;\n\n return {\n files: page,\n nextCursor: hasMore ? String(consumed) : null,\n };\n }\n\n async fetchRepositoryFile(input: {\n installationId: number;\n repositoryId: number;\n ref: string;\n path: string;\n }): Promise<GithubFileContent> {\n const octokit = await mapGithubError(() =>\n getGithubInstallationOctokit(this.getApp(), input.installationId),\n );\n const repository = await this.getRepository({\n installationId: input.installationId,\n repositoryId: input.repositoryId,\n });\n const response = await mapGithubError(\n () =>\n octokit.rest.repos.getContent({\n owner: repository.ownerLogin,\n repo: repository.name,\n path: input.path,\n ref: input.ref,\n }),\n 'file-not-found',\n );\n const data = response.data;\n\n if (Array.isArray(data) || data.type !== 'file') {\n throw new GithubIntegrationProviderError('file-not-found', 'GitHub path is not a file');\n }\n if (data.size > MAX_REPOSITORY_FILE_BYTES) {\n throw new GithubIntegrationProviderError(\n 'content-too-large',\n 'GitHub file content is larger than the supported limit',\n );\n }\n if (typeof data.content !== 'string' || data.encoding !== 'base64') {\n throw new GithubIntegrationProviderError(\n 'malformed-provider-response',\n 'GitHub file response did not include base64 content',\n );\n }\n\n return {\n path: data.path,\n size: data.size,\n content: Buffer.from(data.content, 'base64').toString('utf8'),\n };\n }\n\n async createInstallationAccessToken(input: {\n installationId: number;\n repositoryId: number;\n permissions?: {contents: 'read' | 'write'} | undefined;\n }): Promise<GithubInstallationAccessToken> {\n const response = await mapGithubError(() =>\n this.getApp().octokit.rest.apps.createInstallationAccessToken({\n installation_id: input.installationId,\n repository_ids: [input.repositoryId],\n permissions: input.permissions ?? {contents: 'read'},\n }),\n );\n\n if (typeof response.data.token !== 'string') {\n throw new GithubIntegrationProviderError(\n 'malformed-provider-response',\n 'GitHub installation access token response did not include a token',\n );\n }\n\n recordInstallationTokenFormat(response.data.token);\n\n const expiresAt = new Date(response.data.expires_at);\n if (Number.isNaN(expiresAt.getTime())) {\n throw new GithubIntegrationProviderError(\n 'malformed-provider-response',\n 'GitHub installation access token response did not include a valid expiry',\n );\n }\n\n return {\n token: response.data.token,\n expiresAt,\n ...(response.data.permissions === undefined ? {} : {permissions: response.data.permissions}),\n };\n }\n\n private getApp(): App {\n if (!this.app) {\n this.app = new App({\n appId: config.GITHUB_APP_ID,\n privateKey: normalizedGithubPrivateKey(),\n Octokit: Octokit.plugin(githubInstallationTokenFormatPlugin).defaults({\n baseUrl: normalizedGithubApiBaseUrl(),\n }),\n });\n }\n return this.app;\n }\n}\n\nasync function mapGithubOAuthError<T>(operation: () => Promise<T>): Promise<T> {\n try {\n return await operation();\n } catch (error) {\n if (error instanceof HTTPError) {\n if (error.response.status === 429) {\n throw new GithubIntegrationProviderError(\n 'rate-limited',\n 'GitHub OAuth code exchange failed',\n retryAfterSecondsFromHeaders(error.response.headers),\n );\n }\n if (error.response.status >= 500) {\n throw new GithubIntegrationProviderError(\n 'provider-unavailable',\n 'GitHub OAuth code exchange failed',\n );\n }\n throw new GithubIntegrationProviderError(\n 'access-denied',\n 'GitHub OAuth code exchange failed',\n );\n }\n if (error instanceof TimeoutError) {\n throw new GithubIntegrationProviderError('timeout', 'GitHub OAuth request timed out');\n }\n throw error;\n }\n}\n\nexport async function mapGithubError<T>(\n operation: () => Promise<T>,\n notFoundReason:\n | 'repository-not-found'\n | 'installation-not-found'\n | 'file-not-found' = 'repository-not-found',\n): Promise<T> {\n try {\n return await operation();\n } catch (error) {\n if (error instanceof GithubIntegrationProviderError) throw error;\n if (error instanceof RequestError) {\n if (error.status === 404) {\n throw new GithubIntegrationProviderError(\n notFoundReason,\n error.message,\n undefined,\n error.status,\n );\n }\n if (error.status === 429 || isGithubRateLimitError(error)) {\n throw new GithubIntegrationProviderError(\n 'rate-limited',\n error.message,\n retryAfterSeconds(error),\n error.status,\n );\n }\n if (error.status === 401 || error.status === 403) {\n throw new GithubIntegrationProviderError(\n 'access-denied',\n error.message,\n retryAfterSeconds(error),\n error.status,\n );\n }\n if (error.status >= 500) {\n throw new GithubIntegrationProviderError(\n 'provider-unavailable',\n error.message,\n undefined,\n error.status,\n );\n }\n if (error.status >= 400) {\n throw new GithubIntegrationProviderError(\n 'provider-rejected',\n error.message,\n undefined,\n error.status,\n );\n }\n }\n if (error instanceof Error && error.name === 'AbortError') {\n throw new GithubIntegrationProviderError('timeout', 'GitHub request timed out');\n }\n throw error;\n }\n}\n\nfunction isGithubRateLimitError(error: RequestError): boolean {\n return error.status === 403 && error.response?.headers['x-ratelimit-remaining'] === '0';\n}\n\nfunction retryAfterSeconds(error: RequestError): number | undefined {\n return parseRetryAfterSeconds(error.response?.headers['retry-after']);\n}\n\nfunction retryAfterSecondsFromHeaders(headers: Headers): number | undefined {\n return parseRetryAfterSeconds(headers.get('retry-after'));\n}\n\nfunction parseRetryAfterSeconds(\n retryAfter: string | number | null | undefined,\n): number | undefined {\n if (!retryAfter) return undefined;\n const parsed = Number.parseInt(String(retryAfter), 10);\n return Number.isNaN(parsed) ? undefined : parsed;\n}\n\nfunction cursorToPage(cursor: string | undefined): number {\n if (!cursor) return 1;\n const page = Number.parseInt(cursor, 10);\n return Number.isNaN(page) || page < 1 ? 1 : page;\n}\n\nfunction nextCursorFromLink(link: string | undefined): string | null {\n if (!link) return null;\n const next = link.split(',').find((part) => part.includes('rel=\"next\"'));\n if (!next) return null;\n const match = next.match(NEXT_PAGE_RE);\n return match?.[1] ?? null;\n}\n\nfunction toGithubRepository(repository: {\n id: number;\n owner: {login: string};\n name: string;\n full_name: string;\n default_branch?: string | null | undefined;\n private: boolean;\n visibility?: string | undefined;\n clone_url?: string | null | undefined;\n html_url?: string | null | undefined;\n}): GithubRepository {\n if (!repository.default_branch || !repository.clone_url || !repository.html_url) {\n throw new GithubIntegrationProviderError(\n 'malformed-provider-response',\n 'GitHub repository response is missing required fields',\n );\n }\n return {\n id: repository.id,\n ownerLogin: repository.owner.login,\n name: repository.name,\n fullName: repository.full_name,\n defaultBranch: repository.default_branch,\n private: repository.private,\n visibility: repository.visibility,\n cloneUrl: repository.clone_url,\n htmlUrl: repository.html_url,\n };\n}\n"],"names":["Buffer","MAX_REPOSITORY_FILE_BYTES","ky","HTTPError","TimeoutError","App","Octokit","RequestError","config","normalizedGithubApiBaseUrl","normalizedGithubPrivateKey","GithubIntegrationProviderError","recordInstallationTokenFormat","getGithubInstallationOctokit","githubInstallationTokenFormatPlugin","NEXT_PAGE_RE","TRAILING_SLASHES_RE","MAX_TREE_WALK_DEPTH","createGithubApiClient","OctokitGithubApiClient","exchangeOAuthCode","code","body","mapGithubOAuthError","post","headers","accept","json","client_id","GITHUB_APP_CLIENT_ID","client_secret","GITHUB_APP_CLIENT_SECRET","access_token","listUserInstallations","input","octokit","auth","userAccessToken","baseUrl","page","cursorToPage","cursor","response","mapGithubError","request","per_page","installations","data","installationIds","map","installation","id","nextCursor","nextCursorFromLink","link","getInstallation","installationId","getApp","rest","apps","installation_id","account","login","slug","type","repositorySelection","repository_selection","suspendedAt","suspended_at","Date","htmlUrl","html_url","raw","listInstallationRepositories","listReposAccessibleToInstallation","limit","repositories","toGithubRepository","getRepository","repository_id","repositoryId","listRepositoryFiles","repository","owner","ownerLogin","repo","name","startPath","prefix","replace","offset","Number","parseInt","start","isNaN","collected","overflowLimit","walk","path","depth","length","repos","getContent","ref","error","reason","Array","isArray","push","size","entries","sort","a","b","localeCompare","entry","sorted","slice","consumed","hasMore","files","String","fetchRepositoryFile","content","encoding","from","toString","createInstallationAccessToken","repository_ids","permissions","contents","token","expiresAt","expires_at","getTime","undefined","app","appId","GITHUB_APP_ID","privateKey","plugin","defaults","operation","status","retryAfterSecondsFromHeaders","notFoundReason","message","isGithubRateLimitError","retryAfterSeconds","Error","parseRetryAfterSeconds","get","retryAfter","parsed","next","split","find","part","includes","match","default_branch","clone_url","fullName","full_name","defaultBranch","private","visibility","cloneUrl"],"mappings":"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"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { type App, Octokit } from 'octokit';
|
|
2
|
+
type GithubInstallationTokenFormatOverride = 'enabled' | 'disabled' | undefined;
|
|
3
|
+
export declare function createGithubInstallationTokenFormatPlugin(override: GithubInstallationTokenFormatOverride): Parameters<typeof Octokit.plugin>[0];
|
|
4
|
+
export declare const githubInstallationTokenFormatPlugin: Parameters<typeof Octokit.plugin>[0];
|
|
5
|
+
export declare function getGithubInstallationOctokit(app: App, installationId: number, baseUrl?: string): Promise<Octokit>;
|
|
6
|
+
export {};
|
|
7
|
+
//# sourceMappingURL=github-octokit.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"github-octokit.d.ts","sourceRoot":"","sources":["../../src/api/github-octokit.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,KAAK,GAAG,EAAE,OAAO,EAAC,MAAM,SAAS,CAAC;AAM1C,KAAK,qCAAqC,GAAG,SAAS,GAAG,UAAU,GAAG,SAAS,CAAC;AAEhF,wBAAgB,yCAAyC,CACvD,QAAQ,EAAE,qCAAqC,GAC9C,UAAU,CAAC,OAAO,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CActC;AAED,eAAO,MAAM,mCAAmC,EAAE,UAAU,CAAC,OAAO,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,CACQ,CAAC;AAE9F,wBAAsB,4BAA4B,CAChD,GAAG,EAAE,GAAG,EACR,cAAc,EAAE,MAAM,EACtB,OAAO,SAA+B,GACrC,OAAO,CAAC,OAAO,CAAC,CAelB"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { Octokit } from 'octokit';
|
|
2
|
+
import { config, normalizedGithubApiBaseUrl } from '#config.js';
|
|
3
|
+
import { GithubIntegrationProviderError } from '#core/errors.js';
|
|
4
|
+
import { recordInstallationTokenFormat } from '#metrics/index.js';
|
|
5
|
+
const INSTALLATION_TOKEN_REQUEST_PATH = /\/app\/installations\/[^/]+\/access_tokens(?:\?|$)/u;
|
|
6
|
+
export function createGithubInstallationTokenFormatPlugin(override) {
|
|
7
|
+
return (octokit)=>{
|
|
8
|
+
octokit.hook.before('request', (options)=>{
|
|
9
|
+
if (!override || options.method !== 'POST' || !INSTALLATION_TOKEN_REQUEST_PATH.test(options.url)) {
|
|
10
|
+
return;
|
|
11
|
+
}
|
|
12
|
+
options.headers['x-github-stateless-s2s-token'] = override;
|
|
13
|
+
});
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
export const githubInstallationTokenFormatPlugin = createGithubInstallationTokenFormatPlugin(config.GITHUB_INSTALLATION_TOKEN_FORMAT_OVERRIDE);
|
|
17
|
+
export async function getGithubInstallationOctokit(app, installationId, baseUrl = normalizedGithubApiBaseUrl()) {
|
|
18
|
+
const authentication = await app.octokit.auth({
|
|
19
|
+
type: 'installation',
|
|
20
|
+
installationId
|
|
21
|
+
});
|
|
22
|
+
if (typeof authentication.token !== 'string') {
|
|
23
|
+
throw new GithubIntegrationProviderError('malformed-provider-response', 'GitHub installation authentication did not include a token');
|
|
24
|
+
}
|
|
25
|
+
recordInstallationTokenFormat(authentication.token);
|
|
26
|
+
return new Octokit({
|
|
27
|
+
auth: authentication.token,
|
|
28
|
+
baseUrl
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
//# sourceMappingURL=github-octokit.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/api/github-octokit.ts"],"sourcesContent":["import {type App, Octokit} from 'octokit';\nimport {config, normalizedGithubApiBaseUrl} from '#config.js';\nimport {GithubIntegrationProviderError} from '#core/errors.js';\nimport {recordInstallationTokenFormat} from '#metrics/index.js';\n\nconst INSTALLATION_TOKEN_REQUEST_PATH = /\\/app\\/installations\\/[^/]+\\/access_tokens(?:\\?|$)/u;\ntype GithubInstallationTokenFormatOverride = 'enabled' | 'disabled' | undefined;\n\nexport function createGithubInstallationTokenFormatPlugin(\n override: GithubInstallationTokenFormatOverride,\n): Parameters<typeof Octokit.plugin>[0] {\n return (octokit) => {\n octokit.hook.before('request', (options) => {\n if (\n !override ||\n options.method !== 'POST' ||\n !INSTALLATION_TOKEN_REQUEST_PATH.test(options.url)\n ) {\n return;\n }\n\n options.headers['x-github-stateless-s2s-token'] = override;\n });\n };\n}\n\nexport const githubInstallationTokenFormatPlugin: Parameters<typeof Octokit.plugin>[0] =\n createGithubInstallationTokenFormatPlugin(config.GITHUB_INSTALLATION_TOKEN_FORMAT_OVERRIDE);\n\nexport async function getGithubInstallationOctokit(\n app: App,\n installationId: number,\n baseUrl = normalizedGithubApiBaseUrl(),\n): Promise<Octokit> {\n const authentication = (await app.octokit.auth({\n type: 'installation',\n installationId,\n })) as {token?: unknown};\n\n if (typeof authentication.token !== 'string') {\n throw new GithubIntegrationProviderError(\n 'malformed-provider-response',\n 'GitHub installation authentication did not include a token',\n );\n }\n\n recordInstallationTokenFormat(authentication.token);\n return new Octokit({auth: authentication.token, baseUrl});\n}\n"],"names":["Octokit","config","normalizedGithubApiBaseUrl","GithubIntegrationProviderError","recordInstallationTokenFormat","INSTALLATION_TOKEN_REQUEST_PATH","createGithubInstallationTokenFormatPlugin","override","octokit","hook","before","options","method","test","url","headers","githubInstallationTokenFormatPlugin","GITHUB_INSTALLATION_TOKEN_FORMAT_OVERRIDE","getGithubInstallationOctokit","app","installationId","baseUrl","authentication","auth","type","token"],"mappings":"AAAA,SAAkBA,OAAO,QAAO,UAAU;AAC1C,SAAQC,MAAM,EAAEC,0BAA0B,QAAO,aAAa;AAC9D,SAAQC,8BAA8B,QAAO,kBAAkB;AAC/D,SAAQC,6BAA6B,QAAO,oBAAoB;AAEhE,MAAMC,kCAAkC;AAGxC,OAAO,SAASC,0CACdC,QAA+C;IAE/C,OAAO,CAACC;QACNA,QAAQC,IAAI,CAACC,MAAM,CAAC,WAAW,CAACC;YAC9B,IACE,CAACJ,YACDI,QAAQC,MAAM,KAAK,UACnB,CAACP,gCAAgCQ,IAAI,CAACF,QAAQG,GAAG,GACjD;gBACA;YACF;YAEAH,QAAQI,OAAO,CAAC,+BAA+B,GAAGR;QACpD;IACF;AACF;AAEA,OAAO,MAAMS,sCACXV,0CAA0CL,OAAOgB,yCAAyC,EAAE;AAE9F,OAAO,eAAeC,6BACpBC,GAAQ,EACRC,cAAsB,EACtBC,UAAUnB,4BAA4B;IAEtC,MAAMoB,iBAAkB,MAAMH,IAAIX,OAAO,CAACe,IAAI,CAAC;QAC7CC,MAAM;QACNJ;IACF;IAEA,IAAI,OAAOE,eAAeG,KAAK,KAAK,UAAU;QAC5C,MAAM,IAAItB,+BACR,+BACA;IAEJ;IAEAC,8BAA8BkB,eAAeG,KAAK;IAClD,OAAO,IAAIzB,QAAQ;QAACuB,MAAMD,eAAeG,KAAK;QAAEJ;IAAO;AACzD"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"installation-token-provider.d.ts","sourceRoot":"","sources":["../../src/api/installation-token-provider.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,8BAA8B,EAAC,MAAM,8BAA8B,CAAC;AAOjF,OAAO,EAAC,KAAK,6BAA6B,EAAiB,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"installation-token-provider.d.ts","sourceRoot":"","sources":["../../src/api/installation-token-provider.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,8BAA8B,EAAC,MAAM,8BAA8B,CAAC;AAOjF,OAAO,EAAC,KAAK,6BAA6B,EAAiB,MAAM,aAAa,CAAC;AAM/E,OAAO,EACL,KAAK,sBAAsB,EAC3B,KAAK,4BAA4B,EAEjC,KAAK,mCAAmC,EACzC,MAAM,sCAAsC,CAAC;AAE9C,MAAM,WAAW,+BAA+B;IAC9C,0BAA0B,CAAC,cAAc,EAAE,MAAM,GAAG,OAAO,CAAC,6BAA6B,CAAC,CAAC;CAC5F;AAED,MAAM,WAAW,sCAAsC;IACrD,KAAK,CAAC,EAAE,sBAAsB,GAAG,SAAS,CAAC;IAC3C,4BAA4B,CAAC,EAAE,8BAA8B,GAAG,SAAS,CAAC;IAC1E,WAAW,CAAC,EAAE,4BAA4B,GAAG,SAAS,CAAC;IACvD,QAAQ,CAAC,EAAE,mCAAmC,CAAC,UAAU,CAAC,GAAG,SAAS,CAAC;IACvE,GAAG,CAAC,EAAE,CAAC,MAAM,IAAI,CAAC,GAAG,SAAS,CAAC;CAChC;AAED,wBAAgB,qCAAqC,CACnD,OAAO,GAAE,sCAA2C,GACnD,+BAA+B,CAEjC;AAqLD,wBAAgB,mCAAmC,CAAC,MAAM,EAAE;IAC1D,WAAW,EAAE,MAAM,CAAC;IACpB,cAAc,EAAE,MAAM,CAAC;IACvB,aAAa,EAAE,CAAC,MAAM,EAAE;QAAC,WAAW,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAC,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;CACtF,GAAG,OAAO,CAAC,MAAM,CAAC,CAKlB"}
|
|
@@ -3,8 +3,9 @@ import { config, normalizedGithubApiBaseUrl, normalizedGithubPrivateKey } from '
|
|
|
3
3
|
import { GithubIntegrationProviderError } from '#core/errors.js';
|
|
4
4
|
import { withInstallationTokenLock } from '#db/installation-token-lock.js';
|
|
5
5
|
import { getGithubInstallationByInstallationId } from '#db/installations.js';
|
|
6
|
-
import { recordInstallationTokenLookup } from '#metrics/index.js';
|
|
6
|
+
import { recordInstallationTokenFormat, recordInstallationTokenLookup } from '#metrics/index.js';
|
|
7
7
|
import { mapGithubError } from './client.js';
|
|
8
|
+
import { githubInstallationTokenFormatPlugin } from './github-octokit.js';
|
|
8
9
|
import { githubInstallationTokenNamespace, TOKEN_REFRESH_MARGIN_MS } from './installation-token-envelope.js';
|
|
9
10
|
import { SharedInstallationTokenCache } from './shared-installation-token-cache.js';
|
|
10
11
|
export function createGithubInstallationTokenProvider(options = {}) {
|
|
@@ -24,6 +25,7 @@ let OctokitGithubInstallationTokenProvider = class OctokitGithubInstallationToke
|
|
|
24
25
|
if (typeof response.data.token !== 'string') {
|
|
25
26
|
throw new GithubIntegrationProviderError('malformed-provider-response', 'GitHub installation access token response did not include a token');
|
|
26
27
|
}
|
|
28
|
+
recordInstallationTokenFormat(response.data.token);
|
|
27
29
|
const expiresAt = new Date(response.data.expires_at);
|
|
28
30
|
if (Number.isNaN(expiresAt.getTime())) {
|
|
29
31
|
throw new GithubIntegrationProviderError('malformed-provider-response', 'GitHub installation access token response did not include a valid expiry');
|
|
@@ -41,7 +43,7 @@ let OctokitGithubInstallationTokenProvider = class OctokitGithubInstallationToke
|
|
|
41
43
|
this.app = new App({
|
|
42
44
|
appId: config.GITHUB_APP_ID,
|
|
43
45
|
privateKey: normalizedGithubPrivateKey(),
|
|
44
|
-
Octokit: Octokit.defaults({
|
|
46
|
+
Octokit: Octokit.plugin(githubInstallationTokenFormatPlugin).defaults({
|
|
45
47
|
baseUrl: normalizedGithubApiBaseUrl(),
|
|
46
48
|
throttle: {
|
|
47
49
|
onRateLimit: (_retryAfter, _options, _octokit, retryCount)=>retryCount === 0,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/api/installation-token-provider.ts"],"sourcesContent":["import type {GetIntegrationConnectionByIdFn} from '@shipfox/api-integration-spi';\nimport {App, Octokit} from 'octokit';\nimport {config, normalizedGithubApiBaseUrl, normalizedGithubPrivateKey} from '#config.js';\nimport {GithubIntegrationProviderError} from '#core/errors.js';\nimport {withInstallationTokenLock} from '#db/installation-token-lock.js';\nimport {getGithubInstallationByInstallationId} from '#db/installations.js';\nimport {recordInstallationTokenLookup} from '#metrics/index.js';\nimport {type GithubInstallationAccessToken, mapGithubError} from './client.js';\nimport {\n githubInstallationTokenNamespace,\n TOKEN_REFRESH_MARGIN_MS,\n} from './installation-token-envelope.js';\nimport {\n type InstallationTokenCache,\n type InstallationTokenSecretStore,\n SharedInstallationTokenCache,\n type SharedInstallationTokenCacheOptions,\n} from './shared-installation-token-cache.js';\n\nexport interface GithubInstallationTokenProvider {\n getInstallationAccessToken(installationId: number): Promise<GithubInstallationAccessToken>;\n}\n\nexport interface GithubInstallationTokenProviderOptions {\n cache?: InstallationTokenCache | undefined;\n getIntegrationConnectionById?: GetIntegrationConnectionByIdFn | undefined;\n secretStore?: InstallationTokenSecretStore | undefined;\n withLock?: SharedInstallationTokenCacheOptions['withLock'] | undefined;\n now?: (() => Date) | undefined;\n}\n\nexport function createGithubInstallationTokenProvider(\n options: GithubInstallationTokenProviderOptions = {},\n): GithubInstallationTokenProvider {\n return new OctokitGithubInstallationTokenProvider(createInstallationTokenCache(options));\n}\n\nclass OctokitGithubInstallationTokenProvider implements GithubInstallationTokenProvider {\n private app: App | undefined;\n\n constructor(\n private readonly cache: InstallationTokenCache = new InMemoryInstallationTokenCache(),\n ) {}\n\n getInstallationAccessToken(installationId: number): Promise<GithubInstallationAccessToken> {\n return this.cache.getOrMint(installationId, () =>\n this.mintInstallationAccessToken(installationId),\n );\n }\n\n private async mintInstallationAccessToken(\n installationId: number,\n ): Promise<GithubInstallationAccessToken> {\n const response = await mapGithubError(\n () =>\n this.getApp().octokit.rest.apps.createInstallationAccessToken({\n installation_id: installationId,\n }),\n 'installation-not-found',\n );\n\n if (typeof response.data.token !== 'string') {\n throw new GithubIntegrationProviderError(\n 'malformed-provider-response',\n 'GitHub installation access token response did not include a token',\n );\n }\n\n const expiresAt = new Date(response.data.expires_at);\n if (Number.isNaN(expiresAt.getTime())) {\n throw new GithubIntegrationProviderError(\n 'malformed-provider-response',\n 'GitHub installation access token response did not include a valid expiry',\n );\n }\n\n return {\n token: response.data.token,\n expiresAt,\n ...(response.data.permissions === undefined ? {} : {permissions: response.data.permissions}),\n };\n }\n\n private getApp(): App {\n if (!this.app) {\n this.app = new App({\n appId: config.GITHUB_APP_ID,\n privateKey: normalizedGithubPrivateKey(),\n Octokit: Octokit.defaults({\n baseUrl: normalizedGithubApiBaseUrl(),\n throttle: {\n onRateLimit: (\n _retryAfter: number,\n _options: unknown,\n _octokit: unknown,\n retryCount: number,\n ) => retryCount === 0,\n onSecondaryRateLimit: (\n _retryAfter: number,\n _options: unknown,\n _octokit: unknown,\n retryCount: number,\n ) => retryCount === 0,\n },\n }),\n });\n }\n return this.app;\n }\n}\n\nclass InMemoryInstallationTokenCache implements InstallationTokenCache {\n private readonly tokens = new Map<number, GithubInstallationAccessToken>();\n private readonly inFlightMints = new Map<number, Promise<GithubInstallationAccessToken>>();\n\n constructor(\n private readonly options: {\n refreshMarginMs: number;\n now: () => Date;\n } = {\n refreshMarginMs: TOKEN_REFRESH_MARGIN_MS,\n now: () => new Date(),\n },\n ) {}\n\n getOrMint(\n installationId: number,\n mint: () => Promise<GithubInstallationAccessToken>,\n ): Promise<GithubInstallationAccessToken> {\n const cached = this.tokens.get(installationId);\n if (cached && !this.isInsideRefreshMargin(cached.expiresAt)) {\n recordInstallationTokenLookup('ram-hit');\n return Promise.resolve(cached);\n }\n\n const inFlightMint = this.inFlightMints.get(installationId);\n if (inFlightMint) return inFlightMint;\n\n const freshToken = mint()\n .then((token) => {\n this.tokens.set(installationId, token);\n return token;\n })\n .finally(() => {\n this.inFlightMints.delete(installationId);\n });\n this.inFlightMints.set(installationId, freshToken);\n return freshToken;\n }\n\n private isInsideRefreshMargin(expiresAt: Date): boolean {\n return expiresAt.getTime() <= this.options.now().getTime() + this.options.refreshMarginMs;\n }\n}\n\nclass TieredInstallationTokenCache implements InstallationTokenCache {\n constructor(\n private readonly ram: InstallationTokenCache,\n private readonly shared: InstallationTokenCache,\n ) {}\n\n getOrMint(\n installationId: number,\n mint: () => Promise<GithubInstallationAccessToken>,\n ): Promise<GithubInstallationAccessToken> {\n return this.ram.getOrMint(installationId, () => this.shared.getOrMint(installationId, mint));\n }\n}\n\nfunction createInstallationTokenCache(\n options: GithubInstallationTokenProviderOptions,\n): InstallationTokenCache {\n if (options.cache) return options.cache;\n\n const ram = new InMemoryInstallationTokenCache({\n refreshMarginMs: TOKEN_REFRESH_MARGIN_MS,\n now: options.now ?? (() => new Date()),\n });\n if (!options.getIntegrationConnectionById || !options.secretStore) return ram;\n\n const shared = new SharedInstallationTokenCache({\n secretStore: options.secretStore,\n withLock: options.withLock ?? withInstallationTokenLock,\n resolveWorkspaceId: createGithubInstallationWorkspaceResolver(\n options.getIntegrationConnectionById,\n ),\n now: options.now,\n });\n return new TieredInstallationTokenCache(ram, shared);\n}\n\nfunction createGithubInstallationWorkspaceResolver(\n getIntegrationConnectionById: GetIntegrationConnectionByIdFn,\n) {\n return async (installationId: number): Promise<string> => {\n const installation = await getGithubInstallationByInstallationId(String(installationId));\n if (!installation) {\n throw new GithubIntegrationProviderError(\n 'installation-not-found',\n `GitHub installation not found: ${installationId}`,\n );\n }\n\n const connection = await getIntegrationConnectionById(installation.connectionId);\n if (!connection) {\n throw new GithubIntegrationProviderError(\n 'installation-not-found',\n `GitHub installation connection not found: ${installation.connectionId}`,\n );\n }\n return connection.workspaceId;\n };\n}\n\nexport function deleteGithubInstallationTokenSecret(params: {\n workspaceId: string;\n installationId: number;\n deleteSecrets: (params: {workspaceId: string; namespace: string}) => Promise<number>;\n}): Promise<number> {\n return params.deleteSecrets({\n workspaceId: params.workspaceId,\n namespace: githubInstallationTokenNamespace(params.installationId),\n });\n}\n"],"names":["App","Octokit","config","normalizedGithubApiBaseUrl","normalizedGithubPrivateKey","GithubIntegrationProviderError","withInstallationTokenLock","getGithubInstallationByInstallationId","recordInstallationTokenLookup","mapGithubError","githubInstallationTokenNamespace","TOKEN_REFRESH_MARGIN_MS","SharedInstallationTokenCache","createGithubInstallationTokenProvider","options","OctokitGithubInstallationTokenProvider","createInstallationTokenCache","cache","InMemoryInstallationTokenCache","getInstallationAccessToken","installationId","getOrMint","mintInstallationAccessToken","response","getApp","octokit","rest","apps","createInstallationAccessToken","installation_id","data","token","expiresAt","Date","expires_at","Number","isNaN","getTime","permissions","undefined","app","appId","GITHUB_APP_ID","privateKey","defaults","baseUrl","throttle","onRateLimit","_retryAfter","_options","_octokit","retryCount","onSecondaryRateLimit","refreshMarginMs","now","tokens","Map","inFlightMints","mint","cached","get","isInsideRefreshMargin","Promise","resolve","inFlightMint","freshToken","then","set","finally","delete","TieredInstallationTokenCache","ram","shared","getIntegrationConnectionById","secretStore","withLock","resolveWorkspaceId","createGithubInstallationWorkspaceResolver","installation","String","connection","connectionId","workspaceId","deleteGithubInstallationTokenSecret","params","deleteSecrets","namespace"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../src/api/installation-token-provider.ts"],"sourcesContent":["import type {GetIntegrationConnectionByIdFn} from '@shipfox/api-integration-spi';\nimport {App, Octokit} from 'octokit';\nimport {config, normalizedGithubApiBaseUrl, normalizedGithubPrivateKey} from '#config.js';\nimport {GithubIntegrationProviderError} from '#core/errors.js';\nimport {withInstallationTokenLock} from '#db/installation-token-lock.js';\nimport {getGithubInstallationByInstallationId} from '#db/installations.js';\nimport {recordInstallationTokenFormat, recordInstallationTokenLookup} from '#metrics/index.js';\nimport {type GithubInstallationAccessToken, mapGithubError} from './client.js';\nimport {githubInstallationTokenFormatPlugin} from './github-octokit.js';\nimport {\n githubInstallationTokenNamespace,\n TOKEN_REFRESH_MARGIN_MS,\n} from './installation-token-envelope.js';\nimport {\n type InstallationTokenCache,\n type InstallationTokenSecretStore,\n SharedInstallationTokenCache,\n type SharedInstallationTokenCacheOptions,\n} from './shared-installation-token-cache.js';\n\nexport interface GithubInstallationTokenProvider {\n getInstallationAccessToken(installationId: number): Promise<GithubInstallationAccessToken>;\n}\n\nexport interface GithubInstallationTokenProviderOptions {\n cache?: InstallationTokenCache | undefined;\n getIntegrationConnectionById?: GetIntegrationConnectionByIdFn | undefined;\n secretStore?: InstallationTokenSecretStore | undefined;\n withLock?: SharedInstallationTokenCacheOptions['withLock'] | undefined;\n now?: (() => Date) | undefined;\n}\n\nexport function createGithubInstallationTokenProvider(\n options: GithubInstallationTokenProviderOptions = {},\n): GithubInstallationTokenProvider {\n return new OctokitGithubInstallationTokenProvider(createInstallationTokenCache(options));\n}\n\nclass OctokitGithubInstallationTokenProvider implements GithubInstallationTokenProvider {\n private app: App | undefined;\n\n constructor(\n private readonly cache: InstallationTokenCache = new InMemoryInstallationTokenCache(),\n ) {}\n\n getInstallationAccessToken(installationId: number): Promise<GithubInstallationAccessToken> {\n return this.cache.getOrMint(installationId, () =>\n this.mintInstallationAccessToken(installationId),\n );\n }\n\n private async mintInstallationAccessToken(\n installationId: number,\n ): Promise<GithubInstallationAccessToken> {\n const response = await mapGithubError(\n () =>\n this.getApp().octokit.rest.apps.createInstallationAccessToken({\n installation_id: installationId,\n }),\n 'installation-not-found',\n );\n\n if (typeof response.data.token !== 'string') {\n throw new GithubIntegrationProviderError(\n 'malformed-provider-response',\n 'GitHub installation access token response did not include a token',\n );\n }\n\n recordInstallationTokenFormat(response.data.token);\n\n const expiresAt = new Date(response.data.expires_at);\n if (Number.isNaN(expiresAt.getTime())) {\n throw new GithubIntegrationProviderError(\n 'malformed-provider-response',\n 'GitHub installation access token response did not include a valid expiry',\n );\n }\n\n return {\n token: response.data.token,\n expiresAt,\n ...(response.data.permissions === undefined ? {} : {permissions: response.data.permissions}),\n };\n }\n\n private getApp(): App {\n if (!this.app) {\n this.app = new App({\n appId: config.GITHUB_APP_ID,\n privateKey: normalizedGithubPrivateKey(),\n Octokit: Octokit.plugin(githubInstallationTokenFormatPlugin).defaults({\n baseUrl: normalizedGithubApiBaseUrl(),\n throttle: {\n onRateLimit: (\n _retryAfter: number,\n _options: unknown,\n _octokit: unknown,\n retryCount: number,\n ) => retryCount === 0,\n onSecondaryRateLimit: (\n _retryAfter: number,\n _options: unknown,\n _octokit: unknown,\n retryCount: number,\n ) => retryCount === 0,\n },\n }),\n });\n }\n return this.app;\n }\n}\n\nclass InMemoryInstallationTokenCache implements InstallationTokenCache {\n private readonly tokens = new Map<number, GithubInstallationAccessToken>();\n private readonly inFlightMints = new Map<number, Promise<GithubInstallationAccessToken>>();\n\n constructor(\n private readonly options: {\n refreshMarginMs: number;\n now: () => Date;\n } = {\n refreshMarginMs: TOKEN_REFRESH_MARGIN_MS,\n now: () => new Date(),\n },\n ) {}\n\n getOrMint(\n installationId: number,\n mint: () => Promise<GithubInstallationAccessToken>,\n ): Promise<GithubInstallationAccessToken> {\n const cached = this.tokens.get(installationId);\n if (cached && !this.isInsideRefreshMargin(cached.expiresAt)) {\n recordInstallationTokenLookup('ram-hit');\n return Promise.resolve(cached);\n }\n\n const inFlightMint = this.inFlightMints.get(installationId);\n if (inFlightMint) return inFlightMint;\n\n const freshToken = mint()\n .then((token) => {\n this.tokens.set(installationId, token);\n return token;\n })\n .finally(() => {\n this.inFlightMints.delete(installationId);\n });\n this.inFlightMints.set(installationId, freshToken);\n return freshToken;\n }\n\n private isInsideRefreshMargin(expiresAt: Date): boolean {\n return expiresAt.getTime() <= this.options.now().getTime() + this.options.refreshMarginMs;\n }\n}\n\nclass TieredInstallationTokenCache implements InstallationTokenCache {\n constructor(\n private readonly ram: InstallationTokenCache,\n private readonly shared: InstallationTokenCache,\n ) {}\n\n getOrMint(\n installationId: number,\n mint: () => Promise<GithubInstallationAccessToken>,\n ): Promise<GithubInstallationAccessToken> {\n return this.ram.getOrMint(installationId, () => this.shared.getOrMint(installationId, mint));\n }\n}\n\nfunction createInstallationTokenCache(\n options: GithubInstallationTokenProviderOptions,\n): InstallationTokenCache {\n if (options.cache) return options.cache;\n\n const ram = new InMemoryInstallationTokenCache({\n refreshMarginMs: TOKEN_REFRESH_MARGIN_MS,\n now: options.now ?? (() => new Date()),\n });\n if (!options.getIntegrationConnectionById || !options.secretStore) return ram;\n\n const shared = new SharedInstallationTokenCache({\n secretStore: options.secretStore,\n withLock: options.withLock ?? withInstallationTokenLock,\n resolveWorkspaceId: createGithubInstallationWorkspaceResolver(\n options.getIntegrationConnectionById,\n ),\n now: options.now,\n });\n return new TieredInstallationTokenCache(ram, shared);\n}\n\nfunction createGithubInstallationWorkspaceResolver(\n getIntegrationConnectionById: GetIntegrationConnectionByIdFn,\n) {\n return async (installationId: number): Promise<string> => {\n const installation = await getGithubInstallationByInstallationId(String(installationId));\n if (!installation) {\n throw new GithubIntegrationProviderError(\n 'installation-not-found',\n `GitHub installation not found: ${installationId}`,\n );\n }\n\n const connection = await getIntegrationConnectionById(installation.connectionId);\n if (!connection) {\n throw new GithubIntegrationProviderError(\n 'installation-not-found',\n `GitHub installation connection not found: ${installation.connectionId}`,\n );\n }\n return connection.workspaceId;\n };\n}\n\nexport function deleteGithubInstallationTokenSecret(params: {\n workspaceId: string;\n installationId: number;\n deleteSecrets: (params: {workspaceId: string; namespace: string}) => Promise<number>;\n}): Promise<number> {\n return params.deleteSecrets({\n workspaceId: params.workspaceId,\n namespace: githubInstallationTokenNamespace(params.installationId),\n });\n}\n"],"names":["App","Octokit","config","normalizedGithubApiBaseUrl","normalizedGithubPrivateKey","GithubIntegrationProviderError","withInstallationTokenLock","getGithubInstallationByInstallationId","recordInstallationTokenFormat","recordInstallationTokenLookup","mapGithubError","githubInstallationTokenFormatPlugin","githubInstallationTokenNamespace","TOKEN_REFRESH_MARGIN_MS","SharedInstallationTokenCache","createGithubInstallationTokenProvider","options","OctokitGithubInstallationTokenProvider","createInstallationTokenCache","cache","InMemoryInstallationTokenCache","getInstallationAccessToken","installationId","getOrMint","mintInstallationAccessToken","response","getApp","octokit","rest","apps","createInstallationAccessToken","installation_id","data","token","expiresAt","Date","expires_at","Number","isNaN","getTime","permissions","undefined","app","appId","GITHUB_APP_ID","privateKey","plugin","defaults","baseUrl","throttle","onRateLimit","_retryAfter","_options","_octokit","retryCount","onSecondaryRateLimit","refreshMarginMs","now","tokens","Map","inFlightMints","mint","cached","get","isInsideRefreshMargin","Promise","resolve","inFlightMint","freshToken","then","set","finally","delete","TieredInstallationTokenCache","ram","shared","getIntegrationConnectionById","secretStore","withLock","resolveWorkspaceId","createGithubInstallationWorkspaceResolver","installation","String","connection","connectionId","workspaceId","deleteGithubInstallationTokenSecret","params","deleteSecrets","namespace"],"mappings":"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"}
|
package/dist/config.d.ts
CHANGED
|
@@ -7,6 +7,7 @@ export declare const config: Readonly<{
|
|
|
7
7
|
GITHUB_APP_SLUG: string;
|
|
8
8
|
GITHUB_APP_USERNAME: string | undefined;
|
|
9
9
|
GITHUB_API_BASE_URL: string;
|
|
10
|
+
GITHUB_INSTALLATION_TOKEN_FORMAT_OVERRIDE: "disabled" | "enabled" | undefined;
|
|
10
11
|
GITHUB_INSTALL_STATE_SECRET: string;
|
|
11
12
|
} & import("@shipfox/config").CleanedEnvAccessors>;
|
|
12
13
|
export declare function normalizedGithubPrivateKey(): string;
|
package/dist/config.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAIA,eAAO,MAAM,MAAM
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAIA,eAAO,MAAM,MAAM;;;;;;;;;;;kDAmCjB,CAAC;AAEH,wBAAgB,0BAA0B,IAAI,MAAM,CAEnD;AAED,wBAAgB,yBAAyB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAEjE;AAED,wBAAgB,0BAA0B,IAAI,MAAM,CAEnD"}
|
package/dist/config.js
CHANGED
|
@@ -27,6 +27,14 @@ export const config = createConfig({
|
|
|
27
27
|
desc: 'Base URL used for GitHub REST API requests. Set this only for GitHub Enterprise Server or a compatible test server.',
|
|
28
28
|
default: 'https://api.github.com'
|
|
29
29
|
}),
|
|
30
|
+
GITHUB_INSTALLATION_TOKEN_FORMAT_OVERRIDE: str({
|
|
31
|
+
desc: 'Temporary GitHub installation token format override. Set this to enabled to request stateless tokens, disabled to request stateful tokens, or leave it unset to follow the GitHub rollout.',
|
|
32
|
+
choices: [
|
|
33
|
+
'enabled',
|
|
34
|
+
'disabled'
|
|
35
|
+
],
|
|
36
|
+
default: undefined
|
|
37
|
+
}),
|
|
30
38
|
GITHUB_INSTALL_STATE_SECRET: str({
|
|
31
39
|
desc: 'Secret used to sign the state token that protects the GitHub App install flow. Required.'
|
|
32
40
|
})
|
package/dist/config.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/config.ts"],"sourcesContent":["import {createConfig, str, url} from '@shipfox/config';\n\nconst trailingSlashesPattern = /\\/+$/u;\n\nexport const config = createConfig({\n GITHUB_APP_ID: str({\n desc: \"Numeric ID of the GitHub App, found on the app's settings page. Required.\",\n }),\n GITHUB_APP_PRIVATE_KEY: str({\n desc: 'Private key of the GitHub App in PEM format, used to sign API requests. Newlines may be written as \\\\n and are restored at runtime. Required.',\n }),\n GITHUB_APP_CLIENT_ID: str({\n desc: 'OAuth client ID of the GitHub App, used for user sign-in. Required.',\n }),\n GITHUB_APP_CLIENT_SECRET: str({\n desc: 'OAuth client secret of the GitHub App. Required.',\n }),\n GITHUB_APP_WEBHOOK_SECRET: str({\n desc: 'Secret used to verify the signature of incoming GitHub webhooks. Must match the value set on the GitHub App. Required.',\n }),\n GITHUB_APP_SLUG: str({\n desc: 'URL slug of the GitHub App, used to build install and callback links. Required.',\n }),\n GITHUB_APP_USERNAME: str({\n desc: 'GitHub App username used as the Git commit author when checkout credentials are persisted. Set this to the app username, such as my-app. The [bot] suffix is added automatically. Leave unset to keep Git author identity unset.',\n default: undefined,\n }),\n GITHUB_API_BASE_URL: url({\n desc: 'Base URL used for GitHub REST API requests. Set this only for GitHub Enterprise Server or a compatible test server.',\n default: 'https://api.github.com',\n }),\n GITHUB_INSTALL_STATE_SECRET: str({\n desc: 'Secret used to sign the state token that protects the GitHub App install flow. Required.',\n }),\n});\n\nexport function normalizedGithubPrivateKey(): string {\n return config.GITHUB_APP_PRIVATE_KEY.replaceAll('\\\\n', '\\n');\n}\n\nexport function normalizeGithubApiBaseUrl(baseUrl: string): string {\n return baseUrl.replace(trailingSlashesPattern, '');\n}\n\nexport function normalizedGithubApiBaseUrl(): string {\n return normalizeGithubApiBaseUrl(config.GITHUB_API_BASE_URL);\n}\n"],"names":["createConfig","str","url","trailingSlashesPattern","config","GITHUB_APP_ID","desc","GITHUB_APP_PRIVATE_KEY","GITHUB_APP_CLIENT_ID","GITHUB_APP_CLIENT_SECRET","GITHUB_APP_WEBHOOK_SECRET","GITHUB_APP_SLUG","GITHUB_APP_USERNAME","default","undefined","GITHUB_API_BASE_URL","GITHUB_INSTALL_STATE_SECRET","normalizedGithubPrivateKey","replaceAll","normalizeGithubApiBaseUrl","baseUrl","replace","normalizedGithubApiBaseUrl"],"mappings":"AAAA,SAAQA,YAAY,EAAEC,GAAG,EAAEC,GAAG,QAAO,kBAAkB;AAEvD,MAAMC,yBAAyB;AAE/B,OAAO,MAAMC,SAASJ,aAAa;IACjCK,eAAeJ,IAAI;QACjBK,MAAM;IACR;IACAC,wBAAwBN,IAAI;QAC1BK,MAAM;IACR;IACAE,sBAAsBP,IAAI;QACxBK,MAAM;IACR;IACAG,0BAA0BR,IAAI;QAC5BK,MAAM;IACR;IACAI,2BAA2BT,IAAI;QAC7BK,MAAM;IACR;IACAK,iBAAiBV,IAAI;QACnBK,MAAM;IACR;IACAM,qBAAqBX,IAAI;QACvBK,MAAM;QACNO,SAASC;IACX;IACAC,qBAAqBb,IAAI;QACvBI,MAAM;QACNO,SAAS;IACX;IACAG,
|
|
1
|
+
{"version":3,"sources":["../src/config.ts"],"sourcesContent":["import {createConfig, str, url} from '@shipfox/config';\n\nconst trailingSlashesPattern = /\\/+$/u;\n\nexport const config = createConfig({\n GITHUB_APP_ID: str({\n desc: \"Numeric ID of the GitHub App, found on the app's settings page. Required.\",\n }),\n GITHUB_APP_PRIVATE_KEY: str({\n desc: 'Private key of the GitHub App in PEM format, used to sign API requests. Newlines may be written as \\\\n and are restored at runtime. Required.',\n }),\n GITHUB_APP_CLIENT_ID: str({\n desc: 'OAuth client ID of the GitHub App, used for user sign-in. Required.',\n }),\n GITHUB_APP_CLIENT_SECRET: str({\n desc: 'OAuth client secret of the GitHub App. Required.',\n }),\n GITHUB_APP_WEBHOOK_SECRET: str({\n desc: 'Secret used to verify the signature of incoming GitHub webhooks. Must match the value set on the GitHub App. Required.',\n }),\n GITHUB_APP_SLUG: str({\n desc: 'URL slug of the GitHub App, used to build install and callback links. Required.',\n }),\n GITHUB_APP_USERNAME: str({\n desc: 'GitHub App username used as the Git commit author when checkout credentials are persisted. Set this to the app username, such as my-app. The [bot] suffix is added automatically. Leave unset to keep Git author identity unset.',\n default: undefined,\n }),\n GITHUB_API_BASE_URL: url({\n desc: 'Base URL used for GitHub REST API requests. Set this only for GitHub Enterprise Server or a compatible test server.',\n default: 'https://api.github.com',\n }),\n GITHUB_INSTALLATION_TOKEN_FORMAT_OVERRIDE: str({\n desc: 'Temporary GitHub installation token format override. Set this to enabled to request stateless tokens, disabled to request stateful tokens, or leave it unset to follow the GitHub rollout.',\n choices: ['enabled', 'disabled'] as const,\n default: undefined,\n }),\n GITHUB_INSTALL_STATE_SECRET: str({\n desc: 'Secret used to sign the state token that protects the GitHub App install flow. Required.',\n }),\n});\n\nexport function normalizedGithubPrivateKey(): string {\n return config.GITHUB_APP_PRIVATE_KEY.replaceAll('\\\\n', '\\n');\n}\n\nexport function normalizeGithubApiBaseUrl(baseUrl: string): string {\n return baseUrl.replace(trailingSlashesPattern, '');\n}\n\nexport function normalizedGithubApiBaseUrl(): string {\n return normalizeGithubApiBaseUrl(config.GITHUB_API_BASE_URL);\n}\n"],"names":["createConfig","str","url","trailingSlashesPattern","config","GITHUB_APP_ID","desc","GITHUB_APP_PRIVATE_KEY","GITHUB_APP_CLIENT_ID","GITHUB_APP_CLIENT_SECRET","GITHUB_APP_WEBHOOK_SECRET","GITHUB_APP_SLUG","GITHUB_APP_USERNAME","default","undefined","GITHUB_API_BASE_URL","GITHUB_INSTALLATION_TOKEN_FORMAT_OVERRIDE","choices","GITHUB_INSTALL_STATE_SECRET","normalizedGithubPrivateKey","replaceAll","normalizeGithubApiBaseUrl","baseUrl","replace","normalizedGithubApiBaseUrl"],"mappings":"AAAA,SAAQA,YAAY,EAAEC,GAAG,EAAEC,GAAG,QAAO,kBAAkB;AAEvD,MAAMC,yBAAyB;AAE/B,OAAO,MAAMC,SAASJ,aAAa;IACjCK,eAAeJ,IAAI;QACjBK,MAAM;IACR;IACAC,wBAAwBN,IAAI;QAC1BK,MAAM;IACR;IACAE,sBAAsBP,IAAI;QACxBK,MAAM;IACR;IACAG,0BAA0BR,IAAI;QAC5BK,MAAM;IACR;IACAI,2BAA2BT,IAAI;QAC7BK,MAAM;IACR;IACAK,iBAAiBV,IAAI;QACnBK,MAAM;IACR;IACAM,qBAAqBX,IAAI;QACvBK,MAAM;QACNO,SAASC;IACX;IACAC,qBAAqBb,IAAI;QACvBI,MAAM;QACNO,SAAS;IACX;IACAG,2CAA2Cf,IAAI;QAC7CK,MAAM;QACNW,SAAS;YAAC;YAAW;SAAW;QAChCJ,SAASC;IACX;IACAI,6BAA6BjB,IAAI;QAC/BK,MAAM;IACR;AACF,GAAG;AAEH,OAAO,SAASa;IACd,OAAOf,OAAOG,sBAAsB,CAACa,UAAU,CAAC,OAAO;AACzD;AAEA,OAAO,SAASC,0BAA0BC,OAAe;IACvD,OAAOA,QAAQC,OAAO,CAACpB,wBAAwB;AACjD;AAEA,OAAO,SAASqB;IACd,OAAOH,0BAA0BjB,OAAOW,mBAAmB;AAC7D"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"agent-tools.d.ts","sourceRoot":"","sources":["../../src/core/agent-tools.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAGV,yBAAyB,EACzB,gBAAgB,EAChB,kBAAkB,EAClB,qBAAqB,EACrB,0BAA0B,EAC3B,MAAM,8BAA8B,CAAC;AAGtC,OAAO,EAEL,KAAK,+BAA+B,EACrC,MAAM,qCAAqC,CAAC;AAE7C,OAAO,KAAK,EAAC,kBAAkB,EAAC,MAAM,sBAAsB,CAAC;AAE7D,OAAO,EACL,KAAK,2BAA2B,EAChC,KAAK,iBAAiB,EACtB,KAAK,4BAA4B,EAGlC,MAAM,gCAAgC,CAAC;AAExC,YAAY,EACV,2BAA2B,EAC3B,uBAAuB,EACvB,iBAAiB,EACjB,yBAAyB,EACzB,+BAA+B,EAC/B,iCAAiC,EACjC,4BAA4B,EAC5B,0BAA0B,GAC3B,MAAM,gCAAgC,CAAC;AACxC,OAAO,EACL,oCAAoC,EACpC,0BAA0B,EAC1B,sBAAsB,EACtB,+BAA+B,GAChC,MAAM,gCAAgC,CAAC;AAExC,KAAK,2BAA2B,GAAG,qBAAqB,CAAC,QAAQ,CAAC,CAAC;AAEnE,KAAK,oBAAoB,GAAG;IAC1B,OAAO,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IAC9B,OAAO,EAAE,SAAS;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAC,EAAE,CAAC;IACjD,iBAAiB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,CAAC;CACzD,CAAC;
|
|
1
|
+
{"version":3,"file":"agent-tools.d.ts","sourceRoot":"","sources":["../../src/core/agent-tools.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAGV,yBAAyB,EACzB,gBAAgB,EAChB,kBAAkB,EAClB,qBAAqB,EACrB,0BAA0B,EAC3B,MAAM,8BAA8B,CAAC;AAGtC,OAAO,EAEL,KAAK,+BAA+B,EACrC,MAAM,qCAAqC,CAAC;AAE7C,OAAO,KAAK,EAAC,kBAAkB,EAAC,MAAM,sBAAsB,CAAC;AAE7D,OAAO,EACL,KAAK,2BAA2B,EAChC,KAAK,iBAAiB,EACtB,KAAK,4BAA4B,EAGlC,MAAM,gCAAgC,CAAC;AAExC,YAAY,EACV,2BAA2B,EAC3B,uBAAuB,EACvB,iBAAiB,EACjB,yBAAyB,EACzB,+BAA+B,EAC/B,iCAAiC,EACjC,4BAA4B,EAC5B,0BAA0B,GAC3B,MAAM,gCAAgC,CAAC;AACxC,OAAO,EACL,oCAAoC,EACpC,0BAA0B,EAC1B,sBAAsB,EACtB,+BAA+B,GAChC,MAAM,gCAAgC,CAAC;AAExC,KAAK,2BAA2B,GAAG,qBAAqB,CAAC,QAAQ,CAAC,CAAC;AAEnE,KAAK,oBAAoB,GAAG;IAC1B,OAAO,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IAC9B,OAAO,EAAE,SAAS;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAC,EAAE,CAAC;IACjD,iBAAiB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,CAAC;CACzD,CAAC;AAwFF,qBAAa,wBACX,YACE,kBAAkB,CAChB,2BAA2B,EAC3B,4BAA4B,EAC5B,OAAO,EACP,oBAAoB,CACrB;IAIS,OAAO,CAAC,QAAQ,CAAC,OAAO;IAFpC,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAkC;IAEhE,YAA6B,OAAO,GAAE,+BAAoC,EAEzE;IAED,OAAO,IAAI,SAAS,2BAA2B,EAAE,CAEhD;IAED,gBAAgB,IAAI,yBAAyB,CAE5C;IAEK,WAAW,CACf,KAAK,EAAE,0BAA0B,CAAC,2BAA2B,EAAE,4BAA4B,CAAC,GAC3F,OAAO,CAAC,gBAAgB,CAAC,oBAAoB,CAAC,CAAC,CA8EjD;CACF;AAED,MAAM,WAAW,+BAA+B;IAC9C,6BAA6B,CAAC,EAC1B,CAAC,CAAC,YAAY,EAAE,MAAM,KAAK,OAAO,CAAC,kBAAkB,GAAG,SAAS,CAAC,CAAC,GACnE,SAAS,CAAC;IACd,aAAa,CAAC,EAAE,+BAA+B,GAAG,SAAS,CAAC;IAC5D,YAAY,CAAC,EAAE,uBAAuB,GAAG,SAAS,CAAC;CACpD;AAED,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,OAAO,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAAC,GAAG,SAAS,CAAC;IAClE,MAAM,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC5B,GAAG,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAC1B;AAED,MAAM,WAAW,gBAAgB;IAC/B,OAAO,CAAC,KAAK,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC,kBAAkB,CAAC,CAAC;IACzF,OAAO,CAAC,EAAE,CAAC,CAAC,KAAK,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC,GAAG,SAAS,CAAC;CACjG;AAED,MAAM,MAAM,uBAAuB,GAAG,CAAC,KAAK,EAAE,MAAM,KAAK,gBAAgB,CAAC;AAgE1E,wBAAgB,oBAAoB,CAClC,MAAM,EAAE,iBAAiB,EACzB,MAAM,EAAE,MAAM,GAAG,SAAS,EAC1B,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC5B,MAAM,GAAG,SAAS,CA6HpB;AAyED,wBAAgB,gCAAgC,CAC9C,MAAM,EAAE,iBAAiB,EACzB,MAAM,EAAE,MAAM,GAAG,SAAS,EAC1B,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC5B,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAWzB"}
|