@shipfox/api-integration-github 12.7.0 → 14.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/CHANGELOG.md +23 -0
  3. package/dist/api/client.d.ts +21 -3
  4. package/dist/api/client.d.ts.map +1 -1
  5. package/dist/api/client.js +115 -6
  6. package/dist/api/client.js.map +1 -1
  7. package/dist/api/installation-token-envelope.d.ts.map +1 -1
  8. package/dist/api/installation-token-envelope.js.map +1 -1
  9. package/dist/core/agent-tools.d.ts.map +1 -1
  10. package/dist/core/agent-tools.js +2 -6
  11. package/dist/core/agent-tools.js.map +1 -1
  12. package/dist/core/bot-identity.d.ts +4 -0
  13. package/dist/core/bot-identity.d.ts.map +1 -0
  14. package/dist/core/bot-identity.js +15 -0
  15. package/dist/core/bot-identity.js.map +1 -0
  16. package/dist/core/source-control.d.ts +4 -2
  17. package/dist/core/source-control.d.ts.map +1 -1
  18. package/dist/core/source-control.js +42 -11
  19. package/dist/core/source-control.js.map +1 -1
  20. package/dist/index.d.ts +78 -0
  21. package/dist/index.d.ts.map +1 -1
  22. package/dist/index.js +2 -0
  23. package/dist/index.js.map +1 -1
  24. package/dist/tsconfig.test.tsbuildinfo +1 -1
  25. package/package.json +3 -3
  26. package/src/api/client.test.ts +267 -9
  27. package/src/api/client.ts +184 -9
  28. package/src/api/installation-token-envelope.ts +3 -1
  29. package/src/core/agent-tools.test.ts +1 -0
  30. package/src/core/agent-tools.ts +2 -9
  31. package/src/core/bot-identity.test.ts +19 -0
  32. package/src/core/bot-identity.ts +19 -0
  33. package/src/core/install.test.ts +3 -0
  34. package/src/core/source-control.test.ts +180 -1
  35. package/src/core/source-control.ts +65 -11
  36. package/src/index.test.ts +3 -1
  37. package/src/index.ts +2 -0
  38. package/src/presentation/routes/install.test.ts +3 -0
  39. package/tsconfig.build.tsbuildinfo +1 -1
@@ -1,2 +1,2 @@
1
1
  $ shipfox-swc
2
- Successfully compiled: 31 files with swc (417.05ms)
2
+ Successfully compiled: 32 files with swc (433.51ms)
package/CHANGELOG.md CHANGED
@@ -1,5 +1,28 @@
1
1
  # @shipfox/api-integration-github
2
2
 
3
+ ## 14.0.0
4
+
5
+ ### Major Changes
6
+
7
+ - 18e9bad: Adds source-control ref resolution that pins branch and tag names to commits.
8
+
9
+ ### Minor Changes
10
+
11
+ - 1b71a66: Exposes each provider's event catalog and the fixed-event providers on the integration validation context. Every provider now refuses the reserved `manual` and `cron` connection slugs.
12
+
13
+ ### Patch Changes
14
+
15
+ - Updated dependencies [18e9bad]
16
+ - Updated dependencies [1b71a66]
17
+ - @shipfox/api-integration-spi@2.0.0
18
+ - @shipfox/api-integration-github-dto@14.0.0
19
+
20
+ ## 13.1.0
21
+
22
+ ### Minor Changes
23
+
24
+ - 7ae5396: Attributes new runner commits to the GitHub App bot account. Existing runner workspaces keep their first configured author until recreated. Update downstream author filters that match the previous App ID email address.
25
+
3
26
  ## 12.7.0
4
27
 
5
28
  ### Minor Changes
@@ -38,11 +38,24 @@ export interface GithubFileContent {
38
38
  content: string;
39
39
  size: number;
40
40
  }
41
+ export interface GithubCommit {
42
+ sha: string;
43
+ }
41
44
  export interface GithubUserInstallationPage {
42
45
  installationIds: number[];
43
46
  nextCursor: string | null;
44
47
  }
45
- export interface GithubApiClient {
48
+ export interface GithubBotUser {
49
+ id: number;
50
+ login: string;
51
+ }
52
+ export interface GithubBotUserClient {
53
+ getBotUser(input: {
54
+ username: string;
55
+ installationAccessToken: string;
56
+ }): Promise<GithubBotUser>;
57
+ }
58
+ export interface GithubApiClient extends Partial<GithubBotUserClient> {
46
59
  exchangeOAuthCode(code: string): Promise<string>;
47
60
  listUserInstallations(input: {
48
61
  userAccessToken: string;
@@ -72,6 +85,11 @@ export interface GithubApiClient {
72
85
  ref: string;
73
86
  path: string;
74
87
  }): Promise<GithubFileContent>;
88
+ listRepositoryCommits(input: {
89
+ installationId: number;
90
+ repositoryId: number;
91
+ ref: string;
92
+ }): Promise<GithubCommit[]>;
75
93
  createInstallationAccessToken(input: {
76
94
  installationId: number;
77
95
  repositoryId: number;
@@ -85,6 +103,6 @@ export interface GithubInstallationAccessToken {
85
103
  expiresAt: Date;
86
104
  permissions?: Record<string, 'read' | 'write' | 'admin'> | undefined;
87
105
  }
88
- export declare function createGithubApiClient(): GithubApiClient;
89
- export declare function mapGithubError<T>(operation: () => Promise<T>, notFoundReason?: 'repository-not-found' | 'installation-not-found' | 'file-not-found'): Promise<T>;
106
+ export declare function createGithubApiClient(): GithubApiClient & GithubBotUserClient;
107
+ export declare function mapGithubError<T>(operation: () => Promise<T>, notFoundReason?: 'repository-not-found' | 'installation-not-found' | 'file-not-found' | 'ref-not-found' | 'provider-rejected'): Promise<T>;
90
108
  //# sourceMappingURL=client.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/api/client.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/api/client.ts"],"names":[],"mappings":"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"}
@@ -1,5 +1,6 @@
1
1
  import { Buffer } from 'node:buffer';
2
- import { MAX_REPOSITORY_FILE_BYTES } from '@shipfox/api-integration-spi';
2
+ import { isRecord, MAX_REPOSITORY_FILE_BYTES } from '@shipfox/api-integration-spi';
3
+ import { logger } from '@shipfox/node-opentelemetry';
3
4
  import ky, { HTTPError, TimeoutError } from 'ky';
4
5
  import { App, Octokit, RequestError } from 'octokit';
5
6
  import { config, normalizedGithubApiBaseUrl, normalizedGithubPrivateKey } from '#config.js';
@@ -9,6 +10,7 @@ import { getGithubInstallationOctokit, githubInstallationTokenFormatPlugin } fro
9
10
  const NEXT_PAGE_RE = /[?&]page=(\d+)/;
10
11
  const TRAILING_SLASHES_RE = /\/+$/;
11
12
  const MAX_TREE_WALK_DEPTH = 10;
13
+ const GITHUB_API_TIMEOUT_MS = 10_000;
12
14
  export function createGithubApiClient() {
13
15
  return new OctokitGithubApiClient();
14
16
  }
@@ -29,6 +31,35 @@ let OctokitGithubApiClient = class OctokitGithubApiClient {
29
31
  }
30
32
  return body.access_token;
31
33
  }
34
+ getBotUser(input) {
35
+ const cacheKey = input.username.trim().toLowerCase();
36
+ const cached = this.botUsers.get(cacheKey);
37
+ if (cached) return Promise.resolve(cached);
38
+ const pending = this.botUserLookups.get(cacheKey);
39
+ if (pending?.installationAccessToken === input.installationAccessToken) {
40
+ return pending.promise;
41
+ }
42
+ const lookup = this.fetchBotUser(input).then((botUser)=>{
43
+ const resolved = this.botUsers.get(cacheKey);
44
+ if (resolved) return resolved;
45
+ this.botUsers.set(cacheKey, botUser);
46
+ logger().info({
47
+ githubAppBotLogin: botUser.login,
48
+ githubAppBotUserId: botUser.id
49
+ }, 'Resolved GitHub App bot identity');
50
+ return botUser;
51
+ });
52
+ const trackedLookup = lookup.finally(()=>{
53
+ if (this.botUserLookups.get(cacheKey)?.promise === trackedLookup) {
54
+ this.botUserLookups.delete(cacheKey);
55
+ }
56
+ });
57
+ this.botUserLookups.set(cacheKey, {
58
+ installationAccessToken: input.installationAccessToken,
59
+ promise: trackedLookup
60
+ });
61
+ return trackedLookup;
62
+ }
32
63
  async listUserInstallations(input) {
33
64
  const octokit = new Octokit({
34
65
  auth: input.userAccessToken,
@@ -82,7 +113,10 @@ let OctokitGithubApiClient = class OctokitGithubApiClient {
82
113
  async getRepository(input) {
83
114
  const octokit = await mapGithubError(()=>getGithubInstallationOctokit(this.getApp(), input.installationId));
84
115
  const response = await mapGithubError(()=>octokit.request('GET /repositories/{repository_id}', {
85
- repository_id: input.repositoryId
116
+ repository_id: input.repositoryId,
117
+ request: {
118
+ signal: AbortSignal.timeout(GITHUB_API_TIMEOUT_MS)
119
+ }
86
120
  }));
87
121
  return toGithubRepository(response.data);
88
122
  }
@@ -180,6 +214,30 @@ let OctokitGithubApiClient = class OctokitGithubApiClient {
180
214
  content: Buffer.from(data.content, 'base64').toString('utf8')
181
215
  };
182
216
  }
217
+ async listRepositoryCommits(input) {
218
+ const octokit = await mapGithubError(()=>getGithubInstallationOctokit(this.getApp(), input.installationId));
219
+ const repository = await this.getRepository({
220
+ installationId: input.installationId,
221
+ repositoryId: input.repositoryId
222
+ });
223
+ const response = await mapGithubError(()=>octokit.rest.repos.listCommits({
224
+ owner: repository.ownerLogin,
225
+ repo: repository.name,
226
+ sha: input.ref,
227
+ per_page: 1,
228
+ request: {
229
+ signal: AbortSignal.timeout(GITHUB_API_TIMEOUT_MS)
230
+ }
231
+ }), 'ref-not-found');
232
+ return response.data.map((commit)=>{
233
+ if (typeof commit.sha !== 'string') {
234
+ throw new GithubIntegrationProviderError('malformed-provider-response', 'GitHub commit response is missing the commit sha');
235
+ }
236
+ return {
237
+ sha: commit.sha
238
+ };
239
+ });
240
+ }
183
241
  async createInstallationAccessToken(input) {
184
242
  const response = await mapGithubError(()=>this.getApp().octokit.rest.apps.createInstallationAccessToken({
185
243
  installation_id: input.installationId,
@@ -218,6 +276,49 @@ let OctokitGithubApiClient = class OctokitGithubApiClient {
218
276
  }
219
277
  return this.app;
220
278
  }
279
+ async fetchBotUser(input) {
280
+ const octokit = new Octokit({
281
+ auth: input.installationAccessToken,
282
+ baseUrl: normalizedGithubApiBaseUrl()
283
+ });
284
+ let response;
285
+ try {
286
+ response = await mapGithubError(()=>octokit.rest.users.getByUsername({
287
+ username: input.username,
288
+ request: {
289
+ signal: AbortSignal.timeout(GITHUB_API_TIMEOUT_MS)
290
+ }
291
+ }), 'provider-rejected');
292
+ } catch (error) {
293
+ if (error instanceof GithubIntegrationProviderError && error.status === 404) {
294
+ throw new GithubIntegrationProviderError('provider-rejected', `Configured GitHub bot user ${input.username} was not found`, undefined, error.status);
295
+ }
296
+ throw error;
297
+ }
298
+ const data = response.data;
299
+ if (!isRecord(data)) {
300
+ throw new GithubIntegrationProviderError('malformed-provider-response', 'GitHub bot user response is missing required fields');
301
+ }
302
+ const { id, login, type } = data;
303
+ if (typeof id !== 'number' || !Number.isSafeInteger(id) || id <= 0 || typeof login !== 'string' || login.trim().length === 0) {
304
+ throw new GithubIntegrationProviderError('malformed-provider-response', 'GitHub bot user response is missing required fields');
305
+ }
306
+ if (type !== 'Bot') {
307
+ throw new GithubIntegrationProviderError('malformed-provider-response', 'Configured GitHub username is not a bot account');
308
+ }
309
+ const canonicalLogin = login.trim();
310
+ if (canonicalLogin.toLowerCase() !== input.username.trim().toLowerCase()) {
311
+ throw new GithubIntegrationProviderError('malformed-provider-response', 'GitHub bot user response did not match the configured username');
312
+ }
313
+ return {
314
+ id,
315
+ login: canonicalLogin
316
+ };
317
+ }
318
+ constructor(){
319
+ this.botUsers = new Map();
320
+ this.botUserLookups = new Map();
321
+ }
221
322
  };
222
323
  async function mapGithubOAuthError(operation) {
223
324
  try {
@@ -243,9 +344,15 @@ export async function mapGithubError(operation, notFoundReason = 'repository-not
243
344
  return await operation();
244
345
  } catch (error) {
245
346
  if (error instanceof GithubIntegrationProviderError) throw error;
347
+ if (isGithubTimeoutError(error)) {
348
+ throw new GithubIntegrationProviderError('timeout', 'GitHub request timed out');
349
+ }
246
350
  if (error instanceof RequestError) {
247
351
  if (error.status === 404) {
248
- throw new GithubIntegrationProviderError(notFoundReason, error.message, undefined, error.status);
352
+ throw new GithubIntegrationProviderError(notFoundReason === 'ref-not-found' ? 'repository-not-found' : notFoundReason, error.message, undefined, error.status);
353
+ }
354
+ if (notFoundReason === 'ref-not-found' && (error.status === 409 || error.status === 422)) {
355
+ throw new GithubIntegrationProviderError('ref-not-found', error.message, undefined, error.status);
249
356
  }
250
357
  if (error.status === 429 || isGithubRateLimitError(error)) {
251
358
  throw new GithubIntegrationProviderError('rate-limited', error.message, retryAfterSeconds(error), error.status);
@@ -260,12 +367,14 @@ export async function mapGithubError(operation, notFoundReason = 'repository-not
260
367
  throw new GithubIntegrationProviderError('provider-rejected', error.message, undefined, error.status);
261
368
  }
262
369
  }
263
- if (error instanceof Error && error.name === 'AbortError') {
264
- throw new GithubIntegrationProviderError('timeout', 'GitHub request timed out');
265
- }
266
370
  throw error;
267
371
  }
268
372
  }
373
+ function isGithubTimeoutError(error) {
374
+ if (!(error instanceof Error)) return false;
375
+ if (error.name === 'AbortError' || error.name === 'TimeoutError') return true;
376
+ return error.cause instanceof Error && isGithubTimeoutError(error.cause);
377
+ }
269
378
  function isGithubRateLimitError(error) {
270
379
  return error.status === 403 && error.response?.headers['x-ratelimit-remaining'] === '0';
271
380
  }
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/api/client.ts"],"sourcesContent":["import {Buffer} from 'node:buffer';\nimport {MAX_REPOSITORY_FILE_BYTES} from '@shipfox/api-integration-spi';\nimport ky, {HTTPError, TimeoutError} from 'ky';\nimport {App, Octokit, RequestError} from 'octokit';\nimport {config, normalizedGithubApiBaseUrl, normalizedGithubPrivateKey} from '#config.js';\nimport {GithubIntegrationProviderError} from '#core/errors.js';\nimport {recordInstallationTokenFormat} from '#metrics/index.js';\nimport {\n getGithubInstallationOctokit,\n githubInstallationTokenFormatPlugin,\n} from './github-octokit.js';\n\nconst NEXT_PAGE_RE = /[?&]page=(\\d+)/;\nconst TRAILING_SLASHES_RE = /\\/+$/;\nconst MAX_TREE_WALK_DEPTH = 10;\n\nexport interface GithubAccount {\n login: string;\n type: string;\n}\n\nexport interface GithubInstallationDetails {\n id: number;\n account: GithubAccount;\n repositorySelection: string;\n suspendedAt: Date | null;\n htmlUrl: string;\n raw: Record<string, unknown>;\n}\n\nexport interface GithubRepository {\n id: number;\n ownerLogin: string;\n name: string;\n fullName: string;\n defaultBranch: string;\n private: boolean;\n visibility?: string | undefined;\n cloneUrl: string;\n htmlUrl: string;\n}\n\nexport interface GithubRepositoryPage {\n repositories: GithubRepository[];\n nextCursor: string | null;\n}\n\nexport interface GithubFileEntry {\n path: string;\n size: number | null;\n}\n\nexport interface GithubFilePage {\n files: GithubFileEntry[];\n nextCursor: string | null;\n}\n\nexport interface GithubFileContent {\n path: string;\n content: string;\n size: number;\n}\n\nexport interface GithubUserInstallationPage {\n installationIds: number[];\n nextCursor: string | null;\n}\n\nexport interface GithubApiClient {\n exchangeOAuthCode(code: string): Promise<string>;\n listUserInstallations(input: {\n userAccessToken: string;\n cursor?: string | undefined;\n }): Promise<GithubUserInstallationPage>;\n getInstallation(installationId: number): Promise<GithubInstallationDetails>;\n listInstallationRepositories(input: {\n installationId: number;\n limit: number;\n cursor?: string | undefined;\n }): Promise<GithubRepositoryPage>;\n getRepository(input: {installationId: number; repositoryId: number}): Promise<GithubRepository>;\n listRepositoryFiles(input: {\n installationId: number;\n repositoryId: number;\n ref: string;\n prefix: string;\n limit: number;\n cursor?: string | undefined;\n }): Promise<GithubFilePage>;\n fetchRepositoryFile(input: {\n installationId: number;\n repositoryId: number;\n ref: string;\n path: string;\n }): Promise<GithubFileContent>;\n createInstallationAccessToken(input: {\n installationId: number;\n repositoryId: number;\n permissions?: {contents: 'read' | 'write'} | undefined;\n }): Promise<GithubInstallationAccessToken>;\n}\n\nexport interface GithubInstallationAccessToken {\n token: string;\n expiresAt: Date;\n permissions?: Record<string, 'read' | 'write' | 'admin'> | undefined;\n}\n\nexport function createGithubApiClient(): GithubApiClient {\n return new OctokitGithubApiClient();\n}\n\nclass OctokitGithubApiClient implements GithubApiClient {\n private app: App | undefined;\n\n async exchangeOAuthCode(code: string): Promise<string> {\n const body = await mapGithubOAuthError(() =>\n ky\n .post('https://github.com/login/oauth/access_token', {\n headers: {accept: 'application/json'},\n json: {\n client_id: config.GITHUB_APP_CLIENT_ID,\n client_secret: config.GITHUB_APP_CLIENT_SECRET,\n code,\n },\n })\n .json<{access_token?: unknown}>(),\n );\n\n if (typeof body.access_token !== 'string') {\n throw new GithubIntegrationProviderError(\n 'malformed-provider-response',\n 'GitHub OAuth response did not include an access token',\n );\n }\n return body.access_token;\n }\n\n async listUserInstallations(input: {\n userAccessToken: string;\n cursor?: string | undefined;\n }): Promise<GithubUserInstallationPage> {\n const octokit = new Octokit({\n auth: input.userAccessToken,\n baseUrl: normalizedGithubApiBaseUrl(),\n });\n const page = cursorToPage(input.cursor);\n const response = await mapGithubError(() =>\n octokit.request('GET /user/installations', {\n per_page: 100,\n page,\n }),\n );\n\n const installations = response.data.installations ?? [];\n return {\n installationIds: installations.map((installation) => installation.id),\n nextCursor: nextCursorFromLink(response.headers.link),\n };\n }\n\n async getInstallation(installationId: number): Promise<GithubInstallationDetails> {\n const response = await mapGithubError(() =>\n this.getApp().octokit.rest.apps.getInstallation({installation_id: installationId}),\n );\n const data = response.data;\n const account = data.account;\n if (!account) {\n throw new GithubIntegrationProviderError(\n 'malformed-provider-response',\n 'GitHub installation response did not include an account',\n );\n }\n\n const login = 'login' in account ? account.login : account.slug;\n return {\n id: data.id,\n account: {\n login,\n type: 'type' in account ? account.type : 'Enterprise',\n },\n repositorySelection: data.repository_selection,\n suspendedAt: data.suspended_at ? new Date(data.suspended_at) : null,\n htmlUrl: data.html_url,\n raw: data as unknown as Record<string, unknown>,\n };\n }\n\n async listInstallationRepositories(input: {\n installationId: number;\n limit: number;\n cursor?: string | undefined;\n }): Promise<GithubRepositoryPage> {\n const octokit = await mapGithubError(() =>\n getGithubInstallationOctokit(this.getApp(), input.installationId),\n );\n const page = cursorToPage(input.cursor);\n const response = await mapGithubError(() =>\n octokit.rest.apps.listReposAccessibleToInstallation({\n per_page: input.limit,\n page,\n }),\n );\n\n return {\n repositories: response.data.repositories.map(toGithubRepository),\n nextCursor: nextCursorFromLink(response.headers.link),\n };\n }\n\n async getRepository(input: {\n installationId: number;\n repositoryId: number;\n }): Promise<GithubRepository> {\n const octokit = await mapGithubError(() =>\n getGithubInstallationOctokit(this.getApp(), input.installationId),\n );\n const response = await mapGithubError(() =>\n octokit.request('GET /repositories/{repository_id}', {\n repository_id: input.repositoryId,\n }),\n );\n\n return toGithubRepository(response.data);\n }\n\n async listRepositoryFiles(input: {\n installationId: number;\n repositoryId: number;\n ref: string;\n prefix: string;\n limit: number;\n cursor?: string | undefined;\n }): Promise<GithubFilePage> {\n const octokit = await mapGithubError(() =>\n getGithubInstallationOctokit(this.getApp(), input.installationId),\n );\n const repository = await this.getRepository({\n installationId: input.installationId,\n repositoryId: input.repositoryId,\n });\n const owner = repository.ownerLogin;\n const repo = repository.name;\n const startPath = input.prefix.replace(TRAILING_SLASHES_RE, '');\n const offset = input.cursor ? Number.parseInt(input.cursor, 10) : 0;\n const start = Number.isNaN(offset) || offset < 0 ? 0 : offset;\n const collected: GithubFileEntry[] = [];\n const overflowLimit = start + input.limit + 1;\n\n type GetContentData = Awaited<ReturnType<typeof octokit.rest.repos.getContent>>['data'];\n\n const walk = async (path: string, depth: number): Promise<void> => {\n if (collected.length >= overflowLimit) return;\n if (depth > MAX_TREE_WALK_DEPTH) return;\n\n let data: GetContentData;\n try {\n const response = await mapGithubError(\n () =>\n octokit.rest.repos.getContent({\n owner,\n repo,\n path,\n ref: input.ref,\n }),\n 'file-not-found',\n );\n data = response.data;\n } catch (error) {\n if (error instanceof GithubIntegrationProviderError && error.reason === 'file-not-found') {\n return;\n }\n throw error;\n }\n\n if (!Array.isArray(data)) {\n if (data.type === 'file' && data.path) {\n collected.push({path: data.path, size: typeof data.size === 'number' ? data.size : null});\n }\n return;\n }\n\n const entries = [...data].sort((a, b) => (a.path ?? '').localeCompare(b.path ?? ''));\n for (const entry of entries) {\n if (collected.length >= overflowLimit) return;\n if (!entry.path) continue;\n if (entry.type === 'file') {\n collected.push({\n path: entry.path,\n size: typeof entry.size === 'number' ? entry.size : null,\n });\n } else if (entry.type === 'dir') {\n await walk(entry.path, depth + 1);\n }\n }\n };\n\n await walk(startPath, 0);\n\n const sorted = collected.sort((a, b) => a.path.localeCompare(b.path));\n const page = sorted.slice(start, start + input.limit);\n const consumed = start + page.length;\n const hasMore = consumed < sorted.length;\n\n return {\n files: page,\n nextCursor: hasMore ? String(consumed) : null,\n };\n }\n\n async fetchRepositoryFile(input: {\n installationId: number;\n repositoryId: number;\n ref: string;\n path: string;\n }): Promise<GithubFileContent> {\n const octokit = await mapGithubError(() =>\n getGithubInstallationOctokit(this.getApp(), input.installationId),\n );\n const repository = await this.getRepository({\n installationId: input.installationId,\n repositoryId: input.repositoryId,\n });\n const response = await mapGithubError(\n () =>\n octokit.rest.repos.getContent({\n owner: repository.ownerLogin,\n repo: repository.name,\n path: input.path,\n ref: input.ref,\n }),\n 'file-not-found',\n );\n const data = response.data;\n\n if (Array.isArray(data) || data.type !== 'file') {\n throw new GithubIntegrationProviderError('file-not-found', 'GitHub path is not a file');\n }\n if (data.size > MAX_REPOSITORY_FILE_BYTES) {\n throw new GithubIntegrationProviderError(\n 'content-too-large',\n 'GitHub file content is larger than the supported limit',\n );\n }\n if (typeof data.content !== 'string' || data.encoding !== 'base64') {\n throw new GithubIntegrationProviderError(\n 'malformed-provider-response',\n 'GitHub file response did not include base64 content',\n );\n }\n\n return {\n path: data.path,\n size: data.size,\n content: Buffer.from(data.content, 'base64').toString('utf8'),\n };\n }\n\n async createInstallationAccessToken(input: {\n installationId: number;\n repositoryId: number;\n permissions?: {contents: 'read' | 'write'} | undefined;\n }): Promise<GithubInstallationAccessToken> {\n const response = await mapGithubError(() =>\n this.getApp().octokit.rest.apps.createInstallationAccessToken({\n installation_id: input.installationId,\n repository_ids: [input.repositoryId],\n permissions: input.permissions ?? {contents: 'read'},\n }),\n );\n\n if (typeof response.data.token !== 'string') {\n throw new GithubIntegrationProviderError(\n 'malformed-provider-response',\n 'GitHub installation access token response did not include a token',\n );\n }\n\n recordInstallationTokenFormat(response.data.token);\n\n const expiresAt = new Date(response.data.expires_at);\n if (Number.isNaN(expiresAt.getTime())) {\n throw new GithubIntegrationProviderError(\n 'malformed-provider-response',\n 'GitHub installation access token response did not include a valid expiry',\n );\n }\n\n return {\n token: response.data.token,\n expiresAt,\n ...(response.data.permissions === undefined ? {} : {permissions: response.data.permissions}),\n };\n }\n\n private getApp(): App {\n if (!this.app) {\n this.app = new App({\n appId: config.GITHUB_APP_ID,\n privateKey: normalizedGithubPrivateKey(),\n Octokit: Octokit.plugin(githubInstallationTokenFormatPlugin).defaults({\n baseUrl: normalizedGithubApiBaseUrl(),\n }),\n });\n }\n return this.app;\n }\n}\n\nasync function mapGithubOAuthError<T>(operation: () => Promise<T>): Promise<T> {\n try {\n return await operation();\n } catch (error) {\n if (error instanceof HTTPError) {\n if (error.response.status === 429) {\n throw new GithubIntegrationProviderError(\n 'rate-limited',\n 'GitHub OAuth code exchange failed',\n retryAfterSecondsFromHeaders(error.response.headers),\n );\n }\n if (error.response.status >= 500) {\n throw new GithubIntegrationProviderError(\n 'provider-unavailable',\n 'GitHub OAuth code exchange failed',\n );\n }\n throw new GithubIntegrationProviderError(\n 'access-denied',\n 'GitHub OAuth code exchange failed',\n );\n }\n if (error instanceof TimeoutError) {\n throw new GithubIntegrationProviderError('timeout', 'GitHub OAuth request timed out');\n }\n throw error;\n }\n}\n\nexport async function mapGithubError<T>(\n operation: () => Promise<T>,\n notFoundReason:\n | 'repository-not-found'\n | 'installation-not-found'\n | 'file-not-found' = 'repository-not-found',\n): Promise<T> {\n try {\n return await operation();\n } catch (error) {\n if (error instanceof GithubIntegrationProviderError) throw error;\n if (error instanceof RequestError) {\n if (error.status === 404) {\n throw new GithubIntegrationProviderError(\n notFoundReason,\n error.message,\n undefined,\n error.status,\n );\n }\n if (error.status === 429 || isGithubRateLimitError(error)) {\n throw new GithubIntegrationProviderError(\n 'rate-limited',\n error.message,\n retryAfterSeconds(error),\n error.status,\n );\n }\n if (error.status === 401 || error.status === 403) {\n throw new GithubIntegrationProviderError(\n 'access-denied',\n error.message,\n retryAfterSeconds(error),\n error.status,\n );\n }\n if (error.status >= 500) {\n throw new GithubIntegrationProviderError(\n 'provider-unavailable',\n error.message,\n undefined,\n error.status,\n );\n }\n if (error.status >= 400) {\n throw new GithubIntegrationProviderError(\n 'provider-rejected',\n error.message,\n undefined,\n error.status,\n );\n }\n }\n if (error instanceof Error && error.name === 'AbortError') {\n throw new GithubIntegrationProviderError('timeout', 'GitHub request timed out');\n }\n throw error;\n }\n}\n\nfunction isGithubRateLimitError(error: RequestError): boolean {\n return error.status === 403 && error.response?.headers['x-ratelimit-remaining'] === '0';\n}\n\nfunction retryAfterSeconds(error: RequestError): number | undefined {\n return parseRetryAfterSeconds(error.response?.headers['retry-after']);\n}\n\nfunction retryAfterSecondsFromHeaders(headers: Headers): number | undefined {\n return parseRetryAfterSeconds(headers.get('retry-after'));\n}\n\nfunction parseRetryAfterSeconds(\n retryAfter: string | number | null | undefined,\n): number | undefined {\n if (!retryAfter) return undefined;\n const parsed = Number.parseInt(String(retryAfter), 10);\n return Number.isNaN(parsed) ? undefined : parsed;\n}\n\nfunction cursorToPage(cursor: string | undefined): number {\n if (!cursor) return 1;\n const page = Number.parseInt(cursor, 10);\n return Number.isNaN(page) || page < 1 ? 1 : page;\n}\n\nfunction nextCursorFromLink(link: string | undefined): string | null {\n if (!link) return null;\n const next = link.split(',').find((part) => part.includes('rel=\"next\"'));\n if (!next) return null;\n const match = next.match(NEXT_PAGE_RE);\n return match?.[1] ?? null;\n}\n\nfunction toGithubRepository(repository: {\n id: number;\n owner: {login: string};\n name: string;\n full_name: string;\n default_branch?: string | null | undefined;\n private: boolean;\n visibility?: string | undefined;\n clone_url?: string | null | undefined;\n html_url?: string | null | undefined;\n}): GithubRepository {\n if (!repository.default_branch || !repository.clone_url || !repository.html_url) {\n throw new GithubIntegrationProviderError(\n 'malformed-provider-response',\n 'GitHub repository response is missing required fields',\n );\n }\n return {\n id: repository.id,\n ownerLogin: repository.owner.login,\n name: repository.name,\n fullName: repository.full_name,\n defaultBranch: repository.default_branch,\n private: repository.private,\n visibility: repository.visibility,\n cloneUrl: repository.clone_url,\n htmlUrl: repository.html_url,\n };\n}\n"],"names":["Buffer","MAX_REPOSITORY_FILE_BYTES","ky","HTTPError","TimeoutError","App","Octokit","RequestError","config","normalizedGithubApiBaseUrl","normalizedGithubPrivateKey","GithubIntegrationProviderError","recordInstallationTokenFormat","getGithubInstallationOctokit","githubInstallationTokenFormatPlugin","NEXT_PAGE_RE","TRAILING_SLASHES_RE","MAX_TREE_WALK_DEPTH","createGithubApiClient","OctokitGithubApiClient","exchangeOAuthCode","code","body","mapGithubOAuthError","post","headers","accept","json","client_id","GITHUB_APP_CLIENT_ID","client_secret","GITHUB_APP_CLIENT_SECRET","access_token","listUserInstallations","input","octokit","auth","userAccessToken","baseUrl","page","cursorToPage","cursor","response","mapGithubError","request","per_page","installations","data","installationIds","map","installation","id","nextCursor","nextCursorFromLink","link","getInstallation","installationId","getApp","rest","apps","installation_id","account","login","slug","type","repositorySelection","repository_selection","suspendedAt","suspended_at","Date","htmlUrl","html_url","raw","listInstallationRepositories","listReposAccessibleToInstallation","limit","repositories","toGithubRepository","getRepository","repository_id","repositoryId","listRepositoryFiles","repository","owner","ownerLogin","repo","name","startPath","prefix","replace","offset","Number","parseInt","start","isNaN","collected","overflowLimit","walk","path","depth","length","repos","getContent","ref","error","reason","Array","isArray","push","size","entries","sort","a","b","localeCompare","entry","sorted","slice","consumed","hasMore","files","String","fetchRepositoryFile","content","encoding","from","toString","createInstallationAccessToken","repository_ids","permissions","contents","token","expiresAt","expires_at","getTime","undefined","app","appId","GITHUB_APP_ID","privateKey","plugin","defaults","operation","status","retryAfterSecondsFromHeaders","notFoundReason","message","isGithubRateLimitError","retryAfterSeconds","Error","parseRetryAfterSeconds","get","retryAfter","parsed","next","split","find","part","includes","match","default_branch","clone_url","fullName","full_name","defaultBranch","private","visibility","cloneUrl"],"mappings":"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"}
1
+ {"version":3,"sources":["../../src/api/client.ts"],"sourcesContent":["import {Buffer} from 'node:buffer';\nimport {isRecord, MAX_REPOSITORY_FILE_BYTES} from '@shipfox/api-integration-spi';\nimport {logger} from '@shipfox/node-opentelemetry';\nimport ky, {HTTPError, TimeoutError} from 'ky';\nimport {App, Octokit, RequestError} from 'octokit';\nimport {config, normalizedGithubApiBaseUrl, normalizedGithubPrivateKey} from '#config.js';\nimport {GithubIntegrationProviderError} from '#core/errors.js';\nimport {recordInstallationTokenFormat} from '#metrics/index.js';\nimport {\n getGithubInstallationOctokit,\n githubInstallationTokenFormatPlugin,\n} from './github-octokit.js';\n\nconst NEXT_PAGE_RE = /[?&]page=(\\d+)/;\nconst TRAILING_SLASHES_RE = /\\/+$/;\nconst MAX_TREE_WALK_DEPTH = 10;\nconst GITHUB_API_TIMEOUT_MS = 10_000;\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 GithubCommit {\n sha: string;\n}\n\nexport interface GithubUserInstallationPage {\n installationIds: number[];\n nextCursor: string | null;\n}\n\nexport interface GithubBotUser {\n id: number;\n login: string;\n}\n\nexport interface GithubBotUserClient {\n getBotUser(input: {username: string; installationAccessToken: string}): Promise<GithubBotUser>;\n}\n\nexport interface GithubApiClient extends Partial<GithubBotUserClient> {\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 listRepositoryCommits(input: {\n installationId: number;\n repositoryId: number;\n ref: string;\n }): Promise<GithubCommit[]>;\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 & GithubBotUserClient {\n return new OctokitGithubApiClient();\n}\n\nclass OctokitGithubApiClient implements GithubApiClient, GithubBotUserClient {\n private app: App | undefined;\n private readonly botUsers = new Map<string, GithubBotUser>();\n private readonly botUserLookups = new Map<\n string,\n {installationAccessToken: string; promise: Promise<GithubBotUser>}\n >();\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 getBotUser(input: {username: string; installationAccessToken: string}): Promise<GithubBotUser> {\n const cacheKey = input.username.trim().toLowerCase();\n const cached = this.botUsers.get(cacheKey);\n if (cached) return Promise.resolve(cached);\n\n const pending = this.botUserLookups.get(cacheKey);\n if (pending?.installationAccessToken === input.installationAccessToken) {\n return pending.promise;\n }\n\n const lookup = this.fetchBotUser(input).then((botUser) => {\n const resolved = this.botUsers.get(cacheKey);\n if (resolved) return resolved;\n\n this.botUsers.set(cacheKey, botUser);\n logger().info(\n {githubAppBotLogin: botUser.login, githubAppBotUserId: botUser.id},\n 'Resolved GitHub App bot identity',\n );\n return botUser;\n });\n const trackedLookup = lookup.finally(() => {\n if (this.botUserLookups.get(cacheKey)?.promise === trackedLookup) {\n this.botUserLookups.delete(cacheKey);\n }\n });\n this.botUserLookups.set(cacheKey, {\n installationAccessToken: input.installationAccessToken,\n promise: trackedLookup,\n });\n return trackedLookup;\n }\n\n async listUserInstallations(input: {\n userAccessToken: string;\n cursor?: string | undefined;\n }): Promise<GithubUserInstallationPage> {\n const octokit = new Octokit({\n auth: input.userAccessToken,\n baseUrl: normalizedGithubApiBaseUrl(),\n });\n const page = cursorToPage(input.cursor);\n const response = await mapGithubError(() =>\n octokit.request('GET /user/installations', {\n per_page: 100,\n page,\n }),\n );\n\n const installations = response.data.installations ?? [];\n return {\n installationIds: installations.map((installation) => installation.id),\n nextCursor: nextCursorFromLink(response.headers.link),\n };\n }\n\n async getInstallation(installationId: number): Promise<GithubInstallationDetails> {\n const response = await mapGithubError(() =>\n this.getApp().octokit.rest.apps.getInstallation({installation_id: installationId}),\n );\n const data = response.data;\n const account = data.account;\n if (!account) {\n throw new GithubIntegrationProviderError(\n 'malformed-provider-response',\n 'GitHub installation response did not include an account',\n );\n }\n\n const login = 'login' in account ? account.login : account.slug;\n return {\n id: data.id,\n account: {\n login,\n type: 'type' in account ? account.type : 'Enterprise',\n },\n repositorySelection: data.repository_selection,\n suspendedAt: data.suspended_at ? new Date(data.suspended_at) : null,\n htmlUrl: data.html_url,\n raw: data as unknown as Record<string, unknown>,\n };\n }\n\n async listInstallationRepositories(input: {\n installationId: number;\n limit: number;\n cursor?: string | undefined;\n }): Promise<GithubRepositoryPage> {\n const octokit = await mapGithubError(() =>\n getGithubInstallationOctokit(this.getApp(), input.installationId),\n );\n const page = cursorToPage(input.cursor);\n const response = await mapGithubError(() =>\n octokit.rest.apps.listReposAccessibleToInstallation({\n per_page: input.limit,\n page,\n }),\n );\n\n return {\n repositories: response.data.repositories.map(toGithubRepository),\n nextCursor: nextCursorFromLink(response.headers.link),\n };\n }\n\n async getRepository(input: {\n installationId: number;\n repositoryId: number;\n }): Promise<GithubRepository> {\n const octokit = await mapGithubError(() =>\n getGithubInstallationOctokit(this.getApp(), input.installationId),\n );\n const response = await mapGithubError(() =>\n octokit.request('GET /repositories/{repository_id}', {\n repository_id: input.repositoryId,\n request: {signal: AbortSignal.timeout(GITHUB_API_TIMEOUT_MS)},\n }),\n );\n\n return toGithubRepository(response.data);\n }\n\n async listRepositoryFiles(input: {\n installationId: number;\n repositoryId: number;\n ref: string;\n prefix: string;\n limit: number;\n cursor?: string | undefined;\n }): Promise<GithubFilePage> {\n const octokit = await mapGithubError(() =>\n getGithubInstallationOctokit(this.getApp(), input.installationId),\n );\n const repository = await this.getRepository({\n installationId: input.installationId,\n repositoryId: input.repositoryId,\n });\n const owner = repository.ownerLogin;\n const repo = repository.name;\n const startPath = input.prefix.replace(TRAILING_SLASHES_RE, '');\n const offset = input.cursor ? Number.parseInt(input.cursor, 10) : 0;\n const start = Number.isNaN(offset) || offset < 0 ? 0 : offset;\n const collected: GithubFileEntry[] = [];\n const overflowLimit = start + input.limit + 1;\n\n type GetContentData = Awaited<ReturnType<typeof octokit.rest.repos.getContent>>['data'];\n\n const walk = async (path: string, depth: number): Promise<void> => {\n if (collected.length >= overflowLimit) return;\n if (depth > MAX_TREE_WALK_DEPTH) return;\n\n let data: GetContentData;\n try {\n const response = await mapGithubError(\n () =>\n octokit.rest.repos.getContent({\n owner,\n repo,\n path,\n ref: input.ref,\n }),\n 'file-not-found',\n );\n data = response.data;\n } catch (error) {\n if (error instanceof GithubIntegrationProviderError && error.reason === 'file-not-found') {\n return;\n }\n throw error;\n }\n\n if (!Array.isArray(data)) {\n if (data.type === 'file' && data.path) {\n collected.push({path: data.path, size: typeof data.size === 'number' ? data.size : null});\n }\n return;\n }\n\n const entries = [...data].sort((a, b) => (a.path ?? '').localeCompare(b.path ?? ''));\n for (const entry of entries) {\n if (collected.length >= overflowLimit) return;\n if (!entry.path) continue;\n if (entry.type === 'file') {\n collected.push({\n path: entry.path,\n size: typeof entry.size === 'number' ? entry.size : null,\n });\n } else if (entry.type === 'dir') {\n await walk(entry.path, depth + 1);\n }\n }\n };\n\n await walk(startPath, 0);\n\n const sorted = collected.sort((a, b) => a.path.localeCompare(b.path));\n const page = sorted.slice(start, start + input.limit);\n const consumed = start + page.length;\n const hasMore = consumed < sorted.length;\n\n return {\n files: page,\n nextCursor: hasMore ? String(consumed) : null,\n };\n }\n\n async fetchRepositoryFile(input: {\n installationId: number;\n repositoryId: number;\n ref: string;\n path: string;\n }): Promise<GithubFileContent> {\n const octokit = await mapGithubError(() =>\n getGithubInstallationOctokit(this.getApp(), input.installationId),\n );\n const repository = await this.getRepository({\n installationId: input.installationId,\n repositoryId: input.repositoryId,\n });\n const response = await mapGithubError(\n () =>\n octokit.rest.repos.getContent({\n owner: repository.ownerLogin,\n repo: repository.name,\n path: input.path,\n ref: input.ref,\n }),\n 'file-not-found',\n );\n const data = response.data;\n\n if (Array.isArray(data) || data.type !== 'file') {\n throw new GithubIntegrationProviderError('file-not-found', 'GitHub path is not a file');\n }\n if (data.size > MAX_REPOSITORY_FILE_BYTES) {\n throw new GithubIntegrationProviderError(\n 'content-too-large',\n 'GitHub file content is larger than the supported limit',\n );\n }\n if (typeof data.content !== 'string' || data.encoding !== 'base64') {\n throw new GithubIntegrationProviderError(\n 'malformed-provider-response',\n 'GitHub file response did not include base64 content',\n );\n }\n\n return {\n path: data.path,\n size: data.size,\n content: Buffer.from(data.content, 'base64').toString('utf8'),\n };\n }\n\n async listRepositoryCommits(input: {\n installationId: number;\n repositoryId: number;\n ref: string;\n }): Promise<GithubCommit[]> {\n const octokit = await mapGithubError(() =>\n getGithubInstallationOctokit(this.getApp(), input.installationId),\n );\n const repository = await this.getRepository({\n installationId: input.installationId,\n repositoryId: input.repositoryId,\n });\n const response = await mapGithubError(\n () =>\n octokit.rest.repos.listCommits({\n owner: repository.ownerLogin,\n repo: repository.name,\n sha: input.ref,\n per_page: 1,\n request: {signal: AbortSignal.timeout(GITHUB_API_TIMEOUT_MS)},\n }),\n 'ref-not-found',\n );\n return response.data.map((commit) => {\n if (typeof commit.sha !== 'string') {\n throw new GithubIntegrationProviderError(\n 'malformed-provider-response',\n 'GitHub commit response is missing the commit sha',\n );\n }\n return {sha: commit.sha};\n });\n }\n\n async createInstallationAccessToken(input: {\n installationId: number;\n repositoryId: number;\n permissions?: {contents: 'read' | 'write'} | undefined;\n }): Promise<GithubInstallationAccessToken> {\n const response = await mapGithubError(() =>\n this.getApp().octokit.rest.apps.createInstallationAccessToken({\n installation_id: input.installationId,\n repository_ids: [input.repositoryId],\n permissions: input.permissions ?? {contents: 'read'},\n }),\n );\n\n if (typeof response.data.token !== 'string') {\n throw new GithubIntegrationProviderError(\n 'malformed-provider-response',\n 'GitHub installation access token response did not include a token',\n );\n }\n\n recordInstallationTokenFormat(response.data.token);\n\n const expiresAt = new Date(response.data.expires_at);\n if (Number.isNaN(expiresAt.getTime())) {\n throw new GithubIntegrationProviderError(\n 'malformed-provider-response',\n 'GitHub installation access token response did not include a valid expiry',\n );\n }\n\n return {\n token: response.data.token,\n expiresAt,\n ...(response.data.permissions === undefined ? {} : {permissions: response.data.permissions}),\n };\n }\n\n private getApp(): App {\n if (!this.app) {\n this.app = new App({\n appId: config.GITHUB_APP_ID,\n privateKey: normalizedGithubPrivateKey(),\n Octokit: Octokit.plugin(githubInstallationTokenFormatPlugin).defaults({\n baseUrl: normalizedGithubApiBaseUrl(),\n }),\n });\n }\n return this.app;\n }\n\n private async fetchBotUser(input: {\n username: string;\n installationAccessToken: string;\n }): Promise<GithubBotUser> {\n const octokit = new Octokit({\n auth: input.installationAccessToken,\n baseUrl: normalizedGithubApiBaseUrl(),\n });\n let response: Awaited<ReturnType<typeof octokit.rest.users.getByUsername>>;\n try {\n response = await mapGithubError(\n () =>\n octokit.rest.users.getByUsername({\n username: input.username,\n request: {signal: AbortSignal.timeout(GITHUB_API_TIMEOUT_MS)},\n }),\n 'provider-rejected',\n );\n } catch (error) {\n if (error instanceof GithubIntegrationProviderError && error.status === 404) {\n throw new GithubIntegrationProviderError(\n 'provider-rejected',\n `Configured GitHub bot user ${input.username} was not found`,\n undefined,\n error.status,\n );\n }\n throw error;\n }\n\n const data: unknown = response.data;\n if (!isRecord(data)) {\n throw new GithubIntegrationProviderError(\n 'malformed-provider-response',\n 'GitHub bot user response is missing required fields',\n );\n }\n const {id, login, type} = data;\n if (\n typeof id !== 'number' ||\n !Number.isSafeInteger(id) ||\n id <= 0 ||\n typeof login !== 'string' ||\n login.trim().length === 0\n ) {\n throw new GithubIntegrationProviderError(\n 'malformed-provider-response',\n 'GitHub bot user response is missing required fields',\n );\n }\n if (type !== 'Bot') {\n throw new GithubIntegrationProviderError(\n 'malformed-provider-response',\n 'Configured GitHub username is not a bot account',\n );\n }\n const canonicalLogin = login.trim();\n if (canonicalLogin.toLowerCase() !== input.username.trim().toLowerCase()) {\n throw new GithubIntegrationProviderError(\n 'malformed-provider-response',\n 'GitHub bot user response did not match the configured username',\n );\n }\n return {id, login: canonicalLogin};\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'\n | 'ref-not-found'\n | 'provider-rejected' = 'repository-not-found',\n): Promise<T> {\n try {\n return await operation();\n } catch (error) {\n if (error instanceof GithubIntegrationProviderError) throw error;\n if (isGithubTimeoutError(error)) {\n throw new GithubIntegrationProviderError('timeout', 'GitHub request timed out');\n }\n if (error instanceof RequestError) {\n if (error.status === 404) {\n throw new GithubIntegrationProviderError(\n notFoundReason === 'ref-not-found' ? 'repository-not-found' : notFoundReason,\n error.message,\n undefined,\n error.status,\n );\n }\n if (notFoundReason === 'ref-not-found' && (error.status === 409 || error.status === 422)) {\n throw new GithubIntegrationProviderError(\n 'ref-not-found',\n error.message,\n undefined,\n error.status,\n );\n }\n if (error.status === 429 || isGithubRateLimitError(error)) {\n throw new GithubIntegrationProviderError(\n 'rate-limited',\n error.message,\n retryAfterSeconds(error),\n error.status,\n );\n }\n if (error.status === 401 || error.status === 403) {\n throw new GithubIntegrationProviderError(\n 'access-denied',\n error.message,\n retryAfterSeconds(error),\n error.status,\n );\n }\n if (error.status >= 500) {\n throw new GithubIntegrationProviderError(\n 'provider-unavailable',\n error.message,\n undefined,\n error.status,\n );\n }\n if (error.status >= 400) {\n throw new GithubIntegrationProviderError(\n 'provider-rejected',\n error.message,\n undefined,\n error.status,\n );\n }\n }\n throw error;\n }\n}\n\nfunction isGithubTimeoutError(error: unknown): boolean {\n if (!(error instanceof Error)) return false;\n if (error.name === 'AbortError' || error.name === 'TimeoutError') return true;\n return error.cause instanceof Error && isGithubTimeoutError(error.cause);\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","isRecord","MAX_REPOSITORY_FILE_BYTES","logger","ky","HTTPError","TimeoutError","App","Octokit","RequestError","config","normalizedGithubApiBaseUrl","normalizedGithubPrivateKey","GithubIntegrationProviderError","recordInstallationTokenFormat","getGithubInstallationOctokit","githubInstallationTokenFormatPlugin","NEXT_PAGE_RE","TRAILING_SLASHES_RE","MAX_TREE_WALK_DEPTH","GITHUB_API_TIMEOUT_MS","createGithubApiClient","OctokitGithubApiClient","exchangeOAuthCode","code","body","mapGithubOAuthError","post","headers","accept","json","client_id","GITHUB_APP_CLIENT_ID","client_secret","GITHUB_APP_CLIENT_SECRET","access_token","getBotUser","input","cacheKey","username","trim","toLowerCase","cached","botUsers","get","Promise","resolve","pending","botUserLookups","installationAccessToken","promise","lookup","fetchBotUser","then","botUser","resolved","set","info","githubAppBotLogin","login","githubAppBotUserId","id","trackedLookup","finally","delete","listUserInstallations","octokit","auth","userAccessToken","baseUrl","page","cursorToPage","cursor","response","mapGithubError","request","per_page","installations","data","installationIds","map","installation","nextCursor","nextCursorFromLink","link","getInstallation","installationId","getApp","rest","apps","installation_id","account","slug","type","repositorySelection","repository_selection","suspendedAt","suspended_at","Date","htmlUrl","html_url","raw","listInstallationRepositories","listReposAccessibleToInstallation","limit","repositories","toGithubRepository","getRepository","repository_id","repositoryId","signal","AbortSignal","timeout","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","listRepositoryCommits","listCommits","sha","commit","createInstallationAccessToken","repository_ids","permissions","contents","token","expiresAt","expires_at","getTime","undefined","app","appId","GITHUB_APP_ID","privateKey","plugin","defaults","users","getByUsername","status","isSafeInteger","canonicalLogin","Map","operation","retryAfterSecondsFromHeaders","notFoundReason","isGithubTimeoutError","message","isGithubRateLimitError","retryAfterSeconds","Error","cause","parseRetryAfterSeconds","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 +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,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;AA4ClD,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;IAC3D,YAAY,CAAC,EACT;QACE,OAAO,EAAE,MAAM,CAAC;QAChB,MAAM,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;KAC7B,GACD,SAAS,CAAC;CACf;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,CAW3F;AAED,wBAAgB,8BAA8B,CAAC,GAAG,EAAE,MAAM,GAAG,yBAAyB,GAAG,SAAS,CAmBjG;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,EACpB,YAAY,CAAC,EAAE,yBAAyB,CAAC,cAAc,CAAC,GACvD,8BAA8B,CAOhC;AAED,wBAAgB,eAAe,CAAC,KAAK,EAAE,OAAO,GAAG,8BAA8B,CAc9E"}
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;AA8ClD,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;IAC3D,YAAY,CAAC,EACT;QACE,OAAO,EAAE,MAAM,CAAC;QAChB,MAAM,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;KAC7B,GACD,SAAS,CAAC;CACf;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,CAW3F;AAED,wBAAgB,8BAA8B,CAAC,GAAG,EAAE,MAAM,GAAG,yBAAyB,GAAG,SAAS,CAmBjG;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,EACpB,YAAY,CAAC,EAAE,yBAAyB,CAAC,cAAc,CAAC,GACvD,8BAA8B,CAOhC;AAED,wBAAgB,eAAe,CAAC,KAAK,EAAE,OAAO,GAAG,8BAA8B,CAc9E"}
@@ -1 +1 @@
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 'provider-rejected',\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 backoffErrorSchema = z.object({\n message: z.string(),\n status: z.number().int().optional(),\n});\nconst terminalMintErrorReasons = new Set<IntegrationProviderErrorReason>([\n 'access-denied',\n 'installation-not-found',\n 'provider-rejected',\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 backoffError: backoffErrorSchema.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 backoffError?:\n | {\n message: string;\n status?: number | undefined;\n }\n | 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 ...(envelope.backoffError !== undefined && {backoffError: envelope.backoffError}),\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 backoffError: result.data.backoffError,\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 backoffError?: InstallationTokenEnvelope['backoffError'],\n): GithubIntegrationProviderError {\n return new GithubIntegrationProviderError(\n reason,\n backoffError?.message ?? `GitHub installation token mint is backed off after ${reason}`,\n Math.max(1, Math.ceil(retryAfterMs / 1000)),\n backoffError?.status,\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(\n error.reason,\n error.message,\n error.retryAfterSeconds,\n error.status,\n );\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","backoffErrorSchema","object","message","string","status","number","int","optional","terminalMintErrorReasons","Set","providerErrorReasonSchemaCoversUnion","installationTokenEnvelopeSchema","token","min","expiresAt","datetime","permissions","record","backoffUntil","backoffReason","backoffError","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","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 'provider-rejected',\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 backoffErrorSchema = z.object({\n message: z.string(),\n status: z.number().int().optional(),\n});\nconst terminalMintErrorReasons = new Set<IntegrationProviderErrorReason>([\n 'access-denied',\n 'installation-not-found',\n 'provider-rejected',\n 'malformed-provider-response',\n]);\n\n// Ref reasons describe ref-resolution failures, which a token mint never\n// observes; keep them out of the envelope schema and acknowledge them here.\ntype MissingProviderErrorReason = Exclude<\n IntegrationProviderErrorReason,\n (typeof providerErrorReasons)[number] | 'ref-not-found' | 'ref-invalid'\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 backoffError: backoffErrorSchema.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 backoffError?:\n | {\n message: string;\n status?: number | undefined;\n }\n | 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 ...(envelope.backoffError !== undefined && {backoffError: envelope.backoffError}),\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 backoffError: result.data.backoffError,\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 backoffError?: InstallationTokenEnvelope['backoffError'],\n): GithubIntegrationProviderError {\n return new GithubIntegrationProviderError(\n reason,\n backoffError?.message ?? `GitHub installation token mint is backed off after ${reason}`,\n Math.max(1, Math.ceil(retryAfterMs / 1000)),\n backoffError?.status,\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(\n error.reason,\n error.message,\n error.retryAfterSeconds,\n error.status,\n );\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","backoffErrorSchema","object","message","string","status","number","int","optional","terminalMintErrorReasons","Set","providerErrorReasonSchemaCoversUnion","installationTokenEnvelopeSchema","token","min","expiresAt","datetime","permissions","record","backoffUntil","backoffReason","backoffError","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","Error"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"agent-tools.d.ts","sourceRoot":"","sources":["../../src/core/agent-tools.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"agent-tools.d.ts","sourceRoot":"","sources":["../../src/core/agent-tools.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAGV,yBAAyB,EACzB,gBAAgB,EAChB,kBAAkB,EAClB,qBAAqB,EACrB,0BAA0B,EAC3B,MAAM,8BAA8B,CAAC;AAGtC,OAAO,EAEL,KAAK,+BAA+B,EACrC,MAAM,qCAAqC,CAAC;AAE7C,OAAO,KAAK,EAAC,kBAAkB,EAAC,MAAM,sBAAsB,CAAC;AAG7D,OAAO,EACL,KAAK,2BAA2B,EAChC,KAAK,iBAAiB,EACtB,KAAK,4BAA4B,EAGlC,MAAM,gCAAgC,CAAC;AAExC,YAAY,EACV,2BAA2B,EAC3B,uBAAuB,EACvB,iBAAiB,EACjB,yBAAyB,EACzB,+BAA+B,EAC/B,iCAAiC,EACjC,4BAA4B,EAC5B,0BAA0B,GAC3B,MAAM,gCAAgC,CAAC;AACxC,OAAO,EACL,oCAAoC,EACpC,0BAA0B,EAC1B,sBAAsB,EACtB,+BAA+B,GAChC,MAAM,gCAAgC,CAAC;AAExC,KAAK,2BAA2B,GAAG,qBAAqB,CAAC,QAAQ,CAAC,CAAC;AAEnE,KAAK,oBAAoB,GAAG;IAC1B,OAAO,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IAC9B,OAAO,EAAE,SAAS;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAC,EAAE,CAAC;IACjD,iBAAiB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,CAAC;CACzD,CAAC;AAuFF,qBAAa,wBACX,YACE,kBAAkB,CAChB,2BAA2B,EAC3B,4BAA4B,EAC5B,OAAO,EACP,oBAAoB,CACrB;IAIS,OAAO,CAAC,QAAQ,CAAC,OAAO;IAFpC,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAkC;IAEhE,YAA6B,OAAO,GAAE,+BAAoC,EAEzE;IAED,OAAO,IAAI,SAAS,2BAA2B,EAAE,CAEhD;IAED,gBAAgB,IAAI,yBAAyB,CAE5C;IAEK,WAAW,CACf,KAAK,EAAE,0BAA0B,CAAC,2BAA2B,EAAE,4BAA4B,CAAC,GAC3F,OAAO,CAAC,gBAAgB,CAAC,oBAAoB,CAAC,CAAC,CA8EjD;CACF;AAED,MAAM,WAAW,+BAA+B;IAC9C,6BAA6B,CAAC,EAC1B,CAAC,CAAC,YAAY,EAAE,MAAM,KAAK,OAAO,CAAC,kBAAkB,GAAG,SAAS,CAAC,CAAC,GACnE,SAAS,CAAC;IACd,aAAa,CAAC,EAAE,+BAA+B,GAAG,SAAS,CAAC;IAC5D,YAAY,CAAC,EAAE,uBAAuB,GAAG,SAAS,CAAC;CACpD;AAED,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,OAAO,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAAC,GAAG,SAAS,CAAC;IAClE,MAAM,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC5B,GAAG,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAC1B;AAED,MAAM,WAAW,gBAAgB;IAC/B,OAAO,CAAC,KAAK,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC,kBAAkB,CAAC,CAAC;IACzF,OAAO,CAAC,EAAE,CAAC,CAAC,KAAK,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC,GAAG,SAAS,CAAC;CACjG;AAED,MAAM,MAAM,uBAAuB,GAAG,CAAC,KAAK,EAAE,MAAM,KAAK,gBAAgB,CAAC;AAgE1E,wBAAgB,oBAAoB,CAClC,MAAM,EAAE,iBAAiB,EACzB,MAAM,EAAE,MAAM,GAAG,SAAS,EAC1B,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC5B,MAAM,GAAG,SAAS,CA6HpB;AAyED,wBAAgB,gCAAgC,CAC9C,MAAM,EAAE,iBAAiB,EACzB,MAAM,EAAE,MAAM,GAAG,SAAS,EAC1B,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC5B,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAWzB"}
@@ -1,7 +1,8 @@
1
1
  import { Octokit } from 'octokit';
2
2
  import { mapGithubError } from '#api/client.js';
3
3
  import { createGithubInstallationTokenProvider } from '#api/installation-token-provider.js';
4
- import { config, normalizedGithubApiBaseUrl } from '#config.js';
4
+ import { normalizedGithubApiBaseUrl } from '#config.js';
5
+ import { githubAppBotLogin } from './bot-identity.js';
5
6
  import { GithubIntegrationProviderError } from './errors.js';
6
7
  import { githubAgentToolCatalog, githubAgentToolSelectionCatalog } from './github-agent-tool-catalog.js';
7
8
  export { buildGithubAgentToolSelectionCatalog, DEFAULT_JOB_LOG_TAIL_LINES, githubAgentToolCatalog, githubAgentToolSelectionCatalog } from './github-agent-tool-catalog.js';
@@ -9,7 +10,6 @@ const GITHUB_GRAPHQL_ROUTE = 'POST /graphql';
9
10
  const GITHUB_ARTIFACT_ARCHIVE_FORMAT = 'zip';
10
11
  const GITHUB_ARTIFACT_DOWNLOAD_ROUTE = `GET /repos/{owner}/{repo}/actions/artifacts/{resource_id}/${GITHUB_ARTIFACT_ARCHIVE_FORMAT}`;
11
12
  const GITHUB_ARTIFACT_DOWNLOAD_TIMEOUT_MS = 30_000;
12
- const GITHUB_APP_BOT_SUFFIX = '[bot]';
13
13
  const PENDING_REVIEW_PAGE_SIZE = 100;
14
14
  const PENDING_REVIEW_MAX_PAGE_REQUESTS = 5;
15
15
  const PENDING_REVIEW_LOOKUP_TIMEOUT_MS = 15_000;
@@ -478,10 +478,6 @@ function latestPendingReviewOnPage(data, requiredIdentifier) {
478
478
  malformed
479
479
  };
480
480
  }
481
- function githubAppBotLogin() {
482
- const configuredUsername = config.GITHUB_APP_USERNAME?.trim() || config.GITHUB_APP_SLUG.trim();
483
- return configuredUsername.toLowerCase().endsWith(GITHUB_APP_BOT_SUFFIX) ? configuredUsername : `${configuredUsername}${GITHUB_APP_BOT_SUFFIX}`;
484
- }
485
481
  function githubToolResult(toolId, data, response, parameters, route) {
486
482
  const structuredContent = projectGithubToolOutput(toolId, data, response, parameters, route);
487
483
  if (structuredContent === undefined) {