@opengeni/github 0.5.2-canary.0 → 0.5.5-canary.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +7 -0
- package/dist/index.js +28 -1
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
- package/src/index.ts +32 -0
package/dist/index.d.ts
CHANGED
|
@@ -71,6 +71,12 @@ export type GitHubSignedStatePayload = {
|
|
|
71
71
|
[key: string]: unknown;
|
|
72
72
|
};
|
|
73
73
|
export declare function githubAppMissingSettings(settings: Settings): string[];
|
|
74
|
+
export declare function prReviewGitHubAppMissingSettings(settings: Settings): string[];
|
|
75
|
+
/** Project the separately configured review App onto the ordinary GitHub App
|
|
76
|
+
* authority client. This keeps its OAuth, signing, webhook, and installation
|
|
77
|
+
* identity disjoint from the platform GitHub App while reusing the same
|
|
78
|
+
* personal-owner / organization-owner proof implementation. */
|
|
79
|
+
export declare function settingsForPrReviewGitHubApp(settings: Settings): Settings;
|
|
74
80
|
export type GitHubAppSigningSettings = Pick<Settings, "githubAppId" | "githubAppPrivateKey">;
|
|
75
81
|
export declare function buildGitHubAppManifest(input: {
|
|
76
82
|
appName: string;
|
|
@@ -177,6 +183,7 @@ export declare function createGitHubAppInstallationTokenWithExpiry(settings: Set
|
|
|
177
183
|
export declare function createGitHubAppInstallationTokenWithSigningSettings(settings: GitHubAppSigningSettings, input: {
|
|
178
184
|
installationId: number;
|
|
179
185
|
repositoryIds: number[];
|
|
186
|
+
permissions?: Record<string, "read" | "write">;
|
|
180
187
|
}): Promise<GitHubAppInstallationToken>;
|
|
181
188
|
export declare function githubAppBotIdentity(settings: Settings): {
|
|
182
189
|
name: string;
|
package/dist/index.js
CHANGED
|
@@ -171,6 +171,30 @@ function githubAppMissingSettings(settings) {
|
|
|
171
171
|
};
|
|
172
172
|
return Object.entries(required).flatMap(([name, value]) => value && value.trim() ? [] : [name]);
|
|
173
173
|
}
|
|
174
|
+
function prReviewGitHubAppMissingSettings(settings) {
|
|
175
|
+
const required = {
|
|
176
|
+
OPENGENI_GITHUB_APP_MANIFEST_STATE_SECRET: settings.githubAppManifestStateSecret,
|
|
177
|
+
OPENGENI_ENVIRONMENTS_ENCRYPTION_KEY: settings.environmentsEncryptionKey,
|
|
178
|
+
OPENGENI_PR_REVIEW_GITHUB_APP_ID: settings.prReviewGithubAppId,
|
|
179
|
+
OPENGENI_PR_REVIEW_GITHUB_CLIENT_ID: settings.prReviewGithubClientId,
|
|
180
|
+
OPENGENI_PR_REVIEW_GITHUB_CLIENT_SECRET: settings.prReviewGithubClientSecret,
|
|
181
|
+
OPENGENI_PR_REVIEW_GITHUB_APP_SLUG: settings.prReviewGithubAppSlug,
|
|
182
|
+
OPENGENI_PR_REVIEW_GITHUB_WEBHOOK_SECRET: settings.prReviewGithubWebhookSecret,
|
|
183
|
+
OPENGENI_PR_REVIEW_GITHUB_APP_PRIVATE_KEY: settings.prReviewGithubAppPrivateKey
|
|
184
|
+
};
|
|
185
|
+
return Object.entries(required).flatMap(([name, value]) => value && value.trim() ? [] : [name]);
|
|
186
|
+
}
|
|
187
|
+
function settingsForPrReviewGitHubApp(settings) {
|
|
188
|
+
return {
|
|
189
|
+
...settings,
|
|
190
|
+
githubAppId: settings.prReviewGithubAppId,
|
|
191
|
+
githubClientId: settings.prReviewGithubClientId,
|
|
192
|
+
githubClientSecret: settings.prReviewGithubClientSecret,
|
|
193
|
+
githubAppSlug: settings.prReviewGithubAppSlug,
|
|
194
|
+
githubWebhookSecret: settings.prReviewGithubWebhookSecret,
|
|
195
|
+
githubAppPrivateKey: settings.prReviewGithubAppPrivateKey
|
|
196
|
+
};
|
|
197
|
+
}
|
|
174
198
|
function githubAppTokenMissingSettings(settings) {
|
|
175
199
|
const required = {
|
|
176
200
|
OPENGENI_GITHUB_APP_ID: settings.githubAppId,
|
|
@@ -568,7 +592,8 @@ async function createGitHubAppInstallationTokenWithSigningSettings(settings, inp
|
|
|
568
592
|
const jwt = await createGitHubAppJwt(settings);
|
|
569
593
|
return await createInstallationToken(jwt, {
|
|
570
594
|
installationId: input.installationId,
|
|
571
|
-
repositoryIds
|
|
595
|
+
repositoryIds,
|
|
596
|
+
...input.permissions ? { permissions: input.permissions } : {}
|
|
572
597
|
});
|
|
573
598
|
}
|
|
574
599
|
function githubAppBotIdentity(settings) {
|
|
@@ -921,8 +946,10 @@ export {
|
|
|
921
946
|
organizationAppManifestUrl,
|
|
922
947
|
personalAppManifestUrl,
|
|
923
948
|
personalGitHubGitBrokerRouteId,
|
|
949
|
+
prReviewGitHubAppMissingSettings,
|
|
924
950
|
readSignedState,
|
|
925
951
|
sealPersonalGitHubGitBrokerClaims,
|
|
952
|
+
settingsForPrReviewGitHubApp,
|
|
926
953
|
stateMaxAgeSeconds,
|
|
927
954
|
verifyGitHubInstallationAccessForUser,
|
|
928
955
|
verifySignedState
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["import type { Settings } from \"@opengeni/config\";\nimport type {\n GitHubInstallationBindingCandidate,\n GitHubInstallationBindingProof,\n GitHubRepository,\n GitHubRepositoryPermissions,\n GitHubUserInstallationAccess,\n GitHubUserRepositoryAccess,\n} from \"@opengeni/contracts\";\nimport {\n createCipheriv,\n createDecipheriv,\n createHash,\n createHmac,\n createPrivateKey,\n randomBytes,\n timingSafeEqual,\n} from \"node:crypto\";\nimport { SignJWT, importPKCS8 } from \"jose\";\n\nconst githubApiBase = \"https://api.github.com\";\nconst githubApiVersion = \"2022-11-28\";\nconst githubTokenMintTimeoutMs = 60_000;\n/** Bound for the server-side repository-id lookup at turn start (mint + read). */\nexport const githubRepositoryLookupTimeoutMs = 10_000;\nexport const stateMaxAgeSeconds = 60 * 60;\nconst pkcs8PrivateKeyHeader = `-----BEGIN ${\"PRIVATE KEY\"}-----`;\nconst rsaPrivateKeyHeader = `-----BEGIN ${\"RSA PRIVATE KEY\"}-----`;\n\nconst PERSONAL_GITHUB_GIT_BROKER_TOKEN_PREFIX = \"oggh1\";\nconst PERSONAL_GITHUB_GIT_BROKER_KEY_CONTEXT = \"opengeni:personal-github:git-broker:v1\";\nexport const PERSONAL_GITHUB_GIT_BROKER_TOKEN_TTL_SECONDS = 5 * 60;\n\nexport type PersonalGitHubGitBrokerRepositoryClaim = {\n repositoryId: string;\n fullName: string;\n canonicalUrl: string;\n ref: string;\n access: \"read\" | \"write\";\n selectionGeneration: number;\n routeId: string;\n};\n\nexport type PersonalGitHubGitBrokerClaims = {\n version: 1;\n accountId: string;\n workspaceId: string;\n sessionId: string;\n rootSessionId: string;\n turnId: string;\n attemptId: string;\n executionGeneration: number;\n originWorkspaceId: string;\n connectionId: string;\n connectionAuthorityGeneration: number;\n ownerSubjectId: string;\n credentialBindingId: string;\n selectionGeneration: number;\n nonce: string;\n issuedAt: number;\n expiresAt: number;\n};\n\nexport function personalGitHubGitBrokerRouteId(\n secret: string,\n input: Omit<PersonalGitHubGitBrokerClaims, \"nonce\" | \"issuedAt\" | \"expiresAt\"> & {\n repository: Omit<PersonalGitHubGitBrokerRepositoryClaim, \"routeId\">;\n },\n): string {\n const hmac = createHmac(\"sha256\", personalGitHubGitBrokerKey(secret));\n for (const value of [\n String(input.version),\n input.accountId,\n input.workspaceId,\n input.sessionId,\n input.rootSessionId,\n input.turnId,\n input.attemptId,\n String(input.executionGeneration),\n input.originWorkspaceId,\n input.connectionId,\n String(input.connectionAuthorityGeneration),\n input.ownerSubjectId,\n input.credentialBindingId,\n String(input.selectionGeneration),\n input.repository.repositoryId,\n input.repository.fullName,\n input.repository.canonicalUrl,\n input.repository.ref,\n input.repository.access,\n String(input.repository.selectionGeneration),\n ]) {\n const bytes = Buffer.from(value, \"utf8\");\n hmac.update(Buffer.from(String(bytes.byteLength), \"ascii\"));\n hmac.update(\":\");\n hmac.update(bytes);\n hmac.update(\";\");\n }\n return hmac.digest(\"base64url\");\n}\n\n/**\n * Seal exact Git broker authority into a confidential, authenticated bearer.\n * The payload is encrypted rather than merely signed so tenant, session,\n * connection, and repository identities are not readable from the sandbox's\n * short-lived token file.\n */\nexport function sealPersonalGitHubGitBrokerClaims(\n secret: string,\n claims: PersonalGitHubGitBrokerClaims,\n): string {\n assertPersonalGitHubGitBrokerClaims(claims);\n const iv = randomBytes(12);\n const cipher = createCipheriv(\"aes-256-gcm\", personalGitHubGitBrokerKey(secret), iv);\n cipher.setAAD(Buffer.from(PERSONAL_GITHUB_GIT_BROKER_TOKEN_PREFIX, \"ascii\"));\n const ciphertext = Buffer.concat([cipher.update(JSON.stringify(claims), \"utf8\"), cipher.final()]);\n return [\n PERSONAL_GITHUB_GIT_BROKER_TOKEN_PREFIX,\n iv.toString(\"base64url\"),\n ciphertext.toString(\"base64url\"),\n cipher.getAuthTag().toString(\"base64url\"),\n ].join(\".\");\n}\n\nexport function openPersonalGitHubGitBrokerClaims(\n secret: string,\n token: string,\n nowSeconds = Math.floor(Date.now() / 1_000),\n): PersonalGitHubGitBrokerClaims | null {\n const [prefix, encodedIv, encodedCiphertext, encodedTag, extra] = token.split(\".\");\n if (\n prefix !== PERSONAL_GITHUB_GIT_BROKER_TOKEN_PREFIX ||\n !encodedIv ||\n !encodedCiphertext ||\n !encodedTag ||\n extra !== undefined\n ) {\n return null;\n }\n try {\n const iv = Buffer.from(encodedIv, \"base64url\");\n const ciphertext = Buffer.from(encodedCiphertext, \"base64url\");\n const tag = Buffer.from(encodedTag, \"base64url\");\n if (iv.byteLength !== 12 || tag.byteLength !== 16 || ciphertext.byteLength > 4_096) {\n return null;\n }\n const decipher = createDecipheriv(\"aes-256-gcm\", personalGitHubGitBrokerKey(secret), iv);\n decipher.setAAD(Buffer.from(PERSONAL_GITHUB_GIT_BROKER_TOKEN_PREFIX, \"ascii\"));\n decipher.setAuthTag(tag);\n const payload = JSON.parse(\n Buffer.concat([decipher.update(ciphertext), decipher.final()]).toString(\"utf8\"),\n ) as unknown;\n assertPersonalGitHubGitBrokerClaims(payload);\n if (payload.issuedAt > nowSeconds + 60 || nowSeconds >= payload.expiresAt) return null;\n if (payload.expiresAt - payload.issuedAt > PERSONAL_GITHUB_GIT_BROKER_TOKEN_TTL_SECONDS) {\n return null;\n }\n return payload;\n } catch {\n return null;\n }\n}\n\nfunction personalGitHubGitBrokerKey(secret: string): Buffer {\n const normalized = secret.trim();\n if (!normalized) throw new Error(\"personal GitHub Git broker signing secret is unavailable\");\n return createHash(\"sha256\")\n .update(PERSONAL_GITHUB_GIT_BROKER_KEY_CONTEXT, \"utf8\")\n .update(\"\\0\", \"utf8\")\n .update(normalized, \"utf8\")\n .digest();\n}\n\nfunction assertPersonalGitHubGitBrokerClaims(\n value: unknown,\n): asserts value is PersonalGitHubGitBrokerClaims {\n if (!value || typeof value !== \"object\" || Array.isArray(value)) {\n throw new Error(\"invalid personal GitHub Git broker claims\");\n }\n const claims = value as Record<string, unknown>;\n const expectedKeys = new Set([\n \"version\",\n \"accountId\",\n \"workspaceId\",\n \"sessionId\",\n \"rootSessionId\",\n \"turnId\",\n \"attemptId\",\n \"executionGeneration\",\n \"originWorkspaceId\",\n \"connectionId\",\n \"connectionAuthorityGeneration\",\n \"ownerSubjectId\",\n \"credentialBindingId\",\n \"selectionGeneration\",\n \"nonce\",\n \"issuedAt\",\n \"expiresAt\",\n ]);\n const strings = [\n \"accountId\",\n \"workspaceId\",\n \"sessionId\",\n \"rootSessionId\",\n \"turnId\",\n \"attemptId\",\n \"originWorkspaceId\",\n \"connectionId\",\n \"ownerSubjectId\",\n \"credentialBindingId\",\n \"nonce\",\n ];\n if (\n claims.version !== 1 ||\n strings.some(\n (field) =>\n typeof claims[field] !== \"string\" ||\n claims[field].length === 0 ||\n claims[field].length > (field === \"ownerSubjectId\" ? 512 : 128),\n ) ||\n !positiveIntegerClaim(claims.executionGeneration) ||\n !positiveIntegerClaim(claims.connectionAuthorityGeneration) ||\n !positiveIntegerClaim(claims.selectionGeneration) ||\n !positiveIntegerClaim(claims.issuedAt) ||\n !positiveIntegerClaim(claims.expiresAt) ||\n Object.keys(claims).some((key) => !expectedKeys.has(key))\n ) {\n throw new Error(\"invalid personal GitHub Git broker claims\");\n }\n}\n\nfunction positiveIntegerClaim(value: unknown): value is number {\n return typeof value === \"number\" && Number.isSafeInteger(value) && value > 0;\n}\n\nexport class GitHubAppConfigurationError extends Error {\n constructor(readonly missing: string[]) {\n super(\"GitHub App is not configured\");\n }\n}\n\nexport class GitHubAppApiError extends Error {\n constructor(\n message: string,\n readonly status: number | null = null,\n ) {\n super(message);\n }\n}\n\nexport type GitHubInstallationAuthorityFailure =\n | \"authority_denied\"\n | \"authority_unavailable\"\n | \"installation_missing\"\n | \"installation_suspended\"\n | \"repository_access_empty\";\n\nexport class GitHubInstallationAuthorityError extends GitHubAppApiError {\n constructor(\n readonly reason: GitHubInstallationAuthorityFailure,\n message: string,\n status: number | null = null,\n ) {\n super(message, status);\n }\n}\n\nexport type GitHubAppInstallationSummary = {\n installationId: number;\n accountId: number;\n accountLogin: string | null;\n accountType: string | null;\n suspended: boolean;\n};\n\nexport type GitHubSignedStatePayload = {\n nonce: string;\n iat: number;\n accountId?: string;\n workspaceId?: string;\n [key: string]: unknown;\n};\n\nexport function githubAppMissingSettings(settings: Settings): string[] {\n const required: Record<string, string | undefined> = {\n OPENGENI_GITHUB_APP_ID: settings.githubAppId,\n OPENGENI_GITHUB_CLIENT_ID: settings.githubClientId,\n OPENGENI_GITHUB_CLIENT_SECRET: settings.githubClientSecret,\n OPENGENI_GITHUB_APP_SLUG: settings.githubAppSlug,\n OPENGENI_GITHUB_APP_PRIVATE_KEY: settings.githubAppPrivateKey,\n };\n return Object.entries(required).flatMap(([name, value]) => (value && value.trim() ? [] : [name]));\n}\n\nexport type GitHubAppSigningSettings = Pick<Settings, \"githubAppId\" | \"githubAppPrivateKey\">;\n\nfunction githubAppTokenMissingSettings(settings: GitHubAppSigningSettings): string[] {\n const required: Record<string, string | undefined> = {\n OPENGENI_GITHUB_APP_ID: settings.githubAppId,\n OPENGENI_GITHUB_APP_PRIVATE_KEY: settings.githubAppPrivateKey,\n };\n return Object.entries(required).flatMap(([name, value]) => (value && value.trim() ? [] : [name]));\n}\n\nexport function buildGitHubAppManifest(input: {\n appName: string;\n baseUrl: string;\n public: boolean;\n includeCiPermissions: boolean;\n setupUrl?: string;\n}): Record<string, unknown> {\n const base = input.baseUrl.replace(/\\/+$/, \"\");\n const permissions: Record<string, string> = {\n metadata: \"read\",\n contents: \"write\",\n pull_requests: \"write\",\n // Required for the authenticated-user membership endpoint that proves an\n // active organization owner. Existing installations must approve this\n // permission before organization-owner self-service can succeed.\n members: \"read\",\n };\n if (input.includeCiPermissions) {\n permissions.actions = \"read\";\n permissions.checks = \"read\";\n permissions.statuses = \"write\";\n }\n const manifest: Record<string, unknown> = {\n name: input.appName,\n url: base,\n redirect_url: `${base}/v1/github/app-manifest/callback`,\n callback_urls: [`${base}/v1/github/oauth/callback`],\n public: input.public,\n // A setup URL and OAuth-on-install are mutually exclusive in GitHub's App\n // contract. OpenGeni needs the setup callback to receive the installation\n // id, then starts its own exact user-authorization flow.\n request_oauth_on_install: !input.setupUrl,\n default_permissions: permissions,\n };\n if (input.setupUrl) {\n manifest.setup_url = input.setupUrl;\n manifest.setup_on_update = true;\n }\n return manifest;\n}\n\nexport function personalAppManifestUrl(state: string): string {\n return `https://github.com/settings/apps/new?state=${state}`;\n}\n\nexport function organizationAppManifestUrl(organization: string, state: string): string {\n return `https://github.com/organizations/${encodeURIComponent(organization)}/settings/apps/new?state=${state}`;\n}\n\nexport function githubOAuthAuthorizeUrl(input: {\n clientId: string;\n state: string;\n redirectUri?: string;\n}): string {\n const url = new URL(\"https://github.com/login/oauth/authorize\");\n url.searchParams.set(\"client_id\", input.clientId);\n url.searchParams.set(\"state\", input.state);\n if (input.redirectUri) {\n url.searchParams.set(\"redirect_uri\", input.redirectUri);\n }\n return url.toString();\n}\n\nexport function createSignedState(\n secret: string,\n payloadOrNow: Record<string, unknown> | number = {},\n nowArg = Math.floor(Date.now() / 1000),\n): string {\n const payloadInput = typeof payloadOrNow === \"number\" ? {} : payloadOrNow;\n const now = typeof payloadOrNow === \"number\" ? payloadOrNow : nowArg;\n const payload = {\n ...payloadInput,\n nonce: randomBytes(16).toString(\"base64url\"),\n iat: now,\n };\n const encoded = Buffer.from(JSON.stringify(payload)).toString(\"base64url\");\n return `${encoded}.${signStatePayload(encoded, secret)}`;\n}\n\nexport function readSignedState(\n state: string,\n secret: string,\n now = Math.floor(Date.now() / 1000),\n): GitHubSignedStatePayload | null {\n const [encoded, signature] = state.split(\".\", 2);\n if (!encoded || !signature) {\n return null;\n }\n const expected = signStatePayload(encoded, secret);\n if (!safeEqual(signature, expected)) {\n return null;\n }\n let payload: unknown;\n try {\n payload = JSON.parse(Buffer.from(encoded, \"base64url\").toString(\"utf8\"));\n } catch {\n return null;\n }\n if (\n !payload ||\n typeof payload !== \"object\" ||\n typeof (payload as { iat?: unknown }).iat !== \"number\" ||\n typeof (payload as { nonce?: unknown }).nonce !== \"string\"\n ) {\n return null;\n }\n const age = now - (payload as { iat: number }).iat;\n return age >= 0 && age <= stateMaxAgeSeconds ? (payload as GitHubSignedStatePayload) : null;\n}\n\nexport function verifySignedState(\n state: string,\n secret: string,\n now = Math.floor(Date.now() / 1000),\n): boolean {\n return readSignedState(state, secret, now) !== null;\n}\n\nexport function envLinesFromGitHubManifestConversion(payload: Record<string, unknown>): string[] {\n const privateKey = String(payload.pem ?? \"\").replace(/\\n/g, \"\\\\n\");\n return [\n `OPENGENI_GITHUB_APP_ID=${payload.id ?? \"\"}`,\n `OPENGENI_GITHUB_CLIENT_ID=${payload.client_id ?? \"\"}`,\n `OPENGENI_GITHUB_CLIENT_SECRET=${payload.client_secret ?? \"\"}`,\n `OPENGENI_GITHUB_APP_SLUG=${payload.slug ?? \"\"}`,\n `OPENGENI_GITHUB_WEBHOOK_SECRET=${payload.webhook_secret ?? \"\"}`,\n `OPENGENI_GITHUB_APP_PRIVATE_KEY=\"${privateKey}\"`,\n ];\n}\n\nexport async function convertGitHubAppManifest(code: string): Promise<Record<string, unknown>> {\n const response = await fetch(`${githubApiBase}/app-manifests/${code}/conversions`, {\n method: \"POST\",\n headers: githubHeaders(undefined),\n });\n if (!response.ok) {\n throw new GitHubAppApiError(await githubErrorMessage(response));\n }\n const payload = await response.json();\n if (!payload || typeof payload !== \"object\" || Array.isArray(payload)) {\n throw new GitHubAppApiError(\"GitHub returned an invalid manifest conversion payload\");\n }\n return payload as Record<string, unknown>;\n}\n\nexport async function listGitHubAppInstallationSummaries(\n settings: Settings,\n): Promise<GitHubAppInstallationSummary[]> {\n const missing = githubAppMissingSettings(settings);\n if (missing.length > 0) {\n throw new GitHubAppConfigurationError(missing);\n }\n const jwt = await createGitHubAppJwt(settings);\n const installations = await listInstallations(jwt);\n return installations.map(installationSummaryFromPayload);\n}\n\nexport async function getGitHubAppInstallationSummary(\n settings: Settings,\n installationId: number,\n): Promise<GitHubAppInstallationSummary | null> {\n const installations = await listGitHubAppInstallationSummaries(settings);\n return (\n installations.find((installation) => installation.installationId === installationId) ?? null\n );\n}\n\nexport async function verifyGitHubInstallationAccessForUser(\n settings: Settings,\n input: {\n code: string;\n installationId: number;\n },\n): Promise<GitHubAppInstallationSummary> {\n const token = await exchangeGitHubOAuthCodeForUserToken(settings, input.code);\n const installations = await listUserAccessibleInstallations(token);\n const installation = installations.find(\n (candidate) => candidate.installationId === input.installationId,\n );\n if (!installation) {\n throw new GitHubAppApiError(\"GitHub installation is not accessible to the installing user\");\n }\n return installation;\n}\n\n/**\n * Exchange a GitHub App user-authorization code and discover the installations\n * and repositories the user can explicitly access. This is compatibility\n * discovery metadata only: visibility and repository permission bits do not\n * prove that the human may install, configure, or bind the App installation.\n * No production binding path may treat this result as authority.\n */\nexport async function authorizeGitHubAppUser(\n settings: Settings,\n input: { code: string },\n): Promise<GitHubUserInstallationAccess[]> {\n const token = await exchangeGitHubOAuthCodeForUserToken(settings, input.code);\n const installations = await listUserAccessibleInstallations(token);\n return await Promise.all(\n installations.map(async (installation) => ({\n ...installation,\n repositories: installation.suspended\n ? []\n : await listUserInstallationRepositories(token, installation),\n })),\n );\n}\n\n/**\n * Prove current GitHub installation authority without treating repository\n * administration or installation visibility as delegation authority.\n *\n * GitHub exposes an exact personal-account owner through the authenticated\n * user's immutable id. For organizations, GitHub's authenticated membership\n * endpoint exposes active owners as role=admin. GitHub does not expose an\n * equivalent current-authority receipt for App Managers, so that case remains\n * unsupported and fails closed.\n */\nexport async function authorizeGitHubInstallationBinding(\n settings: Settings,\n input: { code: string; installationId: number },\n): Promise<GitHubInstallationBindingProof> {\n const userToken = await exchangeGitHubOAuthCodeForUserToken(settings, input.code);\n const actor = await getAuthenticatedGitHubUser(userToken);\n const visibleInstallations = await listUserAccessibleInstallations(userToken);\n const visible = visibleInstallations.find(\n (installation) => installation.installationId === input.installationId,\n );\n if (!visible) {\n throw new GitHubInstallationAuthorityError(\n \"authority_denied\",\n \"GitHub did not associate this installation with the authorized user\",\n );\n }\n\n const jwt = await createGitHubAppJwt(settings);\n const livePayload = (await listInstallations(jwt)).find(\n (installation) => asInt(installation.id) === input.installationId,\n );\n if (!livePayload) {\n throw new GitHubInstallationAuthorityError(\n \"installation_missing\",\n \"GitHub App installation was deleted or is not owned by this App\",\n );\n }\n const installation = installationSummaryFromPayload(livePayload);\n if (\n installation.accountId !== visible.accountId ||\n installation.accountLogin !== visible.accountLogin ||\n installation.accountType !== visible.accountType\n ) {\n throw new GitHubInstallationAuthorityError(\n \"authority_denied\",\n \"GitHub installation identity changed during authorization\",\n );\n }\n if (installation.suspended) {\n throw new GitHubInstallationAuthorityError(\n \"installation_suspended\",\n \"GitHub App installation is suspended\",\n );\n }\n\n let authorityKind: GitHubInstallationBindingProof[\"authorityKind\"];\n if (installation.accountType === \"User\" && actor.id === installation.accountId) {\n authorityKind = \"personal_owner\";\n } else if (installation.accountType === \"Organization\" && installation.accountLogin) {\n await assertActiveOrganizationOwner(\n userToken,\n installation.accountId,\n installation.accountLogin,\n );\n authorityKind = \"organization_owner\";\n } else {\n throw new GitHubInstallationAuthorityError(\n \"authority_denied\",\n \"Only a GitHub personal-account owner or organization owner may bind an installation\",\n );\n }\n\n const installationToken = await createInstallationToken(jwt, {\n installationId: installation.installationId,\n });\n const repositories = await listInstallationRepositories(\n installationToken.token,\n installation.installationId,\n { login: installation.accountLogin, type: installation.accountType },\n );\n if (repositories.length === 0) {\n throw new GitHubInstallationAuthorityError(\n \"repository_access_empty\",\n \"GitHub App installation does not currently grant access to any repositories\",\n );\n }\n if (authorityKind === \"organization_owner\") {\n // Repository enumeration is an async provider boundary. Re-read the live\n // owner tuple after it so a role revoked after the chooser proof cannot be\n // durably bound with a later, misleading authority timestamp.\n await assertActiveOrganizationOwner(\n userToken,\n installation.accountId,\n installation.accountLogin!,\n );\n }\n return {\n actorId: actor.id,\n actorLogin: actor.login,\n authorityKind,\n installation,\n repositories,\n };\n}\n\n/**\n * Discover existing installations that the freshly authorized GitHub human\n * can bind as an exact personal owner or active organization owner.\n *\n * `GET /user/installations` is discovery input only. Every candidate is\n * cross-checked against the App's live installation inventory and an\n * organization candidate requires a live `state=active, role=admin`\n * membership proof. The later exact authorization still re-runs the complete\n * proof immediately before the durable bind.\n */\nexport async function discoverGitHubInstallationBindingCandidates(\n settings: Settings,\n input: { code: string },\n): Promise<GitHubInstallationBindingCandidate[]> {\n const userToken = await exchangeGitHubOAuthCodeForUserToken(settings, input.code);\n const actor = await getAuthenticatedGitHubUser(userToken);\n const visibleInstallations = await listUserAccessibleInstallations(userToken);\n const jwt = await createGitHubAppJwt(settings);\n const liveInstallations = new Map(\n (await listInstallations(jwt)).map((payload) => {\n const installation = installationSummaryFromPayload(payload);\n return [installation.installationId, installation] as const;\n }),\n );\n const candidates: GitHubInstallationBindingCandidate[] = [];\n\n for (const visible of visibleInstallations) {\n const installation = liveInstallations.get(visible.installationId);\n if (\n !installation ||\n installation.suspended ||\n installation.accountId !== visible.accountId ||\n installation.accountLogin !== visible.accountLogin ||\n installation.accountType !== visible.accountType\n ) {\n continue;\n }\n if (installation.accountType === \"User\" && actor.id === installation.accountId) {\n candidates.push({ installation, authorityKind: \"personal_owner\" });\n continue;\n }\n if (installation.accountType !== \"Organization\" || !installation.accountLogin) {\n continue;\n }\n try {\n await assertActiveOrganizationOwner(\n userToken,\n installation.accountId,\n installation.accountLogin,\n );\n candidates.push({ installation, authorityKind: \"organization_owner\" });\n } catch (error) {\n if (\n error instanceof GitHubInstallationAuthorityError &&\n error.reason === \"authority_unavailable\"\n ) {\n continue;\n }\n if (\n error instanceof GitHubInstallationAuthorityError &&\n error.reason === \"authority_denied\"\n ) {\n continue;\n }\n throw error;\n }\n }\n\n return candidates.sort((left, right) =>\n (left.installation.accountLogin ?? \"\").localeCompare(right.installation.accountLogin ?? \"\"),\n );\n}\n\nexport async function listGitHubAppRepositories(\n settings: Settings,\n input: {\n installationIds?: number[];\n } = {},\n): Promise<GitHubRepository[]> {\n const missing = githubAppMissingSettings(settings);\n if (missing.length > 0) {\n throw new GitHubAppConfigurationError(missing);\n }\n return await listGitHubAppRepositoriesWithSigningSettings(settings, input);\n}\n\n/** List repositories for a separately registered App that needs only signing credentials. */\nexport async function listGitHubAppRepositoriesWithSigningSettings(\n settings: GitHubAppSigningSettings,\n input: {\n installationIds?: number[];\n } = {},\n): Promise<GitHubRepository[]> {\n const missing = githubAppTokenMissingSettings(settings);\n if (missing.length > 0) {\n throw new GitHubAppConfigurationError(missing);\n }\n const allowedInstallations = input.installationIds ? new Set(input.installationIds) : null;\n if (allowedInstallations && allowedInstallations.size === 0) {\n return [];\n }\n const jwt = await createGitHubAppJwt(settings);\n const installations = await listInstallations(jwt);\n const repositories: GitHubRepository[] = [];\n for (const installation of installations) {\n if (installation.suspended_at) {\n continue;\n }\n const installationId = asInt(installation.id);\n if (installationId === null) {\n continue;\n }\n if (allowedInstallations && !allowedInstallations.has(installationId)) {\n continue;\n }\n const account =\n typeof installation.account === \"object\" && installation.account\n ? (installation.account as Record<string, unknown>)\n : {};\n const token = await createInstallationToken(jwt, { installationId });\n repositories.push(\n ...(await listInstallationRepositories(token.token, installationId, account)),\n );\n }\n repositories.sort((left, right) => left.fullName.localeCompare(right.fullName));\n return repositories;\n}\n\nexport type GitHubAppInstallationRepositoryLookupInput = {\n installationId: number;\n owner: string;\n name: string;\n};\n\nexport type GitHubAppInstallationRepositoryLookup = (\n input: GitHubAppInstallationRepositoryLookupInput,\n) => Promise<GitHubRepository | null>;\n\n/**\n * Resolve one `owner/name` repository through an exact App installation and\n * return GitHub's stable repository identity, or null when that installation\n * cannot see the repository. The server-side lookup token never leaves the\n * caller and grants nothing by itself: the workspace allowlist decides whether\n * the returned id may mint a sandbox-bound token.\n */\nexport async function getGitHubAppInstallationRepository(\n settings: Settings,\n input: GitHubAppInstallationRepositoryLookupInput,\n): Promise<GitHubRepository | null> {\n return await createGitHubAppInstallationRepositoryLookup(settings)(input);\n}\n\n/**\n * One lookup client that reuses a server-side installation token per\n * installation for its lifetime (one worker turn), so several bare repository\n * URIs from the same installation cost one mint plus one read each.\n */\nexport function createGitHubAppInstallationRepositoryLookup(\n settings: Settings,\n): GitHubAppInstallationRepositoryLookup {\n const missing = githubAppMissingSettings(settings);\n if (missing.length > 0) {\n throw new GitHubAppConfigurationError(missing);\n }\n const tokens = new Map<number, Promise<GitHubAppInstallationToken>>();\n const installationToken = (installationId: number): Promise<GitHubAppInstallationToken> => {\n let pending = tokens.get(installationId);\n if (!pending) {\n // Metadata-read only: the lookup needs the repository id, never contents.\n // Bounded well below the sandbox mint timeout so a slow GitHub cannot\n // hold turn start; the caller proceeds bare on expiry.\n pending = createGitHubAppJwt(settings).then((jwt) =>\n createInstallationToken(jwt, {\n installationId,\n permissions: { metadata: \"read\" },\n timeoutMs: githubRepositoryLookupTimeoutMs,\n }),\n );\n pending.catch(() => tokens.delete(installationId));\n tokens.set(installationId, pending);\n }\n return pending;\n };\n return async (input) => {\n const owner = input.owner.trim();\n const name = input.name.trim();\n if (\n !/^[A-Za-z0-9](?:[A-Za-z0-9-]*[A-Za-z0-9])?$/u.test(owner) ||\n !/^[A-Za-z0-9._-]+$/u.test(name)\n ) {\n return null;\n }\n const token = await installationToken(input.installationId);\n const response = await fetch(\n `${githubApiBase}/repos/${encodeURIComponent(owner)}/${encodeURIComponent(name)}`,\n {\n headers: githubHeaders(token.token),\n signal: AbortSignal.timeout(githubRepositoryLookupTimeoutMs),\n },\n );\n if (response.status === 404) {\n return null;\n }\n if (!response.ok) {\n throw new GitHubAppApiError(await githubErrorMessage(response), response.status);\n }\n const payload = await response.json();\n if (!payload || typeof payload !== \"object\" || Array.isArray(payload)) {\n throw new GitHubAppApiError(\"GitHub returned an invalid repository payload\");\n }\n const record = payload as Record<string, unknown>;\n const account =\n record.owner && typeof record.owner === \"object\" && !Array.isArray(record.owner)\n ? (record.owner as Record<string, unknown>)\n : {};\n return repositoryFromPayload(record, input.installationId, account);\n };\n}\n\nexport async function createGitHubAppInstallationToken(\n settings: Settings,\n input: {\n installationId: number;\n repositoryIds: number[];\n },\n): Promise<string> {\n return (await createGitHubAppInstallationTokenWithExpiry(settings, input)).token;\n}\n\nexport type GitHubAppInstallationToken = {\n token: string;\n expiresAt: string | null;\n};\n\nexport async function createGitHubAppInstallationTokenWithExpiry(\n settings: Settings,\n input: {\n installationId: number;\n repositoryIds: number[];\n },\n): Promise<GitHubAppInstallationToken> {\n const missing = githubAppMissingSettings(settings);\n if (missing.length > 0) {\n throw new GitHubAppConfigurationError(missing);\n }\n return await createGitHubAppInstallationTokenWithSigningSettings(settings, input);\n}\n\n/** Mint for a separately registered App without requiring unrelated OAuth settings. */\nexport async function createGitHubAppInstallationTokenWithSigningSettings(\n settings: GitHubAppSigningSettings,\n input: {\n installationId: number;\n repositoryIds: number[];\n },\n): Promise<GitHubAppInstallationToken> {\n const missing = githubAppTokenMissingSettings(settings);\n if (missing.length > 0) {\n throw new GitHubAppConfigurationError(missing);\n }\n if (!Array.isArray(input.repositoryIds)) {\n throw new GitHubAppApiError(\n \"GitHub installation token mint requires an explicit, unique repository allowlist\",\n );\n }\n const repositoryIds = [...new Set(input.repositoryIds)];\n if (\n !Number.isSafeInteger(input.installationId) ||\n input.installationId <= 0 ||\n repositoryIds.length === 0 ||\n repositoryIds.length !== input.repositoryIds.length ||\n repositoryIds.some((id) => !Number.isSafeInteger(id) || id <= 0)\n ) {\n throw new GitHubAppApiError(\n \"GitHub installation token mint requires an explicit, unique repository allowlist\",\n );\n }\n const jwt = await createGitHubAppJwt(settings);\n return await createInstallationToken(jwt, {\n installationId: input.installationId,\n repositoryIds,\n });\n}\n\nexport function githubAppBotIdentity(settings: Settings): { name: string; email: string } | null {\n const appId = settings.githubAppId?.trim();\n const slug = settings.githubAppSlug?.trim();\n if (!appId || !slug) {\n return null;\n }\n const login = `${slug}[bot]`;\n return {\n name: login,\n email: `${appId}+${login}@users.noreply.github.com`,\n };\n}\n\nasync function createGitHubAppJwt(settings: GitHubAppSigningSettings): Promise<string> {\n const privateKey = normalizeGitHubAppPrivateKey(settings.githubAppPrivateKey ?? \"\");\n const appId = settings.githubAppId?.trim();\n if (!appId || !privateKey) {\n throw new GitHubAppConfigurationError(githubAppTokenMissingSettings(settings));\n }\n const key = await importPKCS8(privateKey, \"RS256\");\n const now = Math.floor(Date.now() / 1000);\n return await new SignJWT({})\n .setProtectedHeader({ alg: \"RS256\" })\n .setIssuedAt(now - 60)\n .setExpirationTime(now + 9 * 60)\n .setIssuer(appId)\n .sign(key);\n}\n\nasync function listInstallations(token: string): Promise<Array<Record<string, unknown>>> {\n const out: Array<Record<string, unknown>> = [];\n for (let page = 1; ; page += 1) {\n const payload = await githubGet(\"/app/installations\", token, {\n per_page: \"100\",\n page: String(page),\n });\n if (!Array.isArray(payload)) {\n throw new GitHubAppApiError(\"GitHub returned an invalid installations payload\");\n }\n out.push(\n ...payload.filter((item): item is Record<string, unknown> =>\n Boolean(item && typeof item === \"object\" && !Array.isArray(item)),\n ),\n );\n if (payload.length < 100) {\n return out;\n }\n }\n}\n\nasync function exchangeGitHubOAuthCodeForUserToken(\n settings: Settings,\n code: string,\n): Promise<string> {\n if (!settings.githubClientId || !settings.githubClientSecret) {\n throw new GitHubAppConfigurationError(githubAppMissingSettings(settings));\n }\n const response = await fetch(\"https://github.com/login/oauth/access_token\", {\n method: \"POST\",\n headers: {\n Accept: \"application/json\",\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify({\n client_id: settings.githubClientId,\n client_secret: settings.githubClientSecret,\n code,\n }),\n });\n if (!response.ok) {\n throw new GitHubAppApiError(await githubErrorMessage(response));\n }\n const payload = await response.json();\n if (!payload || typeof payload !== \"object\" || typeof payload.access_token !== \"string\") {\n throw new GitHubAppApiError(\"GitHub returned an invalid OAuth token payload\");\n }\n return payload.access_token;\n}\n\nasync function getAuthenticatedGitHubUser(token: string): Promise<{ id: number; login: string }> {\n const payload = await githubGet(\"/user\", token);\n const id = payload && typeof payload === \"object\" ? asInt(payload.id) : null;\n const login =\n payload && typeof payload === \"object\" && typeof payload.login === \"string\"\n ? payload.login\n : null;\n if (id === null || !login) {\n throw new GitHubAppApiError(\"GitHub returned an invalid authenticated user payload\");\n }\n return { id, login };\n}\n\nasync function getAuthenticatedOrganizationMembership(\n token: string,\n organizationLogin: string,\n): Promise<{ organizationId: number; role: string; state: string }> {\n let payload: any;\n try {\n payload = await githubGet(\n `/user/memberships/orgs/${encodeURIComponent(organizationLogin)}`,\n token,\n );\n } catch (error) {\n if (error instanceof GitHubAppApiError) {\n throw new GitHubInstallationAuthorityError(\n \"authority_unavailable\",\n \"GitHub could not prove current organization-owner membership\",\n error.status,\n );\n }\n throw error;\n }\n const organization =\n payload && typeof payload === \"object\" && payload.organization ? payload.organization : null;\n const organizationId =\n organization && typeof organization === \"object\" ? asInt(organization.id) : null;\n if (\n organizationId === null ||\n typeof payload?.role !== \"string\" ||\n typeof payload?.state !== \"string\"\n ) {\n throw new GitHubInstallationAuthorityError(\n \"authority_unavailable\",\n \"GitHub returned an invalid organization membership proof\",\n );\n }\n return { organizationId, role: payload.role, state: payload.state };\n}\n\nasync function assertActiveOrganizationOwner(\n token: string,\n organizationId: number,\n organizationLogin: string,\n): Promise<void> {\n const membership = await getAuthenticatedOrganizationMembership(token, organizationLogin);\n if (\n membership.organizationId !== organizationId ||\n membership.state !== \"active\" ||\n membership.role !== \"admin\"\n ) {\n throw new GitHubInstallationAuthorityError(\n \"authority_denied\",\n \"Only an active GitHub organization owner may bind this installation\",\n );\n }\n}\n\nasync function listUserAccessibleInstallations(\n token: string,\n): Promise<GitHubAppInstallationSummary[]> {\n const out: GitHubAppInstallationSummary[] = [];\n for (let page = 1; ; page += 1) {\n const payload = await githubGet(\"/user/installations\", token, {\n per_page: \"100\",\n page: String(page),\n });\n const installations: unknown[] | null =\n payload && typeof payload === \"object\" && Array.isArray(payload.installations)\n ? (payload.installations as unknown[])\n : null;\n if (!installations) {\n throw new GitHubAppApiError(\"GitHub returned an invalid user installations payload\");\n }\n out.push(\n ...installations\n .filter((item): item is Record<string, unknown> =>\n Boolean(item && typeof item === \"object\" && !Array.isArray(item)),\n )\n .map(installationSummaryFromPayload),\n );\n if (installations.length < 100) {\n return out;\n }\n }\n}\n\nasync function listUserInstallationRepositories(\n token: string,\n installation: GitHubAppInstallationSummary,\n): Promise<GitHubUserRepositoryAccess[]> {\n const out: GitHubUserRepositoryAccess[] = [];\n const account = {\n ...(installation.accountLogin ? { login: installation.accountLogin } : {}),\n ...(installation.accountType ? { type: installation.accountType } : {}),\n };\n for (let page = 1; ; page += 1) {\n const payload = await githubGet(\n `/user/installations/${installation.installationId}/repositories`,\n token,\n { per_page: \"100\", page: String(page) },\n );\n if (\n !payload ||\n typeof payload !== \"object\" ||\n Array.isArray(payload) ||\n !Array.isArray(payload.repositories)\n ) {\n throw new GitHubAppApiError(\n \"GitHub returned an invalid user installation repositories payload\",\n );\n }\n for (const repository of payload.repositories) {\n if (!repository || typeof repository !== \"object\" || Array.isArray(repository)) {\n continue;\n }\n const record = repository as Record<string, unknown>;\n out.push({\n ...repositoryFromPayload(record, installation.installationId, account),\n permissions: repositoryPermissionsFromPayload(record.permissions),\n });\n }\n if (payload.repositories.length < 100) {\n return out;\n }\n }\n}\n\nasync function createInstallationToken(\n appJwt: string,\n input: {\n installationId: number;\n repositoryIds?: number[];\n /** Narrow the token below the installation's granted permissions. */\n permissions?: Record<string, \"read\" | \"write\">;\n timeoutMs?: number;\n },\n): Promise<GitHubAppInstallationToken> {\n const body: Record<string, unknown> = {};\n if (input.repositoryIds && input.repositoryIds.length > 0) {\n body.repository_ids = input.repositoryIds;\n }\n if (input.permissions && Object.keys(input.permissions).length > 0) {\n body.permissions = input.permissions;\n }\n const scoped = Object.keys(body).length > 0;\n const response = await fetch(\n `${githubApiBase}/app/installations/${input.installationId}/access_tokens`,\n {\n method: \"POST\",\n headers: {\n ...githubHeaders(appJwt),\n ...(scoped ? { \"Content-Type\": \"application/json\" } : {}),\n },\n signal: AbortSignal.timeout(input.timeoutMs ?? githubTokenMintTimeoutMs),\n ...(scoped ? { body: JSON.stringify(body) } : {}),\n },\n );\n if (!response.ok) {\n throw new GitHubAppApiError(await githubErrorMessage(response));\n }\n const payload = await response.json();\n if (!payload || typeof payload !== \"object\" || typeof payload.token !== \"string\") {\n throw new GitHubAppApiError(\"GitHub returned an invalid installation token payload\");\n }\n return {\n token: payload.token,\n expiresAt: typeof payload.expires_at === \"string\" ? payload.expires_at : null,\n };\n}\n\nasync function listInstallationRepositories(\n token: string,\n installationId: number,\n account: Record<string, unknown>,\n): Promise<GitHubRepository[]> {\n const out: GitHubRepository[] = [];\n for (let page = 1; ; page += 1) {\n const payload = await githubGet(\"/installation/repositories\", token, {\n per_page: \"100\",\n page: String(page),\n });\n if (\n !payload ||\n typeof payload !== \"object\" ||\n Array.isArray(payload) ||\n !Array.isArray(payload.repositories)\n ) {\n throw new GitHubAppApiError(\"GitHub returned an invalid repositories payload\");\n }\n for (const repo of payload.repositories) {\n if (repo && typeof repo === \"object\" && !Array.isArray(repo)) {\n out.push(repositoryFromPayload(repo as Record<string, unknown>, installationId, account));\n }\n }\n if (payload.repositories.length < 100) {\n return out;\n }\n }\n}\n\nfunction installationSummaryFromPayload(\n payload: Record<string, unknown>,\n): GitHubAppInstallationSummary {\n const installationId = asInt(payload.id);\n if (installationId === null) {\n throw new GitHubAppApiError(\"GitHub returned an installation without id\");\n }\n const account =\n typeof payload.account === \"object\" && payload.account\n ? (payload.account as Record<string, unknown>)\n : {};\n const accountId = asInt(account.id);\n if (accountId === null) {\n throw new GitHubAppApiError(\"GitHub returned an installation without an account id\");\n }\n return {\n installationId,\n accountId,\n accountLogin: typeof account.login === \"string\" ? account.login : null,\n accountType: typeof account.type === \"string\" ? account.type : null,\n suspended: Boolean(payload.suspended_at),\n };\n}\n\nasync function githubGet(\n path: string,\n token: string,\n params: Record<string, string> = {},\n): Promise<any> {\n const url = new URL(`${githubApiBase}${path}`);\n for (const [key, value] of Object.entries(params)) {\n url.searchParams.set(key, value);\n }\n const response = await fetch(url, { headers: githubHeaders(token) });\n if (!response.ok) {\n throw new GitHubAppApiError(await githubErrorMessage(response), response.status);\n }\n return await response.json();\n}\n\nfunction repositoryFromPayload(\n payload: Record<string, unknown>,\n installationId: number,\n account: Record<string, unknown>,\n): GitHubRepository {\n const id = asInt(payload.id);\n const fullName = String(payload.full_name ?? \"\");\n if (id === null || !fullName) {\n throw new GitHubAppApiError(\"GitHub returned a repository without id/full_name\");\n }\n return {\n id,\n installationId,\n fullName,\n name: String(payload.name ?? fullName.split(\"/\").at(-1) ?? fullName),\n private: Boolean(payload.private),\n htmlUrl: String(payload.html_url ?? `https://github.com/${fullName}`),\n cloneUrl: String(payload.clone_url ?? `https://github.com/${fullName}.git`),\n defaultBranch: String(payload.default_branch ?? \"main\"),\n accountLogin: String(account.login ?? fullName.split(\"/\", 1)[0]),\n accountType: typeof account.type === \"string\" ? account.type : null,\n };\n}\n\nfunction repositoryPermissionsFromPayload(payload: unknown): GitHubRepositoryPermissions {\n const permissions =\n payload && typeof payload === \"object\" && !Array.isArray(payload)\n ? (payload as Record<string, unknown>)\n : {};\n return {\n admin: permissions.admin === true,\n maintain: permissions.maintain === true,\n push: permissions.push === true,\n triage: permissions.triage === true,\n pull: permissions.pull === true,\n };\n}\n\nfunction githubHeaders(token?: string): HeadersInit {\n return {\n Accept: \"application/vnd.github+json\",\n ...(token ? { Authorization: `Bearer ${token}` } : {}),\n \"X-GitHub-Api-Version\": githubApiVersion,\n };\n}\n\nasync function githubErrorMessage(response: Response): Promise<string> {\n try {\n const payload = await response.json();\n if (payload && typeof payload === \"object\" && \"message\" in payload) {\n return `GitHub API ${response.status}: ${String(payload.message)}`;\n }\n } catch {\n // fall through\n }\n return `GitHub API ${response.status}: ${await response.text()}`;\n}\n\nexport function normalizeGitHubAppPrivateKey(value: string): string {\n const privateKey = value.trim().replace(/\\\\n/g, \"\\n\");\n if (!privateKey || privateKey.startsWith(pkcs8PrivateKeyHeader)) {\n return privateKey;\n }\n if (privateKey.startsWith(rsaPrivateKeyHeader)) {\n return createPrivateKey(privateKey).export({ type: \"pkcs8\", format: \"pem\" }).toString();\n }\n return privateKey;\n}\n\nfunction signStatePayload(encoded: string, secret: string): string {\n return createHmac(\"sha256\", secret).update(encoded).digest(\"base64url\");\n}\n\nfunction safeEqual(left: string, right: string): boolean {\n const a = Buffer.from(left);\n const b = Buffer.from(right);\n return a.length === b.length && timingSafeEqual(a, b);\n}\n\nfunction asInt(value: unknown): number | null {\n if (typeof value === \"number\" && Number.isInteger(value)) {\n return value;\n }\n if (typeof value === \"string\" && /^\\d+$/.test(value)) {\n return Number(value);\n }\n return null;\n}\n"],"mappings":"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","names":["installation"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["import type { Settings } from \"@opengeni/config\";\nimport type {\n GitHubInstallationBindingCandidate,\n GitHubInstallationBindingProof,\n GitHubRepository,\n GitHubRepositoryPermissions,\n GitHubUserInstallationAccess,\n GitHubUserRepositoryAccess,\n} from \"@opengeni/contracts\";\nimport {\n createCipheriv,\n createDecipheriv,\n createHash,\n createHmac,\n createPrivateKey,\n randomBytes,\n timingSafeEqual,\n} from \"node:crypto\";\nimport { SignJWT, importPKCS8 } from \"jose\";\n\nconst githubApiBase = \"https://api.github.com\";\nconst githubApiVersion = \"2022-11-28\";\nconst githubTokenMintTimeoutMs = 60_000;\n/** Bound for the server-side repository-id lookup at turn start (mint + read). */\nexport const githubRepositoryLookupTimeoutMs = 10_000;\nexport const stateMaxAgeSeconds = 60 * 60;\nconst pkcs8PrivateKeyHeader = `-----BEGIN ${\"PRIVATE KEY\"}-----`;\nconst rsaPrivateKeyHeader = `-----BEGIN ${\"RSA PRIVATE KEY\"}-----`;\n\nconst PERSONAL_GITHUB_GIT_BROKER_TOKEN_PREFIX = \"oggh1\";\nconst PERSONAL_GITHUB_GIT_BROKER_KEY_CONTEXT = \"opengeni:personal-github:git-broker:v1\";\nexport const PERSONAL_GITHUB_GIT_BROKER_TOKEN_TTL_SECONDS = 5 * 60;\n\nexport type PersonalGitHubGitBrokerRepositoryClaim = {\n repositoryId: string;\n fullName: string;\n canonicalUrl: string;\n ref: string;\n access: \"read\" | \"write\";\n selectionGeneration: number;\n routeId: string;\n};\n\nexport type PersonalGitHubGitBrokerClaims = {\n version: 1;\n accountId: string;\n workspaceId: string;\n sessionId: string;\n rootSessionId: string;\n turnId: string;\n attemptId: string;\n executionGeneration: number;\n originWorkspaceId: string;\n connectionId: string;\n connectionAuthorityGeneration: number;\n ownerSubjectId: string;\n credentialBindingId: string;\n selectionGeneration: number;\n nonce: string;\n issuedAt: number;\n expiresAt: number;\n};\n\nexport function personalGitHubGitBrokerRouteId(\n secret: string,\n input: Omit<PersonalGitHubGitBrokerClaims, \"nonce\" | \"issuedAt\" | \"expiresAt\"> & {\n repository: Omit<PersonalGitHubGitBrokerRepositoryClaim, \"routeId\">;\n },\n): string {\n const hmac = createHmac(\"sha256\", personalGitHubGitBrokerKey(secret));\n for (const value of [\n String(input.version),\n input.accountId,\n input.workspaceId,\n input.sessionId,\n input.rootSessionId,\n input.turnId,\n input.attemptId,\n String(input.executionGeneration),\n input.originWorkspaceId,\n input.connectionId,\n String(input.connectionAuthorityGeneration),\n input.ownerSubjectId,\n input.credentialBindingId,\n String(input.selectionGeneration),\n input.repository.repositoryId,\n input.repository.fullName,\n input.repository.canonicalUrl,\n input.repository.ref,\n input.repository.access,\n String(input.repository.selectionGeneration),\n ]) {\n const bytes = Buffer.from(value, \"utf8\");\n hmac.update(Buffer.from(String(bytes.byteLength), \"ascii\"));\n hmac.update(\":\");\n hmac.update(bytes);\n hmac.update(\";\");\n }\n return hmac.digest(\"base64url\");\n}\n\n/**\n * Seal exact Git broker authority into a confidential, authenticated bearer.\n * The payload is encrypted rather than merely signed so tenant, session,\n * connection, and repository identities are not readable from the sandbox's\n * short-lived token file.\n */\nexport function sealPersonalGitHubGitBrokerClaims(\n secret: string,\n claims: PersonalGitHubGitBrokerClaims,\n): string {\n assertPersonalGitHubGitBrokerClaims(claims);\n const iv = randomBytes(12);\n const cipher = createCipheriv(\"aes-256-gcm\", personalGitHubGitBrokerKey(secret), iv);\n cipher.setAAD(Buffer.from(PERSONAL_GITHUB_GIT_BROKER_TOKEN_PREFIX, \"ascii\"));\n const ciphertext = Buffer.concat([cipher.update(JSON.stringify(claims), \"utf8\"), cipher.final()]);\n return [\n PERSONAL_GITHUB_GIT_BROKER_TOKEN_PREFIX,\n iv.toString(\"base64url\"),\n ciphertext.toString(\"base64url\"),\n cipher.getAuthTag().toString(\"base64url\"),\n ].join(\".\");\n}\n\nexport function openPersonalGitHubGitBrokerClaims(\n secret: string,\n token: string,\n nowSeconds = Math.floor(Date.now() / 1_000),\n): PersonalGitHubGitBrokerClaims | null {\n const [prefix, encodedIv, encodedCiphertext, encodedTag, extra] = token.split(\".\");\n if (\n prefix !== PERSONAL_GITHUB_GIT_BROKER_TOKEN_PREFIX ||\n !encodedIv ||\n !encodedCiphertext ||\n !encodedTag ||\n extra !== undefined\n ) {\n return null;\n }\n try {\n const iv = Buffer.from(encodedIv, \"base64url\");\n const ciphertext = Buffer.from(encodedCiphertext, \"base64url\");\n const tag = Buffer.from(encodedTag, \"base64url\");\n if (iv.byteLength !== 12 || tag.byteLength !== 16 || ciphertext.byteLength > 4_096) {\n return null;\n }\n const decipher = createDecipheriv(\"aes-256-gcm\", personalGitHubGitBrokerKey(secret), iv);\n decipher.setAAD(Buffer.from(PERSONAL_GITHUB_GIT_BROKER_TOKEN_PREFIX, \"ascii\"));\n decipher.setAuthTag(tag);\n const payload = JSON.parse(\n Buffer.concat([decipher.update(ciphertext), decipher.final()]).toString(\"utf8\"),\n ) as unknown;\n assertPersonalGitHubGitBrokerClaims(payload);\n if (payload.issuedAt > nowSeconds + 60 || nowSeconds >= payload.expiresAt) return null;\n if (payload.expiresAt - payload.issuedAt > PERSONAL_GITHUB_GIT_BROKER_TOKEN_TTL_SECONDS) {\n return null;\n }\n return payload;\n } catch {\n return null;\n }\n}\n\nfunction personalGitHubGitBrokerKey(secret: string): Buffer {\n const normalized = secret.trim();\n if (!normalized) throw new Error(\"personal GitHub Git broker signing secret is unavailable\");\n return createHash(\"sha256\")\n .update(PERSONAL_GITHUB_GIT_BROKER_KEY_CONTEXT, \"utf8\")\n .update(\"\\0\", \"utf8\")\n .update(normalized, \"utf8\")\n .digest();\n}\n\nfunction assertPersonalGitHubGitBrokerClaims(\n value: unknown,\n): asserts value is PersonalGitHubGitBrokerClaims {\n if (!value || typeof value !== \"object\" || Array.isArray(value)) {\n throw new Error(\"invalid personal GitHub Git broker claims\");\n }\n const claims = value as Record<string, unknown>;\n const expectedKeys = new Set([\n \"version\",\n \"accountId\",\n \"workspaceId\",\n \"sessionId\",\n \"rootSessionId\",\n \"turnId\",\n \"attemptId\",\n \"executionGeneration\",\n \"originWorkspaceId\",\n \"connectionId\",\n \"connectionAuthorityGeneration\",\n \"ownerSubjectId\",\n \"credentialBindingId\",\n \"selectionGeneration\",\n \"nonce\",\n \"issuedAt\",\n \"expiresAt\",\n ]);\n const strings = [\n \"accountId\",\n \"workspaceId\",\n \"sessionId\",\n \"rootSessionId\",\n \"turnId\",\n \"attemptId\",\n \"originWorkspaceId\",\n \"connectionId\",\n \"ownerSubjectId\",\n \"credentialBindingId\",\n \"nonce\",\n ];\n if (\n claims.version !== 1 ||\n strings.some(\n (field) =>\n typeof claims[field] !== \"string\" ||\n claims[field].length === 0 ||\n claims[field].length > (field === \"ownerSubjectId\" ? 512 : 128),\n ) ||\n !positiveIntegerClaim(claims.executionGeneration) ||\n !positiveIntegerClaim(claims.connectionAuthorityGeneration) ||\n !positiveIntegerClaim(claims.selectionGeneration) ||\n !positiveIntegerClaim(claims.issuedAt) ||\n !positiveIntegerClaim(claims.expiresAt) ||\n Object.keys(claims).some((key) => !expectedKeys.has(key))\n ) {\n throw new Error(\"invalid personal GitHub Git broker claims\");\n }\n}\n\nfunction positiveIntegerClaim(value: unknown): value is number {\n return typeof value === \"number\" && Number.isSafeInteger(value) && value > 0;\n}\n\nexport class GitHubAppConfigurationError extends Error {\n constructor(readonly missing: string[]) {\n super(\"GitHub App is not configured\");\n }\n}\n\nexport class GitHubAppApiError extends Error {\n constructor(\n message: string,\n readonly status: number | null = null,\n ) {\n super(message);\n }\n}\n\nexport type GitHubInstallationAuthorityFailure =\n | \"authority_denied\"\n | \"authority_unavailable\"\n | \"installation_missing\"\n | \"installation_suspended\"\n | \"repository_access_empty\";\n\nexport class GitHubInstallationAuthorityError extends GitHubAppApiError {\n constructor(\n readonly reason: GitHubInstallationAuthorityFailure,\n message: string,\n status: number | null = null,\n ) {\n super(message, status);\n }\n}\n\nexport type GitHubAppInstallationSummary = {\n installationId: number;\n accountId: number;\n accountLogin: string | null;\n accountType: string | null;\n suspended: boolean;\n};\n\nexport type GitHubSignedStatePayload = {\n nonce: string;\n iat: number;\n accountId?: string;\n workspaceId?: string;\n [key: string]: unknown;\n};\n\nexport function githubAppMissingSettings(settings: Settings): string[] {\n const required: Record<string, string | undefined> = {\n OPENGENI_GITHUB_APP_ID: settings.githubAppId,\n OPENGENI_GITHUB_CLIENT_ID: settings.githubClientId,\n OPENGENI_GITHUB_CLIENT_SECRET: settings.githubClientSecret,\n OPENGENI_GITHUB_APP_SLUG: settings.githubAppSlug,\n OPENGENI_GITHUB_APP_PRIVATE_KEY: settings.githubAppPrivateKey,\n };\n return Object.entries(required).flatMap(([name, value]) => (value && value.trim() ? [] : [name]));\n}\n\nexport function prReviewGitHubAppMissingSettings(settings: Settings): string[] {\n const required: Record<string, string | undefined> = {\n OPENGENI_GITHUB_APP_MANIFEST_STATE_SECRET: settings.githubAppManifestStateSecret,\n OPENGENI_ENVIRONMENTS_ENCRYPTION_KEY: settings.environmentsEncryptionKey,\n OPENGENI_PR_REVIEW_GITHUB_APP_ID: settings.prReviewGithubAppId,\n OPENGENI_PR_REVIEW_GITHUB_CLIENT_ID: settings.prReviewGithubClientId,\n OPENGENI_PR_REVIEW_GITHUB_CLIENT_SECRET: settings.prReviewGithubClientSecret,\n OPENGENI_PR_REVIEW_GITHUB_APP_SLUG: settings.prReviewGithubAppSlug,\n OPENGENI_PR_REVIEW_GITHUB_WEBHOOK_SECRET: settings.prReviewGithubWebhookSecret,\n OPENGENI_PR_REVIEW_GITHUB_APP_PRIVATE_KEY: settings.prReviewGithubAppPrivateKey,\n };\n return Object.entries(required).flatMap(([name, value]) => (value && value.trim() ? [] : [name]));\n}\n\n/** Project the separately configured review App onto the ordinary GitHub App\n * authority client. This keeps its OAuth, signing, webhook, and installation\n * identity disjoint from the platform GitHub App while reusing the same\n * personal-owner / organization-owner proof implementation. */\nexport function settingsForPrReviewGitHubApp(settings: Settings): Settings {\n return {\n ...settings,\n githubAppId: settings.prReviewGithubAppId,\n githubClientId: settings.prReviewGithubClientId,\n githubClientSecret: settings.prReviewGithubClientSecret,\n githubAppSlug: settings.prReviewGithubAppSlug,\n githubWebhookSecret: settings.prReviewGithubWebhookSecret,\n githubAppPrivateKey: settings.prReviewGithubAppPrivateKey,\n };\n}\n\nexport type GitHubAppSigningSettings = Pick<Settings, \"githubAppId\" | \"githubAppPrivateKey\">;\n\nfunction githubAppTokenMissingSettings(settings: GitHubAppSigningSettings): string[] {\n const required: Record<string, string | undefined> = {\n OPENGENI_GITHUB_APP_ID: settings.githubAppId,\n OPENGENI_GITHUB_APP_PRIVATE_KEY: settings.githubAppPrivateKey,\n };\n return Object.entries(required).flatMap(([name, value]) => (value && value.trim() ? [] : [name]));\n}\n\nexport function buildGitHubAppManifest(input: {\n appName: string;\n baseUrl: string;\n public: boolean;\n includeCiPermissions: boolean;\n setupUrl?: string;\n}): Record<string, unknown> {\n const base = input.baseUrl.replace(/\\/+$/, \"\");\n const permissions: Record<string, string> = {\n metadata: \"read\",\n contents: \"write\",\n pull_requests: \"write\",\n // Required for the authenticated-user membership endpoint that proves an\n // active organization owner. Existing installations must approve this\n // permission before organization-owner self-service can succeed.\n members: \"read\",\n };\n if (input.includeCiPermissions) {\n permissions.actions = \"read\";\n permissions.checks = \"read\";\n permissions.statuses = \"write\";\n }\n const manifest: Record<string, unknown> = {\n name: input.appName,\n url: base,\n redirect_url: `${base}/v1/github/app-manifest/callback`,\n callback_urls: [`${base}/v1/github/oauth/callback`],\n public: input.public,\n // A setup URL and OAuth-on-install are mutually exclusive in GitHub's App\n // contract. OpenGeni needs the setup callback to receive the installation\n // id, then starts its own exact user-authorization flow.\n request_oauth_on_install: !input.setupUrl,\n default_permissions: permissions,\n };\n if (input.setupUrl) {\n manifest.setup_url = input.setupUrl;\n manifest.setup_on_update = true;\n }\n return manifest;\n}\n\nexport function personalAppManifestUrl(state: string): string {\n return `https://github.com/settings/apps/new?state=${state}`;\n}\n\nexport function organizationAppManifestUrl(organization: string, state: string): string {\n return `https://github.com/organizations/${encodeURIComponent(organization)}/settings/apps/new?state=${state}`;\n}\n\nexport function githubOAuthAuthorizeUrl(input: {\n clientId: string;\n state: string;\n redirectUri?: string;\n}): string {\n const url = new URL(\"https://github.com/login/oauth/authorize\");\n url.searchParams.set(\"client_id\", input.clientId);\n url.searchParams.set(\"state\", input.state);\n if (input.redirectUri) {\n url.searchParams.set(\"redirect_uri\", input.redirectUri);\n }\n return url.toString();\n}\n\nexport function createSignedState(\n secret: string,\n payloadOrNow: Record<string, unknown> | number = {},\n nowArg = Math.floor(Date.now() / 1000),\n): string {\n const payloadInput = typeof payloadOrNow === \"number\" ? {} : payloadOrNow;\n const now = typeof payloadOrNow === \"number\" ? payloadOrNow : nowArg;\n const payload = {\n ...payloadInput,\n nonce: randomBytes(16).toString(\"base64url\"),\n iat: now,\n };\n const encoded = Buffer.from(JSON.stringify(payload)).toString(\"base64url\");\n return `${encoded}.${signStatePayload(encoded, secret)}`;\n}\n\nexport function readSignedState(\n state: string,\n secret: string,\n now = Math.floor(Date.now() / 1000),\n): GitHubSignedStatePayload | null {\n const [encoded, signature] = state.split(\".\", 2);\n if (!encoded || !signature) {\n return null;\n }\n const expected = signStatePayload(encoded, secret);\n if (!safeEqual(signature, expected)) {\n return null;\n }\n let payload: unknown;\n try {\n payload = JSON.parse(Buffer.from(encoded, \"base64url\").toString(\"utf8\"));\n } catch {\n return null;\n }\n if (\n !payload ||\n typeof payload !== \"object\" ||\n typeof (payload as { iat?: unknown }).iat !== \"number\" ||\n typeof (payload as { nonce?: unknown }).nonce !== \"string\"\n ) {\n return null;\n }\n const age = now - (payload as { iat: number }).iat;\n return age >= 0 && age <= stateMaxAgeSeconds ? (payload as GitHubSignedStatePayload) : null;\n}\n\nexport function verifySignedState(\n state: string,\n secret: string,\n now = Math.floor(Date.now() / 1000),\n): boolean {\n return readSignedState(state, secret, now) !== null;\n}\n\nexport function envLinesFromGitHubManifestConversion(payload: Record<string, unknown>): string[] {\n const privateKey = String(payload.pem ?? \"\").replace(/\\n/g, \"\\\\n\");\n return [\n `OPENGENI_GITHUB_APP_ID=${payload.id ?? \"\"}`,\n `OPENGENI_GITHUB_CLIENT_ID=${payload.client_id ?? \"\"}`,\n `OPENGENI_GITHUB_CLIENT_SECRET=${payload.client_secret ?? \"\"}`,\n `OPENGENI_GITHUB_APP_SLUG=${payload.slug ?? \"\"}`,\n `OPENGENI_GITHUB_WEBHOOK_SECRET=${payload.webhook_secret ?? \"\"}`,\n `OPENGENI_GITHUB_APP_PRIVATE_KEY=\"${privateKey}\"`,\n ];\n}\n\nexport async function convertGitHubAppManifest(code: string): Promise<Record<string, unknown>> {\n const response = await fetch(`${githubApiBase}/app-manifests/${code}/conversions`, {\n method: \"POST\",\n headers: githubHeaders(undefined),\n });\n if (!response.ok) {\n throw new GitHubAppApiError(await githubErrorMessage(response));\n }\n const payload = await response.json();\n if (!payload || typeof payload !== \"object\" || Array.isArray(payload)) {\n throw new GitHubAppApiError(\"GitHub returned an invalid manifest conversion payload\");\n }\n return payload as Record<string, unknown>;\n}\n\nexport async function listGitHubAppInstallationSummaries(\n settings: Settings,\n): Promise<GitHubAppInstallationSummary[]> {\n const missing = githubAppMissingSettings(settings);\n if (missing.length > 0) {\n throw new GitHubAppConfigurationError(missing);\n }\n const jwt = await createGitHubAppJwt(settings);\n const installations = await listInstallations(jwt);\n return installations.map(installationSummaryFromPayload);\n}\n\nexport async function getGitHubAppInstallationSummary(\n settings: Settings,\n installationId: number,\n): Promise<GitHubAppInstallationSummary | null> {\n const installations = await listGitHubAppInstallationSummaries(settings);\n return (\n installations.find((installation) => installation.installationId === installationId) ?? null\n );\n}\n\nexport async function verifyGitHubInstallationAccessForUser(\n settings: Settings,\n input: {\n code: string;\n installationId: number;\n },\n): Promise<GitHubAppInstallationSummary> {\n const token = await exchangeGitHubOAuthCodeForUserToken(settings, input.code);\n const installations = await listUserAccessibleInstallations(token);\n const installation = installations.find(\n (candidate) => candidate.installationId === input.installationId,\n );\n if (!installation) {\n throw new GitHubAppApiError(\"GitHub installation is not accessible to the installing user\");\n }\n return installation;\n}\n\n/**\n * Exchange a GitHub App user-authorization code and discover the installations\n * and repositories the user can explicitly access. This is compatibility\n * discovery metadata only: visibility and repository permission bits do not\n * prove that the human may install, configure, or bind the App installation.\n * No production binding path may treat this result as authority.\n */\nexport async function authorizeGitHubAppUser(\n settings: Settings,\n input: { code: string },\n): Promise<GitHubUserInstallationAccess[]> {\n const token = await exchangeGitHubOAuthCodeForUserToken(settings, input.code);\n const installations = await listUserAccessibleInstallations(token);\n return await Promise.all(\n installations.map(async (installation) => ({\n ...installation,\n repositories: installation.suspended\n ? []\n : await listUserInstallationRepositories(token, installation),\n })),\n );\n}\n\n/**\n * Prove current GitHub installation authority without treating repository\n * administration or installation visibility as delegation authority.\n *\n * GitHub exposes an exact personal-account owner through the authenticated\n * user's immutable id. For organizations, GitHub's authenticated membership\n * endpoint exposes active owners as role=admin. GitHub does not expose an\n * equivalent current-authority receipt for App Managers, so that case remains\n * unsupported and fails closed.\n */\nexport async function authorizeGitHubInstallationBinding(\n settings: Settings,\n input: { code: string; installationId: number },\n): Promise<GitHubInstallationBindingProof> {\n const userToken = await exchangeGitHubOAuthCodeForUserToken(settings, input.code);\n const actor = await getAuthenticatedGitHubUser(userToken);\n const visibleInstallations = await listUserAccessibleInstallations(userToken);\n const visible = visibleInstallations.find(\n (installation) => installation.installationId === input.installationId,\n );\n if (!visible) {\n throw new GitHubInstallationAuthorityError(\n \"authority_denied\",\n \"GitHub did not associate this installation with the authorized user\",\n );\n }\n\n const jwt = await createGitHubAppJwt(settings);\n const livePayload = (await listInstallations(jwt)).find(\n (installation) => asInt(installation.id) === input.installationId,\n );\n if (!livePayload) {\n throw new GitHubInstallationAuthorityError(\n \"installation_missing\",\n \"GitHub App installation was deleted or is not owned by this App\",\n );\n }\n const installation = installationSummaryFromPayload(livePayload);\n if (\n installation.accountId !== visible.accountId ||\n installation.accountLogin !== visible.accountLogin ||\n installation.accountType !== visible.accountType\n ) {\n throw new GitHubInstallationAuthorityError(\n \"authority_denied\",\n \"GitHub installation identity changed during authorization\",\n );\n }\n if (installation.suspended) {\n throw new GitHubInstallationAuthorityError(\n \"installation_suspended\",\n \"GitHub App installation is suspended\",\n );\n }\n\n let authorityKind: GitHubInstallationBindingProof[\"authorityKind\"];\n if (installation.accountType === \"User\" && actor.id === installation.accountId) {\n authorityKind = \"personal_owner\";\n } else if (installation.accountType === \"Organization\" && installation.accountLogin) {\n await assertActiveOrganizationOwner(\n userToken,\n installation.accountId,\n installation.accountLogin,\n );\n authorityKind = \"organization_owner\";\n } else {\n throw new GitHubInstallationAuthorityError(\n \"authority_denied\",\n \"Only a GitHub personal-account owner or organization owner may bind an installation\",\n );\n }\n\n const installationToken = await createInstallationToken(jwt, {\n installationId: installation.installationId,\n });\n const repositories = await listInstallationRepositories(\n installationToken.token,\n installation.installationId,\n { login: installation.accountLogin, type: installation.accountType },\n );\n if (repositories.length === 0) {\n throw new GitHubInstallationAuthorityError(\n \"repository_access_empty\",\n \"GitHub App installation does not currently grant access to any repositories\",\n );\n }\n if (authorityKind === \"organization_owner\") {\n // Repository enumeration is an async provider boundary. Re-read the live\n // owner tuple after it so a role revoked after the chooser proof cannot be\n // durably bound with a later, misleading authority timestamp.\n await assertActiveOrganizationOwner(\n userToken,\n installation.accountId,\n installation.accountLogin!,\n );\n }\n return {\n actorId: actor.id,\n actorLogin: actor.login,\n authorityKind,\n installation,\n repositories,\n };\n}\n\n/**\n * Discover existing installations that the freshly authorized GitHub human\n * can bind as an exact personal owner or active organization owner.\n *\n * `GET /user/installations` is discovery input only. Every candidate is\n * cross-checked against the App's live installation inventory and an\n * organization candidate requires a live `state=active, role=admin`\n * membership proof. The later exact authorization still re-runs the complete\n * proof immediately before the durable bind.\n */\nexport async function discoverGitHubInstallationBindingCandidates(\n settings: Settings,\n input: { code: string },\n): Promise<GitHubInstallationBindingCandidate[]> {\n const userToken = await exchangeGitHubOAuthCodeForUserToken(settings, input.code);\n const actor = await getAuthenticatedGitHubUser(userToken);\n const visibleInstallations = await listUserAccessibleInstallations(userToken);\n const jwt = await createGitHubAppJwt(settings);\n const liveInstallations = new Map(\n (await listInstallations(jwt)).map((payload) => {\n const installation = installationSummaryFromPayload(payload);\n return [installation.installationId, installation] as const;\n }),\n );\n const candidates: GitHubInstallationBindingCandidate[] = [];\n\n for (const visible of visibleInstallations) {\n const installation = liveInstallations.get(visible.installationId);\n if (\n !installation ||\n installation.suspended ||\n installation.accountId !== visible.accountId ||\n installation.accountLogin !== visible.accountLogin ||\n installation.accountType !== visible.accountType\n ) {\n continue;\n }\n if (installation.accountType === \"User\" && actor.id === installation.accountId) {\n candidates.push({ installation, authorityKind: \"personal_owner\" });\n continue;\n }\n if (installation.accountType !== \"Organization\" || !installation.accountLogin) {\n continue;\n }\n try {\n await assertActiveOrganizationOwner(\n userToken,\n installation.accountId,\n installation.accountLogin,\n );\n candidates.push({ installation, authorityKind: \"organization_owner\" });\n } catch (error) {\n if (\n error instanceof GitHubInstallationAuthorityError &&\n error.reason === \"authority_unavailable\"\n ) {\n continue;\n }\n if (\n error instanceof GitHubInstallationAuthorityError &&\n error.reason === \"authority_denied\"\n ) {\n continue;\n }\n throw error;\n }\n }\n\n return candidates.sort((left, right) =>\n (left.installation.accountLogin ?? \"\").localeCompare(right.installation.accountLogin ?? \"\"),\n );\n}\n\nexport async function listGitHubAppRepositories(\n settings: Settings,\n input: {\n installationIds?: number[];\n } = {},\n): Promise<GitHubRepository[]> {\n const missing = githubAppMissingSettings(settings);\n if (missing.length > 0) {\n throw new GitHubAppConfigurationError(missing);\n }\n return await listGitHubAppRepositoriesWithSigningSettings(settings, input);\n}\n\n/** List repositories for a separately registered App that needs only signing credentials. */\nexport async function listGitHubAppRepositoriesWithSigningSettings(\n settings: GitHubAppSigningSettings,\n input: {\n installationIds?: number[];\n } = {},\n): Promise<GitHubRepository[]> {\n const missing = githubAppTokenMissingSettings(settings);\n if (missing.length > 0) {\n throw new GitHubAppConfigurationError(missing);\n }\n const allowedInstallations = input.installationIds ? new Set(input.installationIds) : null;\n if (allowedInstallations && allowedInstallations.size === 0) {\n return [];\n }\n const jwt = await createGitHubAppJwt(settings);\n const installations = await listInstallations(jwt);\n const repositories: GitHubRepository[] = [];\n for (const installation of installations) {\n if (installation.suspended_at) {\n continue;\n }\n const installationId = asInt(installation.id);\n if (installationId === null) {\n continue;\n }\n if (allowedInstallations && !allowedInstallations.has(installationId)) {\n continue;\n }\n const account =\n typeof installation.account === \"object\" && installation.account\n ? (installation.account as Record<string, unknown>)\n : {};\n const token = await createInstallationToken(jwt, { installationId });\n repositories.push(\n ...(await listInstallationRepositories(token.token, installationId, account)),\n );\n }\n repositories.sort((left, right) => left.fullName.localeCompare(right.fullName));\n return repositories;\n}\n\nexport type GitHubAppInstallationRepositoryLookupInput = {\n installationId: number;\n owner: string;\n name: string;\n};\n\nexport type GitHubAppInstallationRepositoryLookup = (\n input: GitHubAppInstallationRepositoryLookupInput,\n) => Promise<GitHubRepository | null>;\n\n/**\n * Resolve one `owner/name` repository through an exact App installation and\n * return GitHub's stable repository identity, or null when that installation\n * cannot see the repository. The server-side lookup token never leaves the\n * caller and grants nothing by itself: the workspace allowlist decides whether\n * the returned id may mint a sandbox-bound token.\n */\nexport async function getGitHubAppInstallationRepository(\n settings: Settings,\n input: GitHubAppInstallationRepositoryLookupInput,\n): Promise<GitHubRepository | null> {\n return await createGitHubAppInstallationRepositoryLookup(settings)(input);\n}\n\n/**\n * One lookup client that reuses a server-side installation token per\n * installation for its lifetime (one worker turn), so several bare repository\n * URIs from the same installation cost one mint plus one read each.\n */\nexport function createGitHubAppInstallationRepositoryLookup(\n settings: Settings,\n): GitHubAppInstallationRepositoryLookup {\n const missing = githubAppMissingSettings(settings);\n if (missing.length > 0) {\n throw new GitHubAppConfigurationError(missing);\n }\n const tokens = new Map<number, Promise<GitHubAppInstallationToken>>();\n const installationToken = (installationId: number): Promise<GitHubAppInstallationToken> => {\n let pending = tokens.get(installationId);\n if (!pending) {\n // Metadata-read only: the lookup needs the repository id, never contents.\n // Bounded well below the sandbox mint timeout so a slow GitHub cannot\n // hold turn start; the caller proceeds bare on expiry.\n pending = createGitHubAppJwt(settings).then((jwt) =>\n createInstallationToken(jwt, {\n installationId,\n permissions: { metadata: \"read\" },\n timeoutMs: githubRepositoryLookupTimeoutMs,\n }),\n );\n pending.catch(() => tokens.delete(installationId));\n tokens.set(installationId, pending);\n }\n return pending;\n };\n return async (input) => {\n const owner = input.owner.trim();\n const name = input.name.trim();\n if (\n !/^[A-Za-z0-9](?:[A-Za-z0-9-]*[A-Za-z0-9])?$/u.test(owner) ||\n !/^[A-Za-z0-9._-]+$/u.test(name)\n ) {\n return null;\n }\n const token = await installationToken(input.installationId);\n const response = await fetch(\n `${githubApiBase}/repos/${encodeURIComponent(owner)}/${encodeURIComponent(name)}`,\n {\n headers: githubHeaders(token.token),\n signal: AbortSignal.timeout(githubRepositoryLookupTimeoutMs),\n },\n );\n if (response.status === 404) {\n return null;\n }\n if (!response.ok) {\n throw new GitHubAppApiError(await githubErrorMessage(response), response.status);\n }\n const payload = await response.json();\n if (!payload || typeof payload !== \"object\" || Array.isArray(payload)) {\n throw new GitHubAppApiError(\"GitHub returned an invalid repository payload\");\n }\n const record = payload as Record<string, unknown>;\n const account =\n record.owner && typeof record.owner === \"object\" && !Array.isArray(record.owner)\n ? (record.owner as Record<string, unknown>)\n : {};\n return repositoryFromPayload(record, input.installationId, account);\n };\n}\n\nexport async function createGitHubAppInstallationToken(\n settings: Settings,\n input: {\n installationId: number;\n repositoryIds: number[];\n },\n): Promise<string> {\n return (await createGitHubAppInstallationTokenWithExpiry(settings, input)).token;\n}\n\nexport type GitHubAppInstallationToken = {\n token: string;\n expiresAt: string | null;\n};\n\nexport async function createGitHubAppInstallationTokenWithExpiry(\n settings: Settings,\n input: {\n installationId: number;\n repositoryIds: number[];\n },\n): Promise<GitHubAppInstallationToken> {\n const missing = githubAppMissingSettings(settings);\n if (missing.length > 0) {\n throw new GitHubAppConfigurationError(missing);\n }\n return await createGitHubAppInstallationTokenWithSigningSettings(settings, input);\n}\n\n/** Mint for a separately registered App without requiring unrelated OAuth settings. */\nexport async function createGitHubAppInstallationTokenWithSigningSettings(\n settings: GitHubAppSigningSettings,\n input: {\n installationId: number;\n repositoryIds: number[];\n permissions?: Record<string, \"read\" | \"write\">;\n },\n): Promise<GitHubAppInstallationToken> {\n const missing = githubAppTokenMissingSettings(settings);\n if (missing.length > 0) {\n throw new GitHubAppConfigurationError(missing);\n }\n if (!Array.isArray(input.repositoryIds)) {\n throw new GitHubAppApiError(\n \"GitHub installation token mint requires an explicit, unique repository allowlist\",\n );\n }\n const repositoryIds = [...new Set(input.repositoryIds)];\n if (\n !Number.isSafeInteger(input.installationId) ||\n input.installationId <= 0 ||\n repositoryIds.length === 0 ||\n repositoryIds.length !== input.repositoryIds.length ||\n repositoryIds.some((id) => !Number.isSafeInteger(id) || id <= 0)\n ) {\n throw new GitHubAppApiError(\n \"GitHub installation token mint requires an explicit, unique repository allowlist\",\n );\n }\n const jwt = await createGitHubAppJwt(settings);\n return await createInstallationToken(jwt, {\n installationId: input.installationId,\n repositoryIds,\n ...(input.permissions ? { permissions: input.permissions } : {}),\n });\n}\n\nexport function githubAppBotIdentity(settings: Settings): { name: string; email: string } | null {\n const appId = settings.githubAppId?.trim();\n const slug = settings.githubAppSlug?.trim();\n if (!appId || !slug) {\n return null;\n }\n const login = `${slug}[bot]`;\n return {\n name: login,\n email: `${appId}+${login}@users.noreply.github.com`,\n };\n}\n\nasync function createGitHubAppJwt(settings: GitHubAppSigningSettings): Promise<string> {\n const privateKey = normalizeGitHubAppPrivateKey(settings.githubAppPrivateKey ?? \"\");\n const appId = settings.githubAppId?.trim();\n if (!appId || !privateKey) {\n throw new GitHubAppConfigurationError(githubAppTokenMissingSettings(settings));\n }\n const key = await importPKCS8(privateKey, \"RS256\");\n const now = Math.floor(Date.now() / 1000);\n return await new SignJWT({})\n .setProtectedHeader({ alg: \"RS256\" })\n .setIssuedAt(now - 60)\n .setExpirationTime(now + 9 * 60)\n .setIssuer(appId)\n .sign(key);\n}\n\nasync function listInstallations(token: string): Promise<Array<Record<string, unknown>>> {\n const out: Array<Record<string, unknown>> = [];\n for (let page = 1; ; page += 1) {\n const payload = await githubGet(\"/app/installations\", token, {\n per_page: \"100\",\n page: String(page),\n });\n if (!Array.isArray(payload)) {\n throw new GitHubAppApiError(\"GitHub returned an invalid installations payload\");\n }\n out.push(\n ...payload.filter((item): item is Record<string, unknown> =>\n Boolean(item && typeof item === \"object\" && !Array.isArray(item)),\n ),\n );\n if (payload.length < 100) {\n return out;\n }\n }\n}\n\nasync function exchangeGitHubOAuthCodeForUserToken(\n settings: Settings,\n code: string,\n): Promise<string> {\n if (!settings.githubClientId || !settings.githubClientSecret) {\n throw new GitHubAppConfigurationError(githubAppMissingSettings(settings));\n }\n const response = await fetch(\"https://github.com/login/oauth/access_token\", {\n method: \"POST\",\n headers: {\n Accept: \"application/json\",\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify({\n client_id: settings.githubClientId,\n client_secret: settings.githubClientSecret,\n code,\n }),\n });\n if (!response.ok) {\n throw new GitHubAppApiError(await githubErrorMessage(response));\n }\n const payload = await response.json();\n if (!payload || typeof payload !== \"object\" || typeof payload.access_token !== \"string\") {\n throw new GitHubAppApiError(\"GitHub returned an invalid OAuth token payload\");\n }\n return payload.access_token;\n}\n\nasync function getAuthenticatedGitHubUser(token: string): Promise<{ id: number; login: string }> {\n const payload = await githubGet(\"/user\", token);\n const id = payload && typeof payload === \"object\" ? asInt(payload.id) : null;\n const login =\n payload && typeof payload === \"object\" && typeof payload.login === \"string\"\n ? payload.login\n : null;\n if (id === null || !login) {\n throw new GitHubAppApiError(\"GitHub returned an invalid authenticated user payload\");\n }\n return { id, login };\n}\n\nasync function getAuthenticatedOrganizationMembership(\n token: string,\n organizationLogin: string,\n): Promise<{ organizationId: number; role: string; state: string }> {\n let payload: any;\n try {\n payload = await githubGet(\n `/user/memberships/orgs/${encodeURIComponent(organizationLogin)}`,\n token,\n );\n } catch (error) {\n if (error instanceof GitHubAppApiError) {\n throw new GitHubInstallationAuthorityError(\n \"authority_unavailable\",\n \"GitHub could not prove current organization-owner membership\",\n error.status,\n );\n }\n throw error;\n }\n const organization =\n payload && typeof payload === \"object\" && payload.organization ? payload.organization : null;\n const organizationId =\n organization && typeof organization === \"object\" ? asInt(organization.id) : null;\n if (\n organizationId === null ||\n typeof payload?.role !== \"string\" ||\n typeof payload?.state !== \"string\"\n ) {\n throw new GitHubInstallationAuthorityError(\n \"authority_unavailable\",\n \"GitHub returned an invalid organization membership proof\",\n );\n }\n return { organizationId, role: payload.role, state: payload.state };\n}\n\nasync function assertActiveOrganizationOwner(\n token: string,\n organizationId: number,\n organizationLogin: string,\n): Promise<void> {\n const membership = await getAuthenticatedOrganizationMembership(token, organizationLogin);\n if (\n membership.organizationId !== organizationId ||\n membership.state !== \"active\" ||\n membership.role !== \"admin\"\n ) {\n throw new GitHubInstallationAuthorityError(\n \"authority_denied\",\n \"Only an active GitHub organization owner may bind this installation\",\n );\n }\n}\n\nasync function listUserAccessibleInstallations(\n token: string,\n): Promise<GitHubAppInstallationSummary[]> {\n const out: GitHubAppInstallationSummary[] = [];\n for (let page = 1; ; page += 1) {\n const payload = await githubGet(\"/user/installations\", token, {\n per_page: \"100\",\n page: String(page),\n });\n const installations: unknown[] | null =\n payload && typeof payload === \"object\" && Array.isArray(payload.installations)\n ? (payload.installations as unknown[])\n : null;\n if (!installations) {\n throw new GitHubAppApiError(\"GitHub returned an invalid user installations payload\");\n }\n out.push(\n ...installations\n .filter((item): item is Record<string, unknown> =>\n Boolean(item && typeof item === \"object\" && !Array.isArray(item)),\n )\n .map(installationSummaryFromPayload),\n );\n if (installations.length < 100) {\n return out;\n }\n }\n}\n\nasync function listUserInstallationRepositories(\n token: string,\n installation: GitHubAppInstallationSummary,\n): Promise<GitHubUserRepositoryAccess[]> {\n const out: GitHubUserRepositoryAccess[] = [];\n const account = {\n ...(installation.accountLogin ? { login: installation.accountLogin } : {}),\n ...(installation.accountType ? { type: installation.accountType } : {}),\n };\n for (let page = 1; ; page += 1) {\n const payload = await githubGet(\n `/user/installations/${installation.installationId}/repositories`,\n token,\n { per_page: \"100\", page: String(page) },\n );\n if (\n !payload ||\n typeof payload !== \"object\" ||\n Array.isArray(payload) ||\n !Array.isArray(payload.repositories)\n ) {\n throw new GitHubAppApiError(\n \"GitHub returned an invalid user installation repositories payload\",\n );\n }\n for (const repository of payload.repositories) {\n if (!repository || typeof repository !== \"object\" || Array.isArray(repository)) {\n continue;\n }\n const record = repository as Record<string, unknown>;\n out.push({\n ...repositoryFromPayload(record, installation.installationId, account),\n permissions: repositoryPermissionsFromPayload(record.permissions),\n });\n }\n if (payload.repositories.length < 100) {\n return out;\n }\n }\n}\n\nasync function createInstallationToken(\n appJwt: string,\n input: {\n installationId: number;\n repositoryIds?: number[];\n /** Narrow the token below the installation's granted permissions. */\n permissions?: Record<string, \"read\" | \"write\">;\n timeoutMs?: number;\n },\n): Promise<GitHubAppInstallationToken> {\n const body: Record<string, unknown> = {};\n if (input.repositoryIds && input.repositoryIds.length > 0) {\n body.repository_ids = input.repositoryIds;\n }\n if (input.permissions && Object.keys(input.permissions).length > 0) {\n body.permissions = input.permissions;\n }\n const scoped = Object.keys(body).length > 0;\n const response = await fetch(\n `${githubApiBase}/app/installations/${input.installationId}/access_tokens`,\n {\n method: \"POST\",\n headers: {\n ...githubHeaders(appJwt),\n ...(scoped ? { \"Content-Type\": \"application/json\" } : {}),\n },\n signal: AbortSignal.timeout(input.timeoutMs ?? githubTokenMintTimeoutMs),\n ...(scoped ? { body: JSON.stringify(body) } : {}),\n },\n );\n if (!response.ok) {\n throw new GitHubAppApiError(await githubErrorMessage(response));\n }\n const payload = await response.json();\n if (!payload || typeof payload !== \"object\" || typeof payload.token !== \"string\") {\n throw new GitHubAppApiError(\"GitHub returned an invalid installation token payload\");\n }\n return {\n token: payload.token,\n expiresAt: typeof payload.expires_at === \"string\" ? payload.expires_at : null,\n };\n}\n\nasync function listInstallationRepositories(\n token: string,\n installationId: number,\n account: Record<string, unknown>,\n): Promise<GitHubRepository[]> {\n const out: GitHubRepository[] = [];\n for (let page = 1; ; page += 1) {\n const payload = await githubGet(\"/installation/repositories\", token, {\n per_page: \"100\",\n page: String(page),\n });\n if (\n !payload ||\n typeof payload !== \"object\" ||\n Array.isArray(payload) ||\n !Array.isArray(payload.repositories)\n ) {\n throw new GitHubAppApiError(\"GitHub returned an invalid repositories payload\");\n }\n for (const repo of payload.repositories) {\n if (repo && typeof repo === \"object\" && !Array.isArray(repo)) {\n out.push(repositoryFromPayload(repo as Record<string, unknown>, installationId, account));\n }\n }\n if (payload.repositories.length < 100) {\n return out;\n }\n }\n}\n\nfunction installationSummaryFromPayload(\n payload: Record<string, unknown>,\n): GitHubAppInstallationSummary {\n const installationId = asInt(payload.id);\n if (installationId === null) {\n throw new GitHubAppApiError(\"GitHub returned an installation without id\");\n }\n const account =\n typeof payload.account === \"object\" && payload.account\n ? (payload.account as Record<string, unknown>)\n : {};\n const accountId = asInt(account.id);\n if (accountId === null) {\n throw new GitHubAppApiError(\"GitHub returned an installation without an account id\");\n }\n return {\n installationId,\n accountId,\n accountLogin: typeof account.login === \"string\" ? account.login : null,\n accountType: typeof account.type === \"string\" ? account.type : null,\n suspended: Boolean(payload.suspended_at),\n };\n}\n\nasync function githubGet(\n path: string,\n token: string,\n params: Record<string, string> = {},\n): Promise<any> {\n const url = new URL(`${githubApiBase}${path}`);\n for (const [key, value] of Object.entries(params)) {\n url.searchParams.set(key, value);\n }\n const response = await fetch(url, { headers: githubHeaders(token) });\n if (!response.ok) {\n throw new GitHubAppApiError(await githubErrorMessage(response), response.status);\n }\n return await response.json();\n}\n\nfunction repositoryFromPayload(\n payload: Record<string, unknown>,\n installationId: number,\n account: Record<string, unknown>,\n): GitHubRepository {\n const id = asInt(payload.id);\n const fullName = String(payload.full_name ?? \"\");\n if (id === null || !fullName) {\n throw new GitHubAppApiError(\"GitHub returned a repository without id/full_name\");\n }\n return {\n id,\n installationId,\n fullName,\n name: String(payload.name ?? fullName.split(\"/\").at(-1) ?? fullName),\n private: Boolean(payload.private),\n htmlUrl: String(payload.html_url ?? `https://github.com/${fullName}`),\n cloneUrl: String(payload.clone_url ?? `https://github.com/${fullName}.git`),\n defaultBranch: String(payload.default_branch ?? \"main\"),\n accountLogin: String(account.login ?? fullName.split(\"/\", 1)[0]),\n accountType: typeof account.type === \"string\" ? account.type : null,\n };\n}\n\nfunction repositoryPermissionsFromPayload(payload: unknown): GitHubRepositoryPermissions {\n const permissions =\n payload && typeof payload === \"object\" && !Array.isArray(payload)\n ? (payload as Record<string, unknown>)\n : {};\n return {\n admin: permissions.admin === true,\n maintain: permissions.maintain === true,\n push: permissions.push === true,\n triage: permissions.triage === true,\n pull: permissions.pull === true,\n };\n}\n\nfunction githubHeaders(token?: string): HeadersInit {\n return {\n Accept: \"application/vnd.github+json\",\n ...(token ? { Authorization: `Bearer ${token}` } : {}),\n \"X-GitHub-Api-Version\": githubApiVersion,\n };\n}\n\nasync function githubErrorMessage(response: Response): Promise<string> {\n try {\n const payload = await response.json();\n if (payload && typeof payload === \"object\" && \"message\" in payload) {\n return `GitHub API ${response.status}: ${String(payload.message)}`;\n }\n } catch {\n // fall through\n }\n return `GitHub API ${response.status}: ${await response.text()}`;\n}\n\nexport function normalizeGitHubAppPrivateKey(value: string): string {\n const privateKey = value.trim().replace(/\\\\n/g, \"\\n\");\n if (!privateKey || privateKey.startsWith(pkcs8PrivateKeyHeader)) {\n return privateKey;\n }\n if (privateKey.startsWith(rsaPrivateKeyHeader)) {\n return createPrivateKey(privateKey).export({ type: \"pkcs8\", format: \"pem\" }).toString();\n }\n return privateKey;\n}\n\nfunction signStatePayload(encoded: string, secret: string): string {\n return createHmac(\"sha256\", secret).update(encoded).digest(\"base64url\");\n}\n\nfunction safeEqual(left: string, right: string): boolean {\n const a = Buffer.from(left);\n const b = Buffer.from(right);\n return a.length === b.length && timingSafeEqual(a, b);\n}\n\nfunction asInt(value: unknown): number | null {\n if (typeof value === \"number\" && Number.isInteger(value)) {\n return value;\n }\n if (typeof value === \"string\" && /^\\d+$/.test(value)) {\n return Number(value);\n }\n return null;\n}\n"],"mappings":"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","names":["installation"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@opengeni/github",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.5-canary.0",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -31,8 +31,8 @@
|
|
|
31
31
|
"prepublishOnly": "bash ../../scripts/prepublish-guard"
|
|
32
32
|
},
|
|
33
33
|
"dependencies": {
|
|
34
|
-
"@opengeni/config": "^0.
|
|
35
|
-
"@opengeni/contracts": "^2.
|
|
34
|
+
"@opengeni/config": "^0.20.1-canary.0",
|
|
35
|
+
"@opengeni/contracts": "^2.5.0-canary.0",
|
|
36
36
|
"jose": "^6.1.3"
|
|
37
37
|
}
|
|
38
38
|
}
|
package/src/index.ts
CHANGED
|
@@ -292,6 +292,36 @@ export function githubAppMissingSettings(settings: Settings): string[] {
|
|
|
292
292
|
return Object.entries(required).flatMap(([name, value]) => (value && value.trim() ? [] : [name]));
|
|
293
293
|
}
|
|
294
294
|
|
|
295
|
+
export function prReviewGitHubAppMissingSettings(settings: Settings): string[] {
|
|
296
|
+
const required: Record<string, string | undefined> = {
|
|
297
|
+
OPENGENI_GITHUB_APP_MANIFEST_STATE_SECRET: settings.githubAppManifestStateSecret,
|
|
298
|
+
OPENGENI_ENVIRONMENTS_ENCRYPTION_KEY: settings.environmentsEncryptionKey,
|
|
299
|
+
OPENGENI_PR_REVIEW_GITHUB_APP_ID: settings.prReviewGithubAppId,
|
|
300
|
+
OPENGENI_PR_REVIEW_GITHUB_CLIENT_ID: settings.prReviewGithubClientId,
|
|
301
|
+
OPENGENI_PR_REVIEW_GITHUB_CLIENT_SECRET: settings.prReviewGithubClientSecret,
|
|
302
|
+
OPENGENI_PR_REVIEW_GITHUB_APP_SLUG: settings.prReviewGithubAppSlug,
|
|
303
|
+
OPENGENI_PR_REVIEW_GITHUB_WEBHOOK_SECRET: settings.prReviewGithubWebhookSecret,
|
|
304
|
+
OPENGENI_PR_REVIEW_GITHUB_APP_PRIVATE_KEY: settings.prReviewGithubAppPrivateKey,
|
|
305
|
+
};
|
|
306
|
+
return Object.entries(required).flatMap(([name, value]) => (value && value.trim() ? [] : [name]));
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
/** Project the separately configured review App onto the ordinary GitHub App
|
|
310
|
+
* authority client. This keeps its OAuth, signing, webhook, and installation
|
|
311
|
+
* identity disjoint from the platform GitHub App while reusing the same
|
|
312
|
+
* personal-owner / organization-owner proof implementation. */
|
|
313
|
+
export function settingsForPrReviewGitHubApp(settings: Settings): Settings {
|
|
314
|
+
return {
|
|
315
|
+
...settings,
|
|
316
|
+
githubAppId: settings.prReviewGithubAppId,
|
|
317
|
+
githubClientId: settings.prReviewGithubClientId,
|
|
318
|
+
githubClientSecret: settings.prReviewGithubClientSecret,
|
|
319
|
+
githubAppSlug: settings.prReviewGithubAppSlug,
|
|
320
|
+
githubWebhookSecret: settings.prReviewGithubWebhookSecret,
|
|
321
|
+
githubAppPrivateKey: settings.prReviewGithubAppPrivateKey,
|
|
322
|
+
};
|
|
323
|
+
}
|
|
324
|
+
|
|
295
325
|
export type GitHubAppSigningSettings = Pick<Settings, "githubAppId" | "githubAppPrivateKey">;
|
|
296
326
|
|
|
297
327
|
function githubAppTokenMissingSettings(settings: GitHubAppSigningSettings): string[] {
|
|
@@ -869,6 +899,7 @@ export async function createGitHubAppInstallationTokenWithSigningSettings(
|
|
|
869
899
|
input: {
|
|
870
900
|
installationId: number;
|
|
871
901
|
repositoryIds: number[];
|
|
902
|
+
permissions?: Record<string, "read" | "write">;
|
|
872
903
|
},
|
|
873
904
|
): Promise<GitHubAppInstallationToken> {
|
|
874
905
|
const missing = githubAppTokenMissingSettings(settings);
|
|
@@ -896,6 +927,7 @@ export async function createGitHubAppInstallationTokenWithSigningSettings(
|
|
|
896
927
|
return await createInstallationToken(jwt, {
|
|
897
928
|
installationId: input.installationId,
|
|
898
929
|
repositoryIds,
|
|
930
|
+
...(input.permissions ? { permissions: input.permissions } : {}),
|
|
899
931
|
});
|
|
900
932
|
}
|
|
901
933
|
|