pullfrog 0.1.53 → 0.1.55
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/agents/claudePretoolGate.d.ts +4 -4
- package/dist/cli.mjs +59163 -30518
- package/dist/external.d.ts +1 -1
- package/dist/index.js +54378 -25964
- package/dist/internal/index.d.ts +3 -1
- package/dist/internal.js +132 -13
- package/dist/mcp/git.d.ts +27 -0
- package/dist/mcp/review.d.ts +6 -6
- package/dist/mcp/reviewComments.d.ts +5 -1
- package/dist/models.d.ts +21 -3
- package/dist/toolState.d.ts +7 -0
- package/dist/utils/apiKeys.d.ts +45 -0
- package/dist/utils/buildPullfrogFooter.d.ts +4 -4
- package/dist/utils/claudeSubscription.d.ts +9 -0
- package/dist/utils/codexOAuth.d.ts +11 -0
- package/dist/utils/credentialCheck.d.ts +21 -0
- package/dist/utils/credentialFallback.d.ts +42 -0
- package/dist/utils/github.d.ts +13 -0
- package/dist/utils/normalizeEnv.d.ts +2 -0
- package/dist/utils/providerErrors.d.ts +43 -0
- package/dist/utils/proxy.d.ts +1 -1
- package/dist/utils/runContext.d.ts +10 -0
- package/dist/utils/runContextData.d.ts +3 -0
- package/dist/utils/runErrorRenderer.d.ts +8 -2
- package/dist/utils/runStatusCheck.d.ts +14 -4
- package/dist/yes.js +223 -213
- package/package.json +1 -1
package/dist/internal/index.d.ts
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* Re-exports shared types, values, and utilities needed by the Next.js app.
|
|
4
4
|
*/
|
|
5
5
|
export type { AuthorPermission, AutoTier, EffortPosition, ModelAlias, ModelProvider, Payload, PayloadEvent, ProviderConfig, PushPermission, ShellPermission, ToolPermission, WriteablePayload, XrepoConfig, } from "../external.ts";
|
|
6
|
-
export { AUTO_EFFICIENT, AUTO_INTELLIGENT, DEFAULT_EFFORT_POSITION, DEFAULT_PROXY_MODEL, defaultAutoTier, EFFORT_ALIASES, getAutoSelectHintModel, getModelEffortLevels, getModelEnvVars, getModelManagedCredentials, getModelProvider, getProviderDisplayName, isAutoTier, isCardGatedModel, isEffortPosition, isOssAllowedModel, modelAliases, modelHasStoredAuth, OSS_MODEL_ALLOWLIST, OSS_RECOMMENDED_MODEL, offeredRungs, parseEffortPosition, parseModel, providers, pullfrogMcpName, resolveAutoTier, resolveCliModel, resolveDisplayAlias, resolveModelRung, resolveModelSlug, resolveOpenRouterModel, resolveRung, rungLabel, rungPosition, } from "../external.ts";
|
|
6
|
+
export { AUTO_EFFICIENT, AUTO_INTELLIGENT, DEFAULT_EFFORT_POSITION, DEFAULT_PROXY_MODEL, defaultAutoTier, EFFORT_ALIASES, getAutoSelectHintModel, getModelEffortLevels, getModelEnvVars, getModelManagedCredentials, getModelProvider, getProviderDisplayName, isAutoTier, isCardGatedModel, isEffortPosition, isOssAllowedModel, modelAliases, modelHasStoredAuth, OSS_MODEL_ALLOWLIST, OSS_RECOMMENDED_MODEL, offeredRungs, ossFundedModelNames, parseEffortPosition, parseModel, providers, pullfrogMcpName, resolveAutoTier, resolveCliModel, resolveDisplayAlias, resolveModelRung, resolveModelSlug, resolveOpenRouterModel, resolveRung, rungLabel, rungPosition, } from "../external.ts";
|
|
7
7
|
export type { Mode } from "../modes.ts";
|
|
8
8
|
export { modes } from "../modes.ts";
|
|
9
9
|
export { commercialPaywallBody } from "../utils/billingErrors.ts";
|
|
@@ -11,6 +11,8 @@ export type { BuildPullfrogFooterParams, WorkflowRunFooterInfo, } from "../utils
|
|
|
11
11
|
export { buildPullfrogFooter, PULLFROG_DIVIDER, stripExistingFooter, } from "../utils/buildPullfrogFooter.ts";
|
|
12
12
|
export type { CodexAuthBody } from "../utils/codexOAuth.ts";
|
|
13
13
|
export { decodeJwtExpMs, OAuthInvalidGrantError, parseCodexAuthBody, refreshCodexAuthBody, stringifyCodexAuthBody, } from "../utils/codexOAuth.ts";
|
|
14
|
+
export type { CredentialVerdict } from "../utils/credentialCheck.ts";
|
|
15
|
+
export { isCredentialProbeable, verifyCredential } from "../utils/credentialCheck.ts";
|
|
14
16
|
export type { ResourceUsage, UsageSummary } from "../utils/github.ts";
|
|
15
17
|
export { isPullfrog } from "../utils/isPullfrog.ts";
|
|
16
18
|
export { isLeapingIntoActionCommentBody, LEAPING_INTO_ACTION_PREFIX, } from "../utils/leapingComment.ts";
|
package/dist/internal.js
CHANGED
|
@@ -321,7 +321,12 @@ var providers = {
|
|
|
321
321
|
displayName: "Big Pickle",
|
|
322
322
|
resolve: "opencode/big-pickle",
|
|
323
323
|
preferred: true,
|
|
324
|
-
|
|
324
|
+
// free to RUN, but Zen still refuses it without a key — a keyless run
|
|
325
|
+
// dies at session start with `No provider available`. the `envVars: []`
|
|
326
|
+
// override that used to sit here shadowed the provider's own
|
|
327
|
+
// `OPENCODE_API_KEY` and made every static gate wave it through (#1077).
|
|
328
|
+
// `isFree` stays: it is what skips the proxy mint in run-context, and
|
|
329
|
+
// the model does cost nothing.
|
|
325
330
|
isFree: true
|
|
326
331
|
},
|
|
327
332
|
"claude-opus": {
|
|
@@ -443,14 +448,16 @@ var providers = {
|
|
|
443
448
|
"mimo-v2-pro-free": {
|
|
444
449
|
displayName: "MiMo V2 Pro",
|
|
445
450
|
resolve: "opencode/mimo-v2-pro-free",
|
|
446
|
-
|
|
451
|
+
// free to run, still gated on the provider's own OPENCODE_API_KEY —
|
|
452
|
+
// see the big-pickle note above (#1077).
|
|
447
453
|
isFree: true,
|
|
448
454
|
fallback: "opencode/big-pickle"
|
|
449
455
|
},
|
|
450
456
|
"minimax-m2.5-free": {
|
|
451
457
|
displayName: "MiniMax M2",
|
|
452
458
|
resolve: "opencode/minimax-m2.5-free",
|
|
453
|
-
|
|
459
|
+
// free to run, still gated on the provider's own OPENCODE_API_KEY —
|
|
460
|
+
// see the big-pickle note above (#1077).
|
|
454
461
|
isFree: true,
|
|
455
462
|
fallback: "opencode/big-pickle",
|
|
456
463
|
hidden: true
|
|
@@ -646,7 +653,11 @@ var providers = {
|
|
|
646
653
|
"gemini-flash": {
|
|
647
654
|
displayName: "Gemini Flash",
|
|
648
655
|
resolve: "openrouter/~google/gemini-flash-latest",
|
|
649
|
-
|
|
656
|
+
// no `minimal` here, unlike the google/ and opencode/ entries for the
|
|
657
|
+
// same model: OpenRouter's floating `~google/gemini-flash-latest` stopped
|
|
658
|
+
// publishing that rung. mirroring it is not cosmetic — claude-code
|
|
659
|
+
// hard-errors on an out-of-range `--effort` before it calls the API.
|
|
660
|
+
effort: ["low", "medium", "high"],
|
|
650
661
|
openRouterResolve: "openrouter/~google/gemini-flash-latest"
|
|
651
662
|
},
|
|
652
663
|
grok: {
|
|
@@ -822,10 +833,12 @@ var OSS_MODEL_ALLOWLIST = [
|
|
|
822
833
|
"deepseek/deepseek-flash",
|
|
823
834
|
"deepseek/deepseek-pro",
|
|
824
835
|
"openai/gpt-luna",
|
|
825
|
-
"openrouter/minimax-m2.5"
|
|
826
|
-
"moonshotai/kimi-k2"
|
|
836
|
+
"openrouter/minimax-m2.5"
|
|
827
837
|
];
|
|
828
838
|
var OSS_RECOMMENDED_MODEL = AUTO_TIER_TARGET[AUTO_EFFICIENT];
|
|
839
|
+
function ossFundedModelNames() {
|
|
840
|
+
return OSS_MODEL_ALLOWLIST.map((slug) => resolveDisplayAlias(slug)?.displayName ?? slug);
|
|
841
|
+
}
|
|
829
842
|
var ossAllowedTargets = new Set(
|
|
830
843
|
OSS_MODEL_ALLOWLIST.map((slug) => {
|
|
831
844
|
const target = resolveOpenRouterModel(slug);
|
|
@@ -1295,7 +1308,7 @@ function formatModelLabel(params) {
|
|
|
1295
1308
|
}
|
|
1296
1309
|
const base = alias?.isFree ? `\`${displayName}\` (free)` : `\`${displayName}\``;
|
|
1297
1310
|
if (params.fallbackFrom) {
|
|
1298
|
-
return `${base} (credentials for ${providerDisplayName(params.fallbackFrom)}
|
|
1311
|
+
return `${base} (credentials for ${providerDisplayName(params.fallbackFrom)} were rejected by the provider)`;
|
|
1299
1312
|
}
|
|
1300
1313
|
if (params.clamped) {
|
|
1301
1314
|
const target = isAutoTier(params.clamped.from) ? "the intelligent tier" : `\`${resolveDisplayAlias(params.clamped.from)?.displayName ?? params.clamped.from}\``;
|
|
@@ -1423,6 +1436,7 @@ function parseCodexAuthBody(raw) {
|
|
|
1423
1436
|
if (typeof t.refresh_token !== "string" || t.refresh_token.length === 0) return null;
|
|
1424
1437
|
return {
|
|
1425
1438
|
auth_mode: "chatgpt",
|
|
1439
|
+
...typeof v.refresh_rejected_at === "string" ? { refresh_rejected_at: v.refresh_rejected_at } : {},
|
|
1426
1440
|
tokens: {
|
|
1427
1441
|
access_token: t.access_token,
|
|
1428
1442
|
refresh_token: t.refresh_token,
|
|
@@ -1437,6 +1451,109 @@ function stringifyCodexAuthBody(body) {
|
|
|
1437
1451
|
`;
|
|
1438
1452
|
}
|
|
1439
1453
|
|
|
1454
|
+
// utils/claudeSubscription.ts
|
|
1455
|
+
var CLAUDE_CODE_IDENTITY = "You are Claude Code, Anthropic's official CLI for Claude.";
|
|
1456
|
+
var fallbackResolve = resolveModelSlug("anthropic/claude-haiku");
|
|
1457
|
+
if (!fallbackResolve) {
|
|
1458
|
+
throw new Error("claudeSubscription preflight: anthropic/claude-haiku missing from registry");
|
|
1459
|
+
}
|
|
1460
|
+
var FALLBACK_PROBE_MODEL = fallbackResolve.slice(fallbackResolve.indexOf("/") + 1);
|
|
1461
|
+
async function preflightClaudeSubscription(params) {
|
|
1462
|
+
let res;
|
|
1463
|
+
try {
|
|
1464
|
+
res = await fetch("https://api.anthropic.com/v1/messages", {
|
|
1465
|
+
method: "POST",
|
|
1466
|
+
headers: {
|
|
1467
|
+
authorization: `Bearer ${params.token}`,
|
|
1468
|
+
"anthropic-beta": "claude-code-20250219,oauth-2025-04-20",
|
|
1469
|
+
"anthropic-version": "2023-06-01",
|
|
1470
|
+
"content-type": "application/json",
|
|
1471
|
+
"x-app": "cli"
|
|
1472
|
+
},
|
|
1473
|
+
body: JSON.stringify({
|
|
1474
|
+
model: params.model ?? FALLBACK_PROBE_MODEL,
|
|
1475
|
+
max_tokens: 1,
|
|
1476
|
+
system: CLAUDE_CODE_IDENTITY,
|
|
1477
|
+
messages: [{ role: "user", content: "ok" }]
|
|
1478
|
+
}),
|
|
1479
|
+
signal: AbortSignal.timeout(1e4)
|
|
1480
|
+
});
|
|
1481
|
+
} catch {
|
|
1482
|
+
return { usable: true, status: void 0 };
|
|
1483
|
+
}
|
|
1484
|
+
if (res.status !== 401 && res.status !== 403 && res.status !== 429)
|
|
1485
|
+
return { usable: true, status: res.status };
|
|
1486
|
+
const body = await res.text().catch(() => "");
|
|
1487
|
+
return {
|
|
1488
|
+
usable: false,
|
|
1489
|
+
status: res.status,
|
|
1490
|
+
reason: `${res.status}: ${extractApiErrorMessage(body)}`
|
|
1491
|
+
};
|
|
1492
|
+
}
|
|
1493
|
+
function extractApiErrorMessage(body) {
|
|
1494
|
+
try {
|
|
1495
|
+
const parsed = JSON.parse(body);
|
|
1496
|
+
if (typeof parsed === "object" && parsed !== null && "error" in parsed && typeof parsed.error === "object" && parsed.error !== null && "message" in parsed.error && typeof parsed.error.message === "string") {
|
|
1497
|
+
return parsed.error.message;
|
|
1498
|
+
}
|
|
1499
|
+
} catch {
|
|
1500
|
+
}
|
|
1501
|
+
return body.slice(0, 200);
|
|
1502
|
+
}
|
|
1503
|
+
|
|
1504
|
+
// utils/credentialCheck.ts
|
|
1505
|
+
var PROBES = {
|
|
1506
|
+
ANTHROPIC_API_KEY: {
|
|
1507
|
+
request: (value) => ({
|
|
1508
|
+
url: "https://api.anthropic.com/v1/models",
|
|
1509
|
+
headers: { "x-api-key": value, "anthropic-version": "2023-06-01" }
|
|
1510
|
+
})
|
|
1511
|
+
},
|
|
1512
|
+
OPENROUTER_API_KEY: {
|
|
1513
|
+
request: (value) => ({
|
|
1514
|
+
url: "https://openrouter.ai/api/v1/key",
|
|
1515
|
+
headers: { authorization: `Bearer ${value}` }
|
|
1516
|
+
})
|
|
1517
|
+
},
|
|
1518
|
+
GEMINI_API_KEY: {
|
|
1519
|
+
request: (value) => ({
|
|
1520
|
+
url: `https://generativelanguage.googleapis.com/v1beta/models?key=${encodeURIComponent(value)}`,
|
|
1521
|
+
headers: {}
|
|
1522
|
+
}),
|
|
1523
|
+
deadWhen: (status, body) => status === 400 && body.includes("API_KEY_INVALID")
|
|
1524
|
+
}
|
|
1525
|
+
};
|
|
1526
|
+
PROBES.GOOGLE_GENERATIVE_AI_API_KEY = PROBES.GEMINI_API_KEY;
|
|
1527
|
+
async function verifyClaudeSubscription(value) {
|
|
1528
|
+
const preflight = await preflightClaudeSubscription({ token: value, model: void 0 });
|
|
1529
|
+
if (preflight.status === void 0) return "unknown";
|
|
1530
|
+
return preflight.status === 401 ? "dead" : "alive";
|
|
1531
|
+
}
|
|
1532
|
+
function isCredentialProbeable(envVar) {
|
|
1533
|
+
return envVar === "CLAUDE_CODE_OAUTH_TOKEN" || envVar in PROBES;
|
|
1534
|
+
}
|
|
1535
|
+
async function verifyCredential(params) {
|
|
1536
|
+
const value = params.value.trim();
|
|
1537
|
+
if (value.length === 0) return "dead";
|
|
1538
|
+
if (params.envVar === "CLAUDE_CODE_OAUTH_TOKEN") return verifyClaudeSubscription(value);
|
|
1539
|
+
const probe = PROBES[params.envVar];
|
|
1540
|
+
if (!probe) return "unknown";
|
|
1541
|
+
const request = probe.request(value);
|
|
1542
|
+
try {
|
|
1543
|
+
const response = await fetch(request.url, {
|
|
1544
|
+
headers: request.headers,
|
|
1545
|
+
signal: AbortSignal.timeout(5e3)
|
|
1546
|
+
});
|
|
1547
|
+
if (response.ok) return "alive";
|
|
1548
|
+
if (probe.deadWhen) {
|
|
1549
|
+
return probe.deadWhen(response.status, await response.text()) ? "dead" : "unknown";
|
|
1550
|
+
}
|
|
1551
|
+
return response.status === 401 ? "dead" : "unknown";
|
|
1552
|
+
} catch {
|
|
1553
|
+
return "unknown";
|
|
1554
|
+
}
|
|
1555
|
+
}
|
|
1556
|
+
|
|
1440
1557
|
// utils/isPullfrog.ts
|
|
1441
1558
|
function isPullfrog(actor) {
|
|
1442
1559
|
actor = actor?.toLowerCase().replace("[bot]", "");
|
|
@@ -1562,6 +1679,7 @@ async function createLeapingProgressComment(ctx, target, body) {
|
|
|
1562
1679
|
|
|
1563
1680
|
// utils/runStatusCheck.ts
|
|
1564
1681
|
var RUN_STATUS_CHECK_NAME = "pullfrog";
|
|
1682
|
+
var GITHUB_ACTIONS_APP_SLUG = "github-actions";
|
|
1565
1683
|
var APPROVAL_CHECK_NAME = "pullfrog-approval";
|
|
1566
1684
|
var IN_PROGRESS_OUTPUT = {
|
|
1567
1685
|
title: "Pullfrog is running",
|
|
@@ -1601,10 +1719,6 @@ var TERMINAL_OUTPUT = {
|
|
|
1601
1719
|
skipped: {
|
|
1602
1720
|
title: "Pullfrog run skipped",
|
|
1603
1721
|
summary: "This run was superseded by another Pullfrog run."
|
|
1604
|
-
},
|
|
1605
|
-
stale: {
|
|
1606
|
-
title: "Pullfrog run didn't finish",
|
|
1607
|
-
summary: "Pullfrog never received a completion signal for this run. See the run logs for details."
|
|
1608
1722
|
}
|
|
1609
1723
|
};
|
|
1610
1724
|
function terminalOutput(params) {
|
|
@@ -1623,7 +1737,9 @@ async function createRunStatusCheck(params) {
|
|
|
1623
1737
|
check_name: RUN_STATUS_CHECK_NAME,
|
|
1624
1738
|
status: "in_progress"
|
|
1625
1739
|
}).catch(() => void 0);
|
|
1626
|
-
const reusable = existing?.data.check_runs
|
|
1740
|
+
const reusable = existing?.data.check_runs.find(
|
|
1741
|
+
(run) => run.app?.slug !== GITHUB_ACTIONS_APP_SLUG
|
|
1742
|
+
);
|
|
1627
1743
|
if (reusable) return reusable.id;
|
|
1628
1744
|
const createParams = {
|
|
1629
1745
|
owner: params.owner,
|
|
@@ -1713,6 +1829,7 @@ export {
|
|
|
1713
1829
|
getProviderDisplayName,
|
|
1714
1830
|
isAutoTier,
|
|
1715
1831
|
isCardGatedModel,
|
|
1832
|
+
isCredentialProbeable,
|
|
1716
1833
|
isEffortPosition,
|
|
1717
1834
|
isLeapingIntoActionCommentBody,
|
|
1718
1835
|
isOssAllowedModel,
|
|
@@ -1722,6 +1839,7 @@ export {
|
|
|
1722
1839
|
modelHasStoredAuth,
|
|
1723
1840
|
modes,
|
|
1724
1841
|
offeredRungs,
|
|
1842
|
+
ossFundedModelNames,
|
|
1725
1843
|
parseCodexAuthBody,
|
|
1726
1844
|
parseEffortPosition,
|
|
1727
1845
|
parseModel,
|
|
@@ -1742,5 +1860,6 @@ export {
|
|
|
1742
1860
|
stringifyCodexAuthBody,
|
|
1743
1861
|
stripExistingFooter,
|
|
1744
1862
|
truncateAtLineBoundary,
|
|
1745
|
-
updateProgressComment
|
|
1863
|
+
updateProgressComment,
|
|
1864
|
+
verifyCredential
|
|
1746
1865
|
};
|
package/dist/mcp/git.d.ts
CHANGED
|
@@ -32,6 +32,33 @@ export declare function CommitChangesTool(ctx: ToolContext): import("fastmcp").T
|
|
|
32
32
|
export declare const AUTH_REQUIRED_REDIRECT: Record<string, string>;
|
|
33
33
|
export declare const NOSHELL_BLOCKED_SUBCOMMANDS: Record<string, string>;
|
|
34
34
|
export declare const NOSHELL_BLOCKED_ARGS: string[];
|
|
35
|
+
/**
|
|
36
|
+
* Two malformed-but-unambiguous shapes the models keep emitting: `args[0]`
|
|
37
|
+
* repeating the subcommand, and the whole cmdline pushed into `args` behind
|
|
38
|
+
* `command: "git"`. Both were rejected (or, for the second, run as `git git
|
|
39
|
+
* log …`) and the models re-emitted the byte-identical call rather than
|
|
40
|
+
* adapting — 368 dead round trips over 190 runs in 24h (#1199). The intent is
|
|
41
|
+
* knowable, so normalize rather than reject.
|
|
42
|
+
*
|
|
43
|
+
* The promotion out of `command: "git"` re-clears `subcommandPattern`: without
|
|
44
|
+
* that, `args: ["-c", "alias.x=!evil", "x"]` would move a global git option
|
|
45
|
+
* into the subcommand slot that pattern exists to guard.
|
|
46
|
+
*/
|
|
47
|
+
export declare function normalizeGitInvocation(input: {
|
|
48
|
+
command: string;
|
|
49
|
+
args: string[];
|
|
50
|
+
}): {
|
|
51
|
+
command: string;
|
|
52
|
+
args: string[];
|
|
53
|
+
};
|
|
54
|
+
/**
|
|
55
|
+
* git subcommands whose exit 1 is a documented result rather than a failure.
|
|
56
|
+
* The status code IS the answer and both streams are empty by design, so
|
|
57
|
+
* anything that reads "non-zero means broken" reports a successful negative as
|
|
58
|
+
* an error. See #1152; `merge-base --is-ancestor` keeps its own branch because
|
|
59
|
+
* it returns a boolean rather than a note.
|
|
60
|
+
*/
|
|
61
|
+
export declare function describeEmptyResult(command: string, args: string[]): string | null;
|
|
35
62
|
export declare function GitTool(ctx: ToolContext): import("fastmcp").Tool<any, import("@standard-schema/spec").StandardSchemaV1<{
|
|
36
63
|
command: string;
|
|
37
64
|
args?: string[];
|
package/dist/mcp/review.d.ts
CHANGED
|
@@ -189,8 +189,8 @@ export declare const CreatePullRequestReview: import("arktype/internal/variants/
|
|
|
189
189
|
line: number;
|
|
190
190
|
side?: "LEFT" | "RIGHT";
|
|
191
191
|
body?: string;
|
|
192
|
-
suggestion?: string;
|
|
193
|
-
start_line?: number;
|
|
192
|
+
suggestion?: string | null;
|
|
193
|
+
start_line?: number | null;
|
|
194
194
|
}[];
|
|
195
195
|
}, {}>;
|
|
196
196
|
export declare function CreatePullRequestReviewTool(ctx: ToolContext): import("fastmcp").Tool<any, import("@standard-schema/spec").StandardSchemaV1<{
|
|
@@ -204,8 +204,8 @@ export declare function CreatePullRequestReviewTool(ctx: ToolContext): import("f
|
|
|
204
204
|
line: number;
|
|
205
205
|
side?: "LEFT" | "RIGHT";
|
|
206
206
|
body?: string;
|
|
207
|
-
suggestion?: string;
|
|
208
|
-
start_line?: number;
|
|
207
|
+
suggestion?: string | null;
|
|
208
|
+
start_line?: number | null;
|
|
209
209
|
}[];
|
|
210
210
|
}, {
|
|
211
211
|
pull_number: number;
|
|
@@ -218,8 +218,8 @@ export declare function CreatePullRequestReviewTool(ctx: ToolContext): import("f
|
|
|
218
218
|
line: number;
|
|
219
219
|
side?: "LEFT" | "RIGHT";
|
|
220
220
|
body?: string;
|
|
221
|
-
suggestion?: string;
|
|
222
|
-
start_line?: number;
|
|
221
|
+
suggestion?: string | null;
|
|
222
|
+
start_line?: number | null;
|
|
223
223
|
}[];
|
|
224
224
|
}>> & {
|
|
225
225
|
mutates?: boolean;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { Octokit } from "@octokit/rest";
|
|
2
2
|
import type { ToolContext } from "./server.ts";
|
|
3
|
-
export declare const REVIEW_THREADS_QUERY = "\nquery ($owner: String!, $name: String!, $prNumber: Int
|
|
3
|
+
export declare const REVIEW_THREADS_QUERY = "\nquery ($owner: String!, $name: String!, $prNumber: Int!, $cursor: String) {\n repository(owner: $owner, name: $name) {\n pullRequest(number: $prNumber) {\n reviewThreads(first: 100, after: $cursor) {\n pageInfo { hasNextPage endCursor }\n nodes {\n id\n path\n line\n startLine\n diffSide\n isResolved\n isOutdated\n comments(first: 50) {\n nodes {\n fullDatabaseId\n body\n bodyHTML\n createdAt\n diffHunk\n line\n startLine\n originalLine\n originalStartLine\n author { login }\n pullRequestReview {\n databaseId\n author { login }\n }\n reactionGroups {\n content\n reactors(first: 10) {\n nodes {\n ... on Actor { login }\n }\n }\n }\n }\n }\n }\n }\n }\n }\n}\n";
|
|
4
4
|
export type ReviewThreadComment = {
|
|
5
5
|
fullDatabaseId: string | null;
|
|
6
6
|
body: string;
|
|
@@ -45,6 +45,10 @@ export type ReviewThreadsQueryResponse = {
|
|
|
45
45
|
repository: {
|
|
46
46
|
pullRequest: {
|
|
47
47
|
reviewThreads: {
|
|
48
|
+
pageInfo: {
|
|
49
|
+
hasNextPage: boolean;
|
|
50
|
+
endCursor: string | null;
|
|
51
|
+
};
|
|
48
52
|
nodes: (ReviewThread | null)[] | null;
|
|
49
53
|
} | null;
|
|
50
54
|
} | null;
|
package/dist/models.d.ts
CHANGED
|
@@ -38,7 +38,10 @@ export interface ModelAlias {
|
|
|
38
38
|
openRouterResolve: string | undefined;
|
|
39
39
|
/** top-tier pick for this provider — preferred during auto-select */
|
|
40
40
|
preferred: boolean;
|
|
41
|
-
/** whether this alias
|
|
41
|
+
/** whether this alias costs nothing to run. NOT the same as needing no
|
|
42
|
+
* credential: Zen's free models still require `OPENCODE_API_KEY`, and reading
|
|
43
|
+
* this as "keyless" is what let a keyless run boot onto one and die with
|
|
44
|
+
* `No provider available` (#1077). used to skip the proxy mint. */
|
|
42
45
|
isFree: boolean;
|
|
43
46
|
/** slug of a replacement model to resolve through. presence means this alias
|
|
44
47
|
* never runs as-is — resolution redirects to the replacement and it's hidden
|
|
@@ -122,6 +125,8 @@ export declare function parseModel(slug: string): {
|
|
|
122
125
|
};
|
|
123
126
|
export declare function getModelProvider(slug: string): string;
|
|
124
127
|
export declare function getProviderDisplayName(slug: string): string | undefined;
|
|
128
|
+
/** provider-prefixed specifier → bare model id (`anthropic/claude-opus-5` → `claude-opus-5`). */
|
|
129
|
+
export declare function stripProviderPrefix(specifier: string): string;
|
|
125
130
|
export declare function getModelEnvVars(slug: string): string[];
|
|
126
131
|
/** managed credentials are authored only via `pullfrog auth <provider>` — they
|
|
127
132
|
* count as "configured" for hasAnyKey-style UI checks but are never offered as
|
|
@@ -245,14 +250,27 @@ export declare const DEFAULT_PROXY_MODEL: string;
|
|
|
245
250
|
* DB toggle — changing it is a deploy, which is the right friction. costs below
|
|
246
251
|
* are modelled on the measured OSS token mix, where cache reads are ~87.5% of
|
|
247
252
|
* input and list price therefore ranks models WRONG: Luna 0.39x, Flash 0.45x,
|
|
248
|
-
* Pro 1.00x, MiniMax M2 1.30x
|
|
249
|
-
*
|
|
253
|
+
* Pro 1.00x, MiniMax M2 1.30x.
|
|
254
|
+
*
|
|
255
|
+
* Kimi K2 was funded and then dropped: 13.2x the funded default as MEASURED
|
|
256
|
+
* over 2,203 real runs ($0.4782 vs $0.0362), almost entirely on its cache-read
|
|
257
|
+
* RATE — 8.5x Flash's. it also fails 2.9% of runs against Flash's 0.0%. best
|
|
258
|
+
* achievable was 10.9x (cheapest OpenRouter endpoint), so no routing fixes it.
|
|
259
|
+
* see wiki/oss-model-allowlist.md.
|
|
250
260
|
*/
|
|
251
261
|
export declare const OSS_MODEL_ALLOWLIST: readonly string[];
|
|
252
262
|
/** the pick the console badges. DERIVED from the efficient tier rather than
|
|
253
263
|
* restated, so the badge cannot drift from the model that actually runs — a
|
|
254
264
|
* second literal here would be an invariant nothing enforces. */
|
|
255
265
|
export declare const OSS_RECOMMENDED_MODEL: string;
|
|
266
|
+
/**
|
|
267
|
+
* display names of the funded set, in allowlist order. the single place every
|
|
268
|
+
* surface that NAMES the menu in prose reads it from — the application page,
|
|
269
|
+
* the acceptance email, the model-access error — so none of them can promise a
|
|
270
|
+
* model the program stopped funding. callers format the list themselves; this
|
|
271
|
+
* is a catalog, not copy.
|
|
272
|
+
*/
|
|
273
|
+
export declare function ossFundedModelNames(): string[];
|
|
256
274
|
/**
|
|
257
275
|
* whether an OSS repo's stored pick is one the program funds. resolves through
|
|
258
276
|
* the fallback chain first, so `auto/*` sentinels and deprecated slugs are
|
package/dist/toolState.d.ts
CHANGED
|
@@ -148,6 +148,13 @@ export interface ToolState {
|
|
|
148
148
|
oss?: boolean | undefined;
|
|
149
149
|
todoTracker?: TodoTracker | undefined;
|
|
150
150
|
agentDiagnostic?: AgentDiagnostic | undefined;
|
|
151
|
+
/**
|
|
152
|
+
* a `PATCH /api/workflow-run/:id` came back 404 — the reservation was never
|
|
153
|
+
* claimed, which is permanent for the life of the run (the claim happens at
|
|
154
|
+
* setup, strictly before any PATCH). latched so the remaining calls stop
|
|
155
|
+
* firing: one CI run spent 28 doomed round trips on it. see #1153.
|
|
156
|
+
*/
|
|
157
|
+
workflowRunUnclaimed?: boolean | undefined;
|
|
151
158
|
}
|
|
152
159
|
interface InitToolStateParams {
|
|
153
160
|
progressComment: {
|
package/dist/utils/apiKeys.d.ts
CHANGED
|
@@ -5,6 +5,28 @@
|
|
|
5
5
|
* on our side is the wrong CTA — their key is configured and still stored.
|
|
6
6
|
*/
|
|
7
7
|
export declare const SECRETS_UNAVAILABLE_MARKER = "couldn't load your Pullfrog secrets";
|
|
8
|
+
/**
|
|
9
|
+
* marker for the "your Router wallet is empty AND you have no provider key"
|
|
10
|
+
* body. its own marker rather than the missing-key one because
|
|
11
|
+
* `formatApiKeyErrorSummary` rebuilds any body carrying `MISSING_KEY_MARKER`
|
|
12
|
+
* that doesn't start with it, which would silently discard this copy.
|
|
13
|
+
*/
|
|
14
|
+
export declare const ROUTER_UNFUNDED_MARKER = "your Pullfrog Router balance is empty";
|
|
15
|
+
/**
|
|
16
|
+
* marker for a credential the provider explicitly REJECTED before the agent
|
|
17
|
+
* started. needs a verbatim guard for the same reason as its three siblings,
|
|
18
|
+
* and more sharply: the body quotes the provider's own wording, which is
|
|
19
|
+
* exactly what `isApiKeyAuthError` sniffs — so an Anthropic error whose JSON
|
|
20
|
+
* carries `authentication_error` would be rebuilt into the generic
|
|
21
|
+
* "rotate the key, update the GitHub Actions secret" copy. That is #782
|
|
22
|
+
* verbatim, on the code path built to stop it.
|
|
23
|
+
*
|
|
24
|
+
* Phrased distinctively for the same reason as its siblings: `runErrorRenderer`
|
|
25
|
+
* short-circuits the whole rendering pipeline on a SUBSTRING match against
|
|
26
|
+
* arbitrary agent output, so a marker short enough to occur by accident would
|
|
27
|
+
* hand that agent the ability to suppress every other error body.
|
|
28
|
+
*/
|
|
29
|
+
export declare const CREDENTIAL_REJECTED_MARKER = "was rejected by its provider";
|
|
8
30
|
/**
|
|
9
31
|
* Validate that the resolved model can actually be served by the chosen
|
|
10
32
|
* agent. For routing slugs (Bedrock / Vertex) the auth shape is multi-var
|
|
@@ -26,6 +48,7 @@ export declare function validateAgentApiKey(params: {
|
|
|
26
48
|
/** run-context couldn't hand over Pullfrog-stored secrets, so a missing key
|
|
27
49
|
* says nothing about what the user actually configured. */
|
|
28
50
|
secretsUnavailable?: boolean | undefined;
|
|
51
|
+
routerUnfunded?: boolean | undefined;
|
|
29
52
|
}): void;
|
|
30
53
|
/**
|
|
31
54
|
* Detect agent-runtime auth failures that should be reformatted as an actionable
|
|
@@ -68,6 +91,14 @@ export declare function isApiKeyAuthError(text: string): boolean;
|
|
|
68
91
|
* expiry can't be misread as an LLM credential problem.
|
|
69
92
|
*/
|
|
70
93
|
export declare function isOAuthCredentialExpiredError(text: string): boolean;
|
|
94
|
+
/**
|
|
95
|
+
* The key itself is unsendable, not wrong: a line-wrapped paste leaves an
|
|
96
|
+
* interior newline, and the provider SDK refuses to serialize the header
|
|
97
|
+
* (`API Error: Header '14' has invalid value`). The only member of the
|
|
98
|
+
* credential-error family whose remedy is NOT "rotate the key" — re-saving the
|
|
99
|
+
* same value on one line fixes it. See #1162.
|
|
100
|
+
*/
|
|
101
|
+
export declare function isMalformedKeyError(text: string): boolean;
|
|
71
102
|
/**
|
|
72
103
|
* Anthropic entitlement denial for Claude Pro/Max subscription auth (#1072):
|
|
73
104
|
* an org admin turned off Claude Code subscription access. no credential the
|
|
@@ -75,6 +106,20 @@ export declare function isOAuthCredentialExpiredError(text: string): boolean;
|
|
|
75
106
|
*/
|
|
76
107
|
export declare function isClaudeSubscriptionDisabledError(text: string): boolean;
|
|
77
108
|
export declare function isClaudeSessionLimitError(text: string): boolean;
|
|
109
|
+
/**
|
|
110
|
+
* The provider rejected a credential we checked before the agent started, and
|
|
111
|
+
* nothing else on the account can serve this run. Names the credential and the
|
|
112
|
+
* place it actually lives, rather than guessing from the provider's wording.
|
|
113
|
+
*/
|
|
114
|
+
export declare function buildRejectedCredentialError(params: {
|
|
115
|
+
credential: string;
|
|
116
|
+
/** the provider's own message, when it gave one worth quoting. */
|
|
117
|
+
reason: string | undefined;
|
|
118
|
+
owner: string;
|
|
119
|
+
name: string;
|
|
120
|
+
/** stored in Pullfrog (console / `pullfrog auth`) rather than a GitHub Actions secret. */
|
|
121
|
+
inPullfrogStore: boolean;
|
|
122
|
+
}): string;
|
|
78
123
|
/**
|
|
79
124
|
* Friendly Markdown summary for both the missing-key and invalid-key cases.
|
|
80
125
|
* Used in the catch / result-failure paths in `main.ts` to overwrite the raw
|
|
@@ -18,10 +18,10 @@ export interface BuildPullfrogFooterParams {
|
|
|
18
18
|
/** model slug from payload (e.g., "anthropic/claude-opus"). shown in footer as "Using `Model Name`" */
|
|
19
19
|
model?: string | undefined;
|
|
20
20
|
/**
|
|
21
|
-
* When
|
|
22
|
-
* had configured (e.g. "anthropic/claude-opus") — the footer
|
|
23
|
-
* `Using <
|
|
24
|
-
* so the substitution is visible in PR comments + reviews.
|
|
21
|
+
* When a credential was rejected and the run moved to another model, this is
|
|
22
|
+
* the slug the user had configured (e.g. "anthropic/claude-opus") — the footer
|
|
23
|
+
* renders `Using <model> (credentials for <configured> were rejected by the
|
|
24
|
+
* provider)` so the substitution is visible in PR comments + reviews.
|
|
25
25
|
*/
|
|
26
26
|
fallbackFrom?: string | undefined;
|
|
27
27
|
/**
|
|
@@ -1,7 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `status` is the HTTP status Anthropic answered with, or `undefined` when we
|
|
3
|
+
* never got an answer at all (network failure / timeout). Callers that need to
|
|
4
|
+
* tell "the credential is dead" from "we couldn't ask" — the paste-time check
|
|
5
|
+
* in `credentialCheck.ts` — read it; `usable` alone conflates the two, because
|
|
6
|
+
* fail-open makes both `usable: true`.
|
|
7
|
+
*/
|
|
1
8
|
export type SubscriptionPreflight = {
|
|
2
9
|
usable: true;
|
|
10
|
+
status: number | undefined;
|
|
3
11
|
} | {
|
|
4
12
|
usable: false;
|
|
13
|
+
status: number;
|
|
5
14
|
reason: string;
|
|
6
15
|
};
|
|
7
16
|
/**
|
|
@@ -18,6 +18,17 @@ export interface CodexAuthBody {
|
|
|
18
18
|
account_id?: string;
|
|
19
19
|
};
|
|
20
20
|
last_refresh?: string;
|
|
21
|
+
/**
|
|
22
|
+
* ISO timestamp of an `invalid_grant` rejection. OpenAI rotates the refresh
|
|
23
|
+
* token on every use, so a rejection is PERMANENT — without a latch the
|
|
24
|
+
* server re-issued the identical doomed refresh on every run (455 futile
|
|
25
|
+
* round trips in 7 days, one per run, each holding a Postgres row lock across
|
|
26
|
+
* a 10s external call). Cleared implicitly: `pullfrog auth codex` and
|
|
27
|
+
* `PUT /api/runtime/secret` write a fresh blob without it, which re-arms
|
|
28
|
+
* rotation. Never reaches `auth.json` — `installCodexAuth` builds that file
|
|
29
|
+
* from explicit fields. See [#1101](https://github.com/pullfrog/app/issues/1101).
|
|
30
|
+
*/
|
|
31
|
+
refresh_rejected_at?: string;
|
|
21
32
|
}
|
|
22
33
|
/** OAuth client id Codex CLI and OpenCode both use against `auth.openai.com`.
|
|
23
34
|
* Same chain — a refresh token minted via `codex login --device-auth` can be
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What the provider itself says about a credential. `unknown` is the safe
|
|
3
|
+
* default and covers every outcome that isn't the provider explicitly
|
|
4
|
+
* rejecting it — no probe for this env var, a network failure, a timeout, a
|
|
5
|
+
* 404, a 5xx. Callers must treat `unknown` as "carry on as before": only
|
|
6
|
+
* `dead` is positive evidence, and acting on anything weaker would let a
|
|
7
|
+
* provider outage rewrite a working account's configuration.
|
|
8
|
+
*/
|
|
9
|
+
export type CredentialVerdict = "alive" | "dead" | "unknown";
|
|
10
|
+
/** whether asking the provider about this credential would tell us anything. */
|
|
11
|
+
export declare function isCredentialProbeable(envVar: string): boolean;
|
|
12
|
+
/**
|
|
13
|
+
* Ask the provider whether it still accepts this credential. Used at the two
|
|
14
|
+
* points that were each getting it wrong on their own: the run-time fallback
|
|
15
|
+
* decision, and the console/CLI paste flow that previously stored a dead token
|
|
16
|
+
* without comment.
|
|
17
|
+
*/
|
|
18
|
+
export declare function verifyCredential(params: {
|
|
19
|
+
envVar: string;
|
|
20
|
+
value: string;
|
|
21
|
+
}): Promise<CredentialVerdict>;
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `ok` covers "the credential works", "we couldn't tell", and "something else
|
|
3
|
+
* on this model still works" — the run proceeds as it did before this check
|
|
4
|
+
* existed. The other two are only reached when every credential the configured
|
|
5
|
+
* model could use has been explicitly rejected by its own provider.
|
|
6
|
+
*
|
|
7
|
+
* `replacement` is a concrete model, never a "just auto-select something":
|
|
8
|
+
* deciding an alternative exists and picking it have to be the SAME decision,
|
|
9
|
+
* or the picker can land back on the provider that just failed.
|
|
10
|
+
*/
|
|
11
|
+
export type CredentialOutcome = {
|
|
12
|
+
kind: "ok";
|
|
13
|
+
} | {
|
|
14
|
+
kind: "fellBack";
|
|
15
|
+
credential: string;
|
|
16
|
+
reason: string | undefined;
|
|
17
|
+
replacement: string;
|
|
18
|
+
} | {
|
|
19
|
+
kind: "dead";
|
|
20
|
+
credential: string;
|
|
21
|
+
reason: string | undefined;
|
|
22
|
+
};
|
|
23
|
+
/**
|
|
24
|
+
* Ask the providers whether the credentials behind the configured model still
|
|
25
|
+
* work, BEFORE the agent spawns — so a rejected one becomes either a run on a
|
|
26
|
+
* model that does work or an accurate error, instead of the agent 401ing three
|
|
27
|
+
* seconds in against a "rotate your GitHub Actions secret" CTA naming a place
|
|
28
|
+
* the user never put a key.
|
|
29
|
+
*
|
|
30
|
+
* Failed credentials are deleted from `process.env` only when this run can
|
|
31
|
+
* still proceed without them. Nothing downstream infers anything from that
|
|
32
|
+
* deletion — `claude.ts` runs its own preflight regardless, because this check
|
|
33
|
+
* cannot promise it probed any particular token (see its comment for the three
|
|
34
|
+
* paths that reach it unprobed).
|
|
35
|
+
*
|
|
36
|
+
* Only providers in the `credentialCheck` probe table can be found dead; every
|
|
37
|
+
* other credential is left exactly as it was.
|
|
38
|
+
*/
|
|
39
|
+
export declare function checkConfiguredCredentials(params: {
|
|
40
|
+
model: string | undefined;
|
|
41
|
+
authorized: Set<string>;
|
|
42
|
+
}): Promise<CredentialOutcome>;
|
package/dist/utils/github.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { throttling } from "@octokit/plugin-throttling";
|
|
2
2
|
import { Octokit } from "@octokit/rest";
|
|
3
|
+
import * as yes from "../yes/index.ts";
|
|
3
4
|
/** GitHub Actions OIDC request credentials, stashed before env wipes */
|
|
4
5
|
export interface OidcCredentials {
|
|
5
6
|
requestUrl: string;
|
|
@@ -57,6 +58,18 @@ type AcquireTokenOptions = {
|
|
|
57
58
|
* as transient.
|
|
58
59
|
*/
|
|
59
60
|
export declare function fetchIdTokenFromStash(creds: OidcCredentials): Promise<string>;
|
|
61
|
+
/**
|
|
62
|
+
* One ID token per run for our single audience. `acquireTokenViaOIDC` runs two
|
|
63
|
+
* to four times per run (git token, MCP token, xrepo read token) on top of
|
|
64
|
+
* `resolveRunContextData`'s own mint, and each call used to hit the runner's
|
|
65
|
+
* OIDC endpoint again — back-to-back requests are exactly what provoked the
|
|
66
|
+
* Envoy load-shed (`reset reason: overflow`) that killed 19 runs in 24h, all of
|
|
67
|
+
* them AFTER setup had already succeeded (#1182). The token is valid for
|
|
68
|
+
* minutes, so the TTL costs nothing; failures are never cached, so a caller's
|
|
69
|
+
* own retry still re-mints. Mid-run refreshes take the stashed-credentials path
|
|
70
|
+
* (`opts.oidc`) and deliberately bypass this.
|
|
71
|
+
*/
|
|
72
|
+
export declare const mintIdToken: yes.OpFunction<() => Promise<string>>;
|
|
60
73
|
/**
|
|
61
74
|
* ensure a GitHub token is available in the environment.
|
|
62
75
|
*
|
|
@@ -16,6 +16,8 @@
|
|
|
16
16
|
* value surfaces as a clear "missing key" downstream rather than silently
|
|
17
17
|
* mutating to the empty string.
|
|
18
18
|
*/
|
|
19
|
+
/** C0 controls + DEL — the bytes an HTTP header value cannot carry. */
|
|
20
|
+
export declare function hasControlCharacter(value: string): boolean;
|
|
19
21
|
export declare function sanitizeSecret(key: string, value: string): string | null;
|
|
20
22
|
/**
|
|
21
23
|
* Normalize environment variables to uppercase.
|