pullfrog 0.1.57 → 0.1.60
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/agents/codex.d.ts +1 -0
- package/dist/agents/index.d.ts +1 -0
- package/dist/agents/opencodeShared.d.ts +24 -0
- package/dist/agents/shared.d.ts +7 -0
- package/dist/cli.mjs +1216 -928
- package/dist/external.d.ts +2 -1
- package/dist/index.js +1197 -454
- package/dist/internal.js +31 -3
- package/dist/models.d.ts +42 -1
- package/dist/utils/activity.d.ts +12 -0
- package/dist/utils/agent.d.ts +10 -0
- package/dist/utils/codexHome.d.ts +27 -0
- package/dist/utils/credentialCheck.d.ts +7 -1
- package/dist/utils/roleMirror.d.ts +14 -0
- package/dist/utils/runContext.d.ts +1 -0
- package/package.json +3 -1
package/dist/index.js
CHANGED
|
@@ -3682,7 +3682,7 @@ var require_util2 = __commonJS({
|
|
|
3682
3682
|
"use strict";
|
|
3683
3683
|
var { redirectStatusSet, referrerPolicySet: referrerPolicyTokens, badPortsSet } = require_constants2();
|
|
3684
3684
|
var { getGlobalOrigin } = require_global();
|
|
3685
|
-
var { performance:
|
|
3685
|
+
var { performance: performance10 } = __require("perf_hooks");
|
|
3686
3686
|
var { isBlobLike, toUSVString, ReadableStreamFrom } = require_util();
|
|
3687
3687
|
var assert3 = __require("assert");
|
|
3688
3688
|
var { isUint8Array } = __require("util/types");
|
|
@@ -3845,7 +3845,7 @@ var require_util2 = __commonJS({
|
|
|
3845
3845
|
}
|
|
3846
3846
|
}
|
|
3847
3847
|
function coarsenedSharedCurrentTime(crossOriginIsolatedCapability) {
|
|
3848
|
-
return
|
|
3848
|
+
return performance10.now();
|
|
3849
3849
|
}
|
|
3850
3850
|
function createOpaqueTimingInfo(timingInfo) {
|
|
3851
3851
|
return {
|
|
@@ -19826,14 +19826,14 @@ Support boolean input list: \`true | True | TRUE | false | False | FALSE\``);
|
|
|
19826
19826
|
});
|
|
19827
19827
|
}
|
|
19828
19828
|
exports.group = group2;
|
|
19829
|
-
function
|
|
19829
|
+
function saveState3(name, value2) {
|
|
19830
19830
|
const filePath = process.env["GITHUB_STATE"] || "";
|
|
19831
19831
|
if (filePath) {
|
|
19832
19832
|
return (0, file_command_1.issueFileCommand)("STATE", (0, file_command_1.prepareKeyValueMessage)(name, value2));
|
|
19833
19833
|
}
|
|
19834
19834
|
(0, command_1.issueCommand)("save-state", { name }, (0, utils_1.toCommandValue)(value2));
|
|
19835
19835
|
}
|
|
19836
|
-
exports.saveState =
|
|
19836
|
+
exports.saveState = saveState3;
|
|
19837
19837
|
function getState(name) {
|
|
19838
19838
|
return process.env[`STATE_${name}`] || "";
|
|
19839
19839
|
}
|
|
@@ -27621,11 +27621,11 @@ var require_mode = __commonJS({
|
|
|
27621
27621
|
var require_isexe = __commonJS({
|
|
27622
27622
|
"node_modules/.pnpm/isexe@2.0.0/node_modules/isexe/index.js"(exports, module) {
|
|
27623
27623
|
var fs5 = __require("fs");
|
|
27624
|
-
var
|
|
27624
|
+
var core11;
|
|
27625
27625
|
if (process.platform === "win32" || global.TESTING_WINDOWS) {
|
|
27626
|
-
|
|
27626
|
+
core11 = require_windows();
|
|
27627
27627
|
} else {
|
|
27628
|
-
|
|
27628
|
+
core11 = require_mode();
|
|
27629
27629
|
}
|
|
27630
27630
|
module.exports = isexe;
|
|
27631
27631
|
isexe.sync = sync;
|
|
@@ -27648,7 +27648,7 @@ var require_isexe = __commonJS({
|
|
|
27648
27648
|
});
|
|
27649
27649
|
});
|
|
27650
27650
|
}
|
|
27651
|
-
|
|
27651
|
+
core11(path4, options || {}, function(er, is) {
|
|
27652
27652
|
if (er) {
|
|
27653
27653
|
if (er.code === "EACCES" || options && options.ignoreErrors) {
|
|
27654
27654
|
er = null;
|
|
@@ -27660,7 +27660,7 @@ var require_isexe = __commonJS({
|
|
|
27660
27660
|
}
|
|
27661
27661
|
function sync(path4, options) {
|
|
27662
27662
|
try {
|
|
27663
|
-
return
|
|
27663
|
+
return core11.sync(path4, options || {});
|
|
27664
27664
|
} catch (er) {
|
|
27665
27665
|
if (options && options.ignoreErrors || er.code === "EACCES") {
|
|
27666
27666
|
return false;
|
|
@@ -33026,7 +33026,7 @@ var require_util9 = __commonJS({
|
|
|
33026
33026
|
var { redirectStatusSet, referrerPolicyTokens, badPortsSet } = require_constants9();
|
|
33027
33027
|
var { getGlobalOrigin } = require_global3();
|
|
33028
33028
|
var { collectAnHTTPQuotedString, parseMIMEType } = require_data_url();
|
|
33029
|
-
var { performance:
|
|
33029
|
+
var { performance: performance10 } = __require("node:perf_hooks");
|
|
33030
33030
|
var { ReadableStreamFrom, isValidHTTPToken, normalizedMethodRecordsBase } = require_util8();
|
|
33031
33031
|
var assert3 = __require("node:assert");
|
|
33032
33032
|
var { isUint8Array } = __require("node:util/types");
|
|
@@ -33181,7 +33181,7 @@ var require_util9 = __commonJS({
|
|
|
33181
33181
|
};
|
|
33182
33182
|
}
|
|
33183
33183
|
function coarsenedSharedCurrentTime(crossOriginIsolatedCapability) {
|
|
33184
|
-
return coarsenTime(
|
|
33184
|
+
return coarsenTime(performance10.now(), crossOriginIsolatedCapability);
|
|
33185
33185
|
}
|
|
33186
33186
|
function createOpaqueTimingInfo(timingInfo) {
|
|
33187
33187
|
return {
|
|
@@ -76453,7 +76453,7 @@ var require_core3 = __commonJS({
|
|
|
76453
76453
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
76454
76454
|
var id_1 = require_id();
|
|
76455
76455
|
var ref_1 = require_ref();
|
|
76456
|
-
var
|
|
76456
|
+
var core11 = [
|
|
76457
76457
|
"$schema",
|
|
76458
76458
|
"$id",
|
|
76459
76459
|
"$defs",
|
|
@@ -76463,7 +76463,7 @@ var require_core3 = __commonJS({
|
|
|
76463
76463
|
id_1.default,
|
|
76464
76464
|
ref_1.default
|
|
76465
76465
|
];
|
|
76466
|
-
exports.default =
|
|
76466
|
+
exports.default = core11;
|
|
76467
76467
|
}
|
|
76468
76468
|
});
|
|
76469
76469
|
|
|
@@ -98334,14 +98334,14 @@ var require_turndown_cjs = __commonJS({
|
|
|
98334
98334
|
} else if (node2.nodeType === 1) {
|
|
98335
98335
|
replacement = replacementForNode.call(self2, node2);
|
|
98336
98336
|
}
|
|
98337
|
-
return
|
|
98337
|
+
return join28(output, replacement);
|
|
98338
98338
|
}, "");
|
|
98339
98339
|
}
|
|
98340
98340
|
function postProcess(output) {
|
|
98341
98341
|
var self2 = this;
|
|
98342
98342
|
this.rules.forEach(function(rule) {
|
|
98343
98343
|
if (typeof rule.append === "function") {
|
|
98344
|
-
output =
|
|
98344
|
+
output = join28(output, rule.append(self2.options));
|
|
98345
98345
|
}
|
|
98346
98346
|
});
|
|
98347
98347
|
return output.replace(/^[\t\r\n]+/, "").replace(/[\t\r\n\s]+$/, "");
|
|
@@ -98353,7 +98353,7 @@ var require_turndown_cjs = __commonJS({
|
|
|
98353
98353
|
if (whitespace.leading || whitespace.trailing) content = content.trim();
|
|
98354
98354
|
return whitespace.leading + rule.replacement(content, node2, this.options) + whitespace.trailing;
|
|
98355
98355
|
}
|
|
98356
|
-
function
|
|
98356
|
+
function join28(output, replacement) {
|
|
98357
98357
|
var s1 = trimTrailingNewlines(output);
|
|
98358
98358
|
var s2 = trimLeadingNewlines(replacement);
|
|
98359
98359
|
var nls = Math.max(output.length - s1.length, replacement.length - s2.length);
|
|
@@ -103144,9 +103144,9 @@ var init_file_type = __esm({
|
|
|
103144
103144
|
});
|
|
103145
103145
|
|
|
103146
103146
|
// main.ts
|
|
103147
|
-
import { existsSync as
|
|
103147
|
+
import { existsSync as existsSync9, readdirSync as readdirSync2 } from "node:fs";
|
|
103148
103148
|
import { readFile as readFile5 } from "node:fs/promises";
|
|
103149
|
-
import { join as
|
|
103149
|
+
import { join as join27 } from "node:path";
|
|
103150
103150
|
|
|
103151
103151
|
// node_modules/.pnpm/package-manager-detector@1.8.0/node_modules/package-manager-detector/dist/commands.mjs
|
|
103152
103152
|
function splitRunArgs(args2, valueFlags = []) {
|
|
@@ -104019,6 +104019,22 @@ var providers = {
|
|
|
104019
104019
|
}
|
|
104020
104020
|
}
|
|
104021
104021
|
}),
|
|
104022
|
+
azure: provider({
|
|
104023
|
+
displayName: "Azure OpenAI",
|
|
104024
|
+
// the resource name is half the endpoint URL and the deployment is the model
|
|
104025
|
+
// id, so both are as load-bearing as the key. only the key is sensitive.
|
|
104026
|
+
envVars: ["AZURE_API_KEY", "AZURE_RESOURCE_NAME", "AZURE_DEPLOYMENT"],
|
|
104027
|
+
models: {
|
|
104028
|
+
// single routing entry — the real model is the customer's deployment name,
|
|
104029
|
+
// read from AZURE_DEPLOYMENT at run time. see ModelRouting docs for why
|
|
104030
|
+
// Azure can't be cataloged even though models.dev lists it.
|
|
104031
|
+
byok: {
|
|
104032
|
+
displayName: "Azure OpenAI",
|
|
104033
|
+
resolve: "azure",
|
|
104034
|
+
routing: "azure"
|
|
104035
|
+
}
|
|
104036
|
+
}
|
|
104037
|
+
}),
|
|
104022
104038
|
"openai-compatible": provider({
|
|
104023
104039
|
// "Custom" is the picker group, "OpenAI-compatible" the entry under it, so the
|
|
104024
104040
|
// menu reads `Custom › OpenAI-compatible` and a second custom backend (a
|
|
@@ -104244,6 +104260,11 @@ function getModelEnvVars(slug2) {
|
|
|
104244
104260
|
}
|
|
104245
104261
|
return providerConfig.envVars.slice();
|
|
104246
104262
|
}
|
|
104263
|
+
function getModelManagedCredentials(slug2) {
|
|
104264
|
+
const parsed2 = parseModel(slug2);
|
|
104265
|
+
const providerConfig = providers[parsed2.provider];
|
|
104266
|
+
return providerConfig?.managedCredentials?.slice() ?? [];
|
|
104267
|
+
}
|
|
104247
104268
|
var modelAliases = Object.entries(providers).flatMap(
|
|
104248
104269
|
([providerKey, config3]) => Object.entries(config3.models).map(([modelId, def]) => ({
|
|
104249
104270
|
slug: `${providerKey}/${modelId}`,
|
|
@@ -104341,6 +104362,13 @@ function isOssAllowedModel(slug2) {
|
|
|
104341
104362
|
}
|
|
104342
104363
|
var BEDROCK_MODEL_ID_ENV = "BEDROCK_MODEL_ID";
|
|
104343
104364
|
var VERTEX_MODEL_ID_ENV = "VERTEX_MODEL_ID";
|
|
104365
|
+
var AZURE_PROVIDER = "azure";
|
|
104366
|
+
var AZURE_RESOURCE_NAME_ENV = "AZURE_RESOURCE_NAME";
|
|
104367
|
+
var AZURE_API_KEY_ENV = "AZURE_API_KEY";
|
|
104368
|
+
var AZURE_DEPLOYMENT_ENV = "AZURE_DEPLOYMENT";
|
|
104369
|
+
var AZURE_CONTEXT_ENV = "AZURE_CONTEXT";
|
|
104370
|
+
var AZURE_MAX_OUTPUT_ENV = "AZURE_MAX_OUTPUT";
|
|
104371
|
+
var AZURE_USE_CHAT_COMPLETIONS_ENV = "AZURE_USE_CHAT_COMPLETIONS";
|
|
104344
104372
|
var OPENAI_COMPATIBLE_PROVIDER = "openai-compatible";
|
|
104345
104373
|
var OPENAI_COMPATIBLE_BASE_URL_ENV = "OPENAI_COMPATIBLE_BASE_URL";
|
|
104346
104374
|
var OPENAI_COMPATIBLE_API_KEY_ENV = "OPENAI_COMPATIBLE_API_KEY";
|
|
@@ -104360,6 +104388,8 @@ function formatMcpToolRef(agentId, toolName) {
|
|
|
104360
104388
|
switch (agentId) {
|
|
104361
104389
|
case "claude":
|
|
104362
104390
|
return `mcp__${pullfrogMcpName}__${toolName}`;
|
|
104391
|
+
case "codex":
|
|
104392
|
+
return `${pullfrogMcpName}__${toolName}`;
|
|
104363
104393
|
case "opencode":
|
|
104364
104394
|
return `${pullfrogMcpName}_${toolName}`;
|
|
104365
104395
|
default:
|
|
@@ -104390,6 +104420,11 @@ var DEFAULT_ACTIVITY_TIMEOUT_MS = 3e5;
|
|
|
104390
104420
|
var AGENT_ACTIVITY_TIMEOUT_MS = 9e5;
|
|
104391
104421
|
var AGENT_FIRST_EVENT_TIMEOUT_MS = 12e4;
|
|
104392
104422
|
var DEFAULT_ACTIVITY_CHECK_INTERVAL_MS = 5e3;
|
|
104423
|
+
function watchdogBudgetMs(compiled, envVar) {
|
|
104424
|
+
const raw2 = Number(process.env[envVar]);
|
|
104425
|
+
if (!Number.isFinite(raw2) || raw2 <= 0) return compiled;
|
|
104426
|
+
return Math.min(compiled, raw2);
|
|
104427
|
+
}
|
|
104393
104428
|
var DEBUG_TS_PREFIX = /^(?:\[\d{4}-\d{2}-\d{2}T[^\]]+\]\s+)?/.source;
|
|
104394
104429
|
var ACTIVITY_NOISE_PATTERNS = [
|
|
104395
104430
|
new RegExp(`${DEBUG_TS_PREFIX}\\[mcp-proxy\\]`),
|
|
@@ -104861,7 +104896,8 @@ import { pipeline } from "node:stream/promises";
|
|
|
104861
104896
|
async function installFromNpmTarball(params) {
|
|
104862
104897
|
const tempDir = process.env.PULLFROG_TEMP_DIR;
|
|
104863
104898
|
if (!tempDir) throw new Error("PULLFROG_TEMP_DIR is not set");
|
|
104864
|
-
const
|
|
104899
|
+
const packageRoot = join(tempDir, `npm-${params.packageName.replace(/[^a-z0-9]+/gi, "-")}`);
|
|
104900
|
+
const extractedDir = join(packageRoot, "package");
|
|
104865
104901
|
const cliPath = join(extractedDir, params.executablePath);
|
|
104866
104902
|
if (existsSync2(cliPath)) {
|
|
104867
104903
|
log.debug(`\xBB using cached binary at ${cliPath}`);
|
|
@@ -104887,7 +104923,8 @@ async function installFromNpmTarball(params) {
|
|
|
104887
104923
|
}
|
|
104888
104924
|
}
|
|
104889
104925
|
log.debug(`\xBB installing ${params.packageName}@${resolvedVersion}...`);
|
|
104890
|
-
|
|
104926
|
+
mkdirSync(packageRoot, { recursive: true });
|
|
104927
|
+
const tarballPath = join(packageRoot, "package.tgz");
|
|
104891
104928
|
const npmRegistry = process.env.NPM_REGISTRY || "https://registry.npmjs.org";
|
|
104892
104929
|
let tarballUrl;
|
|
104893
104930
|
if (params.packageName.startsWith("@")) {
|
|
@@ -104907,7 +104944,7 @@ async function installFromNpmTarball(params) {
|
|
|
104907
104944
|
await pipeline(response.body, fileStream);
|
|
104908
104945
|
log.debug(`\xBB downloaded tarball to ${tarballPath}`);
|
|
104909
104946
|
log.debug(`\xBB extracting tarball...`);
|
|
104910
|
-
const extractResult = spawnSync("tar", ["-xzf", tarballPath, "-C",
|
|
104947
|
+
const extractResult = spawnSync("tar", ["-xzf", tarballPath, "-C", packageRoot], {
|
|
104911
104948
|
stdio: "pipe",
|
|
104912
104949
|
encoding: "utf-8"
|
|
104913
104950
|
});
|
|
@@ -104916,9 +104953,6 @@ async function installFromNpmTarball(params) {
|
|
|
104916
104953
|
`Failed to extract tarball: ${extractResult.stderr || extractResult.stdout || "Unknown error"}`
|
|
104917
104954
|
);
|
|
104918
104955
|
}
|
|
104919
|
-
if (!existsSync2(cliPath)) {
|
|
104920
|
-
throw new Error(`Executable not found in extracted package at ${cliPath}`);
|
|
104921
|
-
}
|
|
104922
104956
|
if (params.installDependencies) {
|
|
104923
104957
|
log.debug(`\xBB installing dependencies for ${params.packageName}...`);
|
|
104924
104958
|
const installResult = spawnSync("npm", ["install", "--production"], {
|
|
@@ -104939,6 +104973,9 @@ async function installFromNpmTarball(params) {
|
|
|
104939
104973
|
}
|
|
104940
104974
|
log.debug(`\xBB dependencies installed`);
|
|
104941
104975
|
}
|
|
104976
|
+
if (!existsSync2(cliPath)) {
|
|
104977
|
+
throw new Error(`Executable not found in extracted package at ${cliPath}`);
|
|
104978
|
+
}
|
|
104942
104979
|
chmodSync(cliPath, 493);
|
|
104943
104980
|
log.debug(`\xBB ${params.packageName} installed at ${cliPath}`);
|
|
104944
104981
|
return cliPath;
|
|
@@ -105112,6 +105149,7 @@ function resolveRunAlias(ctx) {
|
|
|
105112
105149
|
}
|
|
105113
105150
|
const model = ctx.resolvedModel;
|
|
105114
105151
|
if (!model) return void 0;
|
|
105152
|
+
if (model.startsWith(`${AZURE_PROVIDER}/`)) return void 0;
|
|
105115
105153
|
return modelAliases.find((a) => !a.fallback && a.resolve === model) ?? matchHostedAnthropicAlias(model);
|
|
105116
105154
|
}
|
|
105117
105155
|
function ossEffortFloor(ctx) {
|
|
@@ -105150,7 +105188,7 @@ var import_semver = __toESM(require_semver2(), 1);
|
|
|
105150
105188
|
// package.json
|
|
105151
105189
|
var package_default = {
|
|
105152
105190
|
name: "pullfrog",
|
|
105153
|
-
version: "0.1.
|
|
105191
|
+
version: "0.1.60",
|
|
105154
105192
|
type: "module",
|
|
105155
105193
|
bin: {
|
|
105156
105194
|
pullfrog: "dist/cli.mjs",
|
|
@@ -105182,6 +105220,8 @@ var package_default = {
|
|
|
105182
105220
|
"@clack/prompts": "^1.2.0",
|
|
105183
105221
|
"@modelcontextprotocol/sdk": "^1.26.0",
|
|
105184
105222
|
"@opencode-ai/sdk": "1.18.5",
|
|
105223
|
+
"@openai/codex": "0.147.0",
|
|
105224
|
+
"@openai/codex-sdk": "0.147.0",
|
|
105185
105225
|
"@octokit/plugin-throttling": "^11.0.3",
|
|
105186
105226
|
"@octokit/rest": "^22.0.0",
|
|
105187
105227
|
"@octokit/webhooks-types": "^7.6.1",
|
|
@@ -107257,7 +107297,7 @@ var claude = agent({
|
|
|
107257
107297
|
if ((isBedrockRoute || isVertexRoute2) && specifier && effort.alias?.effort) {
|
|
107258
107298
|
applyHostedEffortCapabilities({ env: env2, modelId: specifier, levels: effort.alias.effort });
|
|
107259
107299
|
}
|
|
107260
|
-
if (env2.CLAUDE_CODE_OAUTH_TOKEN && !isBedrockRoute && env2.ANTHROPIC_API_KEY) {
|
|
107300
|
+
if (env2.CLAUDE_CODE_OAUTH_TOKEN && !isBedrockRoute && !isVertexRoute2 && !env2.ANTHROPIC_BASE_URL && !env2.ANTHROPIC_AUTH_TOKEN && env2.ANTHROPIC_API_KEY) {
|
|
107261
107301
|
const preflight = await preflightClaudeSubscription({
|
|
107262
107302
|
token: env2.CLAUDE_CODE_OAUTH_TOKEN,
|
|
107263
107303
|
model
|
|
@@ -107303,13 +107343,782 @@ var claude = agent({
|
|
|
107303
107343
|
}
|
|
107304
107344
|
});
|
|
107305
107345
|
|
|
107306
|
-
// agents/
|
|
107346
|
+
// agents/codex.ts
|
|
107307
107347
|
var core2 = __toESM(require_core(), 1);
|
|
107308
|
-
import {
|
|
107309
|
-
import { mkdirSync as mkdirSync6, writeFileSync as writeFileSync5 } from "node:fs";
|
|
107348
|
+
import { existsSync as existsSync4, mkdirSync as mkdirSync6, readFileSync as readFileSync2, writeFileSync as writeFileSync5 } from "node:fs";
|
|
107310
107349
|
import { join as join6 } from "node:path";
|
|
107311
107350
|
import { performance as performance6 } from "node:perf_hooks";
|
|
107312
107351
|
|
|
107352
|
+
// utils/codexHome.ts
|
|
107353
|
+
import { execFileSync as execFileSync3 } from "node:child_process";
|
|
107354
|
+
import { mkdirSync as mkdirSync5, writeFileSync as writeFileSync4 } from "node:fs";
|
|
107355
|
+
import { homedir as homedir2, userInfo } from "node:os";
|
|
107356
|
+
import { join as join5 } from "node:path";
|
|
107357
|
+
|
|
107358
|
+
// utils/codexOAuth.ts
|
|
107359
|
+
function decodeJwtExpMs(token) {
|
|
107360
|
+
const parts = token.split(".");
|
|
107361
|
+
if (parts.length !== 3) return null;
|
|
107362
|
+
let payload;
|
|
107363
|
+
try {
|
|
107364
|
+
payload = JSON.parse(Buffer.from(parts[1], "base64url").toString("utf8"));
|
|
107365
|
+
} catch {
|
|
107366
|
+
return null;
|
|
107367
|
+
}
|
|
107368
|
+
if (typeof payload.exp !== "number" || !Number.isFinite(payload.exp)) return null;
|
|
107369
|
+
return payload.exp * 1e3;
|
|
107370
|
+
}
|
|
107371
|
+
function parseCodexAuthBody(raw2) {
|
|
107372
|
+
let parsed2;
|
|
107373
|
+
try {
|
|
107374
|
+
parsed2 = JSON.parse(raw2);
|
|
107375
|
+
} catch {
|
|
107376
|
+
return null;
|
|
107377
|
+
}
|
|
107378
|
+
if (!parsed2 || typeof parsed2 !== "object") return null;
|
|
107379
|
+
const v = parsed2;
|
|
107380
|
+
if (v.auth_mode !== "chatgpt") return null;
|
|
107381
|
+
const tokens = v.tokens;
|
|
107382
|
+
if (!tokens || typeof tokens !== "object") return null;
|
|
107383
|
+
const t = tokens;
|
|
107384
|
+
if (typeof t.access_token !== "string" || t.access_token.length === 0) return null;
|
|
107385
|
+
if (typeof t.refresh_token !== "string" || t.refresh_token.length === 0) return null;
|
|
107386
|
+
return {
|
|
107387
|
+
auth_mode: "chatgpt",
|
|
107388
|
+
...typeof v.refresh_rejected_at === "string" ? { refresh_rejected_at: v.refresh_rejected_at } : {},
|
|
107389
|
+
tokens: {
|
|
107390
|
+
access_token: t.access_token,
|
|
107391
|
+
refresh_token: t.refresh_token,
|
|
107392
|
+
...typeof t.id_token === "string" ? { id_token: t.id_token } : {},
|
|
107393
|
+
...typeof t.account_id === "string" ? { account_id: t.account_id } : {}
|
|
107394
|
+
},
|
|
107395
|
+
...typeof v.last_refresh === "string" ? { last_refresh: v.last_refresh } : {}
|
|
107396
|
+
};
|
|
107397
|
+
}
|
|
107398
|
+
function stringifyCodexAuthBody(body) {
|
|
107399
|
+
return `${JSON.stringify(body, null, 2)}
|
|
107400
|
+
`;
|
|
107401
|
+
}
|
|
107402
|
+
|
|
107403
|
+
// utils/codexHome.ts
|
|
107404
|
+
var CODEX_AUTH_ENV = "CODEX_AUTH_JSON";
|
|
107405
|
+
var PULLFROG_DATA_DIR = "/var/lib/pullfrog";
|
|
107406
|
+
function isRejectedChain(body) {
|
|
107407
|
+
if (!body.refresh_rejected_at) return false;
|
|
107408
|
+
log.warning(
|
|
107409
|
+
`\xBB ${CODEX_AUTH_ENV} was rejected by OpenAI at ${body.refresh_rejected_at} and cannot be refreshed \u2014 re-run \`npx pullfrog auth codex\`. skipping it for this run.`
|
|
107410
|
+
);
|
|
107411
|
+
return true;
|
|
107412
|
+
}
|
|
107413
|
+
function installCodexAuth() {
|
|
107414
|
+
const raw2 = process.env[CODEX_AUTH_ENV];
|
|
107415
|
+
if (!raw2) return null;
|
|
107416
|
+
const body = parseCodexAuthBody(raw2);
|
|
107417
|
+
if (!body) {
|
|
107418
|
+
log.warning(`\xBB ${CODEX_AUTH_ENV} present but malformed; ignoring`);
|
|
107419
|
+
return null;
|
|
107420
|
+
}
|
|
107421
|
+
if (isRejectedChain(body)) return null;
|
|
107422
|
+
const expiresMs = decodeJwtExpMs(body.tokens.access_token) ?? 0;
|
|
107423
|
+
const xdgDataHome = resolveDataHome();
|
|
107424
|
+
const opencodeDir = join5(xdgDataHome, "opencode");
|
|
107425
|
+
const authPath = join5(opencodeDir, "auth.json");
|
|
107426
|
+
const opencodeAuth = {
|
|
107427
|
+
openai: {
|
|
107428
|
+
type: "oauth",
|
|
107429
|
+
refresh: body.tokens.refresh_token,
|
|
107430
|
+
access: body.tokens.access_token,
|
|
107431
|
+
expires: expiresMs,
|
|
107432
|
+
...body.tokens.account_id ? { accountId: body.tokens.account_id } : {}
|
|
107433
|
+
}
|
|
107434
|
+
};
|
|
107435
|
+
mkdirSync5(opencodeDir, { recursive: true });
|
|
107436
|
+
writeFileSync4(authPath, `${JSON.stringify(opencodeAuth, null, 2)}
|
|
107437
|
+
`, { mode: 384 });
|
|
107438
|
+
process.env.XDG_DATA_HOME = xdgDataHome;
|
|
107439
|
+
log.info(`\xBB installed Codex auth at ${authPath}`);
|
|
107440
|
+
return {
|
|
107441
|
+
authPath,
|
|
107442
|
+
xdgDataHome,
|
|
107443
|
+
originalRefresh: body.tokens.refresh_token,
|
|
107444
|
+
originalIdToken: body.tokens.id_token
|
|
107445
|
+
};
|
|
107446
|
+
}
|
|
107447
|
+
function installCodexHome() {
|
|
107448
|
+
const raw2 = process.env[CODEX_AUTH_ENV];
|
|
107449
|
+
if (!raw2) return null;
|
|
107450
|
+
const body = parseCodexAuthBody(raw2);
|
|
107451
|
+
if (!body) {
|
|
107452
|
+
log.warning(`\xBB ${CODEX_AUTH_ENV} present but malformed; ignoring`);
|
|
107453
|
+
return null;
|
|
107454
|
+
}
|
|
107455
|
+
if (isRejectedChain(body)) return null;
|
|
107456
|
+
if (!body.tokens.id_token) {
|
|
107457
|
+
log.warning(
|
|
107458
|
+
`\xBB ${CODEX_AUTH_ENV} carries no id_token \u2014 the codex CLI cannot load it. re-run \`npx pullfrog auth codex\` to mint a complete credential.`
|
|
107459
|
+
);
|
|
107460
|
+
return null;
|
|
107461
|
+
}
|
|
107462
|
+
const codexHome = join5(resolveDataHome(), "codex");
|
|
107463
|
+
const authPath = join5(codexHome, "auth.json");
|
|
107464
|
+
mkdirSync5(codexHome, { recursive: true });
|
|
107465
|
+
writeFileSync4(authPath, stringifyCodexAuthBody(body), { mode: 384 });
|
|
107466
|
+
log.info(`\xBB installed Codex auth at ${authPath}`);
|
|
107467
|
+
return { codexHome, authPath, originalRefresh: body.tokens.refresh_token };
|
|
107468
|
+
}
|
|
107469
|
+
function resolveDataHome() {
|
|
107470
|
+
if (process.env.CI !== "true") return join5(homedir2(), ".local", "share");
|
|
107471
|
+
bootstrapPullfrogDataDir();
|
|
107472
|
+
return PULLFROG_DATA_DIR;
|
|
107473
|
+
}
|
|
107474
|
+
function bootstrapPullfrogDataDir() {
|
|
107475
|
+
const user = userInfo().username;
|
|
107476
|
+
let primaryGroup;
|
|
107477
|
+
try {
|
|
107478
|
+
primaryGroup = execFileSync3("id", ["-gn", user], { stdio: "pipe", encoding: "utf-8" }).trim();
|
|
107479
|
+
} catch {
|
|
107480
|
+
primaryGroup = user;
|
|
107481
|
+
}
|
|
107482
|
+
try {
|
|
107483
|
+
execFileSync3("sudo", ["-n", "mkdir", "-p", PULLFROG_DATA_DIR], { stdio: "pipe" });
|
|
107484
|
+
execFileSync3("sudo", ["-n", "chown", `${user}:${primaryGroup}`, PULLFROG_DATA_DIR], {
|
|
107485
|
+
stdio: "pipe"
|
|
107486
|
+
});
|
|
107487
|
+
execFileSync3("sudo", ["-n", "chmod", "700", PULLFROG_DATA_DIR], { stdio: "pipe" });
|
|
107488
|
+
} catch (err) {
|
|
107489
|
+
throw new Error(
|
|
107490
|
+
`failed to bootstrap ${PULLFROG_DATA_DIR} (required for codex auth in CI): ${err instanceof Error ? err.message : String(err)}. the MCP shell's mount-namespace sandbox cannot protect the auth file when it lives under $HOME, and silently falling back would contradict the "three independent layers" claim in wiki/codex-auth.md. passwordless sudo is required for codex auth on this runner \u2014 either configure it, or remove CODEX_AUTH_JSON from the run.`
|
|
107491
|
+
);
|
|
107492
|
+
}
|
|
107493
|
+
}
|
|
107494
|
+
|
|
107495
|
+
// utils/secrets.ts
|
|
107496
|
+
var SENSITIVE_PATTERNS = [
|
|
107497
|
+
/_KEY$/i,
|
|
107498
|
+
/_SECRET$/i,
|
|
107499
|
+
/_TOKEN$/i,
|
|
107500
|
+
/_PASSWORD$/i,
|
|
107501
|
+
/_CREDENTIAL$/i
|
|
107502
|
+
];
|
|
107503
|
+
function isSensitiveEnvName(key) {
|
|
107504
|
+
return SENSITIVE_PATTERNS.some((p) => p.test(key));
|
|
107505
|
+
}
|
|
107506
|
+
var SAFE_ENV_PREFIXES = ["GITHUB_", "RUNNER_", "JAVA_HOME_", "GOROOT_"];
|
|
107507
|
+
var SAFE_ENV_NAMES = /* @__PURE__ */ new Set([
|
|
107508
|
+
// system
|
|
107509
|
+
"CI",
|
|
107510
|
+
"HOME",
|
|
107511
|
+
"LANG",
|
|
107512
|
+
"LOGNAME",
|
|
107513
|
+
"PATH",
|
|
107514
|
+
"SHELL",
|
|
107515
|
+
"SHLVL",
|
|
107516
|
+
"TERM",
|
|
107517
|
+
"TMPDIR",
|
|
107518
|
+
"TZ",
|
|
107519
|
+
"USER",
|
|
107520
|
+
"XDG_CONFIG_HOME",
|
|
107521
|
+
"XDG_RUNTIME_DIR",
|
|
107522
|
+
"DEBIAN_FRONTEND",
|
|
107523
|
+
// runner image toolchain
|
|
107524
|
+
"ACCEPT_EULA",
|
|
107525
|
+
"AGENT_TOOLSDIRECTORY",
|
|
107526
|
+
"ANDROID_HOME",
|
|
107527
|
+
"ANDROID_NDK",
|
|
107528
|
+
"ANDROID_NDK_HOME",
|
|
107529
|
+
"ANDROID_NDK_LATEST_HOME",
|
|
107530
|
+
"ANDROID_NDK_ROOT",
|
|
107531
|
+
"ANDROID_SDK_ROOT",
|
|
107532
|
+
"ANT_HOME",
|
|
107533
|
+
"AZURE_EXTENSION_DIR",
|
|
107534
|
+
"BOOTSTRAP_HASKELL_NONINTERACTIVE",
|
|
107535
|
+
"CHROME_BIN",
|
|
107536
|
+
"CHROMEWEBDRIVER",
|
|
107537
|
+
"CONDA",
|
|
107538
|
+
"DOTNET_MULTILEVEL_LOOKUP",
|
|
107539
|
+
"DOTNET_NOLOGO",
|
|
107540
|
+
"DOTNET_SKIP_FIRST_TIME_EXPERIENCE",
|
|
107541
|
+
"EDGEWEBDRIVER",
|
|
107542
|
+
"GECKOWEBDRIVER",
|
|
107543
|
+
"GHCUP_INSTALL_BASE_PREFIX",
|
|
107544
|
+
"GRADLE_HOME",
|
|
107545
|
+
"JAVA_HOME",
|
|
107546
|
+
"HOMEBREW_CLEANUP_PERIODIC_FULL_DAYS",
|
|
107547
|
+
"HOMEBREW_NO_AUTO_UPDATE",
|
|
107548
|
+
"ImageOS",
|
|
107549
|
+
"ImageVersion",
|
|
107550
|
+
"NVM_DIR",
|
|
107551
|
+
"PIPX_BIN_DIR",
|
|
107552
|
+
"PIPX_HOME",
|
|
107553
|
+
"PSModulePath",
|
|
107554
|
+
"SELENIUM_JAR_PATH",
|
|
107555
|
+
"SGX_AESM_ADDR",
|
|
107556
|
+
"SWIFT_PATH",
|
|
107557
|
+
"VCPKG_INSTALLATION_ROOT"
|
|
107558
|
+
]);
|
|
107559
|
+
var _userAllowlist = null;
|
|
107560
|
+
function setEnvAllowlist(raw2) {
|
|
107561
|
+
const names = raw2.split("\n").map((line) => line.trim()).filter(Boolean);
|
|
107562
|
+
_userAllowlist = new Set(names);
|
|
107563
|
+
}
|
|
107564
|
+
function isSafeEnvVar(key) {
|
|
107565
|
+
if (SAFE_ENV_NAMES.has(key)) return true;
|
|
107566
|
+
return SAFE_ENV_PREFIXES.some((p) => key.startsWith(p));
|
|
107567
|
+
}
|
|
107568
|
+
var WORKFLOW_COMMAND_ENV_NAMES = [
|
|
107569
|
+
"GITHUB_ENV",
|
|
107570
|
+
"GITHUB_PATH",
|
|
107571
|
+
"GITHUB_OUTPUT",
|
|
107572
|
+
"GITHUB_STATE",
|
|
107573
|
+
"GITHUB_STEP_SUMMARY",
|
|
107574
|
+
// dropping the five paths above but keeping RUNNER_TEMP would be theatre:
|
|
107575
|
+
// the file-command files live in `$RUNNER_TEMP/_runner_file_commands/`, so the
|
|
107576
|
+
// directory is one `ls` away. nothing in the install path reads it (only our
|
|
107577
|
+
// own leak-surface wipe in `setup.ts`, which runs in the parent), and package
|
|
107578
|
+
// managers use TMPDIR.
|
|
107579
|
+
"RUNNER_TEMP"
|
|
107580
|
+
];
|
|
107581
|
+
function filterEnvForUntrustedCode() {
|
|
107582
|
+
const env2 = filterEnv();
|
|
107583
|
+
for (const name of WORKFLOW_COMMAND_ENV_NAMES) delete env2[name];
|
|
107584
|
+
return env2;
|
|
107585
|
+
}
|
|
107586
|
+
function filterEnv() {
|
|
107587
|
+
const filtered = {};
|
|
107588
|
+
for (const [key, value2] of Object.entries(process.env)) {
|
|
107589
|
+
if (value2 === void 0) continue;
|
|
107590
|
+
const userAllowed = _userAllowlist?.has(key) ?? false;
|
|
107591
|
+
if (isSensitiveEnvName(key) && !userAllowed) continue;
|
|
107592
|
+
if (isSafeEnvVar(key) || userAllowed) {
|
|
107593
|
+
filtered[key] = value2;
|
|
107594
|
+
}
|
|
107595
|
+
}
|
|
107596
|
+
return filtered;
|
|
107597
|
+
}
|
|
107598
|
+
function resolveEnv(mode) {
|
|
107599
|
+
if (mode === "inherit") {
|
|
107600
|
+
return process.env;
|
|
107601
|
+
}
|
|
107602
|
+
if (mode === "restricted" || mode === void 0) {
|
|
107603
|
+
return filterEnv();
|
|
107604
|
+
}
|
|
107605
|
+
return { ...filterEnv(), ...mode };
|
|
107606
|
+
}
|
|
107607
|
+
|
|
107608
|
+
// agents/codex.ts
|
|
107609
|
+
var CODEX_TARGETS = {
|
|
107610
|
+
"linux-x64": "x86_64-unknown-linux-musl",
|
|
107611
|
+
"linux-arm64": "aarch64-unknown-linux-musl",
|
|
107612
|
+
"darwin-x64": "x86_64-apple-darwin",
|
|
107613
|
+
"darwin-arm64": "aarch64-apple-darwin",
|
|
107614
|
+
"win32-x64": "x86_64-pc-windows-msvc",
|
|
107615
|
+
"win32-arm64": "aarch64-pc-windows-msvc"
|
|
107616
|
+
};
|
|
107617
|
+
async function installCodexCli() {
|
|
107618
|
+
const target = CODEX_TARGETS[`${process.platform}-${process.arch}`];
|
|
107619
|
+
if (!target) {
|
|
107620
|
+
throw new Error(`the codex CLI ships no binary for ${process.platform}-${process.arch}`);
|
|
107621
|
+
}
|
|
107622
|
+
const binary = process.platform === "win32" ? "codex.exe" : "codex";
|
|
107623
|
+
return await installFromNpmTarball({
|
|
107624
|
+
packageName: "@openai/codex",
|
|
107625
|
+
version: getDevDependencyVersion("@openai/codex"),
|
|
107626
|
+
executablePath: `node_modules/@openai/codex-${process.platform}-${process.arch}/vendor/${target}/bin/${binary}`,
|
|
107627
|
+
installDependencies: true
|
|
107628
|
+
});
|
|
107629
|
+
}
|
|
107630
|
+
var CODEX_DISABLED_FEATURES = [
|
|
107631
|
+
// browser / desktop control — network reach and arbitrary JS
|
|
107632
|
+
"browser_use",
|
|
107633
|
+
"browser_use_external",
|
|
107634
|
+
"browser_use_full_cdp_access",
|
|
107635
|
+
"computer_use",
|
|
107636
|
+
"in_app_browser",
|
|
107637
|
+
// third-party code loaded into the agent process
|
|
107638
|
+
"plugins",
|
|
107639
|
+
"remote_plugin",
|
|
107640
|
+
"plugin_sharing",
|
|
107641
|
+
"skill_mcp_dependency_install",
|
|
107642
|
+
// lifecycle hooks run commands sourced from repo config, which a PR can edit
|
|
107643
|
+
"hooks",
|
|
107644
|
+
// connectors
|
|
107645
|
+
"apps",
|
|
107646
|
+
// ungated subagent fan-out (see the file header)
|
|
107647
|
+
"multi_agent",
|
|
107648
|
+
"multi_agent_v2"
|
|
107649
|
+
];
|
|
107650
|
+
var CODEX_SHELL_FEATURES = ["shell_tool", "unified_exec"];
|
|
107651
|
+
function tomlBool(entries, value2) {
|
|
107652
|
+
return entries.map((name) => `${name} = ${value2}`).join("\n");
|
|
107653
|
+
}
|
|
107654
|
+
function securityOverrideFlags(params) {
|
|
107655
|
+
const shellEnabled = params.ctx.payload.shell === "enabled";
|
|
107656
|
+
const features = [
|
|
107657
|
+
...CODEX_DISABLED_FEATURES.map((name) => `${name}=false`),
|
|
107658
|
+
...CODEX_SHELL_FEATURES.map((name) => `${name}=${shellEnabled}`)
|
|
107659
|
+
];
|
|
107660
|
+
return [
|
|
107661
|
+
"-c",
|
|
107662
|
+
`approval_policy=${JSON.stringify("never")}`,
|
|
107663
|
+
"-c",
|
|
107664
|
+
`sandbox_mode=${JSON.stringify(params.sandboxMode)}`,
|
|
107665
|
+
// the sandbox's EXTENT has to be locked too, not just its mode: a repo
|
|
107666
|
+
// setting `writable_roots = ["/"]` walked straight out of the checkout with
|
|
107667
|
+
// `sandbox_mode` already flag-locked (measured — apply_patch wrote a file
|
|
107668
|
+
// under $HOME).
|
|
107669
|
+
"-c",
|
|
107670
|
+
`sandbox_workspace_write.writable_roots=[${JSON.stringify(params.repoDir)}]`,
|
|
107671
|
+
"-c",
|
|
107672
|
+
"sandbox_workspace_write.network_access=true",
|
|
107673
|
+
// our own MCP server, for a third time in the same class: a repo pointing
|
|
107674
|
+
// `mcp_servers.pullfrog.url` at its own endpoint (measured — `codex mcp get`
|
|
107675
|
+
// reported the repo's URL) turns every pullfrog tool call into an
|
|
107676
|
+
// exfiltration channel, carrying diffs, file contents and cross-repo
|
|
107677
|
+
// learnings to whoever it names.
|
|
107678
|
+
"-c",
|
|
107679
|
+
`mcp_servers.${pullfrogMcpName}.url=${JSON.stringify(params.mcpServerUrl)}`,
|
|
107680
|
+
"-c",
|
|
107681
|
+
`mcp_servers.${pullfrogMcpName}.${APPROVAL_MODE_KEY}=${JSON.stringify("approve")}`,
|
|
107682
|
+
"-c",
|
|
107683
|
+
`mcp_servers.${pullfrogMcpName}.required=true`,
|
|
107684
|
+
"-c",
|
|
107685
|
+
`mcp_servers.${pullfrogMcpName}.tool_timeout_sec=660`,
|
|
107686
|
+
// `enabled` closes the silent-DoS variant: a repo setting it false would
|
|
107687
|
+
// otherwise strip every GitHub tool and leave the agent narrating failure.
|
|
107688
|
+
"-c",
|
|
107689
|
+
`mcp_servers.${pullfrogMcpName}.enabled=true`,
|
|
107690
|
+
// not a boundary, but the same override: a repo silently re-pointing the
|
|
107691
|
+
// model spends the account's money on a pick it never made, and makes the
|
|
107692
|
+
// "Using `…`" run footer a lie about what actually ran.
|
|
107693
|
+
...params.model ? ["-c", `model=${JSON.stringify(params.model)}`] : [],
|
|
107694
|
+
...params.effortRung ? ["-c", `model_reasoning_effort=${JSON.stringify(params.effortRung)}`] : [],
|
|
107695
|
+
...features.flatMap((entry) => ["-c", `features.${entry}`])
|
|
107696
|
+
];
|
|
107697
|
+
}
|
|
107698
|
+
var CODEX_EFFORTS = ["minimal", "low", "medium", "high", "xhigh"];
|
|
107699
|
+
function resolveCodexModel(ctx) {
|
|
107700
|
+
const specifier = ctx.payload.proxyModel ?? ctx.resolvedModel;
|
|
107701
|
+
if (!specifier) return void 0;
|
|
107702
|
+
try {
|
|
107703
|
+
if (getModelProvider(specifier) === "openai") return stripProviderPrefix(specifier);
|
|
107704
|
+
} catch {
|
|
107705
|
+
}
|
|
107706
|
+
log.warning(`\xBB ${specifier} isn't an OpenAI model \u2014 running codex on its own default instead`);
|
|
107707
|
+
return void 0;
|
|
107708
|
+
}
|
|
107709
|
+
var APPROVAL_MODE_KEY = "default_tools_approval_mode";
|
|
107710
|
+
var APPROVE_TOOLS = `${APPROVAL_MODE_KEY} = "approve"`;
|
|
107711
|
+
function repoMcpApprovals(repoDir) {
|
|
107712
|
+
const path4 = join6(repoDir, ".codex", "config.toml");
|
|
107713
|
+
if (!existsSync4(path4)) return [];
|
|
107714
|
+
const out = [];
|
|
107715
|
+
let section = [];
|
|
107716
|
+
let isServerTable = false;
|
|
107717
|
+
function flush() {
|
|
107718
|
+
if (section.length === 0) return;
|
|
107719
|
+
const declared = section.some((line) => line.trimStart().startsWith(APPROVAL_MODE_KEY));
|
|
107720
|
+
if (isServerTable && !declared) section.splice(1, 0, APPROVE_TOOLS);
|
|
107721
|
+
out.push(...section, "");
|
|
107722
|
+
section = [];
|
|
107723
|
+
}
|
|
107724
|
+
for (const line of readFileSync2(path4, "utf8").split("\n")) {
|
|
107725
|
+
const header = line.trim().match(/^\[(?<key>[^\]]+)\]$/);
|
|
107726
|
+
if (!header?.groups) {
|
|
107727
|
+
if (section.length > 0) section.push(line);
|
|
107728
|
+
continue;
|
|
107729
|
+
}
|
|
107730
|
+
flush();
|
|
107731
|
+
const key = header.groups.key.trim();
|
|
107732
|
+
if (!key.startsWith("mcp_servers.")) continue;
|
|
107733
|
+
const name = key.slice("mcp_servers.".length).replace(/^"([^"]*)"/, "$1");
|
|
107734
|
+
if (name === pullfrogMcpName || name.startsWith(`${pullfrogMcpName}.`)) continue;
|
|
107735
|
+
isServerTable = !name.includes(".");
|
|
107736
|
+
section = [line.trim()];
|
|
107737
|
+
}
|
|
107738
|
+
flush();
|
|
107739
|
+
return out;
|
|
107740
|
+
}
|
|
107741
|
+
function warnIfNativeEditsUnavailable(ctx) {
|
|
107742
|
+
if (process.platform !== "linux" || ctx.payload.shell !== "disabled") return;
|
|
107743
|
+
log.warning(
|
|
107744
|
+
"\xBB this run cannot change files: codex's native write tool depends on bubblewrap, which Ubuntu 24.04 blocks (https://github.com/openai/codex/issues/29908), and the MCP shell is disabled for this repo. read-only work is unaffected \u2014 set `shell: restricted` if the agent needs to edit anything."
|
|
107745
|
+
);
|
|
107746
|
+
}
|
|
107747
|
+
function writeCodexConfig(params) {
|
|
107748
|
+
const shellEnabled = params.ctx.payload.shell === "enabled";
|
|
107749
|
+
const sandboxMode = params.ctx.payload.push === "disabled" ? "read-only" : "workspace-write";
|
|
107750
|
+
const repoDir = process.cwd();
|
|
107751
|
+
const config3 = [
|
|
107752
|
+
"# written by pullfrog",
|
|
107753
|
+
// "never" keeps the run non-interactive WITHOUT
|
|
107754
|
+
// --dangerously-bypass-approvals-and-sandbox, which would also switch the
|
|
107755
|
+
// sandbox off and let the native apply_patch tool write anywhere.
|
|
107756
|
+
'approval_policy = "never"',
|
|
107757
|
+
`sandbox_mode = ${JSON.stringify(sandboxMode)}`,
|
|
107758
|
+
// matches opencode's `webfetch: "allow"` — research is part of the job and
|
|
107759
|
+
// neither harness gates it on a permission.
|
|
107760
|
+
'web_search = "live"',
|
|
107761
|
+
...params.model ? [`model = ${JSON.stringify(params.model)}`] : [],
|
|
107762
|
+
...params.effortRung ? [`model_reasoning_effort = ${JSON.stringify(params.effortRung)}`] : [],
|
|
107763
|
+
"",
|
|
107764
|
+
"[features]",
|
|
107765
|
+
tomlBool(CODEX_DISABLED_FEATURES, false),
|
|
107766
|
+
tomlBool(CODEX_SHELL_FEATURES, shellEnabled),
|
|
107767
|
+
"",
|
|
107768
|
+
"[sandbox_workspace_write]",
|
|
107769
|
+
"network_access = true",
|
|
107770
|
+
// the checkout, named explicitly rather than left to `workspace-write` to
|
|
107771
|
+
// imply. does NOT fix the Linux native-write failure — nothing here does;
|
|
107772
|
+
// see wiki/codex-agent.md "The Linux native-write blocker".
|
|
107773
|
+
`writable_roots = [${JSON.stringify(repoDir)}]`,
|
|
107774
|
+
"",
|
|
107775
|
+
`[mcp_servers.${pullfrogMcpName}]`,
|
|
107776
|
+
`url = ${JSON.stringify(params.ctx.mcpServerUrl)}`,
|
|
107777
|
+
// codex's default MCP approval mode PROMPTS for anything it can't read as
|
|
107778
|
+
// read-only, and a `codex exec` run has nobody to answer — the call comes
|
|
107779
|
+
// back "user cancelled MCP tool call" and the agent silently accomplishes
|
|
107780
|
+
// nothing. our own server is the trusted one; the boundary is what we
|
|
107781
|
+
// register, not a second confirmation.
|
|
107782
|
+
APPROVE_TOOLS,
|
|
107783
|
+
// a run whose GitHub tools never registered can't do its job, so fail loudly
|
|
107784
|
+
// rather than let the agent improvise without them.
|
|
107785
|
+
"required = true",
|
|
107786
|
+
// mirrors the opencode harness's 660s: `checkout_pr` runs a multi-minute
|
|
107787
|
+
// fetch on large repos and enforces its own 600s deadline, so a shorter
|
|
107788
|
+
// client abort turns a working checkout into a spurious tool error.
|
|
107789
|
+
"tool_timeout_sec = 660",
|
|
107790
|
+
"",
|
|
107791
|
+
...repoMcpApprovals(repoDir),
|
|
107792
|
+
// UNTRUSTED, deliberately, and this one line is the whole config-precedence
|
|
107793
|
+
// boundary. Codex loads `<repo>/.codex/config.toml` as a layer that
|
|
107794
|
+
// OUTRANKS ours (project 25 vs user 20), and its denylist strips none of
|
|
107795
|
+
// `sandbox_mode`, `approval_policy`, `features` or `mcp_servers` — so a
|
|
107796
|
+
// fork PR could ship a config re-enabling the native shell. Marking the
|
|
107797
|
+
// checkout untrusted makes that layer load `disabled`, closing the class
|
|
107798
|
+
// outright rather than key by key: `RawMcpServerConfig` alone has ~26
|
|
107799
|
+
// fields and enumeration can never finish.
|
|
107800
|
+
//
|
|
107801
|
+
// It costs nothing. `codex exec`'s only startup gate is a GIT-REPO check
|
|
107802
|
+
// (`exec/src/lib.rs:796` — `get_git_repo_root(...).is_none()`, whose error
|
|
107803
|
+
// text unhelpfully says "Not inside a trusted directory"), so an untrusted
|
|
107804
|
+
// checkout still runs. And the repo's own MCP servers survive, because
|
|
107805
|
+
// `repoMcpApprovals` above copies them into OUR layer — which is why that
|
|
107806
|
+
// copying exists at all.
|
|
107807
|
+
//
|
|
107808
|
+
// Measured both ways at 0.147.0: `trusted` → the repo's `model` won;
|
|
107809
|
+
// `untrusted` → it did not, and the run proceeded normally.
|
|
107810
|
+
`[projects.${JSON.stringify(repoDir)}]`,
|
|
107811
|
+
'trust_level = "untrusted"',
|
|
107812
|
+
""
|
|
107813
|
+
].join("\n");
|
|
107814
|
+
writeFileSync5(join6(params.codexHome, "config.toml"), config3, { mode: 384 });
|
|
107815
|
+
log.info(`\xBB codex config: sandbox=${sandboxMode}, nativeShell=${shellEnabled}`);
|
|
107816
|
+
}
|
|
107817
|
+
function toTodoWriteInput(item) {
|
|
107818
|
+
return {
|
|
107819
|
+
todos: item.items.map((todo, index) => ({
|
|
107820
|
+
id: String(index),
|
|
107821
|
+
content: todo.text,
|
|
107822
|
+
status: todo.completed ? "completed" : "pending"
|
|
107823
|
+
}))
|
|
107824
|
+
};
|
|
107825
|
+
}
|
|
107826
|
+
async function runCodex(params) {
|
|
107827
|
+
const startTime = performance6.now();
|
|
107828
|
+
const thinkingTimer = new ThinkingTimer();
|
|
107829
|
+
const output = new TailBuffer(DEFAULT_MAX_RETAINED_BYTES);
|
|
107830
|
+
const recentStderr = [];
|
|
107831
|
+
let eventCount = 0;
|
|
107832
|
+
let finalOutput = "";
|
|
107833
|
+
let threadId;
|
|
107834
|
+
let turnError = null;
|
|
107835
|
+
let lastProviderError = null;
|
|
107836
|
+
let stdoutBuffer = "";
|
|
107837
|
+
const tokens = { input: 0, cacheRead: 0, output: 0 };
|
|
107838
|
+
function buildUsage2() {
|
|
107839
|
+
if (tokens.input === 0 && tokens.output === 0) return void 0;
|
|
107840
|
+
return {
|
|
107841
|
+
agent: "codex",
|
|
107842
|
+
inputTokens: tokens.input,
|
|
107843
|
+
outputTokens: tokens.output,
|
|
107844
|
+
cacheReadTokens: tokens.cacheRead || void 0
|
|
107845
|
+
};
|
|
107846
|
+
}
|
|
107847
|
+
function onItem(item, phase) {
|
|
107848
|
+
switch (item.type) {
|
|
107849
|
+
case "agent_message":
|
|
107850
|
+
if (phase === "completed" && item.text.trim()) {
|
|
107851
|
+
finalOutput = item.text.trim();
|
|
107852
|
+
log.box(finalOutput, { title: "Pullfrog" });
|
|
107853
|
+
}
|
|
107854
|
+
return;
|
|
107855
|
+
case "reasoning":
|
|
107856
|
+
if (phase === "completed" && item.text.trim()) {
|
|
107857
|
+
log.box(item.text.trim(), { title: "Pullfrog" });
|
|
107858
|
+
}
|
|
107859
|
+
return;
|
|
107860
|
+
case "command_execution":
|
|
107861
|
+
if (phase === "started") {
|
|
107862
|
+
thinkingTimer.markToolCall();
|
|
107863
|
+
log.toolCall({ toolName: "shell", input: { command: item.command } });
|
|
107864
|
+
return;
|
|
107865
|
+
}
|
|
107866
|
+
thinkingTimer.markToolResult();
|
|
107867
|
+
log.startGroup("shell output");
|
|
107868
|
+
log.info(item.aggregated_output || "");
|
|
107869
|
+
log.endGroup();
|
|
107870
|
+
return;
|
|
107871
|
+
case "mcp_tool_call":
|
|
107872
|
+
if (phase === "started") {
|
|
107873
|
+
thinkingTimer.markToolCall();
|
|
107874
|
+
params.onToolUse?.({ toolName: item.tool, input: item.arguments });
|
|
107875
|
+
log.toolCall({ toolName: `${item.server}__${item.tool}`, input: item.arguments });
|
|
107876
|
+
if (item.tool.includes("report_progress")) params.todoTracker?.cancel();
|
|
107877
|
+
return;
|
|
107878
|
+
}
|
|
107879
|
+
thinkingTimer.markToolResult();
|
|
107880
|
+
if (item.error) log.info(`\xBB tool error: ${item.error.message}`);
|
|
107881
|
+
return;
|
|
107882
|
+
case "file_change":
|
|
107883
|
+
if (phase === "completed") {
|
|
107884
|
+
const paths = item.changes.map((c) => `${c.kind} ${c.path}`).join(", ");
|
|
107885
|
+
log.info(`\xBB apply_patch (${item.status}): ${paths}`);
|
|
107886
|
+
}
|
|
107887
|
+
return;
|
|
107888
|
+
case "web_search":
|
|
107889
|
+
if (phase === "started") log.toolCall({ toolName: "web_search", input: item.query });
|
|
107890
|
+
return;
|
|
107891
|
+
case "todo_list":
|
|
107892
|
+
if (params.todoTracker?.enabled) params.todoTracker.update(toTodoWriteInput(item));
|
|
107893
|
+
return;
|
|
107894
|
+
case "error":
|
|
107895
|
+
log.info(`\xBB ${item.message}`);
|
|
107896
|
+
return;
|
|
107897
|
+
default:
|
|
107898
|
+
item;
|
|
107899
|
+
}
|
|
107900
|
+
}
|
|
107901
|
+
function onEvent(event) {
|
|
107902
|
+
switch (event.type) {
|
|
107903
|
+
case "thread.started":
|
|
107904
|
+
threadId = event.thread_id;
|
|
107905
|
+
return;
|
|
107906
|
+
case "turn.completed":
|
|
107907
|
+
tokens.input += event.usage.input_tokens ?? 0;
|
|
107908
|
+
tokens.cacheRead += event.usage.cached_input_tokens ?? 0;
|
|
107909
|
+
tokens.output += event.usage.output_tokens ?? 0;
|
|
107910
|
+
return;
|
|
107911
|
+
case "turn.failed":
|
|
107912
|
+
turnError = event.error.message;
|
|
107913
|
+
log.info(`\xBB turn failed: ${turnError}`);
|
|
107914
|
+
return;
|
|
107915
|
+
case "error":
|
|
107916
|
+
turnError = event.message;
|
|
107917
|
+
log.info(`\xBB codex error: ${event.message}`);
|
|
107918
|
+
return;
|
|
107919
|
+
case "item.started":
|
|
107920
|
+
onItem(event.item, "started");
|
|
107921
|
+
return;
|
|
107922
|
+
case "item.completed":
|
|
107923
|
+
onItem(event.item, "completed");
|
|
107924
|
+
return;
|
|
107925
|
+
case "item.updated":
|
|
107926
|
+
if (event.item.type === "todo_list") onItem(event.item, "started");
|
|
107927
|
+
return;
|
|
107928
|
+
case "turn.started":
|
|
107929
|
+
return;
|
|
107930
|
+
default:
|
|
107931
|
+
event;
|
|
107932
|
+
}
|
|
107933
|
+
}
|
|
107934
|
+
try {
|
|
107935
|
+
const result = await spawn({
|
|
107936
|
+
cmd: params.cliPath,
|
|
107937
|
+
args: params.args,
|
|
107938
|
+
cwd: params.cwd,
|
|
107939
|
+
env: params.env,
|
|
107940
|
+
activityTimeout: AGENT_ACTIVITY_TIMEOUT_MS,
|
|
107941
|
+
onActivityTimeout: params.onActivityTimeout,
|
|
107942
|
+
// stdin MUST be `ignore`: codex reads a piped stdin as an extra `<stdin>`
|
|
107943
|
+
// prompt block and blocks on EOF, so an inherited pipe hangs the run
|
|
107944
|
+
// before the first model call.
|
|
107945
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
107946
|
+
killGroup: true,
|
|
107947
|
+
retain: "none",
|
|
107948
|
+
onStdout: (chunk) => {
|
|
107949
|
+
output.append(chunk);
|
|
107950
|
+
markActivity();
|
|
107951
|
+
stdoutBuffer += chunk;
|
|
107952
|
+
const lines = stdoutBuffer.split("\n");
|
|
107953
|
+
stdoutBuffer = lines.pop() || "";
|
|
107954
|
+
for (const line of lines) {
|
|
107955
|
+
const trimmed = line.trim();
|
|
107956
|
+
if (!trimmed) continue;
|
|
107957
|
+
let event;
|
|
107958
|
+
try {
|
|
107959
|
+
event = JSON.parse(trimmed);
|
|
107960
|
+
} catch {
|
|
107961
|
+
log.debug(`\xBB non-JSON stdout line: ${trimmed.substring(0, 200)}`);
|
|
107962
|
+
continue;
|
|
107963
|
+
}
|
|
107964
|
+
eventCount++;
|
|
107965
|
+
log.debug(JSON.stringify(event, null, 2));
|
|
107966
|
+
const idleMs = getIdleMs();
|
|
107967
|
+
if (idleMs > 1e4) {
|
|
107968
|
+
log.info(
|
|
107969
|
+
`\xBB no activity for ${(idleMs / 1e3).toFixed(1)}s (${eventCount} events processed so far)`
|
|
107970
|
+
);
|
|
107971
|
+
}
|
|
107972
|
+
markActivity();
|
|
107973
|
+
onEvent(event);
|
|
107974
|
+
}
|
|
107975
|
+
},
|
|
107976
|
+
onStderr: (chunk) => {
|
|
107977
|
+
const trimmed = chunk.trim();
|
|
107978
|
+
if (!trimmed) return;
|
|
107979
|
+
recentStderr.push(trimmed);
|
|
107980
|
+
if (recentStderr.length > MAX_STDERR_LINES) recentStderr.shift();
|
|
107981
|
+
const match3 = findProviderErrorMatch(trimmed);
|
|
107982
|
+
if (match3) {
|
|
107983
|
+
lastProviderError = match3.label;
|
|
107984
|
+
log.info(`\xBB provider error detected (${match3.label}): ${match3.excerpt}`);
|
|
107985
|
+
} else {
|
|
107986
|
+
log.debug(trimmed);
|
|
107987
|
+
}
|
|
107988
|
+
}
|
|
107989
|
+
});
|
|
107990
|
+
const duration4 = performance6.now() - startTime;
|
|
107991
|
+
log.info(`\xBB codex completed in ${Math.round(duration4)}ms with exit code ${result.exitCode}`);
|
|
107992
|
+
const usage = buildUsage2();
|
|
107993
|
+
if (usage) {
|
|
107994
|
+
logTokenTable({
|
|
107995
|
+
input: tokens.input - tokens.cacheRead,
|
|
107996
|
+
cacheRead: tokens.cacheRead,
|
|
107997
|
+
cacheWrite: 0,
|
|
107998
|
+
output: tokens.output
|
|
107999
|
+
});
|
|
108000
|
+
}
|
|
108001
|
+
if (result.exitCode !== 0 || turnError) {
|
|
108002
|
+
const stderrSnapshot = recentStderr.join("\n");
|
|
108003
|
+
const error52 = turnError || stderrSnapshot || lastProviderError && `provider error: ${lastProviderError}` || `unknown error - no output from Codex CLI (exit ${result.exitCode})`;
|
|
108004
|
+
log.error(`codex exited with code ${result.exitCode}: ${error52}`);
|
|
108005
|
+
return { success: false, output: finalOutput || output.toString(), error: error52, usage, threadId };
|
|
108006
|
+
}
|
|
108007
|
+
return { success: true, output: finalOutput || output.toString(), usage, threadId };
|
|
108008
|
+
} catch (error52) {
|
|
108009
|
+
const duration4 = performance6.now() - startTime;
|
|
108010
|
+
const message = error52 instanceof Error ? error52.message : String(error52);
|
|
108011
|
+
const isActivityTimeout = error52 instanceof SpawnTimeoutError && error52.code === SPAWN_ACTIVITY_TIMEOUT_CODE;
|
|
108012
|
+
const diagnosis = lastProviderError ? `likely cause: ${lastProviderError}` : eventCount === 0 ? "codex produced 0 stdout events - check if the API is reachable" : `${eventCount} events were processed before the hang`;
|
|
108013
|
+
log.info(
|
|
108014
|
+
`\xBB codex ${isActivityTimeout ? "hung" : "failed"} after ${(duration4 / 1e3).toFixed(1)}s: ${message}`
|
|
108015
|
+
);
|
|
108016
|
+
log.info(`\xBB diagnosis: ${diagnosis}`);
|
|
108017
|
+
if (recentStderr.length > 0) log.info(`\xBB recent stderr:
|
|
108018
|
+
${recentStderr.join("\n")}`);
|
|
108019
|
+
return {
|
|
108020
|
+
success: false,
|
|
108021
|
+
output: finalOutput || output.toString(),
|
|
108022
|
+
error: `${message} [${diagnosis}]`,
|
|
108023
|
+
usage: buildUsage2(),
|
|
108024
|
+
threadId
|
|
108025
|
+
};
|
|
108026
|
+
}
|
|
108027
|
+
}
|
|
108028
|
+
var codex = agent({
|
|
108029
|
+
name: "codex",
|
|
108030
|
+
install: installCodexCli,
|
|
108031
|
+
run: async (ctx) => {
|
|
108032
|
+
const cliPath = await installCodexCli();
|
|
108033
|
+
warnIfNativeEditsUnavailable(ctx);
|
|
108034
|
+
const codexHomeAuth = installCodexHome();
|
|
108035
|
+
const codexHome = codexHomeAuth?.codexHome ?? join6(ctx.tmpdir, ".codex");
|
|
108036
|
+
mkdirSync6(codexHome, { recursive: true });
|
|
108037
|
+
const effort = resolveRunEffort(ctx);
|
|
108038
|
+
if (effort.rung && !CODEX_EFFORTS.includes(effort.rung)) {
|
|
108039
|
+
log.warning(`\xBB effort ${effort.rung} not sent \u2014 codex doesn't accept that level`);
|
|
108040
|
+
}
|
|
108041
|
+
writeCodexConfig({
|
|
108042
|
+
ctx,
|
|
108043
|
+
codexHome,
|
|
108044
|
+
model: resolveCodexModel(ctx),
|
|
108045
|
+
effortRung: effort.rung && CODEX_EFFORTS.includes(effort.rung) ? effort.rung : void 0
|
|
108046
|
+
});
|
|
108047
|
+
if (codexHomeAuth) {
|
|
108048
|
+
core2.saveState(
|
|
108049
|
+
"codex_writeback",
|
|
108050
|
+
JSON.stringify({
|
|
108051
|
+
apiToken: ctx.apiToken,
|
|
108052
|
+
authPath: codexHomeAuth.authPath,
|
|
108053
|
+
originalRefresh: codexHomeAuth.originalRefresh
|
|
108054
|
+
})
|
|
108055
|
+
);
|
|
108056
|
+
}
|
|
108057
|
+
const baseEnv = ctx.payload.shell === "enabled" ? process.env : filterEnv();
|
|
108058
|
+
const env2 = {
|
|
108059
|
+
...baseEnv,
|
|
108060
|
+
CODEX_HOME: codexHome,
|
|
108061
|
+
PWD: process.cwd()
|
|
108062
|
+
};
|
|
108063
|
+
delete env2.CODEX_API_KEY;
|
|
108064
|
+
if (codexHomeAuth) {
|
|
108065
|
+
delete env2.OPENAI_API_KEY;
|
|
108066
|
+
} else if (process.env.OPENAI_API_KEY) {
|
|
108067
|
+
env2.CODEX_API_KEY = process.env.OPENAI_API_KEY;
|
|
108068
|
+
}
|
|
108069
|
+
const securityFlags = securityOverrideFlags({
|
|
108070
|
+
ctx,
|
|
108071
|
+
sandboxMode: ctx.payload.push === "disabled" ? "read-only" : "workspace-write",
|
|
108072
|
+
repoDir: process.cwd(),
|
|
108073
|
+
mcpServerUrl: ctx.mcpServerUrl,
|
|
108074
|
+
model: resolveCodexModel(ctx),
|
|
108075
|
+
effortRung: effort.rung && CODEX_EFFORTS.includes(effort.rung) ? effort.rung : void 0
|
|
108076
|
+
});
|
|
108077
|
+
const runnerArgs = { cliPath, cwd: process.cwd(), env: env2, todoTracker: ctx.todoTracker };
|
|
108078
|
+
const initial = await runCodex({
|
|
108079
|
+
...runnerArgs,
|
|
108080
|
+
args: [...securityFlags, "exec", "--json", ctx.instructions.full],
|
|
108081
|
+
onActivityTimeout: ctx.onActivityTimeout,
|
|
108082
|
+
onToolUse: ctx.onToolUse
|
|
108083
|
+
});
|
|
108084
|
+
const result = await runPostRunRetryLoop({
|
|
108085
|
+
ctx,
|
|
108086
|
+
initialResult: initial,
|
|
108087
|
+
initialUsage: initial.usage,
|
|
108088
|
+
reflectionPrompt: buildReflectionPrompt(ctx.toolState),
|
|
108089
|
+
// a resume needs the thread codex recorded. without one there is nothing
|
|
108090
|
+
// to continue, and `resume --last` would be worse than not resuming: it
|
|
108091
|
+
// picks the newest recorded session in this cwd, which on a self-hosted
|
|
108092
|
+
// runner with a shared CODEX_HOME is somebody else's run.
|
|
108093
|
+
canResume: (r) => r.threadId !== void 0,
|
|
108094
|
+
resume: async (c) => runCodex({
|
|
108095
|
+
...runnerArgs,
|
|
108096
|
+
args: [
|
|
108097
|
+
...securityFlags,
|
|
108098
|
+
"exec",
|
|
108099
|
+
"resume",
|
|
108100
|
+
"--json",
|
|
108101
|
+
c.previousResult.threadId ?? "",
|
|
108102
|
+
c.prompt
|
|
108103
|
+
]
|
|
108104
|
+
})
|
|
108105
|
+
});
|
|
108106
|
+
if (result.success) {
|
|
108107
|
+
await ctx.todoTracker?.flush();
|
|
108108
|
+
} else {
|
|
108109
|
+
ctx.todoTracker?.cancel();
|
|
108110
|
+
}
|
|
108111
|
+
return result;
|
|
108112
|
+
}
|
|
108113
|
+
});
|
|
108114
|
+
|
|
108115
|
+
// agents/opencode.ts
|
|
108116
|
+
var core3 = __toESM(require_core(), 1);
|
|
108117
|
+
import { spawn as nodeSpawn2 } from "node:child_process";
|
|
108118
|
+
import { mkdirSync as mkdirSync7, writeFileSync as writeFileSync6 } from "node:fs";
|
|
108119
|
+
import { join as join7 } from "node:path";
|
|
108120
|
+
import { performance as performance7 } from "node:perf_hooks";
|
|
108121
|
+
|
|
107313
108122
|
// node_modules/.pnpm/@opencode-ai+sdk@1.18.5/node_modules/@opencode-ai/sdk/dist/v2/gen/core/serverSentEvents.gen.js
|
|
107314
108123
|
var createSseClient = ({ onRequest, onSseError, onSseEvent, responseTransformer, responseValidator, sseDefaultRetryDelay, sseMaxRetryAttempts, sseMaxRetryDelay, sseSleepFn, url: url4, ...options }) => {
|
|
107315
108124
|
let lastEventId;
|
|
@@ -112716,114 +113525,6 @@ var import_cross_spawn = __toESM(require_cross_spawn(), 1);
|
|
|
112716
113525
|
// agents/opencode.ts
|
|
112717
113526
|
var import_undici = __toESM(require_undici2(), 1);
|
|
112718
113527
|
|
|
112719
|
-
// utils/codexHome.ts
|
|
112720
|
-
import { execFileSync as execFileSync3 } from "node:child_process";
|
|
112721
|
-
import { mkdirSync as mkdirSync5, writeFileSync as writeFileSync4 } from "node:fs";
|
|
112722
|
-
import { homedir as homedir2, userInfo } from "node:os";
|
|
112723
|
-
import { join as join5 } from "node:path";
|
|
112724
|
-
|
|
112725
|
-
// utils/codexOAuth.ts
|
|
112726
|
-
function decodeJwtExpMs(token) {
|
|
112727
|
-
const parts = token.split(".");
|
|
112728
|
-
if (parts.length !== 3) return null;
|
|
112729
|
-
let payload;
|
|
112730
|
-
try {
|
|
112731
|
-
payload = JSON.parse(Buffer.from(parts[1], "base64url").toString("utf8"));
|
|
112732
|
-
} catch {
|
|
112733
|
-
return null;
|
|
112734
|
-
}
|
|
112735
|
-
if (typeof payload.exp !== "number" || !Number.isFinite(payload.exp)) return null;
|
|
112736
|
-
return payload.exp * 1e3;
|
|
112737
|
-
}
|
|
112738
|
-
function parseCodexAuthBody(raw2) {
|
|
112739
|
-
let parsed2;
|
|
112740
|
-
try {
|
|
112741
|
-
parsed2 = JSON.parse(raw2);
|
|
112742
|
-
} catch {
|
|
112743
|
-
return null;
|
|
112744
|
-
}
|
|
112745
|
-
if (!parsed2 || typeof parsed2 !== "object") return null;
|
|
112746
|
-
const v = parsed2;
|
|
112747
|
-
if (v.auth_mode !== "chatgpt") return null;
|
|
112748
|
-
const tokens = v.tokens;
|
|
112749
|
-
if (!tokens || typeof tokens !== "object") return null;
|
|
112750
|
-
const t = tokens;
|
|
112751
|
-
if (typeof t.access_token !== "string" || t.access_token.length === 0) return null;
|
|
112752
|
-
if (typeof t.refresh_token !== "string" || t.refresh_token.length === 0) return null;
|
|
112753
|
-
return {
|
|
112754
|
-
auth_mode: "chatgpt",
|
|
112755
|
-
...typeof v.refresh_rejected_at === "string" ? { refresh_rejected_at: v.refresh_rejected_at } : {},
|
|
112756
|
-
tokens: {
|
|
112757
|
-
access_token: t.access_token,
|
|
112758
|
-
refresh_token: t.refresh_token,
|
|
112759
|
-
...typeof t.id_token === "string" ? { id_token: t.id_token } : {},
|
|
112760
|
-
...typeof t.account_id === "string" ? { account_id: t.account_id } : {}
|
|
112761
|
-
},
|
|
112762
|
-
...typeof v.last_refresh === "string" ? { last_refresh: v.last_refresh } : {}
|
|
112763
|
-
};
|
|
112764
|
-
}
|
|
112765
|
-
|
|
112766
|
-
// utils/codexHome.ts
|
|
112767
|
-
var CODEX_AUTH_ENV = "CODEX_AUTH_JSON";
|
|
112768
|
-
var PULLFROG_DATA_DIR = "/var/lib/pullfrog";
|
|
112769
|
-
function installCodexAuth() {
|
|
112770
|
-
const raw2 = process.env[CODEX_AUTH_ENV];
|
|
112771
|
-
if (!raw2) return null;
|
|
112772
|
-
const body = parseCodexAuthBody(raw2);
|
|
112773
|
-
if (!body) {
|
|
112774
|
-
log.warning(`\xBB ${CODEX_AUTH_ENV} present but malformed; ignoring`);
|
|
112775
|
-
return null;
|
|
112776
|
-
}
|
|
112777
|
-
const expiresMs = decodeJwtExpMs(body.tokens.access_token) ?? 0;
|
|
112778
|
-
const xdgDataHome = resolveDataHome();
|
|
112779
|
-
const opencodeDir = join5(xdgDataHome, "opencode");
|
|
112780
|
-
const authPath = join5(opencodeDir, "auth.json");
|
|
112781
|
-
const opencodeAuth = {
|
|
112782
|
-
openai: {
|
|
112783
|
-
type: "oauth",
|
|
112784
|
-
refresh: body.tokens.refresh_token,
|
|
112785
|
-
access: body.tokens.access_token,
|
|
112786
|
-
expires: expiresMs,
|
|
112787
|
-
...body.tokens.account_id ? { accountId: body.tokens.account_id } : {}
|
|
112788
|
-
}
|
|
112789
|
-
};
|
|
112790
|
-
mkdirSync5(opencodeDir, { recursive: true });
|
|
112791
|
-
writeFileSync4(authPath, `${JSON.stringify(opencodeAuth, null, 2)}
|
|
112792
|
-
`, { mode: 384 });
|
|
112793
|
-
process.env.XDG_DATA_HOME = xdgDataHome;
|
|
112794
|
-
log.info(`\xBB installed Codex auth at ${authPath}`);
|
|
112795
|
-
return {
|
|
112796
|
-
authPath,
|
|
112797
|
-
xdgDataHome,
|
|
112798
|
-
originalRefresh: body.tokens.refresh_token
|
|
112799
|
-
};
|
|
112800
|
-
}
|
|
112801
|
-
function resolveDataHome() {
|
|
112802
|
-
if (process.env.CI !== "true") return join5(homedir2(), ".local", "share");
|
|
112803
|
-
bootstrapPullfrogDataDir();
|
|
112804
|
-
return PULLFROG_DATA_DIR;
|
|
112805
|
-
}
|
|
112806
|
-
function bootstrapPullfrogDataDir() {
|
|
112807
|
-
const user = userInfo().username;
|
|
112808
|
-
let primaryGroup;
|
|
112809
|
-
try {
|
|
112810
|
-
primaryGroup = execFileSync3("id", ["-gn", user], { stdio: "pipe", encoding: "utf-8" }).trim();
|
|
112811
|
-
} catch {
|
|
112812
|
-
primaryGroup = user;
|
|
112813
|
-
}
|
|
112814
|
-
try {
|
|
112815
|
-
execFileSync3("sudo", ["-n", "mkdir", "-p", PULLFROG_DATA_DIR], { stdio: "pipe" });
|
|
112816
|
-
execFileSync3("sudo", ["-n", "chown", `${user}:${primaryGroup}`, PULLFROG_DATA_DIR], {
|
|
112817
|
-
stdio: "pipe"
|
|
112818
|
-
});
|
|
112819
|
-
execFileSync3("sudo", ["-n", "chmod", "700", PULLFROG_DATA_DIR], { stdio: "pipe" });
|
|
112820
|
-
} catch (err) {
|
|
112821
|
-
throw new Error(
|
|
112822
|
-
`failed to bootstrap ${PULLFROG_DATA_DIR} (required for codex auth in CI): ${err instanceof Error ? err.message : String(err)}. the MCP shell's mount-namespace sandbox cannot protect the auth file when it lives under $HOME, and silently falling back would contradict the "three independent layers" claim in wiki/codex-auth.md. passwordless sudo is required for codex auth on this runner \u2014 either configure it, or remove CODEX_AUTH_JSON from the run.`
|
|
112823
|
-
);
|
|
112824
|
-
}
|
|
112825
|
-
}
|
|
112826
|
-
|
|
112827
113528
|
// utils/worktree.ts
|
|
112828
113529
|
async function dirtyTrackedPaths(params = {}) {
|
|
112829
113530
|
const result = await spawn({
|
|
@@ -113091,6 +113792,30 @@ function openAICompatibleProvider(model) {
|
|
|
113091
113792
|
}
|
|
113092
113793
|
};
|
|
113093
113794
|
}
|
|
113795
|
+
function azureProvider(model) {
|
|
113796
|
+
const prefix = `${AZURE_PROVIDER}/`;
|
|
113797
|
+
if (!model?.startsWith(prefix)) return {};
|
|
113798
|
+
const deployment = model.slice(prefix.length);
|
|
113799
|
+
return {
|
|
113800
|
+
[AZURE_PROVIDER]: {
|
|
113801
|
+
// always sent: opencode reads it as `Boolean(options?.useCompletionUrls)`,
|
|
113802
|
+
// so `false` and absent are the same, and the deep merge keeps the native
|
|
113803
|
+
// loader's `resourceName` either way.
|
|
113804
|
+
options: {
|
|
113805
|
+
useCompletionUrls: process.env[AZURE_USE_CHAT_COMPLETIONS_ENV]?.trim() === "true"
|
|
113806
|
+
},
|
|
113807
|
+
models: {
|
|
113808
|
+
[deployment]: {
|
|
113809
|
+
name: deployment,
|
|
113810
|
+
limit: {
|
|
113811
|
+
context: Number(process.env[AZURE_CONTEXT_ENV]),
|
|
113812
|
+
output: Number(process.env[AZURE_MAX_OUTPUT_ENV])
|
|
113813
|
+
}
|
|
113814
|
+
}
|
|
113815
|
+
}
|
|
113816
|
+
}
|
|
113817
|
+
};
|
|
113818
|
+
}
|
|
113094
113819
|
var KIMI_ENFORCERLESS_PROVIDERS = ["siliconflow", "together"];
|
|
113095
113820
|
function kimiOpenRouterProviderOverrides() {
|
|
113096
113821
|
return Object.fromEntries(
|
|
@@ -113182,7 +113907,8 @@ function buildSecurityConfig(ctx, model) {
|
|
|
113182
113907
|
// see opencodeShared.ts.
|
|
113183
113908
|
provider: {
|
|
113184
113909
|
openrouter: { models: kimiOpenRouterProviderOverrides() },
|
|
113185
|
-
...openAICompatibleProvider(model)
|
|
113910
|
+
...openAICompatibleProvider(model),
|
|
113911
|
+
...azureProvider(model)
|
|
113186
113912
|
}
|
|
113187
113913
|
};
|
|
113188
113914
|
if (model) {
|
|
@@ -113350,7 +114076,7 @@ function isModelOutput(ctx, part) {
|
|
|
113350
114076
|
}
|
|
113351
114077
|
async function dispatchEvent(ctx, event) {
|
|
113352
114078
|
if (event.type === "message.part.updated") {
|
|
113353
|
-
ctx.lastEventAt =
|
|
114079
|
+
ctx.lastEventAt = performance7.now();
|
|
113354
114080
|
if (isModelOutput(ctx, event.properties.part)) ctx.sawModelOutput = true;
|
|
113355
114081
|
await onPartUpdated(ctx, event.properties.part);
|
|
113356
114082
|
return;
|
|
@@ -113417,7 +114143,7 @@ async function onToolPart(ctx, part, label, isOrchestrator) {
|
|
|
113417
114143
|
if (toolName === "task" && status === "running" && isOrchestrator && !ctx.taskDispatchByCallID.has(toolId)) {
|
|
113418
114144
|
const input = part.state.input ?? {};
|
|
113419
114145
|
const dispatched = ctx.labeler.recordTaskDispatch(input);
|
|
113420
|
-
ctx.taskDispatchByCallID.set(toolId, { label: dispatched, startedAt:
|
|
114146
|
+
ctx.taskDispatchByCallID.set(toolId, { label: dispatched, startedAt: performance7.now() });
|
|
113421
114147
|
log.info(
|
|
113422
114148
|
`\xBB dispatching subagent: ${dispatched}` + (input.subagent_type ? ` (subagent_type=${input.subagent_type})` : "")
|
|
113423
114149
|
);
|
|
@@ -113436,6 +114162,7 @@ function processTerminalToolPart(ctx, part, label, isOrchestrator) {
|
|
|
113436
114162
|
const callLine = inputFormatted !== "{}" ? `\xBB ${toolName}(${inputFormatted})` : `\xBB ${toolName}()`;
|
|
113437
114163
|
log.info(withLabel(label, callLine));
|
|
113438
114164
|
if (isOrchestrator) ctx.loggedToolCallIDs.add(toolId);
|
|
114165
|
+
if (isOrchestrator && toolName.startsWith("pullfrog_")) ctx.mcpToolCalls++;
|
|
113439
114166
|
if (state.status === "completed") {
|
|
113440
114167
|
log.debug(withLabel(label, ` output: ${state.output}`));
|
|
113441
114168
|
} else {
|
|
@@ -113447,7 +114174,7 @@ function processTerminalToolPart(ctx, part, label, isOrchestrator) {
|
|
|
113447
114174
|
if (toolName === "task") {
|
|
113448
114175
|
const dispatch = ctx.taskDispatchByCallID.get(toolId);
|
|
113449
114176
|
if (dispatch) {
|
|
113450
|
-
const dur = ((
|
|
114177
|
+
const dur = ((performance7.now() - dispatch.startedAt) / 1e3).toFixed(1);
|
|
113451
114178
|
const outputStr = state.status === "completed" ? state.output : "";
|
|
113452
114179
|
const preview = typeof outputStr === "string" && outputStr.length > 120 ? `${outputStr.slice(0, 120)}\u2026` : outputStr;
|
|
113453
114180
|
log.info(
|
|
@@ -113490,7 +114217,7 @@ function withLabel(label, message) {
|
|
|
113490
114217
|
return label === ORCHESTRATOR_LABEL ? message : formatWithLabel(label, message);
|
|
113491
114218
|
}
|
|
113492
114219
|
async function runPromptTurn(ctx, params) {
|
|
113493
|
-
const start =
|
|
114220
|
+
const start = performance7.now();
|
|
113494
114221
|
const turnStartMs = Date.now();
|
|
113495
114222
|
ctx.currentTurn = newTurn();
|
|
113496
114223
|
const turn = ctx.currentTurn;
|
|
@@ -113524,7 +114251,7 @@ async function runPromptTurn(ctx, params) {
|
|
|
113524
114251
|
} catch (err) {
|
|
113525
114252
|
networkError = err instanceof Error ? err.message : String(err);
|
|
113526
114253
|
}
|
|
113527
|
-
const durationMs =
|
|
114254
|
+
const durationMs = performance7.now() - start;
|
|
113528
114255
|
const aggregatedUsage = await aggregateTurnUsage(ctx, turnStartMs);
|
|
113529
114256
|
const usage = aggregatedUsage ?? buildUsage(turn, assistant);
|
|
113530
114257
|
const finalText = turn.finalText || extractTextFromParts(returnedParts) || "";
|
|
@@ -113674,21 +114401,28 @@ function formatConfigError(name, data) {
|
|
|
113674
114401
|
).join("");
|
|
113675
114402
|
return `${name}${detail}${bullets}`;
|
|
113676
114403
|
}
|
|
114404
|
+
var WATCHDOG_SALVAGE_PROMPT = "Your previous turn was cut off mid-response by a provider stall. Nothing you had not already submitted through a tool was saved. Continue from where you stopped and submit your work now \u2014 do not restart your analysis.";
|
|
113677
114405
|
function startInnerActivityWatchdog(params) {
|
|
113678
114406
|
let fired = false;
|
|
114407
|
+
let everFired = false;
|
|
113679
114408
|
const id = setInterval(() => {
|
|
113680
114409
|
if (fired) return;
|
|
113681
|
-
const idleMs =
|
|
113682
|
-
const
|
|
114410
|
+
const idleMs = performance7.now() - params.ctx.lastEventAt;
|
|
114411
|
+
const compiledMs = params.ctx.sawModelOutput ? params.timeoutMs : AGENT_FIRST_EVENT_TIMEOUT_MS;
|
|
114412
|
+
const budgetMs = everFired ? compiledMs : watchdogBudgetMs(
|
|
114413
|
+
compiledMs,
|
|
114414
|
+
params.ctx.sawModelOutput ? "PULLFROG_E2E_ACTIVITY_TIMEOUT_MS" : "PULLFROG_E2E_FIRST_EVENT_TIMEOUT_MS"
|
|
114415
|
+
);
|
|
113683
114416
|
if (idleMs <= budgetMs) return;
|
|
113684
114417
|
fired = true;
|
|
114418
|
+
everFired = true;
|
|
113685
114419
|
const idleSec = Math.round(idleMs / 1e3);
|
|
113686
114420
|
params.ctx.diagnostic.idleSec = idleSec;
|
|
113687
114421
|
params.ctx.diagnostic.sawModelOutput = params.ctx.sawModelOutput;
|
|
113688
114422
|
log.info(
|
|
113689
114423
|
params.ctx.sawModelOutput ? `\xBB no opencode events for ${idleSec}s \u2014 aborting in-flight prompt and notifying harness` : `\xBB no opencode events for ${idleSec}s \u2014 the provider never returned a first token; aborting in-flight prompt and notifying harness`
|
|
113690
114424
|
);
|
|
113691
|
-
params.
|
|
114425
|
+
params.abortTurn();
|
|
113692
114426
|
try {
|
|
113693
114427
|
params.ctx.onActivityTimeout?.();
|
|
113694
114428
|
} catch (err) {
|
|
@@ -113698,7 +114432,22 @@ function startInnerActivityWatchdog(params) {
|
|
|
113698
114432
|
}
|
|
113699
114433
|
}, 5e3);
|
|
113700
114434
|
id.unref?.();
|
|
113701
|
-
return {
|
|
114435
|
+
return {
|
|
114436
|
+
stop: () => clearInterval(id),
|
|
114437
|
+
/**
|
|
114438
|
+
* Start a turn's idle budget from now. The clock must be reset with the
|
|
114439
|
+
* latch: `lastEventAt` only advances on model output, so a turn following a
|
|
114440
|
+
* stall would inherit the full stalled interval and be aborted on the very
|
|
114441
|
+
* next 5s tick — killing the salvage before the provider could answer. It
|
|
114442
|
+
* is the right semantics for an ordinary resume too, whose budget should
|
|
114443
|
+
* not be pre-spent by the post-run gate checks that ran between turns.
|
|
114444
|
+
*/
|
|
114445
|
+
armForTurn: () => {
|
|
114446
|
+
fired = false;
|
|
114447
|
+
params.ctx.lastEventAt = performance7.now();
|
|
114448
|
+
},
|
|
114449
|
+
firedThisTurn: () => fired
|
|
114450
|
+
};
|
|
113702
114451
|
}
|
|
113703
114452
|
var opencode = agent({
|
|
113704
114453
|
name: "opencode",
|
|
@@ -113717,12 +114466,12 @@ var opencode = agent({
|
|
|
113717
114466
|
const model = vertexModel ?? (isBedrockRoute ? `amazon-bedrock/${rawModel}` : rawModel);
|
|
113718
114467
|
const homeEnv = {
|
|
113719
114468
|
HOME: ctx.tmpdir,
|
|
113720
|
-
XDG_CONFIG_HOME:
|
|
114469
|
+
XDG_CONFIG_HOME: join7(ctx.tmpdir, ".config")
|
|
113721
114470
|
};
|
|
113722
|
-
const opencodePluginDir =
|
|
113723
|
-
|
|
113724
|
-
|
|
113725
|
-
|
|
114471
|
+
const opencodePluginDir = join7(homeEnv.XDG_CONFIG_HOME, "opencode", "plugin");
|
|
114472
|
+
mkdirSync7(opencodePluginDir, { recursive: true });
|
|
114473
|
+
writeFileSync6(
|
|
114474
|
+
join7(opencodePluginDir, PULLFROG_OPENCODE_GATE_PLUGIN_FILENAME),
|
|
113726
114475
|
buildOpencodeSubagentGateSource(ctx.subagentDeniedTools)
|
|
113727
114476
|
);
|
|
113728
114477
|
const agentBrowserVersion = getDevDependencyVersion("agent-browser");
|
|
@@ -113753,12 +114502,13 @@ var opencode = agent({
|
|
|
113753
114502
|
if (codexAuth) {
|
|
113754
114503
|
env2.XDG_DATA_HOME = codexAuth.xdgDataHome;
|
|
113755
114504
|
delete env2.OPENAI_API_KEY;
|
|
113756
|
-
|
|
114505
|
+
core3.saveState(
|
|
113757
114506
|
"codex_writeback",
|
|
113758
114507
|
JSON.stringify({
|
|
113759
114508
|
apiToken: ctx.apiToken,
|
|
113760
114509
|
authPath: codexAuth.authPath,
|
|
113761
|
-
originalRefresh: codexAuth.originalRefresh
|
|
114510
|
+
originalRefresh: codexAuth.originalRefresh,
|
|
114511
|
+
originalIdToken: codexAuth.originalIdToken
|
|
113762
114512
|
})
|
|
113763
114513
|
);
|
|
113764
114514
|
}
|
|
@@ -113809,14 +114559,16 @@ var opencode = agent({
|
|
|
113809
114559
|
variant: effort.rung,
|
|
113810
114560
|
todoTracker: ctx.todoTracker,
|
|
113811
114561
|
onActivityTimeout: ctx.onActivityTimeout,
|
|
114562
|
+
onTurnRecovered: ctx.onTurnRecovered,
|
|
113812
114563
|
onToolUse: ctx.onToolUse,
|
|
113813
114564
|
currentTurn: null,
|
|
113814
114565
|
eventCount: 0,
|
|
113815
|
-
lastEventAt:
|
|
114566
|
+
lastEventAt: performance7.now(),
|
|
113816
114567
|
sawModelOutput: false,
|
|
113817
114568
|
promptMessageID: void 0,
|
|
113818
114569
|
taskDispatchByCallID: /* @__PURE__ */ new Map(),
|
|
113819
114570
|
loggedToolCallIDs: /* @__PURE__ */ new Set(),
|
|
114571
|
+
mcpToolCalls: 0,
|
|
113820
114572
|
recentStderr: server.recentStderr,
|
|
113821
114573
|
diagnostic: {
|
|
113822
114574
|
label: "Pullfrog",
|
|
@@ -113840,9 +114592,14 @@ var opencode = agent({
|
|
|
113840
114592
|
}
|
|
113841
114593
|
}
|
|
113842
114594
|
});
|
|
113843
|
-
const
|
|
113844
|
-
|
|
113845
|
-
|
|
114595
|
+
const runController = new AbortController();
|
|
114596
|
+
let turnController = new AbortController();
|
|
114597
|
+
const nextTurnSignal = () => {
|
|
114598
|
+
turnController = new AbortController();
|
|
114599
|
+
return AbortSignal.any([runController.signal, turnController.signal]);
|
|
114600
|
+
};
|
|
114601
|
+
const eventLoopPromise = consumeEvents(runnerCtx, runController.signal).catch((err) => {
|
|
114602
|
+
if (!runController.signal.aborted) {
|
|
113846
114603
|
log.warning(
|
|
113847
114604
|
`\xBB opencode event subscription ended: ${err instanceof Error ? err.message : String(err)}`
|
|
113848
114605
|
);
|
|
@@ -113858,31 +114615,48 @@ var opencode = agent({
|
|
|
113858
114615
|
// a long synchronous tool call (no part.updated while it runs) can't
|
|
113859
114616
|
// false-positive it.
|
|
113860
114617
|
timeoutMs: AGENT_ACTIVITY_TIMEOUT_MS,
|
|
113861
|
-
|
|
114618
|
+
abortTurn: () => turnController.abort()
|
|
113862
114619
|
});
|
|
113863
114620
|
const sdkModel = parseModel2(model);
|
|
114621
|
+
let salvagesLeft = 1;
|
|
114622
|
+
const runTurn = async (text) => {
|
|
114623
|
+
const attempt = (prompt) => {
|
|
114624
|
+
watchdog.armForTurn();
|
|
114625
|
+
return runTurnGuarded(
|
|
114626
|
+
runnerCtx,
|
|
114627
|
+
() => runPromptTurn(runnerCtx, { text: prompt, model: sdkModel, signal: nextTurnSignal() })
|
|
114628
|
+
);
|
|
114629
|
+
};
|
|
114630
|
+
const standDownIfRecovered = (turn) => {
|
|
114631
|
+
if (watchdog.firedThisTurn() && turn.success) ctx.onTurnRecovered?.();
|
|
114632
|
+
return turn;
|
|
114633
|
+
};
|
|
114634
|
+
const result = await attempt(text);
|
|
114635
|
+
if (!watchdog.firedThisTurn()) return result;
|
|
114636
|
+
if (result.success) return standDownIfRecovered(result);
|
|
114637
|
+
if (salvagesLeft <= 0) return result;
|
|
114638
|
+
salvagesLeft--;
|
|
114639
|
+
ctx.onTurnRecovered?.();
|
|
114640
|
+
log.info("\xBB activity watchdog cut the turn off \u2014 re-prompting once on the same session");
|
|
114641
|
+
const mcpCallsBefore = runnerCtx.mcpToolCalls;
|
|
114642
|
+
const salvaged = await attempt(WATCHDOG_SALVAGE_PROMPT);
|
|
114643
|
+
const usage = mergeAgentUsage(result.usage, salvaged.usage);
|
|
114644
|
+
if (runnerCtx.mcpToolCalls === mcpCallsBefore) {
|
|
114645
|
+
log.info(
|
|
114646
|
+
"\xBB salvage turn reached no Pullfrog tool \u2014 keeping the activity-timeout failure"
|
|
114647
|
+
);
|
|
114648
|
+
return { ...result, usage };
|
|
114649
|
+
}
|
|
114650
|
+
return standDownIfRecovered({ ...salvaged, usage });
|
|
114651
|
+
};
|
|
113864
114652
|
try {
|
|
113865
|
-
const initial = await
|
|
113866
|
-
runnerCtx,
|
|
113867
|
-
() => runPromptTurn(runnerCtx, {
|
|
113868
|
-
text: ctx.instructions.full,
|
|
113869
|
-
model: sdkModel,
|
|
113870
|
-
signal: abortController.signal
|
|
113871
|
-
})
|
|
113872
|
-
);
|
|
114653
|
+
const initial = await runTurn(ctx.instructions.full);
|
|
113873
114654
|
const result = await runPostRunRetryLoop({
|
|
113874
114655
|
ctx,
|
|
113875
114656
|
initialResult: initial,
|
|
113876
114657
|
initialUsage: initial.usage,
|
|
113877
114658
|
reflectionPrompt: buildReflectionPrompt(ctx.toolState),
|
|
113878
|
-
resume: async (c) =>
|
|
113879
|
-
runnerCtx,
|
|
113880
|
-
() => runPromptTurn(runnerCtx, {
|
|
113881
|
-
text: c.prompt,
|
|
113882
|
-
model: sdkModel,
|
|
113883
|
-
signal: abortController.signal
|
|
113884
|
-
})
|
|
113885
|
-
)
|
|
114659
|
+
resume: async (c) => runTurn(c.prompt)
|
|
113886
114660
|
});
|
|
113887
114661
|
if (result.success) {
|
|
113888
114662
|
await ctx.todoTracker?.flush();
|
|
@@ -113892,7 +114666,7 @@ var opencode = agent({
|
|
|
113892
114666
|
return result;
|
|
113893
114667
|
} finally {
|
|
113894
114668
|
watchdog.stop();
|
|
113895
|
-
|
|
114669
|
+
runController.abort();
|
|
113896
114670
|
await eventLoopPromise.catch(() => {
|
|
113897
114671
|
});
|
|
113898
114672
|
}
|
|
@@ -113922,7 +114696,7 @@ async function runTurnGuarded(ctx, fn2) {
|
|
|
113922
114696
|
}
|
|
113923
114697
|
|
|
113924
114698
|
// agents/index.ts
|
|
113925
|
-
var agents = { claude, opencode };
|
|
114699
|
+
var agents = { claude, codex, opencode };
|
|
113926
114700
|
|
|
113927
114701
|
// node_modules/.pnpm/@ark+util@0.56.0/node_modules/@ark/util/out/arrays.js
|
|
113928
114702
|
var liftArray = (data) => Array.isArray(data) ? data : [data];
|
|
@@ -134254,7 +135028,7 @@ var require_core$2 = /* @__PURE__ */ __commonJSMin(((exports) => {
|
|
|
134254
135028
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
134255
135029
|
const id_1 = require_id2();
|
|
134256
135030
|
const ref_1 = require_ref2();
|
|
134257
|
-
const
|
|
135031
|
+
const core11 = [
|
|
134258
135032
|
"$schema",
|
|
134259
135033
|
"$id",
|
|
134260
135034
|
"$defs",
|
|
@@ -134264,7 +135038,7 @@ var require_core$2 = /* @__PURE__ */ __commonJSMin(((exports) => {
|
|
|
134264
135038
|
id_1.default,
|
|
134265
135039
|
ref_1.default
|
|
134266
135040
|
];
|
|
134267
|
-
exports.default =
|
|
135041
|
+
exports.default = core11;
|
|
134268
135042
|
}));
|
|
134269
135043
|
var require_limitNumber2 = /* @__PURE__ */ __commonJSMin(((exports) => {
|
|
134270
135044
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
@@ -136402,7 +137176,7 @@ var require_json_schema_2019_09 = /* @__PURE__ */ __commonJSMin(((exports) => {
|
|
|
136402
137176
|
const metaSchema = require_schema$1();
|
|
136403
137177
|
const applicator = require_applicator$1();
|
|
136404
137178
|
const content = require_content$1();
|
|
136405
|
-
const
|
|
137179
|
+
const core11 = require_core$1();
|
|
136406
137180
|
const format2 = require_format3();
|
|
136407
137181
|
const metadata = require_meta_data$1();
|
|
136408
137182
|
const validation = require_validation$1();
|
|
@@ -136412,7 +137186,7 @@ var require_json_schema_2019_09 = /* @__PURE__ */ __commonJSMin(((exports) => {
|
|
|
136412
137186
|
metaSchema,
|
|
136413
137187
|
applicator,
|
|
136414
137188
|
content,
|
|
136415
|
-
|
|
137189
|
+
core11,
|
|
136416
137190
|
with$data(this, format2),
|
|
136417
137191
|
metadata,
|
|
136418
137192
|
with$data(this, validation)
|
|
@@ -136852,7 +137626,7 @@ var require_json_schema_2020_12 = /* @__PURE__ */ __commonJSMin(((exports) => {
|
|
|
136852
137626
|
const applicator = require_applicator2();
|
|
136853
137627
|
const unevaluated = require_unevaluated();
|
|
136854
137628
|
const content = require_content();
|
|
136855
|
-
const
|
|
137629
|
+
const core11 = require_core4();
|
|
136856
137630
|
const format2 = require_format_annotation();
|
|
136857
137631
|
const metadata = require_meta_data();
|
|
136858
137632
|
const validation = require_validation2();
|
|
@@ -136863,7 +137637,7 @@ var require_json_schema_2020_12 = /* @__PURE__ */ __commonJSMin(((exports) => {
|
|
|
136863
137637
|
applicator,
|
|
136864
137638
|
unevaluated,
|
|
136865
137639
|
content,
|
|
136866
|
-
|
|
137640
|
+
core11,
|
|
136867
137641
|
with$data(this, format2),
|
|
136868
137642
|
metadata,
|
|
136869
137643
|
with$data(this, validation)
|
|
@@ -148305,7 +149079,7 @@ var require_core$22 = /* @__PURE__ */ __commonJSMin2(((exports) => {
|
|
|
148305
149079
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
148306
149080
|
const id_1 = require_id3();
|
|
148307
149081
|
const ref_1 = require_ref3();
|
|
148308
|
-
const
|
|
149082
|
+
const core11 = [
|
|
148309
149083
|
"$schema",
|
|
148310
149084
|
"$id",
|
|
148311
149085
|
"$defs",
|
|
@@ -148315,7 +149089,7 @@ var require_core$22 = /* @__PURE__ */ __commonJSMin2(((exports) => {
|
|
|
148315
149089
|
id_1.default,
|
|
148316
149090
|
ref_1.default
|
|
148317
149091
|
];
|
|
148318
|
-
exports.default =
|
|
149092
|
+
exports.default = core11;
|
|
148319
149093
|
}));
|
|
148320
149094
|
var require_limitNumber3 = /* @__PURE__ */ __commonJSMin2(((exports) => {
|
|
148321
149095
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
@@ -150453,7 +151227,7 @@ var require_json_schema_2019_092 = /* @__PURE__ */ __commonJSMin2(((exports) =>
|
|
|
150453
151227
|
const metaSchema = require_schema$12();
|
|
150454
151228
|
const applicator = require_applicator$12();
|
|
150455
151229
|
const content = require_content$12();
|
|
150456
|
-
const
|
|
151230
|
+
const core11 = require_core$12();
|
|
150457
151231
|
const format2 = require_format4();
|
|
150458
151232
|
const metadata = require_meta_data$12();
|
|
150459
151233
|
const validation = require_validation$12();
|
|
@@ -150463,7 +151237,7 @@ var require_json_schema_2019_092 = /* @__PURE__ */ __commonJSMin2(((exports) =>
|
|
|
150463
151237
|
metaSchema,
|
|
150464
151238
|
applicator,
|
|
150465
151239
|
content,
|
|
150466
|
-
|
|
151240
|
+
core11,
|
|
150467
151241
|
with$data(this, format2),
|
|
150468
151242
|
metadata,
|
|
150469
151243
|
with$data(this, validation)
|
|
@@ -150903,7 +151677,7 @@ var require_json_schema_2020_122 = /* @__PURE__ */ __commonJSMin2(((exports) =>
|
|
|
150903
151677
|
const applicator = require_applicator3();
|
|
150904
151678
|
const unevaluated = require_unevaluated2();
|
|
150905
151679
|
const content = require_content2();
|
|
150906
|
-
const
|
|
151680
|
+
const core11 = require_core5();
|
|
150907
151681
|
const format2 = require_format_annotation2();
|
|
150908
151682
|
const metadata = require_meta_data2();
|
|
150909
151683
|
const validation = require_validation3();
|
|
@@ -150914,7 +151688,7 @@ var require_json_schema_2020_122 = /* @__PURE__ */ __commonJSMin2(((exports) =>
|
|
|
150914
151688
|
applicator,
|
|
150915
151689
|
unevaluated,
|
|
150916
151690
|
content,
|
|
150917
|
-
|
|
151691
|
+
core11,
|
|
150918
151692
|
with$data(this, format2),
|
|
150919
151693
|
metadata,
|
|
150920
151694
|
with$data(this, validation)
|
|
@@ -170875,123 +171649,8 @@ var pbkdf2Async = promisify(pbkdf2);
|
|
|
170875
171649
|
|
|
170876
171650
|
// utils/browser.ts
|
|
170877
171651
|
import { execFileSync as execFileSync4, spawnSync as spawnSync4 } from "node:child_process";
|
|
170878
|
-
import { existsSync as
|
|
171652
|
+
import { existsSync as existsSync5 } from "node:fs";
|
|
170879
171653
|
import { dirname as dirname2 } from "node:path";
|
|
170880
|
-
|
|
170881
|
-
// utils/secrets.ts
|
|
170882
|
-
var SENSITIVE_PATTERNS = [
|
|
170883
|
-
/_KEY$/i,
|
|
170884
|
-
/_SECRET$/i,
|
|
170885
|
-
/_TOKEN$/i,
|
|
170886
|
-
/_PASSWORD$/i,
|
|
170887
|
-
/_CREDENTIAL$/i
|
|
170888
|
-
];
|
|
170889
|
-
function isSensitiveEnvName(key) {
|
|
170890
|
-
return SENSITIVE_PATTERNS.some((p) => p.test(key));
|
|
170891
|
-
}
|
|
170892
|
-
var SAFE_ENV_PREFIXES = ["GITHUB_", "RUNNER_", "JAVA_HOME_", "GOROOT_"];
|
|
170893
|
-
var SAFE_ENV_NAMES = /* @__PURE__ */ new Set([
|
|
170894
|
-
// system
|
|
170895
|
-
"CI",
|
|
170896
|
-
"HOME",
|
|
170897
|
-
"LANG",
|
|
170898
|
-
"LOGNAME",
|
|
170899
|
-
"PATH",
|
|
170900
|
-
"SHELL",
|
|
170901
|
-
"SHLVL",
|
|
170902
|
-
"TERM",
|
|
170903
|
-
"TMPDIR",
|
|
170904
|
-
"TZ",
|
|
170905
|
-
"USER",
|
|
170906
|
-
"XDG_CONFIG_HOME",
|
|
170907
|
-
"XDG_RUNTIME_DIR",
|
|
170908
|
-
"DEBIAN_FRONTEND",
|
|
170909
|
-
// runner image toolchain
|
|
170910
|
-
"ACCEPT_EULA",
|
|
170911
|
-
"AGENT_TOOLSDIRECTORY",
|
|
170912
|
-
"ANDROID_HOME",
|
|
170913
|
-
"ANDROID_NDK",
|
|
170914
|
-
"ANDROID_NDK_HOME",
|
|
170915
|
-
"ANDROID_NDK_LATEST_HOME",
|
|
170916
|
-
"ANDROID_NDK_ROOT",
|
|
170917
|
-
"ANDROID_SDK_ROOT",
|
|
170918
|
-
"ANT_HOME",
|
|
170919
|
-
"AZURE_EXTENSION_DIR",
|
|
170920
|
-
"BOOTSTRAP_HASKELL_NONINTERACTIVE",
|
|
170921
|
-
"CHROME_BIN",
|
|
170922
|
-
"CHROMEWEBDRIVER",
|
|
170923
|
-
"CONDA",
|
|
170924
|
-
"DOTNET_MULTILEVEL_LOOKUP",
|
|
170925
|
-
"DOTNET_NOLOGO",
|
|
170926
|
-
"DOTNET_SKIP_FIRST_TIME_EXPERIENCE",
|
|
170927
|
-
"EDGEWEBDRIVER",
|
|
170928
|
-
"GECKOWEBDRIVER",
|
|
170929
|
-
"GHCUP_INSTALL_BASE_PREFIX",
|
|
170930
|
-
"GRADLE_HOME",
|
|
170931
|
-
"JAVA_HOME",
|
|
170932
|
-
"HOMEBREW_CLEANUP_PERIODIC_FULL_DAYS",
|
|
170933
|
-
"HOMEBREW_NO_AUTO_UPDATE",
|
|
170934
|
-
"ImageOS",
|
|
170935
|
-
"ImageVersion",
|
|
170936
|
-
"NVM_DIR",
|
|
170937
|
-
"PIPX_BIN_DIR",
|
|
170938
|
-
"PIPX_HOME",
|
|
170939
|
-
"PSModulePath",
|
|
170940
|
-
"SELENIUM_JAR_PATH",
|
|
170941
|
-
"SGX_AESM_ADDR",
|
|
170942
|
-
"SWIFT_PATH",
|
|
170943
|
-
"VCPKG_INSTALLATION_ROOT"
|
|
170944
|
-
]);
|
|
170945
|
-
var _userAllowlist = null;
|
|
170946
|
-
function setEnvAllowlist(raw2) {
|
|
170947
|
-
const names = raw2.split("\n").map((line) => line.trim()).filter(Boolean);
|
|
170948
|
-
_userAllowlist = new Set(names);
|
|
170949
|
-
}
|
|
170950
|
-
function isSafeEnvVar(key) {
|
|
170951
|
-
if (SAFE_ENV_NAMES.has(key)) return true;
|
|
170952
|
-
return SAFE_ENV_PREFIXES.some((p) => key.startsWith(p));
|
|
170953
|
-
}
|
|
170954
|
-
var WORKFLOW_COMMAND_ENV_NAMES = [
|
|
170955
|
-
"GITHUB_ENV",
|
|
170956
|
-
"GITHUB_PATH",
|
|
170957
|
-
"GITHUB_OUTPUT",
|
|
170958
|
-
"GITHUB_STATE",
|
|
170959
|
-
"GITHUB_STEP_SUMMARY",
|
|
170960
|
-
// dropping the five paths above but keeping RUNNER_TEMP would be theatre:
|
|
170961
|
-
// the file-command files live in `$RUNNER_TEMP/_runner_file_commands/`, so the
|
|
170962
|
-
// directory is one `ls` away. nothing in the install path reads it (only our
|
|
170963
|
-
// own leak-surface wipe in `setup.ts`, which runs in the parent), and package
|
|
170964
|
-
// managers use TMPDIR.
|
|
170965
|
-
"RUNNER_TEMP"
|
|
170966
|
-
];
|
|
170967
|
-
function filterEnvForUntrustedCode() {
|
|
170968
|
-
const env2 = filterEnv();
|
|
170969
|
-
for (const name of WORKFLOW_COMMAND_ENV_NAMES) delete env2[name];
|
|
170970
|
-
return env2;
|
|
170971
|
-
}
|
|
170972
|
-
function filterEnv() {
|
|
170973
|
-
const filtered = {};
|
|
170974
|
-
for (const [key, value2] of Object.entries(process.env)) {
|
|
170975
|
-
if (value2 === void 0) continue;
|
|
170976
|
-
const userAllowed = _userAllowlist?.has(key) ?? false;
|
|
170977
|
-
if (isSensitiveEnvName(key) && !userAllowed) continue;
|
|
170978
|
-
if (isSafeEnvVar(key) || userAllowed) {
|
|
170979
|
-
filtered[key] = value2;
|
|
170980
|
-
}
|
|
170981
|
-
}
|
|
170982
|
-
return filtered;
|
|
170983
|
-
}
|
|
170984
|
-
function resolveEnv(mode) {
|
|
170985
|
-
if (mode === "inherit") {
|
|
170986
|
-
return process.env;
|
|
170987
|
-
}
|
|
170988
|
-
if (mode === "restricted" || mode === void 0) {
|
|
170989
|
-
return filterEnv();
|
|
170990
|
-
}
|
|
170991
|
-
return { ...filterEnv(), ...mode };
|
|
170992
|
-
}
|
|
170993
|
-
|
|
170994
|
-
// utils/browser.ts
|
|
170995
171654
|
var CHROME_PATHS = ["/usr/bin/google-chrome-stable"];
|
|
170996
171655
|
var systemChromePath;
|
|
170997
171656
|
function findSystemChromePath() {
|
|
@@ -170999,7 +171658,7 @@ function findSystemChromePath() {
|
|
|
170999
171658
|
return systemChromePath || void 0;
|
|
171000
171659
|
}
|
|
171001
171660
|
for (const p of CHROME_PATHS) {
|
|
171002
|
-
if (
|
|
171661
|
+
if (existsSync5(p)) {
|
|
171003
171662
|
systemChromePath = p;
|
|
171004
171663
|
log.info(`found system chrome: ${p}`);
|
|
171005
171664
|
return p;
|
|
@@ -171083,8 +171742,8 @@ function closeBrowserDaemon(toolState) {
|
|
|
171083
171742
|
|
|
171084
171743
|
// mcp/checkout.ts
|
|
171085
171744
|
import { createHash as createHash2 } from "node:crypto";
|
|
171086
|
-
import { statSync, unlinkSync as unlinkSync3, writeFileSync as
|
|
171087
|
-
import { join as
|
|
171745
|
+
import { statSync, unlinkSync as unlinkSync3, writeFileSync as writeFileSync9 } from "node:fs";
|
|
171746
|
+
import { join as join14 } from "node:path";
|
|
171088
171747
|
|
|
171089
171748
|
// node_modules/.pnpm/@ark+schema@0.56.0/node_modules/@ark/schema/out/shared/errors.js
|
|
171090
171749
|
var ArkError = class _ArkError extends CastableBase {
|
|
@@ -178556,8 +179215,8 @@ function ensureRepoState(toolState, init) {
|
|
|
178556
179215
|
|
|
178557
179216
|
// utils/changeImpact.ts
|
|
178558
179217
|
import { spawn as spawn2 } from "node:child_process";
|
|
178559
|
-
import { writeFileSync as
|
|
178560
|
-
import { join as
|
|
179218
|
+
import { writeFileSync as writeFileSync7 } from "node:fs";
|
|
179219
|
+
import { join as join8 } from "node:path";
|
|
178561
179220
|
var MAX_CANDIDATES = 24;
|
|
178562
179221
|
var MAX_ATOM_LENGTH = 128;
|
|
178563
179222
|
var MAX_ATOMS = 12;
|
|
@@ -178904,8 +179563,8 @@ async function createChangeImpactArtifact(ctx, params) {
|
|
|
178904
179563
|
headSha: params.headSha,
|
|
178905
179564
|
lookupError: lookup2.error
|
|
178906
179565
|
});
|
|
178907
|
-
const path4 =
|
|
178908
|
-
|
|
179566
|
+
const path4 = join8(ctx.tmpdir, `pr-${params.pullNumber}-${params.headSha.slice(0, 7)}-impact.md`);
|
|
179567
|
+
writeFileSync7(path4, artifact.content);
|
|
178909
179568
|
return {
|
|
178910
179569
|
path: path4,
|
|
178911
179570
|
candidateCount: candidates.selected.length,
|
|
@@ -179157,8 +179816,8 @@ function readNumber(params) {
|
|
|
179157
179816
|
// utils/gitAuth.ts
|
|
179158
179817
|
import { execSync } from "node:child_process";
|
|
179159
179818
|
import { createHash } from "node:crypto";
|
|
179160
|
-
import { readFileSync as
|
|
179161
|
-
import { join as
|
|
179819
|
+
import { readFileSync as readFileSync3, realpathSync, unlinkSync } from "node:fs";
|
|
179820
|
+
import { join as join9 } from "node:path";
|
|
179162
179821
|
|
|
179163
179822
|
// utils/shell.ts
|
|
179164
179823
|
import { spawnSync as spawnSync5 } from "node:child_process";
|
|
@@ -179219,7 +179878,7 @@ var TRANSIENT_AUTH_PATTERNS = [
|
|
|
179219
179878
|
];
|
|
179220
179879
|
var gitBinary;
|
|
179221
179880
|
function hashFile(path4) {
|
|
179222
|
-
return createHash("sha256").update(
|
|
179881
|
+
return createHash("sha256").update(readFileSync3(path4)).digest("hex");
|
|
179223
179882
|
}
|
|
179224
179883
|
function resolveGit() {
|
|
179225
179884
|
const whichPath = execSync("which git", { encoding: "utf-8" }).trim();
|
|
@@ -179248,7 +179907,7 @@ function resolveHooksDir(cwd, gitPath) {
|
|
|
179248
179907
|
cwd,
|
|
179249
179908
|
log: false
|
|
179250
179909
|
}).trim();
|
|
179251
|
-
const hooksDir =
|
|
179910
|
+
const hooksDir = join9(commonDir, "hooks");
|
|
179252
179911
|
hooksDirCache.set(cwd, hooksDir);
|
|
179253
179912
|
return hooksDir;
|
|
179254
179913
|
}
|
|
@@ -179555,7 +180214,7 @@ function postProcessRangeDiff(raw2, contextLines = 3) {
|
|
|
179555
180214
|
}
|
|
179556
180215
|
|
|
179557
180216
|
// yes/index.ts
|
|
179558
|
-
import { performance as
|
|
180217
|
+
import { performance as performance8 } from "node:perf_hooks";
|
|
179559
180218
|
import { setTimeout as sleep2 } from "node:timers/promises";
|
|
179560
180219
|
|
|
179561
180220
|
// node_modules/.pnpm/lru-cache@11.5.2/node_modules/lru-cache/dist/esm/node/index.min.js
|
|
@@ -180270,14 +180929,14 @@ function _op(fn2, options) {
|
|
|
180270
180929
|
`failed to create cache key${namePrefix ? ` for ${options.name}` : ""}: ${errorMessage}`
|
|
180271
180930
|
);
|
|
180272
180931
|
}
|
|
180273
|
-
const startTime =
|
|
180932
|
+
const startTime = performance8.now();
|
|
180274
180933
|
const timestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
180275
180934
|
log2.debug(`${namePrefix}[${timestamp}] cache lookup started`, { key });
|
|
180276
180935
|
if (shouldCache && lruCache) {
|
|
180277
180936
|
const cached4 = lruCache.get(key);
|
|
180278
180937
|
if (cached4 !== void 0) {
|
|
180279
180938
|
if (keyMap?.get(key) === void 0) keyMap?.set(key, input);
|
|
180280
|
-
const runtime =
|
|
180939
|
+
const runtime = performance8.now() - startTime;
|
|
180281
180940
|
log2.debug(`${namePrefix}[${timestamp}] cache hit (from lru)`, {
|
|
180282
180941
|
key,
|
|
180283
180942
|
runtime: `${Math.round(runtime)}ms`
|
|
@@ -180296,12 +180955,12 @@ function _op(fn2, options) {
|
|
|
180296
180955
|
defaultCacheMiss(key);
|
|
180297
180956
|
const retries = options.retries ?? [];
|
|
180298
180957
|
let lastError;
|
|
180299
|
-
const execStartTime =
|
|
180958
|
+
const execStartTime = performance8.now();
|
|
180300
180959
|
const promise2 = (async () => {
|
|
180301
180960
|
for (let attempt = 0; attempt <= retries.length; attempt++) {
|
|
180302
180961
|
try {
|
|
180303
180962
|
const result = acceptsCtx ? await fn2(input, ctx) : await fn2(input);
|
|
180304
|
-
const execRuntime2 =
|
|
180963
|
+
const execRuntime2 = performance8.now() - execStartTime;
|
|
180305
180964
|
const skip = options.skipCache ? options.skipCache(result) : false;
|
|
180306
180965
|
if (shouldCache && lruCache && result !== null && result !== void 0 && !skip) {
|
|
180307
180966
|
lruCache.set(key, result);
|
|
@@ -180354,7 +181013,7 @@ function _op(fn2, options) {
|
|
|
180354
181013
|
}
|
|
180355
181014
|
}
|
|
180356
181015
|
}
|
|
180357
|
-
const execRuntime =
|
|
181016
|
+
const execRuntime = performance8.now() - execStartTime;
|
|
180358
181017
|
log2.debug(`${namePrefix}[${timestamp}] function failed after all retries`, {
|
|
180359
181018
|
key,
|
|
180360
181019
|
runtime: `${Math.round(execRuntime)}ms`,
|
|
@@ -180419,13 +181078,13 @@ function _op(fn2, options) {
|
|
|
180419
181078
|
|
|
180420
181079
|
// mcp/git.ts
|
|
180421
181080
|
import { randomUUID as randomUUID3 } from "node:crypto";
|
|
180422
|
-
import { writeFileSync as
|
|
180423
|
-
import { join as
|
|
181081
|
+
import { writeFileSync as writeFileSync8 } from "node:fs";
|
|
181082
|
+
import { join as join12 } from "node:path";
|
|
180424
181083
|
|
|
180425
181084
|
// utils/apiCommit.ts
|
|
180426
181085
|
import { execFileSync as execFileSync5 } from "node:child_process";
|
|
180427
181086
|
import { lstat, readlink } from "node:fs/promises";
|
|
180428
|
-
import { join as
|
|
181087
|
+
import { join as join10 } from "node:path";
|
|
180429
181088
|
var GITHUB_API = "https://api.github.com";
|
|
180430
181089
|
var MAX_BLOB_BYTES = 30 * 1024 * 1024;
|
|
180431
181090
|
var BLOB_UPLOAD_CONCURRENCY = 8;
|
|
@@ -180490,7 +181149,7 @@ async function assertApiCommittable(files) {
|
|
|
180490
181149
|
const root = getRepoRoot();
|
|
180491
181150
|
const attrs = $(
|
|
180492
181151
|
"git",
|
|
180493
|
-
["check-attr", "filter", "-z", "--", ...present.map((p) =>
|
|
181152
|
+
["check-attr", "filter", "-z", "--", ...present.map((p) => join10(root, p))],
|
|
180494
181153
|
{ log: false }
|
|
180495
181154
|
);
|
|
180496
181155
|
const parts = attrs.split("\0");
|
|
@@ -180502,7 +181161,7 @@ async function assertApiCommittable(files) {
|
|
|
180502
181161
|
}
|
|
180503
181162
|
}
|
|
180504
181163
|
for (const path4 of present) {
|
|
180505
|
-
const stat = await lstat(
|
|
181164
|
+
const stat = await lstat(join10(root, path4));
|
|
180506
181165
|
if (stat.isDirectory()) {
|
|
180507
181166
|
throw new Error(
|
|
180508
181167
|
`'${path4}' is a directory (nested repository or submodule?) \u2014 signed commits only support files and symlinks.`
|
|
@@ -180511,7 +181170,7 @@ async function assertApiCommittable(files) {
|
|
|
180511
181170
|
}
|
|
180512
181171
|
}
|
|
180513
181172
|
async function createBlobEntry(params) {
|
|
180514
|
-
const absPath =
|
|
181173
|
+
const absPath = join10(params.repoRoot, params.path);
|
|
180515
181174
|
const stat = await lstat(absPath);
|
|
180516
181175
|
if (stat.size > MAX_BLOB_BYTES) {
|
|
180517
181176
|
throw new Error(
|
|
@@ -180688,10 +181347,10 @@ this usually means your local branch has commits that were never pushed \u2014 s
|
|
|
180688
181347
|
}
|
|
180689
181348
|
|
|
180690
181349
|
// utils/github.ts
|
|
180691
|
-
var
|
|
181350
|
+
var core4 = __toESM(require_core(), 1);
|
|
180692
181351
|
import { createSign } from "node:crypto";
|
|
180693
181352
|
import { rename, writeFile } from "node:fs/promises";
|
|
180694
|
-
import { dirname as dirname3, join as
|
|
181353
|
+
import { dirname as dirname3, join as join11 } from "node:path";
|
|
180695
181354
|
|
|
180696
181355
|
// node_modules/.pnpm/@octokit+plugin-throttling@11.0.5_@octokit+core@7.0.7/node_modules/@octokit/plugin-throttling/dist-bundle/index.js
|
|
180697
181356
|
var import_light = __toESM(require_light(), 1);
|
|
@@ -184889,7 +185548,7 @@ async function fetchIdTokenFromStash(creds) {
|
|
|
184889
185548
|
throw new Error("ID token response has no value field");
|
|
184890
185549
|
}
|
|
184891
185550
|
if (isGitHubActions) {
|
|
184892
|
-
|
|
185551
|
+
core4.setSecret(body.value);
|
|
184893
185552
|
}
|
|
184894
185553
|
return body.value;
|
|
184895
185554
|
}
|
|
@@ -184902,7 +185561,7 @@ function translateIdTokenError(error52) {
|
|
|
184902
185561
|
var mintIdToken = op(
|
|
184903
185562
|
async () => {
|
|
184904
185563
|
try {
|
|
184905
|
-
return await
|
|
185564
|
+
return await core4.getIDToken(OIDC_AUDIENCE);
|
|
184906
185565
|
} catch (error52) {
|
|
184907
185566
|
throw translateIdTokenError(error52);
|
|
184908
185567
|
}
|
|
@@ -185111,7 +185770,7 @@ function getGitHubUsageSummary() {
|
|
|
185111
185770
|
}
|
|
185112
185771
|
async function writeGitHubUsageSummaryToFile(path4) {
|
|
185113
185772
|
const summary2 = getGitHubUsageSummary();
|
|
185114
|
-
const tmpPath =
|
|
185773
|
+
const tmpPath = join11(dirname3(path4), `.usage-summary-${process.pid}.tmp`);
|
|
185115
185774
|
await writeFile(tmpPath, JSON.stringify(summary2));
|
|
185116
185775
|
await rename(tmpPath, path4);
|
|
185117
185776
|
}
|
|
@@ -186263,8 +186922,8 @@ function countAhead(head, base, cwd) {
|
|
|
186263
186922
|
function spillGitOutput(params) {
|
|
186264
186923
|
const tempDir = process.env.PULLFROG_TEMP_DIR;
|
|
186265
186924
|
if (!tempDir) throw new Error("PULLFROG_TEMP_DIR not set");
|
|
186266
|
-
const outputPath =
|
|
186267
|
-
|
|
186925
|
+
const outputPath = join12(tempDir, `git-${params.command}-${randomUUID3().slice(0, 8)}.txt`);
|
|
186926
|
+
writeFileSync8(outputPath, params.output);
|
|
186268
186927
|
const previewByLines = params.output.split("\n").slice(0, OVERFLOW_PREVIEW_LINES).join("\n");
|
|
186269
186928
|
const preview = previewByLines.length <= OVERFLOW_PREVIEW_MAX_CHARS ? previewByLines : `${previewByLines.slice(0, OVERFLOW_PREVIEW_MAX_CHARS)}\u2026`;
|
|
186270
186929
|
log.info(
|
|
@@ -187778,9 +188437,9 @@ async function reportReviewNodeId(ctx, params) {
|
|
|
187778
188437
|
import { execFileSync as execFileSync6, execSync as execSync2 } from "node:child_process";
|
|
187779
188438
|
import { mkdtempSync, readdirSync, realpathSync as realpathSync2, unlinkSync as unlinkSync2 } from "node:fs";
|
|
187780
188439
|
import { tmpdir as tmpdir2 } from "node:os";
|
|
187781
|
-
import { join as
|
|
188440
|
+
import { join as join13 } from "node:path";
|
|
187782
188441
|
function createTempDirectory() {
|
|
187783
|
-
const sharedTempDir = mkdtempSync(
|
|
188442
|
+
const sharedTempDir = mkdtempSync(join13(tmpdir2(), "pullfrog-"));
|
|
187784
188443
|
process.env.PULLFROG_TEMP_DIR = sharedTempDir;
|
|
187785
188444
|
log.info(`\xBB created temp dir at ${sharedTempDir}`);
|
|
187786
188445
|
return sharedTempDir;
|
|
@@ -187825,13 +188484,13 @@ function wipeRunnerLeakSurface() {
|
|
|
187825
188484
|
return [];
|
|
187826
188485
|
}
|
|
187827
188486
|
};
|
|
187828
|
-
const fileCommandsDir =
|
|
188487
|
+
const fileCommandsDir = join13(runnerTemp, "_runner_file_commands");
|
|
187829
188488
|
for (const entry of listDir(fileCommandsDir)) {
|
|
187830
|
-
tryUnlink(
|
|
188489
|
+
tryUnlink(join13(fileCommandsDir, entry));
|
|
187831
188490
|
}
|
|
187832
188491
|
for (const entry of listDir(runnerTemp)) {
|
|
187833
188492
|
if (entry.endsWith(".sh") || /^git-credentials-.*\.config$/.test(entry)) {
|
|
187834
|
-
tryUnlink(
|
|
188493
|
+
tryUnlink(join13(runnerTemp, entry));
|
|
187835
188494
|
}
|
|
187836
188495
|
}
|
|
187837
188496
|
if (wiped.length > 0) {
|
|
@@ -188381,11 +189040,11 @@ function CheckoutPrTool(ctx) {
|
|
|
188381
189040
|
headSha: checkoutSha
|
|
188382
189041
|
});
|
|
188383
189042
|
if (incremental.status === "ok") {
|
|
188384
|
-
incrementalDiffPath =
|
|
189043
|
+
incrementalDiffPath = join14(
|
|
188385
189044
|
tempDir,
|
|
188386
189045
|
`pr-${pull_number}-${beforeShort}-${headShort}-incremental.diff`
|
|
188387
189046
|
);
|
|
188388
|
-
|
|
189047
|
+
writeFileSync9(incrementalDiffPath, incremental.diff);
|
|
188389
189048
|
log.info(
|
|
188390
189049
|
`\xBB incremental diff computed (${incremental.diff.length} bytes) \u2192 ${incrementalDiffPath}`
|
|
188391
189050
|
);
|
|
@@ -188398,8 +189057,8 @@ function CheckoutPrTool(ctx) {
|
|
|
188398
189057
|
const diffPreview = formatResult.content.split("\n").slice(0, 100).join("\n");
|
|
188399
189058
|
log.debug(`formatted diff preview (first 100 lines):
|
|
188400
189059
|
${diffPreview}`);
|
|
188401
|
-
const diffPath =
|
|
188402
|
-
|
|
189060
|
+
const diffPath = join14(tempDir, `pr-${pull_number}-${headShort}.diff`);
|
|
189061
|
+
writeFileSync9(diffPath, formatResult.content);
|
|
188403
189062
|
log.debug(`wrote diff to ${diffPath} (${formatResult.content.length} bytes)`);
|
|
188404
189063
|
primary.diffCoverage = createDiffCoverageState({
|
|
188405
189064
|
diffPath,
|
|
@@ -188556,8 +189215,8 @@ ${dirty}`
|
|
|
188556
189215
|
}
|
|
188557
189216
|
|
|
188558
189217
|
// mcp/checkSuite.ts
|
|
188559
|
-
import { mkdirSync as
|
|
188560
|
-
import { join as
|
|
189218
|
+
import { mkdirSync as mkdirSync8, writeFileSync as writeFileSync10 } from "node:fs";
|
|
189219
|
+
import { join as join15 } from "node:path";
|
|
188561
189220
|
var GetCheckSuiteLogs = type({
|
|
188562
189221
|
check_suite_id: type.number.describe("the id from check_suite.id")
|
|
188563
189222
|
});
|
|
@@ -188653,8 +189312,8 @@ function GetCheckSuiteLogsTool(ctx) {
|
|
|
188653
189312
|
if (!tempDir) {
|
|
188654
189313
|
throw new Error("PULLFROG_TEMP_DIR not set");
|
|
188655
189314
|
}
|
|
188656
|
-
const logsDir =
|
|
188657
|
-
|
|
189315
|
+
const logsDir = join15(tempDir, "ci-logs");
|
|
189316
|
+
mkdirSync8(logsDir, { recursive: true });
|
|
188658
189317
|
const jobResults = [];
|
|
188659
189318
|
for (const run of failedRuns) {
|
|
188660
189319
|
const jobs = await ctx.octokit.paginate(ctx.octokit.rest.actions.listJobsForWorkflowRun, {
|
|
@@ -188680,8 +189339,8 @@ function GetCheckSuiteLogsTool(ctx) {
|
|
|
188680
189339
|
);
|
|
188681
189340
|
}
|
|
188682
189341
|
const logsText = await logsResult.text();
|
|
188683
|
-
const logPath =
|
|
188684
|
-
|
|
189342
|
+
const logPath = join15(logsDir, `job-${job.id}.log`);
|
|
189343
|
+
writeFileSync10(logPath, logsText);
|
|
188685
189344
|
const analysis = analyzeLog(logsText, 80);
|
|
188686
189345
|
const failedSteps = job.steps?.filter((s) => s.conclusion === "failure").map((s) => `Step ${s.number}: ${s.name}`) ?? [];
|
|
188687
189346
|
jobResults.push({
|
|
@@ -188729,8 +189388,8 @@ function GetCheckSuiteLogsTool(ctx) {
|
|
|
188729
189388
|
}
|
|
188730
189389
|
|
|
188731
189390
|
// mcp/commitInfo.ts
|
|
188732
|
-
import { writeFileSync as
|
|
188733
|
-
import { join as
|
|
189391
|
+
import { writeFileSync as writeFileSync11 } from "node:fs";
|
|
189392
|
+
import { join as join16 } from "node:path";
|
|
188734
189393
|
var CommitInfo = type({
|
|
188735
189394
|
sha: type.string.describe("the commit SHA (full or abbreviated) to fetch")
|
|
188736
189395
|
});
|
|
@@ -188754,8 +189413,8 @@ function CommitInfoTool(ctx) {
|
|
|
188754
189413
|
"PULLFROG_TEMP_DIR not set - get_commit_info must run in pullfrog action context"
|
|
188755
189414
|
);
|
|
188756
189415
|
}
|
|
188757
|
-
const diffFile =
|
|
188758
|
-
|
|
189416
|
+
const diffFile = join16(tempDir, `commit-${sha.slice(0, 7)}.diff`);
|
|
189417
|
+
writeFileSync11(diffFile, formatResult.content);
|
|
188759
189418
|
log.debug(`wrote commit diff to ${diffFile} (${formatResult.content.length} bytes)`);
|
|
188760
189419
|
return {
|
|
188761
189420
|
sha: data.sha,
|
|
@@ -188888,17 +189547,17 @@ function ReopenCurrentTool(ctx) {
|
|
|
188888
189547
|
}
|
|
188889
189548
|
|
|
188890
189549
|
// prep/index.ts
|
|
188891
|
-
import { performance as
|
|
189550
|
+
import { performance as performance9 } from "node:perf_hooks";
|
|
188892
189551
|
|
|
188893
189552
|
// prep/installNodeDependencies.ts
|
|
188894
|
-
import { existsSync as
|
|
188895
|
-
import { join as
|
|
189553
|
+
import { existsSync as existsSync7 } from "node:fs";
|
|
189554
|
+
import { join as join18 } from "node:path";
|
|
188896
189555
|
|
|
188897
189556
|
// utils/packageManager.ts
|
|
188898
189557
|
var import_semver2 = __toESM(require_semver2(), 1);
|
|
188899
|
-
import { existsSync as
|
|
189558
|
+
import { existsSync as existsSync6 } from "node:fs";
|
|
188900
189559
|
import { mkdir, readFile as readFile2 } from "node:fs/promises";
|
|
188901
|
-
import { delimiter, join as
|
|
189560
|
+
import { delimiter, join as join17 } from "node:path";
|
|
188902
189561
|
var SUPPORTED_NAMES = ["npm", "pnpm", "yarn", "bun"];
|
|
188903
189562
|
var COREPACK_MANAGED = ["pnpm", "yarn"];
|
|
188904
189563
|
function isSupported(name) {
|
|
@@ -188936,8 +189595,8 @@ function parseDevEnginesField(field) {
|
|
|
188936
189595
|
};
|
|
188937
189596
|
}
|
|
188938
189597
|
async function resolvePackageManagerSpec(cwd) {
|
|
188939
|
-
const pkgPath =
|
|
188940
|
-
if (!
|
|
189598
|
+
const pkgPath = join17(cwd, "package.json");
|
|
189599
|
+
if (!existsSync6(pkgPath)) return null;
|
|
188941
189600
|
let pkg;
|
|
188942
189601
|
try {
|
|
188943
189602
|
pkg = JSON.parse(await readFile2(pkgPath, "utf-8"));
|
|
@@ -188995,7 +189654,7 @@ async function currentVersion(name) {
|
|
|
188995
189654
|
return result.stdout.trim();
|
|
188996
189655
|
}
|
|
188997
189656
|
function packageManagerBinDir(tmpdir3) {
|
|
188998
|
-
return
|
|
189657
|
+
return join17(tmpdir3, "pm-bin");
|
|
188999
189658
|
}
|
|
189000
189659
|
async function ensurePackageManager(params) {
|
|
189001
189660
|
const spec = params.spec;
|
|
@@ -189058,7 +189717,7 @@ async function installFallback(name, installSpec) {
|
|
|
189058
189717
|
});
|
|
189059
189718
|
if (result.exitCode !== 0) return result.stderr || `failed to install ${name}`;
|
|
189060
189719
|
if (name === "deno") {
|
|
189061
|
-
const denoPath =
|
|
189720
|
+
const denoPath = join17(process.env.HOME || "", ".deno", "bin");
|
|
189062
189721
|
process.env.PATH = `${denoPath}:${process.env.PATH}`;
|
|
189063
189722
|
}
|
|
189064
189723
|
log.info(`\xBB installed ${name}`);
|
|
@@ -189091,8 +189750,8 @@ async function isCommandAvailable2(command) {
|
|
|
189091
189750
|
var installNodeDependencies = {
|
|
189092
189751
|
name: "installNodeDependencies",
|
|
189093
189752
|
shouldRun: () => {
|
|
189094
|
-
const packageJsonPath =
|
|
189095
|
-
return
|
|
189753
|
+
const packageJsonPath = join18(process.cwd(), "package.json");
|
|
189754
|
+
return existsSync7(packageJsonPath);
|
|
189096
189755
|
},
|
|
189097
189756
|
run: async (options) => {
|
|
189098
189757
|
const declared = await resolvePackageManagerSpec(process.cwd());
|
|
@@ -189186,14 +189845,14 @@ ${errorMessage}`]
|
|
|
189186
189845
|
};
|
|
189187
189846
|
|
|
189188
189847
|
// prep/installPythonDependencies.ts
|
|
189189
|
-
import { existsSync as
|
|
189190
|
-
import { join as
|
|
189848
|
+
import { existsSync as existsSync8, readFileSync as readFileSync4 } from "node:fs";
|
|
189849
|
+
import { join as join19 } from "node:path";
|
|
189191
189850
|
function declaresBuildSystem(path4) {
|
|
189192
|
-
return /^\s*\[\s*build-system\s*\]/m.test(
|
|
189851
|
+
return /^\s*\[\s*build-system\s*\]/m.test(readFileSync4(path4, "utf8"));
|
|
189193
189852
|
}
|
|
189194
189853
|
function configApplies(config3, cwd) {
|
|
189195
|
-
const path4 =
|
|
189196
|
-
return
|
|
189854
|
+
const path4 = join19(cwd, config3.file);
|
|
189855
|
+
return existsSync8(path4) && (config3.applies?.(path4) ?? true);
|
|
189197
189856
|
}
|
|
189198
189857
|
var PYTHON_CONFIGS = [
|
|
189199
189858
|
{
|
|
@@ -189347,7 +190006,7 @@ var installPythonDependencies = {
|
|
|
189347
190006
|
var prepSteps = [installNodeDependencies, installPythonDependencies];
|
|
189348
190007
|
async function runPrepPhase(options) {
|
|
189349
190008
|
log.debug("\xBB starting prep phase...");
|
|
189350
|
-
const startTime =
|
|
190009
|
+
const startTime = performance9.now();
|
|
189351
190010
|
const results = [];
|
|
189352
190011
|
const preDirty = await dirtyTrackedPaths();
|
|
189353
190012
|
try {
|
|
@@ -189369,7 +190028,7 @@ async function runPrepPhase(options) {
|
|
|
189369
190028
|
} finally {
|
|
189370
190029
|
await restoreDirtiedSince({ before: preDirty, actor: "prep" });
|
|
189371
190030
|
}
|
|
189372
|
-
const totalDurationMs =
|
|
190031
|
+
const totalDurationMs = performance9.now() - startTime;
|
|
189373
190032
|
log.debug(`\xBB prep phase completed (${Math.round(totalDurationMs)}ms)`);
|
|
189374
190033
|
return results;
|
|
189375
190034
|
}
|
|
@@ -189514,15 +190173,15 @@ function AwaitDependencyInstallationTool(ctx) {
|
|
|
189514
190173
|
}
|
|
189515
190174
|
|
|
189516
190175
|
// mcp/gh.ts
|
|
189517
|
-
import { mkdirSync as
|
|
189518
|
-
import { join as
|
|
190176
|
+
import { mkdirSync as mkdirSync9 } from "node:fs";
|
|
190177
|
+
import { join as join21 } from "node:path";
|
|
189519
190178
|
|
|
189520
190179
|
// mcp/shell.ts
|
|
189521
190180
|
import { spawn as spawn3, spawnSync as spawnSync6 } from "node:child_process";
|
|
189522
190181
|
import { randomUUID as randomUUID4 } from "node:crypto";
|
|
189523
|
-
import { closeSync, openSync, writeFileSync as
|
|
190182
|
+
import { closeSync, openSync, writeFileSync as writeFileSync12 } from "node:fs";
|
|
189524
190183
|
import { userInfo as userInfo2 } from "node:os";
|
|
189525
|
-
import { join as
|
|
190184
|
+
import { join as join20 } from "node:path";
|
|
189526
190185
|
import { setTimeout as sleep3 } from "node:timers/promises";
|
|
189527
190186
|
var ShellParams = type({
|
|
189528
190187
|
command: "string",
|
|
@@ -189785,8 +190444,8 @@ ${spawnError}` : spawnError;
|
|
|
189785
190444
|
}
|
|
189786
190445
|
function capOutput(output) {
|
|
189787
190446
|
if (output.length <= MAX_OUTPUT_CHARS) return output;
|
|
189788
|
-
const fullPath =
|
|
189789
|
-
|
|
190447
|
+
const fullPath = join20(getTempDir(), `shell-${randomUUID4().slice(0, 8)}.log`);
|
|
190448
|
+
writeFileSync12(fullPath, output);
|
|
189790
190449
|
const elided = output.length - MAX_OUTPUT_CHARS;
|
|
189791
190450
|
return `... [${elided} chars truncated; full output saved to ${fullPath}] ...
|
|
189792
190451
|
${output.slice(-MAX_OUTPUT_CHARS)}`;
|
|
@@ -189840,8 +190499,8 @@ Do NOT use this tool for git commands \u2014 use the dedicated git tools instead
|
|
|
189840
190499
|
if (params.background) {
|
|
189841
190500
|
const tempDir = getTempDir();
|
|
189842
190501
|
const handle = `bg-${randomUUID4().slice(0, 8)}`;
|
|
189843
|
-
const outputPath =
|
|
189844
|
-
const pidPath =
|
|
190502
|
+
const outputPath = join20(tempDir, `${handle}.log`);
|
|
190503
|
+
const pidPath = join20(tempDir, `${handle}.pid`);
|
|
189845
190504
|
const logFd = openSync(outputPath, "a");
|
|
189846
190505
|
let proc;
|
|
189847
190506
|
try {
|
|
@@ -189858,7 +190517,7 @@ Do NOT use this tool for git commands \u2014 use the dedicated git tools instead
|
|
|
189858
190517
|
throw new Error("failed to start background process");
|
|
189859
190518
|
}
|
|
189860
190519
|
proc.unref();
|
|
189861
|
-
|
|
190520
|
+
writeFileSync12(pidPath, `${proc.pid}
|
|
189862
190521
|
`);
|
|
189863
190522
|
ctx.toolState.backgroundProcesses.set(handle, { pid: proc.pid, outputPath, pidPath });
|
|
189864
190523
|
return {
|
|
@@ -189938,8 +190597,8 @@ function GhTool(ctx) {
|
|
|
189938
190597
|
description: `Run the GitHub CLI against ${ctx.repo.owner}/${ctx.repo.name}. Pass argv as an array \u2014 there is no shell, so pipes, redirects and \`&&\` do not work; use gh's own \`--json\`/\`--jq\` flags instead. \`gh api graphql\` is available. Authenticated with ${permissions ? formatPermissions(permissions) : "no permissions"} on this repo only; anything else 403s. Pushing, merging and ref writes are not available \u2014 use the git tools.`,
|
|
189939
190598
|
parameters: GhParams,
|
|
189940
190599
|
execute: execute(async (params) => {
|
|
189941
|
-
const configDir =
|
|
189942
|
-
|
|
190600
|
+
const configDir = join21(ctx.tmpdir, "gh-config");
|
|
190601
|
+
mkdirSync9(configDir, { recursive: true });
|
|
189943
190602
|
const command = ["gh", ...params.args.map(shellQuote)].join(" ");
|
|
189944
190603
|
const result = await runSandboxed({
|
|
189945
190604
|
command,
|
|
@@ -190662,8 +191321,8 @@ function PullRequestInfoTool(ctx) {
|
|
|
190662
191321
|
}
|
|
190663
191322
|
|
|
190664
191323
|
// mcp/reviewComments.ts
|
|
190665
|
-
import { writeFileSync as
|
|
190666
|
-
import { join as
|
|
191324
|
+
import { writeFileSync as writeFileSync13 } from "node:fs";
|
|
191325
|
+
import { join as join22 } from "node:path";
|
|
190667
191326
|
var COMMENTS_PER_THREAD = 50;
|
|
190668
191327
|
var REVIEW_THREADS_QUERY = `
|
|
190669
191328
|
query ($owner: String!, $name: String!, $prNumber: Int!, $cursor: String) {
|
|
@@ -191136,8 +191795,8 @@ function GetReviewCommentsTool(ctx) {
|
|
|
191136
191795
|
throw new Error("PULLFROG_TEMP_DIR not set");
|
|
191137
191796
|
}
|
|
191138
191797
|
const filename = `review-${params.review_id}-threads.md`;
|
|
191139
|
-
const commentsPath =
|
|
191140
|
-
|
|
191798
|
+
const commentsPath = join22(tempDir, filename);
|
|
191799
|
+
writeFileSync13(commentsPath, formatted.content);
|
|
191141
191800
|
log.debug(`wrote ${threadBlocks.length} threads to ${commentsPath}`);
|
|
191142
191801
|
return {
|
|
191143
191802
|
review_id: params.review_id,
|
|
@@ -191470,8 +192129,8 @@ function UploadFileTool(ctx) {
|
|
|
191470
192129
|
}
|
|
191471
192130
|
|
|
191472
192131
|
// mcp/xrepo.ts
|
|
191473
|
-
import { mkdirSync as
|
|
191474
|
-
import { join as
|
|
192132
|
+
import { mkdirSync as mkdirSync10, rmSync as rmSync2 } from "node:fs";
|
|
192133
|
+
import { join as join23 } from "node:path";
|
|
191475
192134
|
function assertValidRepoName(name) {
|
|
191476
192135
|
if (!/^[A-Za-z0-9._-]+$/.test(name) || name.startsWith("-") || name.includes("..")) {
|
|
191477
192136
|
throw new Error(
|
|
@@ -191551,8 +192210,8 @@ function CheckoutRepoTool(ctx) {
|
|
|
191551
192210
|
return { path: existing.dir, access: existing.access, note: "already checked out." };
|
|
191552
192211
|
}
|
|
191553
192212
|
const access = accessFor(ctx, repo);
|
|
191554
|
-
const dir =
|
|
191555
|
-
|
|
192213
|
+
const dir = join23(ctx.tmpdir, "xrepo", repo);
|
|
192214
|
+
mkdirSync10(dir, { recursive: true });
|
|
191556
192215
|
const state = ensureRepoState(ctx.toolState, { owner, name: repo, dir, access });
|
|
191557
192216
|
const rc = resolveRepoCtx(ctx, repo);
|
|
191558
192217
|
try {
|
|
@@ -191798,7 +192457,10 @@ function hasEnvVar2(name) {
|
|
|
191798
192457
|
return typeof val === "string" && val.length > 0;
|
|
191799
192458
|
}
|
|
191800
192459
|
function hasClaudeCodeAuth() {
|
|
191801
|
-
return hasEnvVar2("CLAUDE_CODE_OAUTH_TOKEN") || hasEnvVar2("ANTHROPIC_API_KEY");
|
|
192460
|
+
return hasEnvVar2("CLAUDE_CODE_OAUTH_TOKEN") || hasEnvVar2("ANTHROPIC_API_KEY") || hasEnvVar2("ANTHROPIC_AUTH_TOKEN");
|
|
192461
|
+
}
|
|
192462
|
+
function hasCodexAuth() {
|
|
192463
|
+
return hasEnvVar2("CODEX_AUTH_JSON") || hasEnvVar2("OPENAI_API_KEY");
|
|
191802
192464
|
}
|
|
191803
192465
|
function hasBedrockAuth() {
|
|
191804
192466
|
return hasEnvVar2("AWS_BEARER_TOKEN_BEDROCK") || hasEnvVar2("AWS_ACCESS_KEY_ID") && hasEnvVar2("AWS_SECRET_ACCESS_KEY");
|
|
@@ -191826,6 +192488,15 @@ function resolveSlug(slug2) {
|
|
|
191826
192488
|
}
|
|
191827
192489
|
return vertexId;
|
|
191828
192490
|
}
|
|
192491
|
+
if (alias?.routing === "azure") {
|
|
192492
|
+
const deployment = process.env[AZURE_DEPLOYMENT_ENV]?.trim();
|
|
192493
|
+
if (!deployment) {
|
|
192494
|
+
throw new Error(
|
|
192495
|
+
`${AZURE_DEPLOYMENT_ENV} env var is required when the model is set to "${slug2}". set it to the name of your Azure OpenAI deployment (not the model it serves \u2014 Azure routes on the deployment name). see https://docs.pullfrog.com/azure for setup.`
|
|
192496
|
+
);
|
|
192497
|
+
}
|
|
192498
|
+
return `${AZURE_PROVIDER}/${deployment}`;
|
|
192499
|
+
}
|
|
191829
192500
|
if (alias?.routing === "openai-compatible") {
|
|
191830
192501
|
const modelId = process.env[OPENAI_COMPATIBLE_MODEL_ENV]?.trim();
|
|
191831
192502
|
if (!modelId) {
|
|
@@ -191864,6 +192535,7 @@ function resolveAgent(ctx) {
|
|
|
191864
192535
|
}
|
|
191865
192536
|
log.warning(`\xBB unknown PULLFROG_AGENT="${envAgent}" \u2014 falling through to auto-select`);
|
|
191866
192537
|
}
|
|
192538
|
+
if (ctx.proxyModel) return agents.opencode;
|
|
191867
192539
|
if (ctx.model && hasBedrockAuth() && process.env[BEDROCK_MODEL_ID_ENV]?.trim() === ctx.model) {
|
|
191868
192540
|
return isBedrockAnthropicId(ctx.model) ? agents.claude : agents.opencode;
|
|
191869
192541
|
}
|
|
@@ -191873,12 +192545,17 @@ function resolveAgent(ctx) {
|
|
|
191873
192545
|
if (ctx.model) {
|
|
191874
192546
|
try {
|
|
191875
192547
|
const provider2 = getModelProvider(ctx.model);
|
|
191876
|
-
if (provider2 === "anthropic" && hasClaudeCodeAuth())
|
|
191877
|
-
|
|
191878
|
-
}
|
|
192548
|
+
if (provider2 === "anthropic" && hasClaudeCodeAuth()) return agents.claude;
|
|
192549
|
+
if (provider2 === "openai" && ctx.codexAgent && hasCodexAuth()) return agents.codex;
|
|
191879
192550
|
} catch {
|
|
191880
192551
|
}
|
|
191881
192552
|
}
|
|
192553
|
+
if (!ctx.model) {
|
|
192554
|
+
if (hasEnvVar2("ANTHROPIC_AUTH_TOKEN") && !hasEnvVar2("ANTHROPIC_API_KEY") && !hasEnvVar2("CLAUDE_CODE_OAUTH_TOKEN")) {
|
|
192555
|
+
return agents.claude;
|
|
192556
|
+
}
|
|
192557
|
+
if (ctx.codexAgent && hasCodexAuth() && !hasClaudeCodeAuth()) return agents.codex;
|
|
192558
|
+
}
|
|
191882
192559
|
return agents.opencode;
|
|
191883
192560
|
}
|
|
191884
192561
|
|
|
@@ -191976,10 +192653,37 @@ auto-compaction is disabled, so long runs grow until your endpoint refuses them.
|
|
|
191976
192653
|
|
|
191977
192654
|
for full setup instructions, see https://docs.pullfrog.com/openai-compatible`;
|
|
191978
192655
|
}
|
|
192656
|
+
function buildAzureSetupError(params) {
|
|
192657
|
+
const githubSecretsUrl = `https://github.com/${params.owner}/${params.name}/settings/secrets/actions`;
|
|
192658
|
+
return `Azure OpenAI selected but required configuration is missing: ${params.missing.join(", ")}.
|
|
192659
|
+
|
|
192660
|
+
only the API key is sensitive \u2014 add it as a secret at ${githubSecretsUrl}. the rest is plain workflow \`env:\`:
|
|
192661
|
+
|
|
192662
|
+
${AZURE_RESOURCE_NAME_ENV}: <name>
|
|
192663
|
+
${AZURE_API_KEY_ENV}: \${{ secrets.${AZURE_API_KEY_ENV} }}
|
|
192664
|
+
${AZURE_DEPLOYMENT_ENV}: <deployment-name>
|
|
192665
|
+
${AZURE_CONTEXT_ENV}: "400000"
|
|
192666
|
+
${AZURE_MAX_OUTPUT_ENV}: "128000"
|
|
192667
|
+
|
|
192668
|
+
${AZURE_RESOURCE_NAME_ENV} is the \`<name>\` in your endpoint https://<name>.openai.azure.com.
|
|
192669
|
+
${AZURE_DEPLOYMENT_ENV} is the name of the deployment, which is not necessarily the name of
|
|
192670
|
+
the model it serves \u2014 Azure routes on the deployment name.
|
|
192671
|
+
|
|
192672
|
+
set the last two to the real limits of whichever model your deployment serves. Pullfrog can't
|
|
192673
|
+
discover them \u2014 a deployment name carries no catalog metadata \u2014 and without them completions
|
|
192674
|
+
are capped at 32000 tokens (rejected outright by models with a smaller cap) and auto-compaction
|
|
192675
|
+
is disabled, so long runs grow until Azure refuses them.
|
|
192676
|
+
|
|
192677
|
+
for full setup instructions, see https://docs.pullfrog.com/azure`;
|
|
192678
|
+
}
|
|
191979
192679
|
function hasEnvVar3(name) {
|
|
191980
192680
|
const value2 = process.env[name];
|
|
191981
192681
|
return typeof value2 === "string" && value2.length > 0;
|
|
191982
192682
|
}
|
|
192683
|
+
function modelHasRuntimeAuth(model) {
|
|
192684
|
+
const authVars = [...getModelEnvVars(model), ...getModelManagedCredentials(model)];
|
|
192685
|
+
return authVars.length === 0 || authVars.some(hasEnvVar3);
|
|
192686
|
+
}
|
|
191983
192687
|
function hasPositiveNumberEnvVar(name) {
|
|
191984
192688
|
return Number(process.env[name]) > 0;
|
|
191985
192689
|
}
|
|
@@ -191998,6 +192702,17 @@ function validateOpenAICompatibleSetup(params) {
|
|
|
191998
192702
|
);
|
|
191999
192703
|
}
|
|
192000
192704
|
}
|
|
192705
|
+
function validateAzureSetup(params) {
|
|
192706
|
+
const missing = [];
|
|
192707
|
+
if (!hasEnvVar3(AZURE_API_KEY_ENV)) missing.push(AZURE_API_KEY_ENV);
|
|
192708
|
+
if (!hasEnvVar3(AZURE_RESOURCE_NAME_ENV)) missing.push(AZURE_RESOURCE_NAME_ENV);
|
|
192709
|
+
if (!hasEnvVar3(AZURE_DEPLOYMENT_ENV)) missing.push(AZURE_DEPLOYMENT_ENV);
|
|
192710
|
+
if (!hasPositiveNumberEnvVar(AZURE_CONTEXT_ENV)) missing.push(AZURE_CONTEXT_ENV);
|
|
192711
|
+
if (!hasPositiveNumberEnvVar(AZURE_MAX_OUTPUT_ENV)) missing.push(AZURE_MAX_OUTPUT_ENV);
|
|
192712
|
+
if (missing.length > 0) {
|
|
192713
|
+
throw new Error(buildAzureSetupError({ owner: params.owner, name: params.name, missing }));
|
|
192714
|
+
}
|
|
192715
|
+
}
|
|
192001
192716
|
function validateBedrockSetup(params) {
|
|
192002
192717
|
const hasAuth = hasEnvVar3("AWS_BEARER_TOKEN_BEDROCK") || hasEnvVar3("AWS_ACCESS_KEY_ID") && hasEnvVar3("AWS_SECRET_ACCESS_KEY");
|
|
192003
192718
|
const missing = [];
|
|
@@ -192021,6 +192736,12 @@ function validateVertexSetup(params) {
|
|
|
192021
192736
|
throw new Error(buildVertexSetupError({ owner: params.owner, name: params.name, missing }));
|
|
192022
192737
|
}
|
|
192023
192738
|
}
|
|
192739
|
+
function hasSingleProviderAuth(agentName) {
|
|
192740
|
+
if (agentName === "codex") {
|
|
192741
|
+
return hasEnvVar3("OPENAI_API_KEY") || hasEnvVar3("CODEX_AUTH_JSON");
|
|
192742
|
+
}
|
|
192743
|
+
return hasEnvVar3("ANTHROPIC_API_KEY") || hasEnvVar3("ANTHROPIC_AUTH_TOKEN") || hasEnvVar3("CLAUDE_CODE_OAUTH_TOKEN");
|
|
192744
|
+
}
|
|
192024
192745
|
function validateAgentApiKey(params) {
|
|
192025
192746
|
if (params.model) {
|
|
192026
192747
|
const alias = resolveDisplayAlias(params.model);
|
|
@@ -192036,6 +192757,10 @@ function validateAgentApiKey(params) {
|
|
|
192036
192757
|
validateOpenAICompatibleSetup({ owner: params.owner, name: params.name });
|
|
192037
192758
|
return;
|
|
192038
192759
|
}
|
|
192760
|
+
if (params.model.startsWith(`${AZURE_PROVIDER}/`)) {
|
|
192761
|
+
validateAzureSetup({ owner: params.owner, name: params.name });
|
|
192762
|
+
return;
|
|
192763
|
+
}
|
|
192039
192764
|
if (!params.model.includes("/")) {
|
|
192040
192765
|
if (process.env[VERTEX_MODEL_ID_ENV]?.trim() === params.model) {
|
|
192041
192766
|
validateVertexSetup({ owner: params.owner, name: params.name });
|
|
@@ -192059,7 +192784,7 @@ function validateAgentApiKey(params) {
|
|
|
192059
192784
|
})
|
|
192060
192785
|
);
|
|
192061
192786
|
}
|
|
192062
|
-
if (
|
|
192787
|
+
if (hasSingleProviderAuth(params.agent.name)) return;
|
|
192063
192788
|
throw new Error(
|
|
192064
192789
|
buildKeyError({
|
|
192065
192790
|
owner: params.owner,
|
|
@@ -192071,7 +192796,7 @@ function validateAgentApiKey(params) {
|
|
|
192071
192796
|
);
|
|
192072
192797
|
}
|
|
192073
192798
|
if (params.agent.name === "opencode") {
|
|
192074
|
-
if (params.authorized.
|
|
192799
|
+
if ([...params.authorized].some(modelHasRuntimeAuth)) return;
|
|
192075
192800
|
const reason = getModelsFailure();
|
|
192076
192801
|
if (reason) throw new Error(reason);
|
|
192077
192802
|
throw new Error(
|
|
@@ -192083,7 +192808,7 @@ function validateAgentApiKey(params) {
|
|
|
192083
192808
|
})
|
|
192084
192809
|
);
|
|
192085
192810
|
}
|
|
192086
|
-
if (
|
|
192811
|
+
if (hasSingleProviderAuth(params.agent.name)) return;
|
|
192087
192812
|
throw new Error(
|
|
192088
192813
|
buildKeyError({
|
|
192089
192814
|
owner: params.owner,
|
|
@@ -192347,7 +193072,8 @@ var PROBES = {
|
|
|
192347
193072
|
request: (value2) => ({
|
|
192348
193073
|
url: "https://api.anthropic.com/v1/models",
|
|
192349
193074
|
headers: { "x-api-key": value2, "anthropic-version": "2023-06-01" }
|
|
192350
|
-
})
|
|
193075
|
+
}),
|
|
193076
|
+
hostConfigurable: true
|
|
192351
193077
|
},
|
|
192352
193078
|
OPENROUTER_API_KEY: {
|
|
192353
193079
|
request: (value2) => ({
|
|
@@ -192396,12 +193122,18 @@ function hasEnvVar4(name) {
|
|
|
192396
193122
|
const value2 = process.env[name];
|
|
192397
193123
|
return typeof value2 === "string" && value2.length > 0;
|
|
192398
193124
|
}
|
|
193125
|
+
var HOST_OVERRIDES = {
|
|
193126
|
+
ANTHROPIC_API_KEY: "ANTHROPIC_BASE_URL",
|
|
193127
|
+
CLAUDE_CODE_OAUTH_TOKEN: "ANTHROPIC_BASE_URL"
|
|
193128
|
+
};
|
|
192399
193129
|
function envVarsFor(model) {
|
|
192400
193130
|
return model.includes("/") ? getModelEnvVars(model) : [];
|
|
192401
193131
|
}
|
|
192402
193132
|
async function checkOne(params) {
|
|
192403
193133
|
const value2 = process.env[params.envVar];
|
|
192404
193134
|
if (!value2) return null;
|
|
193135
|
+
const hostOverride = HOST_OVERRIDES[params.envVar];
|
|
193136
|
+
if (hostOverride && hasEnvVar4(hostOverride)) return null;
|
|
192405
193137
|
if (params.envVar === "CLAUDE_CODE_OAUTH_TOKEN") {
|
|
192406
193138
|
const preflight = await preflightClaudeSubscription({
|
|
192407
193139
|
token: value2,
|
|
@@ -192456,9 +193188,9 @@ async function checkConfiguredCredentials(params) {
|
|
|
192456
193188
|
|
|
192457
193189
|
// utils/gitAuthServer.ts
|
|
192458
193190
|
import { randomUUID as randomUUID5 } from "node:crypto";
|
|
192459
|
-
import { writeFileSync as
|
|
193191
|
+
import { writeFileSync as writeFileSync14 } from "node:fs";
|
|
192460
193192
|
import { createServer as createServer3 } from "node:http";
|
|
192461
|
-
import { join as
|
|
193193
|
+
import { join as join24 } from "node:path";
|
|
192462
193194
|
var REVOKED_TRAP_MS = 6e4;
|
|
192463
193195
|
function revokeGitHubToken(token) {
|
|
192464
193196
|
fetch("https://api.github.com/installation/token", {
|
|
@@ -192527,7 +193259,7 @@ async function startGitAuthServer(tmpdir3) {
|
|
|
192527
193259
|
function writeAskpassScript(code) {
|
|
192528
193260
|
const scriptId = randomUUID5();
|
|
192529
193261
|
const scriptName = `askpass-${scriptId}.js`;
|
|
192530
|
-
const scriptPath =
|
|
193262
|
+
const scriptPath = join24(tmpdir3, scriptName);
|
|
192531
193263
|
const content = [
|
|
192532
193264
|
`#!/usr/bin/env node`,
|
|
192533
193265
|
`var a=process.argv[2]||"";`,
|
|
@@ -192540,7 +193272,7 @@ async function startGitAuthServer(tmpdir3) {
|
|
|
192540
193272
|
`r.on("end",function(){process.stdout.write(d+"\\n")})`,
|
|
192541
193273
|
`}).on("error",function(){process.exit(1)})}`
|
|
192542
193274
|
].join("\n");
|
|
192543
|
-
|
|
193275
|
+
writeFileSync14(scriptPath, content, { mode: 448 });
|
|
192544
193276
|
return scriptPath;
|
|
192545
193277
|
}
|
|
192546
193278
|
async function close() {
|
|
@@ -192979,7 +193711,7 @@ function resolveInstructions(ctx) {
|
|
|
192979
193711
|
|
|
192980
193712
|
// utils/learnings.ts
|
|
192981
193713
|
import { mkdir as mkdir2, readFile as readFile3, writeFile as writeFile2 } from "node:fs/promises";
|
|
192982
|
-
import { dirname as dirname4, join as
|
|
193714
|
+
import { dirname as dirname4, join as join25 } from "node:path";
|
|
192983
193715
|
|
|
192984
193716
|
// utils/learningsTruncate.ts
|
|
192985
193717
|
var MAX_LEARNINGS_LENGTH = 1e5;
|
|
@@ -192997,10 +193729,10 @@ function truncateAtLineBoundary(body, cap) {
|
|
|
192997
193729
|
var LEARNINGS_FILE_NAME = "pullfrog-learnings.md";
|
|
192998
193730
|
var XREPO_LEARNINGS_FILE_NAME = "pullfrog-xrepo-learnings.md";
|
|
192999
193731
|
function learningsFilePath(tmpdir3) {
|
|
193000
|
-
return
|
|
193732
|
+
return join25(tmpdir3, LEARNINGS_FILE_NAME);
|
|
193001
193733
|
}
|
|
193002
193734
|
function xrepoLearningsFilePath(tmpdir3) {
|
|
193003
|
-
return
|
|
193735
|
+
return join25(tmpdir3, XREPO_LEARNINGS_FILE_NAME);
|
|
193004
193736
|
}
|
|
193005
193737
|
async function seedLearningsFile(params) {
|
|
193006
193738
|
const path4 = learningsFilePath(params.tmpdir);
|
|
@@ -193116,7 +193848,7 @@ async function persistXrepoLearnings(ctx) {
|
|
|
193116
193848
|
// utils/modelAccess.ts
|
|
193117
193849
|
function decideModelAccess(input) {
|
|
193118
193850
|
if (!input.modelExplicit || !input.model) return { kind: "ok" };
|
|
193119
|
-
const byokAuthorized = !!input.resolvedModel && (input.authorized.has(input.resolvedModel) || !input.resolvedModel.includes("/") || input.resolvedModel.startsWith(`${OPENAI_COMPATIBLE_PROVIDER}/`));
|
|
193851
|
+
const byokAuthorized = !!input.resolvedModel && (input.authorized.has(input.resolvedModel) || !input.resolvedModel.includes("/") || input.resolvedModel.startsWith(`${OPENAI_COMPATIBLE_PROVIDER}/`) || input.resolvedModel.startsWith(`${AZURE_PROVIDER}/`));
|
|
193120
193852
|
if (input.proxyActive) {
|
|
193121
193853
|
const target = resolveOpenRouterModel(input.model);
|
|
193122
193854
|
if (input.oss) {
|
|
@@ -193161,7 +193893,7 @@ function buildModelAccessError(input) {
|
|
|
193161
193893
|
}
|
|
193162
193894
|
|
|
193163
193895
|
// utils/normalizeEnv.ts
|
|
193164
|
-
var
|
|
193896
|
+
var core5 = __toESM(require_core(), 1);
|
|
193165
193897
|
function hasControlCharacter(value2) {
|
|
193166
193898
|
return [...value2].some((ch) => {
|
|
193167
193899
|
const code = ch.charCodeAt(0);
|
|
@@ -193187,7 +193919,7 @@ function sanitizeSecret(key, value2) {
|
|
|
193187
193919
|
`\xBB stripped whitespace from ${key} (whitespace in secret values breaks GitHub Actions log masking)`
|
|
193188
193920
|
);
|
|
193189
193921
|
}
|
|
193190
|
-
|
|
193922
|
+
core5.setSecret(trimmed);
|
|
193191
193923
|
return trimmed;
|
|
193192
193924
|
}
|
|
193193
193925
|
function normalizeEnv() {
|
|
@@ -193231,7 +193963,7 @@ function normalizeEnv() {
|
|
|
193231
193963
|
}
|
|
193232
193964
|
|
|
193233
193965
|
// utils/overrides.ts
|
|
193234
|
-
var
|
|
193966
|
+
var core6 = __toESM(require_core(), 1);
|
|
193235
193967
|
var DENIED_OVERRIDE_NAMES = /* @__PURE__ */ new Set([
|
|
193236
193968
|
"GITHUB_TOKEN",
|
|
193237
193969
|
"GH_TOKEN",
|
|
@@ -193277,7 +194009,7 @@ function applyOverrides(params) {
|
|
|
193277
194009
|
denied.push(key);
|
|
193278
194010
|
continue;
|
|
193279
194011
|
}
|
|
193280
|
-
if (value2.length > 0)
|
|
194012
|
+
if (value2.length > 0) core6.setSecret(value2);
|
|
193281
194013
|
params.env[key] = value2;
|
|
193282
194014
|
applied.push(key);
|
|
193283
194015
|
}
|
|
@@ -193286,8 +194018,8 @@ function applyOverrides(params) {
|
|
|
193286
194018
|
}
|
|
193287
194019
|
|
|
193288
194020
|
// utils/payload.ts
|
|
193289
|
-
var
|
|
193290
|
-
import { readFileSync as
|
|
194021
|
+
var core7 = __toESM(require_core(), 1);
|
|
194022
|
+
import { readFileSync as readFileSync6 } from "node:fs";
|
|
193291
194023
|
import { isAbsolute as isAbsolute2, resolve as resolve2 } from "node:path";
|
|
193292
194024
|
|
|
193293
194025
|
// utils/versioning.ts
|
|
@@ -193374,8 +194106,8 @@ function resolveCwd(cwd) {
|
|
|
193374
194106
|
return workspace ? resolve2(workspace, cwd) : cwd;
|
|
193375
194107
|
}
|
|
193376
194108
|
function resolvePromptInput() {
|
|
193377
|
-
const promptInput =
|
|
193378
|
-
const promptFile =
|
|
194109
|
+
const promptInput = core7.getInput("prompt");
|
|
194110
|
+
const promptFile = core7.getInput("prompt_file");
|
|
193379
194111
|
if (promptInput && promptFile) {
|
|
193380
194112
|
throw new Error("set exactly one of 'prompt' or 'prompt_file' inputs, not both.");
|
|
193381
194113
|
}
|
|
@@ -193401,7 +194133,7 @@ function resolvePromptInput() {
|
|
|
193401
194133
|
function resolvePromptFile(input) {
|
|
193402
194134
|
const workspace = process.env.GITHUB_WORKSPACE;
|
|
193403
194135
|
const path4 = isAbsolute2(input) ? input : workspace ? resolve2(workspace, input) : resolve2(input);
|
|
193404
|
-
const content =
|
|
194136
|
+
const content = readFileSync6(path4, "utf-8");
|
|
193405
194137
|
if (!content.trim()) {
|
|
193406
194138
|
throw new Error(`prompt_file ${JSON.stringify(input)} is empty.`);
|
|
193407
194139
|
}
|
|
@@ -193409,7 +194141,7 @@ function resolvePromptFile(input) {
|
|
|
193409
194141
|
}
|
|
193410
194142
|
function warnIfDeprecatedStatusChecks(value2) {
|
|
193411
194143
|
if (value2 !== void 0) {
|
|
193412
|
-
|
|
194144
|
+
core7.warning(
|
|
193413
194145
|
"`status_checks` is deprecated. Both checks are now repository settings \u2014 open the Pullfrog console for this repo (Automations \u2192 Review PRs) and set them there, then remove `status_checks` from your workflow. It keeps working until you do."
|
|
193414
194146
|
);
|
|
193415
194147
|
}
|
|
@@ -193417,15 +194149,15 @@ function warnIfDeprecatedStatusChecks(value2) {
|
|
|
193417
194149
|
}
|
|
193418
194150
|
function resolveNonPromptInputs() {
|
|
193419
194151
|
return Inputs.omit("prompt", "prompt_file").assert({
|
|
193420
|
-
model:
|
|
193421
|
-
effort:
|
|
193422
|
-
debug:
|
|
193423
|
-
timeout:
|
|
193424
|
-
cwd:
|
|
193425
|
-
push:
|
|
193426
|
-
shell:
|
|
193427
|
-
status_checks: warnIfDeprecatedStatusChecks(
|
|
193428
|
-
progress_comments:
|
|
194152
|
+
model: core7.getInput("model") || void 0,
|
|
194153
|
+
effort: core7.getInput("effort") || void 0,
|
|
194154
|
+
debug: core7.getInput("debug") || void 0,
|
|
194155
|
+
timeout: core7.getInput("timeout") || void 0,
|
|
194156
|
+
cwd: core7.getInput("cwd") || void 0,
|
|
194157
|
+
push: core7.getInput("push") || void 0,
|
|
194158
|
+
shell: core7.getInput("shell") || void 0,
|
|
194159
|
+
status_checks: warnIfDeprecatedStatusChecks(core7.getInput("status_checks") || void 0),
|
|
194160
|
+
progress_comments: core7.getInput("progress_comments") || void 0
|
|
193429
194161
|
});
|
|
193430
194162
|
}
|
|
193431
194163
|
function resolvePayload(resolvedPromptInput, repoSettings) {
|
|
@@ -193493,7 +194225,7 @@ function resolvePayload(resolvedPromptInput, repoSettings) {
|
|
|
193493
194225
|
};
|
|
193494
194226
|
}
|
|
193495
194227
|
function resolveOutputSchema() {
|
|
193496
|
-
const raw2 =
|
|
194228
|
+
const raw2 = core7.getInput("output_schema");
|
|
193497
194229
|
if (!raw2) return void 0;
|
|
193498
194230
|
let parsed2;
|
|
193499
194231
|
try {
|
|
@@ -193509,10 +194241,10 @@ function resolveOutputSchema() {
|
|
|
193509
194241
|
}
|
|
193510
194242
|
|
|
193511
194243
|
// utils/proxy.ts
|
|
193512
|
-
var
|
|
194244
|
+
var core9 = __toESM(require_core(), 1);
|
|
193513
194245
|
|
|
193514
194246
|
// utils/token.ts
|
|
193515
|
-
var
|
|
194247
|
+
var core8 = __toESM(require_core(), 1);
|
|
193516
194248
|
import assert2 from "node:assert/strict";
|
|
193517
194249
|
var mcpTokenValue;
|
|
193518
194250
|
var refreshMcpTokenFn;
|
|
@@ -193520,7 +194252,7 @@ function getMcpTokenRefresh() {
|
|
|
193520
194252
|
return refreshMcpTokenFn;
|
|
193521
194253
|
}
|
|
193522
194254
|
function getJobToken() {
|
|
193523
|
-
const inputToken =
|
|
194255
|
+
const inputToken = core8.getInput("token");
|
|
193524
194256
|
if (inputToken) {
|
|
193525
194257
|
return inputToken;
|
|
193526
194258
|
}
|
|
@@ -193536,7 +194268,7 @@ async function resolveTokens(params) {
|
|
|
193536
194268
|
if (externalToken) {
|
|
193537
194269
|
mcpTokenValue = externalToken;
|
|
193538
194270
|
if (isGitHubActions) {
|
|
193539
|
-
|
|
194271
|
+
core8.setSecret(externalToken);
|
|
193540
194272
|
}
|
|
193541
194273
|
log.info("\xBB using external GH_TOKEN for both git and MCP");
|
|
193542
194274
|
return {
|
|
@@ -193556,7 +194288,7 @@ async function resolveTokens(params) {
|
|
|
193556
194288
|
const gitPermissions = params.push === "disabled" ? { contents: "read" } : { contents: "write", workflows: "write" };
|
|
193557
194289
|
const gitToken = await acquireNewToken({ repos: writeRepos, permissions: gitPermissions });
|
|
193558
194290
|
if (isGitHubActions) {
|
|
193559
|
-
|
|
194291
|
+
core8.setSecret(gitToken);
|
|
193560
194292
|
}
|
|
193561
194293
|
log.info(
|
|
193562
194294
|
`\xBB acquired git token (${Object.entries(gitPermissions).map((e) => e.join(":")).join(", ")})`
|
|
@@ -193573,7 +194305,7 @@ async function resolveTokens(params) {
|
|
|
193573
194305
|
};
|
|
193574
194306
|
const mcpToken = await acquireNewToken({ repos: writeRepos, permissions: mcpPermissions });
|
|
193575
194307
|
if (isGitHubActions) {
|
|
193576
|
-
|
|
194308
|
+
core8.setSecret(mcpToken);
|
|
193577
194309
|
}
|
|
193578
194310
|
log.info(
|
|
193579
194311
|
`\xBB acquired scoped MCP token (${Object.entries(mcpPermissions).map((e) => e.join(":")).join(", ")})`
|
|
@@ -193582,7 +194314,7 @@ async function resolveTokens(params) {
|
|
|
193582
194314
|
let ghToken;
|
|
193583
194315
|
if (ghPermissions) {
|
|
193584
194316
|
ghToken = await acquireNewToken({ permissions: ghPermissions });
|
|
193585
|
-
if (isGitHubActions)
|
|
194317
|
+
if (isGitHubActions) core8.setSecret(ghToken);
|
|
193586
194318
|
log.info(
|
|
193587
194319
|
`\xBB acquired gh token mirroring ${params.authorPermission} (${formatPermissions(ghPermissions)})`
|
|
193588
194320
|
);
|
|
@@ -193593,7 +194325,7 @@ async function resolveTokens(params) {
|
|
|
193593
194325
|
repos: params.xrepo.read,
|
|
193594
194326
|
permissions: { contents: "read" }
|
|
193595
194327
|
});
|
|
193596
|
-
if (isGitHubActions)
|
|
194328
|
+
if (isGitHubActions) core8.setSecret(readToken);
|
|
193597
194329
|
log.info(`\xBB acquired cross-repo read token (contents:read, ${params.xrepo.read.length} repos)`);
|
|
193598
194330
|
}
|
|
193599
194331
|
mcpTokenValue = mcpToken;
|
|
@@ -193612,7 +194344,7 @@ async function resolveTokens(params) {
|
|
|
193612
194344
|
oidc: params.oidc ?? void 0
|
|
193613
194345
|
}).then((fresh) => {
|
|
193614
194346
|
if (isGitHubActions) {
|
|
193615
|
-
|
|
194347
|
+
core8.setSecret(fresh);
|
|
193616
194348
|
}
|
|
193617
194349
|
mcpTokenValue = fresh;
|
|
193618
194350
|
currentMcpToken = fresh;
|
|
@@ -193633,7 +194365,7 @@ async function resolveTokens(params) {
|
|
|
193633
194365
|
permissions: gitPermissions,
|
|
193634
194366
|
oidc: params.oidc ?? void 0
|
|
193635
194367
|
}).then((fresh) => {
|
|
193636
|
-
if (isGitHubActions)
|
|
194368
|
+
if (isGitHubActions) core8.setSecret(fresh);
|
|
193637
194369
|
void revokeGitHubInstallationToken(currentGitToken);
|
|
193638
194370
|
currentGitToken = fresh;
|
|
193639
194371
|
log.warning("\xBB GitHub rejected the git token; re-acquired a fresh git token");
|
|
@@ -193650,7 +194382,7 @@ async function resolveTokens(params) {
|
|
|
193650
194382
|
permissions: { contents: "read" },
|
|
193651
194383
|
oidc: params.oidc ?? void 0
|
|
193652
194384
|
}).then((fresh) => {
|
|
193653
|
-
if (isGitHubActions)
|
|
194385
|
+
if (isGitHubActions) core8.setSecret(fresh);
|
|
193654
194386
|
void revokeGitHubInstallationToken(read2);
|
|
193655
194387
|
currentReadToken = fresh;
|
|
193656
194388
|
log.warning("\xBB GitHub rejected the read token; re-acquired a fresh read token");
|
|
@@ -193864,7 +194596,7 @@ async function resolveProxyModel(ctx) {
|
|
|
193864
194596
|
});
|
|
193865
194597
|
if (!key) return;
|
|
193866
194598
|
process.env.OPENROUTER_API_KEY = key;
|
|
193867
|
-
|
|
194599
|
+
core9.setSecret(key);
|
|
193868
194600
|
ctx.payload.proxyModel = ctx.proxyModel;
|
|
193869
194601
|
ctx.toolState.model = ctx.proxyModel;
|
|
193870
194602
|
const label = ctx.oss ? "oss" : "router";
|
|
@@ -193935,7 +194667,7 @@ async function runProxyResolution(ctx) {
|
|
|
193935
194667
|
|
|
193936
194668
|
// utils/prSummary.ts
|
|
193937
194669
|
import { mkdir as mkdir3, readFile as readFile4, writeFile as writeFile3 } from "node:fs/promises";
|
|
193938
|
-
import { dirname as dirname5, join as
|
|
194670
|
+
import { dirname as dirname5, join as join26 } from "node:path";
|
|
193939
194671
|
var SUMMARY_FILE_NAME = "pullfrog-summary.md";
|
|
193940
194672
|
var SUMMARY_SCAFFOLD = `# PR summary
|
|
193941
194673
|
|
|
@@ -193945,7 +194677,7 @@ var SUMMARY_SCAFFOLD = `# PR summary
|
|
|
193945
194677
|
var MIN_SNAPSHOT_LENGTH = 60;
|
|
193946
194678
|
var MAX_SNAPSHOT_LENGTH = 32768;
|
|
193947
194679
|
function summaryFilePath(tmpdir3) {
|
|
193948
|
-
return
|
|
194680
|
+
return join26(tmpdir3, SUMMARY_FILE_NAME);
|
|
193949
194681
|
}
|
|
193950
194682
|
async function seedSummaryFile(params) {
|
|
193951
194683
|
const path4 = summaryFilePath(params.tmpdir);
|
|
@@ -194074,6 +194806,7 @@ var defaultSettings = {
|
|
|
194074
194806
|
shell: "restricted",
|
|
194075
194807
|
prApproveEnabled: false,
|
|
194076
194808
|
autoMergeEnabled: false,
|
|
194809
|
+
codexAgent: false,
|
|
194077
194810
|
signedCommits: false,
|
|
194078
194811
|
repoIntelligence: false,
|
|
194079
194812
|
progressComments: true,
|
|
@@ -194562,7 +195295,7 @@ ${genericBody}`,
|
|
|
194562
195295
|
}
|
|
194563
195296
|
|
|
194564
195297
|
// utils/runLifecycle.ts
|
|
194565
|
-
var
|
|
195298
|
+
var core10 = __toESM(require_core(), 1);
|
|
194566
195299
|
|
|
194567
195300
|
// utils/autoMerge.ts
|
|
194568
195301
|
function hasBlockingHumanReview(reviews) {
|
|
@@ -194948,7 +195681,7 @@ async function finalizeSuccessRun(input) {
|
|
|
194948
195681
|
}
|
|
194949
195682
|
if (input.toolState.output) {
|
|
194950
195683
|
log.info(`::pullfrog-output::${Buffer.from(input.toolState.output).toString("base64")}`);
|
|
194951
|
-
|
|
195684
|
+
core10.setOutput("result", input.toolState.output);
|
|
194952
195685
|
}
|
|
194953
195686
|
if (input.result.success) {
|
|
194954
195687
|
await approveAfterFix(input.toolContext).catch((error52) => {
|
|
@@ -195412,7 +196145,11 @@ async function main() {
|
|
|
195412
196145
|
}
|
|
195413
196146
|
const resolvedModel = credentials.kind === "fellBack" ? credentials.replacement : configuredModel;
|
|
195414
196147
|
vertexCredentials = materializeVertexCredentials({ model: resolvedModel });
|
|
195415
|
-
const agent2 = resolveAgent({
|
|
196148
|
+
const agent2 = resolveAgent({
|
|
196149
|
+
model: resolvedModel,
|
|
196150
|
+
proxyModel: payload.proxyModel,
|
|
196151
|
+
codexAgent: runContext.repoSettings.codexAgent
|
|
196152
|
+
});
|
|
195416
196153
|
const effectiveModel = payload.proxyModel ?? resolvedModel ?? payload.model;
|
|
195417
196154
|
toolState.model = effectiveModel;
|
|
195418
196155
|
if (!payload.proxyModel) {
|
|
@@ -195596,8 +196333,8 @@ ${instructions.user}` : null,
|
|
|
195596
196333
|
log.info(instructions.full);
|
|
195597
196334
|
});
|
|
195598
196335
|
if (agentId === "opencode") {
|
|
195599
|
-
const pluginDir =
|
|
195600
|
-
const hasPlugins =
|
|
196336
|
+
const pluginDir = join27(process.cwd(), ".opencode", "plugin");
|
|
196337
|
+
const hasPlugins = existsSync9(pluginDir) && readdirSync2(pluginDir).some((f) => /\.[jt]sx?$/.test(f));
|
|
195601
196338
|
if (hasPlugins && toolState.dependencyInstallation?.promise) {
|
|
195602
196339
|
log.info(
|
|
195603
196340
|
"\xBB .opencode/plugin/ detected \u2014 awaiting dependency installation before agent start"
|
|
@@ -195629,16 +196366,14 @@ ${instructions.user}` : null,
|
|
|
195629
196366
|
const onInnerActivityTimeout = () => {
|
|
195630
196367
|
if (innerTimeoutFired) return;
|
|
195631
196368
|
innerTimeoutFired = true;
|
|
195632
|
-
log.info(
|
|
195633
|
-
"\xBB inner activity timeout fired \u2014 stopping MCP server and starting 5min safety-net timer"
|
|
195634
|
-
);
|
|
195635
|
-
mcpHttpServer[Symbol.asyncDispose]().catch((err) => {
|
|
195636
|
-
log.debug(
|
|
195637
|
-
`mcp server stop after inner kill failed: ${err instanceof Error ? err.message : String(err)}`
|
|
195638
|
-
);
|
|
195639
|
-
});
|
|
196369
|
+
log.info("\xBB inner activity timeout fired \u2014 starting 5min safety-net timer");
|
|
195640
196370
|
safetyNetTimer = setTimeout(
|
|
195641
196371
|
() => {
|
|
196372
|
+
mcpHttpServer[Symbol.asyncDispose]().catch((err) => {
|
|
196373
|
+
log.debug(
|
|
196374
|
+
`mcp server stop after inner kill failed: ${err instanceof Error ? err.message : String(err)}`
|
|
196375
|
+
);
|
|
196376
|
+
});
|
|
195642
196377
|
activityTimeout?.forceReject(
|
|
195643
196378
|
"agent still pending 5min after inner activity kill \u2014 forcing exit"
|
|
195644
196379
|
);
|
|
@@ -195647,6 +196382,13 @@ ${instructions.user}` : null,
|
|
|
195647
196382
|
);
|
|
195648
196383
|
safetyNetTimer.unref?.();
|
|
195649
196384
|
};
|
|
196385
|
+
const onTurnRecovered = () => {
|
|
196386
|
+
if (!innerTimeoutFired) return;
|
|
196387
|
+
innerTimeoutFired = false;
|
|
196388
|
+
if (safetyNetTimer) clearTimeout(safetyNetTimer);
|
|
196389
|
+
safetyNetTimer = void 0;
|
|
196390
|
+
log.info("\xBB inner activity safety net stood down \u2014 turn recovered");
|
|
196391
|
+
};
|
|
195650
196392
|
const agentPromise = agent2.run({
|
|
195651
196393
|
payload,
|
|
195652
196394
|
resolvedModel,
|
|
@@ -195667,6 +196409,7 @@ ${instructions.user}` : null,
|
|
|
195667
196409
|
toolState,
|
|
195668
196410
|
apiToken: runContext.apiToken,
|
|
195669
196411
|
onActivityTimeout: onInnerActivityTimeout,
|
|
196412
|
+
onTurnRecovered,
|
|
195670
196413
|
onToolUse: (event) => {
|
|
195671
196414
|
const wasTracked = recordDiffReadFromToolUse({
|
|
195672
196415
|
state: primaryRepoState(toolState).diffCoverage,
|