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/cli.mjs
CHANGED
|
@@ -84177,7 +84177,7 @@ var require_Element = __commonJS({
|
|
|
84177
84177
|
var FilteredElementList = require_FilteredElementList();
|
|
84178
84178
|
var DOMException2 = require_DOMException();
|
|
84179
84179
|
var DOMTokenList = require_DOMTokenList();
|
|
84180
|
-
var
|
|
84180
|
+
var select3 = require_select();
|
|
84181
84181
|
var ContainerNode = require_ContainerNode();
|
|
84182
84182
|
var ChildNode = require_ChildNode();
|
|
84183
84183
|
var NonDocumentTypeChildNode = require_NonDocumentTypeChildNode();
|
|
@@ -84938,7 +84938,7 @@ var require_Element = __commonJS({
|
|
|
84938
84938
|
this.className = v;
|
|
84939
84939
|
} },
|
|
84940
84940
|
matches: { value: function(selector) {
|
|
84941
|
-
return
|
|
84941
|
+
return select3.matches(this, selector);
|
|
84942
84942
|
} },
|
|
84943
84943
|
closest: { value: function(selector) {
|
|
84944
84944
|
var el = this;
|
|
@@ -84951,10 +84951,10 @@ var require_Element = __commonJS({
|
|
|
84951
84951
|
return null;
|
|
84952
84952
|
} },
|
|
84953
84953
|
querySelector: { value: function(selector) {
|
|
84954
|
-
return
|
|
84954
|
+
return select3(selector, this)[0];
|
|
84955
84955
|
} },
|
|
84956
84956
|
querySelectorAll: { value: function(selector) {
|
|
84957
|
-
var nodes =
|
|
84957
|
+
var nodes = select3(selector, this);
|
|
84958
84958
|
return nodes.item ? nodes : new NodeList(nodes);
|
|
84959
84959
|
} }
|
|
84960
84960
|
});
|
|
@@ -85482,7 +85482,7 @@ var require_DocumentFragment = __commonJS({
|
|
|
85482
85482
|
var NodeList = require_NodeList();
|
|
85483
85483
|
var ContainerNode = require_ContainerNode();
|
|
85484
85484
|
var Element = require_Element();
|
|
85485
|
-
var
|
|
85485
|
+
var select3 = require_select();
|
|
85486
85486
|
var utils = require_utils8();
|
|
85487
85487
|
function DocumentFragment(doc) {
|
|
85488
85488
|
ContainerNode.call(this);
|
|
@@ -85511,7 +85511,7 @@ var require_DocumentFragment = __commonJS({
|
|
|
85511
85511
|
context.isHTML = true;
|
|
85512
85512
|
context.getElementsByTagName = Element.prototype.getElementsByTagName;
|
|
85513
85513
|
context.nextElement = Object.getOwnPropertyDescriptor(Element.prototype, "firstElementChild").get;
|
|
85514
|
-
var nodes =
|
|
85514
|
+
var nodes = select3(selector, context);
|
|
85515
85515
|
return nodes.item ? nodes : new NodeList(nodes);
|
|
85516
85516
|
} },
|
|
85517
85517
|
// Utility methods
|
|
@@ -88626,7 +88626,7 @@ var require_Document = __commonJS({
|
|
|
88626
88626
|
var NodeIterator = require_NodeIterator();
|
|
88627
88627
|
var NodeFilter = require_NodeFilter();
|
|
88628
88628
|
var URL2 = require_URL();
|
|
88629
|
-
var
|
|
88629
|
+
var select3 = require_select();
|
|
88630
88630
|
var events = require_events3();
|
|
88631
88631
|
var xml = require_xmlnames();
|
|
88632
88632
|
var html = require_htmlelts();
|
|
@@ -89187,10 +89187,10 @@ var require_Document = __commonJS({
|
|
|
89187
89187
|
return this._templateDocCache;
|
|
89188
89188
|
} },
|
|
89189
89189
|
querySelector: { value: function(selector) {
|
|
89190
|
-
return
|
|
89190
|
+
return select3(selector, this)[0];
|
|
89191
89191
|
} },
|
|
89192
89192
|
querySelectorAll: { value: function(selector) {
|
|
89193
|
-
var nodes =
|
|
89193
|
+
var nodes = select3(selector, this);
|
|
89194
89194
|
return nodes.item ? nodes : new NodeList(nodes);
|
|
89195
89195
|
} }
|
|
89196
89196
|
});
|
|
@@ -107103,7 +107103,7 @@ var import_picocolors = __toESM(require_picocolors(), 1);
|
|
|
107103
107103
|
// package.json
|
|
107104
107104
|
var package_default = {
|
|
107105
107105
|
name: "pullfrog",
|
|
107106
|
-
version: "0.1.
|
|
107106
|
+
version: "0.1.91",
|
|
107107
107107
|
cliContractVersion: "1",
|
|
107108
107108
|
type: "module",
|
|
107109
107109
|
bin: {
|
|
@@ -107478,7 +107478,10 @@ var subscriptionSchema = external_exports.object({
|
|
|
107478
107478
|
owner: external_exports.string(),
|
|
107479
107479
|
inherited: external_exports.boolean(),
|
|
107480
107480
|
shared: external_exports.boolean(),
|
|
107481
|
-
registeredAt: external_exports.string()
|
|
107481
|
+
registeredAt: external_exports.string(),
|
|
107482
|
+
/** the provider's last verdict on the current value; null = usable or never checked. */
|
|
107483
|
+
status: external_exports.enum(["rejected", "exhausted"]).nullish(),
|
|
107484
|
+
resetAt: external_exports.string().nullish()
|
|
107482
107485
|
});
|
|
107483
107486
|
var subscriptionListSchema = external_exports.object({
|
|
107484
107487
|
subscriptions: external_exports.array(subscriptionSchema),
|
|
@@ -110796,10 +110799,12 @@ async function preflightClaudeSubscription(params) {
|
|
|
110796
110799
|
if (res.status !== 401 && res.status !== 403 && res.status !== 429)
|
|
110797
110800
|
return { usable: true, status: res.status };
|
|
110798
110801
|
const body = await res.text().catch(() => "");
|
|
110802
|
+
const reset = Number(res.headers.get("anthropic-ratelimit-unified-reset"));
|
|
110799
110803
|
return {
|
|
110800
110804
|
usable: false,
|
|
110801
110805
|
status: res.status,
|
|
110802
|
-
reason: `${res.status}: ${extractApiErrorMessage(body)}
|
|
110806
|
+
reason: `${res.status}: ${extractApiErrorMessage(body)}`,
|
|
110807
|
+
resetAt: res.status === 429 && reset > 0 ? new Date(reset * 1e3) : void 0
|
|
110803
110808
|
};
|
|
110804
110809
|
}
|
|
110805
110810
|
function extractApiErrorMessage(body) {
|
|
@@ -113524,6 +113529,17 @@ import { join as join6 } from "node:path";
|
|
|
113524
113529
|
var CODEX_AUTH_ENV = "CODEX_AUTH_JSON";
|
|
113525
113530
|
var XAI_AUTH_ENV = "GROK_AUTH_JSON";
|
|
113526
113531
|
var installedSubscriptionPaths = /* @__PURE__ */ new Map();
|
|
113532
|
+
function clearInstalledSubscription(name) {
|
|
113533
|
+
const path4 = installedSubscriptionPaths.get(name);
|
|
113534
|
+
if (!path4 || !existsSync5(path4)) return;
|
|
113535
|
+
const value2 = JSON.parse(readFileSync3(path4, "utf8"));
|
|
113536
|
+
if (!value2 || typeof value2 !== "object") return;
|
|
113537
|
+
if (name === "CODEX_AUTH_JSON" && "openai" in value2) delete value2.openai;
|
|
113538
|
+
if (name === "GROK_AUTH_JSON" && "xai" in value2) delete value2.xai;
|
|
113539
|
+
writeFileSync5(path4, `${JSON.stringify(value2)}
|
|
113540
|
+
`, { mode: 384 });
|
|
113541
|
+
installedSubscriptionPaths.delete(name);
|
|
113542
|
+
}
|
|
113527
113543
|
var PULLFROG_DATA_DIR = "/var/lib/pullfrog";
|
|
113528
113544
|
function isRejectedChain(body) {
|
|
113529
113545
|
if (!body.refresh_rejected_at) return false;
|
|
@@ -113854,8 +113870,8 @@ async function writeBackEntry(apiToken, entry) {
|
|
|
113854
113870
|
}
|
|
113855
113871
|
try {
|
|
113856
113872
|
const poolState = core2.getState("credential_receipts");
|
|
113857
|
-
const
|
|
113858
|
-
const receipt =
|
|
113873
|
+
const receipts2 = external_exports.record(external_exports.string(), external_exports.string()).parse(JSON.parse(poolState || "{}"));
|
|
113874
|
+
const receipt = receipts2[entry.secretName];
|
|
113859
113875
|
if (poolState && !receipt) return;
|
|
113860
113876
|
const response = await apiFetch({
|
|
113861
113877
|
path: receipt ? "/api/runtime/credentials" : "/api/runtime/secret",
|
|
@@ -120200,6 +120216,9 @@ function captureAuthorizedModels(cliPath) {
|
|
|
120200
120216
|
}
|
|
120201
120217
|
log2.debug(`\xBB opencode authorized: ${authorized.size} models`);
|
|
120202
120218
|
}
|
|
120219
|
+
function authorizeModel(model) {
|
|
120220
|
+
getAuthorizedModels().add(model);
|
|
120221
|
+
}
|
|
120203
120222
|
function getAuthorizedModels() {
|
|
120204
120223
|
if (!authorized) {
|
|
120205
120224
|
throw new Error("getAuthorizedModels called before captureAuthorizedModels");
|
|
@@ -134319,8 +134338,8 @@ function liftWireOnlyMaterial(message, kind) {
|
|
|
134319
134338
|
};
|
|
134320
134339
|
}
|
|
134321
134340
|
function codecResultValidator(codec2, method) {
|
|
134322
|
-
const
|
|
134323
|
-
if (!
|
|
134341
|
+
const probe2 = codec2.validateResult(method, void 0);
|
|
134342
|
+
if (!probe2.ok && probe2.reason === "not-in-era") return void 0;
|
|
134324
134343
|
return { "~standard": {
|
|
134325
134344
|
version: 1,
|
|
134326
134345
|
vendor: "mcp-wire-codec",
|
|
@@ -148696,8 +148715,8 @@ function liftWireOnlyMaterial2(message, kind) {
|
|
|
148696
148715
|
};
|
|
148697
148716
|
}
|
|
148698
148717
|
function codecResultValidator2(codec2, method) {
|
|
148699
|
-
const
|
|
148700
|
-
if (!
|
|
148718
|
+
const probe2 = codec2.validateResult(method, void 0);
|
|
148719
|
+
if (!probe2.ok && probe2.reason === "not-in-era") return void 0;
|
|
148701
148720
|
return { "~standard": {
|
|
148702
148721
|
version: 1,
|
|
148703
148722
|
vendor: "mcp-wire-codec",
|
|
@@ -191876,10 +191895,10 @@ function CommitChangesTool(ctx) {
|
|
|
191876
191895
|
}
|
|
191877
191896
|
const requested = new Set(params.files);
|
|
191878
191897
|
const known = new Set(changes.map((c2) => c2.path));
|
|
191879
|
-
const
|
|
191880
|
-
if (
|
|
191898
|
+
const unknown5 = [...requested].filter((p) => !known.has(p));
|
|
191899
|
+
if (unknown5.length > 0) {
|
|
191881
191900
|
throw new Error(
|
|
191882
|
-
`no detected change at: ${
|
|
191901
|
+
`no detected change at: ${unknown5.join(", ")} \u2014 run git status to list changed paths.`
|
|
191883
191902
|
);
|
|
191884
191903
|
}
|
|
191885
191904
|
changes = changes.filter((c2) => requested.has(c2.path));
|
|
@@ -192272,15 +192291,6 @@ function PushTagsTool(ctx) {
|
|
|
192272
192291
|
// utils/buildPullfrogFooter.ts
|
|
192273
192292
|
var PULLFROG_DIVIDER = "<!-- PULLFROG_DIVIDER_DO_NOT_REMOVE_PLZ -->";
|
|
192274
192293
|
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>`;
|
|
192275
|
-
function providerDisplayName(slug2) {
|
|
192276
|
-
try {
|
|
192277
|
-
const key = getModelProvider(slug2);
|
|
192278
|
-
const meta3 = providers[key];
|
|
192279
|
-
return meta3?.displayName ?? key;
|
|
192280
|
-
} catch {
|
|
192281
|
-
return slug2;
|
|
192282
|
-
}
|
|
192283
|
-
}
|
|
192284
192294
|
function formatModelLabel(params) {
|
|
192285
192295
|
const alias = resolveDisplayAlias(params.model) ?? // reverse-lookup: when the caller passes an effective model (proxy or
|
|
192286
192296
|
// resolved target like "openrouter/anthropic/claude-opus-4.7") instead of
|
|
@@ -192295,9 +192305,6 @@ function formatModelLabel(params) {
|
|
|
192295
192305
|
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)`;
|
|
192296
192306
|
}
|
|
192297
192307
|
const base = alias?.isFree ? `\`${displayName}\` (free)` : `\`${displayName}\``;
|
|
192298
|
-
if (params.fallbackFrom) {
|
|
192299
|
-
return `${base} (credentials for ${providerDisplayName(params.fallbackFrom)} were rejected by the provider)`;
|
|
192300
|
-
}
|
|
192301
192308
|
if (params.clamped?.reason === "trial") {
|
|
192302
192309
|
return `${base} (model usage covered by Pullfrog)`;
|
|
192303
192310
|
}
|
|
@@ -192338,7 +192345,6 @@ function buildPullfrogFooter(params) {
|
|
|
192338
192345
|
parts.push(
|
|
192339
192346
|
`Using ${formatModelLabel({
|
|
192340
192347
|
model: params.model,
|
|
192341
|
-
fallbackFrom: params.fallbackFrom,
|
|
192342
192348
|
clamped: params.clamped,
|
|
192343
192349
|
unselectedProxyDefault: params.unselectedProxyDefault,
|
|
192344
192350
|
oss: params.oss
|
|
@@ -192538,7 +192544,6 @@ function buildCommentFooter(ctx, customParts) {
|
|
|
192538
192544
|
} : void 0,
|
|
192539
192545
|
customParts,
|
|
192540
192546
|
model: ctx.toolState.model,
|
|
192541
|
-
fallbackFrom: ctx.toolState.modelFallback?.from,
|
|
192542
192547
|
clamped: ctx.toolState.modelClamped,
|
|
192543
192548
|
unselectedProxyDefault: ctx.toolState.unselectedProxyDefault,
|
|
192544
192549
|
oss: ctx.oss,
|
|
@@ -193528,7 +193533,6 @@ async function createAndSubmitWithFooter(ctx, params, opts) {
|
|
|
193528
193533
|
workflowRun: ctx.runId ? { owner: ctx.repo.owner, repo: ctx.repo.name, runId: ctx.runId, jobId: ctx.jobId } : void 0,
|
|
193529
193534
|
customParts,
|
|
193530
193535
|
model: ctx.toolState.model,
|
|
193531
|
-
fallbackFrom: ctx.toolState.modelFallback?.from,
|
|
193532
193536
|
clamped: ctx.toolState.modelClamped,
|
|
193533
193537
|
unselectedProxyDefault: ctx.toolState.unselectedProxyDefault,
|
|
193534
193538
|
oss: ctx.oss,
|
|
@@ -195904,7 +195908,6 @@ function buildPrBodyWithFooter(ctx, body) {
|
|
|
195904
195908
|
triggeredBy: true,
|
|
195905
195909
|
workflowRun: ctx.runId ? { owner: ctx.repo.owner, repo: ctx.repo.name, runId: ctx.runId, jobId: ctx.jobId } : void 0,
|
|
195906
195910
|
model: ctx.toolState.model,
|
|
195907
|
-
fallbackFrom: ctx.toolState.modelFallback?.from,
|
|
195908
195911
|
clamped: ctx.toolState.modelClamped,
|
|
195909
195912
|
unselectedProxyDefault: ctx.toolState.unselectedProxyDefault,
|
|
195910
195913
|
oss: ctx.oss,
|
|
@@ -196672,8 +196675,9 @@ function ResolveReviewThreadTool(ctx) {
|
|
|
196672
196675
|
}
|
|
196673
196676
|
|
|
196674
196677
|
// mcp/searchIssues.ts
|
|
196678
|
+
var TERM = /^[\p{L}\p{N}_./-]+$/u;
|
|
196675
196679
|
var SearchIssues = type({
|
|
196676
|
-
terms: type.string.
|
|
196680
|
+
terms: type.string.atMostLength(40).array().atLeastLength(1).atMostLength(5).describe("1-5 keywords from the issue; no GitHub qualifiers or query operators")
|
|
196677
196681
|
});
|
|
196678
196682
|
function SearchIssuesTool(ctx) {
|
|
196679
196683
|
return tool({
|
|
@@ -196681,6 +196685,10 @@ function SearchIssuesTool(ctx) {
|
|
|
196681
196685
|
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.",
|
|
196682
196686
|
parameters: SearchIssues,
|
|
196683
196687
|
execute: execute(async (params) => {
|
|
196688
|
+
const invalid = params.terms.find((term) => !TERM.test(term));
|
|
196689
|
+
if (invalid) {
|
|
196690
|
+
throw new Error(`search term "${invalid}" may contain only letters, digits and _ . / -`);
|
|
196691
|
+
}
|
|
196684
196692
|
const repoPath = `/repos/${ctx.repo.owner}/${ctx.repo.name}`.toLowerCase();
|
|
196685
196693
|
const terms = params.terms.map((term) => `"${term}"`).join(" ");
|
|
196686
196694
|
const response = await ctx.octokit.rest.search.issuesAndPullRequests({
|
|
@@ -198048,17 +198056,17 @@ async function verifyCredential(params) {
|
|
|
198048
198056
|
const value2 = params.value.trim();
|
|
198049
198057
|
if (value2.length === 0) return "dead";
|
|
198050
198058
|
if (params.envVar === "CLAUDE_CODE_OAUTH_TOKEN") return verifyClaudeSubscription(value2);
|
|
198051
|
-
const
|
|
198052
|
-
if (!
|
|
198053
|
-
const request2 =
|
|
198059
|
+
const probe2 = PROBES[params.envVar];
|
|
198060
|
+
if (!probe2) return "unknown";
|
|
198061
|
+
const request2 = probe2.request(value2);
|
|
198054
198062
|
try {
|
|
198055
198063
|
const response = await fetch(request2.url, {
|
|
198056
198064
|
headers: request2.headers,
|
|
198057
198065
|
signal: AbortSignal.timeout(5e3)
|
|
198058
198066
|
});
|
|
198059
198067
|
if (response.ok) return "alive";
|
|
198060
|
-
if (
|
|
198061
|
-
return
|
|
198068
|
+
if (probe2.deadWhen) {
|
|
198069
|
+
return probe2.deadWhen(response.status, await response.text()) ? "dead" : "unknown";
|
|
198062
198070
|
}
|
|
198063
198071
|
return response.status === 401 ? "dead" : "unknown";
|
|
198064
198072
|
} catch {
|
|
@@ -198089,14 +198097,6 @@ async function checkOne(params) {
|
|
|
198089
198097
|
const verdict = await verifyCredential({ envVar: params.envVar, value: value2 });
|
|
198090
198098
|
return verdict === "dead" ? { severity: "dead", reason: void 0 } : null;
|
|
198091
198099
|
}
|
|
198092
|
-
function pickReplacement(params) {
|
|
198093
|
-
const candidates = modelAliases.filter(
|
|
198094
|
-
(alias) => !alias.hidden && !alias.fallback && params.authorized.has(alias.resolve) && // a model with no known env vars is routable through credentials we can't
|
|
198095
|
-
// see (Codex auth lands as an on-disk auth.json), so it stays a candidate.
|
|
198096
|
-
!envVarsFor(alias.resolve).some((envVar) => params.deadVars.has(envVar))
|
|
198097
|
-
);
|
|
198098
|
-
return (candidates.find((alias) => alias.preferred) ?? candidates[0])?.resolve;
|
|
198099
|
-
}
|
|
198100
198100
|
async function checkConfiguredCredentials(params) {
|
|
198101
198101
|
if (!params.model) return { kind: "ok" };
|
|
198102
198102
|
const present = envVarsFor(params.model).filter(hasEnvVar4);
|
|
@@ -198122,12 +198122,353 @@ async function checkConfiguredCredentials(params) {
|
|
|
198122
198122
|
failed.forEach(drop);
|
|
198123
198123
|
const first = failed[0];
|
|
198124
198124
|
if (!first) return { kind: "ok" };
|
|
198125
|
-
|
|
198126
|
-
|
|
198127
|
-
|
|
198128
|
-
|
|
198125
|
+
return { kind: "dead", credential: first.envVar, reason: first.failure.reason };
|
|
198126
|
+
}
|
|
198127
|
+
|
|
198128
|
+
// utils/normalizeEnv.ts
|
|
198129
|
+
function hasControlCharacter(value2) {
|
|
198130
|
+
return [...value2].some((ch) => {
|
|
198131
|
+
const code = ch.charCodeAt(0);
|
|
198132
|
+
return code < 32 || code === 127;
|
|
198129
198133
|
});
|
|
198130
|
-
|
|
198134
|
+
}
|
|
198135
|
+
function sanitizeSecret(key, value2) {
|
|
198136
|
+
const trimmed = value2.trim();
|
|
198137
|
+
if (trimmed.length === 0) {
|
|
198138
|
+
log2.warning(
|
|
198139
|
+
`\xBB ${key} is whitespace-only \u2014 leaving env var unchanged. check your secret value.`
|
|
198140
|
+
);
|
|
198141
|
+
return null;
|
|
198142
|
+
}
|
|
198143
|
+
if (key.endsWith("_API_KEY") && hasControlCharacter(trimmed)) {
|
|
198144
|
+
log2.warning(
|
|
198145
|
+
`\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.`
|
|
198146
|
+
);
|
|
198147
|
+
return null;
|
|
198148
|
+
}
|
|
198149
|
+
if (trimmed !== value2) {
|
|
198150
|
+
log2.warning(
|
|
198151
|
+
`\xBB stripped whitespace from ${key} (whitespace in secret values breaks GitHub Actions log masking)`
|
|
198152
|
+
);
|
|
198153
|
+
}
|
|
198154
|
+
maskSecret(trimmed);
|
|
198155
|
+
return trimmed;
|
|
198156
|
+
}
|
|
198157
|
+
function normalizeEnv() {
|
|
198158
|
+
const upperKeys = /* @__PURE__ */ new Map();
|
|
198159
|
+
for (const key of Object.keys(process.env)) {
|
|
198160
|
+
const upper2 = key.toUpperCase();
|
|
198161
|
+
const existing = upperKeys.get(upper2) || [];
|
|
198162
|
+
existing.push(key);
|
|
198163
|
+
upperKeys.set(upper2, existing);
|
|
198164
|
+
}
|
|
198165
|
+
for (const [upperKey, keys] of upperKeys) {
|
|
198166
|
+
if (keys.length === 1) {
|
|
198167
|
+
const key = keys[0];
|
|
198168
|
+
if (key !== upperKey) {
|
|
198169
|
+
process.env[upperKey] = process.env[key];
|
|
198170
|
+
delete process.env[key];
|
|
198171
|
+
}
|
|
198172
|
+
continue;
|
|
198173
|
+
}
|
|
198174
|
+
const values = keys.map((k2) => process.env[k2]);
|
|
198175
|
+
const uniqueValues = new Set(values);
|
|
198176
|
+
if (uniqueValues.size > 1) {
|
|
198177
|
+
log2.warning(
|
|
198178
|
+
`env var conflict: ${keys.join(", ")} have different values. using uppercase ${upperKey}.`
|
|
198179
|
+
);
|
|
198180
|
+
}
|
|
198181
|
+
const preferredKey = keys.find((k2) => k2 === upperKey) || keys[0];
|
|
198182
|
+
const preferredValue = process.env[preferredKey];
|
|
198183
|
+
for (const key of keys) {
|
|
198184
|
+
delete process.env[key];
|
|
198185
|
+
}
|
|
198186
|
+
process.env[upperKey] = preferredValue;
|
|
198187
|
+
}
|
|
198188
|
+
for (const key of Object.keys(process.env)) {
|
|
198189
|
+
if (!isSensitiveEnvName(key)) continue;
|
|
198190
|
+
const value2 = process.env[key];
|
|
198191
|
+
if (typeof value2 !== "string" || value2.length === 0) continue;
|
|
198192
|
+
const sanitized = sanitizeSecret(key, value2);
|
|
198193
|
+
if (sanitized !== null) process.env[key] = sanitized;
|
|
198194
|
+
}
|
|
198195
|
+
}
|
|
198196
|
+
|
|
198197
|
+
// utils/subscriptionProbe.ts
|
|
198198
|
+
var identitySchema = external_exports.object({
|
|
198199
|
+
email: external_exports.string().email().optional(),
|
|
198200
|
+
sub: external_exports.string().optional(),
|
|
198201
|
+
user_id: external_exports.string().optional(),
|
|
198202
|
+
account_id: external_exports.string().optional(),
|
|
198203
|
+
email_verified: external_exports.boolean().optional()
|
|
198204
|
+
});
|
|
198205
|
+
var codexWindowSchema = external_exports.object({ used_percent: external_exports.number().optional(), reset_at: external_exports.number().optional() }).nullish();
|
|
198206
|
+
var codexUsageSchema = external_exports.object({
|
|
198207
|
+
rate_limit: external_exports.object({
|
|
198208
|
+
allowed: external_exports.boolean(),
|
|
198209
|
+
limit_reached: external_exports.boolean(),
|
|
198210
|
+
primary_window: codexWindowSchema,
|
|
198211
|
+
secondary_window: codexWindowSchema
|
|
198212
|
+
}).optional()
|
|
198213
|
+
});
|
|
198214
|
+
var unknown4 = { status: "unknown" };
|
|
198215
|
+
async function readSubscriptionEndpoint(input) {
|
|
198216
|
+
const headers = new Headers({ authorization: `Bearer ${input.token}` });
|
|
198217
|
+
if (input.accountId) headers.set("ChatGPT-Account-Id", input.accountId);
|
|
198218
|
+
if (input.grokBilling) headers.set("x-xai-token-auth", "xai-grok-cli");
|
|
198219
|
+
return fetch(input.url, {
|
|
198220
|
+
headers,
|
|
198221
|
+
redirect: "error",
|
|
198222
|
+
signal: AbortSignal.timeout(1e4)
|
|
198223
|
+
}).catch(() => null);
|
|
198224
|
+
}
|
|
198225
|
+
async function probeSubscription(input) {
|
|
198226
|
+
if (input.name === "CLAUDE_CODE_OAUTH_TOKEN") {
|
|
198227
|
+
const result = await preflightClaudeSubscription({ token: input.value, model: input.model });
|
|
198228
|
+
if (result.usable)
|
|
198229
|
+
return result.status !== void 0 && result.status < 300 ? { status: "usable" } : unknown4;
|
|
198230
|
+
return result.status === 429 ? { status: "exhausted", detail: result.reason, resetAt: result.resetAt } : { status: "rejected", detail: result.reason };
|
|
198231
|
+
}
|
|
198232
|
+
const codex2 = input.name === "CODEX_AUTH_JSON" ? parseCodexAuthBody(input.value) : null;
|
|
198233
|
+
const grok = input.name === "GROK_AUTH_JSON" ? parseXaiAuthBody(input.value) : null;
|
|
198234
|
+
const auth2 = codex2 ?? grok;
|
|
198235
|
+
if (!auth2) return { status: "rejected", detail: "the stored credential is malformed" };
|
|
198236
|
+
if (auth2.refresh_rejected_at)
|
|
198237
|
+
return { status: "rejected", detail: `refresh rejected at ${auth2.refresh_rejected_at}` };
|
|
198238
|
+
if (grok && input.model)
|
|
198239
|
+
return probeInference({
|
|
198240
|
+
name: "XAI_API_KEY",
|
|
198241
|
+
value: grok.tokens.access_token,
|
|
198242
|
+
model: input.model
|
|
198243
|
+
});
|
|
198244
|
+
const response = await readSubscriptionEndpoint({
|
|
198245
|
+
url: codex2 ? "https://chatgpt.com/backend-api/wham/usage" : "https://cli-chat-proxy.grok.com/v1/billing?format=credits",
|
|
198246
|
+
token: auth2.tokens.access_token,
|
|
198247
|
+
accountId: codex2?.tokens.account_id,
|
|
198248
|
+
grokBilling: !!grok
|
|
198249
|
+
});
|
|
198250
|
+
if (response?.status === 401) return { status: "rejected", detail: "rejected with 401" };
|
|
198251
|
+
if (!response?.ok) return unknown4;
|
|
198252
|
+
const body = await response.json().catch(() => null);
|
|
198253
|
+
if (!codex2) return unknown4;
|
|
198254
|
+
const parsed2 = codexUsageSchema.safeParse(body);
|
|
198255
|
+
const limit = parsed2.success ? parsed2.data.rate_limit : void 0;
|
|
198256
|
+
if (!limit) return unknown4;
|
|
198257
|
+
if (limit.allowed && !limit.limit_reached) return { status: "usable" };
|
|
198258
|
+
const windows = [limit.primary_window, limit.secondary_window];
|
|
198259
|
+
const full = windows.filter((window2) => (window2?.used_percent ?? 0) >= 100);
|
|
198260
|
+
const resets = (full.length ? full : windows).flatMap(
|
|
198261
|
+
(window2) => window2?.reset_at ? [window2.reset_at] : []
|
|
198262
|
+
);
|
|
198263
|
+
return {
|
|
198264
|
+
status: "exhausted",
|
|
198265
|
+
detail: "the ChatGPT plan's usage limit is reached",
|
|
198266
|
+
resetAt: resets.length ? new Date(Math.max(...resets) * 1e3) : void 0
|
|
198267
|
+
};
|
|
198268
|
+
}
|
|
198269
|
+
async function probeInference(input) {
|
|
198270
|
+
const anthropic = input.name === "ANTHROPIC_API_KEY";
|
|
198271
|
+
const openai = input.name === "OPENAI_API_KEY";
|
|
198272
|
+
if (!anthropic && !openai && input.name !== "XAI_API_KEY") {
|
|
198273
|
+
const result = await verifyCredential({ envVar: input.name, value: input.value });
|
|
198274
|
+
return result === "dead" ? { status: "rejected", detail: "rejected by its provider" } : result === "alive" ? { status: "usable" } : unknown4;
|
|
198275
|
+
}
|
|
198276
|
+
const headers = new Headers({ "content-type": "application/json" });
|
|
198277
|
+
if (anthropic) {
|
|
198278
|
+
headers.set("x-api-key", input.value);
|
|
198279
|
+
headers.set("anthropic-version", "2023-06-01");
|
|
198280
|
+
} else headers.set("authorization", `Bearer ${input.value}`);
|
|
198281
|
+
const response = await fetch(
|
|
198282
|
+
anthropic ? "https://api.anthropic.com/v1/messages" : openai ? "https://api.openai.com/v1/responses" : "https://api.x.ai/v1/chat/completions",
|
|
198283
|
+
{
|
|
198284
|
+
method: "POST",
|
|
198285
|
+
headers,
|
|
198286
|
+
body: JSON.stringify(
|
|
198287
|
+
openai ? { model: input.model, input: "Reply OK.", max_output_tokens: 16, store: false } : {
|
|
198288
|
+
model: input.model,
|
|
198289
|
+
messages: [{ role: "user", content: "Reply OK." }],
|
|
198290
|
+
max_tokens: 1
|
|
198291
|
+
}
|
|
198292
|
+
),
|
|
198293
|
+
redirect: "error",
|
|
198294
|
+
signal: AbortSignal.timeout(1e4)
|
|
198295
|
+
}
|
|
198296
|
+
).catch(() => null);
|
|
198297
|
+
if (response?.status === 400 && input.name === "XAI_API_KEY") {
|
|
198298
|
+
const error49 = external_exports.object({ error: external_exports.string() }).safeParse(await response.json().catch(() => null));
|
|
198299
|
+
return error49.success && error49.data.error.startsWith("Incorrect API key provided.") ? { status: "rejected", detail: "rejected with 400: incorrect API key" } : unknown4;
|
|
198300
|
+
}
|
|
198301
|
+
if (response?.status === 429 && openai) {
|
|
198302
|
+
const error49 = external_exports.object({ error: external_exports.object({ code: external_exports.string() }) }).safeParse(await response.json().catch(() => null));
|
|
198303
|
+
return error49.success && error49.data.error.code === "insufficient_quota" ? { status: "exhausted", detail: "refused with 429: insufficient quota", resetAt: void 0 } : unknown4;
|
|
198304
|
+
}
|
|
198305
|
+
await response?.body?.cancel();
|
|
198306
|
+
if (!response) return unknown4;
|
|
198307
|
+
if (response.ok) return { status: "usable" };
|
|
198308
|
+
if (response.status === 401 || response.status === 403)
|
|
198309
|
+
return { status: "rejected", detail: `rejected with ${response.status}` };
|
|
198310
|
+
if (response.status === 402)
|
|
198311
|
+
return { status: "exhausted", detail: "refused with 402", resetAt: void 0 };
|
|
198312
|
+
return unknown4;
|
|
198313
|
+
}
|
|
198314
|
+
|
|
198315
|
+
// utils/credentialPool.ts
|
|
198316
|
+
var receipts = {};
|
|
198317
|
+
var workflowCredentials = {};
|
|
198318
|
+
function saveReceipts() {
|
|
198319
|
+
for (const receipt of Object.values(receipts)) maskSecret(receipt);
|
|
198320
|
+
saveSecretState("credential_receipts", JSON.stringify(receipts));
|
|
198321
|
+
}
|
|
198322
|
+
var select2 = op(
|
|
198323
|
+
async (input) => {
|
|
198324
|
+
const response = await apiFetch({
|
|
198325
|
+
path: "/api/runtime/credentials",
|
|
198326
|
+
method: "POST",
|
|
198327
|
+
headers: {
|
|
198328
|
+
authorization: `Bearer ${input.access.token}`,
|
|
198329
|
+
"content-type": "application/json"
|
|
198330
|
+
},
|
|
198331
|
+
body: JSON.stringify({ id: input.candidate.id }),
|
|
198332
|
+
signal: AbortSignal.timeout(35e3)
|
|
198333
|
+
});
|
|
198334
|
+
if (response.status === 404) return null;
|
|
198335
|
+
if (!response.ok)
|
|
198336
|
+
throw new Error("could not load a configured credential; no fallback was charged", {
|
|
198337
|
+
cause: response.status
|
|
198338
|
+
});
|
|
198339
|
+
return selectedCredentialSchema.parse(await response.json());
|
|
198340
|
+
},
|
|
198341
|
+
{
|
|
198342
|
+
retries: [250, 1e3],
|
|
198343
|
+
bail: (error49) => error49 instanceof Error && typeof error49.cause === "number" && error49.cause >= 400 && error49.cause < 500
|
|
198344
|
+
}
|
|
198345
|
+
);
|
|
198346
|
+
function subscriptionForModel(model) {
|
|
198347
|
+
if (!model.includes("/")) return null;
|
|
198348
|
+
const provider2 = getModelProvider(model);
|
|
198349
|
+
return provider2 === "anthropic" ? "CLAUDE_CODE_OAUTH_TOKEN" : provider2 === "openai" ? "CODEX_AUTH_JSON" : provider2 === "xai" ? "GROK_AUTH_JSON" : null;
|
|
198350
|
+
}
|
|
198351
|
+
async function initializeCredentialPool(input) {
|
|
198352
|
+
const access = input.ctx.credentialAccess;
|
|
198353
|
+
if (!access) return;
|
|
198354
|
+
const model = discoveryModel(input.slug);
|
|
198355
|
+
maskSecret(access.token);
|
|
198356
|
+
const names = /* @__PURE__ */ new Set([
|
|
198357
|
+
...access.candidates.map((candidate) => candidate.name),
|
|
198358
|
+
...subscriptionNameSchema.options,
|
|
198359
|
+
"ANTHROPIC_API_KEY",
|
|
198360
|
+
"OPENAI_API_KEY",
|
|
198361
|
+
"XAI_API_KEY"
|
|
198362
|
+
]);
|
|
198363
|
+
for (const name of names) {
|
|
198364
|
+
const value2 = process.env[name];
|
|
198365
|
+
if (value2 && !input.injected.includes(name)) workflowCredentials[name] = value2;
|
|
198366
|
+
}
|
|
198367
|
+
for (const name of subscriptionNameSchema.options) {
|
|
198368
|
+
if (model && subscriptionForModel(model) !== name) continue;
|
|
198369
|
+
if (workflowCredentials[name]) continue;
|
|
198370
|
+
const candidate = access.candidates.find((item) => item.name === name);
|
|
198371
|
+
if (!candidate) continue;
|
|
198372
|
+
const selected = await select2({ access, candidate }).catch(() => {
|
|
198373
|
+
log2.warning(`\xBB could not load ${name} for model discovery; selection will retry if needed`);
|
|
198374
|
+
return null;
|
|
198375
|
+
});
|
|
198376
|
+
if (!selected) continue;
|
|
198377
|
+
const value2 = sanitizeSecret(name, selected.value);
|
|
198378
|
+
if (!value2) continue;
|
|
198379
|
+
process.env[name] = value2;
|
|
198380
|
+
receipts[name] = selected.receipt;
|
|
198381
|
+
}
|
|
198382
|
+
saveReceipts();
|
|
198383
|
+
}
|
|
198384
|
+
function discoveryModel(slug2) {
|
|
198385
|
+
const value2 = process.env.PULLFROG_MODEL?.trim() || slug2?.trim();
|
|
198386
|
+
if (!value2) return void 0;
|
|
198387
|
+
return resolveCliModel(value2) ?? (value2.includes("/") ? value2 : void 0);
|
|
198388
|
+
}
|
|
198389
|
+
function resolvePoolModel(input) {
|
|
198390
|
+
const agent2 = resolveAgent({
|
|
198391
|
+
model: void 0,
|
|
198392
|
+
proxyModel: void 0,
|
|
198393
|
+
codexAgent: input.codexAgent
|
|
198394
|
+
});
|
|
198395
|
+
if (agent2.name === "opencode") return autoSelectModel();
|
|
198396
|
+
const provider2 = agent2.name === "claude" ? "anthropic" : "openai";
|
|
198397
|
+
return modelAliases.find(
|
|
198398
|
+
(alias) => alias.provider === provider2 && alias.preferred && !alias.hidden && !alias.fallback && !alias.routing
|
|
198399
|
+
)?.resolve;
|
|
198400
|
+
}
|
|
198401
|
+
async function selectConfiguredCredential(input) {
|
|
198402
|
+
const access = input.ctx.credentialAccess;
|
|
198403
|
+
const model = input.model;
|
|
198404
|
+
if (!access || !model?.includes("/") || getProviderGatewayUrl(model)) return false;
|
|
198405
|
+
const subscription = subscriptionForModel(model);
|
|
198406
|
+
const names = getModelEnvVars(model).filter((name) => name !== subscription);
|
|
198407
|
+
if (subscription) names.unshift(subscription);
|
|
198408
|
+
const candidates = access.candidates.filter((candidate) => names.includes(candidate.name));
|
|
198409
|
+
if (!candidates.length) return false;
|
|
198410
|
+
const refused = [];
|
|
198411
|
+
for (const name of names.filter((item) => workflowCredentials[item])) {
|
|
198412
|
+
const value2 = workflowCredentials[name];
|
|
198413
|
+
const verdict = await probe({ name, value: value2, model });
|
|
198414
|
+
if (usable(verdict)) {
|
|
198415
|
+
activate({ names, name, value: value2, receipt: void 0, model });
|
|
198416
|
+
return true;
|
|
198417
|
+
}
|
|
198418
|
+
refused.push(`${name} from the workflow: ${verdict.detail}`);
|
|
198419
|
+
log2.info(`\xBB ${name} from the workflow unavailable; trying the next credential`);
|
|
198420
|
+
}
|
|
198421
|
+
for (const candidate of candidates) {
|
|
198422
|
+
const selected = await select2({ access, candidate });
|
|
198423
|
+
if (!selected) continue;
|
|
198424
|
+
const verdict = await probe({ ...selected, model });
|
|
198425
|
+
await reportVerdict({ receipt: selected.receipt, verdict });
|
|
198426
|
+
if (usable(verdict)) {
|
|
198427
|
+
activate({ names, ...selected, model });
|
|
198428
|
+
log2.info(`\xBB selected ${candidate.name} from ${candidate.source} scope`);
|
|
198429
|
+
return true;
|
|
198430
|
+
}
|
|
198431
|
+
refused.push(`${candidate.name} from ${candidate.source} scope: ${verdict.detail}`);
|
|
198432
|
+
log2.info(
|
|
198433
|
+
`\xBB ${candidate.name} from ${candidate.source} scope unavailable; trying the next credential`
|
|
198434
|
+
);
|
|
198435
|
+
}
|
|
198436
|
+
throw new Error(
|
|
198437
|
+
`all configured credentials for ${model} were rejected or exhausted (${refused.join("; ")}); no other model or provider was selected`
|
|
198438
|
+
);
|
|
198439
|
+
}
|
|
198440
|
+
async function probe(input) {
|
|
198441
|
+
const subscription = subscriptionNameSchema.safeParse(input.name);
|
|
198442
|
+
const model = stripProviderPrefix(input.model);
|
|
198443
|
+
return subscription.success ? probeSubscription({ name: subscription.data, value: input.value, model }) : probeInference({ ...input, model });
|
|
198444
|
+
}
|
|
198445
|
+
function usable(verdict) {
|
|
198446
|
+
return verdict.status !== "rejected" && verdict.status !== "exhausted";
|
|
198447
|
+
}
|
|
198448
|
+
async function reportVerdict(input) {
|
|
198449
|
+
if (input.verdict.status === "unknown") return;
|
|
198450
|
+
await apiFetch({
|
|
198451
|
+
path: "/api/runtime/credentials/status",
|
|
198452
|
+
method: "POST",
|
|
198453
|
+
headers: { authorization: `Bearer ${input.receipt}`, "content-type": "application/json" },
|
|
198454
|
+
body: JSON.stringify(input.verdict),
|
|
198455
|
+
signal: AbortSignal.timeout(5e3)
|
|
198456
|
+
}).catch(() => log2.debug("\xBB could not record a credential verdict"));
|
|
198457
|
+
}
|
|
198458
|
+
function activate(input) {
|
|
198459
|
+
for (const name of input.names) {
|
|
198460
|
+
delete process.env[name];
|
|
198461
|
+
delete receipts[name];
|
|
198462
|
+
if (name === "CODEX_AUTH_JSON" || name === "GROK_AUTH_JSON") clearInstalledSubscription(name);
|
|
198463
|
+
}
|
|
198464
|
+
const value2 = sanitizeSecret(input.name, input.value);
|
|
198465
|
+
if (!value2) throw new Error("configured credential was empty");
|
|
198466
|
+
process.env[input.name] = value2;
|
|
198467
|
+
if (input.receipt) receipts[input.name] = input.receipt;
|
|
198468
|
+
installCodexAuth();
|
|
198469
|
+
installXaiAuth();
|
|
198470
|
+
saveReceipts();
|
|
198471
|
+
authorizeModel(input.model);
|
|
198131
198472
|
}
|
|
198132
198473
|
|
|
198133
198474
|
// utils/gitAuthServer.ts
|
|
@@ -198833,75 +199174,6 @@ function buildModelAccessError(input) {
|
|
|
198833
199174
|
return [headline, "", why, "", cta].join("\n");
|
|
198834
199175
|
}
|
|
198835
199176
|
|
|
198836
|
-
// utils/normalizeEnv.ts
|
|
198837
|
-
function hasControlCharacter(value2) {
|
|
198838
|
-
return [...value2].some((ch) => {
|
|
198839
|
-
const code = ch.charCodeAt(0);
|
|
198840
|
-
return code < 32 || code === 127;
|
|
198841
|
-
});
|
|
198842
|
-
}
|
|
198843
|
-
function sanitizeSecret(key, value2) {
|
|
198844
|
-
const trimmed = value2.trim();
|
|
198845
|
-
if (trimmed.length === 0) {
|
|
198846
|
-
log2.warning(
|
|
198847
|
-
`\xBB ${key} is whitespace-only \u2014 leaving env var unchanged. check your secret value.`
|
|
198848
|
-
);
|
|
198849
|
-
return null;
|
|
198850
|
-
}
|
|
198851
|
-
if (key.endsWith("_API_KEY") && hasControlCharacter(trimmed)) {
|
|
198852
|
-
log2.warning(
|
|
198853
|
-
`\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.`
|
|
198854
|
-
);
|
|
198855
|
-
return null;
|
|
198856
|
-
}
|
|
198857
|
-
if (trimmed !== value2) {
|
|
198858
|
-
log2.warning(
|
|
198859
|
-
`\xBB stripped whitespace from ${key} (whitespace in secret values breaks GitHub Actions log masking)`
|
|
198860
|
-
);
|
|
198861
|
-
}
|
|
198862
|
-
maskSecret(trimmed);
|
|
198863
|
-
return trimmed;
|
|
198864
|
-
}
|
|
198865
|
-
function normalizeEnv() {
|
|
198866
|
-
const upperKeys = /* @__PURE__ */ new Map();
|
|
198867
|
-
for (const key of Object.keys(process.env)) {
|
|
198868
|
-
const upper2 = key.toUpperCase();
|
|
198869
|
-
const existing = upperKeys.get(upper2) || [];
|
|
198870
|
-
existing.push(key);
|
|
198871
|
-
upperKeys.set(upper2, existing);
|
|
198872
|
-
}
|
|
198873
|
-
for (const [upperKey, keys] of upperKeys) {
|
|
198874
|
-
if (keys.length === 1) {
|
|
198875
|
-
const key = keys[0];
|
|
198876
|
-
if (key !== upperKey) {
|
|
198877
|
-
process.env[upperKey] = process.env[key];
|
|
198878
|
-
delete process.env[key];
|
|
198879
|
-
}
|
|
198880
|
-
continue;
|
|
198881
|
-
}
|
|
198882
|
-
const values = keys.map((k2) => process.env[k2]);
|
|
198883
|
-
const uniqueValues = new Set(values);
|
|
198884
|
-
if (uniqueValues.size > 1) {
|
|
198885
|
-
log2.warning(
|
|
198886
|
-
`env var conflict: ${keys.join(", ")} have different values. using uppercase ${upperKey}.`
|
|
198887
|
-
);
|
|
198888
|
-
}
|
|
198889
|
-
const preferredKey = keys.find((k2) => k2 === upperKey) || keys[0];
|
|
198890
|
-
const preferredValue = process.env[preferredKey];
|
|
198891
|
-
for (const key of keys) {
|
|
198892
|
-
delete process.env[key];
|
|
198893
|
-
}
|
|
198894
|
-
process.env[upperKey] = preferredValue;
|
|
198895
|
-
}
|
|
198896
|
-
for (const key of Object.keys(process.env)) {
|
|
198897
|
-
if (!isSensitiveEnvName(key)) continue;
|
|
198898
|
-
const value2 = process.env[key];
|
|
198899
|
-
if (typeof value2 !== "string" || value2.length === 0) continue;
|
|
198900
|
-
const sanitized = sanitizeSecret(key, value2);
|
|
198901
|
-
if (sanitized !== null) process.env[key] = sanitized;
|
|
198902
|
-
}
|
|
198903
|
-
}
|
|
198904
|
-
|
|
198905
199177
|
// utils/overrides.ts
|
|
198906
199178
|
var DENIED_OVERRIDE_NAMES = /* @__PURE__ */ new Set([
|
|
198907
199179
|
"GITHUB_TOKEN",
|
|
@@ -199472,7 +199744,6 @@ ${ctx.error}` : ctx.error;
|
|
|
199472
199744
|
workflowRun: runId ? { owner: repoContext.owner, repo: repoContext.name, runId } : void 0,
|
|
199473
199745
|
customParts,
|
|
199474
199746
|
model: ctx.toolState.model,
|
|
199475
|
-
fallbackFrom: ctx.toolState.modelFallback?.from,
|
|
199476
199747
|
clamped: ctx.toolState.modelClamped,
|
|
199477
199748
|
unselectedProxyDefault: ctx.toolState.unselectedProxyDefault,
|
|
199478
199749
|
oss: ctx.toolState.oss,
|
|
@@ -199856,7 +200127,8 @@ async function fetchRunContext(params) {
|
|
|
199856
200127
|
const timeoutId = setTimeout(() => controller.abort(), timeoutMs);
|
|
199857
200128
|
try {
|
|
199858
200129
|
const headers = {
|
|
199859
|
-
Authorization: `Bearer ${params.token}
|
|
200130
|
+
Authorization: `Bearer ${params.token}`,
|
|
200131
|
+
"X-Pullfrog-Credential-Pools": "1"
|
|
199860
200132
|
};
|
|
199861
200133
|
if (params.oidcToken) {
|
|
199862
200134
|
headers["X-GitHub-OIDC-Token"] = params.oidcToken;
|
|
@@ -201153,16 +201425,20 @@ async function main() {
|
|
|
201153
201425
|
const preIntrospectionDirty = repoDir ? await dirtyTrackedPaths({ cwd: repoDir }) : null;
|
|
201154
201426
|
const opencodeCliPath = await agents.opencode.install();
|
|
201155
201427
|
captureBaselineModels(opencodeCliPath);
|
|
201428
|
+
const injected = [];
|
|
201156
201429
|
if (runContext.dbSecrets) {
|
|
201157
201430
|
for (const [key, value2] of Object.entries(runContext.dbSecrets)) {
|
|
201158
201431
|
if (!process.env[key]) {
|
|
201159
201432
|
const sanitized = sanitizeSecret(key, value2);
|
|
201160
|
-
if (sanitized
|
|
201433
|
+
if (sanitized === null) continue;
|
|
201434
|
+
process.env[key] = sanitized;
|
|
201435
|
+
injected.push(key);
|
|
201161
201436
|
}
|
|
201162
201437
|
}
|
|
201163
201438
|
const count = Object.keys(runContext.dbSecrets).length;
|
|
201164
201439
|
if (count > 0) log2.info(`\xBB ${count} db secret(s) loaded`);
|
|
201165
201440
|
}
|
|
201441
|
+
await initializeCredentialPool({ ctx: runContext, slug: payload.model, injected });
|
|
201166
201442
|
installCodexAuth();
|
|
201167
201443
|
installXaiAuth();
|
|
201168
201444
|
captureAuthorizedModels(opencodeCliPath);
|
|
@@ -201236,13 +201512,23 @@ async function main() {
|
|
|
201236
201512
|
}
|
|
201237
201513
|
const gitAuthServer = __using(_stack2, await startGitAuthServer(tmpdir4), true);
|
|
201238
201514
|
setGitAuthServer(gitAuthServer);
|
|
201515
|
+
let resolvedModel = resolveModel({ slug: payload.model });
|
|
201516
|
+
if (!resolvedModel && !payload.proxyModel && runContext.credentialAccess?.candidates.length) {
|
|
201517
|
+
resolvedModel = resolvePoolModel({
|
|
201518
|
+
codexAgent: runContext.repoSettings.codexAgent || payload.codexArm === true
|
|
201519
|
+
});
|
|
201520
|
+
}
|
|
201521
|
+
let poolSelected = !payload.proxyModel && await selectConfiguredCredential({
|
|
201522
|
+
ctx: runContext,
|
|
201523
|
+
model: resolvedModel
|
|
201524
|
+
});
|
|
201239
201525
|
const access = decideModelAccess({
|
|
201240
201526
|
modelExplicit: payload.modelExplicit ?? false,
|
|
201241
201527
|
model: payload.model,
|
|
201242
201528
|
oss: runContext.oss,
|
|
201243
201529
|
proxyActive: !!payload.proxyModel,
|
|
201244
201530
|
subsidyTarget: runContext.proxyModel,
|
|
201245
|
-
resolvedModel
|
|
201531
|
+
resolvedModel,
|
|
201246
201532
|
authorized: getAuthorizedModels()
|
|
201247
201533
|
});
|
|
201248
201534
|
if (access.kind === "error") {
|
|
@@ -201256,15 +201542,19 @@ async function main() {
|
|
|
201256
201542
|
);
|
|
201257
201543
|
}
|
|
201258
201544
|
if (access.kind === "proxy") payload.proxyModel = access.target;
|
|
201259
|
-
if (access.kind === "byok")
|
|
201545
|
+
if (access.kind === "byok") {
|
|
201546
|
+
payload.proxyModel = void 0;
|
|
201547
|
+
if (!poolSelected)
|
|
201548
|
+
poolSelected = await selectConfiguredCredential({
|
|
201549
|
+
ctx: runContext,
|
|
201550
|
+
model: resolvedModel
|
|
201551
|
+
});
|
|
201552
|
+
}
|
|
201260
201553
|
if (runContext.oss && payload.proxyModel) {
|
|
201261
201554
|
payload.effort = ossEffortFloor({ payload });
|
|
201262
201555
|
}
|
|
201263
|
-
|
|
201264
|
-
const credentials = payload.proxyModel ? { kind: "ok" } : await checkConfiguredCredentials({
|
|
201265
|
-
model: configuredModel,
|
|
201266
|
-
authorized: getAuthorizedModels()
|
|
201267
|
-
});
|
|
201556
|
+
if (payload.proxyModel) resolvedModel = void 0;
|
|
201557
|
+
const credentials = payload.proxyModel || poolSelected ? { kind: "ok" } : await checkConfiguredCredentials({ model: resolvedModel });
|
|
201268
201558
|
if (credentials.kind === "dead") {
|
|
201269
201559
|
throw new Error(
|
|
201270
201560
|
buildRejectedCredentialError({
|
|
@@ -201276,13 +201566,6 @@ async function main() {
|
|
|
201276
201566
|
})
|
|
201277
201567
|
);
|
|
201278
201568
|
}
|
|
201279
|
-
if (credentials.kind === "fellBack" && configuredModel) {
|
|
201280
|
-
log2.warning(
|
|
201281
|
-
`\xBB ${credentials.credential} was rejected by its provider \u2014 running ${credentials.replacement} instead of ${configuredModel}`
|
|
201282
|
-
);
|
|
201283
|
-
toolState.modelFallback = { from: configuredModel };
|
|
201284
|
-
}
|
|
201285
|
-
const resolvedModel = credentials.kind === "fellBack" ? credentials.replacement : configuredModel;
|
|
201286
201569
|
vertexCredentials = materializeVertexCredentials({ model: resolvedModel });
|
|
201287
201570
|
let agent2 = resolveAgent({
|
|
201288
201571
|
model: resolvedModel,
|
|
@@ -201592,12 +201875,12 @@ ${instructions.user}` : null,
|
|
|
201592
201875
|
if (payload.timeout === TIMEOUT_DISABLED) {
|
|
201593
201876
|
result = await Promise.race([agentPromise, activityTimeout.promise]);
|
|
201594
201877
|
} else {
|
|
201595
|
-
const
|
|
201596
|
-
if (payload.timeout &&
|
|
201878
|
+
const usable2 = resolveTimeoutMs(payload.timeout);
|
|
201879
|
+
if (payload.timeout && usable2 === null) {
|
|
201597
201880
|
log2.warning(`invalid timeout "${payload.timeout}" (use --notimeout to disable), using 1h`);
|
|
201598
201881
|
}
|
|
201599
|
-
const timeoutMs =
|
|
201600
|
-
const actualTimeout =
|
|
201882
|
+
const timeoutMs = usable2 ?? 36e5;
|
|
201883
|
+
const actualTimeout = usable2 !== null ? payload.timeout : "1h";
|
|
201601
201884
|
let timeoutId;
|
|
201602
201885
|
const timeoutPromise = new Promise((_4, reject) => {
|
|
201603
201886
|
timeoutId = setTimeout(() => {
|
|
@@ -202225,7 +202508,7 @@ function resolveTarget2(repo) {
|
|
|
202225
202508
|
return { owner: match3[1], repo: match3[2] };
|
|
202226
202509
|
}
|
|
202227
202510
|
function cliVersion() {
|
|
202228
|
-
const parts = /^(\d+)\.(\d+)\.(\d+)$/.exec("0.1.
|
|
202511
|
+
const parts = /^(\d+)\.(\d+)\.(\d+)$/.exec("0.1.91");
|
|
202229
202512
|
if (!parts) return "0.0.0";
|
|
202230
202513
|
return `${Number(parts[1])}.${Number(parts[2])}.${Number(parts[3])}`;
|
|
202231
202514
|
}
|
|
@@ -202392,7 +202675,7 @@ async function runCli8(input) {
|
|
|
202392
202675
|
}
|
|
202393
202676
|
|
|
202394
202677
|
// cli.ts
|
|
202395
|
-
var VERSION10 = "0.1.
|
|
202678
|
+
var VERSION10 = "0.1.91";
|
|
202396
202679
|
var bin = basename2(process.argv[1] || "");
|
|
202397
202680
|
var PROG = bin === "pf" || bin === "pullfrog" ? bin : "pullfrog";
|
|
202398
202681
|
var rawArgs = process.argv.slice(2);
|