pullfrog 0.1.90 → 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 +425 -147
- package/dist/index.js +460 -142
- package/dist/internal.js +52 -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,
|
|
@@ -194546,17 +194546,17 @@ async function verifyCredential(params) {
|
|
|
194546
194546
|
const value2 = params.value.trim();
|
|
194547
194547
|
if (value2.length === 0) return "dead";
|
|
194548
194548
|
if (params.envVar === "CLAUDE_CODE_OAUTH_TOKEN") return verifyClaudeSubscription(value2);
|
|
194549
|
-
const
|
|
194550
|
-
if (!
|
|
194551
|
-
const request2 =
|
|
194549
|
+
const probe2 = PROBES[params.envVar];
|
|
194550
|
+
if (!probe2) return "unknown";
|
|
194551
|
+
const request2 = probe2.request(value2);
|
|
194552
194552
|
try {
|
|
194553
194553
|
const response = await fetch(request2.url, {
|
|
194554
194554
|
headers: request2.headers,
|
|
194555
194555
|
signal: AbortSignal.timeout(5e3)
|
|
194556
194556
|
});
|
|
194557
194557
|
if (response.ok) return "alive";
|
|
194558
|
-
if (
|
|
194559
|
-
return
|
|
194558
|
+
if (probe2.deadWhen) {
|
|
194559
|
+
return probe2.deadWhen(response.status, await response.text()) ? "dead" : "unknown";
|
|
194560
194560
|
}
|
|
194561
194561
|
return response.status === 401 ? "dead" : "unknown";
|
|
194562
194562
|
} catch {
|
|
@@ -194587,14 +194587,6 @@ async function checkOne(params) {
|
|
|
194587
194587
|
const verdict = await verifyCredential({ envVar: params.envVar, value: value2 });
|
|
194588
194588
|
return verdict === "dead" ? { severity: "dead", reason: void 0 } : null;
|
|
194589
194589
|
}
|
|
194590
|
-
function pickReplacement(params) {
|
|
194591
|
-
const candidates = modelAliases.filter(
|
|
194592
|
-
(alias) => !alias.hidden && !alias.fallback && params.authorized.has(alias.resolve) && // a model with no known env vars is routable through credentials we can't
|
|
194593
|
-
// see (Codex auth lands as an on-disk auth.json), so it stays a candidate.
|
|
194594
|
-
!envVarsFor(alias.resolve).some((envVar) => params.deadVars.has(envVar))
|
|
194595
|
-
);
|
|
194596
|
-
return (candidates.find((alias) => alias.preferred) ?? candidates[0])?.resolve;
|
|
194597
|
-
}
|
|
194598
194590
|
async function checkConfiguredCredentials(params) {
|
|
194599
194591
|
if (!params.model) return { kind: "ok" };
|
|
194600
194592
|
const present = envVarsFor(params.model).filter(hasEnvVar4);
|
|
@@ -194620,12 +194612,396 @@ async function checkConfiguredCredentials(params) {
|
|
|
194620
194612
|
failed.forEach(drop);
|
|
194621
194613
|
const first = failed[0];
|
|
194622
194614
|
if (!first) return { kind: "ok" };
|
|
194623
|
-
|
|
194624
|
-
|
|
194625
|
-
|
|
194626
|
-
|
|
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;
|
|
194627
194623
|
});
|
|
194628
|
-
|
|
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
|
|
194927
|
+
});
|
|
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);
|
|
194629
195005
|
}
|
|
194630
195006
|
|
|
194631
195007
|
// utils/gitAuthServer.ts
|
|
@@ -195331,75 +195707,6 @@ function buildModelAccessError(input) {
|
|
|
195331
195707
|
return [headline, "", why, "", cta].join("\n");
|
|
195332
195708
|
}
|
|
195333
195709
|
|
|
195334
|
-
// utils/normalizeEnv.ts
|
|
195335
|
-
function hasControlCharacter(value2) {
|
|
195336
|
-
return [...value2].some((ch) => {
|
|
195337
|
-
const code = ch.charCodeAt(0);
|
|
195338
|
-
return code < 32 || code === 127;
|
|
195339
|
-
});
|
|
195340
|
-
}
|
|
195341
|
-
function sanitizeSecret(key, value2) {
|
|
195342
|
-
const trimmed = value2.trim();
|
|
195343
|
-
if (trimmed.length === 0) {
|
|
195344
|
-
log.warning(
|
|
195345
|
-
`\xBB ${key} is whitespace-only \u2014 leaving env var unchanged. check your secret value.`
|
|
195346
|
-
);
|
|
195347
|
-
return null;
|
|
195348
|
-
}
|
|
195349
|
-
if (key.endsWith("_API_KEY") && hasControlCharacter(trimmed)) {
|
|
195350
|
-
log.warning(
|
|
195351
|
-
`\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.`
|
|
195352
|
-
);
|
|
195353
|
-
return null;
|
|
195354
|
-
}
|
|
195355
|
-
if (trimmed !== value2) {
|
|
195356
|
-
log.warning(
|
|
195357
|
-
`\xBB stripped whitespace from ${key} (whitespace in secret values breaks GitHub Actions log masking)`
|
|
195358
|
-
);
|
|
195359
|
-
}
|
|
195360
|
-
maskSecret(trimmed);
|
|
195361
|
-
return trimmed;
|
|
195362
|
-
}
|
|
195363
|
-
function normalizeEnv() {
|
|
195364
|
-
const upperKeys = /* @__PURE__ */ new Map();
|
|
195365
|
-
for (const key of Object.keys(process.env)) {
|
|
195366
|
-
const upper2 = key.toUpperCase();
|
|
195367
|
-
const existing = upperKeys.get(upper2) || [];
|
|
195368
|
-
existing.push(key);
|
|
195369
|
-
upperKeys.set(upper2, existing);
|
|
195370
|
-
}
|
|
195371
|
-
for (const [upperKey, keys] of upperKeys) {
|
|
195372
|
-
if (keys.length === 1) {
|
|
195373
|
-
const key = keys[0];
|
|
195374
|
-
if (key !== upperKey) {
|
|
195375
|
-
process.env[upperKey] = process.env[key];
|
|
195376
|
-
delete process.env[key];
|
|
195377
|
-
}
|
|
195378
|
-
continue;
|
|
195379
|
-
}
|
|
195380
|
-
const values = keys.map((k2) => process.env[k2]);
|
|
195381
|
-
const uniqueValues = new Set(values);
|
|
195382
|
-
if (uniqueValues.size > 1) {
|
|
195383
|
-
log.warning(
|
|
195384
|
-
`env var conflict: ${keys.join(", ")} have different values. using uppercase ${upperKey}.`
|
|
195385
|
-
);
|
|
195386
|
-
}
|
|
195387
|
-
const preferredKey = keys.find((k2) => k2 === upperKey) || keys[0];
|
|
195388
|
-
const preferredValue = process.env[preferredKey];
|
|
195389
|
-
for (const key of keys) {
|
|
195390
|
-
delete process.env[key];
|
|
195391
|
-
}
|
|
195392
|
-
process.env[upperKey] = preferredValue;
|
|
195393
|
-
}
|
|
195394
|
-
for (const key of Object.keys(process.env)) {
|
|
195395
|
-
if (!isSensitiveEnvName(key)) continue;
|
|
195396
|
-
const value2 = process.env[key];
|
|
195397
|
-
if (typeof value2 !== "string" || value2.length === 0) continue;
|
|
195398
|
-
const sanitized = sanitizeSecret(key, value2);
|
|
195399
|
-
if (sanitized !== null) process.env[key] = sanitized;
|
|
195400
|
-
}
|
|
195401
|
-
}
|
|
195402
|
-
|
|
195403
195710
|
// utils/overrides.ts
|
|
195404
195711
|
var DENIED_OVERRIDE_NAMES = /* @__PURE__ */ new Set([
|
|
195405
195712
|
"GITHUB_TOKEN",
|
|
@@ -195970,7 +196277,6 @@ ${ctx.error}` : ctx.error;
|
|
|
195970
196277
|
workflowRun: runId ? { owner: repoContext.owner, repo: repoContext.name, runId } : void 0,
|
|
195971
196278
|
customParts,
|
|
195972
196279
|
model: ctx.toolState.model,
|
|
195973
|
-
fallbackFrom: ctx.toolState.modelFallback?.from,
|
|
195974
196280
|
clamped: ctx.toolState.modelClamped,
|
|
195975
196281
|
unselectedProxyDefault: ctx.toolState.unselectedProxyDefault,
|
|
195976
196282
|
oss: ctx.toolState.oss,
|
|
@@ -196354,7 +196660,8 @@ async function fetchRunContext(params) {
|
|
|
196354
196660
|
const timeoutId = setTimeout(() => controller.abort(), timeoutMs);
|
|
196355
196661
|
try {
|
|
196356
196662
|
const headers = {
|
|
196357
|
-
Authorization: `Bearer ${params.token}
|
|
196663
|
+
Authorization: `Bearer ${params.token}`,
|
|
196664
|
+
"X-Pullfrog-Credential-Pools": "1"
|
|
196358
196665
|
};
|
|
196359
196666
|
if (params.oidcToken) {
|
|
196360
196667
|
headers["X-GitHub-OIDC-Token"] = params.oidcToken;
|
|
@@ -197651,16 +197958,20 @@ async function main() {
|
|
|
197651
197958
|
const preIntrospectionDirty = repoDir ? await dirtyTrackedPaths({ cwd: repoDir }) : null;
|
|
197652
197959
|
const opencodeCliPath = await agents.opencode.install();
|
|
197653
197960
|
captureBaselineModels(opencodeCliPath);
|
|
197961
|
+
const injected = [];
|
|
197654
197962
|
if (runContext.dbSecrets) {
|
|
197655
197963
|
for (const [key, value2] of Object.entries(runContext.dbSecrets)) {
|
|
197656
197964
|
if (!process.env[key]) {
|
|
197657
197965
|
const sanitized = sanitizeSecret(key, value2);
|
|
197658
|
-
if (sanitized
|
|
197966
|
+
if (sanitized === null) continue;
|
|
197967
|
+
process.env[key] = sanitized;
|
|
197968
|
+
injected.push(key);
|
|
197659
197969
|
}
|
|
197660
197970
|
}
|
|
197661
197971
|
const count = Object.keys(runContext.dbSecrets).length;
|
|
197662
197972
|
if (count > 0) log.info(`\xBB ${count} db secret(s) loaded`);
|
|
197663
197973
|
}
|
|
197974
|
+
await initializeCredentialPool({ ctx: runContext, slug: payload.model, injected });
|
|
197664
197975
|
installCodexAuth();
|
|
197665
197976
|
installXaiAuth();
|
|
197666
197977
|
captureAuthorizedModels(opencodeCliPath);
|
|
@@ -197734,13 +198045,23 @@ async function main() {
|
|
|
197734
198045
|
}
|
|
197735
198046
|
const gitAuthServer = __using(_stack2, await startGitAuthServer(tmpdir3), true);
|
|
197736
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
|
+
});
|
|
197737
198058
|
const access = decideModelAccess({
|
|
197738
198059
|
modelExplicit: payload.modelExplicit ?? false,
|
|
197739
198060
|
model: payload.model,
|
|
197740
198061
|
oss: runContext.oss,
|
|
197741
198062
|
proxyActive: !!payload.proxyModel,
|
|
197742
198063
|
subsidyTarget: runContext.proxyModel,
|
|
197743
|
-
resolvedModel
|
|
198064
|
+
resolvedModel,
|
|
197744
198065
|
authorized: getAuthorizedModels()
|
|
197745
198066
|
});
|
|
197746
198067
|
if (access.kind === "error") {
|
|
@@ -197754,15 +198075,19 @@ async function main() {
|
|
|
197754
198075
|
);
|
|
197755
198076
|
}
|
|
197756
198077
|
if (access.kind === "proxy") payload.proxyModel = access.target;
|
|
197757
|
-
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
|
+
}
|
|
197758
198086
|
if (runContext.oss && payload.proxyModel) {
|
|
197759
198087
|
payload.effort = ossEffortFloor({ payload });
|
|
197760
198088
|
}
|
|
197761
|
-
|
|
197762
|
-
const credentials = payload.proxyModel ? { kind: "ok" } : await checkConfiguredCredentials({
|
|
197763
|
-
model: configuredModel,
|
|
197764
|
-
authorized: getAuthorizedModels()
|
|
197765
|
-
});
|
|
198089
|
+
if (payload.proxyModel) resolvedModel = void 0;
|
|
198090
|
+
const credentials = payload.proxyModel || poolSelected ? { kind: "ok" } : await checkConfiguredCredentials({ model: resolvedModel });
|
|
197766
198091
|
if (credentials.kind === "dead") {
|
|
197767
198092
|
throw new Error(
|
|
197768
198093
|
buildRejectedCredentialError({
|
|
@@ -197774,13 +198099,6 @@ async function main() {
|
|
|
197774
198099
|
})
|
|
197775
198100
|
);
|
|
197776
198101
|
}
|
|
197777
|
-
if (credentials.kind === "fellBack" && configuredModel) {
|
|
197778
|
-
log.warning(
|
|
197779
|
-
`\xBB ${credentials.credential} was rejected by its provider \u2014 running ${credentials.replacement} instead of ${configuredModel}`
|
|
197780
|
-
);
|
|
197781
|
-
toolState.modelFallback = { from: configuredModel };
|
|
197782
|
-
}
|
|
197783
|
-
const resolvedModel = credentials.kind === "fellBack" ? credentials.replacement : configuredModel;
|
|
197784
198102
|
vertexCredentials = materializeVertexCredentials({ model: resolvedModel });
|
|
197785
198103
|
let agent2 = resolveAgent({
|
|
197786
198104
|
model: resolvedModel,
|
|
@@ -198090,12 +198408,12 @@ ${instructions.user}` : null,
|
|
|
198090
198408
|
if (payload.timeout === TIMEOUT_DISABLED) {
|
|
198091
198409
|
result = await Promise.race([agentPromise, activityTimeout.promise]);
|
|
198092
198410
|
} else {
|
|
198093
|
-
const
|
|
198094
|
-
if (payload.timeout &&
|
|
198411
|
+
const usable2 = resolveTimeoutMs(payload.timeout);
|
|
198412
|
+
if (payload.timeout && usable2 === null) {
|
|
198095
198413
|
log.warning(`invalid timeout "${payload.timeout}" (use --notimeout to disable), using 1h`);
|
|
198096
198414
|
}
|
|
198097
|
-
const timeoutMs =
|
|
198098
|
-
const actualTimeout =
|
|
198415
|
+
const timeoutMs = usable2 ?? 36e5;
|
|
198416
|
+
const actualTimeout = usable2 !== null ? payload.timeout : "1h";
|
|
198099
198417
|
let timeoutId;
|
|
198100
198418
|
const timeoutPromise = new Promise((_4, reject) => {
|
|
198101
198419
|
timeoutId = setTimeout(() => {
|