@shipfox/api-integration-github 7.1.0 → 9.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.turbo/turbo-build.log +1 -1
- package/CHANGELOG.md +45 -0
- package/dist/api/client.js +1 -1
- package/dist/api/client.js.map +1 -1
- package/dist/api/installation-token-envelope.d.ts +1 -1
- package/dist/api/installation-token-envelope.d.ts.map +1 -1
- package/dist/api/installation-token-envelope.js +1 -1
- package/dist/api/installation-token-envelope.js.map +1 -1
- package/dist/api/installation-token-provider.d.ts +1 -1
- package/dist/api/installation-token-provider.d.ts.map +1 -1
- package/dist/api/installation-token-provider.js.map +1 -1
- package/dist/core/actions-logs.d.ts +1 -1
- package/dist/core/actions-logs.d.ts.map +1 -1
- package/dist/core/actions-logs.js +2 -2
- package/dist/core/actions-logs.js.map +1 -1
- package/dist/core/agent-tool-selection.d.ts +1 -1
- package/dist/core/agent-tool-selection.d.ts.map +1 -1
- package/dist/core/agent-tool-selection.js +1 -1
- package/dist/core/agent-tool-selection.js.map +1 -1
- package/dist/core/agent-tools.d.ts +4 -4
- package/dist/core/agent-tools.d.ts.map +1 -1
- package/dist/core/agent-tools.js +2 -2
- package/dist/core/agent-tools.js.map +1 -1
- package/dist/core/errors.d.ts +1 -1
- package/dist/core/errors.d.ts.map +1 -1
- package/dist/core/errors.js +1 -1
- package/dist/core/errors.js.map +1 -1
- package/dist/core/github-agent-tool-catalog.d.ts +49 -0
- package/dist/core/github-agent-tool-catalog.d.ts.map +1 -0
- package/dist/core/github-agent-tool-catalog.js +867 -0
- package/dist/core/github-agent-tool-catalog.js.map +1 -0
- package/dist/core/install.d.ts +1 -1
- package/dist/core/install.d.ts.map +1 -1
- package/dist/core/install.js.map +1 -1
- package/dist/core/source-control.d.ts +1 -1
- package/dist/core/source-control.d.ts.map +1 -1
- package/dist/core/source-control.js +1 -1
- package/dist/core/source-control.js.map +1 -1
- package/dist/core/webhook-processor.d.ts +1 -1
- package/dist/core/webhook-processor.d.ts.map +1 -1
- package/dist/core/webhook-processor.js +1 -1
- package/dist/core/webhook-processor.js.map +1 -1
- package/dist/core/webhook.d.ts +1 -1
- package/dist/core/webhook.d.ts.map +1 -1
- package/dist/core/webhook.js +1 -1
- package/dist/core/webhook.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/metrics/instance.d.ts +1 -1
- package/dist/metrics/instance.d.ts.map +1 -1
- package/dist/metrics/instance.js.map +1 -1
- package/dist/presentation/dto/integrations.d.ts +8 -7
- package/dist/presentation/dto/integrations.d.ts.map +1 -1
- package/dist/presentation/dto/integrations.js +18 -7
- package/dist/presentation/dto/integrations.js.map +1 -1
- package/dist/presentation/e2eRoutes/create-connection.d.ts +1 -1
- package/dist/presentation/e2eRoutes/create-connection.d.ts.map +1 -1
- package/dist/presentation/e2eRoutes/create-connection.js +1 -1
- package/dist/presentation/e2eRoutes/create-connection.js.map +1 -1
- package/dist/presentation/routes/errors.js +1 -1
- package/dist/presentation/routes/errors.js.map +1 -1
- package/dist/presentation/routes/install.d.ts +1 -1
- package/dist/presentation/routes/install.d.ts.map +1 -1
- package/dist/presentation/routes/install.js.map +1 -1
- package/dist/presentation/routes/webhooks.d.ts +1 -1
- package/dist/presentation/routes/webhooks.d.ts.map +1 -1
- package/dist/presentation/routes/webhooks.js +1 -1
- package/dist/presentation/routes/webhooks.js.map +1 -1
- package/dist/tsconfig.test.tsbuildinfo +1 -1
- package/package.json +17 -15
- package/src/api/client.ts +1 -1
- package/src/api/installation-token-envelope.ts +1 -1
- package/src/api/installation-token-provider.test.ts +1 -1
- package/src/api/installation-token-provider.ts +1 -1
- package/src/core/actions-logs.ts +2 -2
- package/src/core/agent-tool-selection.ts +1 -1
- package/src/core/agent-tools.ts +10 -10
- package/src/core/errors.ts +1 -1
- package/src/core/github-agent-tool-catalog.ts +963 -0
- package/src/core/install.test.ts +1 -1
- package/src/core/install.ts +1 -1
- package/src/core/source-control.ts +1 -1
- package/src/core/webhook-processor.test.ts +1 -1
- package/src/core/webhook-processor.ts +1 -1
- package/src/core/webhook.test.ts +1 -1
- package/src/core/webhook.ts +7 -7
- package/src/index.ts +2 -1
- package/src/metrics/instance.ts +1 -1
- package/src/presentation/dto/integrations.ts +24 -6
- package/src/presentation/e2eRoutes/create-connection.ts +2 -2
- package/src/presentation/e2eRoutes/index.test.ts +1 -1
- package/src/presentation/routes/errors.ts +1 -1
- package/src/presentation/routes/install.test.ts +1 -1
- package/src/presentation/routes/install.ts +1 -1
- package/src/presentation/routes/webhooks.test.ts +1 -1
- package/src/presentation/routes/webhooks.ts +2 -5
- 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: 30 files with swc (444.47ms)
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,50 @@
|
|
|
1
1
|
# @shipfox/api-integration-github
|
|
2
2
|
|
|
3
|
+
## 9.0.1
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 475ce59: Republishes all public packages after restoring release authorization.
|
|
8
|
+
- Updated dependencies [475ce59]
|
|
9
|
+
- @shipfox/api-auth-context@9.0.1
|
|
10
|
+
- @shipfox/api-integration-github-dto@9.0.1
|
|
11
|
+
- @shipfox/api-integration-spi@0.2.1
|
|
12
|
+
- @shipfox/config@1.2.3
|
|
13
|
+
- @shipfox/node-drizzle@0.3.3
|
|
14
|
+
- @shipfox/node-error-monitoring@0.2.1
|
|
15
|
+
- @shipfox/node-fastify@0.3.1
|
|
16
|
+
- @shipfox/node-opentelemetry@0.6.1
|
|
17
|
+
- @shipfox/node-postgres@0.4.3
|
|
18
|
+
|
|
19
|
+
## 9.0.0
|
|
20
|
+
|
|
21
|
+
### Minor Changes
|
|
22
|
+
|
|
23
|
+
- 02974d6: Removes executable policy and test fixtures from public API DTO roots.
|
|
24
|
+
|
|
25
|
+
### Patch Changes
|
|
26
|
+
|
|
27
|
+
- 4a6d124: Separates Integrations provider SPI contracts from the public DTO surface.
|
|
28
|
+
- Updated dependencies [02974d6]
|
|
29
|
+
- Updated dependencies [4a6d124]
|
|
30
|
+
- @shipfox/api-integration-github-dto@9.0.0
|
|
31
|
+
- @shipfox/api-integration-spi@0.2.0
|
|
32
|
+
- @shipfox/api-auth-context@9.0.0
|
|
33
|
+
- @shipfox/config@1.2.2
|
|
34
|
+
- @shipfox/node-drizzle@0.3.2
|
|
35
|
+
- @shipfox/node-error-monitoring@0.2.0
|
|
36
|
+
- @shipfox/node-fastify@0.3.0
|
|
37
|
+
- @shipfox/node-opentelemetry@0.6.0
|
|
38
|
+
- @shipfox/node-postgres@0.4.2
|
|
39
|
+
|
|
40
|
+
## 8.0.0
|
|
41
|
+
|
|
42
|
+
### Patch Changes
|
|
43
|
+
|
|
44
|
+
- Updated dependencies [7f227c6]
|
|
45
|
+
- @shipfox/api-integration-core-dto@8.0.0
|
|
46
|
+
- @shipfox/api-integration-github-dto@8.0.0
|
|
47
|
+
|
|
3
48
|
## 7.1.0
|
|
4
49
|
|
|
5
50
|
### Patch Changes
|
package/dist/api/client.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Buffer } from 'node:buffer';
|
|
2
|
-
import { MAX_REPOSITORY_FILE_BYTES } from '@shipfox/api-integration-
|
|
2
|
+
import { MAX_REPOSITORY_FILE_BYTES } from '@shipfox/api-integration-spi';
|
|
3
3
|
import ky, { HTTPError, TimeoutError } from 'ky';
|
|
4
4
|
import { App, Octokit, RequestError } from 'octokit';
|
|
5
5
|
import { config, normalizedGithubApiBaseUrl, normalizedGithubPrivateKey } from '#config.js';
|
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-core-dto';\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(notFoundReason, error.message);\n }\n if (error.status === 429 || isGithubRateLimitError(error)) {\n throw new GithubIntegrationProviderError(\n 'rate-limited',\n error.message,\n retryAfterSeconds(error),\n );\n }\n if (error.status === 401 || error.status === 403) {\n throw new GithubIntegrationProviderError(\n 'access-denied',\n error.message,\n retryAfterSeconds(error),\n );\n }\n if (error.status >= 500) {\n throw new GithubIntegrationProviderError('provider-unavailable', error.message);\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';\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(notFoundReason, error.message);\n }\n if (error.status === 429 || isGithubRateLimitError(error)) {\n throw new GithubIntegrationProviderError(\n 'rate-limited',\n error.message,\n retryAfterSeconds(error),\n );\n }\n if (error.status === 401 || error.status === 403) {\n throw new GithubIntegrationProviderError(\n 'access-denied',\n error.message,\n retryAfterSeconds(error),\n );\n }\n if (error.status >= 500) {\n throw new GithubIntegrationProviderError('provider-unavailable', error.message);\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,4 +1,4 @@
|
|
|
1
|
-
import { type IntegrationProviderErrorReason } from '@shipfox/api-integration-
|
|
1
|
+
import { type IntegrationProviderErrorReason } from '@shipfox/api-integration-spi';
|
|
2
2
|
import { GithubIntegrationProviderError } from '#core/errors.js';
|
|
3
3
|
export declare const TOKEN_REFRESH_MARGIN_MS: number;
|
|
4
4
|
export declare const TOKEN_VALIDITY_BUFFER_MS: number;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"installation-token-envelope.d.ts","sourceRoot":"","sources":["../../src/api/installation-token-envelope.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,8BAA8B,EACpC,MAAM,
|
|
1
|
+
{"version":3,"file":"installation-token-envelope.d.ts","sourceRoot":"","sources":["../../src/api/installation-token-envelope.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,8BAA8B,EACpC,MAAM,8BAA8B,CAAC;AAEtC,OAAO,EAAC,8BAA8B,EAAC,MAAM,iBAAiB,CAAC;AAE/D,eAAO,MAAM,uBAAuB,QAAgB,CAAC;AACrD,eAAO,MAAM,wBAAwB,QAAY,CAAC;AAClD,eAAO,MAAM,wBAAwB,QAAY,CAAC;AAClD,eAAO,MAAM,wBAAwB,QAAgB,CAAC;AACtD,eAAO,MAAM,mBAAmB,QAAiB,CAAC;AAqClD,MAAM,WAAW,yBAAyB;IACxC,KAAK,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC3B,SAAS,CAAC,EAAE,IAAI,GAAG,SAAS,CAAC;IAC7B,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,GAAG,OAAO,CAAC,GAAG,SAAS,CAAC;IACrE,YAAY,CAAC,EAAE,IAAI,GAAG,SAAS,CAAC;IAChC,aAAa,CAAC,EAAE,8BAA8B,GAAG,SAAS,CAAC;CAC5D;AAED,MAAM,MAAM,cAAc,GAAG,WAAW,GAAG,UAAU,CAAC;AAEtD,MAAM,WAAW,mBAAmB;IAClC,KAAK,EAAE,cAAc,CAAC;IACtB,MAAM,EAAE,8BAA8B,CAAC;IACvC,iBAAiB,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CACxC;AAED,wBAAgB,gCAAgC,CAAC,cAAc,EAAE,MAAM,GAAG,MAAM,CAE/E;AAED,wBAAgB,+BAA+B,CAAC,QAAQ,EAAE,yBAAyB,GAAG,MAAM,CAU3F;AAED,wBAAgB,8BAA8B,CAAC,GAAG,EAAE,MAAM,GAAG,yBAAyB,GAAG,SAAS,CAkBjG;AAED,wBAAgB,MAAM,CACpB,QAAQ,EAAE,yBAAyB,GAAG,SAAS,EAC/C,GAAG,EAAE,IAAI,GACR,QAAQ,IAAI,yBAAyB,GAAG;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,IAAI,CAAA;CAAC,CAM1E;AAED,wBAAgB,YAAY,CAAC,SAAS,EAAE,IAAI,EAAE,GAAG,EAAE,IAAI,GAAG,OAAO,CAEhE;AAED,wBAAgB,UAAU,CAAC,SAAS,EAAE,IAAI,GAAG,SAAS,EAAE,GAAG,EAAE,IAAI,GAAG,OAAO,CAE1E;AAED,wBAAgB,aAAa,CAAC,QAAQ,EAAE,yBAAyB,GAAG,SAAS,EAAE,GAAG,EAAE,IAAI,GAAG,OAAO,CAMjG;AAED,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,OAAO,GAAG,mBAAmB,CAUrE;AAED,wBAAgB,uBAAuB,CAAC,MAAM,EAAE,8BAA8B,GAAG,cAAc,CAE9F;AAED,wBAAgB,SAAS,CAAC,UAAU,EAAE,mBAAmB,GAAG,MAAM,CAKjE;AAED,wBAAgB,wBAAwB,CACtC,MAAM,EAAE,8BAA8B,EACtC,YAAY,EAAE,MAAM,GACnB,8BAA8B,CAMhC;AAED,wBAAgB,eAAe,CAAC,KAAK,EAAE,OAAO,GAAG,8BAA8B,CAS9E"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { IntegrationProviderError } from '@shipfox/api-integration-
|
|
1
|
+
import { IntegrationProviderError } from '@shipfox/api-integration-spi';
|
|
2
2
|
import { z } from 'zod';
|
|
3
3
|
import { GithubIntegrationProviderError } from '#core/errors.js';
|
|
4
4
|
export const TOKEN_REFRESH_MARGIN_MS = 5 * 60 * 1000;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/api/installation-token-envelope.ts"],"sourcesContent":["import {\n IntegrationProviderError,\n type IntegrationProviderErrorReason,\n} from '@shipfox/api-integration-core-dto';\nimport {z} from 'zod';\nimport {GithubIntegrationProviderError} from '#core/errors.js';\n\nexport const TOKEN_REFRESH_MARGIN_MS = 5 * 60 * 1000;\nexport const TOKEN_VALIDITY_BUFFER_MS = 60 * 1000;\nexport const TRANSIENT_BACKOFF_MIN_MS = 30 * 1000;\nexport const TRANSIENT_BACKOFF_MAX_MS = 5 * 60 * 1000;\nexport const TERMINAL_BACKOFF_MS = 15 * 60 * 1000;\n\nconst providerErrorReasons = [\n 'repository-not-found',\n 'installation-not-found',\n 'file-not-found',\n 'access-denied',\n 'rate-limited',\n 'timeout',\n 'provider-unavailable',\n 'malformed-provider-response',\n 'content-too-large',\n 'too-many-files',\n] as const satisfies readonly IntegrationProviderErrorReason[];\n\nconst providerErrorReasonSchema = z.enum(providerErrorReasons);\nconst terminalMintErrorReasons = new Set<IntegrationProviderErrorReason>([\n 'access-denied',\n 'installation-not-found',\n 'malformed-provider-response',\n]);\n\ntype MissingProviderErrorReason = Exclude<\n IntegrationProviderErrorReason,\n (typeof providerErrorReasons)[number]\n>;\nconst providerErrorReasonSchemaCoversUnion: Record<MissingProviderErrorReason, never> = {};\nvoid providerErrorReasonSchemaCoversUnion;\n\nconst installationTokenEnvelopeSchema = z.object({\n token: z.string().min(1).optional(),\n expiresAt: z.string().datetime().optional(),\n permissions: z.record(z.string(), z.enum(['read', 'write', 'admin'])).optional(),\n backoffUntil: z.string().datetime().optional(),\n backoffReason: providerErrorReasonSchema.optional(),\n});\n\nexport interface InstallationTokenEnvelope {\n token?: string | undefined;\n expiresAt?: Date | undefined;\n permissions?: Record<string, 'read' | 'write' | 'admin'> | undefined;\n backoffUntil?: Date | undefined;\n backoffReason?: IntegrationProviderErrorReason | undefined;\n}\n\nexport type MintErrorClass = 'transient' | 'terminal';\n\nexport interface ClassifiedMintError {\n class: MintErrorClass;\n reason: IntegrationProviderErrorReason;\n retryAfterSeconds?: number | undefined;\n}\n\nexport function githubInstallationTokenNamespace(installationId: number): string {\n return `system/github/installation-token/${installationId}`;\n}\n\nexport function encodeInstallationTokenEnvelope(envelope: InstallationTokenEnvelope): string {\n return JSON.stringify({\n ...(envelope.token !== undefined && {token: envelope.token}),\n ...(envelope.expiresAt !== undefined && {expiresAt: envelope.expiresAt.toISOString()}),\n ...(envelope.permissions !== undefined && {permissions: envelope.permissions}),\n ...(envelope.backoffUntil !== undefined && {\n backoffUntil: envelope.backoffUntil.toISOString(),\n }),\n ...(envelope.backoffReason !== undefined && {backoffReason: envelope.backoffReason}),\n });\n}\n\nexport function parseInstallationTokenEnvelope(raw: string): InstallationTokenEnvelope | undefined {\n let parsed: unknown;\n try {\n parsed = JSON.parse(raw);\n } catch {\n return undefined;\n }\n\n const result = installationTokenEnvelopeSchema.safeParse(parsed);\n if (!result.success) return undefined;\n\n return {\n token: result.data.token,\n expiresAt: result.data.expiresAt ? new Date(result.data.expiresAt) : undefined,\n permissions: result.data.permissions,\n backoffUntil: result.data.backoffUntil ? new Date(result.data.backoffUntil) : undefined,\n backoffReason: result.data.backoffReason,\n };\n}\n\nexport function usable(\n envelope: InstallationTokenEnvelope | undefined,\n now: Date,\n): envelope is InstallationTokenEnvelope & {token: string; expiresAt: Date} {\n return (\n envelope?.token !== undefined &&\n envelope.expiresAt !== undefined &&\n !needsRefresh(envelope.expiresAt, now)\n );\n}\n\nexport function needsRefresh(expiresAt: Date, now: Date): boolean {\n return expiresAt.getTime() <= now.getTime() + TOKEN_REFRESH_MARGIN_MS;\n}\n\nexport function stillValid(expiresAt: Date | undefined, now: Date): boolean {\n return expiresAt !== undefined && expiresAt.getTime() > now.getTime() + TOKEN_VALIDITY_BUFFER_MS;\n}\n\nexport function backoffActive(envelope: InstallationTokenEnvelope | undefined, now: Date): boolean {\n return (\n envelope?.backoffUntil !== undefined &&\n envelope.backoffReason !== undefined &&\n envelope.backoffUntil.getTime() > now.getTime()\n );\n}\n\nexport function classifyMintError(error: unknown): ClassifiedMintError {\n if (error instanceof IntegrationProviderError) {\n return {\n reason: error.reason,\n retryAfterSeconds: error.retryAfterSeconds,\n class: mintErrorClassForReason(error.reason),\n };\n }\n\n return {reason: 'provider-unavailable', class: 'transient'};\n}\n\nexport function mintErrorClassForReason(reason: IntegrationProviderErrorReason): MintErrorClass {\n return terminalMintErrorReasons.has(reason) ? 'terminal' : 'transient';\n}\n\nexport function backoffMs(classified: ClassifiedMintError): number {\n if (classified.class === 'terminal') return TERMINAL_BACKOFF_MS;\n\n const retryAfterMs = (classified.retryAfterSeconds ?? 0) * 1000;\n return Math.min(TRANSIENT_BACKOFF_MAX_MS, Math.max(TRANSIENT_BACKOFF_MIN_MS, retryAfterMs));\n}\n\nexport function providerErrorFromBackoff(\n reason: IntegrationProviderErrorReason,\n retryAfterMs: number,\n): GithubIntegrationProviderError {\n return new GithubIntegrationProviderError(\n reason,\n `GitHub installation token mint is backed off after ${reason}`,\n Math.max(1, Math.ceil(retryAfterMs / 1000)),\n );\n}\n\nexport function toProviderError(error: unknown): GithubIntegrationProviderError {\n if (error instanceof GithubIntegrationProviderError) return error;\n if (error instanceof IntegrationProviderError) {\n return new GithubIntegrationProviderError(error.reason, error.message, error.retryAfterSeconds);\n }\n return new GithubIntegrationProviderError(\n 'provider-unavailable',\n error instanceof Error ? error.message : 'GitHub installation token mint failed',\n );\n}\n"],"names":["IntegrationProviderError","z","GithubIntegrationProviderError","TOKEN_REFRESH_MARGIN_MS","TOKEN_VALIDITY_BUFFER_MS","TRANSIENT_BACKOFF_MIN_MS","TRANSIENT_BACKOFF_MAX_MS","TERMINAL_BACKOFF_MS","providerErrorReasons","providerErrorReasonSchema","enum","terminalMintErrorReasons","Set","providerErrorReasonSchemaCoversUnion","installationTokenEnvelopeSchema","object","token","string","min","optional","expiresAt","datetime","permissions","record","backoffUntil","backoffReason","githubInstallationTokenNamespace","installationId","encodeInstallationTokenEnvelope","envelope","JSON","stringify","undefined","toISOString","parseInstallationTokenEnvelope","raw","parsed","parse","result","safeParse","success","data","Date","usable","now","needsRefresh","getTime","stillValid","backoffActive","classifyMintError","error","reason","retryAfterSeconds","class","mintErrorClassForReason","has","backoffMs","classified","retryAfterMs","Math","max","providerErrorFromBackoff","ceil","toProviderError","message","Error"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../src/api/installation-token-envelope.ts"],"sourcesContent":["import {\n IntegrationProviderError,\n type IntegrationProviderErrorReason,\n} from '@shipfox/api-integration-spi';\nimport {z} from 'zod';\nimport {GithubIntegrationProviderError} from '#core/errors.js';\n\nexport const TOKEN_REFRESH_MARGIN_MS = 5 * 60 * 1000;\nexport const TOKEN_VALIDITY_BUFFER_MS = 60 * 1000;\nexport const TRANSIENT_BACKOFF_MIN_MS = 30 * 1000;\nexport const TRANSIENT_BACKOFF_MAX_MS = 5 * 60 * 1000;\nexport const TERMINAL_BACKOFF_MS = 15 * 60 * 1000;\n\nconst providerErrorReasons = [\n 'repository-not-found',\n 'installation-not-found',\n 'file-not-found',\n 'access-denied',\n 'rate-limited',\n 'timeout',\n 'provider-unavailable',\n 'malformed-provider-response',\n 'content-too-large',\n 'too-many-files',\n] as const satisfies readonly IntegrationProviderErrorReason[];\n\nconst providerErrorReasonSchema = z.enum(providerErrorReasons);\nconst terminalMintErrorReasons = new Set<IntegrationProviderErrorReason>([\n 'access-denied',\n 'installation-not-found',\n 'malformed-provider-response',\n]);\n\ntype MissingProviderErrorReason = Exclude<\n IntegrationProviderErrorReason,\n (typeof providerErrorReasons)[number]\n>;\nconst providerErrorReasonSchemaCoversUnion: Record<MissingProviderErrorReason, never> = {};\nvoid providerErrorReasonSchemaCoversUnion;\n\nconst installationTokenEnvelopeSchema = z.object({\n token: z.string().min(1).optional(),\n expiresAt: z.string().datetime().optional(),\n permissions: z.record(z.string(), z.enum(['read', 'write', 'admin'])).optional(),\n backoffUntil: z.string().datetime().optional(),\n backoffReason: providerErrorReasonSchema.optional(),\n});\n\nexport interface InstallationTokenEnvelope {\n token?: string | undefined;\n expiresAt?: Date | undefined;\n permissions?: Record<string, 'read' | 'write' | 'admin'> | undefined;\n backoffUntil?: Date | undefined;\n backoffReason?: IntegrationProviderErrorReason | undefined;\n}\n\nexport type MintErrorClass = 'transient' | 'terminal';\n\nexport interface ClassifiedMintError {\n class: MintErrorClass;\n reason: IntegrationProviderErrorReason;\n retryAfterSeconds?: number | undefined;\n}\n\nexport function githubInstallationTokenNamespace(installationId: number): string {\n return `system/github/installation-token/${installationId}`;\n}\n\nexport function encodeInstallationTokenEnvelope(envelope: InstallationTokenEnvelope): string {\n return JSON.stringify({\n ...(envelope.token !== undefined && {token: envelope.token}),\n ...(envelope.expiresAt !== undefined && {expiresAt: envelope.expiresAt.toISOString()}),\n ...(envelope.permissions !== undefined && {permissions: envelope.permissions}),\n ...(envelope.backoffUntil !== undefined && {\n backoffUntil: envelope.backoffUntil.toISOString(),\n }),\n ...(envelope.backoffReason !== undefined && {backoffReason: envelope.backoffReason}),\n });\n}\n\nexport function parseInstallationTokenEnvelope(raw: string): InstallationTokenEnvelope | undefined {\n let parsed: unknown;\n try {\n parsed = JSON.parse(raw);\n } catch {\n return undefined;\n }\n\n const result = installationTokenEnvelopeSchema.safeParse(parsed);\n if (!result.success) return undefined;\n\n return {\n token: result.data.token,\n expiresAt: result.data.expiresAt ? new Date(result.data.expiresAt) : undefined,\n permissions: result.data.permissions,\n backoffUntil: result.data.backoffUntil ? new Date(result.data.backoffUntil) : undefined,\n backoffReason: result.data.backoffReason,\n };\n}\n\nexport function usable(\n envelope: InstallationTokenEnvelope | undefined,\n now: Date,\n): envelope is InstallationTokenEnvelope & {token: string; expiresAt: Date} {\n return (\n envelope?.token !== undefined &&\n envelope.expiresAt !== undefined &&\n !needsRefresh(envelope.expiresAt, now)\n );\n}\n\nexport function needsRefresh(expiresAt: Date, now: Date): boolean {\n return expiresAt.getTime() <= now.getTime() + TOKEN_REFRESH_MARGIN_MS;\n}\n\nexport function stillValid(expiresAt: Date | undefined, now: Date): boolean {\n return expiresAt !== undefined && expiresAt.getTime() > now.getTime() + TOKEN_VALIDITY_BUFFER_MS;\n}\n\nexport function backoffActive(envelope: InstallationTokenEnvelope | undefined, now: Date): boolean {\n return (\n envelope?.backoffUntil !== undefined &&\n envelope.backoffReason !== undefined &&\n envelope.backoffUntil.getTime() > now.getTime()\n );\n}\n\nexport function classifyMintError(error: unknown): ClassifiedMintError {\n if (error instanceof IntegrationProviderError) {\n return {\n reason: error.reason,\n retryAfterSeconds: error.retryAfterSeconds,\n class: mintErrorClassForReason(error.reason),\n };\n }\n\n return {reason: 'provider-unavailable', class: 'transient'};\n}\n\nexport function mintErrorClassForReason(reason: IntegrationProviderErrorReason): MintErrorClass {\n return terminalMintErrorReasons.has(reason) ? 'terminal' : 'transient';\n}\n\nexport function backoffMs(classified: ClassifiedMintError): number {\n if (classified.class === 'terminal') return TERMINAL_BACKOFF_MS;\n\n const retryAfterMs = (classified.retryAfterSeconds ?? 0) * 1000;\n return Math.min(TRANSIENT_BACKOFF_MAX_MS, Math.max(TRANSIENT_BACKOFF_MIN_MS, retryAfterMs));\n}\n\nexport function providerErrorFromBackoff(\n reason: IntegrationProviderErrorReason,\n retryAfterMs: number,\n): GithubIntegrationProviderError {\n return new GithubIntegrationProviderError(\n reason,\n `GitHub installation token mint is backed off after ${reason}`,\n Math.max(1, Math.ceil(retryAfterMs / 1000)),\n );\n}\n\nexport function toProviderError(error: unknown): GithubIntegrationProviderError {\n if (error instanceof GithubIntegrationProviderError) return error;\n if (error instanceof IntegrationProviderError) {\n return new GithubIntegrationProviderError(error.reason, error.message, error.retryAfterSeconds);\n }\n return new GithubIntegrationProviderError(\n 'provider-unavailable',\n error instanceof Error ? error.message : 'GitHub installation token mint failed',\n );\n}\n"],"names":["IntegrationProviderError","z","GithubIntegrationProviderError","TOKEN_REFRESH_MARGIN_MS","TOKEN_VALIDITY_BUFFER_MS","TRANSIENT_BACKOFF_MIN_MS","TRANSIENT_BACKOFF_MAX_MS","TERMINAL_BACKOFF_MS","providerErrorReasons","providerErrorReasonSchema","enum","terminalMintErrorReasons","Set","providerErrorReasonSchemaCoversUnion","installationTokenEnvelopeSchema","object","token","string","min","optional","expiresAt","datetime","permissions","record","backoffUntil","backoffReason","githubInstallationTokenNamespace","installationId","encodeInstallationTokenEnvelope","envelope","JSON","stringify","undefined","toISOString","parseInstallationTokenEnvelope","raw","parsed","parse","result","safeParse","success","data","Date","usable","now","needsRefresh","getTime","stillValid","backoffActive","classifyMintError","error","reason","retryAfterSeconds","class","mintErrorClassForReason","has","backoffMs","classified","retryAfterMs","Math","max","providerErrorFromBackoff","ceil","toProviderError","message","Error"],"mappings":"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"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { GetIntegrationConnectionByIdFn } from '@shipfox/api-integration-
|
|
1
|
+
import type { GetIntegrationConnectionByIdFn } from '@shipfox/api-integration-spi';
|
|
2
2
|
import { type GithubInstallationAccessToken } from './client.js';
|
|
3
3
|
import { type InstallationTokenCache, type InstallationTokenSecretStore, type SharedInstallationTokenCacheOptions } from './shared-installation-token-cache.js';
|
|
4
4
|
export interface GithubInstallationTokenProvider {
|
|
@@ -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,
|
|
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;AAK/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;AAmLD,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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/api/installation-token-provider.ts"],"sourcesContent":["import type {GetIntegrationConnectionByIdFn} from '@shipfox/api-integration-core-dto';\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 {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,4 +1,4 @@
|
|
|
1
|
-
export { DEFAULT_JOB_LOG_TAIL_LINES } from '
|
|
1
|
+
export { DEFAULT_JOB_LOG_TAIL_LINES } from './github-agent-tool-catalog.js';
|
|
2
2
|
export declare const DEFAULT_JOB_LOG_CONTENT_WINDOW_LINES = 5000;
|
|
3
3
|
export declare const MAX_JOB_LOG_RING_BUFFER_LINES = 100000;
|
|
4
4
|
export declare const MAX_JOB_LOG_LINE_BYTES: number;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"actions-logs.d.ts","sourceRoot":"","sources":["../../src/core/actions-logs.ts"],"names":[],"mappings":"AAGA,OAAO,EAAC,0BAA0B,EAAC,MAAM,
|
|
1
|
+
{"version":3,"file":"actions-logs.d.ts","sourceRoot":"","sources":["../../src/core/actions-logs.ts"],"names":[],"mappings":"AAGA,OAAO,EAAC,0BAA0B,EAAC,MAAM,gCAAgC,CAAC;AAC1E,eAAO,MAAM,oCAAoC,OAAO,CAAC;AACzD,eAAO,MAAM,6BAA6B,SAAS,CAAC;AACpD,eAAO,MAAM,sBAAsB,QAAmB,CAAC;AACvD,eAAO,MAAM,gCAAgC,OAAO,CAAC;AAErD,MAAM,WAAW,kBAAkB;IACjC,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC1B,UAAU,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAChC,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY,CAAC,EAAE,CAAC,MAAM,OAAO,CAAC,MAAM,CAAC,CAAC,GAAG,SAAS,CAAC;CACpD;AAED,MAAM,WAAW,kCAAkC;IACjD,GAAG,EAAE,kBAAkB,CAAC;IACxB,aAAa,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IACpC,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC/B,iBAAiB,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CACxC;AAED,MAAM,WAAW,mCAAmC;IAClD,IAAI,EAAE,SAAS,kBAAkB,EAAE,CAAC;IACpC,KAAK,EAAE,MAAM,CAAC;IACd,aAAa,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IACpC,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC/B,iBAAiB,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CACxC;AAED,MAAM,MAAM,kBAAkB,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAEzD,MAAM,WAAW,yBAAyB;IACxC,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,IAAI,CAAC,EAAE,kBAAkB,EAAE,GAAG,SAAS,CAAC;IACxC,aAAa,CAAC,EAAE;QAAC,OAAO,EAAE,OAAO,CAAC;QAAC,IAAI,EAAE,OAAO,CAAA;KAAC,GAAG,SAAS,CAAC;CAC/D;AAED,wBAAgB,6BAA6B,CAC3C,KAAK,EAAE,kCAAkC,GACxC,OAAO,CAAC,kBAAkB,CAAC,CAE7B;AAED,wBAAsB,8BAA8B,CAClD,KAAK,EAAE,mCAAmC,GACzC,OAAO,CAAC,yBAAyB,CAAC,CAiCpC;AAkCD,wBAAgB,0BAA0B,CACxC,OAAO,EAAE,MAAM,EACf,OAAO,GAAE;IAAC,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAAC,iBAAiB,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;CAAM,GACrF;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAA;CAAC,CAWvC"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Buffer } from 'node:buffer';
|
|
2
|
-
import { DEFAULT_JOB_LOG_TAIL_LINES } from '
|
|
3
|
-
export { DEFAULT_JOB_LOG_TAIL_LINES } from '
|
|
2
|
+
import { DEFAULT_JOB_LOG_TAIL_LINES } from './github-agent-tool-catalog.js';
|
|
3
|
+
export { DEFAULT_JOB_LOG_TAIL_LINES } from './github-agent-tool-catalog.js';
|
|
4
4
|
export const DEFAULT_JOB_LOG_CONTENT_WINDOW_LINES = 5000;
|
|
5
5
|
export const MAX_JOB_LOG_RING_BUFFER_LINES = 100000;
|
|
6
6
|
export const MAX_JOB_LOG_LINE_BYTES = 10 * 1024 * 1024;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/core/actions-logs.ts"],"sourcesContent":["import {Buffer} from 'node:buffer';\nimport {DEFAULT_JOB_LOG_TAIL_LINES} from '@shipfox/api-integration-github-dto';\n\nexport {DEFAULT_JOB_LOG_TAIL_LINES} from '@shipfox/api-integration-github-dto';\nexport const DEFAULT_JOB_LOG_CONTENT_WINDOW_LINES = 5000;\nexport const MAX_JOB_LOG_RING_BUFFER_LINES = 100000;\nexport const MAX_JOB_LOG_LINE_BYTES = 10 * 1024 * 1024;\nexport const MAX_TRUNCATED_JOB_LOG_LINE_CHARS = 1000;\n\nexport interface GithubJobLogSource {\n jobId: number;\n name?: string | undefined;\n conclusion?: string | undefined;\n logsUrl: string;\n fetchContent?: (() => Promise<string>) | undefined;\n}\n\nexport interface BuildGithubSingleJobLogResultInput {\n job: GithubJobLogSource;\n returnContent?: boolean | undefined;\n tailLines?: number | undefined;\n contentWindowSize?: number | undefined;\n}\n\nexport interface BuildGithubFailedJobLogsResultInput {\n jobs: readonly GithubJobLogSource[];\n runId: number;\n returnContent?: boolean | undefined;\n tailLines?: number | undefined;\n contentWindowSize?: number | undefined;\n}\n\nexport type GithubJobLogResult = Record<string, unknown>;\n\nexport interface GithubFailedJobLogsResult {\n message: string;\n run_id: number;\n total_jobs: number;\n failed_jobs: number;\n logs?: GithubJobLogResult[] | undefined;\n return_format?: {content: boolean; urls: boolean} | undefined;\n}\n\nexport function buildGithubSingleJobLogResult(\n input: BuildGithubSingleJobLogResultInput,\n): Promise<GithubJobLogResult> {\n return buildGithubJobLogData(input);\n}\n\nexport async function buildGithubFailedJobLogsResult(\n input: BuildGithubFailedJobLogsResultInput,\n): Promise<GithubFailedJobLogsResult> {\n const failedJobs = input.jobs.filter((job) => job.conclusion === 'failure');\n\n if (failedJobs.length === 0) {\n return {\n message: 'No failed jobs found in this workflow run',\n run_id: input.runId,\n total_jobs: input.jobs.length,\n failed_jobs: 0,\n };\n }\n\n const logs: GithubJobLogResult[] = [];\n for (const job of failedJobs) {\n try {\n logs.push(await buildGithubJobLogData({job, ...input}));\n } catch (error) {\n logs.push({\n job_id: job.jobId,\n ...(job.name ? {job_name: job.name} : {}),\n error: error instanceof Error ? error.message : String(error),\n });\n }\n }\n\n return {\n message: `Retrieved logs for ${failedJobs.length} failed jobs`,\n run_id: input.runId,\n total_jobs: input.jobs.length,\n failed_jobs: failedJobs.length,\n logs,\n return_format: {content: input.returnContent === true, urls: input.returnContent !== true},\n };\n}\n\nfunction buildGithubJobLogData(\n input: BuildGithubSingleJobLogResultInput,\n): Promise<GithubJobLogResult> {\n const result: GithubJobLogResult = {\n job_id: input.job.jobId,\n ...(input.job.name ? {job_name: input.job.name} : {}),\n };\n\n if (input.returnContent === true) {\n const content = input.job.fetchContent ? input.job.fetchContent() : Promise.resolve('');\n return content.then((logsContent) => {\n const processed = processGithubJobLogContent(logsContent, {\n tailLines: input.tailLines,\n contentWindowSize: input.contentWindowSize,\n });\n return {\n ...result,\n logs_content: processed.content,\n message: 'Job logs content retrieved successfully',\n original_length: processed.totalLines,\n };\n });\n }\n\n return Promise.resolve({\n ...result,\n logs_url: input.job.logsUrl,\n message: 'Job logs are available for download',\n note: 'The logs_url provides a download link for the individual job logs in plain text format.\\nUse return_content=true to get the actual log content.',\n });\n}\n\nexport function processGithubJobLogContent(\n content: string,\n options: {tailLines?: number | undefined; contentWindowSize?: number | undefined} = {},\n): {content: string; totalLines: number} {\n const tailLines = normalizeTailLines(options.tailLines);\n const maxLines = Math.min(\n tailLines,\n options.contentWindowSize ?? DEFAULT_JOB_LOG_CONTENT_WINDOW_LINES,\n );\n const processed = processResponseAsRingBufferToEnd(content, maxLines);\n const lines = processed.content.split('\\n');\n const finalLines = lines.length > tailLines ? lines.slice(-tailLines) : lines;\n\n return {content: finalLines.join('\\n'), totalLines: processed.totalLines};\n}\n\nfunction normalizeTailLines(tailLines: number | undefined): number {\n if (tailLines === undefined) return DEFAULT_JOB_LOG_TAIL_LINES;\n if (!Number.isFinite(tailLines) || tailLines <= 0) return DEFAULT_JOB_LOG_TAIL_LINES;\n return Math.trunc(tailLines);\n}\n\nfunction processResponseAsRingBufferToEnd(\n content: string,\n maxJobLogLines: number,\n): {content: string; totalLines: number} {\n let maxLines = Math.trunc(maxJobLogLines);\n if (maxLines <= 0) maxLines = DEFAULT_JOB_LOG_TAIL_LINES;\n if (maxLines > MAX_JOB_LOG_RING_BUFFER_LINES) maxLines = MAX_JOB_LOG_RING_BUFFER_LINES;\n\n const lines = new Array<string>(maxLines);\n const validLines = new Array<boolean>(maxLines).fill(false);\n let totalLines = 0;\n let writeIndex = 0;\n\n const storeLine = (line: string) => {\n lines[writeIndex] = truncateLongLine(line);\n validLines[writeIndex] = true;\n totalLines += 1;\n writeIndex = (writeIndex + 1) % maxLines;\n };\n\n for (const line of splitLogLines(content)) {\n storeLine(line);\n }\n\n const result: string[] = [];\n const linesInBuffer = Math.min(totalLines, maxLines);\n const startIndex = totalLines > maxLines ? writeIndex : 0;\n\n for (let offset = 0; offset < linesInBuffer; offset += 1) {\n const index = (startIndex + offset) % maxLines;\n if (validLines[index]) result.push(lines[index] ?? '');\n }\n\n return {content: result.join('\\n'), totalLines};\n}\n\nfunction splitLogLines(content: string): string[] {\n if (content.length === 0) return [];\n const lines = content.split('\\n');\n if (content.endsWith('\\n')) lines.pop();\n return lines;\n}\n\nfunction truncateLongLine(line: string): string {\n if (Buffer.byteLength(line, 'utf8') < MAX_JOB_LOG_LINE_BYTES) return line;\n return `${Array.from(line).slice(0, MAX_TRUNCATED_JOB_LOG_LINE_CHARS).join('')}...\\n[TRUNCATED]`;\n}\n"],"names":["Buffer","DEFAULT_JOB_LOG_TAIL_LINES","DEFAULT_JOB_LOG_CONTENT_WINDOW_LINES","MAX_JOB_LOG_RING_BUFFER_LINES","MAX_JOB_LOG_LINE_BYTES","MAX_TRUNCATED_JOB_LOG_LINE_CHARS","buildGithubSingleJobLogResult","input","buildGithubJobLogData","buildGithubFailedJobLogsResult","failedJobs","jobs","filter","job","conclusion","length","message","run_id","runId","total_jobs","failed_jobs","logs","push","error","job_id","jobId","name","job_name","Error","String","return_format","content","returnContent","urls","result","fetchContent","Promise","resolve","then","logsContent","processed","processGithubJobLogContent","tailLines","contentWindowSize","logs_content","original_length","totalLines","logs_url","logsUrl","note","options","normalizeTailLines","maxLines","Math","min","processResponseAsRingBufferToEnd","lines","split","finalLines","slice","join","undefined","Number","isFinite","trunc","maxJobLogLines","Array","validLines","fill","writeIndex","storeLine","line","truncateLongLine","splitLogLines","linesInBuffer","startIndex","offset","index","endsWith","pop","byteLength","from"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../src/core/actions-logs.ts"],"sourcesContent":["import {Buffer} from 'node:buffer';\nimport {DEFAULT_JOB_LOG_TAIL_LINES} from './github-agent-tool-catalog.js';\n\nexport {DEFAULT_JOB_LOG_TAIL_LINES} from './github-agent-tool-catalog.js';\nexport const DEFAULT_JOB_LOG_CONTENT_WINDOW_LINES = 5000;\nexport const MAX_JOB_LOG_RING_BUFFER_LINES = 100000;\nexport const MAX_JOB_LOG_LINE_BYTES = 10 * 1024 * 1024;\nexport const MAX_TRUNCATED_JOB_LOG_LINE_CHARS = 1000;\n\nexport interface GithubJobLogSource {\n jobId: number;\n name?: string | undefined;\n conclusion?: string | undefined;\n logsUrl: string;\n fetchContent?: (() => Promise<string>) | undefined;\n}\n\nexport interface BuildGithubSingleJobLogResultInput {\n job: GithubJobLogSource;\n returnContent?: boolean | undefined;\n tailLines?: number | undefined;\n contentWindowSize?: number | undefined;\n}\n\nexport interface BuildGithubFailedJobLogsResultInput {\n jobs: readonly GithubJobLogSource[];\n runId: number;\n returnContent?: boolean | undefined;\n tailLines?: number | undefined;\n contentWindowSize?: number | undefined;\n}\n\nexport type GithubJobLogResult = Record<string, unknown>;\n\nexport interface GithubFailedJobLogsResult {\n message: string;\n run_id: number;\n total_jobs: number;\n failed_jobs: number;\n logs?: GithubJobLogResult[] | undefined;\n return_format?: {content: boolean; urls: boolean} | undefined;\n}\n\nexport function buildGithubSingleJobLogResult(\n input: BuildGithubSingleJobLogResultInput,\n): Promise<GithubJobLogResult> {\n return buildGithubJobLogData(input);\n}\n\nexport async function buildGithubFailedJobLogsResult(\n input: BuildGithubFailedJobLogsResultInput,\n): Promise<GithubFailedJobLogsResult> {\n const failedJobs = input.jobs.filter((job) => job.conclusion === 'failure');\n\n if (failedJobs.length === 0) {\n return {\n message: 'No failed jobs found in this workflow run',\n run_id: input.runId,\n total_jobs: input.jobs.length,\n failed_jobs: 0,\n };\n }\n\n const logs: GithubJobLogResult[] = [];\n for (const job of failedJobs) {\n try {\n logs.push(await buildGithubJobLogData({job, ...input}));\n } catch (error) {\n logs.push({\n job_id: job.jobId,\n ...(job.name ? {job_name: job.name} : {}),\n error: error instanceof Error ? error.message : String(error),\n });\n }\n }\n\n return {\n message: `Retrieved logs for ${failedJobs.length} failed jobs`,\n run_id: input.runId,\n total_jobs: input.jobs.length,\n failed_jobs: failedJobs.length,\n logs,\n return_format: {content: input.returnContent === true, urls: input.returnContent !== true},\n };\n}\n\nfunction buildGithubJobLogData(\n input: BuildGithubSingleJobLogResultInput,\n): Promise<GithubJobLogResult> {\n const result: GithubJobLogResult = {\n job_id: input.job.jobId,\n ...(input.job.name ? {job_name: input.job.name} : {}),\n };\n\n if (input.returnContent === true) {\n const content = input.job.fetchContent ? input.job.fetchContent() : Promise.resolve('');\n return content.then((logsContent) => {\n const processed = processGithubJobLogContent(logsContent, {\n tailLines: input.tailLines,\n contentWindowSize: input.contentWindowSize,\n });\n return {\n ...result,\n logs_content: processed.content,\n message: 'Job logs content retrieved successfully',\n original_length: processed.totalLines,\n };\n });\n }\n\n return Promise.resolve({\n ...result,\n logs_url: input.job.logsUrl,\n message: 'Job logs are available for download',\n note: 'The logs_url provides a download link for the individual job logs in plain text format.\\nUse return_content=true to get the actual log content.',\n });\n}\n\nexport function processGithubJobLogContent(\n content: string,\n options: {tailLines?: number | undefined; contentWindowSize?: number | undefined} = {},\n): {content: string; totalLines: number} {\n const tailLines = normalizeTailLines(options.tailLines);\n const maxLines = Math.min(\n tailLines,\n options.contentWindowSize ?? DEFAULT_JOB_LOG_CONTENT_WINDOW_LINES,\n );\n const processed = processResponseAsRingBufferToEnd(content, maxLines);\n const lines = processed.content.split('\\n');\n const finalLines = lines.length > tailLines ? lines.slice(-tailLines) : lines;\n\n return {content: finalLines.join('\\n'), totalLines: processed.totalLines};\n}\n\nfunction normalizeTailLines(tailLines: number | undefined): number {\n if (tailLines === undefined) return DEFAULT_JOB_LOG_TAIL_LINES;\n if (!Number.isFinite(tailLines) || tailLines <= 0) return DEFAULT_JOB_LOG_TAIL_LINES;\n return Math.trunc(tailLines);\n}\n\nfunction processResponseAsRingBufferToEnd(\n content: string,\n maxJobLogLines: number,\n): {content: string; totalLines: number} {\n let maxLines = Math.trunc(maxJobLogLines);\n if (maxLines <= 0) maxLines = DEFAULT_JOB_LOG_TAIL_LINES;\n if (maxLines > MAX_JOB_LOG_RING_BUFFER_LINES) maxLines = MAX_JOB_LOG_RING_BUFFER_LINES;\n\n const lines = new Array<string>(maxLines);\n const validLines = new Array<boolean>(maxLines).fill(false);\n let totalLines = 0;\n let writeIndex = 0;\n\n const storeLine = (line: string) => {\n lines[writeIndex] = truncateLongLine(line);\n validLines[writeIndex] = true;\n totalLines += 1;\n writeIndex = (writeIndex + 1) % maxLines;\n };\n\n for (const line of splitLogLines(content)) {\n storeLine(line);\n }\n\n const result: string[] = [];\n const linesInBuffer = Math.min(totalLines, maxLines);\n const startIndex = totalLines > maxLines ? writeIndex : 0;\n\n for (let offset = 0; offset < linesInBuffer; offset += 1) {\n const index = (startIndex + offset) % maxLines;\n if (validLines[index]) result.push(lines[index] ?? '');\n }\n\n return {content: result.join('\\n'), totalLines};\n}\n\nfunction splitLogLines(content: string): string[] {\n if (content.length === 0) return [];\n const lines = content.split('\\n');\n if (content.endsWith('\\n')) lines.pop();\n return lines;\n}\n\nfunction truncateLongLine(line: string): string {\n if (Buffer.byteLength(line, 'utf8') < MAX_JOB_LOG_LINE_BYTES) return line;\n return `${Array.from(line).slice(0, MAX_TRUNCATED_JOB_LOG_LINE_CHARS).join('')}...\\n[TRUNCATED]`;\n}\n"],"names":["Buffer","DEFAULT_JOB_LOG_TAIL_LINES","DEFAULT_JOB_LOG_CONTENT_WINDOW_LINES","MAX_JOB_LOG_RING_BUFFER_LINES","MAX_JOB_LOG_LINE_BYTES","MAX_TRUNCATED_JOB_LOG_LINE_CHARS","buildGithubSingleJobLogResult","input","buildGithubJobLogData","buildGithubFailedJobLogsResult","failedJobs","jobs","filter","job","conclusion","length","message","run_id","runId","total_jobs","failed_jobs","logs","push","error","job_id","jobId","name","job_name","Error","String","return_format","content","returnContent","urls","result","fetchContent","Promise","resolve","then","logsContent","processed","processGithubJobLogContent","tailLines","contentWindowSize","logs_content","original_length","totalLines","logs_url","logsUrl","note","options","normalizeTailLines","maxLines","Math","min","processResponseAsRingBufferToEnd","lines","split","finalLines","slice","join","undefined","Number","isFinite","trunc","maxJobLogLines","Array","validLines","fill","writeIndex","storeLine","line","truncateLongLine","splitLogLines","linesInBuffer","startIndex","offset","index","endsWith","pop","byteLength","from"],"mappings":"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"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export { buildGithubAgentToolSelectionCatalog } from '
|
|
1
|
+
export { buildGithubAgentToolSelectionCatalog } from './github-agent-tool-catalog.js';
|
|
2
2
|
//# sourceMappingURL=agent-tool-selection.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"agent-tool-selection.d.ts","sourceRoot":"","sources":["../../src/core/agent-tool-selection.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,oCAAoC,EAAC,MAAM,
|
|
1
|
+
{"version":3,"file":"agent-tool-selection.d.ts","sourceRoot":"","sources":["../../src/core/agent-tool-selection.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,oCAAoC,EAAC,MAAM,gCAAgC,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/core/agent-tool-selection.ts"],"sourcesContent":["export {buildGithubAgentToolSelectionCatalog} from '
|
|
1
|
+
{"version":3,"sources":["../../src/core/agent-tool-selection.ts"],"sourcesContent":["export {buildGithubAgentToolSelectionCatalog} from './github-agent-tool-catalog.js';\n"],"names":["buildGithubAgentToolSelectionCatalog"],"mappings":"AAAA,SAAQA,oCAAoC,QAAO,iCAAiC"}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import type { AgentToolSelectionCatalog, AgentToolSession, AgentToolsProvider, IntegrationConnection, OpenAgentToolsSessionInput } from '@shipfox/api-integration-
|
|
2
|
-
import { type GithubAgentToolCatalogEntry, type GithubAgentToolRequiredScope } from '@shipfox/api-integration-github-dto';
|
|
1
|
+
import type { AgentToolSelectionCatalog, AgentToolSession, AgentToolsProvider, IntegrationConnection, OpenAgentToolsSessionInput } from '@shipfox/api-integration-spi';
|
|
3
2
|
import { type GithubInstallationTokenProvider } from '#api/installation-token-provider.js';
|
|
4
3
|
import type { GithubInstallation } from '#db/installations.js';
|
|
5
|
-
|
|
6
|
-
export {
|
|
4
|
+
import { type GithubAgentToolCatalogEntry, type GithubAgentToolRequiredScope } from './github-agent-tool-catalog.js';
|
|
5
|
+
export type { GithubAgentToolCatalogEntry, GithubAgentToolCategory, GithubAgentToolId, GithubAgentToolPermission, GithubAgentToolPermissionAccess, GithubAgentToolRequiredPermission, GithubAgentToolRequiredScope, GithubAgentToolSensitivity, } from './github-agent-tool-catalog.js';
|
|
6
|
+
export { buildGithubAgentToolSelectionCatalog, DEFAULT_JOB_LOG_TAIL_LINES, githubAgentToolCatalog, githubAgentToolSelectionCatalog, } from './github-agent-tool-catalog.js';
|
|
7
7
|
type GithubIntegrationConnection = IntegrationConnection<'github'>;
|
|
8
8
|
type GithubToolCallResult = {
|
|
9
9
|
isError?: boolean | undefined;
|
|
@@ -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,
|
|
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;AAEtC,OAAO,EAEL,KAAK,+BAA+B,EACrC,MAAM,qCAAqC,CAAC;AAE7C,OAAO,KAAK,EAAC,kBAAkB,EAAC,MAAM,sBAAsB,CAAC;AAE7D,OAAO,EACL,KAAK,2BAA2B,EAEhC,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;AAEF,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;gBAEnC,OAAO,GAAE,+BAAoC;IAI1E,OAAO,IAAI,SAAS,2BAA2B,EAAE;IAIjD,gBAAgB,IAAI,yBAAyB;IAIvC,WAAW,CACf,KAAK,EAAE,0BAA0B,CAAC,2BAA2B,EAAE,4BAA4B,CAAC,GAC3F,OAAO,CAAC,gBAAgB,CAAC,oBAAoB,CAAC,CAAC;CA+CnD;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,gBAAgB;IAC/B,OAAO,CAAC,KAAK,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC;QAAC,IAAI,EAAE,OAAO,CAAA;KAAC,CAAC,CAAC;CACvF;AAED,MAAM,MAAM,uBAAuB,GAAG,CAAC,KAAK,EAAE,MAAM,KAAK,gBAAgB,CAAC"}
|
package/dist/core/agent-tools.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { githubAgentToolCatalog, githubAgentToolSelectionCatalog } from '@shipfox/api-integration-github-dto';
|
|
2
1
|
import { Octokit } from 'octokit';
|
|
3
2
|
import { createGithubInstallationTokenProvider } from '#api/installation-token-provider.js';
|
|
4
3
|
import { normalizedGithubApiBaseUrl } from '#config.js';
|
|
5
4
|
import { GithubIntegrationProviderError } from './errors.js';
|
|
6
|
-
|
|
5
|
+
import { githubAgentToolCatalog, githubAgentToolSelectionCatalog } from './github-agent-tool-catalog.js';
|
|
6
|
+
export { buildGithubAgentToolSelectionCatalog, DEFAULT_JOB_LOG_TAIL_LINES, githubAgentToolCatalog, githubAgentToolSelectionCatalog } from './github-agent-tool-catalog.js';
|
|
7
7
|
export class GithubAgentToolsProvider {
|
|
8
8
|
constructor(options = {}){
|
|
9
9
|
this.options = options;
|