pullfrog 0.1.56 → 0.1.59
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/cli.mjs +1070 -396
- package/dist/external.d.ts +12 -4
- package/dist/index.js +1105 -427
- package/dist/internal.js +18 -0
- package/dist/models.d.ts +42 -1
- package/dist/utils/agent.d.ts +10 -0
- package/dist/utils/codexHome.d.ts +27 -0
- 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:
|
|
@@ -104369,15 +104399,13 @@ function formatMcpToolRef(agentId, toolName) {
|
|
|
104369
104399
|
function extractMcpToolRefs(agentId, text) {
|
|
104370
104400
|
const prefix = formatMcpToolRef(agentId, "");
|
|
104371
104401
|
const escaped = prefix.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
104372
|
-
return [...text.matchAll(new RegExp(
|
|
104402
|
+
return [...text.matchAll(new RegExp(`(?<![A-Za-z0-9_])${escaped}([a-z0-9_]+)`, "g"))].map(
|
|
104403
|
+
(match3) => match3[1]
|
|
104404
|
+
);
|
|
104373
104405
|
}
|
|
104374
|
-
function
|
|
104406
|
+
function findDanglingPromptToolRefs(params) {
|
|
104375
104407
|
const registered = new Set(params.toolNames);
|
|
104376
|
-
|
|
104377
|
-
if (dangling.length === 0) return;
|
|
104378
|
-
throw new Error(
|
|
104379
|
-
`prompt advertises ${dangling.length} tool(s) the MCP server did not register: ${dangling.join(", ")}. registered: ${[...registered].sort().join(", ")}`
|
|
104380
|
-
);
|
|
104408
|
+
return [...new Set(extractMcpToolRefs(params.agentId, params.prompt))].filter((name) => !registered.has(name)).sort();
|
|
104381
104409
|
}
|
|
104382
104410
|
function hasSimilarIssues(params) {
|
|
104383
104411
|
return params.repoIntelligence && !!params.event.issue_number && !params.event.is_pr;
|
|
@@ -104863,7 +104891,8 @@ import { pipeline } from "node:stream/promises";
|
|
|
104863
104891
|
async function installFromNpmTarball(params) {
|
|
104864
104892
|
const tempDir = process.env.PULLFROG_TEMP_DIR;
|
|
104865
104893
|
if (!tempDir) throw new Error("PULLFROG_TEMP_DIR is not set");
|
|
104866
|
-
const
|
|
104894
|
+
const packageRoot = join(tempDir, `npm-${params.packageName.replace(/[^a-z0-9]+/gi, "-")}`);
|
|
104895
|
+
const extractedDir = join(packageRoot, "package");
|
|
104867
104896
|
const cliPath = join(extractedDir, params.executablePath);
|
|
104868
104897
|
if (existsSync2(cliPath)) {
|
|
104869
104898
|
log.debug(`\xBB using cached binary at ${cliPath}`);
|
|
@@ -104889,7 +104918,8 @@ async function installFromNpmTarball(params) {
|
|
|
104889
104918
|
}
|
|
104890
104919
|
}
|
|
104891
104920
|
log.debug(`\xBB installing ${params.packageName}@${resolvedVersion}...`);
|
|
104892
|
-
|
|
104921
|
+
mkdirSync(packageRoot, { recursive: true });
|
|
104922
|
+
const tarballPath = join(packageRoot, "package.tgz");
|
|
104893
104923
|
const npmRegistry = process.env.NPM_REGISTRY || "https://registry.npmjs.org";
|
|
104894
104924
|
let tarballUrl;
|
|
104895
104925
|
if (params.packageName.startsWith("@")) {
|
|
@@ -104909,7 +104939,7 @@ async function installFromNpmTarball(params) {
|
|
|
104909
104939
|
await pipeline(response.body, fileStream);
|
|
104910
104940
|
log.debug(`\xBB downloaded tarball to ${tarballPath}`);
|
|
104911
104941
|
log.debug(`\xBB extracting tarball...`);
|
|
104912
|
-
const extractResult = spawnSync("tar", ["-xzf", tarballPath, "-C",
|
|
104942
|
+
const extractResult = spawnSync("tar", ["-xzf", tarballPath, "-C", packageRoot], {
|
|
104913
104943
|
stdio: "pipe",
|
|
104914
104944
|
encoding: "utf-8"
|
|
104915
104945
|
});
|
|
@@ -104918,9 +104948,6 @@ async function installFromNpmTarball(params) {
|
|
|
104918
104948
|
`Failed to extract tarball: ${extractResult.stderr || extractResult.stdout || "Unknown error"}`
|
|
104919
104949
|
);
|
|
104920
104950
|
}
|
|
104921
|
-
if (!existsSync2(cliPath)) {
|
|
104922
|
-
throw new Error(`Executable not found in extracted package at ${cliPath}`);
|
|
104923
|
-
}
|
|
104924
104951
|
if (params.installDependencies) {
|
|
104925
104952
|
log.debug(`\xBB installing dependencies for ${params.packageName}...`);
|
|
104926
104953
|
const installResult = spawnSync("npm", ["install", "--production"], {
|
|
@@ -104941,6 +104968,9 @@ async function installFromNpmTarball(params) {
|
|
|
104941
104968
|
}
|
|
104942
104969
|
log.debug(`\xBB dependencies installed`);
|
|
104943
104970
|
}
|
|
104971
|
+
if (!existsSync2(cliPath)) {
|
|
104972
|
+
throw new Error(`Executable not found in extracted package at ${cliPath}`);
|
|
104973
|
+
}
|
|
104944
104974
|
chmodSync(cliPath, 493);
|
|
104945
104975
|
log.debug(`\xBB ${params.packageName} installed at ${cliPath}`);
|
|
104946
104976
|
return cliPath;
|
|
@@ -105114,6 +105144,7 @@ function resolveRunAlias(ctx) {
|
|
|
105114
105144
|
}
|
|
105115
105145
|
const model = ctx.resolvedModel;
|
|
105116
105146
|
if (!model) return void 0;
|
|
105147
|
+
if (model.startsWith(`${AZURE_PROVIDER}/`)) return void 0;
|
|
105117
105148
|
return modelAliases.find((a) => !a.fallback && a.resolve === model) ?? matchHostedAnthropicAlias(model);
|
|
105118
105149
|
}
|
|
105119
105150
|
function ossEffortFloor(ctx) {
|
|
@@ -105152,7 +105183,7 @@ var import_semver = __toESM(require_semver2(), 1);
|
|
|
105152
105183
|
// package.json
|
|
105153
105184
|
var package_default = {
|
|
105154
105185
|
name: "pullfrog",
|
|
105155
|
-
version: "0.1.
|
|
105186
|
+
version: "0.1.59",
|
|
105156
105187
|
type: "module",
|
|
105157
105188
|
bin: {
|
|
105158
105189
|
pullfrog: "dist/cli.mjs",
|
|
@@ -105184,6 +105215,8 @@ var package_default = {
|
|
|
105184
105215
|
"@clack/prompts": "^1.2.0",
|
|
105185
105216
|
"@modelcontextprotocol/sdk": "^1.26.0",
|
|
105186
105217
|
"@opencode-ai/sdk": "1.18.5",
|
|
105218
|
+
"@openai/codex": "0.147.0",
|
|
105219
|
+
"@openai/codex-sdk": "0.147.0",
|
|
105187
105220
|
"@octokit/plugin-throttling": "^11.0.3",
|
|
105188
105221
|
"@octokit/rest": "^22.0.0",
|
|
105189
105222
|
"@octokit/webhooks-types": "^7.6.1",
|
|
@@ -107305,13 +107338,782 @@ var claude = agent({
|
|
|
107305
107338
|
}
|
|
107306
107339
|
});
|
|
107307
107340
|
|
|
107308
|
-
// agents/
|
|
107341
|
+
// agents/codex.ts
|
|
107309
107342
|
var core2 = __toESM(require_core(), 1);
|
|
107310
|
-
import {
|
|
107311
|
-
import { mkdirSync as mkdirSync6, writeFileSync as writeFileSync5 } from "node:fs";
|
|
107343
|
+
import { existsSync as existsSync4, mkdirSync as mkdirSync6, readFileSync as readFileSync2, writeFileSync as writeFileSync5 } from "node:fs";
|
|
107312
107344
|
import { join as join6 } from "node:path";
|
|
107313
107345
|
import { performance as performance6 } from "node:perf_hooks";
|
|
107314
107346
|
|
|
107347
|
+
// utils/codexHome.ts
|
|
107348
|
+
import { execFileSync as execFileSync3 } from "node:child_process";
|
|
107349
|
+
import { mkdirSync as mkdirSync5, writeFileSync as writeFileSync4 } from "node:fs";
|
|
107350
|
+
import { homedir as homedir2, userInfo } from "node:os";
|
|
107351
|
+
import { join as join5 } from "node:path";
|
|
107352
|
+
|
|
107353
|
+
// utils/codexOAuth.ts
|
|
107354
|
+
function decodeJwtExpMs(token) {
|
|
107355
|
+
const parts = token.split(".");
|
|
107356
|
+
if (parts.length !== 3) return null;
|
|
107357
|
+
let payload;
|
|
107358
|
+
try {
|
|
107359
|
+
payload = JSON.parse(Buffer.from(parts[1], "base64url").toString("utf8"));
|
|
107360
|
+
} catch {
|
|
107361
|
+
return null;
|
|
107362
|
+
}
|
|
107363
|
+
if (typeof payload.exp !== "number" || !Number.isFinite(payload.exp)) return null;
|
|
107364
|
+
return payload.exp * 1e3;
|
|
107365
|
+
}
|
|
107366
|
+
function parseCodexAuthBody(raw2) {
|
|
107367
|
+
let parsed2;
|
|
107368
|
+
try {
|
|
107369
|
+
parsed2 = JSON.parse(raw2);
|
|
107370
|
+
} catch {
|
|
107371
|
+
return null;
|
|
107372
|
+
}
|
|
107373
|
+
if (!parsed2 || typeof parsed2 !== "object") return null;
|
|
107374
|
+
const v = parsed2;
|
|
107375
|
+
if (v.auth_mode !== "chatgpt") return null;
|
|
107376
|
+
const tokens = v.tokens;
|
|
107377
|
+
if (!tokens || typeof tokens !== "object") return null;
|
|
107378
|
+
const t = tokens;
|
|
107379
|
+
if (typeof t.access_token !== "string" || t.access_token.length === 0) return null;
|
|
107380
|
+
if (typeof t.refresh_token !== "string" || t.refresh_token.length === 0) return null;
|
|
107381
|
+
return {
|
|
107382
|
+
auth_mode: "chatgpt",
|
|
107383
|
+
...typeof v.refresh_rejected_at === "string" ? { refresh_rejected_at: v.refresh_rejected_at } : {},
|
|
107384
|
+
tokens: {
|
|
107385
|
+
access_token: t.access_token,
|
|
107386
|
+
refresh_token: t.refresh_token,
|
|
107387
|
+
...typeof t.id_token === "string" ? { id_token: t.id_token } : {},
|
|
107388
|
+
...typeof t.account_id === "string" ? { account_id: t.account_id } : {}
|
|
107389
|
+
},
|
|
107390
|
+
...typeof v.last_refresh === "string" ? { last_refresh: v.last_refresh } : {}
|
|
107391
|
+
};
|
|
107392
|
+
}
|
|
107393
|
+
function stringifyCodexAuthBody(body) {
|
|
107394
|
+
return `${JSON.stringify(body, null, 2)}
|
|
107395
|
+
`;
|
|
107396
|
+
}
|
|
107397
|
+
|
|
107398
|
+
// utils/codexHome.ts
|
|
107399
|
+
var CODEX_AUTH_ENV = "CODEX_AUTH_JSON";
|
|
107400
|
+
var PULLFROG_DATA_DIR = "/var/lib/pullfrog";
|
|
107401
|
+
function isRejectedChain(body) {
|
|
107402
|
+
if (!body.refresh_rejected_at) return false;
|
|
107403
|
+
log.warning(
|
|
107404
|
+
`\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.`
|
|
107405
|
+
);
|
|
107406
|
+
return true;
|
|
107407
|
+
}
|
|
107408
|
+
function installCodexAuth() {
|
|
107409
|
+
const raw2 = process.env[CODEX_AUTH_ENV];
|
|
107410
|
+
if (!raw2) return null;
|
|
107411
|
+
const body = parseCodexAuthBody(raw2);
|
|
107412
|
+
if (!body) {
|
|
107413
|
+
log.warning(`\xBB ${CODEX_AUTH_ENV} present but malformed; ignoring`);
|
|
107414
|
+
return null;
|
|
107415
|
+
}
|
|
107416
|
+
if (isRejectedChain(body)) return null;
|
|
107417
|
+
const expiresMs = decodeJwtExpMs(body.tokens.access_token) ?? 0;
|
|
107418
|
+
const xdgDataHome = resolveDataHome();
|
|
107419
|
+
const opencodeDir = join5(xdgDataHome, "opencode");
|
|
107420
|
+
const authPath = join5(opencodeDir, "auth.json");
|
|
107421
|
+
const opencodeAuth = {
|
|
107422
|
+
openai: {
|
|
107423
|
+
type: "oauth",
|
|
107424
|
+
refresh: body.tokens.refresh_token,
|
|
107425
|
+
access: body.tokens.access_token,
|
|
107426
|
+
expires: expiresMs,
|
|
107427
|
+
...body.tokens.account_id ? { accountId: body.tokens.account_id } : {}
|
|
107428
|
+
}
|
|
107429
|
+
};
|
|
107430
|
+
mkdirSync5(opencodeDir, { recursive: true });
|
|
107431
|
+
writeFileSync4(authPath, `${JSON.stringify(opencodeAuth, null, 2)}
|
|
107432
|
+
`, { mode: 384 });
|
|
107433
|
+
process.env.XDG_DATA_HOME = xdgDataHome;
|
|
107434
|
+
log.info(`\xBB installed Codex auth at ${authPath}`);
|
|
107435
|
+
return {
|
|
107436
|
+
authPath,
|
|
107437
|
+
xdgDataHome,
|
|
107438
|
+
originalRefresh: body.tokens.refresh_token,
|
|
107439
|
+
originalIdToken: body.tokens.id_token
|
|
107440
|
+
};
|
|
107441
|
+
}
|
|
107442
|
+
function installCodexHome() {
|
|
107443
|
+
const raw2 = process.env[CODEX_AUTH_ENV];
|
|
107444
|
+
if (!raw2) return null;
|
|
107445
|
+
const body = parseCodexAuthBody(raw2);
|
|
107446
|
+
if (!body) {
|
|
107447
|
+
log.warning(`\xBB ${CODEX_AUTH_ENV} present but malformed; ignoring`);
|
|
107448
|
+
return null;
|
|
107449
|
+
}
|
|
107450
|
+
if (isRejectedChain(body)) return null;
|
|
107451
|
+
if (!body.tokens.id_token) {
|
|
107452
|
+
log.warning(
|
|
107453
|
+
`\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.`
|
|
107454
|
+
);
|
|
107455
|
+
return null;
|
|
107456
|
+
}
|
|
107457
|
+
const codexHome = join5(resolveDataHome(), "codex");
|
|
107458
|
+
const authPath = join5(codexHome, "auth.json");
|
|
107459
|
+
mkdirSync5(codexHome, { recursive: true });
|
|
107460
|
+
writeFileSync4(authPath, stringifyCodexAuthBody(body), { mode: 384 });
|
|
107461
|
+
log.info(`\xBB installed Codex auth at ${authPath}`);
|
|
107462
|
+
return { codexHome, authPath, originalRefresh: body.tokens.refresh_token };
|
|
107463
|
+
}
|
|
107464
|
+
function resolveDataHome() {
|
|
107465
|
+
if (process.env.CI !== "true") return join5(homedir2(), ".local", "share");
|
|
107466
|
+
bootstrapPullfrogDataDir();
|
|
107467
|
+
return PULLFROG_DATA_DIR;
|
|
107468
|
+
}
|
|
107469
|
+
function bootstrapPullfrogDataDir() {
|
|
107470
|
+
const user = userInfo().username;
|
|
107471
|
+
let primaryGroup;
|
|
107472
|
+
try {
|
|
107473
|
+
primaryGroup = execFileSync3("id", ["-gn", user], { stdio: "pipe", encoding: "utf-8" }).trim();
|
|
107474
|
+
} catch {
|
|
107475
|
+
primaryGroup = user;
|
|
107476
|
+
}
|
|
107477
|
+
try {
|
|
107478
|
+
execFileSync3("sudo", ["-n", "mkdir", "-p", PULLFROG_DATA_DIR], { stdio: "pipe" });
|
|
107479
|
+
execFileSync3("sudo", ["-n", "chown", `${user}:${primaryGroup}`, PULLFROG_DATA_DIR], {
|
|
107480
|
+
stdio: "pipe"
|
|
107481
|
+
});
|
|
107482
|
+
execFileSync3("sudo", ["-n", "chmod", "700", PULLFROG_DATA_DIR], { stdio: "pipe" });
|
|
107483
|
+
} catch (err) {
|
|
107484
|
+
throw new Error(
|
|
107485
|
+
`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.`
|
|
107486
|
+
);
|
|
107487
|
+
}
|
|
107488
|
+
}
|
|
107489
|
+
|
|
107490
|
+
// utils/secrets.ts
|
|
107491
|
+
var SENSITIVE_PATTERNS = [
|
|
107492
|
+
/_KEY$/i,
|
|
107493
|
+
/_SECRET$/i,
|
|
107494
|
+
/_TOKEN$/i,
|
|
107495
|
+
/_PASSWORD$/i,
|
|
107496
|
+
/_CREDENTIAL$/i
|
|
107497
|
+
];
|
|
107498
|
+
function isSensitiveEnvName(key) {
|
|
107499
|
+
return SENSITIVE_PATTERNS.some((p) => p.test(key));
|
|
107500
|
+
}
|
|
107501
|
+
var SAFE_ENV_PREFIXES = ["GITHUB_", "RUNNER_", "JAVA_HOME_", "GOROOT_"];
|
|
107502
|
+
var SAFE_ENV_NAMES = /* @__PURE__ */ new Set([
|
|
107503
|
+
// system
|
|
107504
|
+
"CI",
|
|
107505
|
+
"HOME",
|
|
107506
|
+
"LANG",
|
|
107507
|
+
"LOGNAME",
|
|
107508
|
+
"PATH",
|
|
107509
|
+
"SHELL",
|
|
107510
|
+
"SHLVL",
|
|
107511
|
+
"TERM",
|
|
107512
|
+
"TMPDIR",
|
|
107513
|
+
"TZ",
|
|
107514
|
+
"USER",
|
|
107515
|
+
"XDG_CONFIG_HOME",
|
|
107516
|
+
"XDG_RUNTIME_DIR",
|
|
107517
|
+
"DEBIAN_FRONTEND",
|
|
107518
|
+
// runner image toolchain
|
|
107519
|
+
"ACCEPT_EULA",
|
|
107520
|
+
"AGENT_TOOLSDIRECTORY",
|
|
107521
|
+
"ANDROID_HOME",
|
|
107522
|
+
"ANDROID_NDK",
|
|
107523
|
+
"ANDROID_NDK_HOME",
|
|
107524
|
+
"ANDROID_NDK_LATEST_HOME",
|
|
107525
|
+
"ANDROID_NDK_ROOT",
|
|
107526
|
+
"ANDROID_SDK_ROOT",
|
|
107527
|
+
"ANT_HOME",
|
|
107528
|
+
"AZURE_EXTENSION_DIR",
|
|
107529
|
+
"BOOTSTRAP_HASKELL_NONINTERACTIVE",
|
|
107530
|
+
"CHROME_BIN",
|
|
107531
|
+
"CHROMEWEBDRIVER",
|
|
107532
|
+
"CONDA",
|
|
107533
|
+
"DOTNET_MULTILEVEL_LOOKUP",
|
|
107534
|
+
"DOTNET_NOLOGO",
|
|
107535
|
+
"DOTNET_SKIP_FIRST_TIME_EXPERIENCE",
|
|
107536
|
+
"EDGEWEBDRIVER",
|
|
107537
|
+
"GECKOWEBDRIVER",
|
|
107538
|
+
"GHCUP_INSTALL_BASE_PREFIX",
|
|
107539
|
+
"GRADLE_HOME",
|
|
107540
|
+
"JAVA_HOME",
|
|
107541
|
+
"HOMEBREW_CLEANUP_PERIODIC_FULL_DAYS",
|
|
107542
|
+
"HOMEBREW_NO_AUTO_UPDATE",
|
|
107543
|
+
"ImageOS",
|
|
107544
|
+
"ImageVersion",
|
|
107545
|
+
"NVM_DIR",
|
|
107546
|
+
"PIPX_BIN_DIR",
|
|
107547
|
+
"PIPX_HOME",
|
|
107548
|
+
"PSModulePath",
|
|
107549
|
+
"SELENIUM_JAR_PATH",
|
|
107550
|
+
"SGX_AESM_ADDR",
|
|
107551
|
+
"SWIFT_PATH",
|
|
107552
|
+
"VCPKG_INSTALLATION_ROOT"
|
|
107553
|
+
]);
|
|
107554
|
+
var _userAllowlist = null;
|
|
107555
|
+
function setEnvAllowlist(raw2) {
|
|
107556
|
+
const names = raw2.split("\n").map((line) => line.trim()).filter(Boolean);
|
|
107557
|
+
_userAllowlist = new Set(names);
|
|
107558
|
+
}
|
|
107559
|
+
function isSafeEnvVar(key) {
|
|
107560
|
+
if (SAFE_ENV_NAMES.has(key)) return true;
|
|
107561
|
+
return SAFE_ENV_PREFIXES.some((p) => key.startsWith(p));
|
|
107562
|
+
}
|
|
107563
|
+
var WORKFLOW_COMMAND_ENV_NAMES = [
|
|
107564
|
+
"GITHUB_ENV",
|
|
107565
|
+
"GITHUB_PATH",
|
|
107566
|
+
"GITHUB_OUTPUT",
|
|
107567
|
+
"GITHUB_STATE",
|
|
107568
|
+
"GITHUB_STEP_SUMMARY",
|
|
107569
|
+
// dropping the five paths above but keeping RUNNER_TEMP would be theatre:
|
|
107570
|
+
// the file-command files live in `$RUNNER_TEMP/_runner_file_commands/`, so the
|
|
107571
|
+
// directory is one `ls` away. nothing in the install path reads it (only our
|
|
107572
|
+
// own leak-surface wipe in `setup.ts`, which runs in the parent), and package
|
|
107573
|
+
// managers use TMPDIR.
|
|
107574
|
+
"RUNNER_TEMP"
|
|
107575
|
+
];
|
|
107576
|
+
function filterEnvForUntrustedCode() {
|
|
107577
|
+
const env2 = filterEnv();
|
|
107578
|
+
for (const name of WORKFLOW_COMMAND_ENV_NAMES) delete env2[name];
|
|
107579
|
+
return env2;
|
|
107580
|
+
}
|
|
107581
|
+
function filterEnv() {
|
|
107582
|
+
const filtered = {};
|
|
107583
|
+
for (const [key, value2] of Object.entries(process.env)) {
|
|
107584
|
+
if (value2 === void 0) continue;
|
|
107585
|
+
const userAllowed = _userAllowlist?.has(key) ?? false;
|
|
107586
|
+
if (isSensitiveEnvName(key) && !userAllowed) continue;
|
|
107587
|
+
if (isSafeEnvVar(key) || userAllowed) {
|
|
107588
|
+
filtered[key] = value2;
|
|
107589
|
+
}
|
|
107590
|
+
}
|
|
107591
|
+
return filtered;
|
|
107592
|
+
}
|
|
107593
|
+
function resolveEnv(mode) {
|
|
107594
|
+
if (mode === "inherit") {
|
|
107595
|
+
return process.env;
|
|
107596
|
+
}
|
|
107597
|
+
if (mode === "restricted" || mode === void 0) {
|
|
107598
|
+
return filterEnv();
|
|
107599
|
+
}
|
|
107600
|
+
return { ...filterEnv(), ...mode };
|
|
107601
|
+
}
|
|
107602
|
+
|
|
107603
|
+
// agents/codex.ts
|
|
107604
|
+
var CODEX_TARGETS = {
|
|
107605
|
+
"linux-x64": "x86_64-unknown-linux-musl",
|
|
107606
|
+
"linux-arm64": "aarch64-unknown-linux-musl",
|
|
107607
|
+
"darwin-x64": "x86_64-apple-darwin",
|
|
107608
|
+
"darwin-arm64": "aarch64-apple-darwin",
|
|
107609
|
+
"win32-x64": "x86_64-pc-windows-msvc",
|
|
107610
|
+
"win32-arm64": "aarch64-pc-windows-msvc"
|
|
107611
|
+
};
|
|
107612
|
+
async function installCodexCli() {
|
|
107613
|
+
const target = CODEX_TARGETS[`${process.platform}-${process.arch}`];
|
|
107614
|
+
if (!target) {
|
|
107615
|
+
throw new Error(`the codex CLI ships no binary for ${process.platform}-${process.arch}`);
|
|
107616
|
+
}
|
|
107617
|
+
const binary = process.platform === "win32" ? "codex.exe" : "codex";
|
|
107618
|
+
return await installFromNpmTarball({
|
|
107619
|
+
packageName: "@openai/codex",
|
|
107620
|
+
version: getDevDependencyVersion("@openai/codex"),
|
|
107621
|
+
executablePath: `node_modules/@openai/codex-${process.platform}-${process.arch}/vendor/${target}/bin/${binary}`,
|
|
107622
|
+
installDependencies: true
|
|
107623
|
+
});
|
|
107624
|
+
}
|
|
107625
|
+
var CODEX_DISABLED_FEATURES = [
|
|
107626
|
+
// browser / desktop control — network reach and arbitrary JS
|
|
107627
|
+
"browser_use",
|
|
107628
|
+
"browser_use_external",
|
|
107629
|
+
"browser_use_full_cdp_access",
|
|
107630
|
+
"computer_use",
|
|
107631
|
+
"in_app_browser",
|
|
107632
|
+
// third-party code loaded into the agent process
|
|
107633
|
+
"plugins",
|
|
107634
|
+
"remote_plugin",
|
|
107635
|
+
"plugin_sharing",
|
|
107636
|
+
"skill_mcp_dependency_install",
|
|
107637
|
+
// lifecycle hooks run commands sourced from repo config, which a PR can edit
|
|
107638
|
+
"hooks",
|
|
107639
|
+
// connectors
|
|
107640
|
+
"apps",
|
|
107641
|
+
// ungated subagent fan-out (see the file header)
|
|
107642
|
+
"multi_agent",
|
|
107643
|
+
"multi_agent_v2"
|
|
107644
|
+
];
|
|
107645
|
+
var CODEX_SHELL_FEATURES = ["shell_tool", "unified_exec"];
|
|
107646
|
+
function tomlBool(entries, value2) {
|
|
107647
|
+
return entries.map((name) => `${name} = ${value2}`).join("\n");
|
|
107648
|
+
}
|
|
107649
|
+
function securityOverrideFlags(params) {
|
|
107650
|
+
const shellEnabled = params.ctx.payload.shell === "enabled";
|
|
107651
|
+
const features = [
|
|
107652
|
+
...CODEX_DISABLED_FEATURES.map((name) => `${name}=false`),
|
|
107653
|
+
...CODEX_SHELL_FEATURES.map((name) => `${name}=${shellEnabled}`)
|
|
107654
|
+
];
|
|
107655
|
+
return [
|
|
107656
|
+
"-c",
|
|
107657
|
+
`approval_policy=${JSON.stringify("never")}`,
|
|
107658
|
+
"-c",
|
|
107659
|
+
`sandbox_mode=${JSON.stringify(params.sandboxMode)}`,
|
|
107660
|
+
// the sandbox's EXTENT has to be locked too, not just its mode: a repo
|
|
107661
|
+
// setting `writable_roots = ["/"]` walked straight out of the checkout with
|
|
107662
|
+
// `sandbox_mode` already flag-locked (measured — apply_patch wrote a file
|
|
107663
|
+
// under $HOME).
|
|
107664
|
+
"-c",
|
|
107665
|
+
`sandbox_workspace_write.writable_roots=[${JSON.stringify(params.repoDir)}]`,
|
|
107666
|
+
"-c",
|
|
107667
|
+
"sandbox_workspace_write.network_access=true",
|
|
107668
|
+
// our own MCP server, for a third time in the same class: a repo pointing
|
|
107669
|
+
// `mcp_servers.pullfrog.url` at its own endpoint (measured — `codex mcp get`
|
|
107670
|
+
// reported the repo's URL) turns every pullfrog tool call into an
|
|
107671
|
+
// exfiltration channel, carrying diffs, file contents and cross-repo
|
|
107672
|
+
// learnings to whoever it names.
|
|
107673
|
+
"-c",
|
|
107674
|
+
`mcp_servers.${pullfrogMcpName}.url=${JSON.stringify(params.mcpServerUrl)}`,
|
|
107675
|
+
"-c",
|
|
107676
|
+
`mcp_servers.${pullfrogMcpName}.${APPROVAL_MODE_KEY}=${JSON.stringify("approve")}`,
|
|
107677
|
+
"-c",
|
|
107678
|
+
`mcp_servers.${pullfrogMcpName}.required=true`,
|
|
107679
|
+
"-c",
|
|
107680
|
+
`mcp_servers.${pullfrogMcpName}.tool_timeout_sec=660`,
|
|
107681
|
+
// `enabled` closes the silent-DoS variant: a repo setting it false would
|
|
107682
|
+
// otherwise strip every GitHub tool and leave the agent narrating failure.
|
|
107683
|
+
"-c",
|
|
107684
|
+
`mcp_servers.${pullfrogMcpName}.enabled=true`,
|
|
107685
|
+
// not a boundary, but the same override: a repo silently re-pointing the
|
|
107686
|
+
// model spends the account's money on a pick it never made, and makes the
|
|
107687
|
+
// "Using `…`" run footer a lie about what actually ran.
|
|
107688
|
+
...params.model ? ["-c", `model=${JSON.stringify(params.model)}`] : [],
|
|
107689
|
+
...params.effortRung ? ["-c", `model_reasoning_effort=${JSON.stringify(params.effortRung)}`] : [],
|
|
107690
|
+
...features.flatMap((entry) => ["-c", `features.${entry}`])
|
|
107691
|
+
];
|
|
107692
|
+
}
|
|
107693
|
+
var CODEX_EFFORTS = ["minimal", "low", "medium", "high", "xhigh"];
|
|
107694
|
+
function resolveCodexModel(ctx) {
|
|
107695
|
+
const specifier = ctx.payload.proxyModel ?? ctx.resolvedModel;
|
|
107696
|
+
if (!specifier) return void 0;
|
|
107697
|
+
try {
|
|
107698
|
+
if (getModelProvider(specifier) === "openai") return stripProviderPrefix(specifier);
|
|
107699
|
+
} catch {
|
|
107700
|
+
}
|
|
107701
|
+
log.warning(`\xBB ${specifier} isn't an OpenAI model \u2014 running codex on its own default instead`);
|
|
107702
|
+
return void 0;
|
|
107703
|
+
}
|
|
107704
|
+
var APPROVAL_MODE_KEY = "default_tools_approval_mode";
|
|
107705
|
+
var APPROVE_TOOLS = `${APPROVAL_MODE_KEY} = "approve"`;
|
|
107706
|
+
function repoMcpApprovals(repoDir) {
|
|
107707
|
+
const path4 = join6(repoDir, ".codex", "config.toml");
|
|
107708
|
+
if (!existsSync4(path4)) return [];
|
|
107709
|
+
const out = [];
|
|
107710
|
+
let section = [];
|
|
107711
|
+
let isServerTable = false;
|
|
107712
|
+
function flush() {
|
|
107713
|
+
if (section.length === 0) return;
|
|
107714
|
+
const declared = section.some((line) => line.trimStart().startsWith(APPROVAL_MODE_KEY));
|
|
107715
|
+
if (isServerTable && !declared) section.splice(1, 0, APPROVE_TOOLS);
|
|
107716
|
+
out.push(...section, "");
|
|
107717
|
+
section = [];
|
|
107718
|
+
}
|
|
107719
|
+
for (const line of readFileSync2(path4, "utf8").split("\n")) {
|
|
107720
|
+
const header = line.trim().match(/^\[(?<key>[^\]]+)\]$/);
|
|
107721
|
+
if (!header?.groups) {
|
|
107722
|
+
if (section.length > 0) section.push(line);
|
|
107723
|
+
continue;
|
|
107724
|
+
}
|
|
107725
|
+
flush();
|
|
107726
|
+
const key = header.groups.key.trim();
|
|
107727
|
+
if (!key.startsWith("mcp_servers.")) continue;
|
|
107728
|
+
const name = key.slice("mcp_servers.".length).replace(/^"([^"]*)"/, "$1");
|
|
107729
|
+
if (name === pullfrogMcpName || name.startsWith(`${pullfrogMcpName}.`)) continue;
|
|
107730
|
+
isServerTable = !name.includes(".");
|
|
107731
|
+
section = [line.trim()];
|
|
107732
|
+
}
|
|
107733
|
+
flush();
|
|
107734
|
+
return out;
|
|
107735
|
+
}
|
|
107736
|
+
function warnIfNativeEditsUnavailable(ctx) {
|
|
107737
|
+
if (process.platform !== "linux" || ctx.payload.shell !== "disabled") return;
|
|
107738
|
+
log.warning(
|
|
107739
|
+
"\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."
|
|
107740
|
+
);
|
|
107741
|
+
}
|
|
107742
|
+
function writeCodexConfig(params) {
|
|
107743
|
+
const shellEnabled = params.ctx.payload.shell === "enabled";
|
|
107744
|
+
const sandboxMode = params.ctx.payload.push === "disabled" ? "read-only" : "workspace-write";
|
|
107745
|
+
const repoDir = process.cwd();
|
|
107746
|
+
const config3 = [
|
|
107747
|
+
"# written by pullfrog",
|
|
107748
|
+
// "never" keeps the run non-interactive WITHOUT
|
|
107749
|
+
// --dangerously-bypass-approvals-and-sandbox, which would also switch the
|
|
107750
|
+
// sandbox off and let the native apply_patch tool write anywhere.
|
|
107751
|
+
'approval_policy = "never"',
|
|
107752
|
+
`sandbox_mode = ${JSON.stringify(sandboxMode)}`,
|
|
107753
|
+
// matches opencode's `webfetch: "allow"` — research is part of the job and
|
|
107754
|
+
// neither harness gates it on a permission.
|
|
107755
|
+
'web_search = "live"',
|
|
107756
|
+
...params.model ? [`model = ${JSON.stringify(params.model)}`] : [],
|
|
107757
|
+
...params.effortRung ? [`model_reasoning_effort = ${JSON.stringify(params.effortRung)}`] : [],
|
|
107758
|
+
"",
|
|
107759
|
+
"[features]",
|
|
107760
|
+
tomlBool(CODEX_DISABLED_FEATURES, false),
|
|
107761
|
+
tomlBool(CODEX_SHELL_FEATURES, shellEnabled),
|
|
107762
|
+
"",
|
|
107763
|
+
"[sandbox_workspace_write]",
|
|
107764
|
+
"network_access = true",
|
|
107765
|
+
// the checkout, named explicitly rather than left to `workspace-write` to
|
|
107766
|
+
// imply. does NOT fix the Linux native-write failure — nothing here does;
|
|
107767
|
+
// see wiki/codex-agent.md "The Linux native-write blocker".
|
|
107768
|
+
`writable_roots = [${JSON.stringify(repoDir)}]`,
|
|
107769
|
+
"",
|
|
107770
|
+
`[mcp_servers.${pullfrogMcpName}]`,
|
|
107771
|
+
`url = ${JSON.stringify(params.ctx.mcpServerUrl)}`,
|
|
107772
|
+
// codex's default MCP approval mode PROMPTS for anything it can't read as
|
|
107773
|
+
// read-only, and a `codex exec` run has nobody to answer — the call comes
|
|
107774
|
+
// back "user cancelled MCP tool call" and the agent silently accomplishes
|
|
107775
|
+
// nothing. our own server is the trusted one; the boundary is what we
|
|
107776
|
+
// register, not a second confirmation.
|
|
107777
|
+
APPROVE_TOOLS,
|
|
107778
|
+
// a run whose GitHub tools never registered can't do its job, so fail loudly
|
|
107779
|
+
// rather than let the agent improvise without them.
|
|
107780
|
+
"required = true",
|
|
107781
|
+
// mirrors the opencode harness's 660s: `checkout_pr` runs a multi-minute
|
|
107782
|
+
// fetch on large repos and enforces its own 600s deadline, so a shorter
|
|
107783
|
+
// client abort turns a working checkout into a spurious tool error.
|
|
107784
|
+
"tool_timeout_sec = 660",
|
|
107785
|
+
"",
|
|
107786
|
+
...repoMcpApprovals(repoDir),
|
|
107787
|
+
// UNTRUSTED, deliberately, and this one line is the whole config-precedence
|
|
107788
|
+
// boundary. Codex loads `<repo>/.codex/config.toml` as a layer that
|
|
107789
|
+
// OUTRANKS ours (project 25 vs user 20), and its denylist strips none of
|
|
107790
|
+
// `sandbox_mode`, `approval_policy`, `features` or `mcp_servers` — so a
|
|
107791
|
+
// fork PR could ship a config re-enabling the native shell. Marking the
|
|
107792
|
+
// checkout untrusted makes that layer load `disabled`, closing the class
|
|
107793
|
+
// outright rather than key by key: `RawMcpServerConfig` alone has ~26
|
|
107794
|
+
// fields and enumeration can never finish.
|
|
107795
|
+
//
|
|
107796
|
+
// It costs nothing. `codex exec`'s only startup gate is a GIT-REPO check
|
|
107797
|
+
// (`exec/src/lib.rs:796` — `get_git_repo_root(...).is_none()`, whose error
|
|
107798
|
+
// text unhelpfully says "Not inside a trusted directory"), so an untrusted
|
|
107799
|
+
// checkout still runs. And the repo's own MCP servers survive, because
|
|
107800
|
+
// `repoMcpApprovals` above copies them into OUR layer — which is why that
|
|
107801
|
+
// copying exists at all.
|
|
107802
|
+
//
|
|
107803
|
+
// Measured both ways at 0.147.0: `trusted` → the repo's `model` won;
|
|
107804
|
+
// `untrusted` → it did not, and the run proceeded normally.
|
|
107805
|
+
`[projects.${JSON.stringify(repoDir)}]`,
|
|
107806
|
+
'trust_level = "untrusted"',
|
|
107807
|
+
""
|
|
107808
|
+
].join("\n");
|
|
107809
|
+
writeFileSync5(join6(params.codexHome, "config.toml"), config3, { mode: 384 });
|
|
107810
|
+
log.info(`\xBB codex config: sandbox=${sandboxMode}, nativeShell=${shellEnabled}`);
|
|
107811
|
+
}
|
|
107812
|
+
function toTodoWriteInput(item) {
|
|
107813
|
+
return {
|
|
107814
|
+
todos: item.items.map((todo, index) => ({
|
|
107815
|
+
id: String(index),
|
|
107816
|
+
content: todo.text,
|
|
107817
|
+
status: todo.completed ? "completed" : "pending"
|
|
107818
|
+
}))
|
|
107819
|
+
};
|
|
107820
|
+
}
|
|
107821
|
+
async function runCodex(params) {
|
|
107822
|
+
const startTime = performance6.now();
|
|
107823
|
+
const thinkingTimer = new ThinkingTimer();
|
|
107824
|
+
const output = new TailBuffer(DEFAULT_MAX_RETAINED_BYTES);
|
|
107825
|
+
const recentStderr = [];
|
|
107826
|
+
let eventCount = 0;
|
|
107827
|
+
let finalOutput = "";
|
|
107828
|
+
let threadId;
|
|
107829
|
+
let turnError = null;
|
|
107830
|
+
let lastProviderError = null;
|
|
107831
|
+
let stdoutBuffer = "";
|
|
107832
|
+
const tokens = { input: 0, cacheRead: 0, output: 0 };
|
|
107833
|
+
function buildUsage2() {
|
|
107834
|
+
if (tokens.input === 0 && tokens.output === 0) return void 0;
|
|
107835
|
+
return {
|
|
107836
|
+
agent: "codex",
|
|
107837
|
+
inputTokens: tokens.input,
|
|
107838
|
+
outputTokens: tokens.output,
|
|
107839
|
+
cacheReadTokens: tokens.cacheRead || void 0
|
|
107840
|
+
};
|
|
107841
|
+
}
|
|
107842
|
+
function onItem(item, phase) {
|
|
107843
|
+
switch (item.type) {
|
|
107844
|
+
case "agent_message":
|
|
107845
|
+
if (phase === "completed" && item.text.trim()) {
|
|
107846
|
+
finalOutput = item.text.trim();
|
|
107847
|
+
log.box(finalOutput, { title: "Pullfrog" });
|
|
107848
|
+
}
|
|
107849
|
+
return;
|
|
107850
|
+
case "reasoning":
|
|
107851
|
+
if (phase === "completed" && item.text.trim()) {
|
|
107852
|
+
log.box(item.text.trim(), { title: "Pullfrog" });
|
|
107853
|
+
}
|
|
107854
|
+
return;
|
|
107855
|
+
case "command_execution":
|
|
107856
|
+
if (phase === "started") {
|
|
107857
|
+
thinkingTimer.markToolCall();
|
|
107858
|
+
log.toolCall({ toolName: "shell", input: { command: item.command } });
|
|
107859
|
+
return;
|
|
107860
|
+
}
|
|
107861
|
+
thinkingTimer.markToolResult();
|
|
107862
|
+
log.startGroup("shell output");
|
|
107863
|
+
log.info(item.aggregated_output || "");
|
|
107864
|
+
log.endGroup();
|
|
107865
|
+
return;
|
|
107866
|
+
case "mcp_tool_call":
|
|
107867
|
+
if (phase === "started") {
|
|
107868
|
+
thinkingTimer.markToolCall();
|
|
107869
|
+
params.onToolUse?.({ toolName: item.tool, input: item.arguments });
|
|
107870
|
+
log.toolCall({ toolName: `${item.server}__${item.tool}`, input: item.arguments });
|
|
107871
|
+
if (item.tool.includes("report_progress")) params.todoTracker?.cancel();
|
|
107872
|
+
return;
|
|
107873
|
+
}
|
|
107874
|
+
thinkingTimer.markToolResult();
|
|
107875
|
+
if (item.error) log.info(`\xBB tool error: ${item.error.message}`);
|
|
107876
|
+
return;
|
|
107877
|
+
case "file_change":
|
|
107878
|
+
if (phase === "completed") {
|
|
107879
|
+
const paths = item.changes.map((c) => `${c.kind} ${c.path}`).join(", ");
|
|
107880
|
+
log.info(`\xBB apply_patch (${item.status}): ${paths}`);
|
|
107881
|
+
}
|
|
107882
|
+
return;
|
|
107883
|
+
case "web_search":
|
|
107884
|
+
if (phase === "started") log.toolCall({ toolName: "web_search", input: item.query });
|
|
107885
|
+
return;
|
|
107886
|
+
case "todo_list":
|
|
107887
|
+
if (params.todoTracker?.enabled) params.todoTracker.update(toTodoWriteInput(item));
|
|
107888
|
+
return;
|
|
107889
|
+
case "error":
|
|
107890
|
+
log.info(`\xBB ${item.message}`);
|
|
107891
|
+
return;
|
|
107892
|
+
default:
|
|
107893
|
+
item;
|
|
107894
|
+
}
|
|
107895
|
+
}
|
|
107896
|
+
function onEvent(event) {
|
|
107897
|
+
switch (event.type) {
|
|
107898
|
+
case "thread.started":
|
|
107899
|
+
threadId = event.thread_id;
|
|
107900
|
+
return;
|
|
107901
|
+
case "turn.completed":
|
|
107902
|
+
tokens.input += event.usage.input_tokens ?? 0;
|
|
107903
|
+
tokens.cacheRead += event.usage.cached_input_tokens ?? 0;
|
|
107904
|
+
tokens.output += event.usage.output_tokens ?? 0;
|
|
107905
|
+
return;
|
|
107906
|
+
case "turn.failed":
|
|
107907
|
+
turnError = event.error.message;
|
|
107908
|
+
log.info(`\xBB turn failed: ${turnError}`);
|
|
107909
|
+
return;
|
|
107910
|
+
case "error":
|
|
107911
|
+
turnError = event.message;
|
|
107912
|
+
log.info(`\xBB codex error: ${event.message}`);
|
|
107913
|
+
return;
|
|
107914
|
+
case "item.started":
|
|
107915
|
+
onItem(event.item, "started");
|
|
107916
|
+
return;
|
|
107917
|
+
case "item.completed":
|
|
107918
|
+
onItem(event.item, "completed");
|
|
107919
|
+
return;
|
|
107920
|
+
case "item.updated":
|
|
107921
|
+
if (event.item.type === "todo_list") onItem(event.item, "started");
|
|
107922
|
+
return;
|
|
107923
|
+
case "turn.started":
|
|
107924
|
+
return;
|
|
107925
|
+
default:
|
|
107926
|
+
event;
|
|
107927
|
+
}
|
|
107928
|
+
}
|
|
107929
|
+
try {
|
|
107930
|
+
const result = await spawn({
|
|
107931
|
+
cmd: params.cliPath,
|
|
107932
|
+
args: params.args,
|
|
107933
|
+
cwd: params.cwd,
|
|
107934
|
+
env: params.env,
|
|
107935
|
+
activityTimeout: AGENT_ACTIVITY_TIMEOUT_MS,
|
|
107936
|
+
onActivityTimeout: params.onActivityTimeout,
|
|
107937
|
+
// stdin MUST be `ignore`: codex reads a piped stdin as an extra `<stdin>`
|
|
107938
|
+
// prompt block and blocks on EOF, so an inherited pipe hangs the run
|
|
107939
|
+
// before the first model call.
|
|
107940
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
107941
|
+
killGroup: true,
|
|
107942
|
+
retain: "none",
|
|
107943
|
+
onStdout: (chunk) => {
|
|
107944
|
+
output.append(chunk);
|
|
107945
|
+
markActivity();
|
|
107946
|
+
stdoutBuffer += chunk;
|
|
107947
|
+
const lines = stdoutBuffer.split("\n");
|
|
107948
|
+
stdoutBuffer = lines.pop() || "";
|
|
107949
|
+
for (const line of lines) {
|
|
107950
|
+
const trimmed = line.trim();
|
|
107951
|
+
if (!trimmed) continue;
|
|
107952
|
+
let event;
|
|
107953
|
+
try {
|
|
107954
|
+
event = JSON.parse(trimmed);
|
|
107955
|
+
} catch {
|
|
107956
|
+
log.debug(`\xBB non-JSON stdout line: ${trimmed.substring(0, 200)}`);
|
|
107957
|
+
continue;
|
|
107958
|
+
}
|
|
107959
|
+
eventCount++;
|
|
107960
|
+
log.debug(JSON.stringify(event, null, 2));
|
|
107961
|
+
const idleMs = getIdleMs();
|
|
107962
|
+
if (idleMs > 1e4) {
|
|
107963
|
+
log.info(
|
|
107964
|
+
`\xBB no activity for ${(idleMs / 1e3).toFixed(1)}s (${eventCount} events processed so far)`
|
|
107965
|
+
);
|
|
107966
|
+
}
|
|
107967
|
+
markActivity();
|
|
107968
|
+
onEvent(event);
|
|
107969
|
+
}
|
|
107970
|
+
},
|
|
107971
|
+
onStderr: (chunk) => {
|
|
107972
|
+
const trimmed = chunk.trim();
|
|
107973
|
+
if (!trimmed) return;
|
|
107974
|
+
recentStderr.push(trimmed);
|
|
107975
|
+
if (recentStderr.length > MAX_STDERR_LINES) recentStderr.shift();
|
|
107976
|
+
const match3 = findProviderErrorMatch(trimmed);
|
|
107977
|
+
if (match3) {
|
|
107978
|
+
lastProviderError = match3.label;
|
|
107979
|
+
log.info(`\xBB provider error detected (${match3.label}): ${match3.excerpt}`);
|
|
107980
|
+
} else {
|
|
107981
|
+
log.debug(trimmed);
|
|
107982
|
+
}
|
|
107983
|
+
}
|
|
107984
|
+
});
|
|
107985
|
+
const duration4 = performance6.now() - startTime;
|
|
107986
|
+
log.info(`\xBB codex completed in ${Math.round(duration4)}ms with exit code ${result.exitCode}`);
|
|
107987
|
+
const usage = buildUsage2();
|
|
107988
|
+
if (usage) {
|
|
107989
|
+
logTokenTable({
|
|
107990
|
+
input: tokens.input - tokens.cacheRead,
|
|
107991
|
+
cacheRead: tokens.cacheRead,
|
|
107992
|
+
cacheWrite: 0,
|
|
107993
|
+
output: tokens.output
|
|
107994
|
+
});
|
|
107995
|
+
}
|
|
107996
|
+
if (result.exitCode !== 0 || turnError) {
|
|
107997
|
+
const stderrSnapshot = recentStderr.join("\n");
|
|
107998
|
+
const error52 = turnError || stderrSnapshot || lastProviderError && `provider error: ${lastProviderError}` || `unknown error - no output from Codex CLI (exit ${result.exitCode})`;
|
|
107999
|
+
log.error(`codex exited with code ${result.exitCode}: ${error52}`);
|
|
108000
|
+
return { success: false, output: finalOutput || output.toString(), error: error52, usage, threadId };
|
|
108001
|
+
}
|
|
108002
|
+
return { success: true, output: finalOutput || output.toString(), usage, threadId };
|
|
108003
|
+
} catch (error52) {
|
|
108004
|
+
const duration4 = performance6.now() - startTime;
|
|
108005
|
+
const message = error52 instanceof Error ? error52.message : String(error52);
|
|
108006
|
+
const isActivityTimeout = error52 instanceof SpawnTimeoutError && error52.code === SPAWN_ACTIVITY_TIMEOUT_CODE;
|
|
108007
|
+
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`;
|
|
108008
|
+
log.info(
|
|
108009
|
+
`\xBB codex ${isActivityTimeout ? "hung" : "failed"} after ${(duration4 / 1e3).toFixed(1)}s: ${message}`
|
|
108010
|
+
);
|
|
108011
|
+
log.info(`\xBB diagnosis: ${diagnosis}`);
|
|
108012
|
+
if (recentStderr.length > 0) log.info(`\xBB recent stderr:
|
|
108013
|
+
${recentStderr.join("\n")}`);
|
|
108014
|
+
return {
|
|
108015
|
+
success: false,
|
|
108016
|
+
output: finalOutput || output.toString(),
|
|
108017
|
+
error: `${message} [${diagnosis}]`,
|
|
108018
|
+
usage: buildUsage2(),
|
|
108019
|
+
threadId
|
|
108020
|
+
};
|
|
108021
|
+
}
|
|
108022
|
+
}
|
|
108023
|
+
var codex = agent({
|
|
108024
|
+
name: "codex",
|
|
108025
|
+
install: installCodexCli,
|
|
108026
|
+
run: async (ctx) => {
|
|
108027
|
+
const cliPath = await installCodexCli();
|
|
108028
|
+
warnIfNativeEditsUnavailable(ctx);
|
|
108029
|
+
const codexHomeAuth = installCodexHome();
|
|
108030
|
+
const codexHome = codexHomeAuth?.codexHome ?? join6(ctx.tmpdir, ".codex");
|
|
108031
|
+
mkdirSync6(codexHome, { recursive: true });
|
|
108032
|
+
const effort = resolveRunEffort(ctx);
|
|
108033
|
+
if (effort.rung && !CODEX_EFFORTS.includes(effort.rung)) {
|
|
108034
|
+
log.warning(`\xBB effort ${effort.rung} not sent \u2014 codex doesn't accept that level`);
|
|
108035
|
+
}
|
|
108036
|
+
writeCodexConfig({
|
|
108037
|
+
ctx,
|
|
108038
|
+
codexHome,
|
|
108039
|
+
model: resolveCodexModel(ctx),
|
|
108040
|
+
effortRung: effort.rung && CODEX_EFFORTS.includes(effort.rung) ? effort.rung : void 0
|
|
108041
|
+
});
|
|
108042
|
+
if (codexHomeAuth) {
|
|
108043
|
+
core2.saveState(
|
|
108044
|
+
"codex_writeback",
|
|
108045
|
+
JSON.stringify({
|
|
108046
|
+
apiToken: ctx.apiToken,
|
|
108047
|
+
authPath: codexHomeAuth.authPath,
|
|
108048
|
+
originalRefresh: codexHomeAuth.originalRefresh
|
|
108049
|
+
})
|
|
108050
|
+
);
|
|
108051
|
+
}
|
|
108052
|
+
const baseEnv = ctx.payload.shell === "enabled" ? process.env : filterEnv();
|
|
108053
|
+
const env2 = {
|
|
108054
|
+
...baseEnv,
|
|
108055
|
+
CODEX_HOME: codexHome,
|
|
108056
|
+
PWD: process.cwd()
|
|
108057
|
+
};
|
|
108058
|
+
delete env2.CODEX_API_KEY;
|
|
108059
|
+
if (codexHomeAuth) {
|
|
108060
|
+
delete env2.OPENAI_API_KEY;
|
|
108061
|
+
} else if (process.env.OPENAI_API_KEY) {
|
|
108062
|
+
env2.CODEX_API_KEY = process.env.OPENAI_API_KEY;
|
|
108063
|
+
}
|
|
108064
|
+
const securityFlags = securityOverrideFlags({
|
|
108065
|
+
ctx,
|
|
108066
|
+
sandboxMode: ctx.payload.push === "disabled" ? "read-only" : "workspace-write",
|
|
108067
|
+
repoDir: process.cwd(),
|
|
108068
|
+
mcpServerUrl: ctx.mcpServerUrl,
|
|
108069
|
+
model: resolveCodexModel(ctx),
|
|
108070
|
+
effortRung: effort.rung && CODEX_EFFORTS.includes(effort.rung) ? effort.rung : void 0
|
|
108071
|
+
});
|
|
108072
|
+
const runnerArgs = { cliPath, cwd: process.cwd(), env: env2, todoTracker: ctx.todoTracker };
|
|
108073
|
+
const initial = await runCodex({
|
|
108074
|
+
...runnerArgs,
|
|
108075
|
+
args: [...securityFlags, "exec", "--json", ctx.instructions.full],
|
|
108076
|
+
onActivityTimeout: ctx.onActivityTimeout,
|
|
108077
|
+
onToolUse: ctx.onToolUse
|
|
108078
|
+
});
|
|
108079
|
+
const result = await runPostRunRetryLoop({
|
|
108080
|
+
ctx,
|
|
108081
|
+
initialResult: initial,
|
|
108082
|
+
initialUsage: initial.usage,
|
|
108083
|
+
reflectionPrompt: buildReflectionPrompt(ctx.toolState),
|
|
108084
|
+
// a resume needs the thread codex recorded. without one there is nothing
|
|
108085
|
+
// to continue, and `resume --last` would be worse than not resuming: it
|
|
108086
|
+
// picks the newest recorded session in this cwd, which on a self-hosted
|
|
108087
|
+
// runner with a shared CODEX_HOME is somebody else's run.
|
|
108088
|
+
canResume: (r) => r.threadId !== void 0,
|
|
108089
|
+
resume: async (c) => runCodex({
|
|
108090
|
+
...runnerArgs,
|
|
108091
|
+
args: [
|
|
108092
|
+
...securityFlags,
|
|
108093
|
+
"exec",
|
|
108094
|
+
"resume",
|
|
108095
|
+
"--json",
|
|
108096
|
+
c.previousResult.threadId ?? "",
|
|
108097
|
+
c.prompt
|
|
108098
|
+
]
|
|
108099
|
+
})
|
|
108100
|
+
});
|
|
108101
|
+
if (result.success) {
|
|
108102
|
+
await ctx.todoTracker?.flush();
|
|
108103
|
+
} else {
|
|
108104
|
+
ctx.todoTracker?.cancel();
|
|
108105
|
+
}
|
|
108106
|
+
return result;
|
|
108107
|
+
}
|
|
108108
|
+
});
|
|
108109
|
+
|
|
108110
|
+
// agents/opencode.ts
|
|
108111
|
+
var core3 = __toESM(require_core(), 1);
|
|
108112
|
+
import { spawn as nodeSpawn2 } from "node:child_process";
|
|
108113
|
+
import { mkdirSync as mkdirSync7, writeFileSync as writeFileSync6 } from "node:fs";
|
|
108114
|
+
import { join as join7 } from "node:path";
|
|
108115
|
+
import { performance as performance7 } from "node:perf_hooks";
|
|
108116
|
+
|
|
107315
108117
|
// node_modules/.pnpm/@opencode-ai+sdk@1.18.5/node_modules/@opencode-ai/sdk/dist/v2/gen/core/serverSentEvents.gen.js
|
|
107316
108118
|
var createSseClient = ({ onRequest, onSseError, onSseEvent, responseTransformer, responseValidator, sseDefaultRetryDelay, sseMaxRetryAttempts, sseMaxRetryDelay, sseSleepFn, url: url4, ...options }) => {
|
|
107317
108119
|
let lastEventId;
|
|
@@ -112718,114 +113520,6 @@ var import_cross_spawn = __toESM(require_cross_spawn(), 1);
|
|
|
112718
113520
|
// agents/opencode.ts
|
|
112719
113521
|
var import_undici = __toESM(require_undici2(), 1);
|
|
112720
113522
|
|
|
112721
|
-
// utils/codexHome.ts
|
|
112722
|
-
import { execFileSync as execFileSync3 } from "node:child_process";
|
|
112723
|
-
import { mkdirSync as mkdirSync5, writeFileSync as writeFileSync4 } from "node:fs";
|
|
112724
|
-
import { homedir as homedir2, userInfo } from "node:os";
|
|
112725
|
-
import { join as join5 } from "node:path";
|
|
112726
|
-
|
|
112727
|
-
// utils/codexOAuth.ts
|
|
112728
|
-
function decodeJwtExpMs(token) {
|
|
112729
|
-
const parts = token.split(".");
|
|
112730
|
-
if (parts.length !== 3) return null;
|
|
112731
|
-
let payload;
|
|
112732
|
-
try {
|
|
112733
|
-
payload = JSON.parse(Buffer.from(parts[1], "base64url").toString("utf8"));
|
|
112734
|
-
} catch {
|
|
112735
|
-
return null;
|
|
112736
|
-
}
|
|
112737
|
-
if (typeof payload.exp !== "number" || !Number.isFinite(payload.exp)) return null;
|
|
112738
|
-
return payload.exp * 1e3;
|
|
112739
|
-
}
|
|
112740
|
-
function parseCodexAuthBody(raw2) {
|
|
112741
|
-
let parsed2;
|
|
112742
|
-
try {
|
|
112743
|
-
parsed2 = JSON.parse(raw2);
|
|
112744
|
-
} catch {
|
|
112745
|
-
return null;
|
|
112746
|
-
}
|
|
112747
|
-
if (!parsed2 || typeof parsed2 !== "object") return null;
|
|
112748
|
-
const v = parsed2;
|
|
112749
|
-
if (v.auth_mode !== "chatgpt") return null;
|
|
112750
|
-
const tokens = v.tokens;
|
|
112751
|
-
if (!tokens || typeof tokens !== "object") return null;
|
|
112752
|
-
const t = tokens;
|
|
112753
|
-
if (typeof t.access_token !== "string" || t.access_token.length === 0) return null;
|
|
112754
|
-
if (typeof t.refresh_token !== "string" || t.refresh_token.length === 0) return null;
|
|
112755
|
-
return {
|
|
112756
|
-
auth_mode: "chatgpt",
|
|
112757
|
-
...typeof v.refresh_rejected_at === "string" ? { refresh_rejected_at: v.refresh_rejected_at } : {},
|
|
112758
|
-
tokens: {
|
|
112759
|
-
access_token: t.access_token,
|
|
112760
|
-
refresh_token: t.refresh_token,
|
|
112761
|
-
...typeof t.id_token === "string" ? { id_token: t.id_token } : {},
|
|
112762
|
-
...typeof t.account_id === "string" ? { account_id: t.account_id } : {}
|
|
112763
|
-
},
|
|
112764
|
-
...typeof v.last_refresh === "string" ? { last_refresh: v.last_refresh } : {}
|
|
112765
|
-
};
|
|
112766
|
-
}
|
|
112767
|
-
|
|
112768
|
-
// utils/codexHome.ts
|
|
112769
|
-
var CODEX_AUTH_ENV = "CODEX_AUTH_JSON";
|
|
112770
|
-
var PULLFROG_DATA_DIR = "/var/lib/pullfrog";
|
|
112771
|
-
function installCodexAuth() {
|
|
112772
|
-
const raw2 = process.env[CODEX_AUTH_ENV];
|
|
112773
|
-
if (!raw2) return null;
|
|
112774
|
-
const body = parseCodexAuthBody(raw2);
|
|
112775
|
-
if (!body) {
|
|
112776
|
-
log.warning(`\xBB ${CODEX_AUTH_ENV} present but malformed; ignoring`);
|
|
112777
|
-
return null;
|
|
112778
|
-
}
|
|
112779
|
-
const expiresMs = decodeJwtExpMs(body.tokens.access_token) ?? 0;
|
|
112780
|
-
const xdgDataHome = resolveDataHome();
|
|
112781
|
-
const opencodeDir = join5(xdgDataHome, "opencode");
|
|
112782
|
-
const authPath = join5(opencodeDir, "auth.json");
|
|
112783
|
-
const opencodeAuth = {
|
|
112784
|
-
openai: {
|
|
112785
|
-
type: "oauth",
|
|
112786
|
-
refresh: body.tokens.refresh_token,
|
|
112787
|
-
access: body.tokens.access_token,
|
|
112788
|
-
expires: expiresMs,
|
|
112789
|
-
...body.tokens.account_id ? { accountId: body.tokens.account_id } : {}
|
|
112790
|
-
}
|
|
112791
|
-
};
|
|
112792
|
-
mkdirSync5(opencodeDir, { recursive: true });
|
|
112793
|
-
writeFileSync4(authPath, `${JSON.stringify(opencodeAuth, null, 2)}
|
|
112794
|
-
`, { mode: 384 });
|
|
112795
|
-
process.env.XDG_DATA_HOME = xdgDataHome;
|
|
112796
|
-
log.info(`\xBB installed Codex auth at ${authPath}`);
|
|
112797
|
-
return {
|
|
112798
|
-
authPath,
|
|
112799
|
-
xdgDataHome,
|
|
112800
|
-
originalRefresh: body.tokens.refresh_token
|
|
112801
|
-
};
|
|
112802
|
-
}
|
|
112803
|
-
function resolveDataHome() {
|
|
112804
|
-
if (process.env.CI !== "true") return join5(homedir2(), ".local", "share");
|
|
112805
|
-
bootstrapPullfrogDataDir();
|
|
112806
|
-
return PULLFROG_DATA_DIR;
|
|
112807
|
-
}
|
|
112808
|
-
function bootstrapPullfrogDataDir() {
|
|
112809
|
-
const user = userInfo().username;
|
|
112810
|
-
let primaryGroup;
|
|
112811
|
-
try {
|
|
112812
|
-
primaryGroup = execFileSync3("id", ["-gn", user], { stdio: "pipe", encoding: "utf-8" }).trim();
|
|
112813
|
-
} catch {
|
|
112814
|
-
primaryGroup = user;
|
|
112815
|
-
}
|
|
112816
|
-
try {
|
|
112817
|
-
execFileSync3("sudo", ["-n", "mkdir", "-p", PULLFROG_DATA_DIR], { stdio: "pipe" });
|
|
112818
|
-
execFileSync3("sudo", ["-n", "chown", `${user}:${primaryGroup}`, PULLFROG_DATA_DIR], {
|
|
112819
|
-
stdio: "pipe"
|
|
112820
|
-
});
|
|
112821
|
-
execFileSync3("sudo", ["-n", "chmod", "700", PULLFROG_DATA_DIR], { stdio: "pipe" });
|
|
112822
|
-
} catch (err) {
|
|
112823
|
-
throw new Error(
|
|
112824
|
-
`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.`
|
|
112825
|
-
);
|
|
112826
|
-
}
|
|
112827
|
-
}
|
|
112828
|
-
|
|
112829
113523
|
// utils/worktree.ts
|
|
112830
113524
|
async function dirtyTrackedPaths(params = {}) {
|
|
112831
113525
|
const result = await spawn({
|
|
@@ -113093,6 +113787,30 @@ function openAICompatibleProvider(model) {
|
|
|
113093
113787
|
}
|
|
113094
113788
|
};
|
|
113095
113789
|
}
|
|
113790
|
+
function azureProvider(model) {
|
|
113791
|
+
const prefix = `${AZURE_PROVIDER}/`;
|
|
113792
|
+
if (!model?.startsWith(prefix)) return {};
|
|
113793
|
+
const deployment = model.slice(prefix.length);
|
|
113794
|
+
return {
|
|
113795
|
+
[AZURE_PROVIDER]: {
|
|
113796
|
+
// always sent: opencode reads it as `Boolean(options?.useCompletionUrls)`,
|
|
113797
|
+
// so `false` and absent are the same, and the deep merge keeps the native
|
|
113798
|
+
// loader's `resourceName` either way.
|
|
113799
|
+
options: {
|
|
113800
|
+
useCompletionUrls: process.env[AZURE_USE_CHAT_COMPLETIONS_ENV]?.trim() === "true"
|
|
113801
|
+
},
|
|
113802
|
+
models: {
|
|
113803
|
+
[deployment]: {
|
|
113804
|
+
name: deployment,
|
|
113805
|
+
limit: {
|
|
113806
|
+
context: Number(process.env[AZURE_CONTEXT_ENV]),
|
|
113807
|
+
output: Number(process.env[AZURE_MAX_OUTPUT_ENV])
|
|
113808
|
+
}
|
|
113809
|
+
}
|
|
113810
|
+
}
|
|
113811
|
+
}
|
|
113812
|
+
};
|
|
113813
|
+
}
|
|
113096
113814
|
var KIMI_ENFORCERLESS_PROVIDERS = ["siliconflow", "together"];
|
|
113097
113815
|
function kimiOpenRouterProviderOverrides() {
|
|
113098
113816
|
return Object.fromEntries(
|
|
@@ -113184,7 +113902,8 @@ function buildSecurityConfig(ctx, model) {
|
|
|
113184
113902
|
// see opencodeShared.ts.
|
|
113185
113903
|
provider: {
|
|
113186
113904
|
openrouter: { models: kimiOpenRouterProviderOverrides() },
|
|
113187
|
-
...openAICompatibleProvider(model)
|
|
113905
|
+
...openAICompatibleProvider(model),
|
|
113906
|
+
...azureProvider(model)
|
|
113188
113907
|
}
|
|
113189
113908
|
};
|
|
113190
113909
|
if (model) {
|
|
@@ -113352,7 +114071,7 @@ function isModelOutput(ctx, part) {
|
|
|
113352
114071
|
}
|
|
113353
114072
|
async function dispatchEvent(ctx, event) {
|
|
113354
114073
|
if (event.type === "message.part.updated") {
|
|
113355
|
-
ctx.lastEventAt =
|
|
114074
|
+
ctx.lastEventAt = performance7.now();
|
|
113356
114075
|
if (isModelOutput(ctx, event.properties.part)) ctx.sawModelOutput = true;
|
|
113357
114076
|
await onPartUpdated(ctx, event.properties.part);
|
|
113358
114077
|
return;
|
|
@@ -113419,7 +114138,7 @@ async function onToolPart(ctx, part, label, isOrchestrator) {
|
|
|
113419
114138
|
if (toolName === "task" && status === "running" && isOrchestrator && !ctx.taskDispatchByCallID.has(toolId)) {
|
|
113420
114139
|
const input = part.state.input ?? {};
|
|
113421
114140
|
const dispatched = ctx.labeler.recordTaskDispatch(input);
|
|
113422
|
-
ctx.taskDispatchByCallID.set(toolId, { label: dispatched, startedAt:
|
|
114141
|
+
ctx.taskDispatchByCallID.set(toolId, { label: dispatched, startedAt: performance7.now() });
|
|
113423
114142
|
log.info(
|
|
113424
114143
|
`\xBB dispatching subagent: ${dispatched}` + (input.subagent_type ? ` (subagent_type=${input.subagent_type})` : "")
|
|
113425
114144
|
);
|
|
@@ -113449,7 +114168,7 @@ function processTerminalToolPart(ctx, part, label, isOrchestrator) {
|
|
|
113449
114168
|
if (toolName === "task") {
|
|
113450
114169
|
const dispatch = ctx.taskDispatchByCallID.get(toolId);
|
|
113451
114170
|
if (dispatch) {
|
|
113452
|
-
const dur = ((
|
|
114171
|
+
const dur = ((performance7.now() - dispatch.startedAt) / 1e3).toFixed(1);
|
|
113453
114172
|
const outputStr = state.status === "completed" ? state.output : "";
|
|
113454
114173
|
const preview = typeof outputStr === "string" && outputStr.length > 120 ? `${outputStr.slice(0, 120)}\u2026` : outputStr;
|
|
113455
114174
|
log.info(
|
|
@@ -113492,7 +114211,7 @@ function withLabel(label, message) {
|
|
|
113492
114211
|
return label === ORCHESTRATOR_LABEL ? message : formatWithLabel(label, message);
|
|
113493
114212
|
}
|
|
113494
114213
|
async function runPromptTurn(ctx, params) {
|
|
113495
|
-
const start =
|
|
114214
|
+
const start = performance7.now();
|
|
113496
114215
|
const turnStartMs = Date.now();
|
|
113497
114216
|
ctx.currentTurn = newTurn();
|
|
113498
114217
|
const turn = ctx.currentTurn;
|
|
@@ -113526,7 +114245,7 @@ async function runPromptTurn(ctx, params) {
|
|
|
113526
114245
|
} catch (err) {
|
|
113527
114246
|
networkError = err instanceof Error ? err.message : String(err);
|
|
113528
114247
|
}
|
|
113529
|
-
const durationMs =
|
|
114248
|
+
const durationMs = performance7.now() - start;
|
|
113530
114249
|
const aggregatedUsage = await aggregateTurnUsage(ctx, turnStartMs);
|
|
113531
114250
|
const usage = aggregatedUsage ?? buildUsage(turn, assistant);
|
|
113532
114251
|
const finalText = turn.finalText || extractTextFromParts(returnedParts) || "";
|
|
@@ -113680,7 +114399,7 @@ function startInnerActivityWatchdog(params) {
|
|
|
113680
114399
|
let fired = false;
|
|
113681
114400
|
const id = setInterval(() => {
|
|
113682
114401
|
if (fired) return;
|
|
113683
|
-
const idleMs =
|
|
114402
|
+
const idleMs = performance7.now() - params.ctx.lastEventAt;
|
|
113684
114403
|
const budgetMs = params.ctx.sawModelOutput ? params.timeoutMs : AGENT_FIRST_EVENT_TIMEOUT_MS;
|
|
113685
114404
|
if (idleMs <= budgetMs) return;
|
|
113686
114405
|
fired = true;
|
|
@@ -113719,12 +114438,12 @@ var opencode = agent({
|
|
|
113719
114438
|
const model = vertexModel ?? (isBedrockRoute ? `amazon-bedrock/${rawModel}` : rawModel);
|
|
113720
114439
|
const homeEnv = {
|
|
113721
114440
|
HOME: ctx.tmpdir,
|
|
113722
|
-
XDG_CONFIG_HOME:
|
|
114441
|
+
XDG_CONFIG_HOME: join7(ctx.tmpdir, ".config")
|
|
113723
114442
|
};
|
|
113724
|
-
const opencodePluginDir =
|
|
113725
|
-
|
|
113726
|
-
|
|
113727
|
-
|
|
114443
|
+
const opencodePluginDir = join7(homeEnv.XDG_CONFIG_HOME, "opencode", "plugin");
|
|
114444
|
+
mkdirSync7(opencodePluginDir, { recursive: true });
|
|
114445
|
+
writeFileSync6(
|
|
114446
|
+
join7(opencodePluginDir, PULLFROG_OPENCODE_GATE_PLUGIN_FILENAME),
|
|
113728
114447
|
buildOpencodeSubagentGateSource(ctx.subagentDeniedTools)
|
|
113729
114448
|
);
|
|
113730
114449
|
const agentBrowserVersion = getDevDependencyVersion("agent-browser");
|
|
@@ -113755,12 +114474,13 @@ var opencode = agent({
|
|
|
113755
114474
|
if (codexAuth) {
|
|
113756
114475
|
env2.XDG_DATA_HOME = codexAuth.xdgDataHome;
|
|
113757
114476
|
delete env2.OPENAI_API_KEY;
|
|
113758
|
-
|
|
114477
|
+
core3.saveState(
|
|
113759
114478
|
"codex_writeback",
|
|
113760
114479
|
JSON.stringify({
|
|
113761
114480
|
apiToken: ctx.apiToken,
|
|
113762
114481
|
authPath: codexAuth.authPath,
|
|
113763
|
-
originalRefresh: codexAuth.originalRefresh
|
|
114482
|
+
originalRefresh: codexAuth.originalRefresh,
|
|
114483
|
+
originalIdToken: codexAuth.originalIdToken
|
|
113764
114484
|
})
|
|
113765
114485
|
);
|
|
113766
114486
|
}
|
|
@@ -113814,7 +114534,7 @@ var opencode = agent({
|
|
|
113814
114534
|
onToolUse: ctx.onToolUse,
|
|
113815
114535
|
currentTurn: null,
|
|
113816
114536
|
eventCount: 0,
|
|
113817
|
-
lastEventAt:
|
|
114537
|
+
lastEventAt: performance7.now(),
|
|
113818
114538
|
sawModelOutput: false,
|
|
113819
114539
|
promptMessageID: void 0,
|
|
113820
114540
|
taskDispatchByCallID: /* @__PURE__ */ new Map(),
|
|
@@ -113924,7 +114644,7 @@ async function runTurnGuarded(ctx, fn2) {
|
|
|
113924
114644
|
}
|
|
113925
114645
|
|
|
113926
114646
|
// agents/index.ts
|
|
113927
|
-
var agents = { claude, opencode };
|
|
114647
|
+
var agents = { claude, codex, opencode };
|
|
113928
114648
|
|
|
113929
114649
|
// node_modules/.pnpm/@ark+util@0.56.0/node_modules/@ark/util/out/arrays.js
|
|
113930
114650
|
var liftArray = (data) => Array.isArray(data) ? data : [data];
|
|
@@ -134256,7 +134976,7 @@ var require_core$2 = /* @__PURE__ */ __commonJSMin(((exports) => {
|
|
|
134256
134976
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
134257
134977
|
const id_1 = require_id2();
|
|
134258
134978
|
const ref_1 = require_ref2();
|
|
134259
|
-
const
|
|
134979
|
+
const core11 = [
|
|
134260
134980
|
"$schema",
|
|
134261
134981
|
"$id",
|
|
134262
134982
|
"$defs",
|
|
@@ -134266,7 +134986,7 @@ var require_core$2 = /* @__PURE__ */ __commonJSMin(((exports) => {
|
|
|
134266
134986
|
id_1.default,
|
|
134267
134987
|
ref_1.default
|
|
134268
134988
|
];
|
|
134269
|
-
exports.default =
|
|
134989
|
+
exports.default = core11;
|
|
134270
134990
|
}));
|
|
134271
134991
|
var require_limitNumber2 = /* @__PURE__ */ __commonJSMin(((exports) => {
|
|
134272
134992
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
@@ -136404,7 +137124,7 @@ var require_json_schema_2019_09 = /* @__PURE__ */ __commonJSMin(((exports) => {
|
|
|
136404
137124
|
const metaSchema = require_schema$1();
|
|
136405
137125
|
const applicator = require_applicator$1();
|
|
136406
137126
|
const content = require_content$1();
|
|
136407
|
-
const
|
|
137127
|
+
const core11 = require_core$1();
|
|
136408
137128
|
const format2 = require_format3();
|
|
136409
137129
|
const metadata = require_meta_data$1();
|
|
136410
137130
|
const validation = require_validation$1();
|
|
@@ -136414,7 +137134,7 @@ var require_json_schema_2019_09 = /* @__PURE__ */ __commonJSMin(((exports) => {
|
|
|
136414
137134
|
metaSchema,
|
|
136415
137135
|
applicator,
|
|
136416
137136
|
content,
|
|
136417
|
-
|
|
137137
|
+
core11,
|
|
136418
137138
|
with$data(this, format2),
|
|
136419
137139
|
metadata,
|
|
136420
137140
|
with$data(this, validation)
|
|
@@ -136854,7 +137574,7 @@ var require_json_schema_2020_12 = /* @__PURE__ */ __commonJSMin(((exports) => {
|
|
|
136854
137574
|
const applicator = require_applicator2();
|
|
136855
137575
|
const unevaluated = require_unevaluated();
|
|
136856
137576
|
const content = require_content();
|
|
136857
|
-
const
|
|
137577
|
+
const core11 = require_core4();
|
|
136858
137578
|
const format2 = require_format_annotation();
|
|
136859
137579
|
const metadata = require_meta_data();
|
|
136860
137580
|
const validation = require_validation2();
|
|
@@ -136865,7 +137585,7 @@ var require_json_schema_2020_12 = /* @__PURE__ */ __commonJSMin(((exports) => {
|
|
|
136865
137585
|
applicator,
|
|
136866
137586
|
unevaluated,
|
|
136867
137587
|
content,
|
|
136868
|
-
|
|
137588
|
+
core11,
|
|
136869
137589
|
with$data(this, format2),
|
|
136870
137590
|
metadata,
|
|
136871
137591
|
with$data(this, validation)
|
|
@@ -148307,7 +149027,7 @@ var require_core$22 = /* @__PURE__ */ __commonJSMin2(((exports) => {
|
|
|
148307
149027
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
148308
149028
|
const id_1 = require_id3();
|
|
148309
149029
|
const ref_1 = require_ref3();
|
|
148310
|
-
const
|
|
149030
|
+
const core11 = [
|
|
148311
149031
|
"$schema",
|
|
148312
149032
|
"$id",
|
|
148313
149033
|
"$defs",
|
|
@@ -148317,7 +149037,7 @@ var require_core$22 = /* @__PURE__ */ __commonJSMin2(((exports) => {
|
|
|
148317
149037
|
id_1.default,
|
|
148318
149038
|
ref_1.default
|
|
148319
149039
|
];
|
|
148320
|
-
exports.default =
|
|
149040
|
+
exports.default = core11;
|
|
148321
149041
|
}));
|
|
148322
149042
|
var require_limitNumber3 = /* @__PURE__ */ __commonJSMin2(((exports) => {
|
|
148323
149043
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
@@ -150455,7 +151175,7 @@ var require_json_schema_2019_092 = /* @__PURE__ */ __commonJSMin2(((exports) =>
|
|
|
150455
151175
|
const metaSchema = require_schema$12();
|
|
150456
151176
|
const applicator = require_applicator$12();
|
|
150457
151177
|
const content = require_content$12();
|
|
150458
|
-
const
|
|
151178
|
+
const core11 = require_core$12();
|
|
150459
151179
|
const format2 = require_format4();
|
|
150460
151180
|
const metadata = require_meta_data$12();
|
|
150461
151181
|
const validation = require_validation$12();
|
|
@@ -150465,7 +151185,7 @@ var require_json_schema_2019_092 = /* @__PURE__ */ __commonJSMin2(((exports) =>
|
|
|
150465
151185
|
metaSchema,
|
|
150466
151186
|
applicator,
|
|
150467
151187
|
content,
|
|
150468
|
-
|
|
151188
|
+
core11,
|
|
150469
151189
|
with$data(this, format2),
|
|
150470
151190
|
metadata,
|
|
150471
151191
|
with$data(this, validation)
|
|
@@ -150905,7 +151625,7 @@ var require_json_schema_2020_122 = /* @__PURE__ */ __commonJSMin2(((exports) =>
|
|
|
150905
151625
|
const applicator = require_applicator3();
|
|
150906
151626
|
const unevaluated = require_unevaluated2();
|
|
150907
151627
|
const content = require_content2();
|
|
150908
|
-
const
|
|
151628
|
+
const core11 = require_core5();
|
|
150909
151629
|
const format2 = require_format_annotation2();
|
|
150910
151630
|
const metadata = require_meta_data2();
|
|
150911
151631
|
const validation = require_validation3();
|
|
@@ -150916,7 +151636,7 @@ var require_json_schema_2020_122 = /* @__PURE__ */ __commonJSMin2(((exports) =>
|
|
|
150916
151636
|
applicator,
|
|
150917
151637
|
unevaluated,
|
|
150918
151638
|
content,
|
|
150919
|
-
|
|
151639
|
+
core11,
|
|
150920
151640
|
with$data(this, format2),
|
|
150921
151641
|
metadata,
|
|
150922
151642
|
with$data(this, validation)
|
|
@@ -170877,123 +171597,8 @@ var pbkdf2Async = promisify(pbkdf2);
|
|
|
170877
171597
|
|
|
170878
171598
|
// utils/browser.ts
|
|
170879
171599
|
import { execFileSync as execFileSync4, spawnSync as spawnSync4 } from "node:child_process";
|
|
170880
|
-
import { existsSync as
|
|
171600
|
+
import { existsSync as existsSync5 } from "node:fs";
|
|
170881
171601
|
import { dirname as dirname2 } from "node:path";
|
|
170882
|
-
|
|
170883
|
-
// utils/secrets.ts
|
|
170884
|
-
var SENSITIVE_PATTERNS = [
|
|
170885
|
-
/_KEY$/i,
|
|
170886
|
-
/_SECRET$/i,
|
|
170887
|
-
/_TOKEN$/i,
|
|
170888
|
-
/_PASSWORD$/i,
|
|
170889
|
-
/_CREDENTIAL$/i
|
|
170890
|
-
];
|
|
170891
|
-
function isSensitiveEnvName(key) {
|
|
170892
|
-
return SENSITIVE_PATTERNS.some((p) => p.test(key));
|
|
170893
|
-
}
|
|
170894
|
-
var SAFE_ENV_PREFIXES = ["GITHUB_", "RUNNER_", "JAVA_HOME_", "GOROOT_"];
|
|
170895
|
-
var SAFE_ENV_NAMES = /* @__PURE__ */ new Set([
|
|
170896
|
-
// system
|
|
170897
|
-
"CI",
|
|
170898
|
-
"HOME",
|
|
170899
|
-
"LANG",
|
|
170900
|
-
"LOGNAME",
|
|
170901
|
-
"PATH",
|
|
170902
|
-
"SHELL",
|
|
170903
|
-
"SHLVL",
|
|
170904
|
-
"TERM",
|
|
170905
|
-
"TMPDIR",
|
|
170906
|
-
"TZ",
|
|
170907
|
-
"USER",
|
|
170908
|
-
"XDG_CONFIG_HOME",
|
|
170909
|
-
"XDG_RUNTIME_DIR",
|
|
170910
|
-
"DEBIAN_FRONTEND",
|
|
170911
|
-
// runner image toolchain
|
|
170912
|
-
"ACCEPT_EULA",
|
|
170913
|
-
"AGENT_TOOLSDIRECTORY",
|
|
170914
|
-
"ANDROID_HOME",
|
|
170915
|
-
"ANDROID_NDK",
|
|
170916
|
-
"ANDROID_NDK_HOME",
|
|
170917
|
-
"ANDROID_NDK_LATEST_HOME",
|
|
170918
|
-
"ANDROID_NDK_ROOT",
|
|
170919
|
-
"ANDROID_SDK_ROOT",
|
|
170920
|
-
"ANT_HOME",
|
|
170921
|
-
"AZURE_EXTENSION_DIR",
|
|
170922
|
-
"BOOTSTRAP_HASKELL_NONINTERACTIVE",
|
|
170923
|
-
"CHROME_BIN",
|
|
170924
|
-
"CHROMEWEBDRIVER",
|
|
170925
|
-
"CONDA",
|
|
170926
|
-
"DOTNET_MULTILEVEL_LOOKUP",
|
|
170927
|
-
"DOTNET_NOLOGO",
|
|
170928
|
-
"DOTNET_SKIP_FIRST_TIME_EXPERIENCE",
|
|
170929
|
-
"EDGEWEBDRIVER",
|
|
170930
|
-
"GECKOWEBDRIVER",
|
|
170931
|
-
"GHCUP_INSTALL_BASE_PREFIX",
|
|
170932
|
-
"GRADLE_HOME",
|
|
170933
|
-
"JAVA_HOME",
|
|
170934
|
-
"HOMEBREW_CLEANUP_PERIODIC_FULL_DAYS",
|
|
170935
|
-
"HOMEBREW_NO_AUTO_UPDATE",
|
|
170936
|
-
"ImageOS",
|
|
170937
|
-
"ImageVersion",
|
|
170938
|
-
"NVM_DIR",
|
|
170939
|
-
"PIPX_BIN_DIR",
|
|
170940
|
-
"PIPX_HOME",
|
|
170941
|
-
"PSModulePath",
|
|
170942
|
-
"SELENIUM_JAR_PATH",
|
|
170943
|
-
"SGX_AESM_ADDR",
|
|
170944
|
-
"SWIFT_PATH",
|
|
170945
|
-
"VCPKG_INSTALLATION_ROOT"
|
|
170946
|
-
]);
|
|
170947
|
-
var _userAllowlist = null;
|
|
170948
|
-
function setEnvAllowlist(raw2) {
|
|
170949
|
-
const names = raw2.split("\n").map((line) => line.trim()).filter(Boolean);
|
|
170950
|
-
_userAllowlist = new Set(names);
|
|
170951
|
-
}
|
|
170952
|
-
function isSafeEnvVar(key) {
|
|
170953
|
-
if (SAFE_ENV_NAMES.has(key)) return true;
|
|
170954
|
-
return SAFE_ENV_PREFIXES.some((p) => key.startsWith(p));
|
|
170955
|
-
}
|
|
170956
|
-
var WORKFLOW_COMMAND_ENV_NAMES = [
|
|
170957
|
-
"GITHUB_ENV",
|
|
170958
|
-
"GITHUB_PATH",
|
|
170959
|
-
"GITHUB_OUTPUT",
|
|
170960
|
-
"GITHUB_STATE",
|
|
170961
|
-
"GITHUB_STEP_SUMMARY",
|
|
170962
|
-
// dropping the five paths above but keeping RUNNER_TEMP would be theatre:
|
|
170963
|
-
// the file-command files live in `$RUNNER_TEMP/_runner_file_commands/`, so the
|
|
170964
|
-
// directory is one `ls` away. nothing in the install path reads it (only our
|
|
170965
|
-
// own leak-surface wipe in `setup.ts`, which runs in the parent), and package
|
|
170966
|
-
// managers use TMPDIR.
|
|
170967
|
-
"RUNNER_TEMP"
|
|
170968
|
-
];
|
|
170969
|
-
function filterEnvForUntrustedCode() {
|
|
170970
|
-
const env2 = filterEnv();
|
|
170971
|
-
for (const name of WORKFLOW_COMMAND_ENV_NAMES) delete env2[name];
|
|
170972
|
-
return env2;
|
|
170973
|
-
}
|
|
170974
|
-
function filterEnv() {
|
|
170975
|
-
const filtered = {};
|
|
170976
|
-
for (const [key, value2] of Object.entries(process.env)) {
|
|
170977
|
-
if (value2 === void 0) continue;
|
|
170978
|
-
const userAllowed = _userAllowlist?.has(key) ?? false;
|
|
170979
|
-
if (isSensitiveEnvName(key) && !userAllowed) continue;
|
|
170980
|
-
if (isSafeEnvVar(key) || userAllowed) {
|
|
170981
|
-
filtered[key] = value2;
|
|
170982
|
-
}
|
|
170983
|
-
}
|
|
170984
|
-
return filtered;
|
|
170985
|
-
}
|
|
170986
|
-
function resolveEnv(mode) {
|
|
170987
|
-
if (mode === "inherit") {
|
|
170988
|
-
return process.env;
|
|
170989
|
-
}
|
|
170990
|
-
if (mode === "restricted" || mode === void 0) {
|
|
170991
|
-
return filterEnv();
|
|
170992
|
-
}
|
|
170993
|
-
return { ...filterEnv(), ...mode };
|
|
170994
|
-
}
|
|
170995
|
-
|
|
170996
|
-
// utils/browser.ts
|
|
170997
171602
|
var CHROME_PATHS = ["/usr/bin/google-chrome-stable"];
|
|
170998
171603
|
var systemChromePath;
|
|
170999
171604
|
function findSystemChromePath() {
|
|
@@ -171001,7 +171606,7 @@ function findSystemChromePath() {
|
|
|
171001
171606
|
return systemChromePath || void 0;
|
|
171002
171607
|
}
|
|
171003
171608
|
for (const p of CHROME_PATHS) {
|
|
171004
|
-
if (
|
|
171609
|
+
if (existsSync5(p)) {
|
|
171005
171610
|
systemChromePath = p;
|
|
171006
171611
|
log.info(`found system chrome: ${p}`);
|
|
171007
171612
|
return p;
|
|
@@ -171085,8 +171690,8 @@ function closeBrowserDaemon(toolState) {
|
|
|
171085
171690
|
|
|
171086
171691
|
// mcp/checkout.ts
|
|
171087
171692
|
import { createHash as createHash2 } from "node:crypto";
|
|
171088
|
-
import { statSync, unlinkSync as unlinkSync3, writeFileSync as
|
|
171089
|
-
import { join as
|
|
171693
|
+
import { statSync, unlinkSync as unlinkSync3, writeFileSync as writeFileSync9 } from "node:fs";
|
|
171694
|
+
import { join as join14 } from "node:path";
|
|
171090
171695
|
|
|
171091
171696
|
// node_modules/.pnpm/@ark+schema@0.56.0/node_modules/@ark/schema/out/shared/errors.js
|
|
171092
171697
|
var ArkError = class _ArkError extends CastableBase {
|
|
@@ -178558,8 +179163,8 @@ function ensureRepoState(toolState, init) {
|
|
|
178558
179163
|
|
|
178559
179164
|
// utils/changeImpact.ts
|
|
178560
179165
|
import { spawn as spawn2 } from "node:child_process";
|
|
178561
|
-
import { writeFileSync as
|
|
178562
|
-
import { join as
|
|
179166
|
+
import { writeFileSync as writeFileSync7 } from "node:fs";
|
|
179167
|
+
import { join as join8 } from "node:path";
|
|
178563
179168
|
var MAX_CANDIDATES = 24;
|
|
178564
179169
|
var MAX_ATOM_LENGTH = 128;
|
|
178565
179170
|
var MAX_ATOMS = 12;
|
|
@@ -178906,8 +179511,8 @@ async function createChangeImpactArtifact(ctx, params) {
|
|
|
178906
179511
|
headSha: params.headSha,
|
|
178907
179512
|
lookupError: lookup2.error
|
|
178908
179513
|
});
|
|
178909
|
-
const path4 =
|
|
178910
|
-
|
|
179514
|
+
const path4 = join8(ctx.tmpdir, `pr-${params.pullNumber}-${params.headSha.slice(0, 7)}-impact.md`);
|
|
179515
|
+
writeFileSync7(path4, artifact.content);
|
|
178911
179516
|
return {
|
|
178912
179517
|
path: path4,
|
|
178913
179518
|
candidateCount: candidates.selected.length,
|
|
@@ -179159,8 +179764,8 @@ function readNumber(params) {
|
|
|
179159
179764
|
// utils/gitAuth.ts
|
|
179160
179765
|
import { execSync } from "node:child_process";
|
|
179161
179766
|
import { createHash } from "node:crypto";
|
|
179162
|
-
import { readFileSync as
|
|
179163
|
-
import { join as
|
|
179767
|
+
import { readFileSync as readFileSync3, realpathSync, unlinkSync } from "node:fs";
|
|
179768
|
+
import { join as join9 } from "node:path";
|
|
179164
179769
|
|
|
179165
179770
|
// utils/shell.ts
|
|
179166
179771
|
import { spawnSync as spawnSync5 } from "node:child_process";
|
|
@@ -179221,7 +179826,7 @@ var TRANSIENT_AUTH_PATTERNS = [
|
|
|
179221
179826
|
];
|
|
179222
179827
|
var gitBinary;
|
|
179223
179828
|
function hashFile(path4) {
|
|
179224
|
-
return createHash("sha256").update(
|
|
179829
|
+
return createHash("sha256").update(readFileSync3(path4)).digest("hex");
|
|
179225
179830
|
}
|
|
179226
179831
|
function resolveGit() {
|
|
179227
179832
|
const whichPath = execSync("which git", { encoding: "utf-8" }).trim();
|
|
@@ -179250,7 +179855,7 @@ function resolveHooksDir(cwd, gitPath) {
|
|
|
179250
179855
|
cwd,
|
|
179251
179856
|
log: false
|
|
179252
179857
|
}).trim();
|
|
179253
|
-
const hooksDir =
|
|
179858
|
+
const hooksDir = join9(commonDir, "hooks");
|
|
179254
179859
|
hooksDirCache.set(cwd, hooksDir);
|
|
179255
179860
|
return hooksDir;
|
|
179256
179861
|
}
|
|
@@ -179557,7 +180162,7 @@ function postProcessRangeDiff(raw2, contextLines = 3) {
|
|
|
179557
180162
|
}
|
|
179558
180163
|
|
|
179559
180164
|
// yes/index.ts
|
|
179560
|
-
import { performance as
|
|
180165
|
+
import { performance as performance8 } from "node:perf_hooks";
|
|
179561
180166
|
import { setTimeout as sleep2 } from "node:timers/promises";
|
|
179562
180167
|
|
|
179563
180168
|
// node_modules/.pnpm/lru-cache@11.5.2/node_modules/lru-cache/dist/esm/node/index.min.js
|
|
@@ -180272,14 +180877,14 @@ function _op(fn2, options) {
|
|
|
180272
180877
|
`failed to create cache key${namePrefix ? ` for ${options.name}` : ""}: ${errorMessage}`
|
|
180273
180878
|
);
|
|
180274
180879
|
}
|
|
180275
|
-
const startTime =
|
|
180880
|
+
const startTime = performance8.now();
|
|
180276
180881
|
const timestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
180277
180882
|
log2.debug(`${namePrefix}[${timestamp}] cache lookup started`, { key });
|
|
180278
180883
|
if (shouldCache && lruCache) {
|
|
180279
180884
|
const cached4 = lruCache.get(key);
|
|
180280
180885
|
if (cached4 !== void 0) {
|
|
180281
180886
|
if (keyMap?.get(key) === void 0) keyMap?.set(key, input);
|
|
180282
|
-
const runtime =
|
|
180887
|
+
const runtime = performance8.now() - startTime;
|
|
180283
180888
|
log2.debug(`${namePrefix}[${timestamp}] cache hit (from lru)`, {
|
|
180284
180889
|
key,
|
|
180285
180890
|
runtime: `${Math.round(runtime)}ms`
|
|
@@ -180298,12 +180903,12 @@ function _op(fn2, options) {
|
|
|
180298
180903
|
defaultCacheMiss(key);
|
|
180299
180904
|
const retries = options.retries ?? [];
|
|
180300
180905
|
let lastError;
|
|
180301
|
-
const execStartTime =
|
|
180906
|
+
const execStartTime = performance8.now();
|
|
180302
180907
|
const promise2 = (async () => {
|
|
180303
180908
|
for (let attempt = 0; attempt <= retries.length; attempt++) {
|
|
180304
180909
|
try {
|
|
180305
180910
|
const result = acceptsCtx ? await fn2(input, ctx) : await fn2(input);
|
|
180306
|
-
const execRuntime2 =
|
|
180911
|
+
const execRuntime2 = performance8.now() - execStartTime;
|
|
180307
180912
|
const skip = options.skipCache ? options.skipCache(result) : false;
|
|
180308
180913
|
if (shouldCache && lruCache && result !== null && result !== void 0 && !skip) {
|
|
180309
180914
|
lruCache.set(key, result);
|
|
@@ -180356,7 +180961,7 @@ function _op(fn2, options) {
|
|
|
180356
180961
|
}
|
|
180357
180962
|
}
|
|
180358
180963
|
}
|
|
180359
|
-
const execRuntime =
|
|
180964
|
+
const execRuntime = performance8.now() - execStartTime;
|
|
180360
180965
|
log2.debug(`${namePrefix}[${timestamp}] function failed after all retries`, {
|
|
180361
180966
|
key,
|
|
180362
180967
|
runtime: `${Math.round(execRuntime)}ms`,
|
|
@@ -180421,13 +181026,13 @@ function _op(fn2, options) {
|
|
|
180421
181026
|
|
|
180422
181027
|
// mcp/git.ts
|
|
180423
181028
|
import { randomUUID as randomUUID3 } from "node:crypto";
|
|
180424
|
-
import { writeFileSync as
|
|
180425
|
-
import { join as
|
|
181029
|
+
import { writeFileSync as writeFileSync8 } from "node:fs";
|
|
181030
|
+
import { join as join12 } from "node:path";
|
|
180426
181031
|
|
|
180427
181032
|
// utils/apiCommit.ts
|
|
180428
181033
|
import { execFileSync as execFileSync5 } from "node:child_process";
|
|
180429
181034
|
import { lstat, readlink } from "node:fs/promises";
|
|
180430
|
-
import { join as
|
|
181035
|
+
import { join as join10 } from "node:path";
|
|
180431
181036
|
var GITHUB_API = "https://api.github.com";
|
|
180432
181037
|
var MAX_BLOB_BYTES = 30 * 1024 * 1024;
|
|
180433
181038
|
var BLOB_UPLOAD_CONCURRENCY = 8;
|
|
@@ -180492,7 +181097,7 @@ async function assertApiCommittable(files) {
|
|
|
180492
181097
|
const root = getRepoRoot();
|
|
180493
181098
|
const attrs = $(
|
|
180494
181099
|
"git",
|
|
180495
|
-
["check-attr", "filter", "-z", "--", ...present.map((p) =>
|
|
181100
|
+
["check-attr", "filter", "-z", "--", ...present.map((p) => join10(root, p))],
|
|
180496
181101
|
{ log: false }
|
|
180497
181102
|
);
|
|
180498
181103
|
const parts = attrs.split("\0");
|
|
@@ -180504,7 +181109,7 @@ async function assertApiCommittable(files) {
|
|
|
180504
181109
|
}
|
|
180505
181110
|
}
|
|
180506
181111
|
for (const path4 of present) {
|
|
180507
|
-
const stat = await lstat(
|
|
181112
|
+
const stat = await lstat(join10(root, path4));
|
|
180508
181113
|
if (stat.isDirectory()) {
|
|
180509
181114
|
throw new Error(
|
|
180510
181115
|
`'${path4}' is a directory (nested repository or submodule?) \u2014 signed commits only support files and symlinks.`
|
|
@@ -180513,7 +181118,7 @@ async function assertApiCommittable(files) {
|
|
|
180513
181118
|
}
|
|
180514
181119
|
}
|
|
180515
181120
|
async function createBlobEntry(params) {
|
|
180516
|
-
const absPath =
|
|
181121
|
+
const absPath = join10(params.repoRoot, params.path);
|
|
180517
181122
|
const stat = await lstat(absPath);
|
|
180518
181123
|
if (stat.size > MAX_BLOB_BYTES) {
|
|
180519
181124
|
throw new Error(
|
|
@@ -180690,10 +181295,10 @@ this usually means your local branch has commits that were never pushed \u2014 s
|
|
|
180690
181295
|
}
|
|
180691
181296
|
|
|
180692
181297
|
// utils/github.ts
|
|
180693
|
-
var
|
|
181298
|
+
var core4 = __toESM(require_core(), 1);
|
|
180694
181299
|
import { createSign } from "node:crypto";
|
|
180695
181300
|
import { rename, writeFile } from "node:fs/promises";
|
|
180696
|
-
import { dirname as dirname3, join as
|
|
181301
|
+
import { dirname as dirname3, join as join11 } from "node:path";
|
|
180697
181302
|
|
|
180698
181303
|
// node_modules/.pnpm/@octokit+plugin-throttling@11.0.5_@octokit+core@7.0.7/node_modules/@octokit/plugin-throttling/dist-bundle/index.js
|
|
180699
181304
|
var import_light = __toESM(require_light(), 1);
|
|
@@ -184891,7 +185496,7 @@ async function fetchIdTokenFromStash(creds) {
|
|
|
184891
185496
|
throw new Error("ID token response has no value field");
|
|
184892
185497
|
}
|
|
184893
185498
|
if (isGitHubActions) {
|
|
184894
|
-
|
|
185499
|
+
core4.setSecret(body.value);
|
|
184895
185500
|
}
|
|
184896
185501
|
return body.value;
|
|
184897
185502
|
}
|
|
@@ -184904,7 +185509,7 @@ function translateIdTokenError(error52) {
|
|
|
184904
185509
|
var mintIdToken = op(
|
|
184905
185510
|
async () => {
|
|
184906
185511
|
try {
|
|
184907
|
-
return await
|
|
185512
|
+
return await core4.getIDToken(OIDC_AUDIENCE);
|
|
184908
185513
|
} catch (error52) {
|
|
184909
185514
|
throw translateIdTokenError(error52);
|
|
184910
185515
|
}
|
|
@@ -185113,7 +185718,7 @@ function getGitHubUsageSummary() {
|
|
|
185113
185718
|
}
|
|
185114
185719
|
async function writeGitHubUsageSummaryToFile(path4) {
|
|
185115
185720
|
const summary2 = getGitHubUsageSummary();
|
|
185116
|
-
const tmpPath =
|
|
185721
|
+
const tmpPath = join11(dirname3(path4), `.usage-summary-${process.pid}.tmp`);
|
|
185117
185722
|
await writeFile(tmpPath, JSON.stringify(summary2));
|
|
185118
185723
|
await rename(tmpPath, path4);
|
|
185119
185724
|
}
|
|
@@ -186265,8 +186870,8 @@ function countAhead(head, base, cwd) {
|
|
|
186265
186870
|
function spillGitOutput(params) {
|
|
186266
186871
|
const tempDir = process.env.PULLFROG_TEMP_DIR;
|
|
186267
186872
|
if (!tempDir) throw new Error("PULLFROG_TEMP_DIR not set");
|
|
186268
|
-
const outputPath =
|
|
186269
|
-
|
|
186873
|
+
const outputPath = join12(tempDir, `git-${params.command}-${randomUUID3().slice(0, 8)}.txt`);
|
|
186874
|
+
writeFileSync8(outputPath, params.output);
|
|
186270
186875
|
const previewByLines = params.output.split("\n").slice(0, OVERFLOW_PREVIEW_LINES).join("\n");
|
|
186271
186876
|
const preview = previewByLines.length <= OVERFLOW_PREVIEW_MAX_CHARS ? previewByLines : `${previewByLines.slice(0, OVERFLOW_PREVIEW_MAX_CHARS)}\u2026`;
|
|
186272
186877
|
log.info(
|
|
@@ -187780,9 +188385,9 @@ async function reportReviewNodeId(ctx, params) {
|
|
|
187780
188385
|
import { execFileSync as execFileSync6, execSync as execSync2 } from "node:child_process";
|
|
187781
188386
|
import { mkdtempSync, readdirSync, realpathSync as realpathSync2, unlinkSync as unlinkSync2 } from "node:fs";
|
|
187782
188387
|
import { tmpdir as tmpdir2 } from "node:os";
|
|
187783
|
-
import { join as
|
|
188388
|
+
import { join as join13 } from "node:path";
|
|
187784
188389
|
function createTempDirectory() {
|
|
187785
|
-
const sharedTempDir = mkdtempSync(
|
|
188390
|
+
const sharedTempDir = mkdtempSync(join13(tmpdir2(), "pullfrog-"));
|
|
187786
188391
|
process.env.PULLFROG_TEMP_DIR = sharedTempDir;
|
|
187787
188392
|
log.info(`\xBB created temp dir at ${sharedTempDir}`);
|
|
187788
188393
|
return sharedTempDir;
|
|
@@ -187827,13 +188432,13 @@ function wipeRunnerLeakSurface() {
|
|
|
187827
188432
|
return [];
|
|
187828
188433
|
}
|
|
187829
188434
|
};
|
|
187830
|
-
const fileCommandsDir =
|
|
188435
|
+
const fileCommandsDir = join13(runnerTemp, "_runner_file_commands");
|
|
187831
188436
|
for (const entry of listDir(fileCommandsDir)) {
|
|
187832
|
-
tryUnlink(
|
|
188437
|
+
tryUnlink(join13(fileCommandsDir, entry));
|
|
187833
188438
|
}
|
|
187834
188439
|
for (const entry of listDir(runnerTemp)) {
|
|
187835
188440
|
if (entry.endsWith(".sh") || /^git-credentials-.*\.config$/.test(entry)) {
|
|
187836
|
-
tryUnlink(
|
|
188441
|
+
tryUnlink(join13(runnerTemp, entry));
|
|
187837
188442
|
}
|
|
187838
188443
|
}
|
|
187839
188444
|
if (wiped.length > 0) {
|
|
@@ -188383,11 +188988,11 @@ function CheckoutPrTool(ctx) {
|
|
|
188383
188988
|
headSha: checkoutSha
|
|
188384
188989
|
});
|
|
188385
188990
|
if (incremental.status === "ok") {
|
|
188386
|
-
incrementalDiffPath =
|
|
188991
|
+
incrementalDiffPath = join14(
|
|
188387
188992
|
tempDir,
|
|
188388
188993
|
`pr-${pull_number}-${beforeShort}-${headShort}-incremental.diff`
|
|
188389
188994
|
);
|
|
188390
|
-
|
|
188995
|
+
writeFileSync9(incrementalDiffPath, incremental.diff);
|
|
188391
188996
|
log.info(
|
|
188392
188997
|
`\xBB incremental diff computed (${incremental.diff.length} bytes) \u2192 ${incrementalDiffPath}`
|
|
188393
188998
|
);
|
|
@@ -188400,8 +189005,8 @@ function CheckoutPrTool(ctx) {
|
|
|
188400
189005
|
const diffPreview = formatResult.content.split("\n").slice(0, 100).join("\n");
|
|
188401
189006
|
log.debug(`formatted diff preview (first 100 lines):
|
|
188402
189007
|
${diffPreview}`);
|
|
188403
|
-
const diffPath =
|
|
188404
|
-
|
|
189008
|
+
const diffPath = join14(tempDir, `pr-${pull_number}-${headShort}.diff`);
|
|
189009
|
+
writeFileSync9(diffPath, formatResult.content);
|
|
188405
189010
|
log.debug(`wrote diff to ${diffPath} (${formatResult.content.length} bytes)`);
|
|
188406
189011
|
primary.diffCoverage = createDiffCoverageState({
|
|
188407
189012
|
diffPath,
|
|
@@ -188558,8 +189163,8 @@ ${dirty}`
|
|
|
188558
189163
|
}
|
|
188559
189164
|
|
|
188560
189165
|
// mcp/checkSuite.ts
|
|
188561
|
-
import { mkdirSync as
|
|
188562
|
-
import { join as
|
|
189166
|
+
import { mkdirSync as mkdirSync8, writeFileSync as writeFileSync10 } from "node:fs";
|
|
189167
|
+
import { join as join15 } from "node:path";
|
|
188563
189168
|
var GetCheckSuiteLogs = type({
|
|
188564
189169
|
check_suite_id: type.number.describe("the id from check_suite.id")
|
|
188565
189170
|
});
|
|
@@ -188655,8 +189260,8 @@ function GetCheckSuiteLogsTool(ctx) {
|
|
|
188655
189260
|
if (!tempDir) {
|
|
188656
189261
|
throw new Error("PULLFROG_TEMP_DIR not set");
|
|
188657
189262
|
}
|
|
188658
|
-
const logsDir =
|
|
188659
|
-
|
|
189263
|
+
const logsDir = join15(tempDir, "ci-logs");
|
|
189264
|
+
mkdirSync8(logsDir, { recursive: true });
|
|
188660
189265
|
const jobResults = [];
|
|
188661
189266
|
for (const run of failedRuns) {
|
|
188662
189267
|
const jobs = await ctx.octokit.paginate(ctx.octokit.rest.actions.listJobsForWorkflowRun, {
|
|
@@ -188682,8 +189287,8 @@ function GetCheckSuiteLogsTool(ctx) {
|
|
|
188682
189287
|
);
|
|
188683
189288
|
}
|
|
188684
189289
|
const logsText = await logsResult.text();
|
|
188685
|
-
const logPath =
|
|
188686
|
-
|
|
189290
|
+
const logPath = join15(logsDir, `job-${job.id}.log`);
|
|
189291
|
+
writeFileSync10(logPath, logsText);
|
|
188687
189292
|
const analysis = analyzeLog(logsText, 80);
|
|
188688
189293
|
const failedSteps = job.steps?.filter((s) => s.conclusion === "failure").map((s) => `Step ${s.number}: ${s.name}`) ?? [];
|
|
188689
189294
|
jobResults.push({
|
|
@@ -188731,8 +189336,8 @@ function GetCheckSuiteLogsTool(ctx) {
|
|
|
188731
189336
|
}
|
|
188732
189337
|
|
|
188733
189338
|
// mcp/commitInfo.ts
|
|
188734
|
-
import { writeFileSync as
|
|
188735
|
-
import { join as
|
|
189339
|
+
import { writeFileSync as writeFileSync11 } from "node:fs";
|
|
189340
|
+
import { join as join16 } from "node:path";
|
|
188736
189341
|
var CommitInfo = type({
|
|
188737
189342
|
sha: type.string.describe("the commit SHA (full or abbreviated) to fetch")
|
|
188738
189343
|
});
|
|
@@ -188756,8 +189361,8 @@ function CommitInfoTool(ctx) {
|
|
|
188756
189361
|
"PULLFROG_TEMP_DIR not set - get_commit_info must run in pullfrog action context"
|
|
188757
189362
|
);
|
|
188758
189363
|
}
|
|
188759
|
-
const diffFile =
|
|
188760
|
-
|
|
189364
|
+
const diffFile = join16(tempDir, `commit-${sha.slice(0, 7)}.diff`);
|
|
189365
|
+
writeFileSync11(diffFile, formatResult.content);
|
|
188761
189366
|
log.debug(`wrote commit diff to ${diffFile} (${formatResult.content.length} bytes)`);
|
|
188762
189367
|
return {
|
|
188763
189368
|
sha: data.sha,
|
|
@@ -188890,17 +189495,17 @@ function ReopenCurrentTool(ctx) {
|
|
|
188890
189495
|
}
|
|
188891
189496
|
|
|
188892
189497
|
// prep/index.ts
|
|
188893
|
-
import { performance as
|
|
189498
|
+
import { performance as performance9 } from "node:perf_hooks";
|
|
188894
189499
|
|
|
188895
189500
|
// prep/installNodeDependencies.ts
|
|
188896
|
-
import { existsSync as
|
|
188897
|
-
import { join as
|
|
189501
|
+
import { existsSync as existsSync7 } from "node:fs";
|
|
189502
|
+
import { join as join18 } from "node:path";
|
|
188898
189503
|
|
|
188899
189504
|
// utils/packageManager.ts
|
|
188900
189505
|
var import_semver2 = __toESM(require_semver2(), 1);
|
|
188901
|
-
import { existsSync as
|
|
189506
|
+
import { existsSync as existsSync6 } from "node:fs";
|
|
188902
189507
|
import { mkdir, readFile as readFile2 } from "node:fs/promises";
|
|
188903
|
-
import { delimiter, join as
|
|
189508
|
+
import { delimiter, join as join17 } from "node:path";
|
|
188904
189509
|
var SUPPORTED_NAMES = ["npm", "pnpm", "yarn", "bun"];
|
|
188905
189510
|
var COREPACK_MANAGED = ["pnpm", "yarn"];
|
|
188906
189511
|
function isSupported(name) {
|
|
@@ -188938,8 +189543,8 @@ function parseDevEnginesField(field) {
|
|
|
188938
189543
|
};
|
|
188939
189544
|
}
|
|
188940
189545
|
async function resolvePackageManagerSpec(cwd) {
|
|
188941
|
-
const pkgPath =
|
|
188942
|
-
if (!
|
|
189546
|
+
const pkgPath = join17(cwd, "package.json");
|
|
189547
|
+
if (!existsSync6(pkgPath)) return null;
|
|
188943
189548
|
let pkg;
|
|
188944
189549
|
try {
|
|
188945
189550
|
pkg = JSON.parse(await readFile2(pkgPath, "utf-8"));
|
|
@@ -188997,7 +189602,7 @@ async function currentVersion(name) {
|
|
|
188997
189602
|
return result.stdout.trim();
|
|
188998
189603
|
}
|
|
188999
189604
|
function packageManagerBinDir(tmpdir3) {
|
|
189000
|
-
return
|
|
189605
|
+
return join17(tmpdir3, "pm-bin");
|
|
189001
189606
|
}
|
|
189002
189607
|
async function ensurePackageManager(params) {
|
|
189003
189608
|
const spec = params.spec;
|
|
@@ -189060,7 +189665,7 @@ async function installFallback(name, installSpec) {
|
|
|
189060
189665
|
});
|
|
189061
189666
|
if (result.exitCode !== 0) return result.stderr || `failed to install ${name}`;
|
|
189062
189667
|
if (name === "deno") {
|
|
189063
|
-
const denoPath =
|
|
189668
|
+
const denoPath = join17(process.env.HOME || "", ".deno", "bin");
|
|
189064
189669
|
process.env.PATH = `${denoPath}:${process.env.PATH}`;
|
|
189065
189670
|
}
|
|
189066
189671
|
log.info(`\xBB installed ${name}`);
|
|
@@ -189093,8 +189698,8 @@ async function isCommandAvailable2(command) {
|
|
|
189093
189698
|
var installNodeDependencies = {
|
|
189094
189699
|
name: "installNodeDependencies",
|
|
189095
189700
|
shouldRun: () => {
|
|
189096
|
-
const packageJsonPath =
|
|
189097
|
-
return
|
|
189701
|
+
const packageJsonPath = join18(process.cwd(), "package.json");
|
|
189702
|
+
return existsSync7(packageJsonPath);
|
|
189098
189703
|
},
|
|
189099
189704
|
run: async (options) => {
|
|
189100
189705
|
const declared = await resolvePackageManagerSpec(process.cwd());
|
|
@@ -189188,14 +189793,14 @@ ${errorMessage}`]
|
|
|
189188
189793
|
};
|
|
189189
189794
|
|
|
189190
189795
|
// prep/installPythonDependencies.ts
|
|
189191
|
-
import { existsSync as
|
|
189192
|
-
import { join as
|
|
189796
|
+
import { existsSync as existsSync8, readFileSync as readFileSync4 } from "node:fs";
|
|
189797
|
+
import { join as join19 } from "node:path";
|
|
189193
189798
|
function declaresBuildSystem(path4) {
|
|
189194
|
-
return /^\s*\[\s*build-system\s*\]/m.test(
|
|
189799
|
+
return /^\s*\[\s*build-system\s*\]/m.test(readFileSync4(path4, "utf8"));
|
|
189195
189800
|
}
|
|
189196
189801
|
function configApplies(config3, cwd) {
|
|
189197
|
-
const path4 =
|
|
189198
|
-
return
|
|
189802
|
+
const path4 = join19(cwd, config3.file);
|
|
189803
|
+
return existsSync8(path4) && (config3.applies?.(path4) ?? true);
|
|
189199
189804
|
}
|
|
189200
189805
|
var PYTHON_CONFIGS = [
|
|
189201
189806
|
{
|
|
@@ -189349,7 +189954,7 @@ var installPythonDependencies = {
|
|
|
189349
189954
|
var prepSteps = [installNodeDependencies, installPythonDependencies];
|
|
189350
189955
|
async function runPrepPhase(options) {
|
|
189351
189956
|
log.debug("\xBB starting prep phase...");
|
|
189352
|
-
const startTime =
|
|
189957
|
+
const startTime = performance9.now();
|
|
189353
189958
|
const results = [];
|
|
189354
189959
|
const preDirty = await dirtyTrackedPaths();
|
|
189355
189960
|
try {
|
|
@@ -189371,7 +189976,7 @@ async function runPrepPhase(options) {
|
|
|
189371
189976
|
} finally {
|
|
189372
189977
|
await restoreDirtiedSince({ before: preDirty, actor: "prep" });
|
|
189373
189978
|
}
|
|
189374
|
-
const totalDurationMs =
|
|
189979
|
+
const totalDurationMs = performance9.now() - startTime;
|
|
189375
189980
|
log.debug(`\xBB prep phase completed (${Math.round(totalDurationMs)}ms)`);
|
|
189376
189981
|
return results;
|
|
189377
189982
|
}
|
|
@@ -189516,15 +190121,15 @@ function AwaitDependencyInstallationTool(ctx) {
|
|
|
189516
190121
|
}
|
|
189517
190122
|
|
|
189518
190123
|
// mcp/gh.ts
|
|
189519
|
-
import { mkdirSync as
|
|
189520
|
-
import { join as
|
|
190124
|
+
import { mkdirSync as mkdirSync9 } from "node:fs";
|
|
190125
|
+
import { join as join21 } from "node:path";
|
|
189521
190126
|
|
|
189522
190127
|
// mcp/shell.ts
|
|
189523
190128
|
import { spawn as spawn3, spawnSync as spawnSync6 } from "node:child_process";
|
|
189524
190129
|
import { randomUUID as randomUUID4 } from "node:crypto";
|
|
189525
|
-
import { closeSync, openSync, writeFileSync as
|
|
190130
|
+
import { closeSync, openSync, writeFileSync as writeFileSync12 } from "node:fs";
|
|
189526
190131
|
import { userInfo as userInfo2 } from "node:os";
|
|
189527
|
-
import { join as
|
|
190132
|
+
import { join as join20 } from "node:path";
|
|
189528
190133
|
import { setTimeout as sleep3 } from "node:timers/promises";
|
|
189529
190134
|
var ShellParams = type({
|
|
189530
190135
|
command: "string",
|
|
@@ -189787,8 +190392,8 @@ ${spawnError}` : spawnError;
|
|
|
189787
190392
|
}
|
|
189788
190393
|
function capOutput(output) {
|
|
189789
190394
|
if (output.length <= MAX_OUTPUT_CHARS) return output;
|
|
189790
|
-
const fullPath =
|
|
189791
|
-
|
|
190395
|
+
const fullPath = join20(getTempDir(), `shell-${randomUUID4().slice(0, 8)}.log`);
|
|
190396
|
+
writeFileSync12(fullPath, output);
|
|
189792
190397
|
const elided = output.length - MAX_OUTPUT_CHARS;
|
|
189793
190398
|
return `... [${elided} chars truncated; full output saved to ${fullPath}] ...
|
|
189794
190399
|
${output.slice(-MAX_OUTPUT_CHARS)}`;
|
|
@@ -189842,8 +190447,8 @@ Do NOT use this tool for git commands \u2014 use the dedicated git tools instead
|
|
|
189842
190447
|
if (params.background) {
|
|
189843
190448
|
const tempDir = getTempDir();
|
|
189844
190449
|
const handle = `bg-${randomUUID4().slice(0, 8)}`;
|
|
189845
|
-
const outputPath =
|
|
189846
|
-
const pidPath =
|
|
190450
|
+
const outputPath = join20(tempDir, `${handle}.log`);
|
|
190451
|
+
const pidPath = join20(tempDir, `${handle}.pid`);
|
|
189847
190452
|
const logFd = openSync(outputPath, "a");
|
|
189848
190453
|
let proc;
|
|
189849
190454
|
try {
|
|
@@ -189860,7 +190465,7 @@ Do NOT use this tool for git commands \u2014 use the dedicated git tools instead
|
|
|
189860
190465
|
throw new Error("failed to start background process");
|
|
189861
190466
|
}
|
|
189862
190467
|
proc.unref();
|
|
189863
|
-
|
|
190468
|
+
writeFileSync12(pidPath, `${proc.pid}
|
|
189864
190469
|
`);
|
|
189865
190470
|
ctx.toolState.backgroundProcesses.set(handle, { pid: proc.pid, outputPath, pidPath });
|
|
189866
190471
|
return {
|
|
@@ -189940,8 +190545,8 @@ function GhTool(ctx) {
|
|
|
189940
190545
|
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.`,
|
|
189941
190546
|
parameters: GhParams,
|
|
189942
190547
|
execute: execute(async (params) => {
|
|
189943
|
-
const configDir =
|
|
189944
|
-
|
|
190548
|
+
const configDir = join21(ctx.tmpdir, "gh-config");
|
|
190549
|
+
mkdirSync9(configDir, { recursive: true });
|
|
189945
190550
|
const command = ["gh", ...params.args.map(shellQuote)].join(" ");
|
|
189946
190551
|
const result = await runSandboxed({
|
|
189947
190552
|
command,
|
|
@@ -190664,8 +191269,8 @@ function PullRequestInfoTool(ctx) {
|
|
|
190664
191269
|
}
|
|
190665
191270
|
|
|
190666
191271
|
// mcp/reviewComments.ts
|
|
190667
|
-
import { writeFileSync as
|
|
190668
|
-
import { join as
|
|
191272
|
+
import { writeFileSync as writeFileSync13 } from "node:fs";
|
|
191273
|
+
import { join as join22 } from "node:path";
|
|
190669
191274
|
var COMMENTS_PER_THREAD = 50;
|
|
190670
191275
|
var REVIEW_THREADS_QUERY = `
|
|
190671
191276
|
query ($owner: String!, $name: String!, $prNumber: Int!, $cursor: String) {
|
|
@@ -191138,8 +191743,8 @@ function GetReviewCommentsTool(ctx) {
|
|
|
191138
191743
|
throw new Error("PULLFROG_TEMP_DIR not set");
|
|
191139
191744
|
}
|
|
191140
191745
|
const filename = `review-${params.review_id}-threads.md`;
|
|
191141
|
-
const commentsPath =
|
|
191142
|
-
|
|
191746
|
+
const commentsPath = join22(tempDir, filename);
|
|
191747
|
+
writeFileSync13(commentsPath, formatted.content);
|
|
191143
191748
|
log.debug(`wrote ${threadBlocks.length} threads to ${commentsPath}`);
|
|
191144
191749
|
return {
|
|
191145
191750
|
review_id: params.review_id,
|
|
@@ -191472,8 +192077,8 @@ function UploadFileTool(ctx) {
|
|
|
191472
192077
|
}
|
|
191473
192078
|
|
|
191474
192079
|
// mcp/xrepo.ts
|
|
191475
|
-
import { mkdirSync as
|
|
191476
|
-
import { join as
|
|
192080
|
+
import { mkdirSync as mkdirSync10, rmSync as rmSync2 } from "node:fs";
|
|
192081
|
+
import { join as join23 } from "node:path";
|
|
191477
192082
|
function assertValidRepoName(name) {
|
|
191478
192083
|
if (!/^[A-Za-z0-9._-]+$/.test(name) || name.startsWith("-") || name.includes("..")) {
|
|
191479
192084
|
throw new Error(
|
|
@@ -191553,8 +192158,8 @@ function CheckoutRepoTool(ctx) {
|
|
|
191553
192158
|
return { path: existing.dir, access: existing.access, note: "already checked out." };
|
|
191554
192159
|
}
|
|
191555
192160
|
const access = accessFor(ctx, repo);
|
|
191556
|
-
const dir =
|
|
191557
|
-
|
|
192161
|
+
const dir = join23(ctx.tmpdir, "xrepo", repo);
|
|
192162
|
+
mkdirSync10(dir, { recursive: true });
|
|
191558
192163
|
const state = ensureRepoState(ctx.toolState, { owner, name: repo, dir, access });
|
|
191559
192164
|
const rc = resolveRepoCtx(ctx, repo);
|
|
191560
192165
|
try {
|
|
@@ -191802,6 +192407,9 @@ function hasEnvVar2(name) {
|
|
|
191802
192407
|
function hasClaudeCodeAuth() {
|
|
191803
192408
|
return hasEnvVar2("CLAUDE_CODE_OAUTH_TOKEN") || hasEnvVar2("ANTHROPIC_API_KEY");
|
|
191804
192409
|
}
|
|
192410
|
+
function hasCodexAuth() {
|
|
192411
|
+
return hasEnvVar2("CODEX_AUTH_JSON") || hasEnvVar2("OPENAI_API_KEY");
|
|
192412
|
+
}
|
|
191805
192413
|
function hasBedrockAuth() {
|
|
191806
192414
|
return hasEnvVar2("AWS_BEARER_TOKEN_BEDROCK") || hasEnvVar2("AWS_ACCESS_KEY_ID") && hasEnvVar2("AWS_SECRET_ACCESS_KEY");
|
|
191807
192415
|
}
|
|
@@ -191828,6 +192436,15 @@ function resolveSlug(slug2) {
|
|
|
191828
192436
|
}
|
|
191829
192437
|
return vertexId;
|
|
191830
192438
|
}
|
|
192439
|
+
if (alias?.routing === "azure") {
|
|
192440
|
+
const deployment = process.env[AZURE_DEPLOYMENT_ENV]?.trim();
|
|
192441
|
+
if (!deployment) {
|
|
192442
|
+
throw new Error(
|
|
192443
|
+
`${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.`
|
|
192444
|
+
);
|
|
192445
|
+
}
|
|
192446
|
+
return `${AZURE_PROVIDER}/${deployment}`;
|
|
192447
|
+
}
|
|
191831
192448
|
if (alias?.routing === "openai-compatible") {
|
|
191832
192449
|
const modelId = process.env[OPENAI_COMPATIBLE_MODEL_ENV]?.trim();
|
|
191833
192450
|
if (!modelId) {
|
|
@@ -191866,6 +192483,7 @@ function resolveAgent(ctx) {
|
|
|
191866
192483
|
}
|
|
191867
192484
|
log.warning(`\xBB unknown PULLFROG_AGENT="${envAgent}" \u2014 falling through to auto-select`);
|
|
191868
192485
|
}
|
|
192486
|
+
if (ctx.proxyModel) return agents.opencode;
|
|
191869
192487
|
if (ctx.model && hasBedrockAuth() && process.env[BEDROCK_MODEL_ID_ENV]?.trim() === ctx.model) {
|
|
191870
192488
|
return isBedrockAnthropicId(ctx.model) ? agents.claude : agents.opencode;
|
|
191871
192489
|
}
|
|
@@ -191875,12 +192493,14 @@ function resolveAgent(ctx) {
|
|
|
191875
192493
|
if (ctx.model) {
|
|
191876
192494
|
try {
|
|
191877
192495
|
const provider2 = getModelProvider(ctx.model);
|
|
191878
|
-
if (provider2 === "anthropic" && hasClaudeCodeAuth())
|
|
191879
|
-
|
|
191880
|
-
}
|
|
192496
|
+
if (provider2 === "anthropic" && hasClaudeCodeAuth()) return agents.claude;
|
|
192497
|
+
if (provider2 === "openai" && ctx.codexAgent && hasCodexAuth()) return agents.codex;
|
|
191881
192498
|
} catch {
|
|
191882
192499
|
}
|
|
191883
192500
|
}
|
|
192501
|
+
if (!ctx.model && ctx.codexAgent && hasCodexAuth() && !hasClaudeCodeAuth()) {
|
|
192502
|
+
return agents.codex;
|
|
192503
|
+
}
|
|
191884
192504
|
return agents.opencode;
|
|
191885
192505
|
}
|
|
191886
192506
|
|
|
@@ -191978,10 +192598,37 @@ auto-compaction is disabled, so long runs grow until your endpoint refuses them.
|
|
|
191978
192598
|
|
|
191979
192599
|
for full setup instructions, see https://docs.pullfrog.com/openai-compatible`;
|
|
191980
192600
|
}
|
|
192601
|
+
function buildAzureSetupError(params) {
|
|
192602
|
+
const githubSecretsUrl = `https://github.com/${params.owner}/${params.name}/settings/secrets/actions`;
|
|
192603
|
+
return `Azure OpenAI selected but required configuration is missing: ${params.missing.join(", ")}.
|
|
192604
|
+
|
|
192605
|
+
only the API key is sensitive \u2014 add it as a secret at ${githubSecretsUrl}. the rest is plain workflow \`env:\`:
|
|
192606
|
+
|
|
192607
|
+
${AZURE_RESOURCE_NAME_ENV}: <name>
|
|
192608
|
+
${AZURE_API_KEY_ENV}: \${{ secrets.${AZURE_API_KEY_ENV} }}
|
|
192609
|
+
${AZURE_DEPLOYMENT_ENV}: <deployment-name>
|
|
192610
|
+
${AZURE_CONTEXT_ENV}: "400000"
|
|
192611
|
+
${AZURE_MAX_OUTPUT_ENV}: "128000"
|
|
192612
|
+
|
|
192613
|
+
${AZURE_RESOURCE_NAME_ENV} is the \`<name>\` in your endpoint https://<name>.openai.azure.com.
|
|
192614
|
+
${AZURE_DEPLOYMENT_ENV} is the name of the deployment, which is not necessarily the name of
|
|
192615
|
+
the model it serves \u2014 Azure routes on the deployment name.
|
|
192616
|
+
|
|
192617
|
+
set the last two to the real limits of whichever model your deployment serves. Pullfrog can't
|
|
192618
|
+
discover them \u2014 a deployment name carries no catalog metadata \u2014 and without them completions
|
|
192619
|
+
are capped at 32000 tokens (rejected outright by models with a smaller cap) and auto-compaction
|
|
192620
|
+
is disabled, so long runs grow until Azure refuses them.
|
|
192621
|
+
|
|
192622
|
+
for full setup instructions, see https://docs.pullfrog.com/azure`;
|
|
192623
|
+
}
|
|
191981
192624
|
function hasEnvVar3(name) {
|
|
191982
192625
|
const value2 = process.env[name];
|
|
191983
192626
|
return typeof value2 === "string" && value2.length > 0;
|
|
191984
192627
|
}
|
|
192628
|
+
function modelHasRuntimeAuth(model) {
|
|
192629
|
+
const authVars = [...getModelEnvVars(model), ...getModelManagedCredentials(model)];
|
|
192630
|
+
return authVars.length === 0 || authVars.some(hasEnvVar3);
|
|
192631
|
+
}
|
|
191985
192632
|
function hasPositiveNumberEnvVar(name) {
|
|
191986
192633
|
return Number(process.env[name]) > 0;
|
|
191987
192634
|
}
|
|
@@ -192000,6 +192647,17 @@ function validateOpenAICompatibleSetup(params) {
|
|
|
192000
192647
|
);
|
|
192001
192648
|
}
|
|
192002
192649
|
}
|
|
192650
|
+
function validateAzureSetup(params) {
|
|
192651
|
+
const missing = [];
|
|
192652
|
+
if (!hasEnvVar3(AZURE_API_KEY_ENV)) missing.push(AZURE_API_KEY_ENV);
|
|
192653
|
+
if (!hasEnvVar3(AZURE_RESOURCE_NAME_ENV)) missing.push(AZURE_RESOURCE_NAME_ENV);
|
|
192654
|
+
if (!hasEnvVar3(AZURE_DEPLOYMENT_ENV)) missing.push(AZURE_DEPLOYMENT_ENV);
|
|
192655
|
+
if (!hasPositiveNumberEnvVar(AZURE_CONTEXT_ENV)) missing.push(AZURE_CONTEXT_ENV);
|
|
192656
|
+
if (!hasPositiveNumberEnvVar(AZURE_MAX_OUTPUT_ENV)) missing.push(AZURE_MAX_OUTPUT_ENV);
|
|
192657
|
+
if (missing.length > 0) {
|
|
192658
|
+
throw new Error(buildAzureSetupError({ owner: params.owner, name: params.name, missing }));
|
|
192659
|
+
}
|
|
192660
|
+
}
|
|
192003
192661
|
function validateBedrockSetup(params) {
|
|
192004
192662
|
const hasAuth = hasEnvVar3("AWS_BEARER_TOKEN_BEDROCK") || hasEnvVar3("AWS_ACCESS_KEY_ID") && hasEnvVar3("AWS_SECRET_ACCESS_KEY");
|
|
192005
192663
|
const missing = [];
|
|
@@ -192023,6 +192681,12 @@ function validateVertexSetup(params) {
|
|
|
192023
192681
|
throw new Error(buildVertexSetupError({ owner: params.owner, name: params.name, missing }));
|
|
192024
192682
|
}
|
|
192025
192683
|
}
|
|
192684
|
+
function hasSingleProviderAuth(agentName) {
|
|
192685
|
+
if (agentName === "codex") {
|
|
192686
|
+
return hasEnvVar3("OPENAI_API_KEY") || hasEnvVar3("CODEX_AUTH_JSON");
|
|
192687
|
+
}
|
|
192688
|
+
return hasEnvVar3("ANTHROPIC_API_KEY") || hasEnvVar3("CLAUDE_CODE_OAUTH_TOKEN");
|
|
192689
|
+
}
|
|
192026
192690
|
function validateAgentApiKey(params) {
|
|
192027
192691
|
if (params.model) {
|
|
192028
192692
|
const alias = resolveDisplayAlias(params.model);
|
|
@@ -192038,6 +192702,10 @@ function validateAgentApiKey(params) {
|
|
|
192038
192702
|
validateOpenAICompatibleSetup({ owner: params.owner, name: params.name });
|
|
192039
192703
|
return;
|
|
192040
192704
|
}
|
|
192705
|
+
if (params.model.startsWith(`${AZURE_PROVIDER}/`)) {
|
|
192706
|
+
validateAzureSetup({ owner: params.owner, name: params.name });
|
|
192707
|
+
return;
|
|
192708
|
+
}
|
|
192041
192709
|
if (!params.model.includes("/")) {
|
|
192042
192710
|
if (process.env[VERTEX_MODEL_ID_ENV]?.trim() === params.model) {
|
|
192043
192711
|
validateVertexSetup({ owner: params.owner, name: params.name });
|
|
@@ -192061,7 +192729,7 @@ function validateAgentApiKey(params) {
|
|
|
192061
192729
|
})
|
|
192062
192730
|
);
|
|
192063
192731
|
}
|
|
192064
|
-
if (
|
|
192732
|
+
if (hasSingleProviderAuth(params.agent.name)) return;
|
|
192065
192733
|
throw new Error(
|
|
192066
192734
|
buildKeyError({
|
|
192067
192735
|
owner: params.owner,
|
|
@@ -192073,7 +192741,7 @@ function validateAgentApiKey(params) {
|
|
|
192073
192741
|
);
|
|
192074
192742
|
}
|
|
192075
192743
|
if (params.agent.name === "opencode") {
|
|
192076
|
-
if (params.authorized.
|
|
192744
|
+
if ([...params.authorized].some(modelHasRuntimeAuth)) return;
|
|
192077
192745
|
const reason = getModelsFailure();
|
|
192078
192746
|
if (reason) throw new Error(reason);
|
|
192079
192747
|
throw new Error(
|
|
@@ -192085,7 +192753,7 @@ function validateAgentApiKey(params) {
|
|
|
192085
192753
|
})
|
|
192086
192754
|
);
|
|
192087
192755
|
}
|
|
192088
|
-
if (
|
|
192756
|
+
if (hasSingleProviderAuth(params.agent.name)) return;
|
|
192089
192757
|
throw new Error(
|
|
192090
192758
|
buildKeyError({
|
|
192091
192759
|
owner: params.owner,
|
|
@@ -192458,9 +193126,9 @@ async function checkConfiguredCredentials(params) {
|
|
|
192458
193126
|
|
|
192459
193127
|
// utils/gitAuthServer.ts
|
|
192460
193128
|
import { randomUUID as randomUUID5 } from "node:crypto";
|
|
192461
|
-
import { writeFileSync as
|
|
193129
|
+
import { writeFileSync as writeFileSync14 } from "node:fs";
|
|
192462
193130
|
import { createServer as createServer3 } from "node:http";
|
|
192463
|
-
import { join as
|
|
193131
|
+
import { join as join24 } from "node:path";
|
|
192464
193132
|
var REVOKED_TRAP_MS = 6e4;
|
|
192465
193133
|
function revokeGitHubToken(token) {
|
|
192466
193134
|
fetch("https://api.github.com/installation/token", {
|
|
@@ -192529,7 +193197,7 @@ async function startGitAuthServer(tmpdir3) {
|
|
|
192529
193197
|
function writeAskpassScript(code) {
|
|
192530
193198
|
const scriptId = randomUUID5();
|
|
192531
193199
|
const scriptName = `askpass-${scriptId}.js`;
|
|
192532
|
-
const scriptPath =
|
|
193200
|
+
const scriptPath = join24(tmpdir3, scriptName);
|
|
192533
193201
|
const content = [
|
|
192534
193202
|
`#!/usr/bin/env node`,
|
|
192535
193203
|
`var a=process.argv[2]||"";`,
|
|
@@ -192542,7 +193210,7 @@ async function startGitAuthServer(tmpdir3) {
|
|
|
192542
193210
|
`r.on("end",function(){process.stdout.write(d+"\\n")})`,
|
|
192543
193211
|
`}).on("error",function(){process.exit(1)})}`
|
|
192544
193212
|
].join("\n");
|
|
192545
|
-
|
|
193213
|
+
writeFileSync14(scriptPath, content, { mode: 448 });
|
|
192546
193214
|
return scriptPath;
|
|
192547
193215
|
}
|
|
192548
193216
|
async function close() {
|
|
@@ -192981,7 +193649,7 @@ function resolveInstructions(ctx) {
|
|
|
192981
193649
|
|
|
192982
193650
|
// utils/learnings.ts
|
|
192983
193651
|
import { mkdir as mkdir2, readFile as readFile3, writeFile as writeFile2 } from "node:fs/promises";
|
|
192984
|
-
import { dirname as dirname4, join as
|
|
193652
|
+
import { dirname as dirname4, join as join25 } from "node:path";
|
|
192985
193653
|
|
|
192986
193654
|
// utils/learningsTruncate.ts
|
|
192987
193655
|
var MAX_LEARNINGS_LENGTH = 1e5;
|
|
@@ -192999,10 +193667,10 @@ function truncateAtLineBoundary(body, cap) {
|
|
|
192999
193667
|
var LEARNINGS_FILE_NAME = "pullfrog-learnings.md";
|
|
193000
193668
|
var XREPO_LEARNINGS_FILE_NAME = "pullfrog-xrepo-learnings.md";
|
|
193001
193669
|
function learningsFilePath(tmpdir3) {
|
|
193002
|
-
return
|
|
193670
|
+
return join25(tmpdir3, LEARNINGS_FILE_NAME);
|
|
193003
193671
|
}
|
|
193004
193672
|
function xrepoLearningsFilePath(tmpdir3) {
|
|
193005
|
-
return
|
|
193673
|
+
return join25(tmpdir3, XREPO_LEARNINGS_FILE_NAME);
|
|
193006
193674
|
}
|
|
193007
193675
|
async function seedLearningsFile(params) {
|
|
193008
193676
|
const path4 = learningsFilePath(params.tmpdir);
|
|
@@ -193118,7 +193786,7 @@ async function persistXrepoLearnings(ctx) {
|
|
|
193118
193786
|
// utils/modelAccess.ts
|
|
193119
193787
|
function decideModelAccess(input) {
|
|
193120
193788
|
if (!input.modelExplicit || !input.model) return { kind: "ok" };
|
|
193121
|
-
const byokAuthorized = !!input.resolvedModel && (input.authorized.has(input.resolvedModel) || !input.resolvedModel.includes("/") || input.resolvedModel.startsWith(`${OPENAI_COMPATIBLE_PROVIDER}/`));
|
|
193789
|
+
const byokAuthorized = !!input.resolvedModel && (input.authorized.has(input.resolvedModel) || !input.resolvedModel.includes("/") || input.resolvedModel.startsWith(`${OPENAI_COMPATIBLE_PROVIDER}/`) || input.resolvedModel.startsWith(`${AZURE_PROVIDER}/`));
|
|
193122
193790
|
if (input.proxyActive) {
|
|
193123
193791
|
const target = resolveOpenRouterModel(input.model);
|
|
193124
193792
|
if (input.oss) {
|
|
@@ -193163,7 +193831,7 @@ function buildModelAccessError(input) {
|
|
|
193163
193831
|
}
|
|
193164
193832
|
|
|
193165
193833
|
// utils/normalizeEnv.ts
|
|
193166
|
-
var
|
|
193834
|
+
var core5 = __toESM(require_core(), 1);
|
|
193167
193835
|
function hasControlCharacter(value2) {
|
|
193168
193836
|
return [...value2].some((ch) => {
|
|
193169
193837
|
const code = ch.charCodeAt(0);
|
|
@@ -193189,7 +193857,7 @@ function sanitizeSecret(key, value2) {
|
|
|
193189
193857
|
`\xBB stripped whitespace from ${key} (whitespace in secret values breaks GitHub Actions log masking)`
|
|
193190
193858
|
);
|
|
193191
193859
|
}
|
|
193192
|
-
|
|
193860
|
+
core5.setSecret(trimmed);
|
|
193193
193861
|
return trimmed;
|
|
193194
193862
|
}
|
|
193195
193863
|
function normalizeEnv() {
|
|
@@ -193233,7 +193901,7 @@ function normalizeEnv() {
|
|
|
193233
193901
|
}
|
|
193234
193902
|
|
|
193235
193903
|
// utils/overrides.ts
|
|
193236
|
-
var
|
|
193904
|
+
var core6 = __toESM(require_core(), 1);
|
|
193237
193905
|
var DENIED_OVERRIDE_NAMES = /* @__PURE__ */ new Set([
|
|
193238
193906
|
"GITHUB_TOKEN",
|
|
193239
193907
|
"GH_TOKEN",
|
|
@@ -193279,7 +193947,7 @@ function applyOverrides(params) {
|
|
|
193279
193947
|
denied.push(key);
|
|
193280
193948
|
continue;
|
|
193281
193949
|
}
|
|
193282
|
-
if (value2.length > 0)
|
|
193950
|
+
if (value2.length > 0) core6.setSecret(value2);
|
|
193283
193951
|
params.env[key] = value2;
|
|
193284
193952
|
applied.push(key);
|
|
193285
193953
|
}
|
|
@@ -193288,8 +193956,8 @@ function applyOverrides(params) {
|
|
|
193288
193956
|
}
|
|
193289
193957
|
|
|
193290
193958
|
// utils/payload.ts
|
|
193291
|
-
var
|
|
193292
|
-
import { readFileSync as
|
|
193959
|
+
var core7 = __toESM(require_core(), 1);
|
|
193960
|
+
import { readFileSync as readFileSync6 } from "node:fs";
|
|
193293
193961
|
import { isAbsolute as isAbsolute2, resolve as resolve2 } from "node:path";
|
|
193294
193962
|
|
|
193295
193963
|
// utils/versioning.ts
|
|
@@ -193376,8 +194044,8 @@ function resolveCwd(cwd) {
|
|
|
193376
194044
|
return workspace ? resolve2(workspace, cwd) : cwd;
|
|
193377
194045
|
}
|
|
193378
194046
|
function resolvePromptInput() {
|
|
193379
|
-
const promptInput =
|
|
193380
|
-
const promptFile =
|
|
194047
|
+
const promptInput = core7.getInput("prompt");
|
|
194048
|
+
const promptFile = core7.getInput("prompt_file");
|
|
193381
194049
|
if (promptInput && promptFile) {
|
|
193382
194050
|
throw new Error("set exactly one of 'prompt' or 'prompt_file' inputs, not both.");
|
|
193383
194051
|
}
|
|
@@ -193403,7 +194071,7 @@ function resolvePromptInput() {
|
|
|
193403
194071
|
function resolvePromptFile(input) {
|
|
193404
194072
|
const workspace = process.env.GITHUB_WORKSPACE;
|
|
193405
194073
|
const path4 = isAbsolute2(input) ? input : workspace ? resolve2(workspace, input) : resolve2(input);
|
|
193406
|
-
const content =
|
|
194074
|
+
const content = readFileSync6(path4, "utf-8");
|
|
193407
194075
|
if (!content.trim()) {
|
|
193408
194076
|
throw new Error(`prompt_file ${JSON.stringify(input)} is empty.`);
|
|
193409
194077
|
}
|
|
@@ -193411,7 +194079,7 @@ function resolvePromptFile(input) {
|
|
|
193411
194079
|
}
|
|
193412
194080
|
function warnIfDeprecatedStatusChecks(value2) {
|
|
193413
194081
|
if (value2 !== void 0) {
|
|
193414
|
-
|
|
194082
|
+
core7.warning(
|
|
193415
194083
|
"`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."
|
|
193416
194084
|
);
|
|
193417
194085
|
}
|
|
@@ -193419,15 +194087,15 @@ function warnIfDeprecatedStatusChecks(value2) {
|
|
|
193419
194087
|
}
|
|
193420
194088
|
function resolveNonPromptInputs() {
|
|
193421
194089
|
return Inputs.omit("prompt", "prompt_file").assert({
|
|
193422
|
-
model:
|
|
193423
|
-
effort:
|
|
193424
|
-
debug:
|
|
193425
|
-
timeout:
|
|
193426
|
-
cwd:
|
|
193427
|
-
push:
|
|
193428
|
-
shell:
|
|
193429
|
-
status_checks: warnIfDeprecatedStatusChecks(
|
|
193430
|
-
progress_comments:
|
|
194090
|
+
model: core7.getInput("model") || void 0,
|
|
194091
|
+
effort: core7.getInput("effort") || void 0,
|
|
194092
|
+
debug: core7.getInput("debug") || void 0,
|
|
194093
|
+
timeout: core7.getInput("timeout") || void 0,
|
|
194094
|
+
cwd: core7.getInput("cwd") || void 0,
|
|
194095
|
+
push: core7.getInput("push") || void 0,
|
|
194096
|
+
shell: core7.getInput("shell") || void 0,
|
|
194097
|
+
status_checks: warnIfDeprecatedStatusChecks(core7.getInput("status_checks") || void 0),
|
|
194098
|
+
progress_comments: core7.getInput("progress_comments") || void 0
|
|
193431
194099
|
});
|
|
193432
194100
|
}
|
|
193433
194101
|
function resolvePayload(resolvedPromptInput, repoSettings) {
|
|
@@ -193495,7 +194163,7 @@ function resolvePayload(resolvedPromptInput, repoSettings) {
|
|
|
193495
194163
|
};
|
|
193496
194164
|
}
|
|
193497
194165
|
function resolveOutputSchema() {
|
|
193498
|
-
const raw2 =
|
|
194166
|
+
const raw2 = core7.getInput("output_schema");
|
|
193499
194167
|
if (!raw2) return void 0;
|
|
193500
194168
|
let parsed2;
|
|
193501
194169
|
try {
|
|
@@ -193511,10 +194179,10 @@ function resolveOutputSchema() {
|
|
|
193511
194179
|
}
|
|
193512
194180
|
|
|
193513
194181
|
// utils/proxy.ts
|
|
193514
|
-
var
|
|
194182
|
+
var core9 = __toESM(require_core(), 1);
|
|
193515
194183
|
|
|
193516
194184
|
// utils/token.ts
|
|
193517
|
-
var
|
|
194185
|
+
var core8 = __toESM(require_core(), 1);
|
|
193518
194186
|
import assert2 from "node:assert/strict";
|
|
193519
194187
|
var mcpTokenValue;
|
|
193520
194188
|
var refreshMcpTokenFn;
|
|
@@ -193522,7 +194190,7 @@ function getMcpTokenRefresh() {
|
|
|
193522
194190
|
return refreshMcpTokenFn;
|
|
193523
194191
|
}
|
|
193524
194192
|
function getJobToken() {
|
|
193525
|
-
const inputToken =
|
|
194193
|
+
const inputToken = core8.getInput("token");
|
|
193526
194194
|
if (inputToken) {
|
|
193527
194195
|
return inputToken;
|
|
193528
194196
|
}
|
|
@@ -193538,7 +194206,7 @@ async function resolveTokens(params) {
|
|
|
193538
194206
|
if (externalToken) {
|
|
193539
194207
|
mcpTokenValue = externalToken;
|
|
193540
194208
|
if (isGitHubActions) {
|
|
193541
|
-
|
|
194209
|
+
core8.setSecret(externalToken);
|
|
193542
194210
|
}
|
|
193543
194211
|
log.info("\xBB using external GH_TOKEN for both git and MCP");
|
|
193544
194212
|
return {
|
|
@@ -193558,7 +194226,7 @@ async function resolveTokens(params) {
|
|
|
193558
194226
|
const gitPermissions = params.push === "disabled" ? { contents: "read" } : { contents: "write", workflows: "write" };
|
|
193559
194227
|
const gitToken = await acquireNewToken({ repos: writeRepos, permissions: gitPermissions });
|
|
193560
194228
|
if (isGitHubActions) {
|
|
193561
|
-
|
|
194229
|
+
core8.setSecret(gitToken);
|
|
193562
194230
|
}
|
|
193563
194231
|
log.info(
|
|
193564
194232
|
`\xBB acquired git token (${Object.entries(gitPermissions).map((e) => e.join(":")).join(", ")})`
|
|
@@ -193575,7 +194243,7 @@ async function resolveTokens(params) {
|
|
|
193575
194243
|
};
|
|
193576
194244
|
const mcpToken = await acquireNewToken({ repos: writeRepos, permissions: mcpPermissions });
|
|
193577
194245
|
if (isGitHubActions) {
|
|
193578
|
-
|
|
194246
|
+
core8.setSecret(mcpToken);
|
|
193579
194247
|
}
|
|
193580
194248
|
log.info(
|
|
193581
194249
|
`\xBB acquired scoped MCP token (${Object.entries(mcpPermissions).map((e) => e.join(":")).join(", ")})`
|
|
@@ -193584,7 +194252,7 @@ async function resolveTokens(params) {
|
|
|
193584
194252
|
let ghToken;
|
|
193585
194253
|
if (ghPermissions) {
|
|
193586
194254
|
ghToken = await acquireNewToken({ permissions: ghPermissions });
|
|
193587
|
-
if (isGitHubActions)
|
|
194255
|
+
if (isGitHubActions) core8.setSecret(ghToken);
|
|
193588
194256
|
log.info(
|
|
193589
194257
|
`\xBB acquired gh token mirroring ${params.authorPermission} (${formatPermissions(ghPermissions)})`
|
|
193590
194258
|
);
|
|
@@ -193595,7 +194263,7 @@ async function resolveTokens(params) {
|
|
|
193595
194263
|
repos: params.xrepo.read,
|
|
193596
194264
|
permissions: { contents: "read" }
|
|
193597
194265
|
});
|
|
193598
|
-
if (isGitHubActions)
|
|
194266
|
+
if (isGitHubActions) core8.setSecret(readToken);
|
|
193599
194267
|
log.info(`\xBB acquired cross-repo read token (contents:read, ${params.xrepo.read.length} repos)`);
|
|
193600
194268
|
}
|
|
193601
194269
|
mcpTokenValue = mcpToken;
|
|
@@ -193614,7 +194282,7 @@ async function resolveTokens(params) {
|
|
|
193614
194282
|
oidc: params.oidc ?? void 0
|
|
193615
194283
|
}).then((fresh) => {
|
|
193616
194284
|
if (isGitHubActions) {
|
|
193617
|
-
|
|
194285
|
+
core8.setSecret(fresh);
|
|
193618
194286
|
}
|
|
193619
194287
|
mcpTokenValue = fresh;
|
|
193620
194288
|
currentMcpToken = fresh;
|
|
@@ -193635,7 +194303,7 @@ async function resolveTokens(params) {
|
|
|
193635
194303
|
permissions: gitPermissions,
|
|
193636
194304
|
oidc: params.oidc ?? void 0
|
|
193637
194305
|
}).then((fresh) => {
|
|
193638
|
-
if (isGitHubActions)
|
|
194306
|
+
if (isGitHubActions) core8.setSecret(fresh);
|
|
193639
194307
|
void revokeGitHubInstallationToken(currentGitToken);
|
|
193640
194308
|
currentGitToken = fresh;
|
|
193641
194309
|
log.warning("\xBB GitHub rejected the git token; re-acquired a fresh git token");
|
|
@@ -193652,7 +194320,7 @@ async function resolveTokens(params) {
|
|
|
193652
194320
|
permissions: { contents: "read" },
|
|
193653
194321
|
oidc: params.oidc ?? void 0
|
|
193654
194322
|
}).then((fresh) => {
|
|
193655
|
-
if (isGitHubActions)
|
|
194323
|
+
if (isGitHubActions) core8.setSecret(fresh);
|
|
193656
194324
|
void revokeGitHubInstallationToken(read2);
|
|
193657
194325
|
currentReadToken = fresh;
|
|
193658
194326
|
log.warning("\xBB GitHub rejected the read token; re-acquired a fresh read token");
|
|
@@ -193866,7 +194534,7 @@ async function resolveProxyModel(ctx) {
|
|
|
193866
194534
|
});
|
|
193867
194535
|
if (!key) return;
|
|
193868
194536
|
process.env.OPENROUTER_API_KEY = key;
|
|
193869
|
-
|
|
194537
|
+
core9.setSecret(key);
|
|
193870
194538
|
ctx.payload.proxyModel = ctx.proxyModel;
|
|
193871
194539
|
ctx.toolState.model = ctx.proxyModel;
|
|
193872
194540
|
const label = ctx.oss ? "oss" : "router";
|
|
@@ -193937,7 +194605,7 @@ async function runProxyResolution(ctx) {
|
|
|
193937
194605
|
|
|
193938
194606
|
// utils/prSummary.ts
|
|
193939
194607
|
import { mkdir as mkdir3, readFile as readFile4, writeFile as writeFile3 } from "node:fs/promises";
|
|
193940
|
-
import { dirname as dirname5, join as
|
|
194608
|
+
import { dirname as dirname5, join as join26 } from "node:path";
|
|
193941
194609
|
var SUMMARY_FILE_NAME = "pullfrog-summary.md";
|
|
193942
194610
|
var SUMMARY_SCAFFOLD = `# PR summary
|
|
193943
194611
|
|
|
@@ -193947,7 +194615,7 @@ var SUMMARY_SCAFFOLD = `# PR summary
|
|
|
193947
194615
|
var MIN_SNAPSHOT_LENGTH = 60;
|
|
193948
194616
|
var MAX_SNAPSHOT_LENGTH = 32768;
|
|
193949
194617
|
function summaryFilePath(tmpdir3) {
|
|
193950
|
-
return
|
|
194618
|
+
return join26(tmpdir3, SUMMARY_FILE_NAME);
|
|
193951
194619
|
}
|
|
193952
194620
|
async function seedSummaryFile(params) {
|
|
193953
194621
|
const path4 = summaryFilePath(params.tmpdir);
|
|
@@ -194076,6 +194744,7 @@ var defaultSettings = {
|
|
|
194076
194744
|
shell: "restricted",
|
|
194077
194745
|
prApproveEnabled: false,
|
|
194078
194746
|
autoMergeEnabled: false,
|
|
194747
|
+
codexAgent: false,
|
|
194079
194748
|
signedCommits: false,
|
|
194080
194749
|
repoIntelligence: false,
|
|
194081
194750
|
progressComments: true,
|
|
@@ -194564,7 +195233,7 @@ ${genericBody}`,
|
|
|
194564
195233
|
}
|
|
194565
195234
|
|
|
194566
195235
|
// utils/runLifecycle.ts
|
|
194567
|
-
var
|
|
195236
|
+
var core10 = __toESM(require_core(), 1);
|
|
194568
195237
|
|
|
194569
195238
|
// utils/autoMerge.ts
|
|
194570
195239
|
function hasBlockingHumanReview(reviews) {
|
|
@@ -194950,7 +195619,7 @@ async function finalizeSuccessRun(input) {
|
|
|
194950
195619
|
}
|
|
194951
195620
|
if (input.toolState.output) {
|
|
194952
195621
|
log.info(`::pullfrog-output::${Buffer.from(input.toolState.output).toString("base64")}`);
|
|
194953
|
-
|
|
195622
|
+
core10.setOutput("result", input.toolState.output);
|
|
194954
195623
|
}
|
|
194955
195624
|
if (input.result.success) {
|
|
194956
195625
|
await approveAfterFix(input.toolContext).catch((error52) => {
|
|
@@ -195414,7 +196083,11 @@ async function main() {
|
|
|
195414
196083
|
}
|
|
195415
196084
|
const resolvedModel = credentials.kind === "fellBack" ? credentials.replacement : configuredModel;
|
|
195416
196085
|
vertexCredentials = materializeVertexCredentials({ model: resolvedModel });
|
|
195417
|
-
const agent2 = resolveAgent({
|
|
196086
|
+
const agent2 = resolveAgent({
|
|
196087
|
+
model: resolvedModel,
|
|
196088
|
+
proxyModel: payload.proxyModel,
|
|
196089
|
+
codexAgent: runContext.repoSettings.codexAgent
|
|
196090
|
+
});
|
|
195418
196091
|
const effectiveModel = payload.proxyModel ?? resolvedModel ?? payload.model;
|
|
195419
196092
|
toolState.model = effectiveModel;
|
|
195420
196093
|
if (!payload.proxyModel) {
|
|
@@ -195574,11 +196247,16 @@ async function main() {
|
|
|
195574
196247
|
xrepoLearningsFilePath: toolState.xrepoLearningsFilePath ?? null,
|
|
195575
196248
|
xrepoLearningsHeadings: runContext.repoSettings.xrepoLearningsHeadings
|
|
195576
196249
|
});
|
|
195577
|
-
|
|
196250
|
+
const danglingToolRefs = findDanglingPromptToolRefs({
|
|
195578
196251
|
agentId,
|
|
195579
196252
|
prompt: instructions.full,
|
|
195580
196253
|
toolNames: mcpHttpServer.toolNames
|
|
195581
196254
|
});
|
|
196255
|
+
if (danglingToolRefs.length > 0) {
|
|
196256
|
+
log.warning(
|
|
196257
|
+
`\xBB prompt advertises ${danglingToolRefs.length} tool(s) the MCP server did not register: ${danglingToolRefs.join(", ")}`
|
|
196258
|
+
);
|
|
196259
|
+
}
|
|
195582
196260
|
const logParts = [
|
|
195583
196261
|
instructions.eventInstructions ? `EVENT-LEVEL INSTRUCTIONS:
|
|
195584
196262
|
${instructions.eventInstructions}` : null,
|
|
@@ -195593,8 +196271,8 @@ ${instructions.user}` : null,
|
|
|
195593
196271
|
log.info(instructions.full);
|
|
195594
196272
|
});
|
|
195595
196273
|
if (agentId === "opencode") {
|
|
195596
|
-
const pluginDir =
|
|
195597
|
-
const hasPlugins =
|
|
196274
|
+
const pluginDir = join27(process.cwd(), ".opencode", "plugin");
|
|
196275
|
+
const hasPlugins = existsSync9(pluginDir) && readdirSync2(pluginDir).some((f) => /\.[jt]sx?$/.test(f));
|
|
195598
196276
|
if (hasPlugins && toolState.dependencyInstallation?.promise) {
|
|
195599
196277
|
log.info(
|
|
195600
196278
|
"\xBB .opencode/plugin/ detected \u2014 awaiting dependency installation before agent start"
|