pullfrog 0.1.57 → 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 +1059 -388
- package/dist/external.d.ts +2 -1
- package/dist/index.js +1094 -419
- 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:
|
|
@@ -104861,7 +104891,8 @@ import { pipeline } from "node:stream/promises";
|
|
|
104861
104891
|
async function installFromNpmTarball(params) {
|
|
104862
104892
|
const tempDir = process.env.PULLFROG_TEMP_DIR;
|
|
104863
104893
|
if (!tempDir) throw new Error("PULLFROG_TEMP_DIR is not set");
|
|
104864
|
-
const
|
|
104894
|
+
const packageRoot = join(tempDir, `npm-${params.packageName.replace(/[^a-z0-9]+/gi, "-")}`);
|
|
104895
|
+
const extractedDir = join(packageRoot, "package");
|
|
104865
104896
|
const cliPath = join(extractedDir, params.executablePath);
|
|
104866
104897
|
if (existsSync2(cliPath)) {
|
|
104867
104898
|
log.debug(`\xBB using cached binary at ${cliPath}`);
|
|
@@ -104887,7 +104918,8 @@ async function installFromNpmTarball(params) {
|
|
|
104887
104918
|
}
|
|
104888
104919
|
}
|
|
104889
104920
|
log.debug(`\xBB installing ${params.packageName}@${resolvedVersion}...`);
|
|
104890
|
-
|
|
104921
|
+
mkdirSync(packageRoot, { recursive: true });
|
|
104922
|
+
const tarballPath = join(packageRoot, "package.tgz");
|
|
104891
104923
|
const npmRegistry = process.env.NPM_REGISTRY || "https://registry.npmjs.org";
|
|
104892
104924
|
let tarballUrl;
|
|
104893
104925
|
if (params.packageName.startsWith("@")) {
|
|
@@ -104907,7 +104939,7 @@ async function installFromNpmTarball(params) {
|
|
|
104907
104939
|
await pipeline(response.body, fileStream);
|
|
104908
104940
|
log.debug(`\xBB downloaded tarball to ${tarballPath}`);
|
|
104909
104941
|
log.debug(`\xBB extracting tarball...`);
|
|
104910
|
-
const extractResult = spawnSync("tar", ["-xzf", tarballPath, "-C",
|
|
104942
|
+
const extractResult = spawnSync("tar", ["-xzf", tarballPath, "-C", packageRoot], {
|
|
104911
104943
|
stdio: "pipe",
|
|
104912
104944
|
encoding: "utf-8"
|
|
104913
104945
|
});
|
|
@@ -104916,9 +104948,6 @@ async function installFromNpmTarball(params) {
|
|
|
104916
104948
|
`Failed to extract tarball: ${extractResult.stderr || extractResult.stdout || "Unknown error"}`
|
|
104917
104949
|
);
|
|
104918
104950
|
}
|
|
104919
|
-
if (!existsSync2(cliPath)) {
|
|
104920
|
-
throw new Error(`Executable not found in extracted package at ${cliPath}`);
|
|
104921
|
-
}
|
|
104922
104951
|
if (params.installDependencies) {
|
|
104923
104952
|
log.debug(`\xBB installing dependencies for ${params.packageName}...`);
|
|
104924
104953
|
const installResult = spawnSync("npm", ["install", "--production"], {
|
|
@@ -104939,6 +104968,9 @@ async function installFromNpmTarball(params) {
|
|
|
104939
104968
|
}
|
|
104940
104969
|
log.debug(`\xBB dependencies installed`);
|
|
104941
104970
|
}
|
|
104971
|
+
if (!existsSync2(cliPath)) {
|
|
104972
|
+
throw new Error(`Executable not found in extracted package at ${cliPath}`);
|
|
104973
|
+
}
|
|
104942
104974
|
chmodSync(cliPath, 493);
|
|
104943
104975
|
log.debug(`\xBB ${params.packageName} installed at ${cliPath}`);
|
|
104944
104976
|
return cliPath;
|
|
@@ -105112,6 +105144,7 @@ function resolveRunAlias(ctx) {
|
|
|
105112
105144
|
}
|
|
105113
105145
|
const model = ctx.resolvedModel;
|
|
105114
105146
|
if (!model) return void 0;
|
|
105147
|
+
if (model.startsWith(`${AZURE_PROVIDER}/`)) return void 0;
|
|
105115
105148
|
return modelAliases.find((a) => !a.fallback && a.resolve === model) ?? matchHostedAnthropicAlias(model);
|
|
105116
105149
|
}
|
|
105117
105150
|
function ossEffortFloor(ctx) {
|
|
@@ -105150,7 +105183,7 @@ var import_semver = __toESM(require_semver2(), 1);
|
|
|
105150
105183
|
// package.json
|
|
105151
105184
|
var package_default = {
|
|
105152
105185
|
name: "pullfrog",
|
|
105153
|
-
version: "0.1.
|
|
105186
|
+
version: "0.1.59",
|
|
105154
105187
|
type: "module",
|
|
105155
105188
|
bin: {
|
|
105156
105189
|
pullfrog: "dist/cli.mjs",
|
|
@@ -105182,6 +105215,8 @@ var package_default = {
|
|
|
105182
105215
|
"@clack/prompts": "^1.2.0",
|
|
105183
105216
|
"@modelcontextprotocol/sdk": "^1.26.0",
|
|
105184
105217
|
"@opencode-ai/sdk": "1.18.5",
|
|
105218
|
+
"@openai/codex": "0.147.0",
|
|
105219
|
+
"@openai/codex-sdk": "0.147.0",
|
|
105185
105220
|
"@octokit/plugin-throttling": "^11.0.3",
|
|
105186
105221
|
"@octokit/rest": "^22.0.0",
|
|
105187
105222
|
"@octokit/webhooks-types": "^7.6.1",
|
|
@@ -107303,13 +107338,782 @@ var claude = agent({
|
|
|
107303
107338
|
}
|
|
107304
107339
|
});
|
|
107305
107340
|
|
|
107306
|
-
// agents/
|
|
107341
|
+
// agents/codex.ts
|
|
107307
107342
|
var core2 = __toESM(require_core(), 1);
|
|
107308
|
-
import {
|
|
107309
|
-
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";
|
|
107310
107344
|
import { join as join6 } from "node:path";
|
|
107311
107345
|
import { performance as performance6 } from "node:perf_hooks";
|
|
107312
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
|
+
|
|
107313
108117
|
// node_modules/.pnpm/@opencode-ai+sdk@1.18.5/node_modules/@opencode-ai/sdk/dist/v2/gen/core/serverSentEvents.gen.js
|
|
107314
108118
|
var createSseClient = ({ onRequest, onSseError, onSseEvent, responseTransformer, responseValidator, sseDefaultRetryDelay, sseMaxRetryAttempts, sseMaxRetryDelay, sseSleepFn, url: url4, ...options }) => {
|
|
107315
108119
|
let lastEventId;
|
|
@@ -112716,114 +113520,6 @@ var import_cross_spawn = __toESM(require_cross_spawn(), 1);
|
|
|
112716
113520
|
// agents/opencode.ts
|
|
112717
113521
|
var import_undici = __toESM(require_undici2(), 1);
|
|
112718
113522
|
|
|
112719
|
-
// utils/codexHome.ts
|
|
112720
|
-
import { execFileSync as execFileSync3 } from "node:child_process";
|
|
112721
|
-
import { mkdirSync as mkdirSync5, writeFileSync as writeFileSync4 } from "node:fs";
|
|
112722
|
-
import { homedir as homedir2, userInfo } from "node:os";
|
|
112723
|
-
import { join as join5 } from "node:path";
|
|
112724
|
-
|
|
112725
|
-
// utils/codexOAuth.ts
|
|
112726
|
-
function decodeJwtExpMs(token) {
|
|
112727
|
-
const parts = token.split(".");
|
|
112728
|
-
if (parts.length !== 3) return null;
|
|
112729
|
-
let payload;
|
|
112730
|
-
try {
|
|
112731
|
-
payload = JSON.parse(Buffer.from(parts[1], "base64url").toString("utf8"));
|
|
112732
|
-
} catch {
|
|
112733
|
-
return null;
|
|
112734
|
-
}
|
|
112735
|
-
if (typeof payload.exp !== "number" || !Number.isFinite(payload.exp)) return null;
|
|
112736
|
-
return payload.exp * 1e3;
|
|
112737
|
-
}
|
|
112738
|
-
function parseCodexAuthBody(raw2) {
|
|
112739
|
-
let parsed2;
|
|
112740
|
-
try {
|
|
112741
|
-
parsed2 = JSON.parse(raw2);
|
|
112742
|
-
} catch {
|
|
112743
|
-
return null;
|
|
112744
|
-
}
|
|
112745
|
-
if (!parsed2 || typeof parsed2 !== "object") return null;
|
|
112746
|
-
const v = parsed2;
|
|
112747
|
-
if (v.auth_mode !== "chatgpt") return null;
|
|
112748
|
-
const tokens = v.tokens;
|
|
112749
|
-
if (!tokens || typeof tokens !== "object") return null;
|
|
112750
|
-
const t = tokens;
|
|
112751
|
-
if (typeof t.access_token !== "string" || t.access_token.length === 0) return null;
|
|
112752
|
-
if (typeof t.refresh_token !== "string" || t.refresh_token.length === 0) return null;
|
|
112753
|
-
return {
|
|
112754
|
-
auth_mode: "chatgpt",
|
|
112755
|
-
...typeof v.refresh_rejected_at === "string" ? { refresh_rejected_at: v.refresh_rejected_at } : {},
|
|
112756
|
-
tokens: {
|
|
112757
|
-
access_token: t.access_token,
|
|
112758
|
-
refresh_token: t.refresh_token,
|
|
112759
|
-
...typeof t.id_token === "string" ? { id_token: t.id_token } : {},
|
|
112760
|
-
...typeof t.account_id === "string" ? { account_id: t.account_id } : {}
|
|
112761
|
-
},
|
|
112762
|
-
...typeof v.last_refresh === "string" ? { last_refresh: v.last_refresh } : {}
|
|
112763
|
-
};
|
|
112764
|
-
}
|
|
112765
|
-
|
|
112766
|
-
// utils/codexHome.ts
|
|
112767
|
-
var CODEX_AUTH_ENV = "CODEX_AUTH_JSON";
|
|
112768
|
-
var PULLFROG_DATA_DIR = "/var/lib/pullfrog";
|
|
112769
|
-
function installCodexAuth() {
|
|
112770
|
-
const raw2 = process.env[CODEX_AUTH_ENV];
|
|
112771
|
-
if (!raw2) return null;
|
|
112772
|
-
const body = parseCodexAuthBody(raw2);
|
|
112773
|
-
if (!body) {
|
|
112774
|
-
log.warning(`\xBB ${CODEX_AUTH_ENV} present but malformed; ignoring`);
|
|
112775
|
-
return null;
|
|
112776
|
-
}
|
|
112777
|
-
const expiresMs = decodeJwtExpMs(body.tokens.access_token) ?? 0;
|
|
112778
|
-
const xdgDataHome = resolveDataHome();
|
|
112779
|
-
const opencodeDir = join5(xdgDataHome, "opencode");
|
|
112780
|
-
const authPath = join5(opencodeDir, "auth.json");
|
|
112781
|
-
const opencodeAuth = {
|
|
112782
|
-
openai: {
|
|
112783
|
-
type: "oauth",
|
|
112784
|
-
refresh: body.tokens.refresh_token,
|
|
112785
|
-
access: body.tokens.access_token,
|
|
112786
|
-
expires: expiresMs,
|
|
112787
|
-
...body.tokens.account_id ? { accountId: body.tokens.account_id } : {}
|
|
112788
|
-
}
|
|
112789
|
-
};
|
|
112790
|
-
mkdirSync5(opencodeDir, { recursive: true });
|
|
112791
|
-
writeFileSync4(authPath, `${JSON.stringify(opencodeAuth, null, 2)}
|
|
112792
|
-
`, { mode: 384 });
|
|
112793
|
-
process.env.XDG_DATA_HOME = xdgDataHome;
|
|
112794
|
-
log.info(`\xBB installed Codex auth at ${authPath}`);
|
|
112795
|
-
return {
|
|
112796
|
-
authPath,
|
|
112797
|
-
xdgDataHome,
|
|
112798
|
-
originalRefresh: body.tokens.refresh_token
|
|
112799
|
-
};
|
|
112800
|
-
}
|
|
112801
|
-
function resolveDataHome() {
|
|
112802
|
-
if (process.env.CI !== "true") return join5(homedir2(), ".local", "share");
|
|
112803
|
-
bootstrapPullfrogDataDir();
|
|
112804
|
-
return PULLFROG_DATA_DIR;
|
|
112805
|
-
}
|
|
112806
|
-
function bootstrapPullfrogDataDir() {
|
|
112807
|
-
const user = userInfo().username;
|
|
112808
|
-
let primaryGroup;
|
|
112809
|
-
try {
|
|
112810
|
-
primaryGroup = execFileSync3("id", ["-gn", user], { stdio: "pipe", encoding: "utf-8" }).trim();
|
|
112811
|
-
} catch {
|
|
112812
|
-
primaryGroup = user;
|
|
112813
|
-
}
|
|
112814
|
-
try {
|
|
112815
|
-
execFileSync3("sudo", ["-n", "mkdir", "-p", PULLFROG_DATA_DIR], { stdio: "pipe" });
|
|
112816
|
-
execFileSync3("sudo", ["-n", "chown", `${user}:${primaryGroup}`, PULLFROG_DATA_DIR], {
|
|
112817
|
-
stdio: "pipe"
|
|
112818
|
-
});
|
|
112819
|
-
execFileSync3("sudo", ["-n", "chmod", "700", PULLFROG_DATA_DIR], { stdio: "pipe" });
|
|
112820
|
-
} catch (err) {
|
|
112821
|
-
throw new Error(
|
|
112822
|
-
`failed to bootstrap ${PULLFROG_DATA_DIR} (required for codex auth in CI): ${err instanceof Error ? err.message : String(err)}. the MCP shell's mount-namespace sandbox cannot protect the auth file when it lives under $HOME, and silently falling back would contradict the "three independent layers" claim in wiki/codex-auth.md. passwordless sudo is required for codex auth on this runner \u2014 either configure it, or remove CODEX_AUTH_JSON from the run.`
|
|
112823
|
-
);
|
|
112824
|
-
}
|
|
112825
|
-
}
|
|
112826
|
-
|
|
112827
113523
|
// utils/worktree.ts
|
|
112828
113524
|
async function dirtyTrackedPaths(params = {}) {
|
|
112829
113525
|
const result = await spawn({
|
|
@@ -113091,6 +113787,30 @@ function openAICompatibleProvider(model) {
|
|
|
113091
113787
|
}
|
|
113092
113788
|
};
|
|
113093
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
|
+
}
|
|
113094
113814
|
var KIMI_ENFORCERLESS_PROVIDERS = ["siliconflow", "together"];
|
|
113095
113815
|
function kimiOpenRouterProviderOverrides() {
|
|
113096
113816
|
return Object.fromEntries(
|
|
@@ -113182,7 +113902,8 @@ function buildSecurityConfig(ctx, model) {
|
|
|
113182
113902
|
// see opencodeShared.ts.
|
|
113183
113903
|
provider: {
|
|
113184
113904
|
openrouter: { models: kimiOpenRouterProviderOverrides() },
|
|
113185
|
-
...openAICompatibleProvider(model)
|
|
113905
|
+
...openAICompatibleProvider(model),
|
|
113906
|
+
...azureProvider(model)
|
|
113186
113907
|
}
|
|
113187
113908
|
};
|
|
113188
113909
|
if (model) {
|
|
@@ -113350,7 +114071,7 @@ function isModelOutput(ctx, part) {
|
|
|
113350
114071
|
}
|
|
113351
114072
|
async function dispatchEvent(ctx, event) {
|
|
113352
114073
|
if (event.type === "message.part.updated") {
|
|
113353
|
-
ctx.lastEventAt =
|
|
114074
|
+
ctx.lastEventAt = performance7.now();
|
|
113354
114075
|
if (isModelOutput(ctx, event.properties.part)) ctx.sawModelOutput = true;
|
|
113355
114076
|
await onPartUpdated(ctx, event.properties.part);
|
|
113356
114077
|
return;
|
|
@@ -113417,7 +114138,7 @@ async function onToolPart(ctx, part, label, isOrchestrator) {
|
|
|
113417
114138
|
if (toolName === "task" && status === "running" && isOrchestrator && !ctx.taskDispatchByCallID.has(toolId)) {
|
|
113418
114139
|
const input = part.state.input ?? {};
|
|
113419
114140
|
const dispatched = ctx.labeler.recordTaskDispatch(input);
|
|
113420
|
-
ctx.taskDispatchByCallID.set(toolId, { label: dispatched, startedAt:
|
|
114141
|
+
ctx.taskDispatchByCallID.set(toolId, { label: dispatched, startedAt: performance7.now() });
|
|
113421
114142
|
log.info(
|
|
113422
114143
|
`\xBB dispatching subagent: ${dispatched}` + (input.subagent_type ? ` (subagent_type=${input.subagent_type})` : "")
|
|
113423
114144
|
);
|
|
@@ -113447,7 +114168,7 @@ function processTerminalToolPart(ctx, part, label, isOrchestrator) {
|
|
|
113447
114168
|
if (toolName === "task") {
|
|
113448
114169
|
const dispatch = ctx.taskDispatchByCallID.get(toolId);
|
|
113449
114170
|
if (dispatch) {
|
|
113450
|
-
const dur = ((
|
|
114171
|
+
const dur = ((performance7.now() - dispatch.startedAt) / 1e3).toFixed(1);
|
|
113451
114172
|
const outputStr = state.status === "completed" ? state.output : "";
|
|
113452
114173
|
const preview = typeof outputStr === "string" && outputStr.length > 120 ? `${outputStr.slice(0, 120)}\u2026` : outputStr;
|
|
113453
114174
|
log.info(
|
|
@@ -113490,7 +114211,7 @@ function withLabel(label, message) {
|
|
|
113490
114211
|
return label === ORCHESTRATOR_LABEL ? message : formatWithLabel(label, message);
|
|
113491
114212
|
}
|
|
113492
114213
|
async function runPromptTurn(ctx, params) {
|
|
113493
|
-
const start =
|
|
114214
|
+
const start = performance7.now();
|
|
113494
114215
|
const turnStartMs = Date.now();
|
|
113495
114216
|
ctx.currentTurn = newTurn();
|
|
113496
114217
|
const turn = ctx.currentTurn;
|
|
@@ -113524,7 +114245,7 @@ async function runPromptTurn(ctx, params) {
|
|
|
113524
114245
|
} catch (err) {
|
|
113525
114246
|
networkError = err instanceof Error ? err.message : String(err);
|
|
113526
114247
|
}
|
|
113527
|
-
const durationMs =
|
|
114248
|
+
const durationMs = performance7.now() - start;
|
|
113528
114249
|
const aggregatedUsage = await aggregateTurnUsage(ctx, turnStartMs);
|
|
113529
114250
|
const usage = aggregatedUsage ?? buildUsage(turn, assistant);
|
|
113530
114251
|
const finalText = turn.finalText || extractTextFromParts(returnedParts) || "";
|
|
@@ -113678,7 +114399,7 @@ function startInnerActivityWatchdog(params) {
|
|
|
113678
114399
|
let fired = false;
|
|
113679
114400
|
const id = setInterval(() => {
|
|
113680
114401
|
if (fired) return;
|
|
113681
|
-
const idleMs =
|
|
114402
|
+
const idleMs = performance7.now() - params.ctx.lastEventAt;
|
|
113682
114403
|
const budgetMs = params.ctx.sawModelOutput ? params.timeoutMs : AGENT_FIRST_EVENT_TIMEOUT_MS;
|
|
113683
114404
|
if (idleMs <= budgetMs) return;
|
|
113684
114405
|
fired = true;
|
|
@@ -113717,12 +114438,12 @@ var opencode = agent({
|
|
|
113717
114438
|
const model = vertexModel ?? (isBedrockRoute ? `amazon-bedrock/${rawModel}` : rawModel);
|
|
113718
114439
|
const homeEnv = {
|
|
113719
114440
|
HOME: ctx.tmpdir,
|
|
113720
|
-
XDG_CONFIG_HOME:
|
|
114441
|
+
XDG_CONFIG_HOME: join7(ctx.tmpdir, ".config")
|
|
113721
114442
|
};
|
|
113722
|
-
const opencodePluginDir =
|
|
113723
|
-
|
|
113724
|
-
|
|
113725
|
-
|
|
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),
|
|
113726
114447
|
buildOpencodeSubagentGateSource(ctx.subagentDeniedTools)
|
|
113727
114448
|
);
|
|
113728
114449
|
const agentBrowserVersion = getDevDependencyVersion("agent-browser");
|
|
@@ -113753,12 +114474,13 @@ var opencode = agent({
|
|
|
113753
114474
|
if (codexAuth) {
|
|
113754
114475
|
env2.XDG_DATA_HOME = codexAuth.xdgDataHome;
|
|
113755
114476
|
delete env2.OPENAI_API_KEY;
|
|
113756
|
-
|
|
114477
|
+
core3.saveState(
|
|
113757
114478
|
"codex_writeback",
|
|
113758
114479
|
JSON.stringify({
|
|
113759
114480
|
apiToken: ctx.apiToken,
|
|
113760
114481
|
authPath: codexAuth.authPath,
|
|
113761
|
-
originalRefresh: codexAuth.originalRefresh
|
|
114482
|
+
originalRefresh: codexAuth.originalRefresh,
|
|
114483
|
+
originalIdToken: codexAuth.originalIdToken
|
|
113762
114484
|
})
|
|
113763
114485
|
);
|
|
113764
114486
|
}
|
|
@@ -113812,7 +114534,7 @@ var opencode = agent({
|
|
|
113812
114534
|
onToolUse: ctx.onToolUse,
|
|
113813
114535
|
currentTurn: null,
|
|
113814
114536
|
eventCount: 0,
|
|
113815
|
-
lastEventAt:
|
|
114537
|
+
lastEventAt: performance7.now(),
|
|
113816
114538
|
sawModelOutput: false,
|
|
113817
114539
|
promptMessageID: void 0,
|
|
113818
114540
|
taskDispatchByCallID: /* @__PURE__ */ new Map(),
|
|
@@ -113922,7 +114644,7 @@ async function runTurnGuarded(ctx, fn2) {
|
|
|
113922
114644
|
}
|
|
113923
114645
|
|
|
113924
114646
|
// agents/index.ts
|
|
113925
|
-
var agents = { claude, opencode };
|
|
114647
|
+
var agents = { claude, codex, opencode };
|
|
113926
114648
|
|
|
113927
114649
|
// node_modules/.pnpm/@ark+util@0.56.0/node_modules/@ark/util/out/arrays.js
|
|
113928
114650
|
var liftArray = (data) => Array.isArray(data) ? data : [data];
|
|
@@ -134254,7 +134976,7 @@ var require_core$2 = /* @__PURE__ */ __commonJSMin(((exports) => {
|
|
|
134254
134976
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
134255
134977
|
const id_1 = require_id2();
|
|
134256
134978
|
const ref_1 = require_ref2();
|
|
134257
|
-
const
|
|
134979
|
+
const core11 = [
|
|
134258
134980
|
"$schema",
|
|
134259
134981
|
"$id",
|
|
134260
134982
|
"$defs",
|
|
@@ -134264,7 +134986,7 @@ var require_core$2 = /* @__PURE__ */ __commonJSMin(((exports) => {
|
|
|
134264
134986
|
id_1.default,
|
|
134265
134987
|
ref_1.default
|
|
134266
134988
|
];
|
|
134267
|
-
exports.default =
|
|
134989
|
+
exports.default = core11;
|
|
134268
134990
|
}));
|
|
134269
134991
|
var require_limitNumber2 = /* @__PURE__ */ __commonJSMin(((exports) => {
|
|
134270
134992
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
@@ -136402,7 +137124,7 @@ var require_json_schema_2019_09 = /* @__PURE__ */ __commonJSMin(((exports) => {
|
|
|
136402
137124
|
const metaSchema = require_schema$1();
|
|
136403
137125
|
const applicator = require_applicator$1();
|
|
136404
137126
|
const content = require_content$1();
|
|
136405
|
-
const
|
|
137127
|
+
const core11 = require_core$1();
|
|
136406
137128
|
const format2 = require_format3();
|
|
136407
137129
|
const metadata = require_meta_data$1();
|
|
136408
137130
|
const validation = require_validation$1();
|
|
@@ -136412,7 +137134,7 @@ var require_json_schema_2019_09 = /* @__PURE__ */ __commonJSMin(((exports) => {
|
|
|
136412
137134
|
metaSchema,
|
|
136413
137135
|
applicator,
|
|
136414
137136
|
content,
|
|
136415
|
-
|
|
137137
|
+
core11,
|
|
136416
137138
|
with$data(this, format2),
|
|
136417
137139
|
metadata,
|
|
136418
137140
|
with$data(this, validation)
|
|
@@ -136852,7 +137574,7 @@ var require_json_schema_2020_12 = /* @__PURE__ */ __commonJSMin(((exports) => {
|
|
|
136852
137574
|
const applicator = require_applicator2();
|
|
136853
137575
|
const unevaluated = require_unevaluated();
|
|
136854
137576
|
const content = require_content();
|
|
136855
|
-
const
|
|
137577
|
+
const core11 = require_core4();
|
|
136856
137578
|
const format2 = require_format_annotation();
|
|
136857
137579
|
const metadata = require_meta_data();
|
|
136858
137580
|
const validation = require_validation2();
|
|
@@ -136863,7 +137585,7 @@ var require_json_schema_2020_12 = /* @__PURE__ */ __commonJSMin(((exports) => {
|
|
|
136863
137585
|
applicator,
|
|
136864
137586
|
unevaluated,
|
|
136865
137587
|
content,
|
|
136866
|
-
|
|
137588
|
+
core11,
|
|
136867
137589
|
with$data(this, format2),
|
|
136868
137590
|
metadata,
|
|
136869
137591
|
with$data(this, validation)
|
|
@@ -148305,7 +149027,7 @@ var require_core$22 = /* @__PURE__ */ __commonJSMin2(((exports) => {
|
|
|
148305
149027
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
148306
149028
|
const id_1 = require_id3();
|
|
148307
149029
|
const ref_1 = require_ref3();
|
|
148308
|
-
const
|
|
149030
|
+
const core11 = [
|
|
148309
149031
|
"$schema",
|
|
148310
149032
|
"$id",
|
|
148311
149033
|
"$defs",
|
|
@@ -148315,7 +149037,7 @@ var require_core$22 = /* @__PURE__ */ __commonJSMin2(((exports) => {
|
|
|
148315
149037
|
id_1.default,
|
|
148316
149038
|
ref_1.default
|
|
148317
149039
|
];
|
|
148318
|
-
exports.default =
|
|
149040
|
+
exports.default = core11;
|
|
148319
149041
|
}));
|
|
148320
149042
|
var require_limitNumber3 = /* @__PURE__ */ __commonJSMin2(((exports) => {
|
|
148321
149043
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
@@ -150453,7 +151175,7 @@ var require_json_schema_2019_092 = /* @__PURE__ */ __commonJSMin2(((exports) =>
|
|
|
150453
151175
|
const metaSchema = require_schema$12();
|
|
150454
151176
|
const applicator = require_applicator$12();
|
|
150455
151177
|
const content = require_content$12();
|
|
150456
|
-
const
|
|
151178
|
+
const core11 = require_core$12();
|
|
150457
151179
|
const format2 = require_format4();
|
|
150458
151180
|
const metadata = require_meta_data$12();
|
|
150459
151181
|
const validation = require_validation$12();
|
|
@@ -150463,7 +151185,7 @@ var require_json_schema_2019_092 = /* @__PURE__ */ __commonJSMin2(((exports) =>
|
|
|
150463
151185
|
metaSchema,
|
|
150464
151186
|
applicator,
|
|
150465
151187
|
content,
|
|
150466
|
-
|
|
151188
|
+
core11,
|
|
150467
151189
|
with$data(this, format2),
|
|
150468
151190
|
metadata,
|
|
150469
151191
|
with$data(this, validation)
|
|
@@ -150903,7 +151625,7 @@ var require_json_schema_2020_122 = /* @__PURE__ */ __commonJSMin2(((exports) =>
|
|
|
150903
151625
|
const applicator = require_applicator3();
|
|
150904
151626
|
const unevaluated = require_unevaluated2();
|
|
150905
151627
|
const content = require_content2();
|
|
150906
|
-
const
|
|
151628
|
+
const core11 = require_core5();
|
|
150907
151629
|
const format2 = require_format_annotation2();
|
|
150908
151630
|
const metadata = require_meta_data2();
|
|
150909
151631
|
const validation = require_validation3();
|
|
@@ -150914,7 +151636,7 @@ var require_json_schema_2020_122 = /* @__PURE__ */ __commonJSMin2(((exports) =>
|
|
|
150914
151636
|
applicator,
|
|
150915
151637
|
unevaluated,
|
|
150916
151638
|
content,
|
|
150917
|
-
|
|
151639
|
+
core11,
|
|
150918
151640
|
with$data(this, format2),
|
|
150919
151641
|
metadata,
|
|
150920
151642
|
with$data(this, validation)
|
|
@@ -170875,123 +171597,8 @@ var pbkdf2Async = promisify(pbkdf2);
|
|
|
170875
171597
|
|
|
170876
171598
|
// utils/browser.ts
|
|
170877
171599
|
import { execFileSync as execFileSync4, spawnSync as spawnSync4 } from "node:child_process";
|
|
170878
|
-
import { existsSync as
|
|
171600
|
+
import { existsSync as existsSync5 } from "node:fs";
|
|
170879
171601
|
import { dirname as dirname2 } from "node:path";
|
|
170880
|
-
|
|
170881
|
-
// utils/secrets.ts
|
|
170882
|
-
var SENSITIVE_PATTERNS = [
|
|
170883
|
-
/_KEY$/i,
|
|
170884
|
-
/_SECRET$/i,
|
|
170885
|
-
/_TOKEN$/i,
|
|
170886
|
-
/_PASSWORD$/i,
|
|
170887
|
-
/_CREDENTIAL$/i
|
|
170888
|
-
];
|
|
170889
|
-
function isSensitiveEnvName(key) {
|
|
170890
|
-
return SENSITIVE_PATTERNS.some((p) => p.test(key));
|
|
170891
|
-
}
|
|
170892
|
-
var SAFE_ENV_PREFIXES = ["GITHUB_", "RUNNER_", "JAVA_HOME_", "GOROOT_"];
|
|
170893
|
-
var SAFE_ENV_NAMES = /* @__PURE__ */ new Set([
|
|
170894
|
-
// system
|
|
170895
|
-
"CI",
|
|
170896
|
-
"HOME",
|
|
170897
|
-
"LANG",
|
|
170898
|
-
"LOGNAME",
|
|
170899
|
-
"PATH",
|
|
170900
|
-
"SHELL",
|
|
170901
|
-
"SHLVL",
|
|
170902
|
-
"TERM",
|
|
170903
|
-
"TMPDIR",
|
|
170904
|
-
"TZ",
|
|
170905
|
-
"USER",
|
|
170906
|
-
"XDG_CONFIG_HOME",
|
|
170907
|
-
"XDG_RUNTIME_DIR",
|
|
170908
|
-
"DEBIAN_FRONTEND",
|
|
170909
|
-
// runner image toolchain
|
|
170910
|
-
"ACCEPT_EULA",
|
|
170911
|
-
"AGENT_TOOLSDIRECTORY",
|
|
170912
|
-
"ANDROID_HOME",
|
|
170913
|
-
"ANDROID_NDK",
|
|
170914
|
-
"ANDROID_NDK_HOME",
|
|
170915
|
-
"ANDROID_NDK_LATEST_HOME",
|
|
170916
|
-
"ANDROID_NDK_ROOT",
|
|
170917
|
-
"ANDROID_SDK_ROOT",
|
|
170918
|
-
"ANT_HOME",
|
|
170919
|
-
"AZURE_EXTENSION_DIR",
|
|
170920
|
-
"BOOTSTRAP_HASKELL_NONINTERACTIVE",
|
|
170921
|
-
"CHROME_BIN",
|
|
170922
|
-
"CHROMEWEBDRIVER",
|
|
170923
|
-
"CONDA",
|
|
170924
|
-
"DOTNET_MULTILEVEL_LOOKUP",
|
|
170925
|
-
"DOTNET_NOLOGO",
|
|
170926
|
-
"DOTNET_SKIP_FIRST_TIME_EXPERIENCE",
|
|
170927
|
-
"EDGEWEBDRIVER",
|
|
170928
|
-
"GECKOWEBDRIVER",
|
|
170929
|
-
"GHCUP_INSTALL_BASE_PREFIX",
|
|
170930
|
-
"GRADLE_HOME",
|
|
170931
|
-
"JAVA_HOME",
|
|
170932
|
-
"HOMEBREW_CLEANUP_PERIODIC_FULL_DAYS",
|
|
170933
|
-
"HOMEBREW_NO_AUTO_UPDATE",
|
|
170934
|
-
"ImageOS",
|
|
170935
|
-
"ImageVersion",
|
|
170936
|
-
"NVM_DIR",
|
|
170937
|
-
"PIPX_BIN_DIR",
|
|
170938
|
-
"PIPX_HOME",
|
|
170939
|
-
"PSModulePath",
|
|
170940
|
-
"SELENIUM_JAR_PATH",
|
|
170941
|
-
"SGX_AESM_ADDR",
|
|
170942
|
-
"SWIFT_PATH",
|
|
170943
|
-
"VCPKG_INSTALLATION_ROOT"
|
|
170944
|
-
]);
|
|
170945
|
-
var _userAllowlist = null;
|
|
170946
|
-
function setEnvAllowlist(raw2) {
|
|
170947
|
-
const names = raw2.split("\n").map((line) => line.trim()).filter(Boolean);
|
|
170948
|
-
_userAllowlist = new Set(names);
|
|
170949
|
-
}
|
|
170950
|
-
function isSafeEnvVar(key) {
|
|
170951
|
-
if (SAFE_ENV_NAMES.has(key)) return true;
|
|
170952
|
-
return SAFE_ENV_PREFIXES.some((p) => key.startsWith(p));
|
|
170953
|
-
}
|
|
170954
|
-
var WORKFLOW_COMMAND_ENV_NAMES = [
|
|
170955
|
-
"GITHUB_ENV",
|
|
170956
|
-
"GITHUB_PATH",
|
|
170957
|
-
"GITHUB_OUTPUT",
|
|
170958
|
-
"GITHUB_STATE",
|
|
170959
|
-
"GITHUB_STEP_SUMMARY",
|
|
170960
|
-
// dropping the five paths above but keeping RUNNER_TEMP would be theatre:
|
|
170961
|
-
// the file-command files live in `$RUNNER_TEMP/_runner_file_commands/`, so the
|
|
170962
|
-
// directory is one `ls` away. nothing in the install path reads it (only our
|
|
170963
|
-
// own leak-surface wipe in `setup.ts`, which runs in the parent), and package
|
|
170964
|
-
// managers use TMPDIR.
|
|
170965
|
-
"RUNNER_TEMP"
|
|
170966
|
-
];
|
|
170967
|
-
function filterEnvForUntrustedCode() {
|
|
170968
|
-
const env2 = filterEnv();
|
|
170969
|
-
for (const name of WORKFLOW_COMMAND_ENV_NAMES) delete env2[name];
|
|
170970
|
-
return env2;
|
|
170971
|
-
}
|
|
170972
|
-
function filterEnv() {
|
|
170973
|
-
const filtered = {};
|
|
170974
|
-
for (const [key, value2] of Object.entries(process.env)) {
|
|
170975
|
-
if (value2 === void 0) continue;
|
|
170976
|
-
const userAllowed = _userAllowlist?.has(key) ?? false;
|
|
170977
|
-
if (isSensitiveEnvName(key) && !userAllowed) continue;
|
|
170978
|
-
if (isSafeEnvVar(key) || userAllowed) {
|
|
170979
|
-
filtered[key] = value2;
|
|
170980
|
-
}
|
|
170981
|
-
}
|
|
170982
|
-
return filtered;
|
|
170983
|
-
}
|
|
170984
|
-
function resolveEnv(mode) {
|
|
170985
|
-
if (mode === "inherit") {
|
|
170986
|
-
return process.env;
|
|
170987
|
-
}
|
|
170988
|
-
if (mode === "restricted" || mode === void 0) {
|
|
170989
|
-
return filterEnv();
|
|
170990
|
-
}
|
|
170991
|
-
return { ...filterEnv(), ...mode };
|
|
170992
|
-
}
|
|
170993
|
-
|
|
170994
|
-
// utils/browser.ts
|
|
170995
171602
|
var CHROME_PATHS = ["/usr/bin/google-chrome-stable"];
|
|
170996
171603
|
var systemChromePath;
|
|
170997
171604
|
function findSystemChromePath() {
|
|
@@ -170999,7 +171606,7 @@ function findSystemChromePath() {
|
|
|
170999
171606
|
return systemChromePath || void 0;
|
|
171000
171607
|
}
|
|
171001
171608
|
for (const p of CHROME_PATHS) {
|
|
171002
|
-
if (
|
|
171609
|
+
if (existsSync5(p)) {
|
|
171003
171610
|
systemChromePath = p;
|
|
171004
171611
|
log.info(`found system chrome: ${p}`);
|
|
171005
171612
|
return p;
|
|
@@ -171083,8 +171690,8 @@ function closeBrowserDaemon(toolState) {
|
|
|
171083
171690
|
|
|
171084
171691
|
// mcp/checkout.ts
|
|
171085
171692
|
import { createHash as createHash2 } from "node:crypto";
|
|
171086
|
-
import { statSync, unlinkSync as unlinkSync3, writeFileSync as
|
|
171087
|
-
import { join as
|
|
171693
|
+
import { statSync, unlinkSync as unlinkSync3, writeFileSync as writeFileSync9 } from "node:fs";
|
|
171694
|
+
import { join as join14 } from "node:path";
|
|
171088
171695
|
|
|
171089
171696
|
// node_modules/.pnpm/@ark+schema@0.56.0/node_modules/@ark/schema/out/shared/errors.js
|
|
171090
171697
|
var ArkError = class _ArkError extends CastableBase {
|
|
@@ -178556,8 +179163,8 @@ function ensureRepoState(toolState, init) {
|
|
|
178556
179163
|
|
|
178557
179164
|
// utils/changeImpact.ts
|
|
178558
179165
|
import { spawn as spawn2 } from "node:child_process";
|
|
178559
|
-
import { writeFileSync as
|
|
178560
|
-
import { join as
|
|
179166
|
+
import { writeFileSync as writeFileSync7 } from "node:fs";
|
|
179167
|
+
import { join as join8 } from "node:path";
|
|
178561
179168
|
var MAX_CANDIDATES = 24;
|
|
178562
179169
|
var MAX_ATOM_LENGTH = 128;
|
|
178563
179170
|
var MAX_ATOMS = 12;
|
|
@@ -178904,8 +179511,8 @@ async function createChangeImpactArtifact(ctx, params) {
|
|
|
178904
179511
|
headSha: params.headSha,
|
|
178905
179512
|
lookupError: lookup2.error
|
|
178906
179513
|
});
|
|
178907
|
-
const path4 =
|
|
178908
|
-
|
|
179514
|
+
const path4 = join8(ctx.tmpdir, `pr-${params.pullNumber}-${params.headSha.slice(0, 7)}-impact.md`);
|
|
179515
|
+
writeFileSync7(path4, artifact.content);
|
|
178909
179516
|
return {
|
|
178910
179517
|
path: path4,
|
|
178911
179518
|
candidateCount: candidates.selected.length,
|
|
@@ -179157,8 +179764,8 @@ function readNumber(params) {
|
|
|
179157
179764
|
// utils/gitAuth.ts
|
|
179158
179765
|
import { execSync } from "node:child_process";
|
|
179159
179766
|
import { createHash } from "node:crypto";
|
|
179160
|
-
import { readFileSync as
|
|
179161
|
-
import { join as
|
|
179767
|
+
import { readFileSync as readFileSync3, realpathSync, unlinkSync } from "node:fs";
|
|
179768
|
+
import { join as join9 } from "node:path";
|
|
179162
179769
|
|
|
179163
179770
|
// utils/shell.ts
|
|
179164
179771
|
import { spawnSync as spawnSync5 } from "node:child_process";
|
|
@@ -179219,7 +179826,7 @@ var TRANSIENT_AUTH_PATTERNS = [
|
|
|
179219
179826
|
];
|
|
179220
179827
|
var gitBinary;
|
|
179221
179828
|
function hashFile(path4) {
|
|
179222
|
-
return createHash("sha256").update(
|
|
179829
|
+
return createHash("sha256").update(readFileSync3(path4)).digest("hex");
|
|
179223
179830
|
}
|
|
179224
179831
|
function resolveGit() {
|
|
179225
179832
|
const whichPath = execSync("which git", { encoding: "utf-8" }).trim();
|
|
@@ -179248,7 +179855,7 @@ function resolveHooksDir(cwd, gitPath) {
|
|
|
179248
179855
|
cwd,
|
|
179249
179856
|
log: false
|
|
179250
179857
|
}).trim();
|
|
179251
|
-
const hooksDir =
|
|
179858
|
+
const hooksDir = join9(commonDir, "hooks");
|
|
179252
179859
|
hooksDirCache.set(cwd, hooksDir);
|
|
179253
179860
|
return hooksDir;
|
|
179254
179861
|
}
|
|
@@ -179555,7 +180162,7 @@ function postProcessRangeDiff(raw2, contextLines = 3) {
|
|
|
179555
180162
|
}
|
|
179556
180163
|
|
|
179557
180164
|
// yes/index.ts
|
|
179558
|
-
import { performance as
|
|
180165
|
+
import { performance as performance8 } from "node:perf_hooks";
|
|
179559
180166
|
import { setTimeout as sleep2 } from "node:timers/promises";
|
|
179560
180167
|
|
|
179561
180168
|
// node_modules/.pnpm/lru-cache@11.5.2/node_modules/lru-cache/dist/esm/node/index.min.js
|
|
@@ -180270,14 +180877,14 @@ function _op(fn2, options) {
|
|
|
180270
180877
|
`failed to create cache key${namePrefix ? ` for ${options.name}` : ""}: ${errorMessage}`
|
|
180271
180878
|
);
|
|
180272
180879
|
}
|
|
180273
|
-
const startTime =
|
|
180880
|
+
const startTime = performance8.now();
|
|
180274
180881
|
const timestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
180275
180882
|
log2.debug(`${namePrefix}[${timestamp}] cache lookup started`, { key });
|
|
180276
180883
|
if (shouldCache && lruCache) {
|
|
180277
180884
|
const cached4 = lruCache.get(key);
|
|
180278
180885
|
if (cached4 !== void 0) {
|
|
180279
180886
|
if (keyMap?.get(key) === void 0) keyMap?.set(key, input);
|
|
180280
|
-
const runtime =
|
|
180887
|
+
const runtime = performance8.now() - startTime;
|
|
180281
180888
|
log2.debug(`${namePrefix}[${timestamp}] cache hit (from lru)`, {
|
|
180282
180889
|
key,
|
|
180283
180890
|
runtime: `${Math.round(runtime)}ms`
|
|
@@ -180296,12 +180903,12 @@ function _op(fn2, options) {
|
|
|
180296
180903
|
defaultCacheMiss(key);
|
|
180297
180904
|
const retries = options.retries ?? [];
|
|
180298
180905
|
let lastError;
|
|
180299
|
-
const execStartTime =
|
|
180906
|
+
const execStartTime = performance8.now();
|
|
180300
180907
|
const promise2 = (async () => {
|
|
180301
180908
|
for (let attempt = 0; attempt <= retries.length; attempt++) {
|
|
180302
180909
|
try {
|
|
180303
180910
|
const result = acceptsCtx ? await fn2(input, ctx) : await fn2(input);
|
|
180304
|
-
const execRuntime2 =
|
|
180911
|
+
const execRuntime2 = performance8.now() - execStartTime;
|
|
180305
180912
|
const skip = options.skipCache ? options.skipCache(result) : false;
|
|
180306
180913
|
if (shouldCache && lruCache && result !== null && result !== void 0 && !skip) {
|
|
180307
180914
|
lruCache.set(key, result);
|
|
@@ -180354,7 +180961,7 @@ function _op(fn2, options) {
|
|
|
180354
180961
|
}
|
|
180355
180962
|
}
|
|
180356
180963
|
}
|
|
180357
|
-
const execRuntime =
|
|
180964
|
+
const execRuntime = performance8.now() - execStartTime;
|
|
180358
180965
|
log2.debug(`${namePrefix}[${timestamp}] function failed after all retries`, {
|
|
180359
180966
|
key,
|
|
180360
180967
|
runtime: `${Math.round(execRuntime)}ms`,
|
|
@@ -180419,13 +181026,13 @@ function _op(fn2, options) {
|
|
|
180419
181026
|
|
|
180420
181027
|
// mcp/git.ts
|
|
180421
181028
|
import { randomUUID as randomUUID3 } from "node:crypto";
|
|
180422
|
-
import { writeFileSync as
|
|
180423
|
-
import { join as
|
|
181029
|
+
import { writeFileSync as writeFileSync8 } from "node:fs";
|
|
181030
|
+
import { join as join12 } from "node:path";
|
|
180424
181031
|
|
|
180425
181032
|
// utils/apiCommit.ts
|
|
180426
181033
|
import { execFileSync as execFileSync5 } from "node:child_process";
|
|
180427
181034
|
import { lstat, readlink } from "node:fs/promises";
|
|
180428
|
-
import { join as
|
|
181035
|
+
import { join as join10 } from "node:path";
|
|
180429
181036
|
var GITHUB_API = "https://api.github.com";
|
|
180430
181037
|
var MAX_BLOB_BYTES = 30 * 1024 * 1024;
|
|
180431
181038
|
var BLOB_UPLOAD_CONCURRENCY = 8;
|
|
@@ -180490,7 +181097,7 @@ async function assertApiCommittable(files) {
|
|
|
180490
181097
|
const root = getRepoRoot();
|
|
180491
181098
|
const attrs = $(
|
|
180492
181099
|
"git",
|
|
180493
|
-
["check-attr", "filter", "-z", "--", ...present.map((p) =>
|
|
181100
|
+
["check-attr", "filter", "-z", "--", ...present.map((p) => join10(root, p))],
|
|
180494
181101
|
{ log: false }
|
|
180495
181102
|
);
|
|
180496
181103
|
const parts = attrs.split("\0");
|
|
@@ -180502,7 +181109,7 @@ async function assertApiCommittable(files) {
|
|
|
180502
181109
|
}
|
|
180503
181110
|
}
|
|
180504
181111
|
for (const path4 of present) {
|
|
180505
|
-
const stat = await lstat(
|
|
181112
|
+
const stat = await lstat(join10(root, path4));
|
|
180506
181113
|
if (stat.isDirectory()) {
|
|
180507
181114
|
throw new Error(
|
|
180508
181115
|
`'${path4}' is a directory (nested repository or submodule?) \u2014 signed commits only support files and symlinks.`
|
|
@@ -180511,7 +181118,7 @@ async function assertApiCommittable(files) {
|
|
|
180511
181118
|
}
|
|
180512
181119
|
}
|
|
180513
181120
|
async function createBlobEntry(params) {
|
|
180514
|
-
const absPath =
|
|
181121
|
+
const absPath = join10(params.repoRoot, params.path);
|
|
180515
181122
|
const stat = await lstat(absPath);
|
|
180516
181123
|
if (stat.size > MAX_BLOB_BYTES) {
|
|
180517
181124
|
throw new Error(
|
|
@@ -180688,10 +181295,10 @@ this usually means your local branch has commits that were never pushed \u2014 s
|
|
|
180688
181295
|
}
|
|
180689
181296
|
|
|
180690
181297
|
// utils/github.ts
|
|
180691
|
-
var
|
|
181298
|
+
var core4 = __toESM(require_core(), 1);
|
|
180692
181299
|
import { createSign } from "node:crypto";
|
|
180693
181300
|
import { rename, writeFile } from "node:fs/promises";
|
|
180694
|
-
import { dirname as dirname3, join as
|
|
181301
|
+
import { dirname as dirname3, join as join11 } from "node:path";
|
|
180695
181302
|
|
|
180696
181303
|
// node_modules/.pnpm/@octokit+plugin-throttling@11.0.5_@octokit+core@7.0.7/node_modules/@octokit/plugin-throttling/dist-bundle/index.js
|
|
180697
181304
|
var import_light = __toESM(require_light(), 1);
|
|
@@ -184889,7 +185496,7 @@ async function fetchIdTokenFromStash(creds) {
|
|
|
184889
185496
|
throw new Error("ID token response has no value field");
|
|
184890
185497
|
}
|
|
184891
185498
|
if (isGitHubActions) {
|
|
184892
|
-
|
|
185499
|
+
core4.setSecret(body.value);
|
|
184893
185500
|
}
|
|
184894
185501
|
return body.value;
|
|
184895
185502
|
}
|
|
@@ -184902,7 +185509,7 @@ function translateIdTokenError(error52) {
|
|
|
184902
185509
|
var mintIdToken = op(
|
|
184903
185510
|
async () => {
|
|
184904
185511
|
try {
|
|
184905
|
-
return await
|
|
185512
|
+
return await core4.getIDToken(OIDC_AUDIENCE);
|
|
184906
185513
|
} catch (error52) {
|
|
184907
185514
|
throw translateIdTokenError(error52);
|
|
184908
185515
|
}
|
|
@@ -185111,7 +185718,7 @@ function getGitHubUsageSummary() {
|
|
|
185111
185718
|
}
|
|
185112
185719
|
async function writeGitHubUsageSummaryToFile(path4) {
|
|
185113
185720
|
const summary2 = getGitHubUsageSummary();
|
|
185114
|
-
const tmpPath =
|
|
185721
|
+
const tmpPath = join11(dirname3(path4), `.usage-summary-${process.pid}.tmp`);
|
|
185115
185722
|
await writeFile(tmpPath, JSON.stringify(summary2));
|
|
185116
185723
|
await rename(tmpPath, path4);
|
|
185117
185724
|
}
|
|
@@ -186263,8 +186870,8 @@ function countAhead(head, base, cwd) {
|
|
|
186263
186870
|
function spillGitOutput(params) {
|
|
186264
186871
|
const tempDir = process.env.PULLFROG_TEMP_DIR;
|
|
186265
186872
|
if (!tempDir) throw new Error("PULLFROG_TEMP_DIR not set");
|
|
186266
|
-
const outputPath =
|
|
186267
|
-
|
|
186873
|
+
const outputPath = join12(tempDir, `git-${params.command}-${randomUUID3().slice(0, 8)}.txt`);
|
|
186874
|
+
writeFileSync8(outputPath, params.output);
|
|
186268
186875
|
const previewByLines = params.output.split("\n").slice(0, OVERFLOW_PREVIEW_LINES).join("\n");
|
|
186269
186876
|
const preview = previewByLines.length <= OVERFLOW_PREVIEW_MAX_CHARS ? previewByLines : `${previewByLines.slice(0, OVERFLOW_PREVIEW_MAX_CHARS)}\u2026`;
|
|
186270
186877
|
log.info(
|
|
@@ -187778,9 +188385,9 @@ async function reportReviewNodeId(ctx, params) {
|
|
|
187778
188385
|
import { execFileSync as execFileSync6, execSync as execSync2 } from "node:child_process";
|
|
187779
188386
|
import { mkdtempSync, readdirSync, realpathSync as realpathSync2, unlinkSync as unlinkSync2 } from "node:fs";
|
|
187780
188387
|
import { tmpdir as tmpdir2 } from "node:os";
|
|
187781
|
-
import { join as
|
|
188388
|
+
import { join as join13 } from "node:path";
|
|
187782
188389
|
function createTempDirectory() {
|
|
187783
|
-
const sharedTempDir = mkdtempSync(
|
|
188390
|
+
const sharedTempDir = mkdtempSync(join13(tmpdir2(), "pullfrog-"));
|
|
187784
188391
|
process.env.PULLFROG_TEMP_DIR = sharedTempDir;
|
|
187785
188392
|
log.info(`\xBB created temp dir at ${sharedTempDir}`);
|
|
187786
188393
|
return sharedTempDir;
|
|
@@ -187825,13 +188432,13 @@ function wipeRunnerLeakSurface() {
|
|
|
187825
188432
|
return [];
|
|
187826
188433
|
}
|
|
187827
188434
|
};
|
|
187828
|
-
const fileCommandsDir =
|
|
188435
|
+
const fileCommandsDir = join13(runnerTemp, "_runner_file_commands");
|
|
187829
188436
|
for (const entry of listDir(fileCommandsDir)) {
|
|
187830
|
-
tryUnlink(
|
|
188437
|
+
tryUnlink(join13(fileCommandsDir, entry));
|
|
187831
188438
|
}
|
|
187832
188439
|
for (const entry of listDir(runnerTemp)) {
|
|
187833
188440
|
if (entry.endsWith(".sh") || /^git-credentials-.*\.config$/.test(entry)) {
|
|
187834
|
-
tryUnlink(
|
|
188441
|
+
tryUnlink(join13(runnerTemp, entry));
|
|
187835
188442
|
}
|
|
187836
188443
|
}
|
|
187837
188444
|
if (wiped.length > 0) {
|
|
@@ -188381,11 +188988,11 @@ function CheckoutPrTool(ctx) {
|
|
|
188381
188988
|
headSha: checkoutSha
|
|
188382
188989
|
});
|
|
188383
188990
|
if (incremental.status === "ok") {
|
|
188384
|
-
incrementalDiffPath =
|
|
188991
|
+
incrementalDiffPath = join14(
|
|
188385
188992
|
tempDir,
|
|
188386
188993
|
`pr-${pull_number}-${beforeShort}-${headShort}-incremental.diff`
|
|
188387
188994
|
);
|
|
188388
|
-
|
|
188995
|
+
writeFileSync9(incrementalDiffPath, incremental.diff);
|
|
188389
188996
|
log.info(
|
|
188390
188997
|
`\xBB incremental diff computed (${incremental.diff.length} bytes) \u2192 ${incrementalDiffPath}`
|
|
188391
188998
|
);
|
|
@@ -188398,8 +189005,8 @@ function CheckoutPrTool(ctx) {
|
|
|
188398
189005
|
const diffPreview = formatResult.content.split("\n").slice(0, 100).join("\n");
|
|
188399
189006
|
log.debug(`formatted diff preview (first 100 lines):
|
|
188400
189007
|
${diffPreview}`);
|
|
188401
|
-
const diffPath =
|
|
188402
|
-
|
|
189008
|
+
const diffPath = join14(tempDir, `pr-${pull_number}-${headShort}.diff`);
|
|
189009
|
+
writeFileSync9(diffPath, formatResult.content);
|
|
188403
189010
|
log.debug(`wrote diff to ${diffPath} (${formatResult.content.length} bytes)`);
|
|
188404
189011
|
primary.diffCoverage = createDiffCoverageState({
|
|
188405
189012
|
diffPath,
|
|
@@ -188556,8 +189163,8 @@ ${dirty}`
|
|
|
188556
189163
|
}
|
|
188557
189164
|
|
|
188558
189165
|
// mcp/checkSuite.ts
|
|
188559
|
-
import { mkdirSync as
|
|
188560
|
-
import { join as
|
|
189166
|
+
import { mkdirSync as mkdirSync8, writeFileSync as writeFileSync10 } from "node:fs";
|
|
189167
|
+
import { join as join15 } from "node:path";
|
|
188561
189168
|
var GetCheckSuiteLogs = type({
|
|
188562
189169
|
check_suite_id: type.number.describe("the id from check_suite.id")
|
|
188563
189170
|
});
|
|
@@ -188653,8 +189260,8 @@ function GetCheckSuiteLogsTool(ctx) {
|
|
|
188653
189260
|
if (!tempDir) {
|
|
188654
189261
|
throw new Error("PULLFROG_TEMP_DIR not set");
|
|
188655
189262
|
}
|
|
188656
|
-
const logsDir =
|
|
188657
|
-
|
|
189263
|
+
const logsDir = join15(tempDir, "ci-logs");
|
|
189264
|
+
mkdirSync8(logsDir, { recursive: true });
|
|
188658
189265
|
const jobResults = [];
|
|
188659
189266
|
for (const run of failedRuns) {
|
|
188660
189267
|
const jobs = await ctx.octokit.paginate(ctx.octokit.rest.actions.listJobsForWorkflowRun, {
|
|
@@ -188680,8 +189287,8 @@ function GetCheckSuiteLogsTool(ctx) {
|
|
|
188680
189287
|
);
|
|
188681
189288
|
}
|
|
188682
189289
|
const logsText = await logsResult.text();
|
|
188683
|
-
const logPath =
|
|
188684
|
-
|
|
189290
|
+
const logPath = join15(logsDir, `job-${job.id}.log`);
|
|
189291
|
+
writeFileSync10(logPath, logsText);
|
|
188685
189292
|
const analysis = analyzeLog(logsText, 80);
|
|
188686
189293
|
const failedSteps = job.steps?.filter((s) => s.conclusion === "failure").map((s) => `Step ${s.number}: ${s.name}`) ?? [];
|
|
188687
189294
|
jobResults.push({
|
|
@@ -188729,8 +189336,8 @@ function GetCheckSuiteLogsTool(ctx) {
|
|
|
188729
189336
|
}
|
|
188730
189337
|
|
|
188731
189338
|
// mcp/commitInfo.ts
|
|
188732
|
-
import { writeFileSync as
|
|
188733
|
-
import { join as
|
|
189339
|
+
import { writeFileSync as writeFileSync11 } from "node:fs";
|
|
189340
|
+
import { join as join16 } from "node:path";
|
|
188734
189341
|
var CommitInfo = type({
|
|
188735
189342
|
sha: type.string.describe("the commit SHA (full or abbreviated) to fetch")
|
|
188736
189343
|
});
|
|
@@ -188754,8 +189361,8 @@ function CommitInfoTool(ctx) {
|
|
|
188754
189361
|
"PULLFROG_TEMP_DIR not set - get_commit_info must run in pullfrog action context"
|
|
188755
189362
|
);
|
|
188756
189363
|
}
|
|
188757
|
-
const diffFile =
|
|
188758
|
-
|
|
189364
|
+
const diffFile = join16(tempDir, `commit-${sha.slice(0, 7)}.diff`);
|
|
189365
|
+
writeFileSync11(diffFile, formatResult.content);
|
|
188759
189366
|
log.debug(`wrote commit diff to ${diffFile} (${formatResult.content.length} bytes)`);
|
|
188760
189367
|
return {
|
|
188761
189368
|
sha: data.sha,
|
|
@@ -188888,17 +189495,17 @@ function ReopenCurrentTool(ctx) {
|
|
|
188888
189495
|
}
|
|
188889
189496
|
|
|
188890
189497
|
// prep/index.ts
|
|
188891
|
-
import { performance as
|
|
189498
|
+
import { performance as performance9 } from "node:perf_hooks";
|
|
188892
189499
|
|
|
188893
189500
|
// prep/installNodeDependencies.ts
|
|
188894
|
-
import { existsSync as
|
|
188895
|
-
import { join as
|
|
189501
|
+
import { existsSync as existsSync7 } from "node:fs";
|
|
189502
|
+
import { join as join18 } from "node:path";
|
|
188896
189503
|
|
|
188897
189504
|
// utils/packageManager.ts
|
|
188898
189505
|
var import_semver2 = __toESM(require_semver2(), 1);
|
|
188899
|
-
import { existsSync as
|
|
189506
|
+
import { existsSync as existsSync6 } from "node:fs";
|
|
188900
189507
|
import { mkdir, readFile as readFile2 } from "node:fs/promises";
|
|
188901
|
-
import { delimiter, join as
|
|
189508
|
+
import { delimiter, join as join17 } from "node:path";
|
|
188902
189509
|
var SUPPORTED_NAMES = ["npm", "pnpm", "yarn", "bun"];
|
|
188903
189510
|
var COREPACK_MANAGED = ["pnpm", "yarn"];
|
|
188904
189511
|
function isSupported(name) {
|
|
@@ -188936,8 +189543,8 @@ function parseDevEnginesField(field) {
|
|
|
188936
189543
|
};
|
|
188937
189544
|
}
|
|
188938
189545
|
async function resolvePackageManagerSpec(cwd) {
|
|
188939
|
-
const pkgPath =
|
|
188940
|
-
if (!
|
|
189546
|
+
const pkgPath = join17(cwd, "package.json");
|
|
189547
|
+
if (!existsSync6(pkgPath)) return null;
|
|
188941
189548
|
let pkg;
|
|
188942
189549
|
try {
|
|
188943
189550
|
pkg = JSON.parse(await readFile2(pkgPath, "utf-8"));
|
|
@@ -188995,7 +189602,7 @@ async function currentVersion(name) {
|
|
|
188995
189602
|
return result.stdout.trim();
|
|
188996
189603
|
}
|
|
188997
189604
|
function packageManagerBinDir(tmpdir3) {
|
|
188998
|
-
return
|
|
189605
|
+
return join17(tmpdir3, "pm-bin");
|
|
188999
189606
|
}
|
|
189000
189607
|
async function ensurePackageManager(params) {
|
|
189001
189608
|
const spec = params.spec;
|
|
@@ -189058,7 +189665,7 @@ async function installFallback(name, installSpec) {
|
|
|
189058
189665
|
});
|
|
189059
189666
|
if (result.exitCode !== 0) return result.stderr || `failed to install ${name}`;
|
|
189060
189667
|
if (name === "deno") {
|
|
189061
|
-
const denoPath =
|
|
189668
|
+
const denoPath = join17(process.env.HOME || "", ".deno", "bin");
|
|
189062
189669
|
process.env.PATH = `${denoPath}:${process.env.PATH}`;
|
|
189063
189670
|
}
|
|
189064
189671
|
log.info(`\xBB installed ${name}`);
|
|
@@ -189091,8 +189698,8 @@ async function isCommandAvailable2(command) {
|
|
|
189091
189698
|
var installNodeDependencies = {
|
|
189092
189699
|
name: "installNodeDependencies",
|
|
189093
189700
|
shouldRun: () => {
|
|
189094
|
-
const packageJsonPath =
|
|
189095
|
-
return
|
|
189701
|
+
const packageJsonPath = join18(process.cwd(), "package.json");
|
|
189702
|
+
return existsSync7(packageJsonPath);
|
|
189096
189703
|
},
|
|
189097
189704
|
run: async (options) => {
|
|
189098
189705
|
const declared = await resolvePackageManagerSpec(process.cwd());
|
|
@@ -189186,14 +189793,14 @@ ${errorMessage}`]
|
|
|
189186
189793
|
};
|
|
189187
189794
|
|
|
189188
189795
|
// prep/installPythonDependencies.ts
|
|
189189
|
-
import { existsSync as
|
|
189190
|
-
import { join as
|
|
189796
|
+
import { existsSync as existsSync8, readFileSync as readFileSync4 } from "node:fs";
|
|
189797
|
+
import { join as join19 } from "node:path";
|
|
189191
189798
|
function declaresBuildSystem(path4) {
|
|
189192
|
-
return /^\s*\[\s*build-system\s*\]/m.test(
|
|
189799
|
+
return /^\s*\[\s*build-system\s*\]/m.test(readFileSync4(path4, "utf8"));
|
|
189193
189800
|
}
|
|
189194
189801
|
function configApplies(config3, cwd) {
|
|
189195
|
-
const path4 =
|
|
189196
|
-
return
|
|
189802
|
+
const path4 = join19(cwd, config3.file);
|
|
189803
|
+
return existsSync8(path4) && (config3.applies?.(path4) ?? true);
|
|
189197
189804
|
}
|
|
189198
189805
|
var PYTHON_CONFIGS = [
|
|
189199
189806
|
{
|
|
@@ -189347,7 +189954,7 @@ var installPythonDependencies = {
|
|
|
189347
189954
|
var prepSteps = [installNodeDependencies, installPythonDependencies];
|
|
189348
189955
|
async function runPrepPhase(options) {
|
|
189349
189956
|
log.debug("\xBB starting prep phase...");
|
|
189350
|
-
const startTime =
|
|
189957
|
+
const startTime = performance9.now();
|
|
189351
189958
|
const results = [];
|
|
189352
189959
|
const preDirty = await dirtyTrackedPaths();
|
|
189353
189960
|
try {
|
|
@@ -189369,7 +189976,7 @@ async function runPrepPhase(options) {
|
|
|
189369
189976
|
} finally {
|
|
189370
189977
|
await restoreDirtiedSince({ before: preDirty, actor: "prep" });
|
|
189371
189978
|
}
|
|
189372
|
-
const totalDurationMs =
|
|
189979
|
+
const totalDurationMs = performance9.now() - startTime;
|
|
189373
189980
|
log.debug(`\xBB prep phase completed (${Math.round(totalDurationMs)}ms)`);
|
|
189374
189981
|
return results;
|
|
189375
189982
|
}
|
|
@@ -189514,15 +190121,15 @@ function AwaitDependencyInstallationTool(ctx) {
|
|
|
189514
190121
|
}
|
|
189515
190122
|
|
|
189516
190123
|
// mcp/gh.ts
|
|
189517
|
-
import { mkdirSync as
|
|
189518
|
-
import { join as
|
|
190124
|
+
import { mkdirSync as mkdirSync9 } from "node:fs";
|
|
190125
|
+
import { join as join21 } from "node:path";
|
|
189519
190126
|
|
|
189520
190127
|
// mcp/shell.ts
|
|
189521
190128
|
import { spawn as spawn3, spawnSync as spawnSync6 } from "node:child_process";
|
|
189522
190129
|
import { randomUUID as randomUUID4 } from "node:crypto";
|
|
189523
|
-
import { closeSync, openSync, writeFileSync as
|
|
190130
|
+
import { closeSync, openSync, writeFileSync as writeFileSync12 } from "node:fs";
|
|
189524
190131
|
import { userInfo as userInfo2 } from "node:os";
|
|
189525
|
-
import { join as
|
|
190132
|
+
import { join as join20 } from "node:path";
|
|
189526
190133
|
import { setTimeout as sleep3 } from "node:timers/promises";
|
|
189527
190134
|
var ShellParams = type({
|
|
189528
190135
|
command: "string",
|
|
@@ -189785,8 +190392,8 @@ ${spawnError}` : spawnError;
|
|
|
189785
190392
|
}
|
|
189786
190393
|
function capOutput(output) {
|
|
189787
190394
|
if (output.length <= MAX_OUTPUT_CHARS) return output;
|
|
189788
|
-
const fullPath =
|
|
189789
|
-
|
|
190395
|
+
const fullPath = join20(getTempDir(), `shell-${randomUUID4().slice(0, 8)}.log`);
|
|
190396
|
+
writeFileSync12(fullPath, output);
|
|
189790
190397
|
const elided = output.length - MAX_OUTPUT_CHARS;
|
|
189791
190398
|
return `... [${elided} chars truncated; full output saved to ${fullPath}] ...
|
|
189792
190399
|
${output.slice(-MAX_OUTPUT_CHARS)}`;
|
|
@@ -189840,8 +190447,8 @@ Do NOT use this tool for git commands \u2014 use the dedicated git tools instead
|
|
|
189840
190447
|
if (params.background) {
|
|
189841
190448
|
const tempDir = getTempDir();
|
|
189842
190449
|
const handle = `bg-${randomUUID4().slice(0, 8)}`;
|
|
189843
|
-
const outputPath =
|
|
189844
|
-
const pidPath =
|
|
190450
|
+
const outputPath = join20(tempDir, `${handle}.log`);
|
|
190451
|
+
const pidPath = join20(tempDir, `${handle}.pid`);
|
|
189845
190452
|
const logFd = openSync(outputPath, "a");
|
|
189846
190453
|
let proc;
|
|
189847
190454
|
try {
|
|
@@ -189858,7 +190465,7 @@ Do NOT use this tool for git commands \u2014 use the dedicated git tools instead
|
|
|
189858
190465
|
throw new Error("failed to start background process");
|
|
189859
190466
|
}
|
|
189860
190467
|
proc.unref();
|
|
189861
|
-
|
|
190468
|
+
writeFileSync12(pidPath, `${proc.pid}
|
|
189862
190469
|
`);
|
|
189863
190470
|
ctx.toolState.backgroundProcesses.set(handle, { pid: proc.pid, outputPath, pidPath });
|
|
189864
190471
|
return {
|
|
@@ -189938,8 +190545,8 @@ function GhTool(ctx) {
|
|
|
189938
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.`,
|
|
189939
190546
|
parameters: GhParams,
|
|
189940
190547
|
execute: execute(async (params) => {
|
|
189941
|
-
const configDir =
|
|
189942
|
-
|
|
190548
|
+
const configDir = join21(ctx.tmpdir, "gh-config");
|
|
190549
|
+
mkdirSync9(configDir, { recursive: true });
|
|
189943
190550
|
const command = ["gh", ...params.args.map(shellQuote)].join(" ");
|
|
189944
190551
|
const result = await runSandboxed({
|
|
189945
190552
|
command,
|
|
@@ -190662,8 +191269,8 @@ function PullRequestInfoTool(ctx) {
|
|
|
190662
191269
|
}
|
|
190663
191270
|
|
|
190664
191271
|
// mcp/reviewComments.ts
|
|
190665
|
-
import { writeFileSync as
|
|
190666
|
-
import { join as
|
|
191272
|
+
import { writeFileSync as writeFileSync13 } from "node:fs";
|
|
191273
|
+
import { join as join22 } from "node:path";
|
|
190667
191274
|
var COMMENTS_PER_THREAD = 50;
|
|
190668
191275
|
var REVIEW_THREADS_QUERY = `
|
|
190669
191276
|
query ($owner: String!, $name: String!, $prNumber: Int!, $cursor: String) {
|
|
@@ -191136,8 +191743,8 @@ function GetReviewCommentsTool(ctx) {
|
|
|
191136
191743
|
throw new Error("PULLFROG_TEMP_DIR not set");
|
|
191137
191744
|
}
|
|
191138
191745
|
const filename = `review-${params.review_id}-threads.md`;
|
|
191139
|
-
const commentsPath =
|
|
191140
|
-
|
|
191746
|
+
const commentsPath = join22(tempDir, filename);
|
|
191747
|
+
writeFileSync13(commentsPath, formatted.content);
|
|
191141
191748
|
log.debug(`wrote ${threadBlocks.length} threads to ${commentsPath}`);
|
|
191142
191749
|
return {
|
|
191143
191750
|
review_id: params.review_id,
|
|
@@ -191470,8 +192077,8 @@ function UploadFileTool(ctx) {
|
|
|
191470
192077
|
}
|
|
191471
192078
|
|
|
191472
192079
|
// mcp/xrepo.ts
|
|
191473
|
-
import { mkdirSync as
|
|
191474
|
-
import { join as
|
|
192080
|
+
import { mkdirSync as mkdirSync10, rmSync as rmSync2 } from "node:fs";
|
|
192081
|
+
import { join as join23 } from "node:path";
|
|
191475
192082
|
function assertValidRepoName(name) {
|
|
191476
192083
|
if (!/^[A-Za-z0-9._-]+$/.test(name) || name.startsWith("-") || name.includes("..")) {
|
|
191477
192084
|
throw new Error(
|
|
@@ -191551,8 +192158,8 @@ function CheckoutRepoTool(ctx) {
|
|
|
191551
192158
|
return { path: existing.dir, access: existing.access, note: "already checked out." };
|
|
191552
192159
|
}
|
|
191553
192160
|
const access = accessFor(ctx, repo);
|
|
191554
|
-
const dir =
|
|
191555
|
-
|
|
192161
|
+
const dir = join23(ctx.tmpdir, "xrepo", repo);
|
|
192162
|
+
mkdirSync10(dir, { recursive: true });
|
|
191556
192163
|
const state = ensureRepoState(ctx.toolState, { owner, name: repo, dir, access });
|
|
191557
192164
|
const rc = resolveRepoCtx(ctx, repo);
|
|
191558
192165
|
try {
|
|
@@ -191800,6 +192407,9 @@ function hasEnvVar2(name) {
|
|
|
191800
192407
|
function hasClaudeCodeAuth() {
|
|
191801
192408
|
return hasEnvVar2("CLAUDE_CODE_OAUTH_TOKEN") || hasEnvVar2("ANTHROPIC_API_KEY");
|
|
191802
192409
|
}
|
|
192410
|
+
function hasCodexAuth() {
|
|
192411
|
+
return hasEnvVar2("CODEX_AUTH_JSON") || hasEnvVar2("OPENAI_API_KEY");
|
|
192412
|
+
}
|
|
191803
192413
|
function hasBedrockAuth() {
|
|
191804
192414
|
return hasEnvVar2("AWS_BEARER_TOKEN_BEDROCK") || hasEnvVar2("AWS_ACCESS_KEY_ID") && hasEnvVar2("AWS_SECRET_ACCESS_KEY");
|
|
191805
192415
|
}
|
|
@@ -191826,6 +192436,15 @@ function resolveSlug(slug2) {
|
|
|
191826
192436
|
}
|
|
191827
192437
|
return vertexId;
|
|
191828
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
|
+
}
|
|
191829
192448
|
if (alias?.routing === "openai-compatible") {
|
|
191830
192449
|
const modelId = process.env[OPENAI_COMPATIBLE_MODEL_ENV]?.trim();
|
|
191831
192450
|
if (!modelId) {
|
|
@@ -191864,6 +192483,7 @@ function resolveAgent(ctx) {
|
|
|
191864
192483
|
}
|
|
191865
192484
|
log.warning(`\xBB unknown PULLFROG_AGENT="${envAgent}" \u2014 falling through to auto-select`);
|
|
191866
192485
|
}
|
|
192486
|
+
if (ctx.proxyModel) return agents.opencode;
|
|
191867
192487
|
if (ctx.model && hasBedrockAuth() && process.env[BEDROCK_MODEL_ID_ENV]?.trim() === ctx.model) {
|
|
191868
192488
|
return isBedrockAnthropicId(ctx.model) ? agents.claude : agents.opencode;
|
|
191869
192489
|
}
|
|
@@ -191873,12 +192493,14 @@ function resolveAgent(ctx) {
|
|
|
191873
192493
|
if (ctx.model) {
|
|
191874
192494
|
try {
|
|
191875
192495
|
const provider2 = getModelProvider(ctx.model);
|
|
191876
|
-
if (provider2 === "anthropic" && hasClaudeCodeAuth())
|
|
191877
|
-
|
|
191878
|
-
}
|
|
192496
|
+
if (provider2 === "anthropic" && hasClaudeCodeAuth()) return agents.claude;
|
|
192497
|
+
if (provider2 === "openai" && ctx.codexAgent && hasCodexAuth()) return agents.codex;
|
|
191879
192498
|
} catch {
|
|
191880
192499
|
}
|
|
191881
192500
|
}
|
|
192501
|
+
if (!ctx.model && ctx.codexAgent && hasCodexAuth() && !hasClaudeCodeAuth()) {
|
|
192502
|
+
return agents.codex;
|
|
192503
|
+
}
|
|
191882
192504
|
return agents.opencode;
|
|
191883
192505
|
}
|
|
191884
192506
|
|
|
@@ -191976,10 +192598,37 @@ auto-compaction is disabled, so long runs grow until your endpoint refuses them.
|
|
|
191976
192598
|
|
|
191977
192599
|
for full setup instructions, see https://docs.pullfrog.com/openai-compatible`;
|
|
191978
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
|
+
}
|
|
191979
192624
|
function hasEnvVar3(name) {
|
|
191980
192625
|
const value2 = process.env[name];
|
|
191981
192626
|
return typeof value2 === "string" && value2.length > 0;
|
|
191982
192627
|
}
|
|
192628
|
+
function modelHasRuntimeAuth(model) {
|
|
192629
|
+
const authVars = [...getModelEnvVars(model), ...getModelManagedCredentials(model)];
|
|
192630
|
+
return authVars.length === 0 || authVars.some(hasEnvVar3);
|
|
192631
|
+
}
|
|
191983
192632
|
function hasPositiveNumberEnvVar(name) {
|
|
191984
192633
|
return Number(process.env[name]) > 0;
|
|
191985
192634
|
}
|
|
@@ -191998,6 +192647,17 @@ function validateOpenAICompatibleSetup(params) {
|
|
|
191998
192647
|
);
|
|
191999
192648
|
}
|
|
192000
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
|
+
}
|
|
192001
192661
|
function validateBedrockSetup(params) {
|
|
192002
192662
|
const hasAuth = hasEnvVar3("AWS_BEARER_TOKEN_BEDROCK") || hasEnvVar3("AWS_ACCESS_KEY_ID") && hasEnvVar3("AWS_SECRET_ACCESS_KEY");
|
|
192003
192663
|
const missing = [];
|
|
@@ -192021,6 +192681,12 @@ function validateVertexSetup(params) {
|
|
|
192021
192681
|
throw new Error(buildVertexSetupError({ owner: params.owner, name: params.name, missing }));
|
|
192022
192682
|
}
|
|
192023
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
|
+
}
|
|
192024
192690
|
function validateAgentApiKey(params) {
|
|
192025
192691
|
if (params.model) {
|
|
192026
192692
|
const alias = resolveDisplayAlias(params.model);
|
|
@@ -192036,6 +192702,10 @@ function validateAgentApiKey(params) {
|
|
|
192036
192702
|
validateOpenAICompatibleSetup({ owner: params.owner, name: params.name });
|
|
192037
192703
|
return;
|
|
192038
192704
|
}
|
|
192705
|
+
if (params.model.startsWith(`${AZURE_PROVIDER}/`)) {
|
|
192706
|
+
validateAzureSetup({ owner: params.owner, name: params.name });
|
|
192707
|
+
return;
|
|
192708
|
+
}
|
|
192039
192709
|
if (!params.model.includes("/")) {
|
|
192040
192710
|
if (process.env[VERTEX_MODEL_ID_ENV]?.trim() === params.model) {
|
|
192041
192711
|
validateVertexSetup({ owner: params.owner, name: params.name });
|
|
@@ -192059,7 +192729,7 @@ function validateAgentApiKey(params) {
|
|
|
192059
192729
|
})
|
|
192060
192730
|
);
|
|
192061
192731
|
}
|
|
192062
|
-
if (
|
|
192732
|
+
if (hasSingleProviderAuth(params.agent.name)) return;
|
|
192063
192733
|
throw new Error(
|
|
192064
192734
|
buildKeyError({
|
|
192065
192735
|
owner: params.owner,
|
|
@@ -192071,7 +192741,7 @@ function validateAgentApiKey(params) {
|
|
|
192071
192741
|
);
|
|
192072
192742
|
}
|
|
192073
192743
|
if (params.agent.name === "opencode") {
|
|
192074
|
-
if (params.authorized.
|
|
192744
|
+
if ([...params.authorized].some(modelHasRuntimeAuth)) return;
|
|
192075
192745
|
const reason = getModelsFailure();
|
|
192076
192746
|
if (reason) throw new Error(reason);
|
|
192077
192747
|
throw new Error(
|
|
@@ -192083,7 +192753,7 @@ function validateAgentApiKey(params) {
|
|
|
192083
192753
|
})
|
|
192084
192754
|
);
|
|
192085
192755
|
}
|
|
192086
|
-
if (
|
|
192756
|
+
if (hasSingleProviderAuth(params.agent.name)) return;
|
|
192087
192757
|
throw new Error(
|
|
192088
192758
|
buildKeyError({
|
|
192089
192759
|
owner: params.owner,
|
|
@@ -192456,9 +193126,9 @@ async function checkConfiguredCredentials(params) {
|
|
|
192456
193126
|
|
|
192457
193127
|
// utils/gitAuthServer.ts
|
|
192458
193128
|
import { randomUUID as randomUUID5 } from "node:crypto";
|
|
192459
|
-
import { writeFileSync as
|
|
193129
|
+
import { writeFileSync as writeFileSync14 } from "node:fs";
|
|
192460
193130
|
import { createServer as createServer3 } from "node:http";
|
|
192461
|
-
import { join as
|
|
193131
|
+
import { join as join24 } from "node:path";
|
|
192462
193132
|
var REVOKED_TRAP_MS = 6e4;
|
|
192463
193133
|
function revokeGitHubToken(token) {
|
|
192464
193134
|
fetch("https://api.github.com/installation/token", {
|
|
@@ -192527,7 +193197,7 @@ async function startGitAuthServer(tmpdir3) {
|
|
|
192527
193197
|
function writeAskpassScript(code) {
|
|
192528
193198
|
const scriptId = randomUUID5();
|
|
192529
193199
|
const scriptName = `askpass-${scriptId}.js`;
|
|
192530
|
-
const scriptPath =
|
|
193200
|
+
const scriptPath = join24(tmpdir3, scriptName);
|
|
192531
193201
|
const content = [
|
|
192532
193202
|
`#!/usr/bin/env node`,
|
|
192533
193203
|
`var a=process.argv[2]||"";`,
|
|
@@ -192540,7 +193210,7 @@ async function startGitAuthServer(tmpdir3) {
|
|
|
192540
193210
|
`r.on("end",function(){process.stdout.write(d+"\\n")})`,
|
|
192541
193211
|
`}).on("error",function(){process.exit(1)})}`
|
|
192542
193212
|
].join("\n");
|
|
192543
|
-
|
|
193213
|
+
writeFileSync14(scriptPath, content, { mode: 448 });
|
|
192544
193214
|
return scriptPath;
|
|
192545
193215
|
}
|
|
192546
193216
|
async function close() {
|
|
@@ -192979,7 +193649,7 @@ function resolveInstructions(ctx) {
|
|
|
192979
193649
|
|
|
192980
193650
|
// utils/learnings.ts
|
|
192981
193651
|
import { mkdir as mkdir2, readFile as readFile3, writeFile as writeFile2 } from "node:fs/promises";
|
|
192982
|
-
import { dirname as dirname4, join as
|
|
193652
|
+
import { dirname as dirname4, join as join25 } from "node:path";
|
|
192983
193653
|
|
|
192984
193654
|
// utils/learningsTruncate.ts
|
|
192985
193655
|
var MAX_LEARNINGS_LENGTH = 1e5;
|
|
@@ -192997,10 +193667,10 @@ function truncateAtLineBoundary(body, cap) {
|
|
|
192997
193667
|
var LEARNINGS_FILE_NAME = "pullfrog-learnings.md";
|
|
192998
193668
|
var XREPO_LEARNINGS_FILE_NAME = "pullfrog-xrepo-learnings.md";
|
|
192999
193669
|
function learningsFilePath(tmpdir3) {
|
|
193000
|
-
return
|
|
193670
|
+
return join25(tmpdir3, LEARNINGS_FILE_NAME);
|
|
193001
193671
|
}
|
|
193002
193672
|
function xrepoLearningsFilePath(tmpdir3) {
|
|
193003
|
-
return
|
|
193673
|
+
return join25(tmpdir3, XREPO_LEARNINGS_FILE_NAME);
|
|
193004
193674
|
}
|
|
193005
193675
|
async function seedLearningsFile(params) {
|
|
193006
193676
|
const path4 = learningsFilePath(params.tmpdir);
|
|
@@ -193116,7 +193786,7 @@ async function persistXrepoLearnings(ctx) {
|
|
|
193116
193786
|
// utils/modelAccess.ts
|
|
193117
193787
|
function decideModelAccess(input) {
|
|
193118
193788
|
if (!input.modelExplicit || !input.model) return { kind: "ok" };
|
|
193119
|
-
const byokAuthorized = !!input.resolvedModel && (input.authorized.has(input.resolvedModel) || !input.resolvedModel.includes("/") || input.resolvedModel.startsWith(`${OPENAI_COMPATIBLE_PROVIDER}/`));
|
|
193789
|
+
const byokAuthorized = !!input.resolvedModel && (input.authorized.has(input.resolvedModel) || !input.resolvedModel.includes("/") || input.resolvedModel.startsWith(`${OPENAI_COMPATIBLE_PROVIDER}/`) || input.resolvedModel.startsWith(`${AZURE_PROVIDER}/`));
|
|
193120
193790
|
if (input.proxyActive) {
|
|
193121
193791
|
const target = resolveOpenRouterModel(input.model);
|
|
193122
193792
|
if (input.oss) {
|
|
@@ -193161,7 +193831,7 @@ function buildModelAccessError(input) {
|
|
|
193161
193831
|
}
|
|
193162
193832
|
|
|
193163
193833
|
// utils/normalizeEnv.ts
|
|
193164
|
-
var
|
|
193834
|
+
var core5 = __toESM(require_core(), 1);
|
|
193165
193835
|
function hasControlCharacter(value2) {
|
|
193166
193836
|
return [...value2].some((ch) => {
|
|
193167
193837
|
const code = ch.charCodeAt(0);
|
|
@@ -193187,7 +193857,7 @@ function sanitizeSecret(key, value2) {
|
|
|
193187
193857
|
`\xBB stripped whitespace from ${key} (whitespace in secret values breaks GitHub Actions log masking)`
|
|
193188
193858
|
);
|
|
193189
193859
|
}
|
|
193190
|
-
|
|
193860
|
+
core5.setSecret(trimmed);
|
|
193191
193861
|
return trimmed;
|
|
193192
193862
|
}
|
|
193193
193863
|
function normalizeEnv() {
|
|
@@ -193231,7 +193901,7 @@ function normalizeEnv() {
|
|
|
193231
193901
|
}
|
|
193232
193902
|
|
|
193233
193903
|
// utils/overrides.ts
|
|
193234
|
-
var
|
|
193904
|
+
var core6 = __toESM(require_core(), 1);
|
|
193235
193905
|
var DENIED_OVERRIDE_NAMES = /* @__PURE__ */ new Set([
|
|
193236
193906
|
"GITHUB_TOKEN",
|
|
193237
193907
|
"GH_TOKEN",
|
|
@@ -193277,7 +193947,7 @@ function applyOverrides(params) {
|
|
|
193277
193947
|
denied.push(key);
|
|
193278
193948
|
continue;
|
|
193279
193949
|
}
|
|
193280
|
-
if (value2.length > 0)
|
|
193950
|
+
if (value2.length > 0) core6.setSecret(value2);
|
|
193281
193951
|
params.env[key] = value2;
|
|
193282
193952
|
applied.push(key);
|
|
193283
193953
|
}
|
|
@@ -193286,8 +193956,8 @@ function applyOverrides(params) {
|
|
|
193286
193956
|
}
|
|
193287
193957
|
|
|
193288
193958
|
// utils/payload.ts
|
|
193289
|
-
var
|
|
193290
|
-
import { readFileSync as
|
|
193959
|
+
var core7 = __toESM(require_core(), 1);
|
|
193960
|
+
import { readFileSync as readFileSync6 } from "node:fs";
|
|
193291
193961
|
import { isAbsolute as isAbsolute2, resolve as resolve2 } from "node:path";
|
|
193292
193962
|
|
|
193293
193963
|
// utils/versioning.ts
|
|
@@ -193374,8 +194044,8 @@ function resolveCwd(cwd) {
|
|
|
193374
194044
|
return workspace ? resolve2(workspace, cwd) : cwd;
|
|
193375
194045
|
}
|
|
193376
194046
|
function resolvePromptInput() {
|
|
193377
|
-
const promptInput =
|
|
193378
|
-
const promptFile =
|
|
194047
|
+
const promptInput = core7.getInput("prompt");
|
|
194048
|
+
const promptFile = core7.getInput("prompt_file");
|
|
193379
194049
|
if (promptInput && promptFile) {
|
|
193380
194050
|
throw new Error("set exactly one of 'prompt' or 'prompt_file' inputs, not both.");
|
|
193381
194051
|
}
|
|
@@ -193401,7 +194071,7 @@ function resolvePromptInput() {
|
|
|
193401
194071
|
function resolvePromptFile(input) {
|
|
193402
194072
|
const workspace = process.env.GITHUB_WORKSPACE;
|
|
193403
194073
|
const path4 = isAbsolute2(input) ? input : workspace ? resolve2(workspace, input) : resolve2(input);
|
|
193404
|
-
const content =
|
|
194074
|
+
const content = readFileSync6(path4, "utf-8");
|
|
193405
194075
|
if (!content.trim()) {
|
|
193406
194076
|
throw new Error(`prompt_file ${JSON.stringify(input)} is empty.`);
|
|
193407
194077
|
}
|
|
@@ -193409,7 +194079,7 @@ function resolvePromptFile(input) {
|
|
|
193409
194079
|
}
|
|
193410
194080
|
function warnIfDeprecatedStatusChecks(value2) {
|
|
193411
194081
|
if (value2 !== void 0) {
|
|
193412
|
-
|
|
194082
|
+
core7.warning(
|
|
193413
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."
|
|
193414
194084
|
);
|
|
193415
194085
|
}
|
|
@@ -193417,15 +194087,15 @@ function warnIfDeprecatedStatusChecks(value2) {
|
|
|
193417
194087
|
}
|
|
193418
194088
|
function resolveNonPromptInputs() {
|
|
193419
194089
|
return Inputs.omit("prompt", "prompt_file").assert({
|
|
193420
|
-
model:
|
|
193421
|
-
effort:
|
|
193422
|
-
debug:
|
|
193423
|
-
timeout:
|
|
193424
|
-
cwd:
|
|
193425
|
-
push:
|
|
193426
|
-
shell:
|
|
193427
|
-
status_checks: warnIfDeprecatedStatusChecks(
|
|
193428
|
-
progress_comments:
|
|
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
|
|
193429
194099
|
});
|
|
193430
194100
|
}
|
|
193431
194101
|
function resolvePayload(resolvedPromptInput, repoSettings) {
|
|
@@ -193493,7 +194163,7 @@ function resolvePayload(resolvedPromptInput, repoSettings) {
|
|
|
193493
194163
|
};
|
|
193494
194164
|
}
|
|
193495
194165
|
function resolveOutputSchema() {
|
|
193496
|
-
const raw2 =
|
|
194166
|
+
const raw2 = core7.getInput("output_schema");
|
|
193497
194167
|
if (!raw2) return void 0;
|
|
193498
194168
|
let parsed2;
|
|
193499
194169
|
try {
|
|
@@ -193509,10 +194179,10 @@ function resolveOutputSchema() {
|
|
|
193509
194179
|
}
|
|
193510
194180
|
|
|
193511
194181
|
// utils/proxy.ts
|
|
193512
|
-
var
|
|
194182
|
+
var core9 = __toESM(require_core(), 1);
|
|
193513
194183
|
|
|
193514
194184
|
// utils/token.ts
|
|
193515
|
-
var
|
|
194185
|
+
var core8 = __toESM(require_core(), 1);
|
|
193516
194186
|
import assert2 from "node:assert/strict";
|
|
193517
194187
|
var mcpTokenValue;
|
|
193518
194188
|
var refreshMcpTokenFn;
|
|
@@ -193520,7 +194190,7 @@ function getMcpTokenRefresh() {
|
|
|
193520
194190
|
return refreshMcpTokenFn;
|
|
193521
194191
|
}
|
|
193522
194192
|
function getJobToken() {
|
|
193523
|
-
const inputToken =
|
|
194193
|
+
const inputToken = core8.getInput("token");
|
|
193524
194194
|
if (inputToken) {
|
|
193525
194195
|
return inputToken;
|
|
193526
194196
|
}
|
|
@@ -193536,7 +194206,7 @@ async function resolveTokens(params) {
|
|
|
193536
194206
|
if (externalToken) {
|
|
193537
194207
|
mcpTokenValue = externalToken;
|
|
193538
194208
|
if (isGitHubActions) {
|
|
193539
|
-
|
|
194209
|
+
core8.setSecret(externalToken);
|
|
193540
194210
|
}
|
|
193541
194211
|
log.info("\xBB using external GH_TOKEN for both git and MCP");
|
|
193542
194212
|
return {
|
|
@@ -193556,7 +194226,7 @@ async function resolveTokens(params) {
|
|
|
193556
194226
|
const gitPermissions = params.push === "disabled" ? { contents: "read" } : { contents: "write", workflows: "write" };
|
|
193557
194227
|
const gitToken = await acquireNewToken({ repos: writeRepos, permissions: gitPermissions });
|
|
193558
194228
|
if (isGitHubActions) {
|
|
193559
|
-
|
|
194229
|
+
core8.setSecret(gitToken);
|
|
193560
194230
|
}
|
|
193561
194231
|
log.info(
|
|
193562
194232
|
`\xBB acquired git token (${Object.entries(gitPermissions).map((e) => e.join(":")).join(", ")})`
|
|
@@ -193573,7 +194243,7 @@ async function resolveTokens(params) {
|
|
|
193573
194243
|
};
|
|
193574
194244
|
const mcpToken = await acquireNewToken({ repos: writeRepos, permissions: mcpPermissions });
|
|
193575
194245
|
if (isGitHubActions) {
|
|
193576
|
-
|
|
194246
|
+
core8.setSecret(mcpToken);
|
|
193577
194247
|
}
|
|
193578
194248
|
log.info(
|
|
193579
194249
|
`\xBB acquired scoped MCP token (${Object.entries(mcpPermissions).map((e) => e.join(":")).join(", ")})`
|
|
@@ -193582,7 +194252,7 @@ async function resolveTokens(params) {
|
|
|
193582
194252
|
let ghToken;
|
|
193583
194253
|
if (ghPermissions) {
|
|
193584
194254
|
ghToken = await acquireNewToken({ permissions: ghPermissions });
|
|
193585
|
-
if (isGitHubActions)
|
|
194255
|
+
if (isGitHubActions) core8.setSecret(ghToken);
|
|
193586
194256
|
log.info(
|
|
193587
194257
|
`\xBB acquired gh token mirroring ${params.authorPermission} (${formatPermissions(ghPermissions)})`
|
|
193588
194258
|
);
|
|
@@ -193593,7 +194263,7 @@ async function resolveTokens(params) {
|
|
|
193593
194263
|
repos: params.xrepo.read,
|
|
193594
194264
|
permissions: { contents: "read" }
|
|
193595
194265
|
});
|
|
193596
|
-
if (isGitHubActions)
|
|
194266
|
+
if (isGitHubActions) core8.setSecret(readToken);
|
|
193597
194267
|
log.info(`\xBB acquired cross-repo read token (contents:read, ${params.xrepo.read.length} repos)`);
|
|
193598
194268
|
}
|
|
193599
194269
|
mcpTokenValue = mcpToken;
|
|
@@ -193612,7 +194282,7 @@ async function resolveTokens(params) {
|
|
|
193612
194282
|
oidc: params.oidc ?? void 0
|
|
193613
194283
|
}).then((fresh) => {
|
|
193614
194284
|
if (isGitHubActions) {
|
|
193615
|
-
|
|
194285
|
+
core8.setSecret(fresh);
|
|
193616
194286
|
}
|
|
193617
194287
|
mcpTokenValue = fresh;
|
|
193618
194288
|
currentMcpToken = fresh;
|
|
@@ -193633,7 +194303,7 @@ async function resolveTokens(params) {
|
|
|
193633
194303
|
permissions: gitPermissions,
|
|
193634
194304
|
oidc: params.oidc ?? void 0
|
|
193635
194305
|
}).then((fresh) => {
|
|
193636
|
-
if (isGitHubActions)
|
|
194306
|
+
if (isGitHubActions) core8.setSecret(fresh);
|
|
193637
194307
|
void revokeGitHubInstallationToken(currentGitToken);
|
|
193638
194308
|
currentGitToken = fresh;
|
|
193639
194309
|
log.warning("\xBB GitHub rejected the git token; re-acquired a fresh git token");
|
|
@@ -193650,7 +194320,7 @@ async function resolveTokens(params) {
|
|
|
193650
194320
|
permissions: { contents: "read" },
|
|
193651
194321
|
oidc: params.oidc ?? void 0
|
|
193652
194322
|
}).then((fresh) => {
|
|
193653
|
-
if (isGitHubActions)
|
|
194323
|
+
if (isGitHubActions) core8.setSecret(fresh);
|
|
193654
194324
|
void revokeGitHubInstallationToken(read2);
|
|
193655
194325
|
currentReadToken = fresh;
|
|
193656
194326
|
log.warning("\xBB GitHub rejected the read token; re-acquired a fresh read token");
|
|
@@ -193864,7 +194534,7 @@ async function resolveProxyModel(ctx) {
|
|
|
193864
194534
|
});
|
|
193865
194535
|
if (!key) return;
|
|
193866
194536
|
process.env.OPENROUTER_API_KEY = key;
|
|
193867
|
-
|
|
194537
|
+
core9.setSecret(key);
|
|
193868
194538
|
ctx.payload.proxyModel = ctx.proxyModel;
|
|
193869
194539
|
ctx.toolState.model = ctx.proxyModel;
|
|
193870
194540
|
const label = ctx.oss ? "oss" : "router";
|
|
@@ -193935,7 +194605,7 @@ async function runProxyResolution(ctx) {
|
|
|
193935
194605
|
|
|
193936
194606
|
// utils/prSummary.ts
|
|
193937
194607
|
import { mkdir as mkdir3, readFile as readFile4, writeFile as writeFile3 } from "node:fs/promises";
|
|
193938
|
-
import { dirname as dirname5, join as
|
|
194608
|
+
import { dirname as dirname5, join as join26 } from "node:path";
|
|
193939
194609
|
var SUMMARY_FILE_NAME = "pullfrog-summary.md";
|
|
193940
194610
|
var SUMMARY_SCAFFOLD = `# PR summary
|
|
193941
194611
|
|
|
@@ -193945,7 +194615,7 @@ var SUMMARY_SCAFFOLD = `# PR summary
|
|
|
193945
194615
|
var MIN_SNAPSHOT_LENGTH = 60;
|
|
193946
194616
|
var MAX_SNAPSHOT_LENGTH = 32768;
|
|
193947
194617
|
function summaryFilePath(tmpdir3) {
|
|
193948
|
-
return
|
|
194618
|
+
return join26(tmpdir3, SUMMARY_FILE_NAME);
|
|
193949
194619
|
}
|
|
193950
194620
|
async function seedSummaryFile(params) {
|
|
193951
194621
|
const path4 = summaryFilePath(params.tmpdir);
|
|
@@ -194074,6 +194744,7 @@ var defaultSettings = {
|
|
|
194074
194744
|
shell: "restricted",
|
|
194075
194745
|
prApproveEnabled: false,
|
|
194076
194746
|
autoMergeEnabled: false,
|
|
194747
|
+
codexAgent: false,
|
|
194077
194748
|
signedCommits: false,
|
|
194078
194749
|
repoIntelligence: false,
|
|
194079
194750
|
progressComments: true,
|
|
@@ -194562,7 +195233,7 @@ ${genericBody}`,
|
|
|
194562
195233
|
}
|
|
194563
195234
|
|
|
194564
195235
|
// utils/runLifecycle.ts
|
|
194565
|
-
var
|
|
195236
|
+
var core10 = __toESM(require_core(), 1);
|
|
194566
195237
|
|
|
194567
195238
|
// utils/autoMerge.ts
|
|
194568
195239
|
function hasBlockingHumanReview(reviews) {
|
|
@@ -194948,7 +195619,7 @@ async function finalizeSuccessRun(input) {
|
|
|
194948
195619
|
}
|
|
194949
195620
|
if (input.toolState.output) {
|
|
194950
195621
|
log.info(`::pullfrog-output::${Buffer.from(input.toolState.output).toString("base64")}`);
|
|
194951
|
-
|
|
195622
|
+
core10.setOutput("result", input.toolState.output);
|
|
194952
195623
|
}
|
|
194953
195624
|
if (input.result.success) {
|
|
194954
195625
|
await approveAfterFix(input.toolContext).catch((error52) => {
|
|
@@ -195412,7 +196083,11 @@ async function main() {
|
|
|
195412
196083
|
}
|
|
195413
196084
|
const resolvedModel = credentials.kind === "fellBack" ? credentials.replacement : configuredModel;
|
|
195414
196085
|
vertexCredentials = materializeVertexCredentials({ model: resolvedModel });
|
|
195415
|
-
const agent2 = resolveAgent({
|
|
196086
|
+
const agent2 = resolveAgent({
|
|
196087
|
+
model: resolvedModel,
|
|
196088
|
+
proxyModel: payload.proxyModel,
|
|
196089
|
+
codexAgent: runContext.repoSettings.codexAgent
|
|
196090
|
+
});
|
|
195416
196091
|
const effectiveModel = payload.proxyModel ?? resolvedModel ?? payload.model;
|
|
195417
196092
|
toolState.model = effectiveModel;
|
|
195418
196093
|
if (!payload.proxyModel) {
|
|
@@ -195596,8 +196271,8 @@ ${instructions.user}` : null,
|
|
|
195596
196271
|
log.info(instructions.full);
|
|
195597
196272
|
});
|
|
195598
196273
|
if (agentId === "opencode") {
|
|
195599
|
-
const pluginDir =
|
|
195600
|
-
const hasPlugins =
|
|
196274
|
+
const pluginDir = join27(process.cwd(), ".opencode", "plugin");
|
|
196275
|
+
const hasPlugins = existsSync9(pluginDir) && readdirSync2(pluginDir).some((f) => /\.[jt]sx?$/.test(f));
|
|
195601
196276
|
if (hasPlugins && toolState.dependencyInstallation?.promise) {
|
|
195602
196277
|
log.info(
|
|
195603
196278
|
"\xBB .opencode/plugin/ detected \u2014 awaiting dependency installation before agent start"
|