pullfrog 0.1.52 → 0.1.54
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 +59270 -30564
- package/dist/external.d.ts +9 -1
- package/dist/index.js +54454 -25979
- package/dist/internal/index.d.ts +3 -1
- package/dist/internal.js +166 -42
- 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/mcp/shell.d.ts +1 -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/payload.d.ts +3 -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
|
@@ -207,9 +207,9 @@ var providers = {
|
|
|
207
207
|
},
|
|
208
208
|
"gemini-flash": {
|
|
209
209
|
displayName: "Gemini Flash",
|
|
210
|
-
resolve: "google/gemini-3.
|
|
210
|
+
resolve: "google/gemini-3.6-flash",
|
|
211
211
|
effort: ["minimal", "low", "medium", "high"],
|
|
212
|
-
openRouterResolve: "openrouter/google/gemini-3.
|
|
212
|
+
openRouterResolve: "openrouter/google/gemini-3.6-flash"
|
|
213
213
|
}
|
|
214
214
|
}
|
|
215
215
|
}),
|
|
@@ -219,26 +219,26 @@ var providers = {
|
|
|
219
219
|
models: {
|
|
220
220
|
grok: {
|
|
221
221
|
displayName: "Grok",
|
|
222
|
-
resolve: "xai/grok-4.
|
|
223
|
-
effort: ["
|
|
224
|
-
openRouterResolve: "openrouter/x-ai/grok-4.
|
|
222
|
+
resolve: "xai/grok-4.5",
|
|
223
|
+
effort: ["low", "medium", "high"],
|
|
224
|
+
openRouterResolve: "openrouter/x-ai/grok-4.5",
|
|
225
225
|
preferred: true
|
|
226
226
|
},
|
|
227
227
|
// legacy aliases — xAI retired the entire fast/code-fast line on
|
|
228
228
|
// 2026-05-15 (https://docs.x.ai/developers/migration/may-15-deprecation)
|
|
229
229
|
// and now redirects every deprecated text-model slug to grok-4.3 at
|
|
230
230
|
// standard pricing. fall back to the live `xai/grok` so the alias
|
|
231
|
-
// chain resolves to grok-4.
|
|
231
|
+
// chain resolves to grok-4.5 for both direct-key and OpenRouter users.
|
|
232
232
|
"grok-fast": {
|
|
233
233
|
displayName: "Grok Fast",
|
|
234
234
|
resolve: "xai/grok-4-1-fast",
|
|
235
|
-
openRouterResolve: "openrouter/x-ai/grok-4.
|
|
235
|
+
openRouterResolve: "openrouter/x-ai/grok-4.5",
|
|
236
236
|
fallback: "xai/grok"
|
|
237
237
|
},
|
|
238
238
|
"grok-code-fast": {
|
|
239
239
|
displayName: "Grok Code Fast",
|
|
240
240
|
resolve: "xai/grok-code-fast-1",
|
|
241
|
-
openRouterResolve: "openrouter/x-ai/grok-4.
|
|
241
|
+
openRouterResolve: "openrouter/x-ai/grok-4.5",
|
|
242
242
|
fallback: "xai/grok"
|
|
243
243
|
}
|
|
244
244
|
}
|
|
@@ -255,20 +255,25 @@ var providers = {
|
|
|
255
255
|
openRouterResolve: "openrouter/deepseek/deepseek-v4-pro",
|
|
256
256
|
preferred: true
|
|
257
257
|
},
|
|
258
|
-
// DeepSeek
|
|
259
|
-
//
|
|
260
|
-
// left the April preview live
|
|
261
|
-
//
|
|
262
|
-
//
|
|
263
|
-
//
|
|
264
|
-
//
|
|
258
|
+
// DeepSeek upgrades the `deepseek-v4-flash` API model in place, so the
|
|
259
|
+
// direct `resolve` needs no version. OpenRouter instead forked the 0731
|
|
260
|
+
// release into its own id and left the April preview live under the
|
|
261
|
+
// unversioned one at a HIGHER price ($0.14/$0.28 vs $0.09/$0.18) — so the
|
|
262
|
+
// OpenRouter route can never use the bare id. it tracks the rolling
|
|
263
|
+
// `~deepseek/*-latest` pointer (rule 4), which resolves to 0731 at the
|
|
264
|
+
// same price today and follows the next in-place upgrade on its own,
|
|
265
|
+
// rather than pinning a dated snapshot that goes stale every release.
|
|
266
|
+
// 0731 gained a `low` rung and renamed the top to `max`, unlike Pro
|
|
267
|
+
// (`high, max` direct / `high, xhigh` via OpenRouter). that ladder now
|
|
268
|
+
// reads the same on BOTH routes — the in-place direct upgrade caught up
|
|
269
|
+
// to the fork — so `low`, not `high`, is position 0 either way, which is
|
|
265
270
|
// why the OSS effort floor in `main.ts` pins the `high` rung by name.
|
|
266
271
|
"deepseek-flash": {
|
|
267
272
|
displayName: "DeepSeek Flash",
|
|
268
273
|
resolve: "deepseek/deepseek-v4-flash",
|
|
269
|
-
effort: ["high", "max"],
|
|
274
|
+
effort: ["low", "high", "max"],
|
|
270
275
|
openRouterEffort: ["low", "high", "max"],
|
|
271
|
-
openRouterResolve: "openrouter
|
|
276
|
+
openRouterResolve: "openrouter/~deepseek/deepseek-v4-flash-latest"
|
|
272
277
|
},
|
|
273
278
|
// legacy aliases — deepseek retires these on 2026-07-24; transparently
|
|
274
279
|
// upgrade existing users to the v4 family via the fallback chain.
|
|
@@ -316,7 +321,12 @@ var providers = {
|
|
|
316
321
|
displayName: "Big Pickle",
|
|
317
322
|
resolve: "opencode/big-pickle",
|
|
318
323
|
preferred: true,
|
|
319
|
-
|
|
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.
|
|
320
330
|
isFree: true
|
|
321
331
|
},
|
|
322
332
|
"claude-opus": {
|
|
@@ -414,21 +424,19 @@ var providers = {
|
|
|
414
424
|
},
|
|
415
425
|
"gemini-flash": {
|
|
416
426
|
displayName: "Gemini Flash",
|
|
417
|
-
resolve: "opencode/gemini-3.
|
|
427
|
+
resolve: "opencode/gemini-3.6-flash",
|
|
418
428
|
effort: ["minimal", "low", "medium", "high"],
|
|
419
|
-
openRouterResolve: "openrouter/google/gemini-3.
|
|
429
|
+
openRouterResolve: "openrouter/google/gemini-3.6-flash"
|
|
420
430
|
},
|
|
421
431
|
"kimi-k2": {
|
|
422
432
|
displayName: "Kimi K2",
|
|
423
|
-
|
|
424
|
-
// router/oss proxy runs) takes the newer k2.7-code.
|
|
425
|
-
resolve: "opencode/kimi-k2.6",
|
|
433
|
+
resolve: "opencode/kimi-k2.7-code",
|
|
426
434
|
openRouterResolve: "openrouter/moonshotai/kimi-k2.7-code"
|
|
427
435
|
},
|
|
428
|
-
//
|
|
436
|
+
// slug pins the m2 line for DB stability; resolve tracks the current m2.7.
|
|
429
437
|
"minimax-m2.5": {
|
|
430
438
|
displayName: "MiniMax M2",
|
|
431
|
-
resolve: "opencode/minimax-m2.
|
|
439
|
+
resolve: "opencode/minimax-m2.7",
|
|
432
440
|
openRouterResolve: "openrouter/minimax/minimax-m2.7"
|
|
433
441
|
},
|
|
434
442
|
"gpt-5-nano": {
|
|
@@ -440,14 +448,16 @@ var providers = {
|
|
|
440
448
|
"mimo-v2-pro-free": {
|
|
441
449
|
displayName: "MiMo V2 Pro",
|
|
442
450
|
resolve: "opencode/mimo-v2-pro-free",
|
|
443
|
-
|
|
451
|
+
// free to run, still gated on the provider's own OPENCODE_API_KEY —
|
|
452
|
+
// see the big-pickle note above (#1077).
|
|
444
453
|
isFree: true,
|
|
445
454
|
fallback: "opencode/big-pickle"
|
|
446
455
|
},
|
|
447
456
|
"minimax-m2.5-free": {
|
|
448
457
|
displayName: "MiniMax M2",
|
|
449
458
|
resolve: "opencode/minimax-m2.5-free",
|
|
450
|
-
|
|
459
|
+
// free to run, still gated on the provider's own OPENCODE_API_KEY —
|
|
460
|
+
// see the big-pickle note above (#1077).
|
|
451
461
|
isFree: true,
|
|
452
462
|
fallback: "opencode/big-pickle",
|
|
453
463
|
hidden: true
|
|
@@ -643,14 +653,18 @@ var providers = {
|
|
|
643
653
|
"gemini-flash": {
|
|
644
654
|
displayName: "Gemini Flash",
|
|
645
655
|
resolve: "openrouter/~google/gemini-flash-latest",
|
|
646
|
-
|
|
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"],
|
|
647
661
|
openRouterResolve: "openrouter/~google/gemini-flash-latest"
|
|
648
662
|
},
|
|
649
663
|
grok: {
|
|
650
664
|
displayName: "Grok",
|
|
651
|
-
resolve: "openrouter/x-ai/grok-4.
|
|
652
|
-
effort: ["
|
|
653
|
-
openRouterResolve: "openrouter/x-ai/grok-4.
|
|
665
|
+
resolve: "openrouter/x-ai/grok-4.5",
|
|
666
|
+
effort: ["low", "medium", "high"],
|
|
667
|
+
openRouterResolve: "openrouter/x-ai/grok-4.5"
|
|
654
668
|
},
|
|
655
669
|
"deepseek-pro": {
|
|
656
670
|
displayName: "DeepSeek Pro",
|
|
@@ -658,11 +672,13 @@ var providers = {
|
|
|
658
672
|
effort: ["high", "xhigh"],
|
|
659
673
|
openRouterResolve: "openrouter/deepseek/deepseek-v4-pro"
|
|
660
674
|
},
|
|
675
|
+
// rolling pointer, not the bare id — see `deepseek/deepseek-flash` for why
|
|
676
|
+
// OpenRouter's unversioned `deepseek-v4-flash` is the stale April preview.
|
|
661
677
|
"deepseek-flash": {
|
|
662
678
|
displayName: "DeepSeek Flash",
|
|
663
|
-
resolve: "openrouter
|
|
679
|
+
resolve: "openrouter/~deepseek/deepseek-v4-flash-latest",
|
|
664
680
|
effort: ["low", "high", "max"],
|
|
665
|
-
openRouterResolve: "openrouter
|
|
681
|
+
openRouterResolve: "openrouter/~deepseek/deepseek-v4-flash-latest"
|
|
666
682
|
},
|
|
667
683
|
// legacy alias — deepseek retires this on 2026-07-24; transparently
|
|
668
684
|
// upgrade existing users to the v4 family via the fallback chain.
|
|
@@ -817,10 +833,12 @@ var OSS_MODEL_ALLOWLIST = [
|
|
|
817
833
|
"deepseek/deepseek-flash",
|
|
818
834
|
"deepseek/deepseek-pro",
|
|
819
835
|
"openai/gpt-luna",
|
|
820
|
-
"openrouter/minimax-m2.5"
|
|
821
|
-
"moonshotai/kimi-k2"
|
|
836
|
+
"openrouter/minimax-m2.5"
|
|
822
837
|
];
|
|
823
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
|
+
}
|
|
824
842
|
var ossAllowedTargets = new Set(
|
|
825
843
|
OSS_MODEL_ALLOWLIST.map((slug) => {
|
|
826
844
|
const target = resolveOpenRouterModel(slug);
|
|
@@ -1290,7 +1308,7 @@ function formatModelLabel(params) {
|
|
|
1290
1308
|
}
|
|
1291
1309
|
const base = alias?.isFree ? `\`${displayName}\` (free)` : `\`${displayName}\``;
|
|
1292
1310
|
if (params.fallbackFrom) {
|
|
1293
|
-
return `${base} (credentials for ${providerDisplayName(params.fallbackFrom)}
|
|
1311
|
+
return `${base} (credentials for ${providerDisplayName(params.fallbackFrom)} were rejected by the provider)`;
|
|
1294
1312
|
}
|
|
1295
1313
|
if (params.clamped) {
|
|
1296
1314
|
const target = isAutoTier(params.clamped.from) ? "the intelligent tier" : `\`${resolveDisplayAlias(params.clamped.from)?.displayName ?? params.clamped.from}\``;
|
|
@@ -1418,6 +1436,7 @@ function parseCodexAuthBody(raw) {
|
|
|
1418
1436
|
if (typeof t.refresh_token !== "string" || t.refresh_token.length === 0) return null;
|
|
1419
1437
|
return {
|
|
1420
1438
|
auth_mode: "chatgpt",
|
|
1439
|
+
...typeof v.refresh_rejected_at === "string" ? { refresh_rejected_at: v.refresh_rejected_at } : {},
|
|
1421
1440
|
tokens: {
|
|
1422
1441
|
access_token: t.access_token,
|
|
1423
1442
|
refresh_token: t.refresh_token,
|
|
@@ -1432,6 +1451,109 @@ function stringifyCodexAuthBody(body) {
|
|
|
1432
1451
|
`;
|
|
1433
1452
|
}
|
|
1434
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
|
+
|
|
1435
1557
|
// utils/isPullfrog.ts
|
|
1436
1558
|
function isPullfrog(actor) {
|
|
1437
1559
|
actor = actor?.toLowerCase().replace("[bot]", "");
|
|
@@ -1557,6 +1679,7 @@ async function createLeapingProgressComment(ctx, target, body) {
|
|
|
1557
1679
|
|
|
1558
1680
|
// utils/runStatusCheck.ts
|
|
1559
1681
|
var RUN_STATUS_CHECK_NAME = "pullfrog";
|
|
1682
|
+
var GITHUB_ACTIONS_APP_SLUG = "github-actions";
|
|
1560
1683
|
var APPROVAL_CHECK_NAME = "pullfrog-approval";
|
|
1561
1684
|
var IN_PROGRESS_OUTPUT = {
|
|
1562
1685
|
title: "Pullfrog is running",
|
|
@@ -1596,10 +1719,6 @@ var TERMINAL_OUTPUT = {
|
|
|
1596
1719
|
skipped: {
|
|
1597
1720
|
title: "Pullfrog run skipped",
|
|
1598
1721
|
summary: "This run was superseded by another Pullfrog run."
|
|
1599
|
-
},
|
|
1600
|
-
stale: {
|
|
1601
|
-
title: "Pullfrog run didn't finish",
|
|
1602
|
-
summary: "Pullfrog never received a completion signal for this run. See the run logs for details."
|
|
1603
1722
|
}
|
|
1604
1723
|
};
|
|
1605
1724
|
function terminalOutput(params) {
|
|
@@ -1618,7 +1737,9 @@ async function createRunStatusCheck(params) {
|
|
|
1618
1737
|
check_name: RUN_STATUS_CHECK_NAME,
|
|
1619
1738
|
status: "in_progress"
|
|
1620
1739
|
}).catch(() => void 0);
|
|
1621
|
-
const reusable = existing?.data.check_runs
|
|
1740
|
+
const reusable = existing?.data.check_runs.find(
|
|
1741
|
+
(run) => run.app?.slug !== GITHUB_ACTIONS_APP_SLUG
|
|
1742
|
+
);
|
|
1622
1743
|
if (reusable) return reusable.id;
|
|
1623
1744
|
const createParams = {
|
|
1624
1745
|
owner: params.owner,
|
|
@@ -1708,6 +1829,7 @@ export {
|
|
|
1708
1829
|
getProviderDisplayName,
|
|
1709
1830
|
isAutoTier,
|
|
1710
1831
|
isCardGatedModel,
|
|
1832
|
+
isCredentialProbeable,
|
|
1711
1833
|
isEffortPosition,
|
|
1712
1834
|
isLeapingIntoActionCommentBody,
|
|
1713
1835
|
isOssAllowedModel,
|
|
@@ -1717,6 +1839,7 @@ export {
|
|
|
1717
1839
|
modelHasStoredAuth,
|
|
1718
1840
|
modes,
|
|
1719
1841
|
offeredRungs,
|
|
1842
|
+
ossFundedModelNames,
|
|
1720
1843
|
parseCodexAuthBody,
|
|
1721
1844
|
parseEffortPosition,
|
|
1722
1845
|
parseModel,
|
|
@@ -1737,5 +1860,6 @@ export {
|
|
|
1737
1860
|
stringifyCodexAuthBody,
|
|
1738
1861
|
stripExistingFooter,
|
|
1739
1862
|
truncateAtLineBoundary,
|
|
1740
|
-
updateProgressComment
|
|
1863
|
+
updateProgressComment,
|
|
1864
|
+
verifyCredential
|
|
1741
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/mcp/shell.d.ts
CHANGED
|
@@ -6,7 +6,7 @@ export declare const ShellParams: import("arktype/internal/variants/object.ts").
|
|
|
6
6
|
working_directory?: string;
|
|
7
7
|
background?: boolean;
|
|
8
8
|
}, {}>;
|
|
9
|
-
export type SandboxMethod = "unshare" | "sudo-unshare" | "none";
|
|
9
|
+
export type SandboxMethod = "unshare" | "sudo-unshare" | "userns-unshare" | "none";
|
|
10
10
|
/** get the current sandbox method (for testing/diagnostics) */
|
|
11
11
|
export declare function getSandboxMethod(): SandboxMethod;
|
|
12
12
|
/** chars of shell output kept inline in the agent reply. anything past this
|
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
|
/**
|