Switch to payload

This commit is contained in:
Colin McDonnell
2025-11-18 23:15:44 -08:00
parent 3ef1635bb6
commit 06a19567c0
13 changed files with 614 additions and 410 deletions
+2 -2
View File
@@ -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,
+2 -2
View File
@@ -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 = "";
+2 -2
View File
@@ -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];
+3 -3
View File
@@ -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 {
+12 -8
View File
@@ -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}`;
+2 -1
View File
@@ -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<string, McpStdioServerConfig>;
cliPath: string;
}
+328 -383
View File
@@ -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,
+23 -5
View File
@@ -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<MainResult> {
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<MainResult> {
const apiKey = inputs[matchingInputKey]!;
const result = await agent.run({
prompt: inputs.prompt,
payload,
mcpServers,
githubInstallationToken,
apiKey,
+56 -1
View File
@@ -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);
+8 -2
View File
@@ -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;
];
+2 -1
View File
@@ -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",
+37
View File
@@ -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<string, any>;
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;
};
+137
View File
@@ -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: {}