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
|
@@ -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.
|
package/dist/utils/payload.d.ts
CHANGED
|
@@ -7,6 +7,7 @@ export declare const JsonPayload: import("arktype/internal/variants/object.ts").
|
|
|
7
7
|
model?: string | undefined;
|
|
8
8
|
modelExplicit?: boolean | undefined;
|
|
9
9
|
effort?: string | number | undefined;
|
|
10
|
+
debug?: boolean | undefined;
|
|
10
11
|
triggerer?: string | undefined;
|
|
11
12
|
baseInstructions?: string | undefined;
|
|
12
13
|
eventInstructions?: string;
|
|
@@ -33,6 +34,7 @@ export declare const Inputs: import("arktype/internal/variants/object.ts").Objec
|
|
|
33
34
|
prompt_file?: string | undefined;
|
|
34
35
|
model?: string | undefined;
|
|
35
36
|
effort?: string | undefined;
|
|
37
|
+
debug?: "disabled" | "enabled" | undefined;
|
|
36
38
|
timeout?: string | undefined;
|
|
37
39
|
push?: "disabled" | "enabled" | "restricted" | undefined;
|
|
38
40
|
shell?: "disabled" | "enabled" | "restricted" | undefined;
|
|
@@ -50,6 +52,7 @@ export declare function resolvePayload(resolvedPromptInput: ResolvedPromptInput,
|
|
|
50
52
|
model: string | undefined;
|
|
51
53
|
modelExplicit: boolean;
|
|
52
54
|
effort: number | undefined;
|
|
55
|
+
debug: true | undefined;
|
|
53
56
|
prompt: string;
|
|
54
57
|
triggerer: string | undefined;
|
|
55
58
|
baseInstructions: string | undefined;
|
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
/** Stable label for the BYOK provider-billing-exhausted classification. */
|
|
2
2
|
export declare const PROVIDER_BILLING_EXHAUSTED_LABEL = "provider billing exhausted";
|
|
3
|
+
/** Stable label for "OpenRouter can route this model nowhere you permit". */
|
|
4
|
+
export declare const PROVIDER_NO_ENDPOINTS_LABEL = "provider no routable endpoints";
|
|
5
|
+
export declare function findAnthropicSpendCap(text: string): {
|
|
6
|
+
regainAt: string | null;
|
|
7
|
+
} | null;
|
|
3
8
|
/**
|
|
4
9
|
* Result of a provider-error scan: the classification label plus a
|
|
5
10
|
* human-readable excerpt centered on the matched line. The excerpt is what
|
|
@@ -22,6 +27,44 @@ export declare function isRouterKeylimitExhaustedError(text: string): boolean;
|
|
|
22
27
|
* is the user's own provider account.
|
|
23
28
|
*/
|
|
24
29
|
export declare function isProviderBillingExhausted(text: string): boolean;
|
|
30
|
+
/**
|
|
31
|
+
* OpenRouter accepted the request and found nowhere to send it: the account's
|
|
32
|
+
* data policy or guardrail settings exclude every provider serving the picked
|
|
33
|
+
* model. Nothing is billed and no credential is at fault — the only fix is the
|
|
34
|
+
* privacy settings page or a different model. See #1164.
|
|
35
|
+
*/
|
|
36
|
+
export declare function isProviderNoRoutableEndpoints(text: string): boolean;
|
|
37
|
+
/**
|
|
38
|
+
* OpenRouter's per-key ceiling, not an empty wallet. Topping up credits alone
|
|
39
|
+
* will not clear it, so it needs a different headline and a second lever than
|
|
40
|
+
* the generic billing-exhausted copy. Both shapes classify as
|
|
41
|
+
* `PROVIDER_BILLING_EXHAUSTED_LABEL`; this narrows within that class.
|
|
42
|
+
*
|
|
43
|
+
* TWO wire forms, and the obvious one is the rarer one. #1071's `Key limit
|
|
44
|
+
* exceeded (total limit)` names itself; #1164's is the `requires more credits,
|
|
45
|
+
* or fewer max_tokens` form, which is indistinguishable from a drained wallet
|
|
46
|
+
* except for the remedy the provider appends — a link to that key's own page.
|
|
47
|
+
* Anchor on the `/keys/<id>` URL rather than the "adjust the key's total limit"
|
|
48
|
+
* sentence, because the apostrophe in it arrives from provider JSON and may be
|
|
49
|
+
* typographic. A genuinely empty wallet (`Insufficient credits. Add more using
|
|
50
|
+
* …/settings/credits`) carries no `/keys/` path and correctly stays out.
|
|
51
|
+
*/
|
|
52
|
+
export declare function isOpenRouterKeyLimitExceeded(text: string): boolean;
|
|
53
|
+
/**
|
|
54
|
+
* The upstream is having a moment: Anthropic's `API Error: 529 Overloaded`,
|
|
55
|
+
* OpenRouter's `provider_unavailable` / `timeout`, OpenCode Zen's `Streaming
|
|
56
|
+
* response failed: [5xx]`. Three transports, one condition — nothing the user
|
|
57
|
+
* configured is wrong and the next run will probably work, which is exactly
|
|
58
|
+
* what the contentless `Run failed.` comment failed to say across 28 runs that
|
|
59
|
+
* had already done up to 38 tool calls of real work (#1173).
|
|
60
|
+
*
|
|
61
|
+
* `error_type` is matched as the machine-readable FIELD it is rather than by
|
|
62
|
+
* substring-matching the human prose around it, which varies per upstream.
|
|
63
|
+
* Deliberately narrow on 5xx so it cannot reach a 401/402 that
|
|
64
|
+
* `isApiKeyAuthError` / `isProviderBillingExhausted` own — and `renderRunError`
|
|
65
|
+
* orders it after both regardless.
|
|
66
|
+
*/
|
|
67
|
+
export declare function isTransientUpstreamError(text: string): boolean;
|
|
25
68
|
/**
|
|
26
69
|
* Extract `providerID=foo` from agent error logs (OpenCode emits this on
|
|
27
70
|
* `provider error detected (...)` lines). Returns the lowercase provider
|
package/dist/utils/proxy.d.ts
CHANGED
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
* exist yet at this point in the pipeline.
|
|
16
16
|
*
|
|
17
17
|
* - 402 → `BillingError` (card declined, balance empty, 3DS, etc.)
|
|
18
|
-
* -
|
|
18
|
+
* - 5xx → `TransientError` (the mint is down — retried in-run first)
|
|
19
19
|
* - 404 → `TransientError` (stale repo↔account link — re-homes on next webhook)
|
|
20
20
|
*/
|
|
21
21
|
import type { ToolState } from "../toolState.ts";
|
|
@@ -76,6 +76,16 @@ export interface RunContext {
|
|
|
76
76
|
* distinction a transient failure renders as "you have no API key".
|
|
77
77
|
*/
|
|
78
78
|
secretsUnavailable?: boolean | undefined;
|
|
79
|
+
/**
|
|
80
|
+
* the Router was this account's funding path and its wallet is empty, so the
|
|
81
|
+
* server declined the mint rather than 402ing — the run falls through to
|
|
82
|
+
* BYOK, which is the documented affordance for a router-mode account whose
|
|
83
|
+
* key lives in workflow `env:`. only meaningful when the key search then
|
|
84
|
+
* comes up dry, where it turns "go add an API key" into copy that also names
|
|
85
|
+
* topping up. defaults false: an unreachable server must not assert a
|
|
86
|
+
* funding state. see wiki/billing.md.
|
|
87
|
+
*/
|
|
88
|
+
routerUnfunded?: boolean | undefined;
|
|
79
89
|
}
|
|
80
90
|
/**
|
|
81
91
|
* fetch run context from Pullfrog API
|
|
@@ -17,6 +17,9 @@ export interface RunContextData {
|
|
|
17
17
|
/** stored secrets couldn't be materialized for this run — not the same as
|
|
18
18
|
* the user having none. see `RunContext.secretsUnavailable`. */
|
|
19
19
|
secretsUnavailable?: boolean | undefined;
|
|
20
|
+
/** the Router was declined because the wallet is empty. see
|
|
21
|
+
* `RunContext.routerUnfunded`. */
|
|
22
|
+
routerUnfunded?: boolean | undefined;
|
|
20
23
|
}
|
|
21
24
|
interface ResolveRunContextDataParams {
|
|
22
25
|
octokit: OctokitWithPlugins;
|
|
@@ -35,6 +35,12 @@
|
|
|
35
35
|
* `maximum context length is N tokens`. Actionable, so it renders on
|
|
36
36
|
* both surfaces rather than collapsing to the one-line comment.
|
|
37
37
|
*
|
|
38
|
+
* 4c. Transient upstream failure (#1173) — Anthropic `529 Overloaded`,
|
|
39
|
+
* OpenRouter `provider_unavailable` / `timeout`, Zen `Streaming response
|
|
40
|
+
* failed: [5xx]`. Last of the classified branches so every more specific
|
|
41
|
+
* one wins first. Renders on both surfaces: the remedy is re-triggering,
|
|
42
|
+
* which the user can only know if we say so.
|
|
43
|
+
*
|
|
38
44
|
* 5. Activity-timeout hang — `errorMessage` starts with
|
|
39
45
|
* `"activity timeout"` or `"agent still pending"` AND none of the
|
|
40
46
|
* above matched. The harness keeps structured diagnostic state on
|
|
@@ -52,8 +58,8 @@
|
|
|
52
58
|
* the hang case, since the raw internal string helps nobody on the PR.
|
|
53
59
|
*
|
|
54
60
|
* Net: the actionable classifications (billing, API-key, model-not-found,
|
|
55
|
-
* no-provider-available, context-overflow) render identical
|
|
56
|
-
* surfaces; the non-actionable ones (hang,
|
|
61
|
+
* no-provider-available, context-overflow, transient-upstream) render identical
|
|
62
|
+
* bodies on both surfaces; the non-actionable ones (hang,
|
|
57
63
|
* generic) keep the forensics in the Actions job summary and show a calm
|
|
58
64
|
* one-liner in the PR comment, whose footer already carries Pullfrog
|
|
59
65
|
* branding + rerun links.
|
|
@@ -35,11 +35,18 @@ export declare const RUN_STATUS_CHECK_NAME = "pullfrog";
|
|
|
35
35
|
/** the review-verdict check. opt-in, terminal-only, and deliberately separate from the above. */
|
|
36
36
|
export declare const APPROVAL_CHECK_NAME = "pullfrog-approval";
|
|
37
37
|
/**
|
|
38
|
-
* The terminal states we report
|
|
39
|
-
*
|
|
40
|
-
*
|
|
38
|
+
* The terminal states we report: exactly GitHub's check-run `conclusion` enum.
|
|
39
|
+
*
|
|
40
|
+
* NOT `WorkflowRunStatus`. `stale` is a valid `workflow_run.conclusion` and is
|
|
41
|
+
* NOT a member of the Checks API enum, which GitHub's own 422 enumerates as
|
|
42
|
+
* `["success", "failure", "neutral", "cancelled", "timed_out",
|
|
43
|
+
* "action_required", "skipped"]`. Admitting it here let
|
|
44
|
+
* `checkConclusionFromStatus` type-check while producing a value the wire
|
|
45
|
+
* always rejects, so 100% of reaper-expired rows failed close-out — and since
|
|
46
|
+
* the failure left `checkRunId` set, the hourly sweep re-sent the same illegal
|
|
47
|
+
* conclusion forever. See [#1178](https://github.com/pullfrog/app/issues/1178).
|
|
41
48
|
*/
|
|
42
|
-
export type RunStatusCheckConclusion = "success" | "failure" | "cancelled" | "timed_out" | "action_required" | "neutral" | "skipped"
|
|
49
|
+
export type RunStatusCheckConclusion = "success" | "failure" | "cancelled" | "timed_out" | "action_required" | "neutral" | "skipped";
|
|
43
50
|
/**
|
|
44
51
|
* Parse the on-the-wire `{ id: string }` shape (the form carried in `JsonPayload`) into
|
|
45
52
|
* a check-run id. Mirrors `parseProgressComment` — returns undefined when the id isn't a
|
|
@@ -101,6 +108,9 @@ export interface RunStatusCheckOctokit {
|
|
|
101
108
|
data: {
|
|
102
109
|
check_runs: {
|
|
103
110
|
id: number;
|
|
111
|
+
app?: {
|
|
112
|
+
slug?: string | null;
|
|
113
|
+
} | null;
|
|
104
114
|
}[];
|
|
105
115
|
};
|
|
106
116
|
}>;
|