pullfrog 0.1.99 → 0.1.101
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 +689 -737
- package/dist/external.d.ts +1 -1
- package/dist/index.js +687 -735
- package/dist/internal/index.d.ts +3 -2
- package/dist/internal.js +70 -19
- package/dist/models.d.ts +4 -0
- package/dist/toolState.d.ts +2 -4
- package/dist/utils/agentHangReport.d.ts +6 -10
- package/dist/utils/apiKeys.d.ts +47 -7
- package/dist/utils/billingErrors.d.ts +6 -3
- package/dist/utils/buildPullfrogFooter.d.ts +17 -4
- package/dist/utils/errorReport.d.ts +0 -1
- package/dist/utils/failureBody.d.ts +27 -0
- package/dist/utils/leapingComment.d.ts +9 -0
- package/dist/utils/proxy.d.ts +1 -0
- package/dist/utils/roleMirror.d.ts +3 -2
- package/dist/utils/runContext.d.ts +3 -0
- package/dist/utils/runContextData.d.ts +2 -0
- package/dist/utils/runErrorRenderer.d.ts +44 -69
- package/dist/utils/secretCommands.d.ts +5 -14
- package/package.json +1 -1
package/dist/cli.mjs
CHANGED
|
@@ -64372,7 +64372,7 @@ var require_receiver2 = __commonJS({
|
|
|
64372
64372
|
const buffer = this.consume(2);
|
|
64373
64373
|
const fin = (buffer[0] & 128) !== 0;
|
|
64374
64374
|
const opcode = buffer[0] & 15;
|
|
64375
|
-
const
|
|
64375
|
+
const masked2 = (buffer[1] & 128) === 128;
|
|
64376
64376
|
const fragmented = !fin && opcode !== opcodes.CONTINUATION;
|
|
64377
64377
|
const payloadLength = buffer[1] & 127;
|
|
64378
64378
|
const rsv1 = buffer[0] & 64;
|
|
@@ -64382,7 +64382,7 @@ var require_receiver2 = __commonJS({
|
|
|
64382
64382
|
failWebsocketConnection(this.#handler, 1002, "Invalid opcode received");
|
|
64383
64383
|
return callback();
|
|
64384
64384
|
}
|
|
64385
|
-
if (
|
|
64385
|
+
if (masked2) {
|
|
64386
64386
|
failWebsocketConnection(this.#handler, 1002, "Frame cannot be masked");
|
|
64387
64387
|
return callback();
|
|
64388
64388
|
}
|
|
@@ -64430,7 +64430,7 @@ var require_receiver2 = __commonJS({
|
|
|
64430
64430
|
this.#info.compressed = rsv1 !== 0;
|
|
64431
64431
|
}
|
|
64432
64432
|
this.#info.opcode = opcode;
|
|
64433
|
-
this.#info.masked =
|
|
64433
|
+
this.#info.masked = masked2;
|
|
64434
64434
|
this.#info.fin = fin;
|
|
64435
64435
|
this.#info.fragmented = fragmented;
|
|
64436
64436
|
} else if (this.#state === parserStates.PAYLOADLENGTH_16) {
|
|
@@ -107049,7 +107049,7 @@ var import_picocolors = __toESM(require_picocolors(), 1);
|
|
|
107049
107049
|
// package.json
|
|
107050
107050
|
var package_default = {
|
|
107051
107051
|
name: "pullfrog",
|
|
107052
|
-
version: "0.1.
|
|
107052
|
+
version: "0.1.101",
|
|
107053
107053
|
cliContractVersion: "1",
|
|
107054
107054
|
type: "module",
|
|
107055
107055
|
bin: {
|
|
@@ -109232,9 +109232,9 @@ var providers2 = {
|
|
|
109232
109232
|
models: {
|
|
109233
109233
|
"gemini-pro": {
|
|
109234
109234
|
displayName: "Gemini Pro",
|
|
109235
|
-
resolve: "google/gemini-
|
|
109235
|
+
resolve: "google/gemini-4-argon",
|
|
109236
109236
|
effort: ["low", "medium", "high"],
|
|
109237
|
-
openRouterResolve: "openrouter/google/gemini-
|
|
109237
|
+
openRouterResolve: "openrouter/google/gemini-4-argon",
|
|
109238
109238
|
preferred: true
|
|
109239
109239
|
// Inherit (subagents stay on Pro). Google has no in-between tier;
|
|
109240
109240
|
// dropping to Flash for review work was a meaningful capability cliff
|
|
@@ -109424,7 +109424,7 @@ var providers2 = {
|
|
|
109424
109424
|
displayName: "Kimi K3",
|
|
109425
109425
|
description: "1M context, but only on Allegretto and above \xB7 Moderato caps it at 256K",
|
|
109426
109426
|
resolve: "kimi-code-plan-cn/k3",
|
|
109427
|
-
effort: ["low", "high", "max"],
|
|
109427
|
+
effort: ["none", "low", "high", "max"],
|
|
109428
109428
|
subagentModel: "kimi-k2"
|
|
109429
109429
|
},
|
|
109430
109430
|
// `preferred` sits on the 256K variant rather than the 1M flagship, which
|
|
@@ -109439,7 +109439,7 @@ var providers2 = {
|
|
|
109439
109439
|
displayName: "Kimi K3 256K",
|
|
109440
109440
|
description: "Same model at 256K for ~half the quota \xB7 Moderato or above",
|
|
109441
109441
|
resolve: "kimi-code-plan-cn/k3-256k",
|
|
109442
|
-
effort: ["low", "high", "max"],
|
|
109442
|
+
effort: ["none", "low", "high", "max"],
|
|
109443
109443
|
preferred: true,
|
|
109444
109444
|
subagentModel: "kimi-k2"
|
|
109445
109445
|
},
|
|
@@ -109447,9 +109447,10 @@ var providers2 = {
|
|
|
109447
109447
|
displayName: "Kimi K2",
|
|
109448
109448
|
description: "Included in every Kimi membership tier",
|
|
109449
109449
|
resolve: "kimi-code-plan-cn/kimi-for-coding",
|
|
109450
|
-
// models.dev published
|
|
109451
|
-
// mirrors it, so main was red until the
|
|
109452
|
-
|
|
109450
|
+
// models.dev published `low, high, max` on 2026-09-14 and added `none`
|
|
109451
|
+
// on 2026-10-19; the catalog gate mirrors it, so main was red until the
|
|
109452
|
+
// alias carried it too.
|
|
109453
|
+
effort: ["none", "low", "high", "max"]
|
|
109453
109454
|
},
|
|
109454
109455
|
"kimi-k2-highspeed": {
|
|
109455
109456
|
displayName: "Kimi K2 HighSpeed",
|
|
@@ -109619,9 +109620,9 @@ var providers2 = {
|
|
|
109619
109620
|
},
|
|
109620
109621
|
"gemini-pro": {
|
|
109621
109622
|
displayName: "Gemini Pro",
|
|
109622
|
-
resolve: "opencode/gemini-
|
|
109623
|
+
resolve: "opencode/gemini-4-argon",
|
|
109623
109624
|
effort: ["low", "medium", "high"],
|
|
109624
|
-
openRouterResolve: "openrouter/google/gemini-
|
|
109625
|
+
openRouterResolve: "openrouter/google/gemini-4-argon"
|
|
109625
109626
|
// Inherit — see google/gemini-pro for rationale.
|
|
109626
109627
|
},
|
|
109627
109628
|
"gemini-flash": {
|
|
@@ -110353,8 +110354,8 @@ var providers2 = {
|
|
|
110353
110354
|
},
|
|
110354
110355
|
"gemini-pro": {
|
|
110355
110356
|
displayName: "Gemini Pro",
|
|
110356
|
-
resolve: "vercel/google/gemini-
|
|
110357
|
-
effort: ["low", "high"]
|
|
110357
|
+
resolve: "vercel/google/gemini-4-argon",
|
|
110358
|
+
effort: ["low", "medium", "high"]
|
|
110358
110359
|
},
|
|
110359
110360
|
"gemini-flash": {
|
|
110360
110361
|
displayName: "Gemini Flash",
|
|
@@ -110573,6 +110574,13 @@ if (!defaultProxyAlias?.openRouterResolve) {
|
|
|
110573
110574
|
}
|
|
110574
110575
|
var DEFAULT_PROXY_MODEL = defaultProxyAlias.openRouterResolve;
|
|
110575
110576
|
var defaultProxyDisplayName = defaultProxyAlias.displayName;
|
|
110577
|
+
var trialProxyAlias = resolveDisplayAlias("openai/gpt-luna");
|
|
110578
|
+
if (!trialProxyAlias?.openRouterResolve) {
|
|
110579
|
+
throw new Error("TRIAL_PROXY_MODEL: openai/gpt-luna has no openRouterResolve");
|
|
110580
|
+
}
|
|
110581
|
+
var TRIAL_PROXY_MODEL = trialProxyAlias.openRouterResolve;
|
|
110582
|
+
var TRIAL_PROXY_DISPLAY_NAME = trialProxyAlias.displayName;
|
|
110583
|
+
var TRIAL_FUNDED_USD_LIMIT = 10;
|
|
110576
110584
|
for (const [name, ladder] of Object.entries({ ...PROVIDER_LADDERS, router: ROUTER_LADDER })) {
|
|
110577
110585
|
const terminal = /* @__PURE__ */ new Set();
|
|
110578
110586
|
for (const slug2 of Object.values(ladder)) {
|
|
@@ -111280,6 +111288,8 @@ var PROVIDER_ERROR_PATTERNS = [
|
|
|
111280
111288
|
},
|
|
111281
111289
|
// OpenAI's `You have no credits remaining` (#1474).
|
|
111282
111290
|
{ regex: /no credits remaining/i, label: PROVIDER_BILLING_EXHAUSTED_LABEL },
|
|
111291
|
+
// Codex on a ChatGPT workspace: `Your workspace is out of credits. Add credits to continue.`
|
|
111292
|
+
{ regex: /workspace is out of credits/i, label: PROVIDER_BILLING_EXHAUSTED_LABEL },
|
|
111283
111293
|
{ regex: /credit balance is too low/i, label: PROVIDER_BILLING_EXHAUSTED_LABEL },
|
|
111284
111294
|
// "spending cap" (Gemini) and "spending limit" (xAI, #1076) are the same
|
|
111285
111295
|
// condition in two house styles — a configured ceiling was reached.
|
|
@@ -111309,6 +111319,9 @@ var PROVIDER_ERROR_PATTERNS = [
|
|
|
111309
111319
|
// succeeds" while the same log held nine of these. Measured on a live account
|
|
111310
111320
|
// whose ChatGPT subscription was capped.
|
|
111311
111321
|
{ regex: /\bthe usage limit has been reached\b/i, label: PROVIDER_USAGE_LIMIT_LABEL },
|
|
111322
|
+
// the Codex CLI's wording for the same capped plan: `You’ve hit your usage limit. … try
|
|
111323
|
+
// again at 7:58 AM.` Claude's `hit your session limit` names its window and stays out.
|
|
111324
|
+
{ regex: /\bhit your usage limit\b/i, label: PROVIDER_USAGE_LIMIT_LABEL },
|
|
111312
111325
|
// the OpenRouter shapes `isRouterKeylimitExhaustedError` matches. on a Router
|
|
111313
111326
|
// run those mean the PULLFROG wallet is empty and `renderRunError` returns a
|
|
111314
111327
|
// `BillingError` before ever reaching this list; on a BYOK run the very same
|
|
@@ -111413,7 +111426,8 @@ function isProviderMissingCredential(text) {
|
|
|
111413
111426
|
return /API key is missing\. Pass it using the/i.test(text) || /Missing Authentication header/i.test(text);
|
|
111414
111427
|
}
|
|
111415
111428
|
function isTransientUpstreamError(text) {
|
|
111416
|
-
return /API Error:\s*5\d\d/i.test(text) || /\bOverloaded\b/.test(text) || /"error_type"\s*:\s*"(?:provider_unavailable|timeout)"/i.test(text) || /Streaming response failed:\s*\[5\d\d\]/i.test(text)
|
|
111429
|
+
return /API Error:\s*5\d\d/i.test(text) || /\bOverloaded\b/.test(text) || /"error_type"\s*:\s*"(?:provider_unavailable|timeout)"/i.test(text) || /Streaming response failed:\s*\[5\d\d\]/i.test(text) || // the Codex CLI's `Selected model is at capacity. Please try a different model.`
|
|
111430
|
+
/\bmodel is at capacity\b/i.test(text);
|
|
111417
111431
|
}
|
|
111418
111432
|
function extractProviderId(text) {
|
|
111419
111433
|
const match3 = text.match(/\bproviderID=([a-z0-9_-]+)/i);
|
|
@@ -113798,9 +113812,20 @@ async function writeBackEntry(apiToken, entry) {
|
|
|
113798
113812
|
|
|
113799
113813
|
// utils/secretCommands.ts
|
|
113800
113814
|
var core3 = __toESM(require_core(), 1);
|
|
113815
|
+
var masked = /* @__PURE__ */ new Set();
|
|
113801
113816
|
function maskSecret(value2) {
|
|
113817
|
+
masked.add(value2);
|
|
113802
113818
|
if (isGitHubActions) core3.setSecret(value2);
|
|
113803
113819
|
}
|
|
113820
|
+
function redactSecrets(text) {
|
|
113821
|
+
let redacted = text;
|
|
113822
|
+
for (const value2 of masked) {
|
|
113823
|
+
for (const part of [value2, ...value2.split("\n").map((line) => line.trim())]) {
|
|
113824
|
+
if (part.length >= 8) redacted = redacted.replaceAll(part, "***");
|
|
113825
|
+
}
|
|
113826
|
+
}
|
|
113827
|
+
return redacted;
|
|
113828
|
+
}
|
|
113804
113829
|
function saveSecretState(name, value2) {
|
|
113805
113830
|
if (isGitHubActions) core3.saveState(name, value2);
|
|
113806
113831
|
}
|
|
@@ -192601,7 +192626,7 @@ function formatModelLabel(params) {
|
|
|
192601
192626
|
}
|
|
192602
192627
|
const base = alias?.isFree ? `\`${modelName}\` (free)` : `\`${modelName}\``;
|
|
192603
192628
|
if (params.clamped?.reason === "trial") {
|
|
192604
|
-
return `${base} (
|
|
192629
|
+
return `${base} (free trial credits)`;
|
|
192605
192630
|
}
|
|
192606
192631
|
if (params.clamped) {
|
|
192607
192632
|
const target = isAutoTier(params.clamped.from) ? "the intelligent tier" : `\`${resolveDisplayAlias(params.clamped.from)?.displayName ?? params.clamped.from}\``;
|
|
@@ -192612,13 +192637,14 @@ function formatModelLabel(params) {
|
|
|
192612
192637
|
}
|
|
192613
192638
|
return base;
|
|
192614
192639
|
}
|
|
192615
|
-
function buildTrialDisclosure(
|
|
192616
|
-
const consoleUrl2 = owner ? `https://pullfrog.com/console/${owner}` : "https://pullfrog.com/console";
|
|
192640
|
+
function buildTrialDisclosure(params) {
|
|
192641
|
+
const consoleUrl2 = params.owner ? `https://pullfrog.com/console/${params.owner}#billing` : "https://pullfrog.com/console";
|
|
192642
|
+
const left = params.creditsLeftUsd === void 0 ? "" : ` ($${Math.max(0, params.creditsLeftUsd).toFixed(2)} of $${TRIAL_FUNDED_USD_LIMIT} left)`;
|
|
192617
192643
|
return [
|
|
192618
192644
|
"---",
|
|
192619
192645
|
"",
|
|
192620
192646
|
"> [!IMPORTANT]",
|
|
192621
|
-
`> **
|
|
192647
|
+
`> **This run used your free trial credits**${left} on \`${TRIAL_PROXY_DISPLAY_NAME}\`, a fast, lightweight model. [Connect a provider \u2192](${consoleUrl2}) to run the model you choose.`
|
|
192622
192648
|
].join("\n");
|
|
192623
192649
|
}
|
|
192624
192650
|
function buildPullfrogFooter(params) {
|
|
@@ -192641,13 +192667,13 @@ function buildPullfrogFooter(params) {
|
|
|
192641
192667
|
`Using ${formatModelLabel({
|
|
192642
192668
|
model: params.model,
|
|
192643
192669
|
clamped: params.clamped,
|
|
192644
|
-
unselectedProxyDefault: params.unselectedProxyDefault,
|
|
192670
|
+
unselectedProxyDefault: params.unselectedProxyDefault && !params.failed,
|
|
192645
192671
|
oss: params.oss
|
|
192646
192672
|
})}`
|
|
192647
192673
|
);
|
|
192648
192674
|
}
|
|
192649
|
-
const allParts = [...parts, "[\u{1D54F}](https://x.com/pullfrogai)"];
|
|
192650
|
-
const disclosure = params.clamped?.reason === "trial" ? `${buildTrialDisclosure(params.owner)}
|
|
192675
|
+
const allParts = params.failed ? parts : [...parts, "[\u{1D54F}](https://x.com/pullfrogai)"];
|
|
192676
|
+
const disclosure = params.clamped?.reason === "trial" && !params.failed ? `${buildTrialDisclosure({ owner: params.owner, creditsLeftUsd: params.clamped.creditsLeftUsd })}
|
|
192651
192677
|
|
|
192652
192678
|
` : "";
|
|
192653
192679
|
return `
|
|
@@ -192825,7 +192851,8 @@ async function fetchRunContext(params) {
|
|
|
192825
192851
|
credentialAccess: data.credentialAccess,
|
|
192826
192852
|
secretsUnavailable: data.secretsUnavailable,
|
|
192827
192853
|
routerUnfunded: data.routerUnfunded,
|
|
192828
|
-
trialFallback: data.trialFallback
|
|
192854
|
+
trialFallback: data.trialFallback,
|
|
192855
|
+
trialCreditsLeftUsd: data.trialCreditsLeftUsd
|
|
192829
192856
|
};
|
|
192830
192857
|
} catch {
|
|
192831
192858
|
clearTimeout(timeoutId);
|
|
@@ -192939,6 +192966,9 @@ function aggregateUsage(entries) {
|
|
|
192939
192966
|
return out;
|
|
192940
192967
|
}
|
|
192941
192968
|
|
|
192969
|
+
// utils/leapingComment.ts
|
|
192970
|
+
var TODO_SPINNER_URL = "https://uploads.pullfrog.com/Progress%20Indicator.gif";
|
|
192971
|
+
|
|
192942
192972
|
// mcp/comment.ts
|
|
192943
192973
|
function isNotFoundError(error49) {
|
|
192944
192974
|
return error49 instanceof Error && error49.message.includes("Not Found");
|
|
@@ -195050,7 +195080,7 @@ function mirrorRolePermissions(role) {
|
|
|
195050
195080
|
case "maintain":
|
|
195051
195081
|
case "write":
|
|
195052
195082
|
case "triage":
|
|
195053
|
-
return { contents: "read", issues: "write", pull_requests: "write" };
|
|
195083
|
+
return { checks: "read", contents: "read", issues: "write", pull_requests: "write" };
|
|
195054
195084
|
case "read":
|
|
195055
195085
|
case "none":
|
|
195056
195086
|
case void 0:
|
|
@@ -197719,6 +197749,40 @@ function subagentDeniedToolNames(ctx, outputSchema) {
|
|
|
197719
197749
|
return names;
|
|
197720
197750
|
}
|
|
197721
197751
|
|
|
197752
|
+
// utils/failureBody.ts
|
|
197753
|
+
function renderFailure(params) {
|
|
197754
|
+
const blocks = [
|
|
197755
|
+
params.detail ? `**${params.headline}** ${params.detail}` : `**${params.headline}**`
|
|
197756
|
+
];
|
|
197757
|
+
if (params.list) blocks.push(params.list.map((item) => `- ${item}`).join("\n"));
|
|
197758
|
+
if (params.error) blocks.push(fenced(params.error));
|
|
197759
|
+
if (params.details) {
|
|
197760
|
+
blocks.push(
|
|
197761
|
+
`<details><summary>${params.details.summary}</summary>
|
|
197762
|
+
|
|
197763
|
+
${fenced(params.details.text)}
|
|
197764
|
+
|
|
197765
|
+
</details>`
|
|
197766
|
+
);
|
|
197767
|
+
}
|
|
197768
|
+
if (params.links?.length) {
|
|
197769
|
+
blocks.push(params.links.map((link2) => `[${link2.label} \u2192](${link2.url})`).join(" \xB7 "));
|
|
197770
|
+
}
|
|
197771
|
+
return blocks.join("\n\n");
|
|
197772
|
+
}
|
|
197773
|
+
function fenced(text) {
|
|
197774
|
+
let longest = 0;
|
|
197775
|
+
for (const match3 of text.matchAll(/`+/g)) longest = Math.max(longest, match3[0].length);
|
|
197776
|
+
const fence = "`".repeat(Math.max(3, longest + 1));
|
|
197777
|
+
return `${fence}
|
|
197778
|
+
${text}
|
|
197779
|
+
${fence}`;
|
|
197780
|
+
}
|
|
197781
|
+
var DISCORD_LINK = {
|
|
197782
|
+
label: "Ask in Discord",
|
|
197783
|
+
url: "https://discord.gg/8y96raFg8e"
|
|
197784
|
+
};
|
|
197785
|
+
|
|
197722
197786
|
// utils/providerDashboards.ts
|
|
197723
197787
|
var PROVIDER_DASHBOARDS = {
|
|
197724
197788
|
anthropic: {
|
|
@@ -197829,134 +197893,145 @@ var PROVIDER_DASHBOARDS = {
|
|
|
197829
197893
|
};
|
|
197830
197894
|
|
|
197831
197895
|
// utils/apiKeys.ts
|
|
197832
|
-
var MISSING_KEY_MARKER = "
|
|
197833
|
-
var BYOK_SLUG_MARKER = "
|
|
197834
|
-
var BYOK_CONFIG_MARKER = "
|
|
197896
|
+
var MISSING_KEY_MARKER = "No API key found";
|
|
197897
|
+
var BYOK_SLUG_MARKER = "must be set when the model is";
|
|
197898
|
+
var BYOK_CONFIG_MARKER = "is missing required settings:";
|
|
197835
197899
|
var BYOK_SETUP_PATTERN = new RegExp(
|
|
197836
|
-
`^(
|
|
197900
|
+
`^(?:\`[A-Z][A-Z0-9_]+\` ${BYOK_SLUG_MARKER}|[A-Z][\\w-]*(?: [\\w-]+){0,3} ${BYOK_CONFIG_MARKER})`
|
|
197837
197901
|
);
|
|
197838
197902
|
function isByokSetupError(text) {
|
|
197839
197903
|
return BYOK_SETUP_PATTERN.test(text);
|
|
197840
197904
|
}
|
|
197841
|
-
var SECRETS_UNAVAILABLE_MARKER = "couldn't load your
|
|
197842
|
-
var ROUTER_UNFUNDED_MARKER = "
|
|
197905
|
+
var SECRETS_UNAVAILABLE_MARKER = "couldn't load your stored secrets";
|
|
197906
|
+
var ROUTER_UNFUNDED_MARKER = "Your Pullfrog Router balance is empty";
|
|
197907
|
+
var TRIAL_SPENT_MARKER = "reached the limit of your free trial credits";
|
|
197908
|
+
function buildTrialSpentError(params) {
|
|
197909
|
+
return renderFailure({
|
|
197910
|
+
headline: `You've ${TRIAL_SPENT_MARKER}.`,
|
|
197911
|
+
detail: `Your $${TRIAL_FUNDED_USD_LIMIT} of trial usage credits have been used up, so you'll need to connect a provider to continue using Pullfrog.`,
|
|
197912
|
+
links: [providerLink(params.owner)]
|
|
197913
|
+
});
|
|
197914
|
+
}
|
|
197915
|
+
function repoConsoleUrl(repo) {
|
|
197916
|
+
return `${getApiUrl()}/console/${repo.owner}/${repo.name}`;
|
|
197917
|
+
}
|
|
197918
|
+
function pickModelLink(repo) {
|
|
197919
|
+
return { label: "Pick another model", url: repoConsoleUrl(repo) };
|
|
197920
|
+
}
|
|
197921
|
+
function providerLink(owner) {
|
|
197922
|
+
return { label: "Connect a provider", url: `${getApiUrl()}/console/${owner}#billing` };
|
|
197923
|
+
}
|
|
197843
197924
|
var CREDENTIAL_REJECTED_MARKER = "was rejected by its provider";
|
|
197844
197925
|
var CREDENTIAL_POOL_REFUSED_MARKER = "No configured credential can serve this run";
|
|
197845
197926
|
var NoUsableCredentialError = class extends Error {
|
|
197846
197927
|
};
|
|
197847
197928
|
function throwKeyError(params) {
|
|
197848
|
-
if (params.secretsUnavailable) throw new Error(buildSecretsUnavailableError(
|
|
197929
|
+
if (params.secretsUnavailable) throw new Error(buildSecretsUnavailableError());
|
|
197849
197930
|
if (params.routerUnfunded) throw new NoUsableCredentialError(buildRouterUnfundedError(params));
|
|
197850
197931
|
throw new NoUsableCredentialError(buildMissingApiKeyError(params));
|
|
197851
197932
|
}
|
|
197852
197933
|
function buildRouterUnfundedError(params) {
|
|
197853
|
-
|
|
197854
|
-
|
|
197855
|
-
|
|
197856
|
-
|
|
197857
|
-
|
|
197858
|
-
|
|
197859
|
-
|
|
197860
|
-
"",
|
|
197861
|
-
`[Top up Router \u2192](${billingUrl}) \xB7 [Model settings \u2192](${settingsUrl}) \xB7 [Setup docs \u2192](https://docs.pullfrog.com/keys) \xB7 [Ask in Discord \u2192](https://discord.gg/8y96raFg8e)`
|
|
197862
|
-
].join("\n");
|
|
197934
|
+
return renderFailure({
|
|
197935
|
+
headline: `${ROUTER_UNFUNDED_MARKER}, and this repo has no API key to fall back on.`,
|
|
197936
|
+
detail: "Add Router credit, or connect your own provider key or subscription.",
|
|
197937
|
+
links: [
|
|
197938
|
+
{ label: "Billing and providers", url: `${getApiUrl()}/console/${params.owner}#billing` }
|
|
197939
|
+
]
|
|
197940
|
+
});
|
|
197863
197941
|
}
|
|
197864
|
-
function buildSecretsUnavailableError(
|
|
197865
|
-
|
|
197866
|
-
|
|
197867
|
-
|
|
197868
|
-
|
|
197869
|
-
|
|
197870
|
-
"**To fix:** re-run the job. If it keeps happening, tell us in Discord.",
|
|
197871
|
-
"",
|
|
197872
|
-
`[Model settings \u2192](${settingsUrl}) \xB7 [Setup docs \u2192](https://docs.pullfrog.com/keys) \xB7 [Ask in Discord \u2192](https://discord.gg/8y96raFg8e)`
|
|
197873
|
-
].join("\n");
|
|
197942
|
+
function buildSecretsUnavailableError() {
|
|
197943
|
+
return renderFailure({
|
|
197944
|
+
headline: `Pullfrog ${SECRETS_UNAVAILABLE_MARKER} for this run.`,
|
|
197945
|
+
detail: "They're still saved; this is a temporary failure on our side. Re-trigger to try again, and tell us in Discord if it keeps happening.",
|
|
197946
|
+
links: [DISCORD_LINK]
|
|
197947
|
+
});
|
|
197874
197948
|
}
|
|
197875
197949
|
function buildMissingApiKeyError(params) {
|
|
197876
|
-
const githubSecretsUrl = `https://github.com/${params.owner}/${params.name}/settings/secrets/actions`;
|
|
197877
|
-
const settingsUrl = `${getApiUrl()}/console/${params.owner}/${params.name}`;
|
|
197878
197950
|
const envVars = params.model?.includes("/") ? getModelEnvVars(params.model) : [];
|
|
197879
197951
|
const [primary, ...alternates] = envVars;
|
|
197880
197952
|
const envVarList = primary ? `\`${primary}\`${alternates.length > 0 ? ` (or ${alternates.map((v) => `\`${v}\``).join(" / ")})` : ""}` : void 0;
|
|
197881
|
-
const
|
|
197953
|
+
const alias = params.model ? resolveDisplayAlias(params.model) ?? modelAliases.find((a2) => !a2.fallback && a2.resolve === params.model) : void 0;
|
|
197954
|
+
const modelName = params.model ? alias?.displayName ?? `\`${params.model}\`` : void 0;
|
|
197882
197955
|
const unhosted = params.model?.includes("/") ? getModelManagedCredentials(params.model).find((name) => unhostedSubscriptions.has(name)) : void 0;
|
|
197883
|
-
const
|
|
197956
|
+
const detail = unhosted ? `Your ${SUBSCRIPTION_CREDENTIALS[unhosted]?.label} is stored, but this runner has no passwordless \`sudo\`, which Pullfrog needs to keep it in \`${PULLFROG_DATA_DIR}\`, out of the agent's reach. Allow passwordless \`sudo\` for the runner user, run on a GitHub-hosted runner, or add an API key.` : envVarList ? `It needs ${envVarList}. Connect it in Pullfrog or add it as a GitHub Actions secret, or pick a model you already have a key for.` : "Pullfrog needs a key or subscription for at least one model provider. Connect one in Pullfrog, or add a key as a GitHub Actions secret.";
|
|
197957
|
+
return renderFailure({
|
|
197958
|
+
headline: envVarList && modelName ? `${MISSING_KEY_MARKER} for ${modelName}.` : `${MISSING_KEY_MARKER}.`,
|
|
197959
|
+
detail,
|
|
197960
|
+
links: [providerLink(params.owner), pickModelLink(params)]
|
|
197961
|
+
});
|
|
197962
|
+
}
|
|
197963
|
+
function buildSetupError(params) {
|
|
197964
|
+
const missing = params.missing.map((name) => `\`${name}\``).join(", ");
|
|
197884
197965
|
return [
|
|
197885
|
-
|
|
197886
|
-
|
|
197887
|
-
|
|
197888
|
-
|
|
197889
|
-
|
|
197890
|
-
].join("\n");
|
|
197966
|
+
`${params.provider} ${BYOK_CONFIG_MARKER} ${missing}. ${params.where}`,
|
|
197967
|
+
`\`\`\`yaml
|
|
197968
|
+
${params.env.join("\n")}
|
|
197969
|
+
\`\`\``,
|
|
197970
|
+
`${params.note ? `${params.note} ` : ""}[${params.provider} setup guide](${params.guide})`
|
|
197971
|
+
].join("\n\n");
|
|
197972
|
+
}
|
|
197973
|
+
function secretsUrl(params) {
|
|
197974
|
+
return `https://github.com/${params.owner}/${params.name}/settings/secrets/actions`;
|
|
197891
197975
|
}
|
|
197892
197976
|
function buildBedrockSetupError(params) {
|
|
197893
|
-
|
|
197894
|
-
|
|
197895
|
-
|
|
197896
|
-
|
|
197897
|
-
|
|
197898
|
-
|
|
197899
|
-
|
|
197900
|
-
|
|
197901
|
-
|
|
197902
|
-
|
|
197903
|
-
|
|
197904
|
-
|
|
197977
|
+
return buildSetupError({
|
|
197978
|
+
provider: "Amazon Bedrock",
|
|
197979
|
+
missing: params.missing,
|
|
197980
|
+
where: `Add them to [this repo's Actions secrets](${secretsUrl(params)}) and pass them in the workflow's \`env:\` block:`,
|
|
197981
|
+
env: [
|
|
197982
|
+
`AWS_BEARER_TOKEN_BEDROCK: \${{ secrets.AWS_BEARER_TOKEN_BEDROCK }}`,
|
|
197983
|
+
`AWS_REGION: \${{ secrets.AWS_REGION }}`,
|
|
197984
|
+
`${BEDROCK_MODEL_ID_ENV}: \${{ secrets.${BEDROCK_MODEL_ID_ENV} }}`
|
|
197985
|
+
],
|
|
197986
|
+
note: "`AWS_ACCESS_KEY_ID` and `AWS_SECRET_ACCESS_KEY` (plus `AWS_SESSION_TOKEN` if you use one) can replace the bearer token.",
|
|
197987
|
+
guide: "https://docs.pullfrog.com/bedrock"
|
|
197988
|
+
});
|
|
197905
197989
|
}
|
|
197906
197990
|
function buildVertexSetupError(params) {
|
|
197907
|
-
|
|
197908
|
-
|
|
197909
|
-
|
|
197910
|
-
|
|
197911
|
-
|
|
197912
|
-
|
|
197913
|
-
|
|
197914
|
-
|
|
197915
|
-
|
|
197916
|
-
|
|
197917
|
-
|
|
197991
|
+
return buildSetupError({
|
|
197992
|
+
provider: "Google Vertex AI",
|
|
197993
|
+
missing: params.missing,
|
|
197994
|
+
where: `Add the service account to [this repo's Actions secrets](${secretsUrl(params)}) and set the rest in the workflow's \`env:\` block:`,
|
|
197995
|
+
env: [
|
|
197996
|
+
`${VERTEX_SERVICE_ACCOUNT_JSON_ENV}: \${{ secrets.${VERTEX_SERVICE_ACCOUNT_JSON_ENV} }}`,
|
|
197997
|
+
`${GOOGLE_CLOUD_PROJECT_ENV}: my-project`,
|
|
197998
|
+
`${VERTEX_LOCATION_ENV}: global`,
|
|
197999
|
+
`${VERTEX_MODEL_ID_ENV}: <vertex-model-id>`
|
|
198000
|
+
],
|
|
198001
|
+
guide: "https://docs.pullfrog.com/vertex"
|
|
198002
|
+
});
|
|
197918
198003
|
}
|
|
197919
198004
|
function buildOpenAICompatibleSetupError(params) {
|
|
197920
|
-
|
|
197921
|
-
|
|
197922
|
-
|
|
197923
|
-
|
|
197924
|
-
|
|
197925
|
-
|
|
197926
|
-
|
|
197927
|
-
|
|
197928
|
-
|
|
197929
|
-
|
|
197930
|
-
|
|
197931
|
-
|
|
197932
|
-
|
|
197933
|
-
|
|
197934
|
-
auto-compaction is disabled, so long runs grow until your endpoint refuses them.
|
|
197935
|
-
|
|
197936
|
-
for full setup instructions, see https://docs.pullfrog.com/openai-compatible`;
|
|
198005
|
+
return buildSetupError({
|
|
198006
|
+
provider: "OpenAI-compatible endpoint",
|
|
198007
|
+
missing: params.missing,
|
|
198008
|
+
where: `Set them on the OpenAI-compatible provider in [Pullfrog's provider settings](${getApiUrl()}/console/${params.owner}#billing), or in the workflow's \`env:\` block with the key as an [Actions secret](${secretsUrl(params)}):`,
|
|
198009
|
+
env: [
|
|
198010
|
+
`${OPENAI_COMPATIBLE_BASE_URL_ENV}: https://your-endpoint.example.com/v1`,
|
|
198011
|
+
`${OPENAI_COMPATIBLE_API_KEY_ENV}: \${{ secrets.${OPENAI_COMPATIBLE_API_KEY_ENV} }}`,
|
|
198012
|
+
`${OPENAI_COMPATIBLE_MODEL_ENV}: <model-id>`,
|
|
198013
|
+
`${OPENAI_COMPATIBLE_CONTEXT_ENV}: "128000"`,
|
|
198014
|
+
`${OPENAI_COMPATIBLE_MAX_OUTPUT_ENV}: "16384"`
|
|
198015
|
+
],
|
|
198016
|
+
note: "Use the real context and output limits of the model your endpoint serves: Pullfrog can't discover them, and without them long runs grow until the endpoint refuses them.",
|
|
198017
|
+
guide: "https://docs.pullfrog.com/openai-compatible"
|
|
198018
|
+
});
|
|
197937
198019
|
}
|
|
197938
198020
|
function buildAzureSetupError(params) {
|
|
197939
|
-
|
|
197940
|
-
|
|
197941
|
-
|
|
197942
|
-
|
|
197943
|
-
|
|
197944
|
-
|
|
197945
|
-
|
|
197946
|
-
|
|
197947
|
-
|
|
197948
|
-
|
|
197949
|
-
|
|
197950
|
-
|
|
197951
|
-
|
|
197952
|
-
|
|
197953
|
-
|
|
197954
|
-
set the last two to the real limits of whichever model your deployment serves. Pullfrog can't
|
|
197955
|
-
discover them \u2014 a deployment name carries no catalog metadata \u2014 and without them completions
|
|
197956
|
-
are capped at 32000 tokens (rejected outright by models with a smaller cap) and auto-compaction
|
|
197957
|
-
is disabled, so long runs grow until Azure refuses them.
|
|
197958
|
-
|
|
197959
|
-
for full setup instructions, see https://docs.pullfrog.com/azure`;
|
|
198021
|
+
return buildSetupError({
|
|
198022
|
+
provider: "Azure OpenAI",
|
|
198023
|
+
missing: params.missing,
|
|
198024
|
+
where: `Set them on the Azure OpenAI provider in [Pullfrog's provider settings](${getApiUrl()}/console/${params.owner}#billing), or in the workflow's \`env:\` block with the key as an [Actions secret](${secretsUrl(params)}):`,
|
|
198025
|
+
env: [
|
|
198026
|
+
`${AZURE_RESOURCE_NAME_ENV}: <name>`,
|
|
198027
|
+
`${AZURE_API_KEY_ENV}: \${{ secrets.${AZURE_API_KEY_ENV} }}`,
|
|
198028
|
+
`${AZURE_DEPLOYMENT_ENV}: <deployment-name>`,
|
|
198029
|
+
`${AZURE_CONTEXT_ENV}: "400000"`,
|
|
198030
|
+
`${AZURE_MAX_OUTPUT_ENV}: "128000"`
|
|
198031
|
+
],
|
|
198032
|
+
note: `\`${AZURE_RESOURCE_NAME_ENV}\` is the \`<name>\` in \`https://<name>.openai.azure.com\`, and \`${AZURE_DEPLOYMENT_ENV}\` is the deployment's name, not the model it serves. Use that model's real context and output limits: Pullfrog can't discover them, and without them long runs grow until Azure refuses them.`,
|
|
198033
|
+
guide: "https://docs.pullfrog.com/azure"
|
|
198034
|
+
});
|
|
197960
198035
|
}
|
|
197961
198036
|
function hasEnvVar2(name) {
|
|
197962
198037
|
const value2 = process.env[name];
|
|
@@ -198116,7 +198191,7 @@ function isMalformedKeyError(text) {
|
|
|
198116
198191
|
return /Header '\d+' has invalid value/i.test(text);
|
|
198117
198192
|
}
|
|
198118
198193
|
function isClaudeSubscriptionDisabledError(text) {
|
|
198119
|
-
return /disabled Claude subscription access/i.test(text);
|
|
198194
|
+
return /disabled Claude subscription access/i.test(text) || /OAuth authentication is currently not allowed for this organization/i.test(text);
|
|
198120
198195
|
}
|
|
198121
198196
|
function isKimiPlanTierError(text) {
|
|
198122
198197
|
return /Your current (?:subscription does not have access to|plan supports only)\b/i.test(text);
|
|
@@ -198129,138 +198204,179 @@ var SUBSCRIPTION_CREDENTIALS = {
|
|
|
198129
198204
|
CLAUDE_CODE_OAUTH_TOKEN: {
|
|
198130
198205
|
label: "Claude Pro/Max subscription",
|
|
198131
198206
|
command: "pullfrog auth claude",
|
|
198132
|
-
|
|
198207
|
+
provider: "anthropic"
|
|
198133
198208
|
},
|
|
198134
198209
|
CODEX_AUTH_JSON: {
|
|
198135
198210
|
label: "ChatGPT subscription",
|
|
198136
198211
|
command: "pullfrog auth codex",
|
|
198137
|
-
|
|
198212
|
+
provider: "openai"
|
|
198138
198213
|
},
|
|
198139
198214
|
GROK_AUTH_JSON: {
|
|
198140
198215
|
label: "Grok subscription",
|
|
198141
198216
|
command: "pullfrog auth grok",
|
|
198142
|
-
|
|
198217
|
+
provider: "xai"
|
|
198143
198218
|
}
|
|
198144
198219
|
};
|
|
198220
|
+
function subscriptionFor(model) {
|
|
198221
|
+
const provider2 = model?.split("/")[0];
|
|
198222
|
+
for (const [env2, subscription] of Object.entries(SUBSCRIPTION_CREDENTIALS)) {
|
|
198223
|
+
if (subscription.provider === provider2) return { env: env2, ...subscription };
|
|
198224
|
+
}
|
|
198225
|
+
return void 0;
|
|
198226
|
+
}
|
|
198227
|
+
var SIGN_IN_AGAIN = (command) => `Sign in again by running \`${command}\` in a terminal`;
|
|
198228
|
+
var CLAUDE_ORG_DISABLED = {
|
|
198229
|
+
headline: "Your Claude organization doesn't allow subscription sign-in for Claude Code.",
|
|
198230
|
+
detail: "Ask its admin to allow it in the Claude Console, or connect an Anthropic API key instead."
|
|
198231
|
+
};
|
|
198232
|
+
function providerDisplayName(id) {
|
|
198233
|
+
for (const [key, provider2] of Object.entries(providers2)) {
|
|
198234
|
+
if (key === id) return provider2.displayName;
|
|
198235
|
+
}
|
|
198236
|
+
return void 0;
|
|
198237
|
+
}
|
|
198145
198238
|
function buildRejectedCredentialError(params) {
|
|
198146
|
-
const settingsUrl = `${getApiUrl()}/console/${params.owner}/${params.name}`;
|
|
198147
198239
|
const subscription = SUBSCRIPTION_CREDENTIALS[params.credential];
|
|
198148
|
-
const
|
|
198240
|
+
const details = params.reason ? { summary: "The provider's response", text: params.reason } : void 0;
|
|
198149
198241
|
if (subscription) {
|
|
198150
|
-
|
|
198151
|
-
|
|
198152
|
-
|
|
198153
|
-
|
|
198154
|
-
|
|
198155
|
-
|
|
198156
|
-
|
|
198157
|
-
}
|
|
198158
|
-
const
|
|
198159
|
-
const keysUrl =
|
|
198160
|
-
const
|
|
198161
|
-
|
|
198162
|
-
|
|
198163
|
-
|
|
198164
|
-
|
|
198165
|
-
""
|
|
198166
|
-
|
|
198167
|
-
|
|
198168
|
-
}
|
|
198169
|
-
|
|
198242
|
+
const orgDisabled = params.reason !== void 0 && isClaudeSubscriptionDisabledError(params.reason);
|
|
198243
|
+
return renderFailure({
|
|
198244
|
+
headline: `Your ${subscription.label} ${CREDENTIAL_REJECTED_MARKER}.`,
|
|
198245
|
+
detail: orgDisabled ? `${CLAUDE_ORG_DISABLED.headline} ${CLAUDE_ORG_DISABLED.detail}` : `${SIGN_IN_AGAIN(subscription.command)}, or pick a model you have an API key for.`,
|
|
198246
|
+
details,
|
|
198247
|
+
links: [orgDisabled ? providerLink(params.owner) : pickModelLink(params)]
|
|
198248
|
+
});
|
|
198249
|
+
}
|
|
198250
|
+
const provider2 = credentialProvider(params.credential);
|
|
198251
|
+
const keysUrl = provider2 ? PROVIDER_DASHBOARDS[provider2.id]?.keys : void 0;
|
|
198252
|
+
const update = params.inPullfrogStore ? {
|
|
198253
|
+
label: "Update it in Pullfrog",
|
|
198254
|
+
url: `${getApiUrl()}/console/${params.owner}/${params.name}`
|
|
198255
|
+
} : { label: "Update the Actions secret", url: secretsUrl(params) };
|
|
198256
|
+
return renderFailure({
|
|
198257
|
+
headline: `Your ${provider2 ? `${provider2.displayName} key` : "key"} \`${params.credential}\` ${CREDENTIAL_REJECTED_MARKER}.`,
|
|
198258
|
+
detail: `Issue a new key and update the copy ${params.inPullfrogStore ? "stored in Pullfrog" : "in this repo's GitHub Actions secrets"}.`,
|
|
198259
|
+
details,
|
|
198260
|
+
links: keysUrl ? [{ label: "Issue a new key", url: keysUrl }, update] : [update]
|
|
198261
|
+
});
|
|
198262
|
+
}
|
|
198263
|
+
function credentialProvider(credential) {
|
|
198170
198264
|
for (const [id, provider2] of Object.entries(providers2)) {
|
|
198171
|
-
if (provider2.envVars.includes(credential)) return
|
|
198265
|
+
if (provider2.envVars.includes(credential)) return { id, displayName: provider2.displayName };
|
|
198172
198266
|
}
|
|
198173
198267
|
return void 0;
|
|
198174
198268
|
}
|
|
198175
198269
|
function buildCredentialPoolRefusedError(params) {
|
|
198176
|
-
const
|
|
198177
|
-
|
|
198178
|
-
|
|
198179
|
-
|
|
198180
|
-
|
|
198181
|
-
|
|
198182
|
-
|
|
198183
|
-
|
|
198184
|
-
|
|
198185
|
-
|
|
198270
|
+
const [first = "", second, ...rest] = params.refused.map(
|
|
198271
|
+
(credential) => describeRefusal({ credential, owner: params.owner, name: params.name })
|
|
198272
|
+
);
|
|
198273
|
+
return renderFailure({
|
|
198274
|
+
headline: `${CREDENTIAL_POOL_REFUSED_MARKER}.`,
|
|
198275
|
+
detail: second === void 0 ? first : `Every credential for \`${params.model}\` was turned down:`,
|
|
198276
|
+
list: second === void 0 ? void 0 : [first, second, ...rest],
|
|
198277
|
+
details: {
|
|
198278
|
+
summary: "The providers' responses",
|
|
198279
|
+
text: params.refused.map((credential) => `${credential.name}: ${credential.verdict.detail}`).join("\n")
|
|
198280
|
+
},
|
|
198281
|
+
links: [pickModelLink(params)]
|
|
198282
|
+
});
|
|
198186
198283
|
}
|
|
198187
198284
|
function describeRefusal(params) {
|
|
198188
198285
|
const credential = params.credential;
|
|
198189
198286
|
const verdict = credential.verdict;
|
|
198190
|
-
const workflow = credential.source === "workflow";
|
|
198191
|
-
const head = `\`${credential.name}\` from ${workflow ? "the workflow" : `${credential.source} scope`}: \`${verdict.detail}\` \u2014`;
|
|
198192
198287
|
const subscription = SUBSCRIPTION_CREDENTIALS[credential.name];
|
|
198193
|
-
const dashboard =
|
|
198288
|
+
const dashboard = PROVIDER_DASHBOARDS[credentialProvider(credential.name)?.id ?? ""];
|
|
198289
|
+
const what = subscription ? `Your ${subscription.label}` : `Your \`${credential.name}\``;
|
|
198194
198290
|
if (verdict.status === "exhausted" && subscription) {
|
|
198195
|
-
const
|
|
198196
|
-
return `${
|
|
198291
|
+
const reset = verdict.resetAt ? `, which resets at **${verdict.resetAt.toISOString().slice(0, 16).replace("T", " ")} UTC**` : "";
|
|
198292
|
+
return `${what} has hit its usage limit${reset}. Re-trigger after it resets, or add an API key for this provider, which Pullfrog falls back to automatically.`;
|
|
198293
|
+
}
|
|
198294
|
+
if (verdict.status === "exhausted") {
|
|
198295
|
+
return `${what} is out of credit or quota. ${dashboard ? `[Add credit](${dashboard.billing})` : "Add credit"} with the provider, or raise its quota.`;
|
|
198197
198296
|
}
|
|
198198
|
-
if (verdict.
|
|
198199
|
-
return `${
|
|
198200
|
-
|
|
198201
|
-
|
|
198202
|
-
const
|
|
198203
|
-
const
|
|
198204
|
-
return `${
|
|
198297
|
+
if (subscription && isClaudeSubscriptionDisabledError(verdict.detail)) {
|
|
198298
|
+
return `${CLAUDE_ORG_DISABLED.headline} ${CLAUDE_ORG_DISABLED.detail}`;
|
|
198299
|
+
}
|
|
198300
|
+
if (subscription) return `${what} was rejected. ${SIGN_IN_AGAIN(subscription.command)}.`;
|
|
198301
|
+
const issue3 = dashboard?.keys ? `[Issue a new key](${dashboard.keys})` : "Issue a new key";
|
|
198302
|
+
const where = credential.source === "workflow" ? `in [this repo's GitHub Actions secrets](${secretsUrl(params)})` : `in [Pullfrog](${getApiUrl()}/console/${params.owner}#billing)`;
|
|
198303
|
+
return `${what} was rejected. ${issue3} and update it ${where}.`;
|
|
198205
198304
|
}
|
|
198206
198305
|
function formatApiKeyErrorSummary(params) {
|
|
198207
198306
|
if (params.raw.includes(MISSING_KEY_MARKER)) {
|
|
198208
|
-
if (params.raw.startsWith(`**${MISSING_KEY_MARKER}
|
|
198307
|
+
if (params.raw.startsWith(`**${MISSING_KEY_MARKER}`)) return params.raw;
|
|
198209
198308
|
return buildMissingApiKeyError({ owner: params.owner, name: params.name });
|
|
198210
198309
|
}
|
|
198211
|
-
const
|
|
198212
|
-
const
|
|
198310
|
+
const details = params.output ? { summary: "The provider's response", text: params.output } : void 0;
|
|
198311
|
+
const subscription = subscriptionFor(params.model);
|
|
198213
198312
|
if (isClaudeSessionLimitError(params.raw)) {
|
|
198214
198313
|
const reset = CLAUDE_SESSION_LIMIT_PATTERN.exec(params.raw)?.[1]?.trim();
|
|
198215
|
-
|
|
198216
|
-
|
|
198217
|
-
|
|
198218
|
-
|
|
198219
|
-
|
|
198220
|
-
|
|
198314
|
+
return renderFailure({
|
|
198315
|
+
headline: "Your Claude Pro/Max subscription has hit its usage limit.",
|
|
198316
|
+
detail: `${reset ? `It resets at **${reset}**. ` : ""}Re-trigger after that, or connect an Anthropic API key, which Pullfrog falls back to automatically.`,
|
|
198317
|
+
details,
|
|
198318
|
+
links: [providerLink(params.owner)]
|
|
198319
|
+
});
|
|
198221
198320
|
}
|
|
198222
198321
|
if (isMalformedKeyError(params.raw)) {
|
|
198223
|
-
return
|
|
198224
|
-
"
|
|
198225
|
-
"",
|
|
198226
|
-
|
|
198227
|
-
|
|
198322
|
+
return renderFailure({
|
|
198323
|
+
headline: "Your stored API key can't be sent as an HTTP header.",
|
|
198324
|
+
detail: "It contains a line break or control character, usually from a key pasted across several lines. Re-save it as a single line; the key itself is likely fine.",
|
|
198325
|
+
links: [
|
|
198326
|
+
{
|
|
198327
|
+
label: "Pullfrog provider settings",
|
|
198328
|
+
url: `${getApiUrl()}/console/${params.owner}#billing`
|
|
198329
|
+
},
|
|
198330
|
+
{ label: "GitHub Actions secrets", url: secretsUrl(params) }
|
|
198331
|
+
]
|
|
198332
|
+
});
|
|
198228
198333
|
}
|
|
198229
198334
|
if (isClaudeSubscriptionDisabledError(params.raw)) {
|
|
198230
|
-
return
|
|
198231
|
-
|
|
198232
|
-
|
|
198233
|
-
|
|
198234
|
-
|
|
198335
|
+
return renderFailure({
|
|
198336
|
+
...CLAUDE_ORG_DISABLED,
|
|
198337
|
+
details,
|
|
198338
|
+
links: [providerLink(params.owner)]
|
|
198339
|
+
});
|
|
198235
198340
|
}
|
|
198236
198341
|
if (isKimiPlanTierError(params.raw)) {
|
|
198237
|
-
return
|
|
198238
|
-
"
|
|
198239
|
-
"",
|
|
198240
|
-
|
|
198241
|
-
|
|
198342
|
+
return renderFailure({
|
|
198343
|
+
headline: "Your Kimi membership doesn't include the model this run asked for.",
|
|
198344
|
+
detail: "Kimi K3 needs a Moderato plan or above, and K3's 1M context and Kimi K2 HighSpeed need Allegretto or above. Upgrade the membership, or switch this repo to **Kimi K2**, which every tier includes.",
|
|
198345
|
+
details,
|
|
198346
|
+
links: [
|
|
198347
|
+
{ label: "Kimi plans", url: "https://www.kimi.com/membership/pricing" },
|
|
198348
|
+
pickModelLink(params)
|
|
198349
|
+
]
|
|
198350
|
+
});
|
|
198242
198351
|
}
|
|
198243
198352
|
if (isOAuthCredentialExpiredError(params.raw)) {
|
|
198244
|
-
return
|
|
198245
|
-
|
|
198246
|
-
"",
|
|
198247
|
-
|
|
198248
|
-
|
|
198249
|
-
|
|
198250
|
-
|
|
198251
|
-
if (subscription &&
|
|
198252
|
-
|
|
198253
|
-
|
|
198254
|
-
|
|
198255
|
-
|
|
198256
|
-
|
|
198257
|
-
|
|
198258
|
-
}
|
|
198259
|
-
return
|
|
198260
|
-
|
|
198261
|
-
"",
|
|
198262
|
-
|
|
198263
|
-
|
|
198353
|
+
return renderFailure({
|
|
198354
|
+
headline: subscription ? `Your ${subscription.label}'s sign-in has expired or been revoked.` : "Your provider sign-in has expired or been revoked.",
|
|
198355
|
+
detail: subscription ? `${SIGN_IN_AGAIN(subscription.command)}, then re-trigger.` : "Sign in again with `pullfrog auth claude`, `pullfrog auth codex` or `pullfrog auth grok` in a terminal, whichever this repo uses, then re-trigger.",
|
|
198356
|
+
details,
|
|
198357
|
+
links: [pickModelLink(params)]
|
|
198358
|
+
});
|
|
198359
|
+
}
|
|
198360
|
+
if (subscription && hasEnvVar2(subscription.env) && params.credential !== "api_key") {
|
|
198361
|
+
return renderFailure({
|
|
198362
|
+
headline: `Your ${subscription.label} was rejected during this run.`,
|
|
198363
|
+
detail: `${SIGN_IN_AGAIN(subscription.command)}, then re-trigger.`,
|
|
198364
|
+
details,
|
|
198365
|
+
links: [pickModelLink(params)]
|
|
198366
|
+
});
|
|
198367
|
+
}
|
|
198368
|
+
return renderFailure({
|
|
198369
|
+
headline: `Your ${providerDisplayName(params.model?.split("/")[0]) ?? "model provider"} API key was rejected.`,
|
|
198370
|
+
detail: "Issue a new key with the provider, then update the copy Pullfrog uses, in Pullfrog or in this repo's GitHub Actions secrets.",
|
|
198371
|
+
details,
|
|
198372
|
+
links: [
|
|
198373
|
+
{
|
|
198374
|
+
label: "Pullfrog provider settings",
|
|
198375
|
+
url: `${getApiUrl()}/console/${params.owner}#billing`
|
|
198376
|
+
},
|
|
198377
|
+
{ label: "GitHub Actions secrets", url: secretsUrl(params) }
|
|
198378
|
+
]
|
|
198379
|
+
});
|
|
198264
198380
|
}
|
|
198265
198381
|
|
|
198266
198382
|
// utils/agent.ts
|
|
@@ -198286,7 +198402,7 @@ function resolveSlug(slug2) {
|
|
|
198286
198402
|
const bedrockId = process.env[BEDROCK_MODEL_ID_ENV]?.trim();
|
|
198287
198403
|
if (!bedrockId) {
|
|
198288
198404
|
throw new Error(
|
|
198289
|
-
|
|
198405
|
+
`\`${BEDROCK_MODEL_ID_ENV}\` ${BYOK_SLUG_MARKER} \`${slug2}\`. Set it in the workflow's \`env:\` block to a Bedrock model ID from the AWS console. [Amazon Bedrock setup guide](https://docs.pullfrog.com/bedrock)`
|
|
198290
198406
|
);
|
|
198291
198407
|
}
|
|
198292
198408
|
return bedrockId;
|
|
@@ -198295,7 +198411,7 @@ function resolveSlug(slug2) {
|
|
|
198295
198411
|
const vertexId = process.env[VERTEX_MODEL_ID_ENV]?.trim();
|
|
198296
198412
|
if (!vertexId) {
|
|
198297
198413
|
throw new Error(
|
|
198298
|
-
|
|
198414
|
+
`\`${VERTEX_MODEL_ID_ENV}\` ${BYOK_SLUG_MARKER} \`${slug2}\`. Set it in the workflow's \`env:\` block to a Vertex AI model ID from Model Garden. [Google Vertex AI setup guide](https://docs.pullfrog.com/vertex)`
|
|
198299
198415
|
);
|
|
198300
198416
|
}
|
|
198301
198417
|
return vertexId;
|
|
@@ -198304,7 +198420,7 @@ function resolveSlug(slug2) {
|
|
|
198304
198420
|
const deployment = process.env[AZURE_DEPLOYMENT_ENV]?.trim();
|
|
198305
198421
|
if (!deployment) {
|
|
198306
198422
|
throw new Error(
|
|
198307
|
-
|
|
198423
|
+
`\`${AZURE_DEPLOYMENT_ENV}\` ${BYOK_SLUG_MARKER} \`${slug2}\`. Set it to your deployment's name, not the model it serves, on the Azure OpenAI provider in Pullfrog or in the workflow's \`env:\` block. [Azure OpenAI setup guide](https://docs.pullfrog.com/azure)`
|
|
198308
198424
|
);
|
|
198309
198425
|
}
|
|
198310
198426
|
return `${AZURE_PROVIDER}/${deployment}`;
|
|
@@ -198313,7 +198429,7 @@ function resolveSlug(slug2) {
|
|
|
198313
198429
|
const modelId = process.env[OPENAI_COMPATIBLE_MODEL_ENV]?.trim();
|
|
198314
198430
|
if (!modelId) {
|
|
198315
198431
|
throw new Error(
|
|
198316
|
-
|
|
198432
|
+
`\`${OPENAI_COMPATIBLE_MODEL_ENV}\` ${BYOK_SLUG_MARKER} \`${slug2}\`. Set it to the model ID your endpoint serves, on the OpenAI-compatible provider in Pullfrog or in the workflow's \`env:\` block. [OpenAI-compatible setup guide](https://docs.pullfrog.com/openai-compatible)`
|
|
198317
198433
|
);
|
|
198318
198434
|
}
|
|
198319
198435
|
return `${OPENAI_COMPATIBLE_PROVIDER}/${modelId}`;
|
|
@@ -198385,9 +198501,12 @@ var BillingError = class extends Error {
|
|
|
198385
198501
|
}
|
|
198386
198502
|
};
|
|
198387
198503
|
var TransientError = class extends Error {
|
|
198388
|
-
|
|
198504
|
+
/** the customer-facing follow-up to `message`, which is the headline. */
|
|
198505
|
+
detail;
|
|
198506
|
+
constructor(message2, detail = "This is on our side, and nothing was billed. The next run should succeed; if it keeps failing, check [status.pullfrog.com](https://status.pullfrog.com).") {
|
|
198389
198507
|
super(message2);
|
|
198390
198508
|
this.name = "TransientError";
|
|
198509
|
+
this.detail = detail;
|
|
198391
198510
|
}
|
|
198392
198511
|
};
|
|
198393
198512
|
function billingConsoleUrl(owner) {
|
|
@@ -198422,41 +198541,34 @@ function formatCommercialGateSummary(params) {
|
|
|
198422
198541
|
});
|
|
198423
198542
|
}
|
|
198424
198543
|
function formatBillingErrorSummary(error49, owner) {
|
|
198544
|
+
const billing = { label: "Add credit", url: billingConsoleUrl(owner) };
|
|
198425
198545
|
if (error49.code === "router_requires_card") {
|
|
198426
|
-
return
|
|
198427
|
-
"
|
|
198428
|
-
"",
|
|
198429
|
-
|
|
198430
|
-
|
|
198431
|
-
`[Add a card to top up \u2192](${billingConsoleUrl(owner)}) \xB7 [Bring your own key \u2192](${billingConsoleUrl(owner)})`
|
|
198432
|
-
].join("\n");
|
|
198546
|
+
return renderFailure({
|
|
198547
|
+
headline: "Your Pullfrog Router balance is empty.",
|
|
198548
|
+
detail: "Add a card to top it up, or connect your own provider key. Router usage is billed at provider cost with no markup.",
|
|
198549
|
+
links: [{ label: "Billing and providers", url: billingConsoleUrl(owner) }]
|
|
198550
|
+
});
|
|
198433
198551
|
}
|
|
198434
198552
|
if (error49.code === "router_balance_exhausted") {
|
|
198435
|
-
return
|
|
198436
|
-
"
|
|
198437
|
-
"",
|
|
198438
|
-
|
|
198439
|
-
|
|
198440
|
-
`[Top up balance \u2192](${billingConsoleUrl(owner)}) \xB7 [Enable auto-reload \u2192](${billingConsoleUrl(owner)})`
|
|
198441
|
-
].join("\n");
|
|
198553
|
+
return renderFailure({
|
|
198554
|
+
headline: "Your Pullfrog Router balance is used up.",
|
|
198555
|
+
detail: "Auto-reload is off, so runs paused once the balance passed its overdraft buffer. Add credit or turn on auto-reload.",
|
|
198556
|
+
links: [billing]
|
|
198557
|
+
});
|
|
198442
198558
|
}
|
|
198443
198559
|
if (error49.code === "router_keylimit_exhausted") {
|
|
198444
|
-
return
|
|
198445
|
-
|
|
198446
|
-
"",
|
|
198447
|
-
|
|
198448
|
-
|
|
198449
|
-
`[Top up balance \u2192](${billingConsoleUrl(owner)}) \xB7 [Enable auto-reload \u2192](${billingConsoleUrl(owner)})`
|
|
198450
|
-
].join("\n");
|
|
198560
|
+
return renderFailure({
|
|
198561
|
+
headline: "Your Pullfrog Router balance ran out partway through this run.",
|
|
198562
|
+
detail: "Add credit or turn on auto-reload to keep runs going.",
|
|
198563
|
+
links: [billing]
|
|
198564
|
+
});
|
|
198451
198565
|
}
|
|
198452
198566
|
if (error49.code === "router_monthly_limit") {
|
|
198453
|
-
return
|
|
198454
|
-
"
|
|
198455
|
-
"",
|
|
198456
|
-
|
|
198457
|
-
|
|
198458
|
-
`[Top up balance \u2192](${billingConsoleUrl(owner)}) \xB7 [Adjust limit \u2192](${billingConsoleUrl(owner)})`
|
|
198459
|
-
].join("\n");
|
|
198567
|
+
return renderFailure({
|
|
198568
|
+
headline: "Your Pullfrog Router balance is empty, and the monthly spend limit blocks the next auto-reload.",
|
|
198569
|
+
detail: "Add credit by hand, ask an org owner to raise the limit, or wait for it to reset at 00:00 UTC on the 1st.",
|
|
198570
|
+
links: [billing]
|
|
198571
|
+
});
|
|
198460
198572
|
}
|
|
198461
198573
|
if (error49.code === "commercial_plan_required") {
|
|
198462
198574
|
return formatCommercialGateSummary({
|
|
@@ -198478,39 +198590,27 @@ function formatBillingErrorSummary(error49, owner) {
|
|
|
198478
198590
|
}
|
|
198479
198591
|
if (error49.needsReauthentication) {
|
|
198480
198592
|
const code = error49.declineCode ?? "authentication_required";
|
|
198481
|
-
return
|
|
198482
|
-
|
|
198483
|
-
"",
|
|
198484
|
-
|
|
198485
|
-
|
|
198486
|
-
`[Top up balance \u2192](${billingConsoleUrl(owner)})`
|
|
198487
|
-
].join("\n");
|
|
198593
|
+
return renderFailure({
|
|
198594
|
+
headline: `Your card issuer requires 3D Secure on every charge (\`${code}\`).`,
|
|
198595
|
+
detail: "Pullfrog can't complete a 3D Secure check from inside a workflow. Add credit once by hand, and later runs draw on that balance without another check.",
|
|
198596
|
+
links: [billing]
|
|
198597
|
+
});
|
|
198488
198598
|
}
|
|
198489
198599
|
if (error49.declineCode) {
|
|
198490
|
-
return
|
|
198491
|
-
|
|
198492
|
-
"",
|
|
198493
|
-
"Update
|
|
198494
|
-
|
|
198495
|
-
`[Update payment method \u2192](${billingConsoleUrl(owner)})`
|
|
198496
|
-
].join("\n");
|
|
198600
|
+
return renderFailure({
|
|
198601
|
+
headline: `Your card was declined (\`${error49.declineCode}\`).`,
|
|
198602
|
+
detail: "Update your payment method, and Pullfrog will retry on the next run.",
|
|
198603
|
+
links: [{ label: "Update payment method", url: billingConsoleUrl(owner) }]
|
|
198604
|
+
});
|
|
198497
198605
|
}
|
|
198498
|
-
return
|
|
198499
|
-
"
|
|
198500
|
-
"",
|
|
198501
|
-
|
|
198502
|
-
|
|
198503
|
-
`[Manage billing \u2192](${billingConsoleUrl(owner)})`
|
|
198504
|
-
].join("\n");
|
|
198606
|
+
return renderFailure({
|
|
198607
|
+
headline: "Your Pullfrog balance is empty.",
|
|
198608
|
+
detail: "Add credit or turn on auto-reload to keep runs going.",
|
|
198609
|
+
links: [billing]
|
|
198610
|
+
});
|
|
198505
198611
|
}
|
|
198506
|
-
function formatTransientErrorSummary(error49
|
|
198507
|
-
return
|
|
198508
|
-
"**Pullfrog billing is temporarily unavailable.**",
|
|
198509
|
-
"",
|
|
198510
|
-
error49.message,
|
|
198511
|
-
"",
|
|
198512
|
-
`Usually transient; the next dispatch should succeed. If it persists, check [status.pullfrog.com](https://status.pullfrog.com) or [your console](${billingConsoleUrl(owner)}).`
|
|
198513
|
-
].join("\n");
|
|
198612
|
+
function formatTransientErrorSummary(error49) {
|
|
198613
|
+
return renderFailure({ headline: error49.message, detail: error49.detail });
|
|
198514
198614
|
}
|
|
198515
198615
|
|
|
198516
198616
|
// utils/credentialCheck.ts
|
|
@@ -198840,10 +198940,7 @@ var select2 = mutation({
|
|
|
198840
198940
|
signal: ctx.signal
|
|
198841
198941
|
});
|
|
198842
198942
|
if (response.status === 404) return null;
|
|
198843
|
-
if (!response.ok)
|
|
198844
|
-
throw new Error("could not load a configured credential; no fallback was charged", {
|
|
198845
|
-
cause: response.status
|
|
198846
|
-
});
|
|
198943
|
+
if (!response.ok) throw new Error(buildSecretsUnavailableError(), { cause: response.status });
|
|
198847
198944
|
return selectedCredentialSchema.parse(await response.json());
|
|
198848
198945
|
},
|
|
198849
198946
|
retry: (error49, attempt) => error49 instanceof Error && typeof error49.cause === "number" && error49.cause >= 400 && error49.cause < 500 ? -1 : delay2([250, 1e3], attempt),
|
|
@@ -199657,26 +199754,16 @@ function ossAllowedLabels() {
|
|
|
199657
199754
|
return ossFundedModelNames().map((name) => `\`${name}\``).join(", ");
|
|
199658
199755
|
}
|
|
199659
199756
|
function buildModelAccessError(input) {
|
|
199660
|
-
const
|
|
199661
|
-
|
|
199662
|
-
|
|
199663
|
-
|
|
199664
|
-
const branch = {
|
|
199665
|
-
oss: {
|
|
199666
|
-
why: `This repo runs on Pullfrog's OSS subsidy, which funds a set of pre-approved models: ${ossAllowedLabels()}. Pick one of those, or add your own provider key to run a different one.`,
|
|
199667
|
-
cta: `[Configure model \u2192](${settingsUrl}) \xB7 [Add a provider key \u2192](${secretsUrl}) \xB7 [Setup docs \u2192](${docsUrl})`
|
|
199668
|
-
},
|
|
199669
|
-
byok_no_key: {
|
|
199670
|
-
why: `No provider key for \`${input.model}\` is present in this run's environment. Add the matching provider key as a GitHub Actions secret, or pick a model you already have a key for.`,
|
|
199671
|
-
cta: `[Open repo secrets \u2192](${secretsUrl}) \xB7 [Configure model \u2192](${settingsUrl}) \xB7 [Setup docs \u2192](${docsUrl})`
|
|
199672
|
-
},
|
|
199673
|
-
router: {
|
|
199674
|
-
why: "Pullfrog Router can't serve this model. Pick a Router-supported model, or add your own provider key to run it directly.",
|
|
199675
|
-
cta: `[Configure model \u2192](${settingsUrl}) \xB7 [Add a provider key \u2192](${secretsUrl})`
|
|
199676
|
-
}
|
|
199757
|
+
const why = {
|
|
199758
|
+
oss: `This repo runs on Pullfrog's OSS subsidy, which funds a set of pre-approved models: ${ossAllowedLabels()}. Pick one of those, or connect your own provider to run a different one.`,
|
|
199759
|
+
byok_no_key: `This run has no API key for \`${input.model}\`. Connect one, or pick a model you already have a key for.`,
|
|
199760
|
+
router: "Pullfrog Router can't serve this model. Pick a Router-supported model, or connect your own provider to run it directly."
|
|
199677
199761
|
};
|
|
199678
|
-
|
|
199679
|
-
|
|
199762
|
+
return renderFailure({
|
|
199763
|
+
headline: `The ${MODEL_ACCESS_MARKER}: \`${input.model}\`.`,
|
|
199764
|
+
detail: why[input.reason],
|
|
199765
|
+
links: [pickModelLink(input), providerLink(input.owner)]
|
|
199766
|
+
});
|
|
199680
199767
|
}
|
|
199681
199768
|
|
|
199682
199769
|
// utils/overrides.ts
|
|
@@ -200233,9 +200320,6 @@ async function revokeGitHubInstallationToken(token) {
|
|
|
200233
200320
|
|
|
200234
200321
|
// utils/errorReport.ts
|
|
200235
200322
|
async function reportErrorToComment(ctx) {
|
|
200236
|
-
const formattedError = ctx.title ? `${ctx.title}
|
|
200237
|
-
|
|
200238
|
-
${ctx.error}` : ctx.error;
|
|
200239
200323
|
const repoContext = parseRepoContext();
|
|
200240
200324
|
const octokit = createOctokit(getGitHubInstallationToken(), getMcpTokenRefresh());
|
|
200241
200325
|
const runId = process.env.GITHUB_RUN_ID ? Number.parseInt(process.env.GITHUB_RUN_ID, 10) : void 0;
|
|
@@ -200254,9 +200338,10 @@ ${ctx.error}` : ctx.error;
|
|
|
200254
200338
|
clamped: ctx.toolState.modelClamped,
|
|
200255
200339
|
unselectedProxyDefault: ctx.toolState.unselectedProxyDefault,
|
|
200256
200340
|
oss: ctx.toolState.oss,
|
|
200257
|
-
owner: repoContext.owner
|
|
200341
|
+
owner: repoContext.owner,
|
|
200342
|
+
failed: true
|
|
200258
200343
|
});
|
|
200259
|
-
const body = `${
|
|
200344
|
+
const body = `${redactSecrets(ctx.error)}${footer}`;
|
|
200260
200345
|
const comment = ctx.toolState.progressComment;
|
|
200261
200346
|
if (comment) {
|
|
200262
200347
|
await updateProgressComment(
|
|
@@ -200296,8 +200381,9 @@ async function mintProxyKey(ctx) {
|
|
|
200296
200381
|
const r2 = await apiFetch({ path: "/api/proxy-token", method: "POST", headers });
|
|
200297
200382
|
if (r2.status >= 500) {
|
|
200298
200383
|
const body = await r2.json().catch(() => null);
|
|
200384
|
+
log2.warning(`proxy key mint: ${r2.status} ${body?.error ?? ""}`);
|
|
200299
200385
|
throw new TransientError(
|
|
200300
|
-
|
|
200386
|
+
"Pullfrog's billing service is temporarily unavailable, so this run didn't start."
|
|
200301
200387
|
);
|
|
200302
200388
|
}
|
|
200303
200389
|
return r2;
|
|
@@ -200315,7 +200401,8 @@ async function mintProxyKey(ctx) {
|
|
|
200315
200401
|
}
|
|
200316
200402
|
if (response.status === 404) {
|
|
200317
200403
|
throw new TransientError(
|
|
200318
|
-
"Pullfrog couldn't match this repository to its account
|
|
200404
|
+
"Pullfrog couldn't match this repository to its account.",
|
|
200405
|
+
"It may have just been renamed or transferred, and the link refreshes on the next run. If it keeps failing, reinstall the GitHub App on the repository's current owner."
|
|
200319
200406
|
);
|
|
200320
200407
|
}
|
|
200321
200408
|
if (!response.ok) {
|
|
@@ -200424,7 +200511,7 @@ async function runProxyResolution(ctx) {
|
|
|
200424
200511
|
throw error49;
|
|
200425
200512
|
}
|
|
200426
200513
|
if (error49 instanceof TransientError) {
|
|
200427
|
-
const summary3 = formatTransientErrorSummary(error49
|
|
200514
|
+
const summary3 = formatTransientErrorSummary(error49);
|
|
200428
200515
|
await writeSummary(summary3).catch(() => {
|
|
200429
200516
|
});
|
|
200430
200517
|
await reportErrorToComment({
|
|
@@ -200449,13 +200536,14 @@ async function resolveTrialFallback(ctx) {
|
|
|
200449
200536
|
if (!key) return false;
|
|
200450
200537
|
process.env.OPENROUTER_API_KEY = key;
|
|
200451
200538
|
maskSecret(key);
|
|
200452
|
-
ctx.payload.proxyModel =
|
|
200453
|
-
ctx.toolState.model =
|
|
200539
|
+
ctx.payload.proxyModel = TRIAL_PROXY_MODEL;
|
|
200540
|
+
ctx.toolState.model = TRIAL_PROXY_MODEL;
|
|
200454
200541
|
ctx.toolState.modelClamped = {
|
|
200455
|
-
from: ctx.configuredModel ??
|
|
200456
|
-
reason: "trial"
|
|
200542
|
+
from: ctx.configuredModel ?? TRIAL_PROXY_MODEL,
|
|
200543
|
+
reason: "trial",
|
|
200544
|
+
creditsLeftUsd: ctx.creditsLeftUsd
|
|
200457
200545
|
};
|
|
200458
|
-
log2.info(`\xBB proxy: trial \u2192 ${
|
|
200546
|
+
log2.info(`\xBB proxy: trial \u2192 ${TRIAL_PROXY_MODEL}`);
|
|
200459
200547
|
return true;
|
|
200460
200548
|
}
|
|
200461
200549
|
|
|
@@ -200568,14 +200656,27 @@ ${collapsible}` : output;
|
|
|
200568
200656
|
return { success: true, output: ctx.result.output || "" };
|
|
200569
200657
|
} catch (writeError) {
|
|
200570
200658
|
const error50 = `failed to deliver agent result: ${getErrorMessage2(writeError)}`;
|
|
200571
|
-
await reportErrorToComment({
|
|
200659
|
+
await reportErrorToComment({
|
|
200660
|
+
toolState,
|
|
200661
|
+
error: renderFailure({
|
|
200662
|
+
headline: "Pullfrog couldn't post its answer to GitHub.",
|
|
200663
|
+
detail: "GitHub refused the write; its error is below. Re-trigger to try again.",
|
|
200664
|
+
error: getErrorMessage2(writeError)
|
|
200665
|
+
})
|
|
200666
|
+
}).catch(() => {
|
|
200572
200667
|
});
|
|
200573
200668
|
return { success: false, error: error50, output: ctx.result.output || "" };
|
|
200574
200669
|
}
|
|
200575
200670
|
}
|
|
200576
200671
|
const error49 = ctx.result.error || "agent completed without reporting progress";
|
|
200577
200672
|
try {
|
|
200578
|
-
await reportErrorToComment({
|
|
200673
|
+
await reportErrorToComment({
|
|
200674
|
+
toolState,
|
|
200675
|
+
error: renderFailure({
|
|
200676
|
+
headline: "The agent finished without posting anything.",
|
|
200677
|
+
detail: "Re-trigger to try again."
|
|
200678
|
+
})
|
|
200679
|
+
});
|
|
200579
200680
|
} catch {
|
|
200580
200681
|
}
|
|
200581
200682
|
return { success: false, error: error49, output: ctx.result.output || "" };
|
|
@@ -200636,63 +200737,57 @@ async function resolveRunContextData(params) {
|
|
|
200636
200737
|
// outcome as the server-side failure: the run never sees stored secrets.
|
|
200637
200738
|
secretsUnavailable: runContext.secretsUnavailable || !!process.env.ACTIONS_ID_TOKEN_REQUEST_URL && oidcToken === void 0,
|
|
200638
200739
|
routerUnfunded: runContext.routerUnfunded,
|
|
200639
|
-
trialFallback: runContext.trialFallback
|
|
200740
|
+
trialFallback: runContext.trialFallback,
|
|
200741
|
+
trialCreditsLeftUsd: runContext.trialCreditsLeftUsd
|
|
200640
200742
|
};
|
|
200641
200743
|
}
|
|
200642
200744
|
|
|
200643
200745
|
// utils/agentHangReport.ts
|
|
200644
200746
|
var MAX_STDERR_BYTES = 3e3;
|
|
200645
200747
|
function formatAgentHangBody(input) {
|
|
200646
|
-
|
|
200647
|
-
if (
|
|
200648
|
-
|
|
200649
|
-
}
|
|
200650
|
-
const
|
|
200651
|
-
|
|
200652
|
-
|
|
200653
|
-
|
|
200654
|
-
|
|
200655
|
-
|
|
200656
|
-
|
|
200657
|
-
|
|
200658
|
-
|
|
200659
|
-
const tail = renderStderrTail(input.diagnostic.recentStderr);
|
|
200660
|
-
if (tail) {
|
|
200661
|
-
const fence = pickFence(tail);
|
|
200662
|
-
parts.push(
|
|
200663
|
-
"",
|
|
200664
|
-
"<details><summary>Recent agent stderr</summary>",
|
|
200665
|
-
"",
|
|
200666
|
-
fence,
|
|
200667
|
-
tail,
|
|
200668
|
-
fence,
|
|
200669
|
-
"",
|
|
200670
|
-
"</details>"
|
|
200671
|
-
);
|
|
200748
|
+
const diagnostic = input.diagnostic;
|
|
200749
|
+
if (!diagnostic) return null;
|
|
200750
|
+
const output = renderStderrTail(diagnostic.recentStderr);
|
|
200751
|
+
const details = output ? { summary: "Last agent output", text: output } : void 0;
|
|
200752
|
+
const idle = formatIdle(diagnostic.idleSec ?? parseIdleSec(input.errorMessage));
|
|
200753
|
+
if (diagnostic.lastProviderError === PROVIDER_BILLING_EXHAUSTED_LABEL) {
|
|
200754
|
+
const billingUrl = extractBillingUrl(diagnostic.recentStderr);
|
|
200755
|
+
return renderFailure({
|
|
200756
|
+
headline: "Your model provider's account is out of credit.",
|
|
200757
|
+
detail: "The provider kept refusing the request, so Pullfrog stopped the run. Top up the account, then re-trigger.",
|
|
200758
|
+
details,
|
|
200759
|
+
links: billingUrl ? [{ label: "Top up", url: billingUrl }] : []
|
|
200760
|
+
});
|
|
200672
200761
|
}
|
|
200673
|
-
|
|
200674
|
-
|
|
200675
|
-
|
|
200676
|
-
|
|
200677
|
-
|
|
200678
|
-
|
|
200679
|
-
|
|
200762
|
+
if (diagnostic.lastProviderError) {
|
|
200763
|
+
return renderFailure({
|
|
200764
|
+
headline: "The model provider kept returning errors, so Pullfrog stopped this run.",
|
|
200765
|
+
detail: `The last one was \`${diagnostic.lastProviderError}\`. Re-trigger to try again.`,
|
|
200766
|
+
details
|
|
200767
|
+
});
|
|
200768
|
+
}
|
|
200769
|
+
if (!diagnostic.sawModelOutput) {
|
|
200770
|
+
return renderFailure({
|
|
200771
|
+
headline: `The model never responded, so Pullfrog stopped this run${idle ? ` after ${idle}` : ""}.`,
|
|
200772
|
+
detail: "The request went out but nothing came back. This is usually temporary, so re-trigger to try again.",
|
|
200773
|
+
details
|
|
200774
|
+
});
|
|
200680
200775
|
}
|
|
200681
|
-
return
|
|
200776
|
+
return renderFailure({
|
|
200777
|
+
headline: `The agent went quiet${idle ? ` for ${idle}` : ""}, so Pullfrog stopped this run.`,
|
|
200778
|
+
detail: "Re-trigger to try again.",
|
|
200779
|
+
details
|
|
200780
|
+
});
|
|
200781
|
+
}
|
|
200782
|
+
function formatIdle(seconds) {
|
|
200783
|
+
if (seconds === void 0) return void 0;
|
|
200784
|
+
if (seconds < 90) return `${seconds} seconds`;
|
|
200785
|
+
return `${Math.round(seconds / 60)} minutes`;
|
|
200682
200786
|
}
|
|
200683
200787
|
function parseIdleSec(message2) {
|
|
200684
200788
|
const match3 = /no output for (\d+)s/.exec(message2);
|
|
200685
200789
|
return match3 ? Number(match3[1]) : void 0;
|
|
200686
200790
|
}
|
|
200687
|
-
function formatEventsPart(diagnostic) {
|
|
200688
|
-
if (diagnostic.lastProviderError) {
|
|
200689
|
-
return `${diagnostic.eventCount} events were processed before the failure.`;
|
|
200690
|
-
}
|
|
200691
|
-
if (!diagnostic.sawModelOutput) {
|
|
200692
|
-
return "The model produced no output at all before the stall \u2014 the request was sent but nothing came back. This is usually transient; re-running often succeeds.";
|
|
200693
|
-
}
|
|
200694
|
-
return `${diagnostic.eventCount} events were processed before the failure.`;
|
|
200695
|
-
}
|
|
200696
200791
|
function renderStderrTail(lines) {
|
|
200697
200792
|
if (lines.length === 0) return "";
|
|
200698
200793
|
const joined = lines.join("\n");
|
|
@@ -200700,13 +200795,6 @@ function renderStderrTail(lines) {
|
|
|
200700
200795
|
return `... (older lines truncated)
|
|
200701
200796
|
${joined.slice(-MAX_STDERR_BYTES)}`;
|
|
200702
200797
|
}
|
|
200703
|
-
function pickFence(content) {
|
|
200704
|
-
let max = 0;
|
|
200705
|
-
for (const match3 of content.matchAll(/`+/g)) {
|
|
200706
|
-
if (match3[0].length > max) max = match3[0].length;
|
|
200707
|
-
}
|
|
200708
|
-
return "`".repeat(Math.max(3, max + 1));
|
|
200709
|
-
}
|
|
200710
200798
|
function extractBillingUrl(lines) {
|
|
200711
200799
|
const urlPattern = /https:\/\/(?:opencode\.ai\/[^\s"]*billing[^\s"]*|console\.anthropic\.com[^\s"]*|console\.cloud\.google\.com[^\s"]*billing[^\s"]*)/i;
|
|
200712
200800
|
for (let i2 = lines.length - 1; i2 >= 0; i2--) {
|
|
@@ -200715,28 +200803,6 @@ function extractBillingUrl(lines) {
|
|
|
200715
200803
|
}
|
|
200716
200804
|
return void 0;
|
|
200717
200805
|
}
|
|
200718
|
-
function formatBillingExhaustedBody(diagnostic) {
|
|
200719
|
-
const headline = `**${diagnostic.label} stopped** \u2014 your model provider returned a billing-exhausted response.`;
|
|
200720
|
-
const billingUrl = extractBillingUrl(diagnostic.recentStderr);
|
|
200721
|
-
const cta = billingUrl ? `Top up your provider balance, then re-run: [${billingUrl}](${billingUrl})` : "Top up your model-provider balance (or rotate to a key with remaining credits) and re-run.";
|
|
200722
|
-
const explanation = "The agent kept retrying the request because the provider marked the failure as transient. Pullfrog's activity-timeout watchdog ended the run after no further events were emitted.";
|
|
200723
|
-
const parts = [headline, "", explanation, "", cta];
|
|
200724
|
-
const tail = renderStderrTail(diagnostic.recentStderr);
|
|
200725
|
-
if (tail) {
|
|
200726
|
-
const fence = pickFence(tail);
|
|
200727
|
-
parts.push(
|
|
200728
|
-
"",
|
|
200729
|
-
"<details><summary>Recent agent stderr</summary>",
|
|
200730
|
-
"",
|
|
200731
|
-
fence,
|
|
200732
|
-
tail,
|
|
200733
|
-
fence,
|
|
200734
|
-
"",
|
|
200735
|
-
"</details>"
|
|
200736
|
-
);
|
|
200737
|
-
}
|
|
200738
|
-
return parts.join("\n");
|
|
200739
|
-
}
|
|
200740
200806
|
|
|
200741
200807
|
// utils/runErrorRenderer.ts
|
|
200742
200808
|
function isProviderModelNotFoundError(message2) {
|
|
@@ -200751,84 +200817,9 @@ function isNoProviderAvailableError(message2) {
|
|
|
200751
200817
|
function isZenFreeTierGateError(message2) {
|
|
200752
200818
|
return /free tier can only be used from within OpenCode/i.test(message2);
|
|
200753
200819
|
}
|
|
200754
|
-
|
|
200755
|
-
|
|
200756
|
-
|
|
200757
|
-
"**Zen's free tier refused a request this run made.** OpenCode Zen currently rejects free-model requests whose tool list is missing one of the built-in tools it expects ([upstream issue](https://github.com/anomalyco/opencode/issues/49587)). Pullfrog sends all of them, but opencode's mid-run context-compaction request carries no tools at all, and a repo-level `opencode.json` that denies `bash`, `glob`, `grep` or `read` strips that tool from every request.",
|
|
200758
|
-
"",
|
|
200759
|
-
'Pick a paid model for this repo, drop any `"deny"` on `bash`, `glob`, `grep` or `read` from the repo\'s `opencode.json` if it sets one, or split the PR so the run stays under the compaction threshold and re-trigger.',
|
|
200760
|
-
"",
|
|
200761
|
-
`[Model settings \u2192](${settingsUrl}) \xB7 [Ask in Discord \u2192](https://discord.gg/8y96raFg8e)`,
|
|
200762
|
-
"",
|
|
200763
|
-
`\`\`\`
|
|
200764
|
-
${input.raw}
|
|
200765
|
-
\`\`\``
|
|
200766
|
-
].join("\n");
|
|
200767
|
-
}
|
|
200768
|
-
function formatNoProviderAvailableSummary(input) {
|
|
200769
|
-
const settingsUrl = `${getApiUrl()}/console/${input.owner}/${input.name}`;
|
|
200770
|
-
return [
|
|
200771
|
-
"**The provider refused to serve this model.** It's listed in the catalog, but the account this run used has no access to it \u2014 so the request was accepted and then declined.",
|
|
200772
|
-
"",
|
|
200773
|
-
"This often happens when Pullfrog auto-selects a model your account can't reach. Pin an explicit model for this repo, or add credentials for the provider that was picked.",
|
|
200774
|
-
"",
|
|
200775
|
-
`[Model settings \u2192](${settingsUrl}) \xB7 [Setup docs \u2192](https://docs.pullfrog.com/keys) \xB7 [Ask in Discord \u2192](https://discord.gg/8y96raFg8e)`,
|
|
200776
|
-
"",
|
|
200777
|
-
`\`\`\`
|
|
200778
|
-
${input.raw}
|
|
200779
|
-
\`\`\``
|
|
200780
|
-
].join("\n");
|
|
200781
|
-
}
|
|
200782
|
-
function isRunTimeoutError(message2) {
|
|
200783
|
-
return message2.startsWith("agent run timed out after");
|
|
200784
|
-
}
|
|
200785
|
-
function formatRunTimeoutSummary(input) {
|
|
200786
|
-
const settingsUrl = `${getApiUrl()}/console/${input.owner}/${input.name}`;
|
|
200787
|
-
return [
|
|
200788
|
-
"**This run hit its time limit while still working, so it stopped before finishing.**",
|
|
200789
|
-
"",
|
|
200790
|
-
"Give runs longer with `--timeout=2h` (or `--notimeout`) in this repo's instructions \u2014 or raise the workflow's `timeout` input if it sets one, since that input wins. A smaller PR or a lower effort also finishes sooner.",
|
|
200791
|
-
"",
|
|
200792
|
-
`[Repo settings \u2192](${settingsUrl}) \xB7 [Flags \u2192](https://docs.pullfrog.com/flags)`,
|
|
200793
|
-
"",
|
|
200794
|
-
`\`\`\`
|
|
200795
|
-
${input.raw}
|
|
200796
|
-
\`\`\``
|
|
200797
|
-
].join("\n");
|
|
200798
|
-
}
|
|
200799
|
-
function formatContextOverflowSummary(input) {
|
|
200800
|
-
const settingsUrl = `${getApiUrl()}/console/${input.owner}/${input.name}`;
|
|
200801
|
-
return [
|
|
200802
|
-
"**This run exceeded the model's context window.** Pullfrog read more than the model could hold, so it stopped before finishing.",
|
|
200803
|
-
"",
|
|
200804
|
-
`Pick a model with a larger context window, or split this PR into smaller ones and re-trigger.`,
|
|
200805
|
-
"",
|
|
200806
|
-
`[Model settings \u2192](${settingsUrl}) \xB7 [Ask in Discord \u2192](https://discord.gg/8y96raFg8e)`,
|
|
200807
|
-
"",
|
|
200808
|
-
`\`\`\`
|
|
200809
|
-
${input.raw}
|
|
200810
|
-
\`\`\``
|
|
200811
|
-
].join("\n");
|
|
200812
|
-
}
|
|
200813
|
-
function formatGenericFailure(errorMessage) {
|
|
200814
|
-
return [
|
|
200815
|
-
"Pullfrog ran into an unexpected error and couldn't finish this run. The underlying error is below \u2014 re-trigger Pullfrog to try again, and reach out to support if it keeps happening.",
|
|
200816
|
-
"",
|
|
200817
|
-
"```",
|
|
200818
|
-
errorMessage,
|
|
200819
|
-
"```"
|
|
200820
|
-
].join("\n");
|
|
200821
|
-
}
|
|
200822
|
-
function formatMinimalFailureComment(repo) {
|
|
200823
|
-
const runId = process.env.GITHUB_RUN_ID;
|
|
200824
|
-
if (!runId) return "**Run failed.**";
|
|
200825
|
-
const server = process.env.GITHUB_SERVER_URL ?? "https://github.com";
|
|
200826
|
-
const url4 = `${server}/${repo.owner}/${repo.name}/actions/runs/${runId}`;
|
|
200827
|
-
return `**Run failed.** [View the logs \u2192](${url4})`;
|
|
200828
|
-
}
|
|
200829
|
-
var PROVIDER_BILLING_URLS = Object.fromEntries(
|
|
200830
|
-
Object.entries(PROVIDER_DASHBOARDS).map(([id, dashboard]) => [id, dashboard.billing])
|
|
200831
|
-
);
|
|
200820
|
+
var RUN_TIMEOUT_PATTERN = /^agent run timed out after (\S+)/;
|
|
200821
|
+
var STOP_SCRIPT_PATTERN = /^stop hook failed \(exit code (-?\d+)\): ([\s\S]*)$/;
|
|
200822
|
+
var UNSUBMITTED_REVIEW_PATTERN = /^\w+ mode finished without calling /;
|
|
200832
200823
|
function detectProviderId(message2) {
|
|
200833
200824
|
const harnessId = extractProviderId(message2);
|
|
200834
200825
|
if (harnessId) return harnessId;
|
|
@@ -200840,292 +200831,242 @@ function detectProviderId(message2) {
|
|
|
200840
200831
|
if (/platform\.openai\.com/i.test(message2)) return "openai";
|
|
200841
200832
|
return null;
|
|
200842
200833
|
}
|
|
200843
|
-
|
|
200844
|
-
|
|
200845
|
-
|
|
200846
|
-
|
|
200847
|
-
|
|
200848
|
-
|
|
200849
|
-
|
|
200850
|
-
|
|
200851
|
-
|
|
200852
|
-
|
|
200853
|
-
|
|
200854
|
-
|
|
200855
|
-
`\`\`\`
|
|
200856
|
-
${input.errorMessage}
|
|
200857
|
-
\`\`\``
|
|
200858
|
-
].join("\n");
|
|
200859
|
-
}
|
|
200860
|
-
const spendCap = findAnthropicSpendCap(input.errorMessage);
|
|
200861
|
-
if (spendCap) {
|
|
200862
|
-
const regains = spendCap.regainAt ? ` Access returns on **${spendCap.regainAt}**.` : "";
|
|
200863
|
-
return [
|
|
200864
|
-
`**Your \`anthropic\` account has reached its configured API usage limit.**${regains}`,
|
|
200865
|
-
"",
|
|
200866
|
-
"Anthropic refused the request against a spend limit set in your Console, not an empty balance \u2014 raise or remove that limit to run before it resets.",
|
|
200867
|
-
"",
|
|
200868
|
-
`[Anthropic billing settings \u2192](${PROVIDER_BILLING_URLS.anthropic})`,
|
|
200869
|
-
"",
|
|
200870
|
-
`\`\`\`
|
|
200871
|
-
${input.errorMessage}
|
|
200872
|
-
\`\`\``
|
|
200873
|
-
].join("\n");
|
|
200834
|
+
function resolveAccount(input) {
|
|
200835
|
+
const subscription = input.credential === "subscription" ? subscriptionFor(input.model) : void 0;
|
|
200836
|
+
if (subscription) {
|
|
200837
|
+
const plan = Object.values(PROVIDER_DASHBOARDS).find(
|
|
200838
|
+
(dashboard2) => dashboard2.credential === subscription.env
|
|
200839
|
+
);
|
|
200840
|
+
return {
|
|
200841
|
+
name: plan?.name ?? null,
|
|
200842
|
+
phrase: subscription.label,
|
|
200843
|
+
billingUrl: plan?.billing,
|
|
200844
|
+
subscription: true
|
|
200845
|
+
};
|
|
200874
200846
|
}
|
|
200875
|
-
const
|
|
200876
|
-
const
|
|
200877
|
-
|
|
200878
|
-
|
|
200879
|
-
|
|
200880
|
-
|
|
200881
|
-
|
|
200882
|
-
|
|
200883
|
-
|
|
200884
|
-
`\`\`\`
|
|
200885
|
-
${input.errorMessage}
|
|
200886
|
-
\`\`\``
|
|
200887
|
-
].join("\n");
|
|
200847
|
+
const id = detectProviderId(input.errorMessage) ?? input.model?.split("/")[0] ?? null;
|
|
200848
|
+
const dashboard = id ? PROVIDER_DASHBOARDS[id] : void 0;
|
|
200849
|
+
const name = dashboard?.name ?? providerDisplayName(id) ?? null;
|
|
200850
|
+
return {
|
|
200851
|
+
name,
|
|
200852
|
+
phrase: name ? `${name} account` : "model provider account",
|
|
200853
|
+
billingUrl: dashboard?.billing,
|
|
200854
|
+
subscription: false
|
|
200855
|
+
};
|
|
200888
200856
|
}
|
|
200889
|
-
function
|
|
200890
|
-
|
|
200891
|
-
|
|
200892
|
-
|
|
200893
|
-
|
|
200894
|
-
"",
|
|
200895
|
-
"The provider refused or dropped the request mid-run \u2014 nothing in your repo or your credentials is at fault. Re-trigger Pullfrog; if it keeps happening, check the provider's status page.",
|
|
200896
|
-
"",
|
|
200897
|
-
`\`\`\`
|
|
200898
|
-
${errorMessage}
|
|
200899
|
-
\`\`\``
|
|
200900
|
-
].join("\n");
|
|
200857
|
+
function providerResponse(ctx) {
|
|
200858
|
+
return {
|
|
200859
|
+
summary: ctx.account.name ? `${ctx.account.name}'s response` : "The provider's response",
|
|
200860
|
+
text: ctx.errorMessage
|
|
200861
|
+
};
|
|
200901
200862
|
}
|
|
200902
|
-
function
|
|
200903
|
-
|
|
200904
|
-
|
|
200905
|
-
|
|
200906
|
-
|
|
200907
|
-
|
|
200908
|
-
|
|
200909
|
-
|
|
200910
|
-
|
|
200911
|
-
|
|
200912
|
-
|
|
200913
|
-
|
|
200863
|
+
function formatProviderBillingExhausted(ctx) {
|
|
200864
|
+
if (isOpenRouterKeyLimitExceeded(ctx.errorMessage)) {
|
|
200865
|
+
const openrouter = PROVIDER_DASHBOARDS.openrouter;
|
|
200866
|
+
return renderFailure({
|
|
200867
|
+
headline: "Your OpenRouter key's spending limit is too low for this request.",
|
|
200868
|
+
detail: "OpenRouter checks the key's own limit as well as your balance, so raise or remove the limit; adding credit alone may not be enough.",
|
|
200869
|
+
details: providerResponse(ctx),
|
|
200870
|
+
links: openrouter?.keys ? [
|
|
200871
|
+
{ label: "Raise the key's limit", url: openrouter.keys },
|
|
200872
|
+
{ label: "Add credit", url: openrouter.billing }
|
|
200873
|
+
] : []
|
|
200874
|
+
});
|
|
200875
|
+
}
|
|
200876
|
+
const spendCap = findAnthropicSpendCap(ctx.errorMessage);
|
|
200877
|
+
if (spendCap) {
|
|
200878
|
+
const regains = spendCap.regainAt ? `Access returns on **${spendCap.regainAt}**. ` : "";
|
|
200879
|
+
const anthropic = PROVIDER_DASHBOARDS.anthropic;
|
|
200880
|
+
return renderFailure({
|
|
200881
|
+
headline: "Your Anthropic account has reached its API spend limit.",
|
|
200882
|
+
detail: `${regains}This is a limit set in your Anthropic Console, not an empty balance; raise or remove it to run before then.`,
|
|
200883
|
+
details: providerResponse(ctx),
|
|
200884
|
+
links: anthropic ? [{ label: "Anthropic billing settings", url: anthropic.billing }] : []
|
|
200885
|
+
});
|
|
200886
|
+
}
|
|
200887
|
+
return renderFailure({
|
|
200888
|
+
headline: `Your ${ctx.account.phrase} is out of credit.`,
|
|
200889
|
+
detail: "Top it up, then re-trigger Pullfrog.",
|
|
200890
|
+
details: providerResponse(ctx),
|
|
200891
|
+
links: ctx.account.billingUrl ? [{ label: `Top up ${ctx.account.name}`, url: ctx.account.billingUrl }] : []
|
|
200892
|
+
});
|
|
200893
|
+
}
|
|
200894
|
+
function formatProviderUsageLimit(ctx) {
|
|
200895
|
+
const tryAgainAt = /try again at (\d{1,2}:\d{2}\s*[AP]M)/i.exec(ctx.errorMessage)?.[1];
|
|
200896
|
+
const resets = tryAgainAt ? `${ctx.account.name ?? "The provider"} says to try again at ${tryAgainAt}. ` : "";
|
|
200897
|
+
return renderFailure({
|
|
200898
|
+
headline: `Your ${ctx.account.phrase} has hit its usage limit.`,
|
|
200899
|
+
detail: ctx.account.subscription ? `${resets}It resets on its own, so re-trigger after that, or connect an API key, which Pullfrog falls back to automatically.` : `${resets}Raise the limit with the provider, or re-trigger once it resets.`,
|
|
200900
|
+
details: providerResponse(ctx),
|
|
200901
|
+
links: [providerLink(ctx.repo.owner), pickModelLink(ctx.repo)]
|
|
200902
|
+
});
|
|
200903
|
+
}
|
|
200904
|
+
function formatTransientUpstreamBody(ctx) {
|
|
200905
|
+
if (/\bmodel is at capacity\b/i.test(ctx.errorMessage)) {
|
|
200906
|
+
return renderFailure({
|
|
200907
|
+
headline: `${ctx.account.name ? `${ctx.account.name}'s model` : "The model"} is at capacity right now.`,
|
|
200908
|
+
detail: "Re-trigger in a few minutes, or pick another model.",
|
|
200909
|
+
details: providerResponse(ctx),
|
|
200910
|
+
links: [pickModelLink(ctx.repo)]
|
|
200911
|
+
});
|
|
200912
|
+
}
|
|
200913
|
+
return renderFailure({
|
|
200914
|
+
headline: `${ctx.account.name ?? "The model provider"} had a temporary failure.`,
|
|
200915
|
+
detail: "Nothing on your side is at fault. Re-trigger to try again; if it keeps happening, check the provider's status page.",
|
|
200916
|
+
details: providerResponse(ctx)
|
|
200917
|
+
});
|
|
200918
|
+
}
|
|
200919
|
+
function formatProviderNoRoutableEndpoints(ctx) {
|
|
200920
|
+
return renderFailure({
|
|
200921
|
+
headline: "OpenRouter can't route this model anywhere your account allows.",
|
|
200922
|
+
detail: "Your OpenRouter data policy excludes every provider serving it, so nothing ran and nothing was billed. Allow more providers, or pick a model your policy covers.",
|
|
200923
|
+
details: providerResponse(ctx),
|
|
200924
|
+
links: [
|
|
200925
|
+
{ label: "OpenRouter data policy", url: "https://openrouter.ai/settings/privacy" },
|
|
200926
|
+
pickModelLink(ctx.repo)
|
|
200927
|
+
]
|
|
200928
|
+
});
|
|
200914
200929
|
}
|
|
200915
|
-
function formatProviderMissingCredential(
|
|
200916
|
-
return
|
|
200917
|
-
"
|
|
200918
|
-
"",
|
|
200919
|
-
|
|
200920
|
-
|
|
200921
|
-
|
|
200922
|
-
"",
|
|
200923
|
-
`\`\`\`
|
|
200924
|
-
${input.errorMessage}
|
|
200925
|
-
\`\`\``
|
|
200926
|
-
].join("\n");
|
|
200930
|
+
function formatProviderMissingCredential(ctx) {
|
|
200931
|
+
return renderFailure({
|
|
200932
|
+
headline: "No credential reached the model provider.",
|
|
200933
|
+
detail: "The run used a provider it had no key for, so nothing was billed and no key of yours was rejected. Connect a key for that provider, or pick a model you have one for.",
|
|
200934
|
+
details: providerResponse(ctx),
|
|
200935
|
+
links: [providerLink(ctx.repo.owner), pickModelLink(ctx.repo)]
|
|
200936
|
+
});
|
|
200927
200937
|
}
|
|
200928
|
-
function
|
|
200929
|
-
|
|
200930
|
-
|
|
200931
|
-
|
|
200932
|
-
|
|
200933
|
-
"",
|
|
200934
|
-
`
|
|
200935
|
-
|
|
200938
|
+
function formatProviderModelNotFound(ctx) {
|
|
200939
|
+
const model = /Model not found: (\S+?)\.?(?:\s|$)/.exec(ctx.errorMessage)?.[1];
|
|
200940
|
+
const suggestions = /Did you mean: ([^?\n]+)\?/.exec(ctx.errorMessage)?.[1]?.split(",").map((id) => `\`${id.trim()}\``);
|
|
200941
|
+
const suggested = suggestions?.length ? ` OpenCode suggests ${suggestions.slice(0, -1).join(", ")}${suggestions.length > 1 ? " or " : ""}${suggestions.at(-1)}.` : "";
|
|
200942
|
+
return renderFailure({
|
|
200943
|
+
headline: model ? `\`${model}\` isn't in OpenCode's catalog.` : "This repo's model isn't in OpenCode's catalog.",
|
|
200944
|
+
detail: `Pick another model wherever this repo sets it: the Pullfrog console, the workflow's \`model\` input or the \`PULLFROG_MODEL\` variable.${suggested}`,
|
|
200945
|
+
details: { summary: "OpenCode's response", text: ctx.errorMessage },
|
|
200946
|
+
links: [pickModelLink(ctx.repo)]
|
|
200947
|
+
});
|
|
200936
200948
|
}
|
|
200937
|
-
function
|
|
200938
|
-
return
|
|
200939
|
-
|
|
200940
|
-
|
|
200941
|
-
|
|
200942
|
-
|
|
200949
|
+
function formatNoProviderAvailable(ctx) {
|
|
200950
|
+
return renderFailure({
|
|
200951
|
+
headline: "The provider won't serve this model to this account.",
|
|
200952
|
+
detail: "It's in the catalog, but this account's plan can't reach it, which often happens when Pullfrog auto-selects it. Pin a model for this repo, or connect the provider that was picked.",
|
|
200953
|
+
details: providerResponse(ctx),
|
|
200954
|
+
links: [pickModelLink(ctx.repo), providerLink(ctx.repo.owner)]
|
|
200955
|
+
});
|
|
200943
200956
|
}
|
|
200944
|
-
function
|
|
200945
|
-
return
|
|
200946
|
-
"
|
|
200947
|
-
"",
|
|
200948
|
-
|
|
200949
|
-
|
|
200950
|
-
|
|
200951
|
-
"",
|
|
200952
|
-
`\`\`\`
|
|
200953
|
-
${input.raw}
|
|
200954
|
-
\`\`\``
|
|
200955
|
-
].join("\n");
|
|
200957
|
+
function formatZenFreeTierGate(ctx) {
|
|
200958
|
+
return renderFailure({
|
|
200959
|
+
headline: "OpenCode Zen's free tier refused a request from this run.",
|
|
200960
|
+
detail: "Zen rejects free-model requests that don't list its built-in tools, and OpenCode's mid-run context compaction sends none ([upstream issue](https://github.com/anomalyco/opencode/issues/49587)). Pick a paid model, or split the PR so the run finishes before compaction. If this repo's `opencode.json` denies `bash`, `glob`, `grep` or `read`, remove that too.",
|
|
200961
|
+
details: { summary: "OpenCode Zen's response", text: ctx.errorMessage },
|
|
200962
|
+
links: [pickModelLink(ctx.repo)]
|
|
200963
|
+
});
|
|
200956
200964
|
}
|
|
200957
|
-
function
|
|
200958
|
-
return
|
|
200959
|
-
"
|
|
200960
|
-
"",
|
|
200961
|
-
|
|
200962
|
-
|
|
200963
|
-
|
|
200964
|
-
"",
|
|
200965
|
-
`\`\`\`
|
|
200966
|
-
${input.raw}
|
|
200967
|
-
\`\`\``
|
|
200968
|
-
].join("\n");
|
|
200965
|
+
function formatUnrecognized(ctx) {
|
|
200966
|
+
return renderFailure({
|
|
200967
|
+
headline: "An unrecognized error occurred.",
|
|
200968
|
+
detail: "The error is below.",
|
|
200969
|
+
error: ctx.errorMessage,
|
|
200970
|
+
links: [DISCORD_LINK]
|
|
200971
|
+
});
|
|
200969
200972
|
}
|
|
200970
|
-
function
|
|
200971
|
-
const
|
|
200972
|
-
if (
|
|
200973
|
-
|
|
200974
|
-
return { summary: body, comment: body };
|
|
200975
|
-
}
|
|
200976
|
-
if (input.errorMessage.includes(MODEL_ACCESS_MARKER)) {
|
|
200977
|
-
return { summary: input.errorMessage, comment: input.errorMessage };
|
|
200978
|
-
}
|
|
200979
|
-
if (input.errorMessage.includes(SECRETS_UNAVAILABLE_MARKER)) {
|
|
200980
|
-
return { summary: input.errorMessage, comment: input.errorMessage };
|
|
200981
|
-
}
|
|
200982
|
-
if (input.errorMessage.includes(ROUTER_UNFUNDED_MARKER)) {
|
|
200983
|
-
return { summary: input.errorMessage, comment: input.errorMessage };
|
|
200984
|
-
}
|
|
200985
|
-
if (input.errorMessage.includes(CREDENTIAL_REJECTED_MARKER) || input.errorMessage.includes(CREDENTIAL_POOL_REFUSED_MARKER)) {
|
|
200986
|
-
return { summary: input.errorMessage, comment: input.errorMessage };
|
|
200987
|
-
}
|
|
200988
|
-
const isHang = input.errorMessage.startsWith("activity timeout") || input.errorMessage.startsWith("agent still pending");
|
|
200989
|
-
const hangBody = isHang ? formatAgentHangBody({
|
|
200990
|
-
diagnostic: input.agentDiagnostic,
|
|
200991
|
-
isHang: true,
|
|
200992
|
-
errorMessage: input.errorMessage
|
|
200993
|
-
}) : null;
|
|
200994
|
-
if (isProviderBillingExhausted(input.errorMessage)) {
|
|
200995
|
-
const body = formatProviderBillingExhausted({ errorMessage: input.errorMessage });
|
|
200996
|
-
return { summary: `### \u274C Pullfrog failed
|
|
200997
|
-
|
|
200998
|
-
${body}`, comment: body };
|
|
200999
|
-
}
|
|
201000
|
-
if (isProviderNoRoutableEndpoints(input.errorMessage)) {
|
|
201001
|
-
const body = formatProviderNoRoutableEndpoints({
|
|
201002
|
-
owner: input.repo.owner,
|
|
201003
|
-
name: input.repo.name,
|
|
201004
|
-
errorMessage: input.errorMessage
|
|
201005
|
-
});
|
|
201006
|
-
return { summary: `### \u274C Pullfrog failed
|
|
201007
|
-
|
|
201008
|
-
${body}`, comment: body };
|
|
201009
|
-
}
|
|
201010
|
-
if (isProviderUsageLimit(input.errorMessage)) {
|
|
201011
|
-
const body = formatProviderUsageLimit({
|
|
201012
|
-
owner: input.repo.owner,
|
|
201013
|
-
name: input.repo.name,
|
|
201014
|
-
raw: input.errorMessage
|
|
201015
|
-
});
|
|
201016
|
-
return { summary: `### \u274C Pullfrog failed
|
|
201017
|
-
|
|
201018
|
-
${body}`, comment: body };
|
|
200973
|
+
function renderRunErrorBody(ctx) {
|
|
200974
|
+
const message2 = ctx.errorMessage;
|
|
200975
|
+
if (ctx.trial && isRouterKeylimitExhaustedError(message2)) {
|
|
200976
|
+
return buildTrialSpentError({ owner: ctx.repo.owner });
|
|
201019
200977
|
}
|
|
201020
|
-
if (
|
|
201021
|
-
|
|
201022
|
-
|
|
201023
|
-
|
|
201024
|
-
|
|
201025
|
-
});
|
|
201026
|
-
return { summary: `### \u274C Pullfrog failed
|
|
201027
|
-
|
|
201028
|
-
${body}`, comment: body };
|
|
200978
|
+
if (ctx.routerActive && isRouterKeylimitExhaustedError(message2)) {
|
|
200979
|
+
return formatBillingErrorSummary(
|
|
200980
|
+
new BillingError(message2, { code: "router_keylimit_exhausted" }),
|
|
200981
|
+
ctx.repo.owner
|
|
200982
|
+
);
|
|
201029
200983
|
}
|
|
201030
|
-
if (
|
|
201031
|
-
|
|
201032
|
-
|
|
201033
|
-
|
|
201034
|
-
|
|
200984
|
+
if ([
|
|
200985
|
+
MODEL_ACCESS_MARKER,
|
|
200986
|
+
SECRETS_UNAVAILABLE_MARKER,
|
|
200987
|
+
ROUTER_UNFUNDED_MARKER,
|
|
200988
|
+
TRIAL_SPENT_MARKER,
|
|
200989
|
+
CREDENTIAL_REJECTED_MARKER,
|
|
200990
|
+
CREDENTIAL_POOL_REFUSED_MARKER
|
|
200991
|
+
].some((marker) => message2.includes(marker))) {
|
|
200992
|
+
return message2;
|
|
200993
|
+
}
|
|
200994
|
+
const isHang = message2.startsWith("activity timeout") || message2.startsWith("agent still pending");
|
|
200995
|
+
const hangBody = isHang ? formatAgentHangBody({ diagnostic: ctx.agentDiagnostic, errorMessage: message2 }) : null;
|
|
200996
|
+
if (isProviderBillingExhausted(message2)) return formatProviderBillingExhausted(ctx);
|
|
200997
|
+
if (isProviderNoRoutableEndpoints(message2)) return formatProviderNoRoutableEndpoints(ctx);
|
|
200998
|
+
if (isProviderUsageLimit(message2)) return formatProviderUsageLimit(ctx);
|
|
200999
|
+
if (isProviderMissingCredential(message2)) return formatProviderMissingCredential(ctx);
|
|
201000
|
+
if (isByokSetupError(message2)) {
|
|
201001
|
+
return renderFailure({
|
|
201002
|
+
headline: "This repo's model isn't fully set up yet.",
|
|
201003
|
+
detail: message2
|
|
201004
|
+
});
|
|
201005
|
+
}
|
|
201006
|
+
const apiKeySource = hangBody ?? message2;
|
|
201007
|
+
if (isApiKeyAuthError(apiKeySource)) {
|
|
201008
|
+
return formatApiKeyErrorSummary({
|
|
201009
|
+
owner: ctx.repo.owner,
|
|
201010
|
+
name: ctx.repo.name,
|
|
201011
|
+
raw: apiKeySource,
|
|
201012
|
+
model: ctx.model,
|
|
201013
|
+
credential: ctx.credential,
|
|
201014
|
+
output: hangBody ? void 0 : message2
|
|
201015
|
+
});
|
|
201016
|
+
}
|
|
201017
|
+
if (isProviderModelNotFoundError(message2)) return formatProviderModelNotFound(ctx);
|
|
201018
|
+
if (isNoProviderAvailableError(message2)) return formatNoProviderAvailable(ctx);
|
|
201019
|
+
if (isZenFreeTierGateError(message2)) return formatZenFreeTierGate(ctx);
|
|
201020
|
+
if (isContextOverflowError(message2)) {
|
|
201021
|
+
return renderFailure({
|
|
201022
|
+
headline: "This run outgrew the model's context window.",
|
|
201023
|
+
detail: "Pick a model with a larger context window, or split the PR into smaller ones and re-trigger.",
|
|
201024
|
+
links: [pickModelLink(ctx.repo)]
|
|
201025
|
+
});
|
|
201026
|
+
}
|
|
201027
|
+
const timeout = RUN_TIMEOUT_PATTERN.exec(message2)?.[1];
|
|
201028
|
+
if (timeout) {
|
|
201029
|
+
return renderFailure({
|
|
201030
|
+
headline: `This run hit its ${timeout} time limit while still working.`,
|
|
201031
|
+
detail: "Give runs longer with `--timeout=2h` (or `--notimeout`) in this repo's instructions, or raise the workflow's `timeout` input if it sets one, since that input wins. A smaller PR or a lower effort also finishes sooner.",
|
|
201032
|
+
links: [
|
|
201033
|
+
{ label: "Repo settings", url: repoConsoleUrl(ctx.repo) },
|
|
201034
|
+
{ label: "Flags", url: "https://docs.pullfrog.com/flags" }
|
|
201035
|
+
]
|
|
201035
201036
|
});
|
|
201036
|
-
return { summary: `### \u274C Pullfrog failed
|
|
201037
|
-
|
|
201038
|
-
${body}`, comment: body };
|
|
201039
|
-
}
|
|
201040
|
-
const apiKeySource = hangBody ?? input.errorMessage;
|
|
201041
|
-
const apiKeyErrorSummary = isApiKeyAuthError(apiKeySource) ? formatApiKeyErrorSummary({
|
|
201042
|
-
owner: input.repo.owner,
|
|
201043
|
-
name: input.repo.name,
|
|
201044
|
-
raw: apiKeySource
|
|
201045
|
-
}) : null;
|
|
201046
|
-
if (apiKeyErrorSummary) {
|
|
201047
|
-
return { summary: apiKeyErrorSummary, comment: apiKeyErrorSummary };
|
|
201048
201037
|
}
|
|
201049
|
-
|
|
201050
|
-
|
|
201051
|
-
|
|
201052
|
-
|
|
201053
|
-
|
|
201038
|
+
const stopScript = STOP_SCRIPT_PATTERN.exec(message2);
|
|
201039
|
+
if (stopScript) {
|
|
201040
|
+
return renderFailure({
|
|
201041
|
+
headline: "This repo's stop script failed, so the run ended without finishing.",
|
|
201042
|
+
detail: `It exited with code ${stopScript[1]}. Fix the script in this repo's settings, or re-trigger if the failure was temporary.`,
|
|
201043
|
+
details: stopScript[2]?.trim() ? { summary: "Script output", text: stopScript[2].trim() } : void 0,
|
|
201044
|
+
links: [{ label: "Repo settings", url: repoConsoleUrl(ctx.repo) }]
|
|
201054
201045
|
});
|
|
201055
|
-
return { summary: body, comment: body };
|
|
201056
201046
|
}
|
|
201057
|
-
if (
|
|
201058
|
-
|
|
201059
|
-
|
|
201060
|
-
|
|
201061
|
-
raw: input.errorMessage
|
|
201047
|
+
if (UNSUBMITTED_REVIEW_PATTERN.test(message2)) {
|
|
201048
|
+
return renderFailure({
|
|
201049
|
+
headline: "The agent finished without posting its review.",
|
|
201050
|
+
detail: "It was reminded and still didn't, so the run stopped. Re-trigger to try again."
|
|
201062
201051
|
});
|
|
201063
|
-
return { summary: `### \u274C Pullfrog failed
|
|
201064
|
-
|
|
201065
|
-
${body}`, comment: body };
|
|
201066
201052
|
}
|
|
201067
|
-
if (
|
|
201068
|
-
|
|
201069
|
-
|
|
201070
|
-
|
|
201071
|
-
|
|
201053
|
+
if (isTransientUpstreamError(message2)) return formatTransientUpstreamBody(ctx);
|
|
201054
|
+
if (hangBody) return hangBody;
|
|
201055
|
+
if (message2.startsWith("provider error: ")) {
|
|
201056
|
+
return renderFailure({
|
|
201057
|
+
headline: `${ctx.account.name ?? "The model provider"} returned an error, so this run stopped.`,
|
|
201058
|
+
detail: "If it names your key, plan or balance, fix that with the provider or pick another model.",
|
|
201059
|
+
error: message2.slice("provider error: ".length),
|
|
201060
|
+
links: [pickModelLink(ctx.repo)]
|
|
201072
201061
|
});
|
|
201073
|
-
return { summary: `### \u274C Pullfrog failed
|
|
201074
|
-
|
|
201075
|
-
${body}`, comment: body };
|
|
201076
201062
|
}
|
|
201077
|
-
|
|
201078
|
-
|
|
201079
|
-
|
|
201080
|
-
|
|
201081
|
-
|
|
201082
|
-
});
|
|
201083
|
-
return { summary: `### \u274C Pullfrog failed
|
|
201084
|
-
|
|
201085
|
-
${body}`, comment: body };
|
|
201086
|
-
}
|
|
201087
|
-
if (isRunTimeoutError(input.errorMessage)) {
|
|
201088
|
-
const body = formatRunTimeoutSummary({
|
|
201089
|
-
owner: input.repo.owner,
|
|
201090
|
-
name: input.repo.name,
|
|
201091
|
-
raw: input.errorMessage
|
|
201092
|
-
});
|
|
201093
|
-
return { summary: `### \u274C Pullfrog failed
|
|
201094
|
-
|
|
201095
|
-
${body}`, comment: body };
|
|
201096
|
-
}
|
|
201097
|
-
if (isTransientUpstreamError(input.errorMessage)) {
|
|
201098
|
-
const body = formatTransientUpstreamBody(input.errorMessage);
|
|
201099
|
-
return { summary: `### \u274C Pullfrog failed
|
|
201100
|
-
|
|
201101
|
-
${body}`, comment: body };
|
|
201102
|
-
}
|
|
201103
|
-
if (hangBody) {
|
|
201104
|
-
const explained = input.agentDiagnostic?.lastProviderError !== void 0 || input.agentDiagnostic?.sawModelOutput === false;
|
|
201105
|
-
return {
|
|
201106
|
-
summary: `### \u274C Pullfrog failed
|
|
201107
|
-
|
|
201108
|
-
${hangBody}`,
|
|
201109
|
-
comment: explained ? hangBody : formatMinimalFailureComment(input.repo)
|
|
201110
|
-
};
|
|
201111
|
-
}
|
|
201112
|
-
if (input.errorMessage.startsWith("provider error: ")) {
|
|
201113
|
-
const body = formatProviderErrorBody({
|
|
201114
|
-
owner: input.repo.owner,
|
|
201115
|
-
name: input.repo.name,
|
|
201116
|
-
raw: input.errorMessage
|
|
201117
|
-
});
|
|
201118
|
-
return { summary: `### \u274C Pullfrog failed
|
|
201063
|
+
return formatUnrecognized(ctx);
|
|
201064
|
+
}
|
|
201065
|
+
function renderRunError(input) {
|
|
201066
|
+
const body = renderRunErrorBody({ ...input, account: resolveAccount(input) });
|
|
201067
|
+
return { summary: `### \u274C Pullfrog failed
|
|
201119
201068
|
|
|
201120
201069
|
${body}`, comment: body };
|
|
201121
|
-
}
|
|
201122
|
-
const genericBody = formatGenericFailure(input.errorMessage);
|
|
201123
|
-
return {
|
|
201124
|
-
summary: `### \u274C Pullfrog failed
|
|
201125
|
-
|
|
201126
|
-
${genericBody}`,
|
|
201127
|
-
comment: formatMinimalFailureComment(input.repo)
|
|
201128
|
-
};
|
|
201129
201070
|
}
|
|
201130
201071
|
|
|
201131
201072
|
// utils/runLifecycle.ts
|
|
@@ -201551,7 +201492,10 @@ async function finalizeSuccessRun(input) {
|
|
|
201551
201492
|
errorMessage: input.result.error || "agent run failed",
|
|
201552
201493
|
repo: input.repo,
|
|
201553
201494
|
agentDiagnostic: input.toolState.agentDiagnostic,
|
|
201554
|
-
routerActive: !!input.toolContext.payload.proxyModel
|
|
201495
|
+
routerActive: !!input.toolContext.payload.proxyModel,
|
|
201496
|
+
model: input.toolState.model,
|
|
201497
|
+
credential: input.toolState.credential,
|
|
201498
|
+
trial: input.toolState.modelClamped?.reason === "trial"
|
|
201555
201499
|
}) : null;
|
|
201556
201500
|
if (rendered) {
|
|
201557
201501
|
await reportErrorToComment({
|
|
@@ -201712,7 +201656,7 @@ function renderTodoMarkdown(todos) {
|
|
|
201712
201656
|
case "cancelled":
|
|
201713
201657
|
return `- ~~${todo.content}~~`;
|
|
201714
201658
|
case "in_progress":
|
|
201715
|
-
return `- [ ] <img src="
|
|
201659
|
+
return `- [ ] <img src="${TODO_SPINNER_URL}" width="11" style="visibility: visible; max-width: 100%;" /> ${todo.content}`;
|
|
201716
201660
|
case "pending":
|
|
201717
201661
|
return `- [ ] ${todo.content}`;
|
|
201718
201662
|
default:
|
|
@@ -202099,10 +202043,15 @@ async function main() {
|
|
|
202099
202043
|
const funded = runContext.trialFallback && missingKey instanceof NoUsableCredentialError ? await resolveTrialFallback({
|
|
202100
202044
|
payload,
|
|
202101
202045
|
configuredModel: effectiveModel,
|
|
202046
|
+
creditsLeftUsd: runContext.trialCreditsLeftUsd,
|
|
202102
202047
|
oidcCredentials,
|
|
202103
202048
|
repo: runContext.repo,
|
|
202104
202049
|
toolState
|
|
202105
202050
|
}) : false;
|
|
202051
|
+
const trialSpent = runContext.trialCreditsLeftUsd !== void 0 && runContext.trialCreditsLeftUsd <= 0;
|
|
202052
|
+
if (!funded && trialSpent && missingKey instanceof NoUsableCredentialError) {
|
|
202053
|
+
throw new Error(buildTrialSpentError({ owner: runContext.repo.owner }));
|
|
202054
|
+
}
|
|
202106
202055
|
if (!funded) throw missingKey;
|
|
202107
202056
|
agent2 = resolveAgent2({
|
|
202108
202057
|
model: resolvedModel,
|
|
@@ -202433,7 +202382,10 @@ ${instructions.user}` : null,
|
|
|
202433
202382
|
errorMessage,
|
|
202434
202383
|
repo: runContext.repo,
|
|
202435
202384
|
agentDiagnostic: toolState.agentDiagnostic,
|
|
202436
|
-
routerActive: !!payload.proxyModel
|
|
202385
|
+
routerActive: !!payload.proxyModel,
|
|
202386
|
+
model: toolState.model,
|
|
202387
|
+
credential: toolState.credential,
|
|
202388
|
+
trial: toolState.modelClamped?.reason === "trial"
|
|
202437
202389
|
});
|
|
202438
202390
|
await writeRunErrorOutputs({ rendered, toolState });
|
|
202439
202391
|
if (toolContext) {
|
|
@@ -203015,7 +202967,7 @@ function resolveTarget2(repo) {
|
|
|
203015
202967
|
return { owner: match3[1], repo: match3[2] };
|
|
203016
202968
|
}
|
|
203017
202969
|
function cliVersion() {
|
|
203018
|
-
const parts = /^(\d+)\.(\d+)\.(\d+)$/.exec("0.1.
|
|
202970
|
+
const parts = /^(\d+)\.(\d+)\.(\d+)$/.exec("0.1.101");
|
|
203019
202971
|
if (!parts) return "0.0.0";
|
|
203020
202972
|
return `${Number(parts[1])}.${Number(parts[2])}.${Number(parts[3])}`;
|
|
203021
202973
|
}
|
|
@@ -203182,7 +203134,7 @@ async function runCli8(input) {
|
|
|
203182
203134
|
}
|
|
203183
203135
|
|
|
203184
203136
|
// cli.ts
|
|
203185
|
-
var VERSION10 = "0.1.
|
|
203137
|
+
var VERSION10 = "0.1.101";
|
|
203186
203138
|
var bin = basename2(process.argv[1] || "");
|
|
203187
203139
|
var PROG = bin === "pf" || bin === "pullfrog" ? bin : "pullfrog";
|
|
203188
203140
|
var rawArgs = process.argv.slice(2);
|