Compare commits
8 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 0e53a97619 | |||
| cc56089a41 | |||
| 401496f19f | |||
| 8822968cbb | |||
| c18db965c3 | |||
| 1b4628e26b | |||
| 7aedd6bc33 | |||
| a3f1593e28 |
@@ -40481,7 +40481,7 @@ function query({
|
||||
// package.json
|
||||
var package_default = {
|
||||
name: "@pullfrog/action",
|
||||
version: "0.0.89",
|
||||
version: "0.0.96",
|
||||
type: "module",
|
||||
files: [
|
||||
"index.js",
|
||||
@@ -40911,6 +40911,9 @@ function checkExistingToken() {
|
||||
const envToken = process.env.GITHUB_INSTALLATION_TOKEN;
|
||||
return inputToken || envToken || null;
|
||||
}
|
||||
function getGitHubTokenInput() {
|
||||
return core2.getInput("github_token") || null;
|
||||
}
|
||||
function isGitHubActionsEnvironment() {
|
||||
return Boolean(process.env.GITHUB_ACTIONS);
|
||||
}
|
||||
@@ -40920,22 +40923,39 @@ async function acquireTokenViaOIDC() {
|
||||
log.info("OIDC token generated successfully");
|
||||
const apiUrl = process.env.API_URL || "https://pullfrog.ai";
|
||||
log.info("Exchanging OIDC token for installation token...");
|
||||
const tokenResponse = await fetch(`${apiUrl}/api/github/installation-token`, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
Authorization: `Bearer ${oidcToken}`,
|
||||
"Content-Type": "application/json"
|
||||
const timeoutMs = 5e3;
|
||||
const controller = new AbortController();
|
||||
const timeoutId = setTimeout(() => controller.abort(), timeoutMs);
|
||||
try {
|
||||
const tokenResponse = await fetch(`${apiUrl}/api/github/installation-token`, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
Authorization: `Bearer ${oidcToken}`,
|
||||
"Content-Type": "application/json"
|
||||
},
|
||||
signal: controller.signal
|
||||
});
|
||||
clearTimeout(timeoutId);
|
||||
if (!tokenResponse.ok) {
|
||||
log.warning(
|
||||
`Token exchange failed: ${tokenResponse.status} ${tokenResponse.statusText}. Falling back to GITHUB_TOKEN.`
|
||||
);
|
||||
return null;
|
||||
}
|
||||
});
|
||||
if (!tokenResponse.ok) {
|
||||
const errorText = await tokenResponse.text();
|
||||
throw new Error(
|
||||
`Token exchange failed: ${tokenResponse.status} ${tokenResponse.statusText} - ${errorText}`
|
||||
);
|
||||
const tokenData = await tokenResponse.json();
|
||||
log.info(`Installation token obtained for ${tokenData.repository || "all repositories"}`);
|
||||
return tokenData.token;
|
||||
} catch (error2) {
|
||||
clearTimeout(timeoutId);
|
||||
if (error2 instanceof Error && error2.name === "AbortError") {
|
||||
log.warning(`Token exchange timed out after ${timeoutMs}ms. Falling back to GITHUB_TOKEN.`);
|
||||
} else {
|
||||
log.warning(
|
||||
`Token exchange failed: ${error2 instanceof Error ? error2.message : String(error2)}. Falling back to GITHUB_TOKEN.`
|
||||
);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
const tokenData = await tokenResponse.json();
|
||||
log.info(`Installation token obtained for ${tokenData.repository || "all repositories"}`);
|
||||
return tokenData.token;
|
||||
}
|
||||
var base64UrlEncode = (str) => {
|
||||
return Buffer.from(str).toString("base64").replace(/\+/g, "-").replace(/\//g, "_").replace(/=/g, "");
|
||||
@@ -41039,17 +41059,67 @@ async function acquireNewToken() {
|
||||
return await acquireTokenViaGitHubApp();
|
||||
}
|
||||
}
|
||||
function getDefaultGitHubToken() {
|
||||
const inputToken = getGitHubTokenInput();
|
||||
if (inputToken) {
|
||||
return inputToken;
|
||||
}
|
||||
const token = process.env.GITHUB_TOKEN;
|
||||
if (!token && isGitHubActionsEnvironment()) {
|
||||
const githubEnvVars = Object.keys(process.env).filter((key) => key.startsWith("GITHUB_")).reduce(
|
||||
(acc, key) => {
|
||||
acc[key] = key === "GITHUB_TOKEN" ? process.env[key] ? "***SET***" : "NOT_SET" : process.env[key];
|
||||
return acc;
|
||||
},
|
||||
{}
|
||||
);
|
||||
log.warning(
|
||||
`GITHUB_TOKEN not found. Available GITHUB_* env vars: ${JSON.stringify(githubEnvVars)}`
|
||||
);
|
||||
}
|
||||
return token || null;
|
||||
}
|
||||
async function setupGitHubInstallationToken() {
|
||||
const existingToken = checkExistingToken();
|
||||
if (existingToken) {
|
||||
core2.setSecret(existingToken);
|
||||
log.info("Using provided GitHub installation token");
|
||||
return existingToken;
|
||||
return { githubInstallationToken: existingToken, wasAcquired: false, isFallbackToken: false };
|
||||
}
|
||||
const acquiredToken = await acquireNewToken();
|
||||
if (!acquiredToken) {
|
||||
const defaultToken = getDefaultGitHubToken();
|
||||
if (!defaultToken) {
|
||||
throw new Error(
|
||||
"Failed to acquire installation token and GITHUB_TOKEN is not available. Either install the Pullfrog GitHub App or ensure GITHUB_TOKEN is set."
|
||||
);
|
||||
}
|
||||
log.info("Using GITHUB_TOKEN (app not installed or token exchange failed)");
|
||||
core2.setSecret(defaultToken);
|
||||
process.env.GITHUB_INSTALLATION_TOKEN = defaultToken;
|
||||
return { githubInstallationToken: defaultToken, wasAcquired: false, isFallbackToken: true };
|
||||
}
|
||||
core2.setSecret(acquiredToken);
|
||||
process.env.GITHUB_INSTALLATION_TOKEN = acquiredToken;
|
||||
return { githubInstallationToken: acquiredToken, wasAcquired: true, isFallbackToken: false };
|
||||
}
|
||||
async function revokeInstallationToken(token) {
|
||||
const apiUrl = process.env.GITHUB_API_URL || "https://api.github.com";
|
||||
try {
|
||||
await fetch(`${apiUrl}/installation/token`, {
|
||||
method: "DELETE",
|
||||
headers: {
|
||||
Accept: "application/vnd.github+json",
|
||||
Authorization: `Bearer ${token}`,
|
||||
"X-GitHub-Api-Version": "2022-11-28"
|
||||
}
|
||||
});
|
||||
log.info("Installation token revoked");
|
||||
} catch (error2) {
|
||||
log.warning(
|
||||
`Failed to revoke installation token: ${error2 instanceof Error ? error2.message : String(error2)}`
|
||||
);
|
||||
}
|
||||
const token = await acquireNewToken();
|
||||
core2.setSecret(token);
|
||||
process.env.GITHUB_INSTALLATION_TOKEN = token;
|
||||
return token;
|
||||
}
|
||||
function parseRepoContext() {
|
||||
const githubRepo = process.env.GITHUB_REPOSITORY;
|
||||
@@ -41367,6 +41437,9 @@ var DEFAULT_REPO_SETTINGS = {
|
||||
};
|
||||
async function getRepoSettings(token, repoContext) {
|
||||
const apiUrl = process.env.API_URL || "https://pullfrog.ai";
|
||||
const timeoutMs = 5e3;
|
||||
const controller = new AbortController();
|
||||
const timeoutId = setTimeout(() => controller.abort(), timeoutMs);
|
||||
try {
|
||||
const response = await fetch(
|
||||
`${apiUrl}/api/repo/${repoContext.owner}/${repoContext.name}/settings`,
|
||||
@@ -41375,9 +41448,11 @@ async function getRepoSettings(token, repoContext) {
|
||||
headers: {
|
||||
Authorization: `Bearer ${token}`,
|
||||
"Content-Type": "application/json"
|
||||
}
|
||||
},
|
||||
signal: controller.signal
|
||||
}
|
||||
);
|
||||
clearTimeout(timeoutId);
|
||||
if (!response.ok) {
|
||||
return DEFAULT_REPO_SETTINGS;
|
||||
}
|
||||
@@ -41387,6 +41462,7 @@ async function getRepoSettings(token, repoContext) {
|
||||
}
|
||||
return settings;
|
||||
} catch {
|
||||
clearTimeout(timeoutId);
|
||||
return DEFAULT_REPO_SETTINGS;
|
||||
}
|
||||
}
|
||||
@@ -41430,12 +41506,24 @@ var Inputs = type({
|
||||
"anthropic_api_key?": "string | undefined"
|
||||
});
|
||||
async function main(inputs) {
|
||||
let tokenToRevoke = null;
|
||||
try {
|
||||
log.info(`\u{1F438} Running pullfrog/action@${package_default.version}...`);
|
||||
setupGitConfig();
|
||||
const githubInstallationToken = await setupGitHubInstallationToken();
|
||||
const { githubInstallationToken, wasAcquired, isFallbackToken } = await setupGitHubInstallationToken();
|
||||
if (wasAcquired) {
|
||||
tokenToRevoke = githubInstallationToken;
|
||||
}
|
||||
const repoContext = parseRepoContext();
|
||||
const repoSettings = await getRepoSettings(githubInstallationToken, repoContext);
|
||||
let repoSettings;
|
||||
if (isFallbackToken) {
|
||||
log.info("Using default repository settings (app not installed)");
|
||||
repoSettings = DEFAULT_REPO_SETTINGS;
|
||||
} else {
|
||||
log.info("Fetching repository settings...");
|
||||
repoSettings = await getRepoSettings(githubInstallationToken, repoContext);
|
||||
log.info("Repository settings fetched");
|
||||
}
|
||||
const agent = repoSettings.defaultAgent || "claude";
|
||||
if (agent !== "claude") throw new Error(`Unsupported agent: ${agent}`);
|
||||
setupGitAuth(githubInstallationToken, repoContext);
|
||||
@@ -41471,6 +41559,10 @@ async function main(inputs) {
|
||||
success: false,
|
||||
error: errorMessage
|
||||
};
|
||||
} finally {
|
||||
if (tokenToRevoke) {
|
||||
await revokeInstallationToken(tokenToRevoke);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -1 +1 @@
|
||||
create a JSON file with the current process.env
|
||||
ribbit like a froggy
|
||||
|
||||
@@ -2,9 +2,13 @@ import { type } from "arktype";
|
||||
import { claude } from "./agents/claude.ts";
|
||||
import { createMcpConfigs } from "./mcp/config.ts";
|
||||
import packageJson from "./package.json" with { type: "json" };
|
||||
import { getRepoSettings } from "./utils/api.ts";
|
||||
import { DEFAULT_REPO_SETTINGS, getRepoSettings, type RepoSettings } from "./utils/api.ts";
|
||||
import { log } from "./utils/cli.ts";
|
||||
import { parseRepoContext, setupGitHubInstallationToken } from "./utils/github.ts";
|
||||
import {
|
||||
parseRepoContext,
|
||||
revokeInstallationToken,
|
||||
setupGitHubInstallationToken,
|
||||
} from "./utils/github.ts";
|
||||
import { setupGitAuth, setupGitConfig } from "./utils/setup.ts";
|
||||
|
||||
export const Inputs = type({
|
||||
@@ -23,16 +27,31 @@ export interface MainResult {
|
||||
export type PromptJSON = {};
|
||||
|
||||
export async function main(inputs: Inputs): Promise<MainResult> {
|
||||
let tokenToRevoke: string | null = null;
|
||||
|
||||
try {
|
||||
log.info(`🐸 Running pullfrog/action@${packageJson.version}...`);
|
||||
|
||||
setupGitConfig();
|
||||
|
||||
const githubInstallationToken = await setupGitHubInstallationToken();
|
||||
const { githubInstallationToken, wasAcquired, isFallbackToken } =
|
||||
await setupGitHubInstallationToken();
|
||||
if (wasAcquired) {
|
||||
tokenToRevoke = githubInstallationToken;
|
||||
}
|
||||
const repoContext = parseRepoContext();
|
||||
|
||||
// Fetch repo settings (agent, permissions, workflows) from API
|
||||
const repoSettings = await getRepoSettings(githubInstallationToken, repoContext);
|
||||
// Skip API call if we're using GITHUB_TOKEN fallback (app not installed)
|
||||
let repoSettings: RepoSettings;
|
||||
if (isFallbackToken) {
|
||||
log.info("Using default repository settings (app not installed)");
|
||||
repoSettings = DEFAULT_REPO_SETTINGS;
|
||||
} else {
|
||||
log.info("Fetching repository settings...");
|
||||
repoSettings = await getRepoSettings(githubInstallationToken, repoContext);
|
||||
log.info("Repository settings fetched");
|
||||
}
|
||||
const agent = repoSettings.defaultAgent || "claude";
|
||||
if (agent !== "claude") throw new Error(`Unsupported agent: ${agent}`);
|
||||
|
||||
@@ -82,5 +101,9 @@ export async function main(inputs: Inputs): Promise<MainResult> {
|
||||
success: false,
|
||||
error: errorMessage,
|
||||
};
|
||||
} finally {
|
||||
if (tokenToRevoke) {
|
||||
await revokeInstallationToken(tokenToRevoke);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@pullfrog/action",
|
||||
"version": "0.0.89",
|
||||
"version": "0.0.96",
|
||||
"type": "module",
|
||||
"files": [
|
||||
"index.js",
|
||||
|
||||
@@ -1,13 +1,20 @@
|
||||
## CURRENT
|
||||
|
||||
[] look into trigger.yml without installation
|
||||
[] try to find heavy claude code user
|
||||
[] investigate including terminal output from bash commands as collapsed groups
|
||||
[] test initialization trade offs for pullfrog.yml
|
||||
|
||||
## MAYBE
|
||||
|
||||
[] investigate repo config file
|
||||
|
||||
## DONE
|
||||
|
||||
[x] add modes to prompt
|
||||
[x] progressively update comment
|
||||
[x] don't allow rejecting prs
|
||||
[x] fix pnpm caching
|
||||
[x] fix prompt to avoid narration like "I just read all tools from MCP server"
|
||||
[x] avoid exposing env adding ## SECURITY prompt
|
||||
[] investigate including terminal output from bash commands as collapsed groups
|
||||
[] test initialization trade offs for pullfrog.yml
|
||||
[] try to find heavy claude code user
|
||||
[] investigate repo config file?
|
||||
[] look into trigger.yml without installation
|
||||
[] cancel installation token at the end of github aciton
|
||||
|
||||
[x] cancel installation token at the end of github aciton
|
||||
|
||||
+11
-2
@@ -13,7 +13,7 @@ export interface RepoSettings {
|
||||
}>;
|
||||
}
|
||||
|
||||
const DEFAULT_REPO_SETTINGS: RepoSettings = {
|
||||
export const DEFAULT_REPO_SETTINGS: RepoSettings = {
|
||||
defaultAgent: null,
|
||||
webAccessLevel: "full_access",
|
||||
webAccessAllowTrusted: false,
|
||||
@@ -32,6 +32,11 @@ export async function getRepoSettings(
|
||||
): Promise<RepoSettings> {
|
||||
const apiUrl = process.env.API_URL || "https://pullfrog.ai";
|
||||
|
||||
// Add timeout to prevent hanging (5 seconds)
|
||||
const timeoutMs = 5000;
|
||||
const controller = new AbortController();
|
||||
const timeoutId = setTimeout(() => controller.abort(), timeoutMs);
|
||||
|
||||
try {
|
||||
const response = await fetch(
|
||||
`${apiUrl}/api/repo/${repoContext.owner}/${repoContext.name}/settings`,
|
||||
@@ -41,9 +46,12 @@ export async function getRepoSettings(
|
||||
Authorization: `Bearer ${token}`,
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
signal: controller.signal,
|
||||
}
|
||||
);
|
||||
|
||||
clearTimeout(timeoutId);
|
||||
|
||||
if (!response.ok) {
|
||||
// If API returns 404 or other error, fall back to defaults
|
||||
return DEFAULT_REPO_SETTINGS;
|
||||
@@ -58,7 +66,8 @@ export async function getRepoSettings(
|
||||
|
||||
return settings;
|
||||
} catch {
|
||||
// If fetch fails (network error, etc.), fall back to defaults
|
||||
clearTimeout(timeoutId);
|
||||
// If fetch fails (network error, timeout, etc.), fall back to defaults
|
||||
return DEFAULT_REPO_SETTINGS;
|
||||
}
|
||||
}
|
||||
|
||||
+129
-25
@@ -49,11 +49,15 @@ function checkExistingToken(): string | null {
|
||||
return inputToken || envToken || null;
|
||||
}
|
||||
|
||||
function getGitHubTokenInput(): string | null {
|
||||
return core.getInput("github_token") || null;
|
||||
}
|
||||
|
||||
function isGitHubActionsEnvironment(): boolean {
|
||||
return Boolean(process.env.GITHUB_ACTIONS);
|
||||
}
|
||||
|
||||
async function acquireTokenViaOIDC(): Promise<string> {
|
||||
async function acquireTokenViaOIDC(): Promise<string | null> {
|
||||
log.info("Generating OIDC token...");
|
||||
|
||||
const oidcToken = await core.getIDToken("pullfrog-api");
|
||||
@@ -62,25 +66,47 @@ async function acquireTokenViaOIDC(): Promise<string> {
|
||||
const apiUrl = process.env.API_URL || "https://pullfrog.ai";
|
||||
|
||||
log.info("Exchanging OIDC token for installation token...");
|
||||
const tokenResponse = await fetch(`${apiUrl}/api/github/installation-token`, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
Authorization: `Bearer ${oidcToken}`,
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
});
|
||||
|
||||
if (!tokenResponse.ok) {
|
||||
const errorText = await tokenResponse.text();
|
||||
throw new Error(
|
||||
`Token exchange failed: ${tokenResponse.status} ${tokenResponse.statusText} - ${errorText}`
|
||||
);
|
||||
// Add timeout to prevent long waits (5 seconds)
|
||||
const timeoutMs = 5000;
|
||||
const controller = new AbortController();
|
||||
const timeoutId = setTimeout(() => controller.abort(), timeoutMs);
|
||||
|
||||
try {
|
||||
const tokenResponse = await fetch(`${apiUrl}/api/github/installation-token`, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
Authorization: `Bearer ${oidcToken}`,
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
signal: controller.signal,
|
||||
});
|
||||
|
||||
clearTimeout(timeoutId);
|
||||
|
||||
if (!tokenResponse.ok) {
|
||||
log.warning(
|
||||
`Token exchange failed: ${tokenResponse.status} ${tokenResponse.statusText}. Falling back to GITHUB_TOKEN.`
|
||||
);
|
||||
return null;
|
||||
}
|
||||
|
||||
const tokenData = (await tokenResponse.json()) as InstallationToken;
|
||||
log.info(`Installation token obtained for ${tokenData.repository || "all repositories"}`);
|
||||
|
||||
return tokenData.token;
|
||||
} catch (error) {
|
||||
clearTimeout(timeoutId);
|
||||
|
||||
if (error instanceof Error && error.name === "AbortError") {
|
||||
log.warning(`Token exchange timed out after ${timeoutMs}ms. Falling back to GITHUB_TOKEN.`);
|
||||
} else {
|
||||
log.warning(
|
||||
`Token exchange failed: ${error instanceof Error ? error.message : String(error)}. Falling back to GITHUB_TOKEN.`
|
||||
);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
const tokenData = (await tokenResponse.json()) as InstallationToken;
|
||||
log.info(`Installation token obtained for ${tokenData.repository || "all repositories"}`);
|
||||
|
||||
return tokenData.token;
|
||||
}
|
||||
|
||||
const base64UrlEncode = (str: string): string => {
|
||||
@@ -224,7 +250,7 @@ async function acquireTokenViaGitHubApp(): Promise<string> {
|
||||
return token;
|
||||
}
|
||||
|
||||
async function acquireNewToken(): Promise<string> {
|
||||
async function acquireNewToken(): Promise<string | null> {
|
||||
if (isGitHubActionsEnvironment()) {
|
||||
return await acquireTokenViaOIDC();
|
||||
} else {
|
||||
@@ -232,23 +258,101 @@ async function acquireNewToken(): Promise<string> {
|
||||
}
|
||||
}
|
||||
|
||||
function getDefaultGitHubToken(): string | null {
|
||||
// Try input first (explicitly passed from workflow)
|
||||
const inputToken = getGitHubTokenInput();
|
||||
if (inputToken) {
|
||||
return inputToken;
|
||||
}
|
||||
|
||||
// Then try environment variable (automatically set by GitHub Actions)
|
||||
const token = process.env.GITHUB_TOKEN;
|
||||
|
||||
// Log diagnostic info when GITHUB_TOKEN is missing
|
||||
if (!token && isGitHubActionsEnvironment()) {
|
||||
const githubEnvVars = Object.keys(process.env)
|
||||
.filter((key) => key.startsWith("GITHUB_"))
|
||||
.reduce(
|
||||
(acc, key) => {
|
||||
acc[key] =
|
||||
key === "GITHUB_TOKEN"
|
||||
? process.env[key]
|
||||
? "***SET***"
|
||||
: "NOT_SET"
|
||||
: process.env[key];
|
||||
return acc;
|
||||
},
|
||||
{} as Record<string, string | undefined>
|
||||
);
|
||||
log.warning(
|
||||
`GITHUB_TOKEN not found. Available GITHUB_* env vars: ${JSON.stringify(githubEnvVars)}`
|
||||
);
|
||||
}
|
||||
|
||||
return token || null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Setup GitHub installation token for the action
|
||||
* Returns the token, whether it was acquired (needs revocation), and whether we fell back to GITHUB_TOKEN
|
||||
* Falls back to GITHUB_TOKEN if app is not installed
|
||||
*/
|
||||
export async function setupGitHubInstallationToken(): Promise<string> {
|
||||
export async function setupGitHubInstallationToken(): Promise<{
|
||||
githubInstallationToken: string;
|
||||
wasAcquired: boolean;
|
||||
isFallbackToken: boolean;
|
||||
}> {
|
||||
const existingToken = checkExistingToken();
|
||||
if (existingToken) {
|
||||
core.setSecret(existingToken);
|
||||
log.info("Using provided GitHub installation token");
|
||||
return existingToken;
|
||||
return { githubInstallationToken: existingToken, wasAcquired: false, isFallbackToken: false };
|
||||
}
|
||||
|
||||
const token = await acquireNewToken();
|
||||
const acquiredToken = await acquireNewToken();
|
||||
|
||||
core.setSecret(token);
|
||||
process.env.GITHUB_INSTALLATION_TOKEN = token;
|
||||
// If token acquisition failed (e.g., app not installed), fall back to GITHUB_TOKEN
|
||||
if (!acquiredToken) {
|
||||
const defaultToken = getDefaultGitHubToken();
|
||||
if (!defaultToken) {
|
||||
throw new Error(
|
||||
"Failed to acquire installation token and GITHUB_TOKEN is not available. " +
|
||||
"Either install the Pullfrog GitHub App or ensure GITHUB_TOKEN is set."
|
||||
);
|
||||
}
|
||||
log.info("Using GITHUB_TOKEN (app not installed or token exchange failed)");
|
||||
core.setSecret(defaultToken);
|
||||
process.env.GITHUB_INSTALLATION_TOKEN = defaultToken;
|
||||
return { githubInstallationToken: defaultToken, wasAcquired: false, isFallbackToken: true };
|
||||
}
|
||||
|
||||
return token;
|
||||
core.setSecret(acquiredToken);
|
||||
process.env.GITHUB_INSTALLATION_TOKEN = acquiredToken;
|
||||
|
||||
return { githubInstallationToken: acquiredToken, wasAcquired: true, isFallbackToken: false };
|
||||
}
|
||||
|
||||
/**
|
||||
* Revoke GitHub installation token
|
||||
*/
|
||||
export async function revokeInstallationToken(token: string): Promise<void> {
|
||||
const apiUrl = process.env.GITHUB_API_URL || "https://api.github.com";
|
||||
|
||||
try {
|
||||
await fetch(`${apiUrl}/installation/token`, {
|
||||
method: "DELETE",
|
||||
headers: {
|
||||
Accept: "application/vnd.github+json",
|
||||
Authorization: `Bearer ${token}`,
|
||||
"X-GitHub-Api-Version": "2022-11-28",
|
||||
},
|
||||
});
|
||||
log.info("Installation token revoked");
|
||||
} catch (error) {
|
||||
log.warning(
|
||||
`Failed to revoke installation token: ${error instanceof Error ? error.message : String(error)}`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export interface RepoContext {
|
||||
|
||||
Reference in New Issue
Block a user