pullfrog 0.1.39 → 0.1.40

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.mjs CHANGED
@@ -102494,7 +102494,7 @@ async function preflightClaudeSubscription(params) {
102494
102494
  } catch {
102495
102495
  return { usable: true };
102496
102496
  }
102497
- if (res.status !== 401 && res.status !== 429) return { usable: true };
102497
+ if (res.status !== 401 && res.status !== 403 && res.status !== 429) return { usable: true };
102498
102498
  const body = await res.text().catch(() => "");
102499
102499
  return { usable: false, reason: `${res.status}: ${extractApiErrorMessage(body)}` };
102500
102500
  }
@@ -102997,7 +102997,7 @@ function extractExcerpt(text, matchIndex) {
102997
102997
  }
102998
102998
  return excerpt.trim();
102999
102999
  }
103000
- var ROUTER_KEYLIMIT_EXHAUSTED_PATTERN = /requires more credits.*?fewer max_tokens|requested up to \d+ tokens.*?can only afford/is;
103000
+ var ROUTER_KEYLIMIT_EXHAUSTED_PATTERN = /requires more credits.*?fewer max_tokens|requested up to \d+ tokens.*?can only afford|Key limit exceeded \(total limit\)/is;
103001
103001
  function isRouterKeylimitExhaustedError(text) {
103002
103002
  return ROUTER_KEYLIMIT_EXHAUSTED_PATTERN.test(text);
103003
103003
  }
@@ -103022,7 +103022,7 @@ var import_semver = __toESM(require_semver2(), 1);
103022
103022
  // package.json
103023
103023
  var package_default = {
103024
103024
  name: "pullfrog",
103025
- version: "0.1.39",
103025
+ version: "0.1.40",
103026
103026
  type: "module",
103027
103027
  bin: {
103028
103028
  pullfrog: "dist/cli.mjs",
@@ -110466,7 +110466,6 @@ function newTurn() {
110466
110466
  finalText: "",
110467
110467
  tokens: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
110468
110468
  costUsd: 0,
110469
- sessionError: null,
110470
110469
  lastToolError: null
110471
110470
  };
110472
110471
  }
@@ -110497,7 +110496,6 @@ async function dispatchEvent(ctx, event) {
110497
110496
  if (sessionID !== ctx.orchestratorSessionID) return;
110498
110497
  const err = event.properties.error;
110499
110498
  const message = err ? extractErrorMessage(err) : "(no error payload)";
110500
- if (ctx.currentTurn) ctx.currentTurn.sessionError = message;
110501
110499
  log.info(`\xBB ${ctx.label} session error: ${message}`);
110502
110500
  return;
110503
110501
  }
@@ -110690,14 +110688,6 @@ async function runPromptTurn(ctx, params) {
110690
110688
  usage
110691
110689
  };
110692
110690
  }
110693
- if (turn.sessionError) {
110694
- return {
110695
- success: false,
110696
- output: finalText,
110697
- error: `session error: ${turn.sessionError}`,
110698
- usage
110699
- };
110700
- }
110701
110691
  return { success: true, output: finalText, usage };
110702
110692
  }
110703
110693
  async function aggregateTurnUsage(ctx, turnStartMs) {
@@ -164715,10 +164705,13 @@ function validateAgentApiKey(params) {
164715
164705
  }
164716
164706
  function isApiKeyAuthError(text) {
164717
164707
  if (!text) return false;
164718
- return text.includes(MISSING_KEY_MARKER) || /Invalid API key/i.test(text) || /\bUser not found\b/i.test(text) || /\bInvalid authentication\b/i.test(text) || /authentication_error/i.test(text) || /Invalid bearer token/i.test(text) || /api_error_status\s*=\s*401/i.test(text) || /API Error:\s*401/i.test(text) || /Failed to authenticate\. API Error:/i.test(text) || /Your api key:.*is invalid/i.test(text) || isOAuthCredentialExpiredError(text);
164708
+ return text.includes(MISSING_KEY_MARKER) || /Invalid API key/i.test(text) || /\bUser not found\b/i.test(text) || /\bInvalid authentication\b/i.test(text) || /authentication_error/i.test(text) || /Invalid bearer token/i.test(text) || /api_error_status\s*=\s*401/i.test(text) || /API Error:\s*401/i.test(text) || /Failed to authenticate\. API Error:/i.test(text) || /Your api key:.*is invalid/i.test(text) || isClaudeSubscriptionDisabledError(text) || isOAuthCredentialExpiredError(text);
164719
164709
  }
164720
164710
  function isOAuthCredentialExpiredError(text) {
164721
- return /authentication token has expired/i.test(text) || /Token refresh failed/i.test(text);
164711
+ return /authentication token has (expired|been invalidated)/i.test(text) || /OAuth access token has expired/i.test(text) || /Token refresh failed/i.test(text);
164712
+ }
164713
+ function isClaudeSubscriptionDisabledError(text) {
164714
+ return /disabled Claude subscription access/i.test(text);
164722
164715
  }
164723
164716
  function formatApiKeyErrorSummary(params) {
164724
164717
  if (params.raw.includes(MISSING_KEY_MARKER)) {
@@ -164727,11 +164720,18 @@ function formatApiKeyErrorSummary(params) {
164727
164720
  }
164728
164721
  const githubSecretsUrl = `https://github.com/${params.owner}/${params.name}/settings/secrets/actions`;
164729
164722
  const settingsUrl = `${getApiUrl()}/console/${params.owner}/${params.name}`;
164723
+ if (isClaudeSubscriptionDisabledError(params.raw)) {
164724
+ return [
164725
+ `**Your organization has disabled Claude subscription access for Claude Code.** Ask your Claude organization's admin to re-enable it in the Claude Console, or set an \`ANTHROPIC_API_KEY\` for this repo instead, then re-trigger the run.`,
164726
+ "",
164727
+ `[Add repo secret \u2192](${githubSecretsUrl}) \xB7 [Model settings \u2192](${settingsUrl}) \xB7 [Setup docs \u2192](https://docs.pullfrog.com/keys) \xB7 [Ask in Discord \u2192](https://discord.gg/8y96raFg8e)`
164728
+ ].join("\n");
164729
+ }
164730
164730
  if (isOAuthCredentialExpiredError(params.raw)) {
164731
164731
  return [
164732
- `**Your provider OAuth credential has expired.** Re-authenticate the provider connection (e.g. \`pullfrog auth codex\`), then re-trigger the run.`,
164732
+ `**Your provider OAuth credential has expired or been revoked.** Re-authenticate the provider connection (e.g. \`pullfrog auth claude\` / \`pullfrog auth codex\`), then re-trigger the run.`,
164733
164733
  "",
164734
- `[Model settings \u2192](${settingsUrl}) \xB7 [Setup docs \u2192](https://docs.pullfrog.com/keys) \xB7 [Ask in Discord \u2192](https://discord.gg/8y96raFg8e)`
164734
+ `[Claude subscription \u2192](https://docs.pullfrog.com/claude-auth) \xB7 [ChatGPT subscription \u2192](https://docs.pullfrog.com/codex-auth) \xB7 [Model settings \u2192](${settingsUrl}) \xB7 [Ask in Discord \u2192](https://discord.gg/8y96raFg8e)`
164735
164735
  ].join("\n");
164736
164736
  }
164737
164737
  return [
@@ -168517,7 +168517,7 @@ async function runCli4(input) {
168517
168517
  }
168518
168518
 
168519
168519
  // cli.ts
168520
- var VERSION10 = "0.1.39";
168520
+ var VERSION10 = "0.1.40";
168521
168521
  var bin = basename2(process.argv[1] || "");
168522
168522
  var PROG = bin === "pf" || bin === "pullfrog" ? bin : "pullfrog";
168523
168523
  var rawArgs = process.argv.slice(2);
package/dist/index.js CHANGED
@@ -100565,7 +100565,7 @@ async function preflightClaudeSubscription(params) {
100565
100565
  } catch {
100566
100566
  return { usable: true };
100567
100567
  }
100568
- if (res.status !== 401 && res.status !== 429) return { usable: true };
100568
+ if (res.status !== 401 && res.status !== 403 && res.status !== 429) return { usable: true };
100569
100569
  const body = await res.text().catch(() => "");
100570
100570
  return { usable: false, reason: `${res.status}: ${extractApiErrorMessage(body)}` };
100571
100571
  }
@@ -101068,7 +101068,7 @@ function extractExcerpt(text, matchIndex) {
101068
101068
  }
101069
101069
  return excerpt.trim();
101070
101070
  }
101071
- var ROUTER_KEYLIMIT_EXHAUSTED_PATTERN = /requires more credits.*?fewer max_tokens|requested up to \d+ tokens.*?can only afford/is;
101071
+ var ROUTER_KEYLIMIT_EXHAUSTED_PATTERN = /requires more credits.*?fewer max_tokens|requested up to \d+ tokens.*?can only afford|Key limit exceeded \(total limit\)/is;
101072
101072
  function isRouterKeylimitExhaustedError(text) {
101073
101073
  return ROUTER_KEYLIMIT_EXHAUSTED_PATTERN.test(text);
101074
101074
  }
@@ -101093,7 +101093,7 @@ var import_semver = __toESM(require_semver2(), 1);
101093
101093
  // package.json
101094
101094
  var package_default = {
101095
101095
  name: "pullfrog",
101096
- version: "0.1.39",
101096
+ version: "0.1.40",
101097
101097
  type: "module",
101098
101098
  bin: {
101099
101099
  pullfrog: "dist/cli.mjs",
@@ -108579,7 +108579,6 @@ function newTurn() {
108579
108579
  finalText: "",
108580
108580
  tokens: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
108581
108581
  costUsd: 0,
108582
- sessionError: null,
108583
108582
  lastToolError: null
108584
108583
  };
108585
108584
  }
@@ -108610,7 +108609,6 @@ async function dispatchEvent(ctx, event) {
108610
108609
  if (sessionID !== ctx.orchestratorSessionID) return;
108611
108610
  const err = event.properties.error;
108612
108611
  const message = err ? extractErrorMessage(err) : "(no error payload)";
108613
- if (ctx.currentTurn) ctx.currentTurn.sessionError = message;
108614
108612
  log.info(`\xBB ${ctx.label} session error: ${message}`);
108615
108613
  return;
108616
108614
  }
@@ -108803,14 +108801,6 @@ async function runPromptTurn(ctx, params) {
108803
108801
  usage
108804
108802
  };
108805
108803
  }
108806
- if (turn.sessionError) {
108807
- return {
108808
- success: false,
108809
- output: finalText,
108810
- error: `session error: ${turn.sessionError}`,
108811
- usage
108812
- };
108813
- }
108814
108804
  return { success: true, output: finalText, usage };
108815
108805
  }
108816
108806
  async function aggregateTurnUsage(ctx, turnStartMs) {
@@ -162828,10 +162818,13 @@ function validateAgentApiKey(params) {
162828
162818
  }
162829
162819
  function isApiKeyAuthError(text) {
162830
162820
  if (!text) return false;
162831
- return text.includes(MISSING_KEY_MARKER) || /Invalid API key/i.test(text) || /\bUser not found\b/i.test(text) || /\bInvalid authentication\b/i.test(text) || /authentication_error/i.test(text) || /Invalid bearer token/i.test(text) || /api_error_status\s*=\s*401/i.test(text) || /API Error:\s*401/i.test(text) || /Failed to authenticate\. API Error:/i.test(text) || /Your api key:.*is invalid/i.test(text) || isOAuthCredentialExpiredError(text);
162821
+ return text.includes(MISSING_KEY_MARKER) || /Invalid API key/i.test(text) || /\bUser not found\b/i.test(text) || /\bInvalid authentication\b/i.test(text) || /authentication_error/i.test(text) || /Invalid bearer token/i.test(text) || /api_error_status\s*=\s*401/i.test(text) || /API Error:\s*401/i.test(text) || /Failed to authenticate\. API Error:/i.test(text) || /Your api key:.*is invalid/i.test(text) || isClaudeSubscriptionDisabledError(text) || isOAuthCredentialExpiredError(text);
162832
162822
  }
162833
162823
  function isOAuthCredentialExpiredError(text) {
162834
- return /authentication token has expired/i.test(text) || /Token refresh failed/i.test(text);
162824
+ return /authentication token has (expired|been invalidated)/i.test(text) || /OAuth access token has expired/i.test(text) || /Token refresh failed/i.test(text);
162825
+ }
162826
+ function isClaudeSubscriptionDisabledError(text) {
162827
+ return /disabled Claude subscription access/i.test(text);
162835
162828
  }
162836
162829
  function formatApiKeyErrorSummary(params) {
162837
162830
  if (params.raw.includes(MISSING_KEY_MARKER)) {
@@ -162840,11 +162833,18 @@ function formatApiKeyErrorSummary(params) {
162840
162833
  }
162841
162834
  const githubSecretsUrl = `https://github.com/${params.owner}/${params.name}/settings/secrets/actions`;
162842
162835
  const settingsUrl = `${getApiUrl()}/console/${params.owner}/${params.name}`;
162836
+ if (isClaudeSubscriptionDisabledError(params.raw)) {
162837
+ return [
162838
+ `**Your organization has disabled Claude subscription access for Claude Code.** Ask your Claude organization's admin to re-enable it in the Claude Console, or set an \`ANTHROPIC_API_KEY\` for this repo instead, then re-trigger the run.`,
162839
+ "",
162840
+ `[Add repo secret \u2192](${githubSecretsUrl}) \xB7 [Model settings \u2192](${settingsUrl}) \xB7 [Setup docs \u2192](https://docs.pullfrog.com/keys) \xB7 [Ask in Discord \u2192](https://discord.gg/8y96raFg8e)`
162841
+ ].join("\n");
162842
+ }
162843
162843
  if (isOAuthCredentialExpiredError(params.raw)) {
162844
162844
  return [
162845
- `**Your provider OAuth credential has expired.** Re-authenticate the provider connection (e.g. \`pullfrog auth codex\`), then re-trigger the run.`,
162845
+ `**Your provider OAuth credential has expired or been revoked.** Re-authenticate the provider connection (e.g. \`pullfrog auth claude\` / \`pullfrog auth codex\`), then re-trigger the run.`,
162846
162846
  "",
162847
- `[Model settings \u2192](${settingsUrl}) \xB7 [Setup docs \u2192](https://docs.pullfrog.com/keys) \xB7 [Ask in Discord \u2192](https://discord.gg/8y96raFg8e)`
162847
+ `[Claude subscription \u2192](https://docs.pullfrog.com/claude-auth) \xB7 [ChatGPT subscription \u2192](https://docs.pullfrog.com/codex-auth) \xB7 [Model settings \u2192](${settingsUrl}) \xB7 [Ask in Discord \u2192](https://discord.gg/8y96raFg8e)`
162848
162848
  ].join("\n");
162849
162849
  }
162850
162850
  return [
@@ -37,6 +37,9 @@ export declare function validateAgentApiKey(params: {
37
37
  * ****XXXX is invalid`. anchored to "Your api key: ... is invalid" so it
38
38
  * can't collide with DeepSeek's already-handled `Insufficient balance`
39
39
  * billing shape (which routes to formatProviderBillingExhausted).
40
+ * - org-disabled Claude subscription (#1072): `Your organization has
41
+ * disabled Claude subscription access for Claude Code`, an entitlement
42
+ * denial with its own remedy — see isClaudeSubscriptionDisabledError.
40
43
  */
41
44
  export declare function isApiKeyAuthError(text: string): boolean;
42
45
  /**
@@ -46,8 +49,17 @@ export declare function isApiKeyAuthError(text: string): boolean;
46
49
  * distinct copy for these. Patterns are deliberately narrow:
47
50
  * "authentication token has expired" (not bare "token has expired") so a
48
51
  * GitHub installation-token expiry can't be misread as an LLM key problem.
52
+ * also covers server-side revocation ("authentication token has been
53
+ * invalidated", OpenAI `token_invalidated`, #1041) and the Claude CLI's
54
+ * "OAuth access token has expired" phrasing (#1072) — same remediation.
49
55
  */
50
56
  export declare function isOAuthCredentialExpiredError(text: string): boolean;
57
+ /**
58
+ * Anthropic entitlement denial for Claude Pro/Max subscription auth (#1072):
59
+ * an org admin turned off Claude Code subscription access. no credential the
60
+ * user controls fixes it, so it gets its own copy instead of the re-auth CTA.
61
+ */
62
+ export declare function isClaudeSubscriptionDisabledError(text: string): boolean;
51
63
  /**
52
64
  * Friendly Markdown summary for both the missing-key and invalid-key cases.
53
65
  * Used in the catch / result-failure paths in `main.ts` to overwrite the raw
@@ -16,7 +16,9 @@ export type SubscriptionPreflight = {
16
16
  * per-model ("You've hit your Opus limit"), so a cheaper stand-in could pass
17
17
  * preflight and still leave the run dead on arrival.
18
18
  *
19
- * fail-open by design: only 401 (revoked/expired token) and 429
19
+ * fail-open by design: only 401 (revoked/expired token), 403 (Anthropic
20
+ * `permission_error` — the credential isn't entitled to serve this run, e.g.
21
+ * an org disabled Claude Code subscription access, #1072) and 429
20
22
  * (session/weekly/per-model limit) mark the token unusable. network errors,
21
23
  * 5xx, and request-shape drift (400) all keep today's subscription-first
22
24
  * behavior, so the preflight can never fail a run that would have worked —
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pullfrog",
3
- "version": "0.1.39",
3
+ "version": "0.1.40",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "pullfrog": "dist/cli.mjs",