pullfrog 0.1.89 → 0.1.91
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/cli.mjs +431 -148
- package/dist/index.js +466 -143
- package/dist/internal/index.d.ts +1 -0
- package/dist/internal.js +193 -33
- package/dist/toolState.d.ts +0 -3
- package/dist/utils/buildPullfrogFooter.d.ts +0 -7
- package/dist/utils/claudeSubscription.d.ts +1 -0
- package/dist/utils/credentialFallback.d.ts +8 -16
- package/dist/utils/credentialPool.d.ts +18 -0
- package/dist/utils/openCodeModels.d.ts +4 -0
- package/dist/utils/subscriptionCredentials.d.ts +10 -0
- package/dist/utils/subscriptionProbe.d.ts +18 -2
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -83904,7 +83904,7 @@ var require_Element = __commonJS({
|
|
|
83904
83904
|
var FilteredElementList = require_FilteredElementList();
|
|
83905
83905
|
var DOMException2 = require_DOMException();
|
|
83906
83906
|
var DOMTokenList = require_DOMTokenList();
|
|
83907
|
-
var
|
|
83907
|
+
var select2 = require_select();
|
|
83908
83908
|
var ContainerNode = require_ContainerNode();
|
|
83909
83909
|
var ChildNode = require_ChildNode();
|
|
83910
83910
|
var NonDocumentTypeChildNode = require_NonDocumentTypeChildNode();
|
|
@@ -84665,7 +84665,7 @@ var require_Element = __commonJS({
|
|
|
84665
84665
|
this.className = v;
|
|
84666
84666
|
} },
|
|
84667
84667
|
matches: { value: function(selector) {
|
|
84668
|
-
return
|
|
84668
|
+
return select2.matches(this, selector);
|
|
84669
84669
|
} },
|
|
84670
84670
|
closest: { value: function(selector) {
|
|
84671
84671
|
var el = this;
|
|
@@ -84678,10 +84678,10 @@ var require_Element = __commonJS({
|
|
|
84678
84678
|
return null;
|
|
84679
84679
|
} },
|
|
84680
84680
|
querySelector: { value: function(selector) {
|
|
84681
|
-
return
|
|
84681
|
+
return select2(selector, this)[0];
|
|
84682
84682
|
} },
|
|
84683
84683
|
querySelectorAll: { value: function(selector) {
|
|
84684
|
-
var nodes =
|
|
84684
|
+
var nodes = select2(selector, this);
|
|
84685
84685
|
return nodes.item ? nodes : new NodeList(nodes);
|
|
84686
84686
|
} }
|
|
84687
84687
|
});
|
|
@@ -85209,7 +85209,7 @@ var require_DocumentFragment = __commonJS({
|
|
|
85209
85209
|
var NodeList = require_NodeList();
|
|
85210
85210
|
var ContainerNode = require_ContainerNode();
|
|
85211
85211
|
var Element = require_Element();
|
|
85212
|
-
var
|
|
85212
|
+
var select2 = require_select();
|
|
85213
85213
|
var utils = require_utils8();
|
|
85214
85214
|
function DocumentFragment(doc) {
|
|
85215
85215
|
ContainerNode.call(this);
|
|
@@ -85238,7 +85238,7 @@ var require_DocumentFragment = __commonJS({
|
|
|
85238
85238
|
context.isHTML = true;
|
|
85239
85239
|
context.getElementsByTagName = Element.prototype.getElementsByTagName;
|
|
85240
85240
|
context.nextElement = Object.getOwnPropertyDescriptor(Element.prototype, "firstElementChild").get;
|
|
85241
|
-
var nodes =
|
|
85241
|
+
var nodes = select2(selector, context);
|
|
85242
85242
|
return nodes.item ? nodes : new NodeList(nodes);
|
|
85243
85243
|
} },
|
|
85244
85244
|
// Utility methods
|
|
@@ -88353,7 +88353,7 @@ var require_Document = __commonJS({
|
|
|
88353
88353
|
var NodeIterator = require_NodeIterator();
|
|
88354
88354
|
var NodeFilter = require_NodeFilter();
|
|
88355
88355
|
var URL2 = require_URL();
|
|
88356
|
-
var
|
|
88356
|
+
var select2 = require_select();
|
|
88357
88357
|
var events = require_events3();
|
|
88358
88358
|
var xml = require_xmlnames();
|
|
88359
88359
|
var html = require_htmlelts();
|
|
@@ -88914,10 +88914,10 @@ var require_Document = __commonJS({
|
|
|
88914
88914
|
return this._templateDocCache;
|
|
88915
88915
|
} },
|
|
88916
88916
|
querySelector: { value: function(selector) {
|
|
88917
|
-
return
|
|
88917
|
+
return select2(selector, this)[0];
|
|
88918
88918
|
} },
|
|
88919
88919
|
querySelectorAll: { value: function(selector) {
|
|
88920
|
-
var nodes =
|
|
88920
|
+
var nodes = select2(selector, this);
|
|
88921
88921
|
return nodes.item ? nodes : new NodeList(nodes);
|
|
88922
88922
|
} }
|
|
88923
88923
|
});
|
|
@@ -105082,10 +105082,12 @@ async function preflightClaudeSubscription(params) {
|
|
|
105082
105082
|
if (res.status !== 401 && res.status !== 403 && res.status !== 429)
|
|
105083
105083
|
return { usable: true, status: res.status };
|
|
105084
105084
|
const body = await res.text().catch(() => "");
|
|
105085
|
+
const reset = Number(res.headers.get("anthropic-ratelimit-unified-reset"));
|
|
105085
105086
|
return {
|
|
105086
105087
|
usable: false,
|
|
105087
105088
|
status: res.status,
|
|
105088
|
-
reason: `${res.status}: ${extractApiErrorMessage(body)}
|
|
105089
|
+
reason: `${res.status}: ${extractApiErrorMessage(body)}`,
|
|
105090
|
+
resetAt: res.status === 429 && reset > 0 ? new Date(reset * 1e3) : void 0
|
|
105089
105091
|
};
|
|
105090
105092
|
}
|
|
105091
105093
|
function extractApiErrorMessage(body) {
|
|
@@ -105729,7 +105731,7 @@ var import_semver = __toESM(require_semver2(), 1);
|
|
|
105729
105731
|
// package.json
|
|
105730
105732
|
var package_default = {
|
|
105731
105733
|
name: "pullfrog",
|
|
105732
|
-
version: "0.1.
|
|
105734
|
+
version: "0.1.91",
|
|
105733
105735
|
cliContractVersion: "1",
|
|
105734
105736
|
type: "module",
|
|
105735
105737
|
bin: {
|
|
@@ -108001,6 +108003,17 @@ function parseXaiAuthBody(raw2) {
|
|
|
108001
108003
|
var CODEX_AUTH_ENV = "CODEX_AUTH_JSON";
|
|
108002
108004
|
var XAI_AUTH_ENV = "GROK_AUTH_JSON";
|
|
108003
108005
|
var installedSubscriptionPaths = /* @__PURE__ */ new Map();
|
|
108006
|
+
function clearInstalledSubscription(name) {
|
|
108007
|
+
const path4 = installedSubscriptionPaths.get(name);
|
|
108008
|
+
if (!path4 || !existsSync4(path4)) return;
|
|
108009
|
+
const value2 = JSON.parse(readFileSync2(path4, "utf8"));
|
|
108010
|
+
if (!value2 || typeof value2 !== "object") return;
|
|
108011
|
+
if (name === "CODEX_AUTH_JSON" && "openai" in value2) delete value2.openai;
|
|
108012
|
+
if (name === "GROK_AUTH_JSON" && "xai" in value2) delete value2.xai;
|
|
108013
|
+
writeFileSync4(path4, `${JSON.stringify(value2)}
|
|
108014
|
+
`, { mode: 384 });
|
|
108015
|
+
installedSubscriptionPaths.delete(name);
|
|
108016
|
+
}
|
|
108004
108017
|
var PULLFROG_DATA_DIR = "/var/lib/pullfrog";
|
|
108005
108018
|
function isRejectedChain(body) {
|
|
108006
108019
|
if (!body.refresh_rejected_at) return false;
|
|
@@ -116693,6 +116706,9 @@ function captureAuthorizedModels(cliPath) {
|
|
|
116693
116706
|
}
|
|
116694
116707
|
log.debug(`\xBB opencode authorized: ${authorized.size} models`);
|
|
116695
116708
|
}
|
|
116709
|
+
function authorizeModel(model) {
|
|
116710
|
+
getAuthorizedModels().add(model);
|
|
116711
|
+
}
|
|
116696
116712
|
function getAuthorizedModels() {
|
|
116697
116713
|
if (!authorized) {
|
|
116698
116714
|
throw new Error("getAuthorizedModels called before captureAuthorizedModels");
|
|
@@ -130812,8 +130828,8 @@ function liftWireOnlyMaterial(message, kind) {
|
|
|
130812
130828
|
};
|
|
130813
130829
|
}
|
|
130814
130830
|
function codecResultValidator(codec2, method) {
|
|
130815
|
-
const
|
|
130816
|
-
if (!
|
|
130831
|
+
const probe2 = codec2.validateResult(method, void 0);
|
|
130832
|
+
if (!probe2.ok && probe2.reason === "not-in-era") return void 0;
|
|
130817
130833
|
return { "~standard": {
|
|
130818
130834
|
version: 1,
|
|
130819
130835
|
vendor: "mcp-wire-codec",
|
|
@@ -145189,8 +145205,8 @@ function liftWireOnlyMaterial2(message, kind) {
|
|
|
145189
145205
|
};
|
|
145190
145206
|
}
|
|
145191
145207
|
function codecResultValidator2(codec2, method) {
|
|
145192
|
-
const
|
|
145193
|
-
if (!
|
|
145208
|
+
const probe2 = codec2.validateResult(method, void 0);
|
|
145209
|
+
if (!probe2.ok && probe2.reason === "not-in-era") return void 0;
|
|
145194
145210
|
return { "~standard": {
|
|
145195
145211
|
version: 1,
|
|
145196
145212
|
vendor: "mcp-wire-codec",
|
|
@@ -188369,10 +188385,10 @@ function CommitChangesTool(ctx) {
|
|
|
188369
188385
|
}
|
|
188370
188386
|
const requested = new Set(params.files);
|
|
188371
188387
|
const known = new Set(changes.map((c) => c.path));
|
|
188372
|
-
const
|
|
188373
|
-
if (
|
|
188388
|
+
const unknown5 = [...requested].filter((p) => !known.has(p));
|
|
188389
|
+
if (unknown5.length > 0) {
|
|
188374
188390
|
throw new Error(
|
|
188375
|
-
`no detected change at: ${
|
|
188391
|
+
`no detected change at: ${unknown5.join(", ")} \u2014 run git status to list changed paths.`
|
|
188376
188392
|
);
|
|
188377
188393
|
}
|
|
188378
188394
|
changes = changes.filter((c) => requested.has(c.path));
|
|
@@ -188765,15 +188781,6 @@ function PushTagsTool(ctx) {
|
|
|
188765
188781
|
// utils/buildPullfrogFooter.ts
|
|
188766
188782
|
var PULLFROG_DIVIDER = "<!-- PULLFROG_DIVIDER_DO_NOT_REMOVE_PLZ -->";
|
|
188767
188783
|
var FROG_LOGO = `<a href="https://pullfrog.com"><picture><source media="(prefers-color-scheme: dark)" srcset="https://pullfrog.com/logos/frog-white-full-18px.png"><img src="https://pullfrog.com/logos/frog-green-full-18px.png" width="9px" height="9px" style="vertical-align: middle; " alt="Pullfrog"></picture></a>`;
|
|
188768
|
-
function providerDisplayName(slug2) {
|
|
188769
|
-
try {
|
|
188770
|
-
const key = getModelProvider(slug2);
|
|
188771
|
-
const meta3 = providers[key];
|
|
188772
|
-
return meta3?.displayName ?? key;
|
|
188773
|
-
} catch {
|
|
188774
|
-
return slug2;
|
|
188775
|
-
}
|
|
188776
|
-
}
|
|
188777
188784
|
function formatModelLabel(params) {
|
|
188778
188785
|
const alias = resolveDisplayAlias(params.model) ?? // reverse-lookup: when the caller passes an effective model (proxy or
|
|
188779
188786
|
// resolved target like "openrouter/anthropic/claude-opus-4.7") instead of
|
|
@@ -188788,9 +188795,6 @@ function formatModelLabel(params) {
|
|
|
188788
188795
|
return `${ossBase} (${configured} not used \u2014 pick one of the [funded models](https://docs.pullfrog.com/models#pullfrog-for-oss) or add a [provider key](https://docs.pullfrog.com/keys) to run your own)`;
|
|
188789
188796
|
}
|
|
188790
188797
|
const base = alias?.isFree ? `\`${displayName}\` (free)` : `\`${displayName}\``;
|
|
188791
|
-
if (params.fallbackFrom) {
|
|
188792
|
-
return `${base} (credentials for ${providerDisplayName(params.fallbackFrom)} were rejected by the provider)`;
|
|
188793
|
-
}
|
|
188794
188798
|
if (params.clamped?.reason === "trial") {
|
|
188795
188799
|
return `${base} (model usage covered by Pullfrog)`;
|
|
188796
188800
|
}
|
|
@@ -188831,7 +188835,6 @@ function buildPullfrogFooter(params) {
|
|
|
188831
188835
|
parts.push(
|
|
188832
188836
|
`Using ${formatModelLabel({
|
|
188833
188837
|
model: params.model,
|
|
188834
|
-
fallbackFrom: params.fallbackFrom,
|
|
188835
188838
|
clamped: params.clamped,
|
|
188836
188839
|
unselectedProxyDefault: params.unselectedProxyDefault,
|
|
188837
188840
|
oss: params.oss
|
|
@@ -189031,7 +189034,6 @@ function buildCommentFooter(ctx, customParts) {
|
|
|
189031
189034
|
} : void 0,
|
|
189032
189035
|
customParts,
|
|
189033
189036
|
model: ctx.toolState.model,
|
|
189034
|
-
fallbackFrom: ctx.toolState.modelFallback?.from,
|
|
189035
189037
|
clamped: ctx.toolState.modelClamped,
|
|
189036
189038
|
unselectedProxyDefault: ctx.toolState.unselectedProxyDefault,
|
|
189037
189039
|
oss: ctx.oss,
|
|
@@ -190021,7 +190023,6 @@ async function createAndSubmitWithFooter(ctx, params, opts) {
|
|
|
190021
190023
|
workflowRun: ctx.runId ? { owner: ctx.repo.owner, repo: ctx.repo.name, runId: ctx.runId, jobId: ctx.jobId } : void 0,
|
|
190022
190024
|
customParts,
|
|
190023
190025
|
model: ctx.toolState.model,
|
|
190024
|
-
fallbackFrom: ctx.toolState.modelFallback?.from,
|
|
190025
190026
|
clamped: ctx.toolState.modelClamped,
|
|
190026
190027
|
unselectedProxyDefault: ctx.toolState.unselectedProxyDefault,
|
|
190027
190028
|
oss: ctx.oss,
|
|
@@ -192397,7 +192398,6 @@ function buildPrBodyWithFooter(ctx, body) {
|
|
|
192397
192398
|
triggeredBy: true,
|
|
192398
192399
|
workflowRun: ctx.runId ? { owner: ctx.repo.owner, repo: ctx.repo.name, runId: ctx.runId, jobId: ctx.jobId } : void 0,
|
|
192399
192400
|
model: ctx.toolState.model,
|
|
192400
|
-
fallbackFrom: ctx.toolState.modelFallback?.from,
|
|
192401
192401
|
clamped: ctx.toolState.modelClamped,
|
|
192402
192402
|
unselectedProxyDefault: ctx.toolState.unselectedProxyDefault,
|
|
192403
192403
|
oss: ctx.oss,
|
|
@@ -193165,8 +193165,9 @@ function ResolveReviewThreadTool(ctx) {
|
|
|
193165
193165
|
}
|
|
193166
193166
|
|
|
193167
193167
|
// mcp/searchIssues.ts
|
|
193168
|
+
var TERM = /^[\p{L}\p{N}_./-]+$/u;
|
|
193168
193169
|
var SearchIssues = type({
|
|
193169
|
-
terms: type.string.
|
|
193170
|
+
terms: type.string.atMostLength(40).array().atLeastLength(1).atMostLength(5).describe("1-5 keywords from the issue; no GitHub qualifiers or query operators")
|
|
193170
193171
|
});
|
|
193171
193172
|
function SearchIssuesTool(ctx) {
|
|
193172
193173
|
return tool({
|
|
@@ -193174,6 +193175,10 @@ function SearchIssuesTool(ctx) {
|
|
|
193174
193175
|
description: "Find open or closed issues in this repository by keyword. Inspect promising matches with get_issue before deciding whether they are duplicates or related. Results are bounded, not an exhaustive duplicate check.",
|
|
193175
193176
|
parameters: SearchIssues,
|
|
193176
193177
|
execute: execute(async (params) => {
|
|
193178
|
+
const invalid = params.terms.find((term) => !TERM.test(term));
|
|
193179
|
+
if (invalid) {
|
|
193180
|
+
throw new Error(`search term "${invalid}" may contain only letters, digits and _ . / -`);
|
|
193181
|
+
}
|
|
193177
193182
|
const repoPath = `/repos/${ctx.repo.owner}/${ctx.repo.name}`.toLowerCase();
|
|
193178
193183
|
const terms = params.terms.map((term) => `"${term}"`).join(" ");
|
|
193179
193184
|
const response = await ctx.octokit.rest.search.issuesAndPullRequests({
|
|
@@ -194541,17 +194546,17 @@ async function verifyCredential(params) {
|
|
|
194541
194546
|
const value2 = params.value.trim();
|
|
194542
194547
|
if (value2.length === 0) return "dead";
|
|
194543
194548
|
if (params.envVar === "CLAUDE_CODE_OAUTH_TOKEN") return verifyClaudeSubscription(value2);
|
|
194544
|
-
const
|
|
194545
|
-
if (!
|
|
194546
|
-
const request2 =
|
|
194549
|
+
const probe2 = PROBES[params.envVar];
|
|
194550
|
+
if (!probe2) return "unknown";
|
|
194551
|
+
const request2 = probe2.request(value2);
|
|
194547
194552
|
try {
|
|
194548
194553
|
const response = await fetch(request2.url, {
|
|
194549
194554
|
headers: request2.headers,
|
|
194550
194555
|
signal: AbortSignal.timeout(5e3)
|
|
194551
194556
|
});
|
|
194552
194557
|
if (response.ok) return "alive";
|
|
194553
|
-
if (
|
|
194554
|
-
return
|
|
194558
|
+
if (probe2.deadWhen) {
|
|
194559
|
+
return probe2.deadWhen(response.status, await response.text()) ? "dead" : "unknown";
|
|
194555
194560
|
}
|
|
194556
194561
|
return response.status === 401 ? "dead" : "unknown";
|
|
194557
194562
|
} catch {
|
|
@@ -194582,14 +194587,6 @@ async function checkOne(params) {
|
|
|
194582
194587
|
const verdict = await verifyCredential({ envVar: params.envVar, value: value2 });
|
|
194583
194588
|
return verdict === "dead" ? { severity: "dead", reason: void 0 } : null;
|
|
194584
194589
|
}
|
|
194585
|
-
function pickReplacement(params) {
|
|
194586
|
-
const candidates = modelAliases.filter(
|
|
194587
|
-
(alias) => !alias.hidden && !alias.fallback && params.authorized.has(alias.resolve) && // a model with no known env vars is routable through credentials we can't
|
|
194588
|
-
// see (Codex auth lands as an on-disk auth.json), so it stays a candidate.
|
|
194589
|
-
!envVarsFor(alias.resolve).some((envVar) => params.deadVars.has(envVar))
|
|
194590
|
-
);
|
|
194591
|
-
return (candidates.find((alias) => alias.preferred) ?? candidates[0])?.resolve;
|
|
194592
|
-
}
|
|
194593
194590
|
async function checkConfiguredCredentials(params) {
|
|
194594
194591
|
if (!params.model) return { kind: "ok" };
|
|
194595
194592
|
const present = envVarsFor(params.model).filter(hasEnvVar4);
|
|
@@ -194615,12 +194612,396 @@ async function checkConfiguredCredentials(params) {
|
|
|
194615
194612
|
failed.forEach(drop);
|
|
194616
194613
|
const first = failed[0];
|
|
194617
194614
|
if (!first) return { kind: "ok" };
|
|
194618
|
-
|
|
194619
|
-
|
|
194620
|
-
|
|
194621
|
-
|
|
194615
|
+
return { kind: "dead", credential: first.envVar, reason: first.failure.reason };
|
|
194616
|
+
}
|
|
194617
|
+
|
|
194618
|
+
// utils/normalizeEnv.ts
|
|
194619
|
+
function hasControlCharacter(value2) {
|
|
194620
|
+
return [...value2].some((ch) => {
|
|
194621
|
+
const code = ch.charCodeAt(0);
|
|
194622
|
+
return code < 32 || code === 127;
|
|
194623
|
+
});
|
|
194624
|
+
}
|
|
194625
|
+
function sanitizeSecret(key, value2) {
|
|
194626
|
+
const trimmed = value2.trim();
|
|
194627
|
+
if (trimmed.length === 0) {
|
|
194628
|
+
log.warning(
|
|
194629
|
+
`\xBB ${key} is whitespace-only \u2014 leaving env var unchanged. check your secret value.`
|
|
194630
|
+
);
|
|
194631
|
+
return null;
|
|
194632
|
+
}
|
|
194633
|
+
if (key.endsWith("_API_KEY") && hasControlCharacter(trimmed)) {
|
|
194634
|
+
log.warning(
|
|
194635
|
+
`\xBB ${key} contains a line break or control character and cannot be sent as an HTTP header \u2014 leaving env var unchanged. re-save the secret as a single line.`
|
|
194636
|
+
);
|
|
194637
|
+
return null;
|
|
194638
|
+
}
|
|
194639
|
+
if (trimmed !== value2) {
|
|
194640
|
+
log.warning(
|
|
194641
|
+
`\xBB stripped whitespace from ${key} (whitespace in secret values breaks GitHub Actions log masking)`
|
|
194642
|
+
);
|
|
194643
|
+
}
|
|
194644
|
+
maskSecret(trimmed);
|
|
194645
|
+
return trimmed;
|
|
194646
|
+
}
|
|
194647
|
+
function normalizeEnv() {
|
|
194648
|
+
const upperKeys = /* @__PURE__ */ new Map();
|
|
194649
|
+
for (const key of Object.keys(process.env)) {
|
|
194650
|
+
const upper2 = key.toUpperCase();
|
|
194651
|
+
const existing = upperKeys.get(upper2) || [];
|
|
194652
|
+
existing.push(key);
|
|
194653
|
+
upperKeys.set(upper2, existing);
|
|
194654
|
+
}
|
|
194655
|
+
for (const [upperKey, keys] of upperKeys) {
|
|
194656
|
+
if (keys.length === 1) {
|
|
194657
|
+
const key = keys[0];
|
|
194658
|
+
if (key !== upperKey) {
|
|
194659
|
+
process.env[upperKey] = process.env[key];
|
|
194660
|
+
delete process.env[key];
|
|
194661
|
+
}
|
|
194662
|
+
continue;
|
|
194663
|
+
}
|
|
194664
|
+
const values = keys.map((k2) => process.env[k2]);
|
|
194665
|
+
const uniqueValues = new Set(values);
|
|
194666
|
+
if (uniqueValues.size > 1) {
|
|
194667
|
+
log.warning(
|
|
194668
|
+
`env var conflict: ${keys.join(", ")} have different values. using uppercase ${upperKey}.`
|
|
194669
|
+
);
|
|
194670
|
+
}
|
|
194671
|
+
const preferredKey = keys.find((k2) => k2 === upperKey) || keys[0];
|
|
194672
|
+
const preferredValue = process.env[preferredKey];
|
|
194673
|
+
for (const key of keys) {
|
|
194674
|
+
delete process.env[key];
|
|
194675
|
+
}
|
|
194676
|
+
process.env[upperKey] = preferredValue;
|
|
194677
|
+
}
|
|
194678
|
+
for (const key of Object.keys(process.env)) {
|
|
194679
|
+
if (!isSensitiveEnvName(key)) continue;
|
|
194680
|
+
const value2 = process.env[key];
|
|
194681
|
+
if (typeof value2 !== "string" || value2.length === 0) continue;
|
|
194682
|
+
const sanitized = sanitizeSecret(key, value2);
|
|
194683
|
+
if (sanitized !== null) process.env[key] = sanitized;
|
|
194684
|
+
}
|
|
194685
|
+
}
|
|
194686
|
+
|
|
194687
|
+
// utils/subscriptionCredentials.ts
|
|
194688
|
+
var subscriptionNameSchema = external_exports.enum([
|
|
194689
|
+
"CLAUDE_CODE_OAUTH_TOKEN",
|
|
194690
|
+
"CODEX_AUTH_JSON",
|
|
194691
|
+
"GROK_AUTH_JSON"
|
|
194692
|
+
]);
|
|
194693
|
+
var subscriptionSchema = external_exports.object({
|
|
194694
|
+
id: external_exports.string(),
|
|
194695
|
+
credentialId: external_exports.string(),
|
|
194696
|
+
name: subscriptionNameSchema,
|
|
194697
|
+
label: external_exports.string(),
|
|
194698
|
+
owner: external_exports.string(),
|
|
194699
|
+
inherited: external_exports.boolean(),
|
|
194700
|
+
shared: external_exports.boolean(),
|
|
194701
|
+
registeredAt: external_exports.string(),
|
|
194702
|
+
/** the provider's last verdict on the current value; null = usable or never checked. */
|
|
194703
|
+
status: external_exports.enum(["rejected", "exhausted"]).nullish(),
|
|
194704
|
+
resetAt: external_exports.string().nullish()
|
|
194705
|
+
});
|
|
194706
|
+
var subscriptionListSchema = external_exports.object({
|
|
194707
|
+
subscriptions: external_exports.array(subscriptionSchema),
|
|
194708
|
+
writable: external_exports.boolean()
|
|
194709
|
+
});
|
|
194710
|
+
var subscriptionInputSchema = external_exports.object({
|
|
194711
|
+
name: subscriptionNameSchema,
|
|
194712
|
+
value: external_exports.string().trim().min(1).refine((value2) => Buffer.byteLength(value2, "utf8") <= 49152),
|
|
194713
|
+
replaceId: external_exports.string().optional()
|
|
194714
|
+
});
|
|
194715
|
+
var credentialCandidateSchema = external_exports.object({
|
|
194716
|
+
id: external_exports.string(),
|
|
194717
|
+
name: external_exports.string(),
|
|
194718
|
+
source: external_exports.enum(["repo", "account", "personal"])
|
|
194719
|
+
});
|
|
194720
|
+
var credentialAccessSchema = external_exports.object({
|
|
194721
|
+
token: external_exports.string(),
|
|
194722
|
+
candidates: external_exports.array(credentialCandidateSchema)
|
|
194723
|
+
});
|
|
194724
|
+
var selectedCredentialSchema = external_exports.object({
|
|
194725
|
+
name: external_exports.string(),
|
|
194726
|
+
value: external_exports.string(),
|
|
194727
|
+
receipt: external_exports.string()
|
|
194728
|
+
});
|
|
194729
|
+
|
|
194730
|
+
// utils/subscriptionProbe.ts
|
|
194731
|
+
var identitySchema = external_exports.object({
|
|
194732
|
+
email: external_exports.string().email().optional(),
|
|
194733
|
+
sub: external_exports.string().optional(),
|
|
194734
|
+
user_id: external_exports.string().optional(),
|
|
194735
|
+
account_id: external_exports.string().optional(),
|
|
194736
|
+
email_verified: external_exports.boolean().optional()
|
|
194737
|
+
});
|
|
194738
|
+
var codexWindowSchema = external_exports.object({ used_percent: external_exports.number().optional(), reset_at: external_exports.number().optional() }).nullish();
|
|
194739
|
+
var codexUsageSchema = external_exports.object({
|
|
194740
|
+
rate_limit: external_exports.object({
|
|
194741
|
+
allowed: external_exports.boolean(),
|
|
194742
|
+
limit_reached: external_exports.boolean(),
|
|
194743
|
+
primary_window: codexWindowSchema,
|
|
194744
|
+
secondary_window: codexWindowSchema
|
|
194745
|
+
}).optional()
|
|
194746
|
+
});
|
|
194747
|
+
var unknown4 = { status: "unknown" };
|
|
194748
|
+
async function readSubscriptionEndpoint(input) {
|
|
194749
|
+
const headers = new Headers({ authorization: `Bearer ${input.token}` });
|
|
194750
|
+
if (input.accountId) headers.set("ChatGPT-Account-Id", input.accountId);
|
|
194751
|
+
if (input.grokBilling) headers.set("x-xai-token-auth", "xai-grok-cli");
|
|
194752
|
+
return fetch(input.url, {
|
|
194753
|
+
headers,
|
|
194754
|
+
redirect: "error",
|
|
194755
|
+
signal: AbortSignal.timeout(1e4)
|
|
194756
|
+
}).catch(() => null);
|
|
194757
|
+
}
|
|
194758
|
+
async function probeSubscription(input) {
|
|
194759
|
+
if (input.name === "CLAUDE_CODE_OAUTH_TOKEN") {
|
|
194760
|
+
const result = await preflightClaudeSubscription({ token: input.value, model: input.model });
|
|
194761
|
+
if (result.usable)
|
|
194762
|
+
return result.status !== void 0 && result.status < 300 ? { status: "usable" } : unknown4;
|
|
194763
|
+
return result.status === 429 ? { status: "exhausted", detail: result.reason, resetAt: result.resetAt } : { status: "rejected", detail: result.reason };
|
|
194764
|
+
}
|
|
194765
|
+
const codex2 = input.name === "CODEX_AUTH_JSON" ? parseCodexAuthBody(input.value) : null;
|
|
194766
|
+
const grok = input.name === "GROK_AUTH_JSON" ? parseXaiAuthBody(input.value) : null;
|
|
194767
|
+
const auth2 = codex2 ?? grok;
|
|
194768
|
+
if (!auth2) return { status: "rejected", detail: "the stored credential is malformed" };
|
|
194769
|
+
if (auth2.refresh_rejected_at)
|
|
194770
|
+
return { status: "rejected", detail: `refresh rejected at ${auth2.refresh_rejected_at}` };
|
|
194771
|
+
if (grok && input.model)
|
|
194772
|
+
return probeInference({
|
|
194773
|
+
name: "XAI_API_KEY",
|
|
194774
|
+
value: grok.tokens.access_token,
|
|
194775
|
+
model: input.model
|
|
194776
|
+
});
|
|
194777
|
+
const response = await readSubscriptionEndpoint({
|
|
194778
|
+
url: codex2 ? "https://chatgpt.com/backend-api/wham/usage" : "https://cli-chat-proxy.grok.com/v1/billing?format=credits",
|
|
194779
|
+
token: auth2.tokens.access_token,
|
|
194780
|
+
accountId: codex2?.tokens.account_id,
|
|
194781
|
+
grokBilling: !!grok
|
|
194782
|
+
});
|
|
194783
|
+
if (response?.status === 401) return { status: "rejected", detail: "rejected with 401" };
|
|
194784
|
+
if (!response?.ok) return unknown4;
|
|
194785
|
+
const body = await response.json().catch(() => null);
|
|
194786
|
+
if (!codex2) return unknown4;
|
|
194787
|
+
const parsed2 = codexUsageSchema.safeParse(body);
|
|
194788
|
+
const limit = parsed2.success ? parsed2.data.rate_limit : void 0;
|
|
194789
|
+
if (!limit) return unknown4;
|
|
194790
|
+
if (limit.allowed && !limit.limit_reached) return { status: "usable" };
|
|
194791
|
+
const windows = [limit.primary_window, limit.secondary_window];
|
|
194792
|
+
const full = windows.filter((window2) => (window2?.used_percent ?? 0) >= 100);
|
|
194793
|
+
const resets = (full.length ? full : windows).flatMap(
|
|
194794
|
+
(window2) => window2?.reset_at ? [window2.reset_at] : []
|
|
194795
|
+
);
|
|
194796
|
+
return {
|
|
194797
|
+
status: "exhausted",
|
|
194798
|
+
detail: "the ChatGPT plan's usage limit is reached",
|
|
194799
|
+
resetAt: resets.length ? new Date(Math.max(...resets) * 1e3) : void 0
|
|
194800
|
+
};
|
|
194801
|
+
}
|
|
194802
|
+
async function probeInference(input) {
|
|
194803
|
+
const anthropic = input.name === "ANTHROPIC_API_KEY";
|
|
194804
|
+
const openai = input.name === "OPENAI_API_KEY";
|
|
194805
|
+
if (!anthropic && !openai && input.name !== "XAI_API_KEY") {
|
|
194806
|
+
const result = await verifyCredential({ envVar: input.name, value: input.value });
|
|
194807
|
+
return result === "dead" ? { status: "rejected", detail: "rejected by its provider" } : result === "alive" ? { status: "usable" } : unknown4;
|
|
194808
|
+
}
|
|
194809
|
+
const headers = new Headers({ "content-type": "application/json" });
|
|
194810
|
+
if (anthropic) {
|
|
194811
|
+
headers.set("x-api-key", input.value);
|
|
194812
|
+
headers.set("anthropic-version", "2023-06-01");
|
|
194813
|
+
} else headers.set("authorization", `Bearer ${input.value}`);
|
|
194814
|
+
const response = await fetch(
|
|
194815
|
+
anthropic ? "https://api.anthropic.com/v1/messages" : openai ? "https://api.openai.com/v1/responses" : "https://api.x.ai/v1/chat/completions",
|
|
194816
|
+
{
|
|
194817
|
+
method: "POST",
|
|
194818
|
+
headers,
|
|
194819
|
+
body: JSON.stringify(
|
|
194820
|
+
openai ? { model: input.model, input: "Reply OK.", max_output_tokens: 16, store: false } : {
|
|
194821
|
+
model: input.model,
|
|
194822
|
+
messages: [{ role: "user", content: "Reply OK." }],
|
|
194823
|
+
max_tokens: 1
|
|
194824
|
+
}
|
|
194825
|
+
),
|
|
194826
|
+
redirect: "error",
|
|
194827
|
+
signal: AbortSignal.timeout(1e4)
|
|
194828
|
+
}
|
|
194829
|
+
).catch(() => null);
|
|
194830
|
+
if (response?.status === 400 && input.name === "XAI_API_KEY") {
|
|
194831
|
+
const error49 = external_exports.object({ error: external_exports.string() }).safeParse(await response.json().catch(() => null));
|
|
194832
|
+
return error49.success && error49.data.error.startsWith("Incorrect API key provided.") ? { status: "rejected", detail: "rejected with 400: incorrect API key" } : unknown4;
|
|
194833
|
+
}
|
|
194834
|
+
if (response?.status === 429 && openai) {
|
|
194835
|
+
const error49 = external_exports.object({ error: external_exports.object({ code: external_exports.string() }) }).safeParse(await response.json().catch(() => null));
|
|
194836
|
+
return error49.success && error49.data.error.code === "insufficient_quota" ? { status: "exhausted", detail: "refused with 429: insufficient quota", resetAt: void 0 } : unknown4;
|
|
194837
|
+
}
|
|
194838
|
+
await response?.body?.cancel();
|
|
194839
|
+
if (!response) return unknown4;
|
|
194840
|
+
if (response.ok) return { status: "usable" };
|
|
194841
|
+
if (response.status === 401 || response.status === 403)
|
|
194842
|
+
return { status: "rejected", detail: `rejected with ${response.status}` };
|
|
194843
|
+
if (response.status === 402)
|
|
194844
|
+
return { status: "exhausted", detail: "refused with 402", resetAt: void 0 };
|
|
194845
|
+
return unknown4;
|
|
194846
|
+
}
|
|
194847
|
+
|
|
194848
|
+
// utils/credentialPool.ts
|
|
194849
|
+
var receipts = {};
|
|
194850
|
+
var workflowCredentials = {};
|
|
194851
|
+
function saveReceipts() {
|
|
194852
|
+
for (const receipt of Object.values(receipts)) maskSecret(receipt);
|
|
194853
|
+
saveSecretState("credential_receipts", JSON.stringify(receipts));
|
|
194854
|
+
}
|
|
194855
|
+
var select = op(
|
|
194856
|
+
async (input) => {
|
|
194857
|
+
const response = await apiFetch({
|
|
194858
|
+
path: "/api/runtime/credentials",
|
|
194859
|
+
method: "POST",
|
|
194860
|
+
headers: {
|
|
194861
|
+
authorization: `Bearer ${input.access.token}`,
|
|
194862
|
+
"content-type": "application/json"
|
|
194863
|
+
},
|
|
194864
|
+
body: JSON.stringify({ id: input.candidate.id }),
|
|
194865
|
+
signal: AbortSignal.timeout(35e3)
|
|
194866
|
+
});
|
|
194867
|
+
if (response.status === 404) return null;
|
|
194868
|
+
if (!response.ok)
|
|
194869
|
+
throw new Error("could not load a configured credential; no fallback was charged", {
|
|
194870
|
+
cause: response.status
|
|
194871
|
+
});
|
|
194872
|
+
return selectedCredentialSchema.parse(await response.json());
|
|
194873
|
+
},
|
|
194874
|
+
{
|
|
194875
|
+
retries: [250, 1e3],
|
|
194876
|
+
bail: (error49) => error49 instanceof Error && typeof error49.cause === "number" && error49.cause >= 400 && error49.cause < 500
|
|
194877
|
+
}
|
|
194878
|
+
);
|
|
194879
|
+
function subscriptionForModel(model) {
|
|
194880
|
+
if (!model.includes("/")) return null;
|
|
194881
|
+
const provider2 = getModelProvider(model);
|
|
194882
|
+
return provider2 === "anthropic" ? "CLAUDE_CODE_OAUTH_TOKEN" : provider2 === "openai" ? "CODEX_AUTH_JSON" : provider2 === "xai" ? "GROK_AUTH_JSON" : null;
|
|
194883
|
+
}
|
|
194884
|
+
async function initializeCredentialPool(input) {
|
|
194885
|
+
const access = input.ctx.credentialAccess;
|
|
194886
|
+
if (!access) return;
|
|
194887
|
+
const model = discoveryModel(input.slug);
|
|
194888
|
+
maskSecret(access.token);
|
|
194889
|
+
const names = /* @__PURE__ */ new Set([
|
|
194890
|
+
...access.candidates.map((candidate) => candidate.name),
|
|
194891
|
+
...subscriptionNameSchema.options,
|
|
194892
|
+
"ANTHROPIC_API_KEY",
|
|
194893
|
+
"OPENAI_API_KEY",
|
|
194894
|
+
"XAI_API_KEY"
|
|
194895
|
+
]);
|
|
194896
|
+
for (const name of names) {
|
|
194897
|
+
const value2 = process.env[name];
|
|
194898
|
+
if (value2 && !input.injected.includes(name)) workflowCredentials[name] = value2;
|
|
194899
|
+
}
|
|
194900
|
+
for (const name of subscriptionNameSchema.options) {
|
|
194901
|
+
if (model && subscriptionForModel(model) !== name) continue;
|
|
194902
|
+
if (workflowCredentials[name]) continue;
|
|
194903
|
+
const candidate = access.candidates.find((item) => item.name === name);
|
|
194904
|
+
if (!candidate) continue;
|
|
194905
|
+
const selected = await select({ access, candidate }).catch(() => {
|
|
194906
|
+
log.warning(`\xBB could not load ${name} for model discovery; selection will retry if needed`);
|
|
194907
|
+
return null;
|
|
194908
|
+
});
|
|
194909
|
+
if (!selected) continue;
|
|
194910
|
+
const value2 = sanitizeSecret(name, selected.value);
|
|
194911
|
+
if (!value2) continue;
|
|
194912
|
+
process.env[name] = value2;
|
|
194913
|
+
receipts[name] = selected.receipt;
|
|
194914
|
+
}
|
|
194915
|
+
saveReceipts();
|
|
194916
|
+
}
|
|
194917
|
+
function discoveryModel(slug2) {
|
|
194918
|
+
const value2 = process.env.PULLFROG_MODEL?.trim() || slug2?.trim();
|
|
194919
|
+
if (!value2) return void 0;
|
|
194920
|
+
return resolveCliModel(value2) ?? (value2.includes("/") ? value2 : void 0);
|
|
194921
|
+
}
|
|
194922
|
+
function resolvePoolModel(input) {
|
|
194923
|
+
const agent2 = resolveAgent({
|
|
194924
|
+
model: void 0,
|
|
194925
|
+
proxyModel: void 0,
|
|
194926
|
+
codexAgent: input.codexAgent
|
|
194622
194927
|
});
|
|
194623
|
-
|
|
194928
|
+
if (agent2.name === "opencode") return autoSelectModel();
|
|
194929
|
+
const provider2 = agent2.name === "claude" ? "anthropic" : "openai";
|
|
194930
|
+
return modelAliases.find(
|
|
194931
|
+
(alias) => alias.provider === provider2 && alias.preferred && !alias.hidden && !alias.fallback && !alias.routing
|
|
194932
|
+
)?.resolve;
|
|
194933
|
+
}
|
|
194934
|
+
async function selectConfiguredCredential(input) {
|
|
194935
|
+
const access = input.ctx.credentialAccess;
|
|
194936
|
+
const model = input.model;
|
|
194937
|
+
if (!access || !model?.includes("/") || getProviderGatewayUrl(model)) return false;
|
|
194938
|
+
const subscription = subscriptionForModel(model);
|
|
194939
|
+
const names = getModelEnvVars(model).filter((name) => name !== subscription);
|
|
194940
|
+
if (subscription) names.unshift(subscription);
|
|
194941
|
+
const candidates = access.candidates.filter((candidate) => names.includes(candidate.name));
|
|
194942
|
+
if (!candidates.length) return false;
|
|
194943
|
+
const refused = [];
|
|
194944
|
+
for (const name of names.filter((item) => workflowCredentials[item])) {
|
|
194945
|
+
const value2 = workflowCredentials[name];
|
|
194946
|
+
const verdict = await probe({ name, value: value2, model });
|
|
194947
|
+
if (usable(verdict)) {
|
|
194948
|
+
activate({ names, name, value: value2, receipt: void 0, model });
|
|
194949
|
+
return true;
|
|
194950
|
+
}
|
|
194951
|
+
refused.push(`${name} from the workflow: ${verdict.detail}`);
|
|
194952
|
+
log.info(`\xBB ${name} from the workflow unavailable; trying the next credential`);
|
|
194953
|
+
}
|
|
194954
|
+
for (const candidate of candidates) {
|
|
194955
|
+
const selected = await select({ access, candidate });
|
|
194956
|
+
if (!selected) continue;
|
|
194957
|
+
const verdict = await probe({ ...selected, model });
|
|
194958
|
+
await reportVerdict({ receipt: selected.receipt, verdict });
|
|
194959
|
+
if (usable(verdict)) {
|
|
194960
|
+
activate({ names, ...selected, model });
|
|
194961
|
+
log.info(`\xBB selected ${candidate.name} from ${candidate.source} scope`);
|
|
194962
|
+
return true;
|
|
194963
|
+
}
|
|
194964
|
+
refused.push(`${candidate.name} from ${candidate.source} scope: ${verdict.detail}`);
|
|
194965
|
+
log.info(
|
|
194966
|
+
`\xBB ${candidate.name} from ${candidate.source} scope unavailable; trying the next credential`
|
|
194967
|
+
);
|
|
194968
|
+
}
|
|
194969
|
+
throw new Error(
|
|
194970
|
+
`all configured credentials for ${model} were rejected or exhausted (${refused.join("; ")}); no other model or provider was selected`
|
|
194971
|
+
);
|
|
194972
|
+
}
|
|
194973
|
+
async function probe(input) {
|
|
194974
|
+
const subscription = subscriptionNameSchema.safeParse(input.name);
|
|
194975
|
+
const model = stripProviderPrefix(input.model);
|
|
194976
|
+
return subscription.success ? probeSubscription({ name: subscription.data, value: input.value, model }) : probeInference({ ...input, model });
|
|
194977
|
+
}
|
|
194978
|
+
function usable(verdict) {
|
|
194979
|
+
return verdict.status !== "rejected" && verdict.status !== "exhausted";
|
|
194980
|
+
}
|
|
194981
|
+
async function reportVerdict(input) {
|
|
194982
|
+
if (input.verdict.status === "unknown") return;
|
|
194983
|
+
await apiFetch({
|
|
194984
|
+
path: "/api/runtime/credentials/status",
|
|
194985
|
+
method: "POST",
|
|
194986
|
+
headers: { authorization: `Bearer ${input.receipt}`, "content-type": "application/json" },
|
|
194987
|
+
body: JSON.stringify(input.verdict),
|
|
194988
|
+
signal: AbortSignal.timeout(5e3)
|
|
194989
|
+
}).catch(() => log.debug("\xBB could not record a credential verdict"));
|
|
194990
|
+
}
|
|
194991
|
+
function activate(input) {
|
|
194992
|
+
for (const name of input.names) {
|
|
194993
|
+
delete process.env[name];
|
|
194994
|
+
delete receipts[name];
|
|
194995
|
+
if (name === "CODEX_AUTH_JSON" || name === "GROK_AUTH_JSON") clearInstalledSubscription(name);
|
|
194996
|
+
}
|
|
194997
|
+
const value2 = sanitizeSecret(input.name, input.value);
|
|
194998
|
+
if (!value2) throw new Error("configured credential was empty");
|
|
194999
|
+
process.env[input.name] = value2;
|
|
195000
|
+
if (input.receipt) receipts[input.name] = input.receipt;
|
|
195001
|
+
installCodexAuth();
|
|
195002
|
+
installXaiAuth();
|
|
195003
|
+
saveReceipts();
|
|
195004
|
+
authorizeModel(input.model);
|
|
194624
195005
|
}
|
|
194625
195006
|
|
|
194626
195007
|
// utils/gitAuthServer.ts
|
|
@@ -195326,75 +195707,6 @@ function buildModelAccessError(input) {
|
|
|
195326
195707
|
return [headline, "", why, "", cta].join("\n");
|
|
195327
195708
|
}
|
|
195328
195709
|
|
|
195329
|
-
// utils/normalizeEnv.ts
|
|
195330
|
-
function hasControlCharacter(value2) {
|
|
195331
|
-
return [...value2].some((ch) => {
|
|
195332
|
-
const code = ch.charCodeAt(0);
|
|
195333
|
-
return code < 32 || code === 127;
|
|
195334
|
-
});
|
|
195335
|
-
}
|
|
195336
|
-
function sanitizeSecret(key, value2) {
|
|
195337
|
-
const trimmed = value2.trim();
|
|
195338
|
-
if (trimmed.length === 0) {
|
|
195339
|
-
log.warning(
|
|
195340
|
-
`\xBB ${key} is whitespace-only \u2014 leaving env var unchanged. check your secret value.`
|
|
195341
|
-
);
|
|
195342
|
-
return null;
|
|
195343
|
-
}
|
|
195344
|
-
if (key.endsWith("_API_KEY") && hasControlCharacter(trimmed)) {
|
|
195345
|
-
log.warning(
|
|
195346
|
-
`\xBB ${key} contains a line break or control character and cannot be sent as an HTTP header \u2014 leaving env var unchanged. re-save the secret as a single line.`
|
|
195347
|
-
);
|
|
195348
|
-
return null;
|
|
195349
|
-
}
|
|
195350
|
-
if (trimmed !== value2) {
|
|
195351
|
-
log.warning(
|
|
195352
|
-
`\xBB stripped whitespace from ${key} (whitespace in secret values breaks GitHub Actions log masking)`
|
|
195353
|
-
);
|
|
195354
|
-
}
|
|
195355
|
-
maskSecret(trimmed);
|
|
195356
|
-
return trimmed;
|
|
195357
|
-
}
|
|
195358
|
-
function normalizeEnv() {
|
|
195359
|
-
const upperKeys = /* @__PURE__ */ new Map();
|
|
195360
|
-
for (const key of Object.keys(process.env)) {
|
|
195361
|
-
const upper2 = key.toUpperCase();
|
|
195362
|
-
const existing = upperKeys.get(upper2) || [];
|
|
195363
|
-
existing.push(key);
|
|
195364
|
-
upperKeys.set(upper2, existing);
|
|
195365
|
-
}
|
|
195366
|
-
for (const [upperKey, keys] of upperKeys) {
|
|
195367
|
-
if (keys.length === 1) {
|
|
195368
|
-
const key = keys[0];
|
|
195369
|
-
if (key !== upperKey) {
|
|
195370
|
-
process.env[upperKey] = process.env[key];
|
|
195371
|
-
delete process.env[key];
|
|
195372
|
-
}
|
|
195373
|
-
continue;
|
|
195374
|
-
}
|
|
195375
|
-
const values = keys.map((k2) => process.env[k2]);
|
|
195376
|
-
const uniqueValues = new Set(values);
|
|
195377
|
-
if (uniqueValues.size > 1) {
|
|
195378
|
-
log.warning(
|
|
195379
|
-
`env var conflict: ${keys.join(", ")} have different values. using uppercase ${upperKey}.`
|
|
195380
|
-
);
|
|
195381
|
-
}
|
|
195382
|
-
const preferredKey = keys.find((k2) => k2 === upperKey) || keys[0];
|
|
195383
|
-
const preferredValue = process.env[preferredKey];
|
|
195384
|
-
for (const key of keys) {
|
|
195385
|
-
delete process.env[key];
|
|
195386
|
-
}
|
|
195387
|
-
process.env[upperKey] = preferredValue;
|
|
195388
|
-
}
|
|
195389
|
-
for (const key of Object.keys(process.env)) {
|
|
195390
|
-
if (!isSensitiveEnvName(key)) continue;
|
|
195391
|
-
const value2 = process.env[key];
|
|
195392
|
-
if (typeof value2 !== "string" || value2.length === 0) continue;
|
|
195393
|
-
const sanitized = sanitizeSecret(key, value2);
|
|
195394
|
-
if (sanitized !== null) process.env[key] = sanitized;
|
|
195395
|
-
}
|
|
195396
|
-
}
|
|
195397
|
-
|
|
195398
195710
|
// utils/overrides.ts
|
|
195399
195711
|
var DENIED_OVERRIDE_NAMES = /* @__PURE__ */ new Set([
|
|
195400
195712
|
"GITHUB_TOKEN",
|
|
@@ -195965,7 +196277,6 @@ ${ctx.error}` : ctx.error;
|
|
|
195965
196277
|
workflowRun: runId ? { owner: repoContext.owner, repo: repoContext.name, runId } : void 0,
|
|
195966
196278
|
customParts,
|
|
195967
196279
|
model: ctx.toolState.model,
|
|
195968
|
-
fallbackFrom: ctx.toolState.modelFallback?.from,
|
|
195969
196280
|
clamped: ctx.toolState.modelClamped,
|
|
195970
196281
|
unselectedProxyDefault: ctx.toolState.unselectedProxyDefault,
|
|
195971
196282
|
oss: ctx.toolState.oss,
|
|
@@ -196349,7 +196660,8 @@ async function fetchRunContext(params) {
|
|
|
196349
196660
|
const timeoutId = setTimeout(() => controller.abort(), timeoutMs);
|
|
196350
196661
|
try {
|
|
196351
196662
|
const headers = {
|
|
196352
|
-
Authorization: `Bearer ${params.token}
|
|
196663
|
+
Authorization: `Bearer ${params.token}`,
|
|
196664
|
+
"X-Pullfrog-Credential-Pools": "1"
|
|
196353
196665
|
};
|
|
196354
196666
|
if (params.oidcToken) {
|
|
196355
196667
|
headers["X-GitHub-OIDC-Token"] = params.oidcToken;
|
|
@@ -197646,16 +197958,20 @@ async function main() {
|
|
|
197646
197958
|
const preIntrospectionDirty = repoDir ? await dirtyTrackedPaths({ cwd: repoDir }) : null;
|
|
197647
197959
|
const opencodeCliPath = await agents.opencode.install();
|
|
197648
197960
|
captureBaselineModels(opencodeCliPath);
|
|
197961
|
+
const injected = [];
|
|
197649
197962
|
if (runContext.dbSecrets) {
|
|
197650
197963
|
for (const [key, value2] of Object.entries(runContext.dbSecrets)) {
|
|
197651
197964
|
if (!process.env[key]) {
|
|
197652
197965
|
const sanitized = sanitizeSecret(key, value2);
|
|
197653
|
-
if (sanitized
|
|
197966
|
+
if (sanitized === null) continue;
|
|
197967
|
+
process.env[key] = sanitized;
|
|
197968
|
+
injected.push(key);
|
|
197654
197969
|
}
|
|
197655
197970
|
}
|
|
197656
197971
|
const count = Object.keys(runContext.dbSecrets).length;
|
|
197657
197972
|
if (count > 0) log.info(`\xBB ${count} db secret(s) loaded`);
|
|
197658
197973
|
}
|
|
197974
|
+
await initializeCredentialPool({ ctx: runContext, slug: payload.model, injected });
|
|
197659
197975
|
installCodexAuth();
|
|
197660
197976
|
installXaiAuth();
|
|
197661
197977
|
captureAuthorizedModels(opencodeCliPath);
|
|
@@ -197729,13 +198045,23 @@ async function main() {
|
|
|
197729
198045
|
}
|
|
197730
198046
|
const gitAuthServer = __using(_stack2, await startGitAuthServer(tmpdir3), true);
|
|
197731
198047
|
setGitAuthServer(gitAuthServer);
|
|
198048
|
+
let resolvedModel = resolveModel({ slug: payload.model });
|
|
198049
|
+
if (!resolvedModel && !payload.proxyModel && runContext.credentialAccess?.candidates.length) {
|
|
198050
|
+
resolvedModel = resolvePoolModel({
|
|
198051
|
+
codexAgent: runContext.repoSettings.codexAgent || payload.codexArm === true
|
|
198052
|
+
});
|
|
198053
|
+
}
|
|
198054
|
+
let poolSelected = !payload.proxyModel && await selectConfiguredCredential({
|
|
198055
|
+
ctx: runContext,
|
|
198056
|
+
model: resolvedModel
|
|
198057
|
+
});
|
|
197732
198058
|
const access = decideModelAccess({
|
|
197733
198059
|
modelExplicit: payload.modelExplicit ?? false,
|
|
197734
198060
|
model: payload.model,
|
|
197735
198061
|
oss: runContext.oss,
|
|
197736
198062
|
proxyActive: !!payload.proxyModel,
|
|
197737
198063
|
subsidyTarget: runContext.proxyModel,
|
|
197738
|
-
resolvedModel
|
|
198064
|
+
resolvedModel,
|
|
197739
198065
|
authorized: getAuthorizedModels()
|
|
197740
198066
|
});
|
|
197741
198067
|
if (access.kind === "error") {
|
|
@@ -197749,15 +198075,19 @@ async function main() {
|
|
|
197749
198075
|
);
|
|
197750
198076
|
}
|
|
197751
198077
|
if (access.kind === "proxy") payload.proxyModel = access.target;
|
|
197752
|
-
if (access.kind === "byok")
|
|
198078
|
+
if (access.kind === "byok") {
|
|
198079
|
+
payload.proxyModel = void 0;
|
|
198080
|
+
if (!poolSelected)
|
|
198081
|
+
poolSelected = await selectConfiguredCredential({
|
|
198082
|
+
ctx: runContext,
|
|
198083
|
+
model: resolvedModel
|
|
198084
|
+
});
|
|
198085
|
+
}
|
|
197753
198086
|
if (runContext.oss && payload.proxyModel) {
|
|
197754
198087
|
payload.effort = ossEffortFloor({ payload });
|
|
197755
198088
|
}
|
|
197756
|
-
|
|
197757
|
-
const credentials = payload.proxyModel ? { kind: "ok" } : await checkConfiguredCredentials({
|
|
197758
|
-
model: configuredModel,
|
|
197759
|
-
authorized: getAuthorizedModels()
|
|
197760
|
-
});
|
|
198089
|
+
if (payload.proxyModel) resolvedModel = void 0;
|
|
198090
|
+
const credentials = payload.proxyModel || poolSelected ? { kind: "ok" } : await checkConfiguredCredentials({ model: resolvedModel });
|
|
197761
198091
|
if (credentials.kind === "dead") {
|
|
197762
198092
|
throw new Error(
|
|
197763
198093
|
buildRejectedCredentialError({
|
|
@@ -197769,13 +198099,6 @@ async function main() {
|
|
|
197769
198099
|
})
|
|
197770
198100
|
);
|
|
197771
198101
|
}
|
|
197772
|
-
if (credentials.kind === "fellBack" && configuredModel) {
|
|
197773
|
-
log.warning(
|
|
197774
|
-
`\xBB ${credentials.credential} was rejected by its provider \u2014 running ${credentials.replacement} instead of ${configuredModel}`
|
|
197775
|
-
);
|
|
197776
|
-
toolState.modelFallback = { from: configuredModel };
|
|
197777
|
-
}
|
|
197778
|
-
const resolvedModel = credentials.kind === "fellBack" ? credentials.replacement : configuredModel;
|
|
197779
198102
|
vertexCredentials = materializeVertexCredentials({ model: resolvedModel });
|
|
197780
198103
|
let agent2 = resolveAgent({
|
|
197781
198104
|
model: resolvedModel,
|
|
@@ -198085,12 +198408,12 @@ ${instructions.user}` : null,
|
|
|
198085
198408
|
if (payload.timeout === TIMEOUT_DISABLED) {
|
|
198086
198409
|
result = await Promise.race([agentPromise, activityTimeout.promise]);
|
|
198087
198410
|
} else {
|
|
198088
|
-
const
|
|
198089
|
-
if (payload.timeout &&
|
|
198411
|
+
const usable2 = resolveTimeoutMs(payload.timeout);
|
|
198412
|
+
if (payload.timeout && usable2 === null) {
|
|
198090
198413
|
log.warning(`invalid timeout "${payload.timeout}" (use --notimeout to disable), using 1h`);
|
|
198091
198414
|
}
|
|
198092
|
-
const timeoutMs =
|
|
198093
|
-
const actualTimeout =
|
|
198415
|
+
const timeoutMs = usable2 ?? 36e5;
|
|
198416
|
+
const actualTimeout = usable2 !== null ? payload.timeout : "1h";
|
|
198094
198417
|
let timeoutId;
|
|
198095
198418
|
const timeoutPromise = new Promise((_4, reject) => {
|
|
198096
198419
|
timeoutId = setTimeout(() => {
|