@@ -232,6 +232,9 @@ export default {
232232 )
233233 }
234234
235+ /**
236+ * Used by the GitHub action to get GitHub installation access token given the OIDC token
237+ */
235238 if ( request . method === "POST" && method === "exchange_github_app_token" ) {
236239 const EXPECTED_AUDIENCE = "opencode-github-action"
237240 const GITHUB_ISSUER = "https://token.actions.githubusercontent.com"
@@ -285,6 +288,38 @@ export default {
285288 } )
286289 }
287290
291+ /**
292+ * Used by the opencode CLI to check if the GitHub app is installed
293+ */
294+ if ( request . method === "GET" && method === "get_github_app_installation" ) {
295+ const owner = url . searchParams . get ( "owner" )
296+ const repo = url . searchParams . get ( "repo" )
297+
298+ const auth = createAppAuth ( {
299+ appId : Resource . GITHUB_APP_ID . value ,
300+ privateKey : Resource . GITHUB_APP_PRIVATE_KEY . value ,
301+ } )
302+ const appAuth = await auth ( { type : "app" } )
303+
304+ // Lookup installation
305+ const octokit = new Octokit ( { auth : appAuth . token } )
306+ let installation
307+ try {
308+ const ret = await octokit . apps . getRepoInstallation ( { owner, repo } )
309+ installation = ret . data
310+ } catch ( err ) {
311+ if ( err instanceof Error && err . message . includes ( "Not Found" ) ) {
312+ // not installed
313+ } else {
314+ throw err
315+ }
316+ }
317+
318+ return new Response ( JSON . stringify ( { installation } ) , {
319+ headers : { "Content-Type" : "application/json" } ,
320+ } )
321+ }
322+
288323 return new Response ( "Not Found" , { status : 404 } )
289324 } ,
290325}
0 commit comments