Updates
This commit is contained in:
@@ -100536,11 +100536,8 @@ function buildRuntimeContext(repo) {
|
||||
}
|
||||
return lines.join("\n");
|
||||
}
|
||||
var addInstructions = ({
|
||||
payload,
|
||||
repo,
|
||||
useNativeBash = false
|
||||
}) => {
|
||||
var addInstructions = ({ payload, repo }) => {
|
||||
const useNativeBash = !repo.isPublic;
|
||||
let encodedEvent = "";
|
||||
const eventKeys = Object.keys(payload.event);
|
||||
if (eventKeys.length === 1 && eventKeys[0] === "trigger") {
|
||||
@@ -104754,6 +104751,7 @@ var claude = agent({
|
||||
delete process.env.ANTHROPIC_API_KEY;
|
||||
const prompt = addInstructions({ payload, repo });
|
||||
log.group("Full prompt", () => log.info(prompt));
|
||||
const disallowedTools = repo.isPublic ? ["Bash"] : [];
|
||||
const sandboxOptions = payload.sandbox ? {
|
||||
permissionMode: "default",
|
||||
disallowedTools: ["Bash", "WebSearch", "WebFetch", "Write"],
|
||||
@@ -104764,7 +104762,7 @@ var claude = agent({
|
||||
}
|
||||
} : {
|
||||
permissionMode: "bypassPermissions",
|
||||
disallowedTools: ["Bash"]
|
||||
disallowedTools
|
||||
};
|
||||
if (payload.sandbox) {
|
||||
log.info("\u{1F512} sandbox mode enabled: restricting to read-only operations");
|
||||
@@ -105229,6 +105227,11 @@ var codex = agent({
|
||||
if (payload.sandbox) {
|
||||
log.info("\u{1F512} sandbox mode enabled: restricting to read-only operations");
|
||||
}
|
||||
if (repo.isPublic) {
|
||||
log.info(
|
||||
"\u{1F512} public repo: instructions direct to MCP bash (native shell NOT disabled in SDK)"
|
||||
);
|
||||
}
|
||||
const codex2 = new Codex(codexOptions);
|
||||
const thread = codex2.startThread(
|
||||
payload.sandbox ? {
|
||||
@@ -105243,7 +105246,7 @@ var codex = agent({
|
||||
}
|
||||
);
|
||||
try {
|
||||
const streamedTurn = await thread.runStreamed(addInstructions({ payload, repo, useNativeBash: true }));
|
||||
const streamedTurn = await thread.runStreamed(addInstructions({ payload, repo }));
|
||||
let finalOutput2 = "";
|
||||
for await (const event of streamedTurn.events) {
|
||||
const handler2 = messageHandlers2[event.type];
|
||||
@@ -105387,7 +105390,7 @@ var cursor = agent({
|
||||
},
|
||||
run: async ({ payload, apiKey, cliPath, mcpServers, repo }) => {
|
||||
configureCursorMcpServers({ mcpServers, cliPath });
|
||||
configureCursorSandbox({ sandbox: payload.sandbox ?? false });
|
||||
configureCursorSandbox({ sandbox: payload.sandbox ?? false, isPublicRepo: repo.isPublic });
|
||||
const loggedModelCallIds = /* @__PURE__ */ new Set();
|
||||
const messageHandlers5 = {
|
||||
system: (_event) => {
|
||||
@@ -105552,24 +105555,30 @@ function configureCursorMcpServers({ mcpServers }) {
|
||||
writeFileSync(mcpConfigPath, JSON.stringify({ mcpServers: cursorMcpServers }, null, 2), "utf-8");
|
||||
log.info(`\xBB MCP config written to ${mcpConfigPath}`);
|
||||
}
|
||||
function configureCursorSandbox({ sandbox }) {
|
||||
function configureCursorSandbox({
|
||||
sandbox,
|
||||
isPublicRepo
|
||||
}) {
|
||||
const realHome = homedir2();
|
||||
const cursorConfigDir = join7(realHome, ".config", "cursor");
|
||||
const cliConfigPath = join7(cursorConfigDir, "cli-config.json");
|
||||
mkdirSync4(cursorConfigDir, { recursive: true });
|
||||
const denyShell = isPublicRepo ? ["Shell(*)"] : [];
|
||||
const config4 = sandbox ? {
|
||||
permissions: {
|
||||
allow: ["Read(**)"],
|
||||
deny: ["Write(**)", "Shell(*)"]
|
||||
deny: ["Write(**)", ...denyShell]
|
||||
}
|
||||
} : {
|
||||
permissions: {
|
||||
allow: ["Read(**)", "Write(**)"],
|
||||
deny: ["Shell(*)"]
|
||||
deny: denyShell
|
||||
}
|
||||
};
|
||||
writeFileSync(cliConfigPath, JSON.stringify(config4, null, 2), "utf-8");
|
||||
log.info(`\xBB CLI config written to ${cliConfigPath} (sandbox: ${sandbox})`);
|
||||
log.info(
|
||||
`\xBB CLI config written to ${cliConfigPath} (sandbox: ${sandbox}, isPublicRepo: ${isPublicRepo})`
|
||||
);
|
||||
}
|
||||
|
||||
// agents/gemini.ts
|
||||
@@ -105737,21 +105746,29 @@ var gemini = agent({
|
||||
});
|
||||
},
|
||||
run: async ({ payload, apiKey, mcpServers, cliPath, repo }) => {
|
||||
configureGeminiMcpServers({ mcpServers, cliPath });
|
||||
configureGeminiMcpServers({ mcpServers, isPublicRepo: repo.isPublic });
|
||||
if (!apiKey) {
|
||||
throw new Error("google_api_key or gemini_api_key is required for gemini agent");
|
||||
}
|
||||
const sessionPrompt = addInstructions({ payload, repo, useNativeBash: true });
|
||||
const sessionPrompt = addInstructions({ payload, repo });
|
||||
log.group("Full prompt", () => log.info(sessionPrompt));
|
||||
const args3 = payload.sandbox ? [
|
||||
"--allowed-tools",
|
||||
"read_file,list_directory,search_file_content,glob,save_memory,write_todos",
|
||||
"--allowed-mcp-server-names",
|
||||
"gh_pullfrog",
|
||||
"--output-format=stream-json",
|
||||
"-p",
|
||||
sessionPrompt
|
||||
] : ["--yolo", "--output-format=stream-json", "-p", sessionPrompt];
|
||||
let args3;
|
||||
if (payload.sandbox) {
|
||||
args3 = [
|
||||
"--allowed-tools",
|
||||
"read_file,list_directory,search_file_content,glob,save_memory,write_todos",
|
||||
"--allowed-mcp-server-names",
|
||||
"gh_pullfrog",
|
||||
"--output-format=stream-json",
|
||||
"-p",
|
||||
sessionPrompt
|
||||
];
|
||||
} else {
|
||||
args3 = ["--yolo", "--output-format=stream-json", "-p", sessionPrompt];
|
||||
if (repo.isPublic) {
|
||||
log.info("\u{1F512} public repo: native shell disabled via excludeTools, using MCP bash");
|
||||
}
|
||||
}
|
||||
if (payload.sandbox) {
|
||||
log.info("\u{1F512} sandbox mode enabled: restricting to read-only operations");
|
||||
}
|
||||
@@ -105818,7 +105835,7 @@ var gemini = agent({
|
||||
}
|
||||
}
|
||||
});
|
||||
function configureGeminiMcpServers({ mcpServers }) {
|
||||
function configureGeminiMcpServers({ mcpServers, isPublicRepo }) {
|
||||
const realHome = homedir3();
|
||||
const geminiConfigDir = join8(realHome, ".gemini");
|
||||
const settingsPath = join8(geminiConfigDir, "settings.json");
|
||||
@@ -105847,6 +105864,9 @@ function configureGeminiMcpServers({ mcpServers }) {
|
||||
...existingSettings,
|
||||
mcpServers: geminiMcpServers
|
||||
};
|
||||
if (isPublicRepo) {
|
||||
newSettings.excludeTools = ["run_shell_command"];
|
||||
}
|
||||
writeFileSync2(settingsPath, JSON.stringify(newSettings, null, 2), "utf-8");
|
||||
log.info(`\xBB MCP config written to ${settingsPath}`);
|
||||
}
|
||||
@@ -105868,7 +105888,11 @@ var opencode = agent({
|
||||
const tempHome = process.env.PULLFROG_TEMP_DIR;
|
||||
const configDir = join9(tempHome, ".config", "opencode");
|
||||
mkdirSync6(configDir, { recursive: true });
|
||||
configureOpenCode({ mcpServers, sandbox: payload.sandbox ?? false });
|
||||
configureOpenCode({
|
||||
mcpServers,
|
||||
sandbox: payload.sandbox ?? false,
|
||||
isPublicRepo: repo.isPublic
|
||||
});
|
||||
const prompt = addInstructions({ payload, repo });
|
||||
log.group("Full prompt", () => log.info(prompt));
|
||||
const args3 = ["run", prompt, "--format", "json"];
|
||||
@@ -105985,7 +106009,7 @@ var opencode = agent({
|
||||
};
|
||||
}
|
||||
});
|
||||
function configureOpenCode({ mcpServers, sandbox }) {
|
||||
function configureOpenCode({ mcpServers, sandbox, isPublicRepo }) {
|
||||
const tempHome = process.env.PULLFROG_TEMP_DIR;
|
||||
const configDir = join9(tempHome, ".config", "opencode");
|
||||
mkdirSync6(configDir, { recursive: true });
|
||||
@@ -106005,7 +106029,20 @@ function configureOpenCode({ mcpServers, sandbox }) {
|
||||
url: serverConfig.url
|
||||
};
|
||||
}
|
||||
const permission = sandbox ? { edit: "deny", bash: "deny", webfetch: "deny", doom_loop: "allow", external_directory: "allow" } : { edit: "allow", bash: "deny", webfetch: "allow", doom_loop: "allow", external_directory: "allow" };
|
||||
const bashPermission = isPublicRepo ? "deny" : "allow";
|
||||
const permission = sandbox ? {
|
||||
edit: "deny",
|
||||
bash: "deny",
|
||||
webfetch: "deny",
|
||||
doom_loop: "allow",
|
||||
external_directory: "allow"
|
||||
} : {
|
||||
edit: "allow",
|
||||
bash: bashPermission,
|
||||
webfetch: "allow",
|
||||
doom_loop: "allow",
|
||||
external_directory: "allow"
|
||||
};
|
||||
const config4 = {
|
||||
mcp: opencodeMcpServers,
|
||||
permission
|
||||
@@ -137976,38 +138013,24 @@ var BashParams = type({
|
||||
"timeout?": "number",
|
||||
"working_directory?": "string"
|
||||
});
|
||||
var SENSITIVE_PATTERNS = [
|
||||
/_KEY$/i,
|
||||
/_SECRET$/i,
|
||||
/_TOKEN$/i,
|
||||
/PASSWORD/i,
|
||||
/CREDENTIAL/i,
|
||||
/AUTH/i,
|
||||
/^ANTHROPIC/i,
|
||||
/^OPENAI/i,
|
||||
/^GEMINI/i,
|
||||
/^GOOGLE/i,
|
||||
/^CLAUDE/i,
|
||||
/^CODEX/i,
|
||||
/^CURSOR/i,
|
||||
/^AWS/i,
|
||||
/^GITHUB_TOKEN$/i,
|
||||
/^GH_TOKEN$/i
|
||||
];
|
||||
var SENSITIVE_PATTERNS = [/_KEY$/i, /_SECRET$/i, /_TOKEN$/i, /_PASSWORD$/i, /_CREDENTIAL$/i];
|
||||
function isSensitive(key) {
|
||||
return SENSITIVE_PATTERNS.some((p) => p.test(key));
|
||||
}
|
||||
function filterEnv() {
|
||||
function filterEnv(isPublicRepo) {
|
||||
const filtered = {};
|
||||
for (const [key, value2] of Object.entries(process.env)) {
|
||||
if (value2 !== void 0 && !isSensitive(key)) filtered[key] = value2;
|
||||
if (value2 === void 0) continue;
|
||||
if (isPublicRepo && isSensitive(key)) continue;
|
||||
filtered[key] = value2;
|
||||
}
|
||||
return filtered;
|
||||
}
|
||||
function spawnSandboxed(command, options) {
|
||||
const stdio = ["ignore", "pipe", "pipe"];
|
||||
const spawnOpts = { env: options.env, cwd: options.cwd, stdio, detached: true };
|
||||
return process.env.CI === "true" ? spawn5("unshare", ["--pid", "--fork", "--mount-proc", "bash", "-c", command], spawnOpts) : spawn5("bash", ["-c", command], spawnOpts);
|
||||
const useNamespaceIsolation = process.env.CI === "true" && options.isPublicRepo;
|
||||
return useNamespaceIsolation ? spawn5("unshare", ["--pid", "--fork", "--mount-proc", "bash", "-c", command], spawnOpts) : spawn5("bash", ["-c", command], spawnOpts);
|
||||
}
|
||||
async function killProcessGroup(proc) {
|
||||
if (!proc.pid) return;
|
||||
@@ -138022,21 +138045,27 @@ async function killProcessGroup(proc) {
|
||||
}
|
||||
}
|
||||
}
|
||||
function BashTool(_ctx) {
|
||||
function BashTool(ctx) {
|
||||
const isPublicRepo = !ctx.repo.private;
|
||||
return tool({
|
||||
name: "bash",
|
||||
description: `Execute shell commands securely. Environment is filtered to remove API keys and secrets.
|
||||
description: `Execute shell commands securely.${isPublicRepo ? " Environment is filtered to remove API keys and secrets." : ""}
|
||||
|
||||
Use this tool to:
|
||||
- Run shell commands (ls, cat, grep, find, etc.)
|
||||
- Execute build tools (npm, pnpm, cargo, make, etc.)
|
||||
- Run tests and linters
|
||||
- Perform git operations`,
|
||||
- Perform git operations
|
||||
${isPublicRepo ? "\nThe command runs in a bash shell with a filtered environment that excludes sensitive variables like API keys and tokens." : ""}`,
|
||||
parameters: BashParams,
|
||||
execute: execute(async (params) => {
|
||||
const timeout = Math.min(params.timeout ?? 12e4, 6e5);
|
||||
const cwd2 = params.working_directory ?? process.cwd();
|
||||
const proc = spawnSandboxed(params.command, { env: filterEnv(), cwd: cwd2 });
|
||||
const proc = spawnSandboxed(params.command, {
|
||||
env: filterEnv(isPublicRepo),
|
||||
cwd: cwd2,
|
||||
isPublicRepo
|
||||
});
|
||||
let stdout = "", stderr = "", timedOut = false, exited = false;
|
||||
proc.stdout?.on("data", (chunk) => {
|
||||
stdout += chunk.toString();
|
||||
@@ -138609,7 +138638,8 @@ ${encode(eventWithoutContext)}`;
|
||||
repo: {
|
||||
owner: ctx.owner,
|
||||
name: ctx.name,
|
||||
defaultBranch: ctx.repo.default_branch
|
||||
defaultBranch: ctx.repo.default_branch,
|
||||
isPublic: !ctx.repo.private
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user