From 06a19567c0d63b237af27c4a4af7cc7703e3a49a Mon Sep 17 00:00:00 2001 From: Colin McDonnell Date: Tue, 18 Nov 2025 23:15:44 -0800 Subject: [PATCH] Switch to payload --- agents/claude.ts | 4 +- agents/codex.ts | 4 +- agents/cursor.ts | 4 +- agents/gemini.ts | 6 +- agents/instructions.ts | 20 +- agents/shared.ts | 3 +- entry.js | 711 +++++++++++++++++++---------------------- main.ts | 28 +- mcp-server.js | 57 +++- modes.ts | 10 +- package.json | 3 +- payload.ts | 37 +++ pnpm-lock.yaml | 137 ++++++++ 13 files changed, 614 insertions(+), 410 deletions(-) create mode 100644 payload.ts diff --git a/agents/claude.ts b/agents/claude.ts index a00a036..c6798ba 100644 --- a/agents/claude.ts +++ b/agents/claude.ts @@ -15,11 +15,11 @@ export const claude = agent({ executablePath: "cli.js", }); }, - run: async ({ prompt, mcpServers, apiKey, cliPath }) => { + run: async ({ payload, mcpServers, apiKey, cliPath }) => { process.env.ANTHROPIC_API_KEY = apiKey; const queryInstance = query({ - prompt: addInstructions(prompt), + prompt: addInstructions(payload), options: { permissionMode: "bypassPermissions", mcpServers, diff --git a/agents/codex.ts b/agents/codex.ts index 52de896..72b96ff 100644 --- a/agents/codex.ts +++ b/agents/codex.ts @@ -14,7 +14,7 @@ export const codex = agent({ executablePath: "bin/codex.js", }); }, - run: async ({ prompt, mcpServers, apiKey, cliPath, githubInstallationToken }) => { + run: async ({ payload, mcpServers, apiKey, cliPath, githubInstallationToken }) => { process.env.OPENAI_API_KEY = apiKey; process.env.GITHUB_INSTALLATION_TOKEN = githubInstallationToken; @@ -39,7 +39,7 @@ export const codex = agent({ try { // Use runStreamed to get streaming events similar to claude.ts - const streamedTurn = await thread.runStreamed(addInstructions(prompt)); + const streamedTurn = await thread.runStreamed(addInstructions(payload)); // Stream events and handle them let finalOutput = ""; diff --git a/agents/cursor.ts b/agents/cursor.ts index aa6508d..2ac7b6f 100644 --- a/agents/cursor.ts +++ b/agents/cursor.ts @@ -14,7 +14,7 @@ export const cursor = agent({ executableName: "cursor-agent", }); }, - run: async ({ prompt, apiKey, cliPath, githubInstallationToken, mcpServers }) => { + run: async ({ payload, apiKey, cliPath, githubInstallationToken, mcpServers }) => { process.env.CURSOR_API_KEY = apiKey; process.env.GITHUB_INSTALLATION_TOKEN = githubInstallationToken; @@ -24,7 +24,7 @@ export const cursor = agent({ // Run cursor-agent in non-interactive mode with the prompt // Using -p flag for prompt, --output-format text for plain text output // and --approve-mcps to automatically approve all MCP servers - const fullPrompt = addInstructions(prompt); + const fullPrompt = addInstructions(payload); // Find temp directory from cliPath to set HOME for MCP config lookup const tempDir = cliPath.split("/.local/bin/")[0]; diff --git a/agents/gemini.ts b/agents/gemini.ts index 3890b61..05ae52f 100644 --- a/agents/gemini.ts +++ b/agents/gemini.ts @@ -15,7 +15,7 @@ export const gemini = agent({ installDependencies: true, }); }, - run: async ({ prompt, apiKey, mcpServers, githubInstallationToken, cliPath }) => { + run: async ({ payload, apiKey, mcpServers, githubInstallationToken, cliPath }) => { configureGeminiMcpServers({ mcpServers, cliPath }); if (!apiKey) { throw new Error("google_api_key or gemini_api_key is required for gemini agent"); @@ -25,8 +25,8 @@ export const gemini = agent({ process.env.GEMINI_API_KEY = apiKey; process.env.GITHUB_INSTALLATION_TOKEN = githubInstallationToken; - const sessionPrompt = addInstructions(prompt); - log.info(`Starting Gemini CLI with prompt: ${prompt.substring(0, 100)}...`); + const sessionPrompt = addInstructions(payload); + log.info(`Starting Gemini CLI with prompt: ${payload.prompt.substring(0, 100)}...`); let finalOutput = ""; try { diff --git a/agents/instructions.ts b/agents/instructions.ts index e792614..c540daa 100644 --- a/agents/instructions.ts +++ b/agents/instructions.ts @@ -1,13 +1,15 @@ import { ghPullfrogMcpName } from "../mcp/index.ts"; import { modes } from "../modes.ts"; +import type { Payload } from "../payload.ts"; -const userPromptHeader = `****** USER PROMPT ******\n`; +// const userPromptHeader = `************* USER PROMPT *************\n`; -export const instructions = ` +export const addInstructions = (payload: Payload) => + `************* GENERAL INSTRUCTIONS ************* # General instructions You are a diligent, detail-oriented, no-nonsense software engineering agent. -You will perform the task that is asked of you below ${userPromptHeader}. +You will perform the task described in the *USER PROMPT* below. You are careful, to-the-point, and kind. You only say things you know to be true. You have an extreme bias toward minimalism in your code and responses. Your code is focused, elegant, and production-ready. @@ -48,12 +50,14 @@ Ensure after your edits are done, your final comments do not contain intermediat choose the appropriate mode based on the prompt payload: -${modes.map((w) => ` - "${w.name}": ${w.description}`).join("\n")} +${[...modes, ...payload.modes].map((w) => ` - "${w.name}": ${w.description}`).join("\n")} ## Modes -${modes.map((w) => `### ${w.name}\n\n${w.prompt}`).join("\n\n")} -`; +${[...modes, ...payload.modes].map((w) => `### ${w.name}\n\n${w.prompt}`).join("\n\n")} -export const addInstructions = (prompt: string) => - `****** GENERAL INSTRUCTIONS ******\n${instructions}\n\n${userPromptHeader}${prompt}`; +************* USER PROMPT ************* + +${payload.prompt} + +${payload.event}`; diff --git a/agents/shared.ts b/agents/shared.ts index 0d43741..ce2e296 100644 --- a/agents/shared.ts +++ b/agents/shared.ts @@ -5,6 +5,7 @@ import { tmpdir } from "node:os"; import { join } from "node:path"; import { pipeline } from "node:stream/promises"; import type { McpStdioServerConfig } from "@anthropic-ai/claude-agent-sdk"; +import type { Payload } from "../payload.ts"; import { log } from "../utils/cli.ts"; /** @@ -23,7 +24,7 @@ export interface AgentResult { export interface AgentConfig { apiKey: string; githubInstallationToken: string; - prompt: string; + payload: Payload; mcpServers: Record; cliPath: string; } diff --git a/entry.js b/entry.js index 4e86173..9c0eb54 100755 --- a/entry.js +++ b/entry.js @@ -32913,12 +32913,13 @@ var package_default = { }, dependencies: { "@actions/core": "^1.11.1", + "@actions/github": "^6.0.1", "@anthropic-ai/claude-agent-sdk": "0.1.37", - "@openai/codex-sdk": "0.58.0", "@ark/fs": "0.53.0", "@ark/util": "0.53.0", "@octokit/rest": "^22.0.0", "@octokit/webhooks-types": "^7.6.1", + "@openai/codex-sdk": "0.58.0", "@standard-schema/spec": "1.0.0", arktype: "2.1.25", dotenv: "^17.2.3", @@ -33214,13 +33215,11 @@ var modes = [ ]; // agents/instructions.ts -var userPromptHeader = `****** USER PROMPT ****** -`; -var instructions = ` +var addInstructions = (payload) => `************* GENERAL INSTRUCTIONS ************* # General instructions You are a diligent, detail-oriented, no-nonsense software engineering agent. -You will perform the task that is asked of you below ${userPromptHeader}. +You will perform the task described in the *USER PROMPT* below. You are careful, to-the-point, and kind. You only say things you know to be true. You have an extreme bias toward minimalism in your code and responses. Your code is focused, elegant, and production-ready. @@ -33261,18 +33260,19 @@ Ensure after your edits are done, your final comments do not contain intermediat choose the appropriate mode based on the prompt payload: -${modes.map((w) => ` - "${w.name}": ${w.description}`).join("\n")} +${[...modes, ...payload.modes].map((w) => ` - "${w.name}": ${w.description}`).join("\n")} ## Modes -${modes.map((w) => `### ${w.name} +${[...modes, ...payload.modes].map((w) => `### ${w.name} ${w.prompt}`).join("\n\n")} -`; -var addInstructions = (prompt) => `****** GENERAL INSTRUCTIONS ****** -${instructions} -${userPromptHeader}${prompt}`; +************* USER PROMPT ************* + +${payload.prompt} + +${payload.event}`; // agents/shared.ts import { spawnSync } from "node:child_process"; @@ -33284,7 +33284,8 @@ import { pipeline } from "node:stream/promises"; async function installFromNpmTarball({ packageName, version, - executablePath + executablePath, + installDependencies }) { let resolvedVersion = version; if (version.startsWith("^") || version.startsWith("~") || version === "latest") { @@ -33342,6 +33343,20 @@ async function installFromNpmTarball({ if (!existsSync2(cliPath)) { throw new Error(`Executable not found in extracted package at ${cliPath}`); } + if (installDependencies) { + log.info(`Installing dependencies for ${packageName}...`); + const installResult = spawnSync("npm", ["install", "--production"], { + cwd: extractedDir, + stdio: "pipe", + encoding: "utf-8" + }); + if (installResult.status !== 0) { + throw new Error( + `Failed to install dependencies: ${installResult.stderr || installResult.stdout || "Unknown error"}` + ); + } + log.info(`\u2713 Dependencies installed`); + } chmodSync(cliPath, 493); log.info(`\u2713 ${packageName} installed at ${cliPath}`); return cliPath; @@ -33405,10 +33420,10 @@ var claude = agent({ executablePath: "cli.js" }); }, - run: async ({ prompt, mcpServers, apiKey, cliPath }) => { + run: async ({ payload, mcpServers, apiKey, cliPath }) => { process.env.ANTHROPIC_API_KEY = apiKey; const queryInstance = query({ - prompt: addInstructions(prompt), + prompt: addInstructions(payload), options: { permissionMode: "bypassPermissions", mcpServers, @@ -33855,12 +33870,10 @@ var codex = agent({ executablePath: "bin/codex.js" }); }, - run: async ({ prompt, mcpServers, apiKey, cliPath, githubInstallationToken }) => { + run: async ({ payload, mcpServers, apiKey, cliPath, githubInstallationToken }) => { process.env.OPENAI_API_KEY = apiKey; process.env.GITHUB_INSTALLATION_TOKEN = githubInstallationToken; - if (mcpServers && Object.keys(mcpServers).length > 0) { - configureMcpServers({ mcpServers, apiKey, cliPath }); - } + configureCodexMcpServers({ mcpServers, cliPath }); const codexOptions = { apiKey, codexPathOverride: cliPath @@ -33872,7 +33885,7 @@ var codex = agent({ networkAccessEnabled: true }); try { - const streamedTurn = await thread.runStreamed(addInstructions(prompt)); + const streamedTurn = await thread.runStreamed(addInstructions(payload)); let finalOutput = ""; for await (const event of streamedTurn.events) { const handler = messageHandlers2[event.type]; @@ -33968,17 +33981,8 @@ var messageHandlers2 = { log.error(`Error: ${event.message}`); } }; -function configureMcpServers({ - mcpServers, - apiKey, - cliPath -}) { - log.info("Configuring MCP servers for Codex..."); +function configureCodexMcpServers({ mcpServers, cliPath }) { for (const [serverName, serverConfig] of Object.entries(mcpServers)) { - if (!("command" in serverConfig)) { - log.warning(`MCP server '${serverName}' is not a stdio server, skipping...`); - continue; - } const command = serverConfig.command; const args2 = serverConfig.args || []; const envVars = serverConfig.env || {}; @@ -33990,11 +33994,7 @@ function configureMcpServers({ log.info(`Adding MCP server '${serverName}'...`); const addResult = spawnSync2("node", [cliPath, ...addArgs], { stdio: "pipe", - encoding: "utf-8", - env: { - ...process.env, - OPENAI_API_KEY: apiKey - } + encoding: "utf-8" }); if (addResult.status !== 0) { throw new Error( @@ -34011,27 +34011,25 @@ import { mkdirSync as mkdirSync2, writeFileSync as writeFileSync2 } from "node:f import { join as join5 } from "node:path"; var cursor = agent({ name: "cursor", - inputKey: "cursor_api_key", + inputKeys: ["cursor_api_key"], install: async () => { return await installFromCurl({ installUrl: "https://cursor.com/install", executableName: "cursor-agent" }); }, - run: async ({ prompt, mcpServers, apiKey, cliPath, githubInstallationToken }) => { + run: async ({ payload, apiKey, cliPath, githubInstallationToken, mcpServers }) => { process.env.CURSOR_API_KEY = apiKey; process.env.GITHUB_INSTALLATION_TOKEN = githubInstallationToken; - if (mcpServers && Object.keys(mcpServers).length > 0) { - configureMcpServers2({ mcpServers, cliPath }); - } + configureCursorMcpServers({ mcpServers, cliPath }); try { - const fullPrompt = addInstructions(prompt); + const fullPrompt = addInstructions(payload); const tempDir = cliPath.split("/.local/bin/")[0]; log.info("Running Cursor CLI..."); return new Promise((resolve) => { const child = spawn3( cliPath, - ["--print", fullPrompt, "--output-format", "text", "--approve-mcps"], + ["--print", fullPrompt, "--output-format", "text", "--approve-mcps", "--force"], { cwd: process.cwd(), // Run in current working directory @@ -34048,37 +34046,31 @@ var cursor = agent({ ); let stdout = ""; let stderr = ""; - let hasOutput = false; - const timeout = setTimeout(() => { - if (!hasOutput && child.exitCode === null) { - log.warning("Cursor CLI appears to be hanging, killing process..."); - child.kill("SIGTERM"); - resolve({ - success: false, - error: "Cursor CLI timed out - no output received", - output: stdout.trim() - }); - } - }, 3e5); child.on("spawn", () => { log.debug("Cursor CLI process spawned"); }); child.stdout?.on("data", (data) => { - hasOutput = true; const text = data.toString(); stdout += text; process.stdout.write(text); }); child.stderr?.on("data", (data) => { - hasOutput = true; const text = data.toString(); stderr += text; process.stderr.write(text); log.warning(text); }); - child.on("close", (code) => { - clearTimeout(timeout); - if (code !== 0) { + child.on("close", (code, signal) => { + if (signal) { + log.warning(`Cursor CLI terminated by signal: ${signal}`); + } + if (code === 0) { + log.success("Cursor CLI completed successfully"); + resolve({ + success: true, + output: stdout.trim() + }); + } else { const errorMessage = stderr || `Cursor CLI exited with code ${code}`; log.error(`Cursor CLI failed: ${errorMessage}`); resolve({ @@ -34109,222 +34101,16 @@ var cursor = agent({ } } }); -function configureMcpServers2({ - mcpServers, - cliPath -}) { - log.info("Configuring MCP servers for Cursor..."); +function configureCursorMcpServers({ mcpServers, cliPath }) { const tempDir = cliPath.split("/.local/bin/")[0]; const cursorConfigDir = join5(tempDir, ".cursor"); const mcpConfigPath = join5(cursorConfigDir, "mcp.json"); - const mcpConfig = { - mcpServers: {} - }; - for (const [serverName, serverConfig] of Object.entries(mcpServers)) { - if (!("command" in serverConfig)) { - log.warning(`MCP server '${serverName}' is not a stdio server, skipping...`); - continue; - } - mcpConfig.mcpServers[serverName] = serverConfig; - log.info(`Adding MCP server '${serverName}'...`); - } - if (Object.keys(mcpConfig.mcpServers).length === 0) { - log.info("No MCP servers to configure"); - return; - } mkdirSync2(cursorConfigDir, { recursive: true }); - writeFileSync2(mcpConfigPath, JSON.stringify(mcpConfig, null, 2), "utf-8"); - log.info(`\u2713 MCP configuration written to ${mcpConfigPath}`); - log.info("MCP servers configured. Cursor CLI will use --approve-mcps to auto-approve servers."); + writeFileSync2(mcpConfigPath, JSON.stringify({ mcpServers }, null, 2), "utf-8"); } -// utils/github.ts -var core2 = __toESM(require_core(), 1); -import { createSign } from "node:crypto"; -function checkExistingToken() { - const inputToken = core2.getInput("github_installation_token"); - const envToken = process.env.GITHUB_INSTALLATION_TOKEN; - return inputToken || envToken || null; -} -function isGitHubActionsEnvironment() { - return Boolean(process.env.GITHUB_ACTIONS); -} -async function acquireTokenViaOIDC() { - log.info("Generating OIDC token..."); - const oidcToken = await core2.getIDToken("pullfrog-api"); - log.info("OIDC token generated successfully"); - const apiUrl = process.env.API_URL || "https://pullfrog.ai"; - log.info("Exchanging OIDC token for installation token..."); - 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) { - throw new Error(`Token exchange failed: ${tokenResponse.status} ${tokenResponse.statusText}`); - } - 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") { - throw new Error(`Token exchange timed out after ${timeoutMs}ms`); - } - throw error2; - } -} -var base64UrlEncode = (str) => { - return Buffer.from(str).toString("base64").replace(/\+/g, "-").replace(/\//g, "_").replace(/=/g, ""); -}; -var generateJWT = (appId, privateKey) => { - const now = Math.floor(Date.now() / 1e3); - const payload = { - iat: now - 60, - exp: now + 5 * 60, - iss: appId - }; - const header = { - alg: "RS256", - typ: "JWT" - }; - const encodedHeader = base64UrlEncode(JSON.stringify(header)); - const encodedPayload = base64UrlEncode(JSON.stringify(payload)); - const signaturePart = `${encodedHeader}.${encodedPayload}`; - const signature = createSign("RSA-SHA256").update(signaturePart).sign(privateKey, "base64").replace(/\+/g, "-").replace(/\//g, "_").replace(/=/g, ""); - return `${signaturePart}.${signature}`; -}; -var githubRequest = async (path4, options = {}) => { - const { method = "GET", headers = {}, body } = options; - const url2 = `https://api.github.com${path4}`; - const requestHeaders = { - Accept: "application/vnd.github.v3+json", - "User-Agent": "Pullfrog-Installation-Token-Generator/1.0", - ...headers - }; - const response = await fetch(url2, { - method, - headers: requestHeaders, - ...body && { body } - }); - if (!response.ok) { - const errorText = await response.text(); - throw new Error( - `GitHub API request failed: ${response.status} ${response.statusText} -${errorText}` - ); - } - return response.json(); -}; -var checkRepositoryAccess = async (token, repoOwner, repoName) => { - try { - const response = await githubRequest("/installation/repositories", { - headers: { Authorization: `token ${token}` } - }); - return response.repositories.some( - (repo) => repo.owner.login === repoOwner && repo.name === repoName - ); - } catch { - return false; - } -}; -var createInstallationToken = async (jwt, installationId) => { - const response = await githubRequest( - `/app/installations/${installationId}/access_tokens`, - { - method: "POST", - headers: { Authorization: `Bearer ${jwt}` } - } - ); - return response.token; -}; -var findInstallationId = async (jwt, repoOwner, repoName) => { - const installations = await githubRequest("/app/installations", { - headers: { Authorization: `Bearer ${jwt}` } - }); - for (const installation of installations) { - try { - const tempToken = await createInstallationToken(jwt, installation.id); - const hasAccess = await checkRepositoryAccess(tempToken, repoOwner, repoName); - if (hasAccess) { - return installation.id; - } - } catch { - } - } - throw new Error( - `No installation found with access to ${repoOwner}/${repoName}. Ensure the GitHub App is installed on the target repository.` - ); -}; -async function acquireTokenViaGitHubApp() { - const repoContext = parseRepoContext(); - const config = { - appId: process.env.GITHUB_APP_ID, - privateKey: process.env.GITHUB_PRIVATE_KEY?.replace(/\\n/g, "\n"), - repoOwner: repoContext.owner, - repoName: repoContext.name - }; - const jwt = generateJWT(config.appId, config.privateKey); - const installationId = await findInstallationId(jwt, config.repoOwner, config.repoName); - const token = await createInstallationToken(jwt, installationId); - return token; -} -async function acquireNewToken() { - if (isGitHubActionsEnvironment()) { - return await acquireTokenViaOIDC(); - } else { - return await acquireTokenViaGitHubApp(); - } -} -async function setupGitHubInstallationToken() { - const existingToken = checkExistingToken(); - if (existingToken) { - core2.setSecret(existingToken); - log.info("Using provided GitHub installation token"); - return { githubInstallationToken: existingToken, wasAcquired: false }; - } - const acquiredToken = await acquireNewToken(); - core2.setSecret(acquiredToken); - process.env.GITHUB_INSTALLATION_TOKEN = acquiredToken; - return { githubInstallationToken: acquiredToken, wasAcquired: true }; -} -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)}` - ); - } -} -function parseRepoContext() { - const githubRepo = process.env.GITHUB_REPOSITORY; - if (!githubRepo) { - throw new Error("GITHUB_REPOSITORY environment variable is required"); - } - const [owner, name] = githubRepo.split("/"); - if (!owner || !name) { - throw new Error(`Invalid GITHUB_REPOSITORY format: ${githubRepo}. Expected 'owner/repo'`); - } - return { owner, name }; -} +// agents/gemini.ts +import { spawnSync as spawnSync3 } from "node:child_process"; // utils/subprocess.ts import { spawn as nodeSpawn } from "node:child_process"; @@ -34401,151 +34187,106 @@ async function spawn4(options) { }); } -// agents/jules.ts -var jules = agent({ - name: "jules", +// agents/gemini.ts +var gemini = agent({ + name: "gemini", inputKeys: ["google_api_key", "gemini_api_key"], install: async () => { return await installFromNpmTarball({ - packageName: "@google/jules", + packageName: "@google/gemini-cli", version: "latest", - executablePath: "run.cjs" + executablePath: "dist/index.js", + installDependencies: true }); }, - run: async ({ - prompt, - apiKey, - mcpServers: _mcpServers, - githubInstallationToken: _githubInstallationToken, - cliPath - }) => { + run: async ({ payload, apiKey, mcpServers, githubInstallationToken, cliPath }) => { + configureGeminiMcpServers({ mcpServers, cliPath }); if (!apiKey) { - throw new Error("google_api_key is required for jules agent"); + throw new Error("google_api_key or gemini_api_key is required for gemini agent"); } - process.env.GOOGLE_API_KEY = apiKey; - const repoContext = parseRepoContext(); - const repoName = `${repoContext.owner}/${repoContext.name}`; - log.info(`Creating Jules session for ${repoName}...`); - const sessionPrompt = addInstructions(prompt); - log.info(`Starting session with prompt: ${prompt.substring(0, 100)}...`); - let sessionId; + process.env.GEMINI_API_KEY = apiKey; + process.env.GITHUB_INSTALLATION_TOKEN = githubInstallationToken; + const sessionPrompt = addInstructions(payload); + log.info(`Starting Gemini CLI with prompt: ${payload.prompt.substring(0, 100)}...`); + let finalOutput = ""; try { - const createResult = await spawn4({ + const result = await spawn4({ cmd: "node", - args: [cliPath, "remote", "new", "--repo", repoName, "--session", sessionPrompt], + args: [cliPath, "--yolo", "--output-format", "text", sessionPrompt], + env: { + GEMINI_API_KEY: apiKey, + GITHUB_INSTALLATION_TOKEN: githubInstallationToken + }, onStdout: (chunk) => { - log.info(chunk.trim()); - const match2 = chunk.match(/session[:\s]+(\d+)/i) || chunk.match(/id[:\s]+(\d+)/i); - if (match2 && !sessionId) { - sessionId = match2[1]; - log.info(`\u2713 Session ID: ${sessionId}`); + const trimmed = chunk.trim(); + if (trimmed) { + log.info(trimmed); + finalOutput += trimmed + "\n"; } }, onStderr: (chunk) => { - log.warning(chunk.trim()); + const trimmed = chunk.trim(); + if (trimmed) { + log.warning(trimmed); + finalOutput += trimmed + "\n"; + } } }); - if (createResult.exitCode !== 0) { - throw new Error( - `Failed to create Jules session: ${createResult.stderr || createResult.stdout || "Unknown error"}` - ); - } - if (!sessionId) { - const output = createResult.stdout + createResult.stderr; - const match2 = output.match(/session[:\s]+(\d+)/i) || output.match(/id[:\s]+(\d+)/i); - if (match2) { - sessionId = match2[1]; - } - } - if (!sessionId) { - log.warning("Could not extract session ID from output. Session may have been created."); - log.info(`Output: ${createResult.stdout}`); - } else { - log.info(`\u2713 Session created: ${sessionId}`); + if (result.exitCode !== 0) { + const errorMessage = result.stderr || result.stdout || "Unknown error"; + log.error(`Gemini CLI exited with code ${result.exitCode}: ${errorMessage}`); + return { + success: false, + error: errorMessage, + output: finalOutput || result.stdout || "" + }; } + finalOutput = finalOutput || result.stdout || "Gemini CLI completed successfully."; + log.info("\u2713 Gemini CLI completed successfully"); + return { + success: true, + output: finalOutput + }; } catch (error2) { const errorMessage = error2 instanceof Error ? error2.message : String(error2); - log.error(`Failed to create Jules session: ${errorMessage}`); + log.error(`Failed to run Gemini CLI: ${errorMessage}`); return { success: false, error: errorMessage, - output: "" + output: finalOutput || "" }; } - log.info("Monitoring session progress..."); - let finalOutput = ""; - const maxPollAttempts = 300; - let pollAttempts = 0; - while (pollAttempts < maxPollAttempts) { - await new Promise((resolve) => setTimeout(resolve, 1e4)); - pollAttempts++; - try { - const listResult = await spawn4({ - cmd: "node", - args: [cliPath, "remote", "list", "--session"], - onStdout: (chunk) => { - const trimmed = chunk.trim(); - if (trimmed) { - log.info(trimmed); - } - } - }); - if (listResult.exitCode === 0) { - const output = listResult.stdout; - if (sessionId && output.includes(sessionId)) { - if (output.includes("completed") || output.includes("done") || output.includes("finished")) { - log.info("Session appears to be completed"); - finalOutput = "Session completed. Pulling results..."; - break; - } - } - } - } catch (error2) { - const errorMessage = error2 instanceof Error ? error2.message : String(error2); - log.warning(`Error checking session status: ${errorMessage}`); - } - } - if (sessionId) { - try { - log.info(`Pulling results for session ${sessionId}...`); - const pullResult = await spawn4({ - cmd: "node", - args: [cliPath, "remote", "pull", "--session", sessionId], - onStdout: (chunk) => { - log.info(chunk.trim()); - }, - onStderr: (chunk) => { - log.warning(chunk.trim()); - } - }); - if (pullResult.exitCode === 0) { - finalOutput = pullResult.stdout || "Results pulled successfully."; - } else { - log.warning(`Failed to pull results: ${pullResult.stderr || pullResult.stdout}`); - finalOutput = finalOutput || "Session completed. Check Jules dashboard for results."; - } - } catch (error2) { - const errorMessage = error2 instanceof Error ? error2.message : String(error2); - log.warning(`Error pulling results: ${errorMessage}`); - } - } - if (pollAttempts >= maxPollAttempts) { - log.warning("Session monitoring timeout reached. Session may still be in progress."); - finalOutput = finalOutput || "Session monitoring timeout. Check Jules dashboard for session status."; - } - return { - success: true, - output: finalOutput || "Jules session completed. Check the Jules dashboard for results." - }; } }); +function configureGeminiMcpServers({ mcpServers, cliPath }) { + for (const [serverName, serverConfig] of Object.entries(mcpServers)) { + const command = serverConfig.command; + const args2 = serverConfig.args || []; + const envVars = serverConfig.env || {}; + const addArgs = ["mcp", "add", serverName, command, ...args2]; + for (const [key, value2] of Object.entries(envVars)) { + addArgs.push("--env", `${key}=${value2}`); + } + log.info(`Adding MCP server '${serverName}'...`); + const addResult = spawnSync3("node", [cliPath, ...addArgs], { + stdio: "pipe", + encoding: "utf-8" + }); + if (addResult.status !== 0) { + throw new Error( + `gemini mcp add failed: ${addResult.stderr || addResult.stdout || "Unknown error"}` + ); + } + log.info(`\u2713 MCP server '${serverName}' configured`); + } +} // agents/index.ts var agents = { claude, codex, cursor, - jules + gemini }; // node_modules/.pnpm/@ark+schema@0.53.0/node_modules/@ark/schema/out/shared/registry.js @@ -42292,6 +42033,194 @@ var dirOfCaller = () => dirname2(filePath(caller({ methodName: "dirOfCaller", up var fromHere = (...joinWith) => join6(dirOfCaller(), ...joinWith); var fsRoot = parse(process3.cwd()).root; +// utils/github.ts +var core2 = __toESM(require_core(), 1); +import { createSign } from "node:crypto"; +function checkExistingToken() { + const inputToken = core2.getInput("github_installation_token"); + const envToken = process.env.GITHUB_INSTALLATION_TOKEN; + return inputToken || envToken || null; +} +function isGitHubActionsEnvironment() { + return Boolean(process.env.GITHUB_ACTIONS); +} +async function acquireTokenViaOIDC() { + log.info("Generating OIDC token..."); + const oidcToken = await core2.getIDToken("pullfrog-api"); + log.info("OIDC token generated successfully"); + const apiUrl = process.env.API_URL || "https://pullfrog.ai"; + log.info("Exchanging OIDC token for installation token..."); + 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) { + throw new Error(`Token exchange failed: ${tokenResponse.status} ${tokenResponse.statusText}`); + } + 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") { + throw new Error(`Token exchange timed out after ${timeoutMs}ms`); + } + throw error2; + } +} +var base64UrlEncode = (str) => { + return Buffer.from(str).toString("base64").replace(/\+/g, "-").replace(/\//g, "_").replace(/=/g, ""); +}; +var generateJWT = (appId, privateKey) => { + const now = Math.floor(Date.now() / 1e3); + const payload = { + iat: now - 60, + exp: now + 5 * 60, + iss: appId + }; + const header = { + alg: "RS256", + typ: "JWT" + }; + const encodedHeader = base64UrlEncode(JSON.stringify(header)); + const encodedPayload = base64UrlEncode(JSON.stringify(payload)); + const signaturePart = `${encodedHeader}.${encodedPayload}`; + const signature = createSign("RSA-SHA256").update(signaturePart).sign(privateKey, "base64").replace(/\+/g, "-").replace(/\//g, "_").replace(/=/g, ""); + return `${signaturePart}.${signature}`; +}; +var githubRequest = async (path4, options = {}) => { + const { method = "GET", headers = {}, body } = options; + const url2 = `https://api.github.com${path4}`; + const requestHeaders = { + Accept: "application/vnd.github.v3+json", + "User-Agent": "Pullfrog-Installation-Token-Generator/1.0", + ...headers + }; + const response = await fetch(url2, { + method, + headers: requestHeaders, + ...body && { body } + }); + if (!response.ok) { + const errorText = await response.text(); + throw new Error( + `GitHub API request failed: ${response.status} ${response.statusText} +${errorText}` + ); + } + return response.json(); +}; +var checkRepositoryAccess = async (token, repoOwner, repoName) => { + try { + const response = await githubRequest("/installation/repositories", { + headers: { Authorization: `token ${token}` } + }); + return response.repositories.some( + (repo) => repo.owner.login === repoOwner && repo.name === repoName + ); + } catch { + return false; + } +}; +var createInstallationToken = async (jwt, installationId) => { + const response = await githubRequest( + `/app/installations/${installationId}/access_tokens`, + { + method: "POST", + headers: { Authorization: `Bearer ${jwt}` } + } + ); + return response.token; +}; +var findInstallationId = async (jwt, repoOwner, repoName) => { + const installations = await githubRequest("/app/installations", { + headers: { Authorization: `Bearer ${jwt}` } + }); + for (const installation of installations) { + try { + const tempToken = await createInstallationToken(jwt, installation.id); + const hasAccess = await checkRepositoryAccess(tempToken, repoOwner, repoName); + if (hasAccess) { + return installation.id; + } + } catch { + } + } + throw new Error( + `No installation found with access to ${repoOwner}/${repoName}. Ensure the GitHub App is installed on the target repository.` + ); +}; +async function acquireTokenViaGitHubApp() { + const repoContext = parseRepoContext(); + const config = { + appId: process.env.GITHUB_APP_ID, + privateKey: process.env.GITHUB_PRIVATE_KEY?.replace(/\\n/g, "\n"), + repoOwner: repoContext.owner, + repoName: repoContext.name + }; + const jwt = generateJWT(config.appId, config.privateKey); + const installationId = await findInstallationId(jwt, config.repoOwner, config.repoName); + const token = await createInstallationToken(jwt, installationId); + return token; +} +async function acquireNewToken() { + if (isGitHubActionsEnvironment()) { + return await acquireTokenViaOIDC(); + } else { + return await acquireTokenViaGitHubApp(); + } +} +async function setupGitHubInstallationToken() { + const existingToken = checkExistingToken(); + if (existingToken) { + core2.setSecret(existingToken); + log.info("Using provided GitHub installation token"); + return { githubInstallationToken: existingToken, wasAcquired: false }; + } + const acquiredToken = await acquireNewToken(); + core2.setSecret(acquiredToken); + process.env.GITHUB_INSTALLATION_TOKEN = acquiredToken; + return { githubInstallationToken: acquiredToken, wasAcquired: true }; +} +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)}` + ); + } +} +function parseRepoContext() { + const githubRepo = process.env.GITHUB_REPOSITORY; + if (!githubRepo) { + throw new Error("GITHUB_REPOSITORY environment variable is required"); + } + const [owner, name] = githubRepo.split("/"); + if (!owner || !name) { + throw new Error(`Invalid GITHUB_REPOSITORY format: ${githubRepo}. Expected 'owner/repo'`); + } + return { owner, name }; +} + // mcp/config.ts function createMcpConfigs(githubInstallationToken) { const repoContext = parseRepoContext(); @@ -42460,7 +42389,23 @@ async function main(inputs) { log.debug(`\u{1F4CB} MCP Config: ${JSON.stringify(mcpServers, null, 2)}`); const cliPath = await agent2.install(); log.info(`Running ${agentName}...`); - log.box(inputs.prompt, { title: "Prompt" }); + let payload; + try { + const parsedPrompt = JSON.parse(inputs.prompt); + if (!("~pullfrog" in parsedPrompt)) { + throw new Error("Invalid prompt: not a pullfrog webhook payload"); + } + payload = parsedPrompt; + } catch { + payload = { + "~pullfrog": true, + agent: null, + prompt: inputs.prompt, + event: {}, + modes + }; + } + log.box(payload.prompt, { title: "Prompt" }); const matchingInputKey = agent2.inputKeys.find((inputKey) => inputs[inputKey]); if (!matchingInputKey) { throwMissingApiKeyError({ @@ -42472,7 +42417,7 @@ async function main(inputs) { } const apiKey = inputs[matchingInputKey]; const result = await agent2.run({ - prompt: inputs.prompt, + payload, mcpServers, githubInstallationToken, apiKey, diff --git a/main.ts b/main.ts index dd09ff4..1ac98a0 100644 --- a/main.ts +++ b/main.ts @@ -2,7 +2,9 @@ import { flatMorph } from "@ark/util"; import { type } from "arktype"; import { agents } from "./agents/index.ts"; import { createMcpConfigs } from "./mcp/config.ts"; +import { modes } from "./modes.ts"; import packageJson from "./package.json" with { type: "json" }; +import type { Payload } from "./payload.ts"; import { fetchRepoSettings } from "./utils/api.ts"; import { log } from "./utils/cli.ts"; import { @@ -122,11 +124,27 @@ export async function main(inputs: Inputs): Promise { const cliPath = await agent.install(); log.info(`Running ${agentName}...`); - log.box(inputs.prompt, { title: "Prompt" }); - // TODO: check if `inputs.prompts` is JSON - // if yes, check if it's a webhook payload or toJSON(github.event) - // for webhook payloads, check the specified `agent` field + let payload: Payload; + + try { + // attempt JSON parsing + const parsedPrompt = JSON.parse(inputs.prompt); + if (!("~pullfrog" in parsedPrompt)) { + throw new Error("Invalid prompt: not a pullfrog webhook payload"); + } + payload = parsedPrompt as Payload; + } catch { + payload = { + "~pullfrog": true, + agent: null, + prompt: inputs.prompt, + event: {}, + modes, + }; + } + + log.box(payload.prompt, { title: "Prompt" }); const matchingInputKey = agent.inputKeys.find((inputKey) => inputs[inputKey]); @@ -142,7 +160,7 @@ export async function main(inputs: Inputs): Promise { const apiKey = inputs[matchingInputKey]!; const result = await agent.run({ - prompt: inputs.prompt, + payload, mcpServers, githubInstallationToken, apiKey, diff --git a/mcp-server.js b/mcp-server.js index fa1d19a..1a08a48 100755 --- a/mcp-server.js +++ b/mcp-server.js @@ -97760,6 +97760,10 @@ var schema = ark.schema; var define2 = ark.define; var declare = ark.declare; +// mcp/shared.ts +import { appendFileSync } from "node:fs"; +import { join } from "node:path"; + // node_modules/.pnpm/universal-user-agent@7.0.3/node_modules/universal-user-agent/index.js function getUserAgent() { if (typeof navigator === "object" && "userAgent" in navigator) { @@ -101412,7 +101416,58 @@ var getMcpContext = cached2(() => { }) }; }); -var tool = (tool2) => tool2; +function getLogPath() { + return join(process.cwd(), "log.txt"); +} +function logToolCall({ + toolName, + request: request2, + error: error41, + success +}) { + const logPath = getLogPath(); + const timestamp = (/* @__PURE__ */ new Date()).toISOString(); + const requestStr = JSON.stringify(request2, null, 2); + let logEntry = `[${timestamp}] Tool: ${toolName} +Request: ${requestStr} +`; + if (error41) { + const errorMessage = error41 instanceof Error ? error41.message : String(error41); + const errorStack = error41 instanceof Error ? error41.stack : void 0; + logEntry += `Error: ${errorMessage} +`; + if (errorStack) { + logEntry += `Stack: ${errorStack} +`; + } + logEntry += `Status: FAILED +`; + } else if (success !== void 0) { + logEntry += `Status: ${success ? "SUCCESS" : "FAILED"} +`; + } + logEntry += `${"=".repeat(80)} + +`; + appendFileSync(logPath, logEntry, "utf-8"); +} +var tool = (toolDef) => { + const toolName = toolDef.name; + const originalExecute = toolDef.execute; + toolDef.execute = async (args2, context) => { + try { + logToolCall({ toolName, request: args2 }); + const result = await originalExecute(args2, context); + const isError = result && typeof result === "object" && "isError" in result && result.isError === true; + logToolCall({ toolName, request: args2, success: !isError }); + return result; + } catch (error41) { + logToolCall({ toolName, request: args2, error: error41 }); + throw error41; + } + }; + return toolDef; +}; var addTools = (server2, tools) => { for (const tool2 of tools) { server2.addTool(tool2); diff --git a/modes.ts b/modes.ts index 4d9e8b8..05a469e 100644 --- a/modes.ts +++ b/modes.ts @@ -1,6 +1,12 @@ import { ghPullfrogMcpName } from "./mcp/index.ts"; -export const modes = [ +export interface Mode { + name: string; + description: string; + prompt: string; +} + +export const modes: Mode[] = [ { name: "Plan", description: @@ -51,4 +57,4 @@ export const modes = [ 3. As your work progresses, update your Working Comment to share progress and results using ${ghPullfrogMcpName}/update_working_comment. Do not create additional comments unless you are explicitly asked to do so. 4. When you finish the task, update the Working Comment a final time with a summary of the results and links to any created issues, PRs, etc.`, }, -] as const; +]; diff --git a/package.json b/package.json index 9eed737..d26fcdf 100644 --- a/package.json +++ b/package.json @@ -24,12 +24,13 @@ }, "dependencies": { "@actions/core": "^1.11.1", + "@actions/github": "^6.0.1", "@anthropic-ai/claude-agent-sdk": "0.1.37", - "@openai/codex-sdk": "0.58.0", "@ark/fs": "0.53.0", "@ark/util": "0.53.0", "@octokit/rest": "^22.0.0", "@octokit/webhooks-types": "^7.6.1", + "@openai/codex-sdk": "0.58.0", "@standard-schema/spec": "1.0.0", "arktype": "2.1.25", "dotenv": "^17.2.3", diff --git a/payload.ts b/payload.ts new file mode 100644 index 0000000..4d46aa2 --- /dev/null +++ b/payload.ts @@ -0,0 +1,37 @@ +/** May be a `github.event` payload that has been stringified. This case needs to be detected and handled appropriately. */ + +import type { Mode } from "./modes.ts"; + +// type Payload = GithubEventPayload | WorkflowDispatchPayload; +// type GithubEventPayload = Record; + +export type Payload = { + "~pullfrog": true; + + /** + * Agent slug identifier (e.g., "claude", "codex", "gemini") + */ + readonly agent: string | null; + + /** + * The prompt/instructions for the agent to execute + */ + readonly prompt: string; + + /** + * Event data from webhook payload. + */ + readonly event: object; + + /** + * Execution mode configuration + */ + modes: readonly Mode[]; + + /** + * Optional IDs of the issue, PR, or comment that the agent is working on + */ + readonly comment_id?: number | null; + readonly issue_id?: number | null; + readonly pr_id?: number | null; +}; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 5b98aa0..0555a11 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -11,6 +11,9 @@ importers: '@actions/core': specifier: ^1.11.1 version: 1.11.1 + '@actions/github': + specifier: ^6.0.1 + version: 6.0.1 '@anthropic-ai/claude-agent-sdk': specifier: 0.1.37 version: 0.1.37(zod@3.25.76) @@ -75,6 +78,9 @@ packages: '@actions/exec@1.1.1': resolution: {integrity: sha512-+sCcHHbVdk93a0XT19ECtO/gIXoxvdsgQLzb2fE2/5sIZmWQuluYyjPQtrtTHdU1YzTZ7bAPN4sITq2xi1679w==} + '@actions/github@6.0.1': + resolution: {integrity: sha512-xbZVcaqD4XnQAe35qSQqskb3SqIAfRyLBrHMd/8TuL7hJSz2QtbDwnNM8zWx4zO5l2fnGtseNE3MbEvD7BxVMw==} + '@actions/http-client@2.2.3': resolution: {integrity: sha512-mx8hyJi/hjFvbPokCg4uRd4ZX78t+YyRPtnKWwIl+RzNaVuFpQHfmlGVfsKEJN8LwTCvL+DfVgAM04XaHkm6bA==} @@ -324,10 +330,18 @@ packages: resolution: {integrity: sha512-kOQ4+fHuT4KbR2iq2IjeV32HiihueuOf1vJkq18z08CLZ1UQrTc8BXJpVfxZkq45+inLLD+D4xx4nBjUelJa4Q==} engines: {node: '>=18'} + '@octokit/auth-token@4.0.0': + resolution: {integrity: sha512-tY/msAuJo6ARbK6SPIxZrPBms3xPbfwBrulZe0Wtr/DIY9lje2HeV1uoebShn6mx7SjCHif6EjMvoREj+gZ+SA==} + engines: {node: '>= 18'} + '@octokit/auth-token@6.0.0': resolution: {integrity: sha512-P4YJBPdPSpWTQ1NU4XYdvHvXJJDxM6YwpS0FZHRgP7YFkdVxsWcpWGy/NVqlAA7PcPCnMacXlRm1y2PFZRWL/w==} engines: {node: '>= 20'} + '@octokit/core@5.2.2': + resolution: {integrity: sha512-/g2d4sW9nUDJOMz3mabVQvOGhVa4e/BN/Um7yca9Bb2XTzPPnfTWHWQg+IsEYO7M3Vx+EXvaM/I2pJWIMun1bg==} + engines: {node: '>= 18'} + '@octokit/core@7.0.5': resolution: {integrity: sha512-t54CUOsFMappY1Jbzb7fetWeO0n6K0k/4+/ZpkS+3Joz8I4VcvY9OiEBFRYISqaI2fq5sCiPtAjRDOzVYG8m+Q==} engines: {node: '>= 20'} @@ -336,10 +350,24 @@ packages: resolution: {integrity: sha512-7P1dRAZxuWAOPI7kXfio88trNi/MegQ0IJD3vfgC3b+LZo1Qe6gRJc2v0mz2USWWJOKrB2h5spXCzGbw+fAdqA==} engines: {node: '>= 20'} + '@octokit/endpoint@9.0.6': + resolution: {integrity: sha512-H1fNTMA57HbkFESSt3Y9+FBICv+0jFceJFPWDePYlR/iMGrwM5ph+Dd4XRQs+8X+PUFURLQgX9ChPfhJ/1uNQw==} + engines: {node: '>= 18'} + + '@octokit/graphql@7.1.1': + resolution: {integrity: sha512-3mkDltSfcDUoa176nlGoA32RGjeWjl3K7F/BwHwRMJUW/IteSa4bnSV8p2ThNkcIcZU2umkZWxwETSSCJf2Q7g==} + engines: {node: '>= 18'} + '@octokit/graphql@9.0.2': resolution: {integrity: sha512-iz6KzZ7u95Fzy9Nt2L8cG88lGRMr/qy1Q36ih/XVzMIlPDMYwaNLE/ENhqmIzgPrlNWiYJkwmveEetvxAgFBJw==} engines: {node: '>= 20'} + '@octokit/openapi-types@20.0.0': + resolution: {integrity: sha512-EtqRBEjp1dL/15V7WiX5LJMIxxkdiGJnabzYx5Apx4FkQIFgAfKumXeYAqqJCj1s+BMX4cPFIFC4OLCR6stlnA==} + + '@octokit/openapi-types@24.2.0': + resolution: {integrity: sha512-9sIH3nSUttelJSXUrmGzl7QUBFul0/mB8HRYl3fOlgHbIWG+WnYDXU3v/2zMtAvuzZ/ed00Ei6on975FhBfzrg==} + '@octokit/openapi-types@26.0.0': resolution: {integrity: sha512-7AtcfKtpo77j7Ts73b4OWhOZHTKo/gGY8bB3bNBQz4H+GRSWqx2yvj8TXRsbdTE0eRmYmXOEY66jM7mJ7LzfsA==} @@ -349,18 +377,34 @@ packages: peerDependencies: '@octokit/core': '>=6' + '@octokit/plugin-paginate-rest@9.2.2': + resolution: {integrity: sha512-u3KYkGF7GcZnSD/3UP0S7K5XUFT2FkOQdcfXZGZQPGv3lm4F2Xbf71lvjldr8c1H3nNbF+33cLEkWYbokGWqiQ==} + engines: {node: '>= 18'} + peerDependencies: + '@octokit/core': '5' + '@octokit/plugin-request-log@6.0.0': resolution: {integrity: sha512-UkOzeEN3W91/eBq9sPZNQ7sUBvYCqYbrrD8gTbBuGtHEuycE4/awMXcYvx6sVYo7LypPhmQwwpUe4Yyu4QZN5Q==} engines: {node: '>= 20'} peerDependencies: '@octokit/core': '>=6' + '@octokit/plugin-rest-endpoint-methods@10.4.1': + resolution: {integrity: sha512-xV1b+ceKV9KytQe3zCVqjg+8GTGfDYwaT1ATU5isiUyVtlVAO3HNdzpS4sr4GBx4hxQ46s7ITtZrAsxG22+rVg==} + engines: {node: '>= 18'} + peerDependencies: + '@octokit/core': '5' + '@octokit/plugin-rest-endpoint-methods@16.1.0': resolution: {integrity: sha512-nCsyiKoGRnhH5LkH8hJEZb9swpqOcsW+VXv1QoyUNQXJeVODG4+xM6UICEqyqe9XFr6LkL8BIiFCPev8zMDXPw==} engines: {node: '>= 20'} peerDependencies: '@octokit/core': '>=6' + '@octokit/request-error@5.1.1': + resolution: {integrity: sha512-v9iyEQJH6ZntoENr9/yXxjuezh4My67CBSu9r6Ve/05Iu5gNgnisNWOsoJHTP6k0Rr0+HQIpnH+kyammu90q/g==} + engines: {node: '>= 18'} + '@octokit/request-error@7.0.1': resolution: {integrity: sha512-CZpFwV4+1uBrxu7Cw8E5NCXDWFNf18MSY23TdxCBgjw1tXXHvTrZVsXlW8hgFTOLw8RQR1BBrMvYRtuyaijHMA==} engines: {node: '>= 20'} @@ -369,10 +413,20 @@ packages: resolution: {integrity: sha512-TXnouHIYLtgDhKo+N6mXATnDBkV05VwbR0TtMWpgTHIoQdRQfCSzmy/LGqR1AbRMbijq/EckC/E3/ZNcU92NaQ==} engines: {node: '>= 20'} + '@octokit/request@8.4.1': + resolution: {integrity: sha512-qnB2+SY3hkCmBxZsR/MPCybNmbJe4KAlfWErXq+rBKkQJlbjdJeS85VI9r8UqeLYLvnAenU8Q1okM/0MBsAGXw==} + engines: {node: '>= 18'} + '@octokit/rest@22.0.0': resolution: {integrity: sha512-z6tmTu9BTnw51jYGulxrlernpsQYXpui1RK21vmXn8yF5bp6iX16yfTtJYGK5Mh1qDkvDOmp2n8sRMcQmR8jiA==} engines: {node: '>= 20'} + '@octokit/types@12.6.0': + resolution: {integrity: sha512-1rhSOfRa6H9w4YwK0yrf5faDaDTb+yLyBUKOCV4xtCDB5VmIPqd/v9yr9o6SAzOAlRxMiRiCic6JVM1/kunVkw==} + + '@octokit/types@13.10.0': + resolution: {integrity: sha512-ifLaO34EbbPj0Xgro4G5lP5asESjwHracYJvVaPIyXMuiuXLlhic3S47cBdTb+jfODkTE5YtGCLt3Ay3+J97sA==} + '@octokit/types@15.0.0': resolution: {integrity: sha512-8o6yDfmoGJUIeR9OfYU0/TUJTnMPG2r68+1yEdUeG2Fdqpj8Qetg0ziKIgcBm0RW/j29H41WP37CYCEhp6GoHQ==} @@ -442,6 +496,9 @@ packages: resolution: {integrity: sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==} engines: {node: '>=8'} + before-after-hook@2.2.3: + resolution: {integrity: sha512-NzUnlZexiaH/46WDhANlyR2bXRopNg4F/zuSA3OpZnllCUgRaOF2znDioDWrmbNVsuZk6l9pMquQB38cfBZwkQ==} + before-after-hook@4.0.0: resolution: {integrity: sha512-q6tR3RPqIB1pMiTRMFcZwuG5T8vwp+vUvEG0vuI6B+Rikh5BfPp2fQ82c925FOs+b0lcFQ8CFrL+KbilfZFhOQ==} @@ -509,6 +566,9 @@ packages: resolution: {integrity: sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==} engines: {node: '>= 0.8'} + deprecation@2.3.1: + resolution: {integrity: sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ==} + dotenv@17.2.3: resolution: {integrity: sha512-JVUnt+DUIzu87TABbhPmNfVdBDt18BLOWjMUFJMSi/Qqg7NTYtabbvSNJGOJ7afbRuv9D/lngizHtP7QyLQ+9w==} engines: {node: '>=12'} @@ -953,6 +1013,9 @@ packages: resolution: {integrity: sha512-+QBBXBCvifc56fsbuxZQ6Sic3wqqc3WWaqxs58gvJrcOuN83HGTCwz3oS5phzU9LthRNE9VrJCFCLUgHeeFnfA==} engines: {node: '>=18'} + universal-user-agent@6.0.1: + resolution: {integrity: sha512-yCzhz6FN2wU1NiiQRogkTQszlQSlpWaw8SvVegAc+bDxbzHgh1vX8uIe8OYyMH6DwH+sdTJsgMl36+mSMdRJIQ==} + universal-user-agent@7.0.3: resolution: {integrity: sha512-TmnEAEAsBJVZM/AADELsK76llnwcf9vMKuPz8JflO1frO8Lchitr0fNaN9d+Ap0BjKtqWqd/J17qeDnXh8CL2A==} @@ -1040,6 +1103,16 @@ snapshots: dependencies: '@actions/io': 1.1.3 + '@actions/github@6.0.1': + dependencies: + '@actions/http-client': 2.2.3 + '@octokit/core': 5.2.2 + '@octokit/plugin-paginate-rest': 9.2.2(@octokit/core@5.2.2) + '@octokit/plugin-rest-endpoint-methods': 10.4.1(@octokit/core@5.2.2) + '@octokit/request': 8.4.1 + '@octokit/request-error': 5.1.1 + undici: 5.29.0 + '@actions/http-client@2.2.3': dependencies: tunnel: 0.0.6 @@ -1208,8 +1281,20 @@ snapshots: transitivePeerDependencies: - supports-color + '@octokit/auth-token@4.0.0': {} + '@octokit/auth-token@6.0.0': {} + '@octokit/core@5.2.2': + dependencies: + '@octokit/auth-token': 4.0.0 + '@octokit/graphql': 7.1.1 + '@octokit/request': 8.4.1 + '@octokit/request-error': 5.1.1 + '@octokit/types': 13.10.0 + before-after-hook: 2.2.3 + universal-user-agent: 6.0.1 + '@octokit/core@7.0.5': dependencies: '@octokit/auth-token': 6.0.0 @@ -1225,12 +1310,27 @@ snapshots: '@octokit/types': 15.0.0 universal-user-agent: 7.0.3 + '@octokit/endpoint@9.0.6': + dependencies: + '@octokit/types': 13.10.0 + universal-user-agent: 6.0.1 + + '@octokit/graphql@7.1.1': + dependencies: + '@octokit/request': 8.4.1 + '@octokit/types': 13.10.0 + universal-user-agent: 6.0.1 + '@octokit/graphql@9.0.2': dependencies: '@octokit/request': 10.0.5 '@octokit/types': 15.0.0 universal-user-agent: 7.0.3 + '@octokit/openapi-types@20.0.0': {} + + '@octokit/openapi-types@24.2.0': {} + '@octokit/openapi-types@26.0.0': {} '@octokit/plugin-paginate-rest@13.2.0(@octokit/core@7.0.5)': @@ -1238,15 +1338,31 @@ snapshots: '@octokit/core': 7.0.5 '@octokit/types': 15.0.0 + '@octokit/plugin-paginate-rest@9.2.2(@octokit/core@5.2.2)': + dependencies: + '@octokit/core': 5.2.2 + '@octokit/types': 12.6.0 + '@octokit/plugin-request-log@6.0.0(@octokit/core@7.0.5)': dependencies: '@octokit/core': 7.0.5 + '@octokit/plugin-rest-endpoint-methods@10.4.1(@octokit/core@5.2.2)': + dependencies: + '@octokit/core': 5.2.2 + '@octokit/types': 12.6.0 + '@octokit/plugin-rest-endpoint-methods@16.1.0(@octokit/core@7.0.5)': dependencies: '@octokit/core': 7.0.5 '@octokit/types': 15.0.0 + '@octokit/request-error@5.1.1': + dependencies: + '@octokit/types': 13.10.0 + deprecation: 2.3.1 + once: 1.4.0 + '@octokit/request-error@7.0.1': dependencies: '@octokit/types': 15.0.0 @@ -1259,6 +1375,13 @@ snapshots: fast-content-type-parse: 3.0.0 universal-user-agent: 7.0.3 + '@octokit/request@8.4.1': + dependencies: + '@octokit/endpoint': 9.0.6 + '@octokit/request-error': 5.1.1 + '@octokit/types': 13.10.0 + universal-user-agent: 6.0.1 + '@octokit/rest@22.0.0': dependencies: '@octokit/core': 7.0.5 @@ -1266,6 +1389,14 @@ snapshots: '@octokit/plugin-request-log': 6.0.0(@octokit/core@7.0.5) '@octokit/plugin-rest-endpoint-methods': 16.1.0(@octokit/core@7.0.5) + '@octokit/types@12.6.0': + dependencies: + '@octokit/openapi-types': 20.0.0 + + '@octokit/types@13.10.0': + dependencies: + '@octokit/openapi-types': 24.2.0 + '@octokit/types@15.0.0': dependencies: '@octokit/openapi-types': 26.0.0 @@ -1337,6 +1468,8 @@ snapshots: astral-regex@2.0.0: {} + before-after-hook@2.2.3: {} + before-after-hook@4.0.0: {} body-parser@2.2.0: @@ -1404,6 +1537,8 @@ snapshots: depd@2.0.0: {} + deprecation@2.3.1: {} + dotenv@17.2.3: {} dunder-proto@1.0.1: @@ -1889,6 +2024,8 @@ snapshots: unicorn-magic@0.3.0: {} + universal-user-agent@6.0.1: {} + universal-user-agent@7.0.3: {} unpipe@1.0.0: {}