pullfrog 0.1.90 → 0.1.92

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/index.js CHANGED
@@ -83904,7 +83904,7 @@ var require_Element = __commonJS({
83904
83904
  var FilteredElementList = require_FilteredElementList();
83905
83905
  var DOMException2 = require_DOMException();
83906
83906
  var DOMTokenList = require_DOMTokenList();
83907
- var select = require_select();
83907
+ var select2 = require_select();
83908
83908
  var ContainerNode = require_ContainerNode();
83909
83909
  var ChildNode = require_ChildNode();
83910
83910
  var NonDocumentTypeChildNode = require_NonDocumentTypeChildNode();
@@ -84665,7 +84665,7 @@ var require_Element = __commonJS({
84665
84665
  this.className = v;
84666
84666
  } },
84667
84667
  matches: { value: function(selector) {
84668
- return select.matches(this, selector);
84668
+ return select2.matches(this, selector);
84669
84669
  } },
84670
84670
  closest: { value: function(selector) {
84671
84671
  var el = this;
@@ -84678,10 +84678,10 @@ var require_Element = __commonJS({
84678
84678
  return null;
84679
84679
  } },
84680
84680
  querySelector: { value: function(selector) {
84681
- return select(selector, this)[0];
84681
+ return select2(selector, this)[0];
84682
84682
  } },
84683
84683
  querySelectorAll: { value: function(selector) {
84684
- var nodes = select(selector, this);
84684
+ var nodes = select2(selector, this);
84685
84685
  return nodes.item ? nodes : new NodeList(nodes);
84686
84686
  } }
84687
84687
  });
@@ -85209,7 +85209,7 @@ var require_DocumentFragment = __commonJS({
85209
85209
  var NodeList = require_NodeList();
85210
85210
  var ContainerNode = require_ContainerNode();
85211
85211
  var Element = require_Element();
85212
- var select = require_select();
85212
+ var select2 = require_select();
85213
85213
  var utils = require_utils8();
85214
85214
  function DocumentFragment(doc) {
85215
85215
  ContainerNode.call(this);
@@ -85238,7 +85238,7 @@ var require_DocumentFragment = __commonJS({
85238
85238
  context.isHTML = true;
85239
85239
  context.getElementsByTagName = Element.prototype.getElementsByTagName;
85240
85240
  context.nextElement = Object.getOwnPropertyDescriptor(Element.prototype, "firstElementChild").get;
85241
- var nodes = select(selector, context);
85241
+ var nodes = select2(selector, context);
85242
85242
  return nodes.item ? nodes : new NodeList(nodes);
85243
85243
  } },
85244
85244
  // Utility methods
@@ -88353,7 +88353,7 @@ var require_Document = __commonJS({
88353
88353
  var NodeIterator = require_NodeIterator();
88354
88354
  var NodeFilter = require_NodeFilter();
88355
88355
  var URL2 = require_URL();
88356
- var select = require_select();
88356
+ var select2 = require_select();
88357
88357
  var events = require_events3();
88358
88358
  var xml = require_xmlnames();
88359
88359
  var html = require_htmlelts();
@@ -88914,10 +88914,10 @@ var require_Document = __commonJS({
88914
88914
  return this._templateDocCache;
88915
88915
  } },
88916
88916
  querySelector: { value: function(selector) {
88917
- return select(selector, this)[0];
88917
+ return select2(selector, this)[0];
88918
88918
  } },
88919
88919
  querySelectorAll: { value: function(selector) {
88920
- var nodes = select(selector, this);
88920
+ var nodes = select2(selector, this);
88921
88921
  return nodes.item ? nodes : new NodeList(nodes);
88922
88922
  } }
88923
88923
  });
@@ -104680,6 +104680,9 @@ function stripProviderPrefix(specifier) {
104680
104680
  const slashIndex = specifier.indexOf("/");
104681
104681
  return slashIndex > 0 ? specifier.slice(slashIndex + 1) : specifier;
104682
104682
  }
104683
+ function modelIdLabel(specifier) {
104684
+ return specifier.slice(specifier.lastIndexOf("/") + 1).replace(/^~/, "");
104685
+ }
104683
104686
  function getModelEnvVars(slug2) {
104684
104687
  const parsed2 = parseModel(slug2);
104685
104688
  const providerConfig = providers[parsed2.provider];
@@ -105082,10 +105085,12 @@ async function preflightClaudeSubscription(params) {
105082
105085
  if (res.status !== 401 && res.status !== 403 && res.status !== 429)
105083
105086
  return { usable: true, status: res.status };
105084
105087
  const body = await res.text().catch(() => "");
105088
+ const reset = Number(res.headers.get("anthropic-ratelimit-unified-reset"));
105085
105089
  return {
105086
105090
  usable: false,
105087
105091
  status: res.status,
105088
- reason: `${res.status}: ${extractApiErrorMessage(body)}`
105092
+ reason: `${res.status}: ${extractApiErrorMessage(body)}`,
105093
+ resetAt: res.status === 429 && reset > 0 ? new Date(reset * 1e3) : void 0
105089
105094
  };
105090
105095
  }
105091
105096
  function extractApiErrorMessage(body) {
@@ -105729,7 +105734,7 @@ var import_semver = __toESM(require_semver2(), 1);
105729
105734
  // package.json
105730
105735
  var package_default = {
105731
105736
  name: "pullfrog",
105732
- version: "0.1.90",
105737
+ version: "0.1.92",
105733
105738
  cliContractVersion: "1",
105734
105739
  type: "module",
105735
105740
  bin: {
@@ -108001,6 +108006,17 @@ function parseXaiAuthBody(raw2) {
108001
108006
  var CODEX_AUTH_ENV = "CODEX_AUTH_JSON";
108002
108007
  var XAI_AUTH_ENV = "GROK_AUTH_JSON";
108003
108008
  var installedSubscriptionPaths = /* @__PURE__ */ new Map();
108009
+ function clearInstalledSubscription(name) {
108010
+ const path4 = installedSubscriptionPaths.get(name);
108011
+ if (!path4 || !existsSync4(path4)) return;
108012
+ const value2 = JSON.parse(readFileSync2(path4, "utf8"));
108013
+ if (!value2 || typeof value2 !== "object") return;
108014
+ if (name === "CODEX_AUTH_JSON" && "openai" in value2) delete value2.openai;
108015
+ if (name === "GROK_AUTH_JSON" && "xai" in value2) delete value2.xai;
108016
+ writeFileSync4(path4, `${JSON.stringify(value2)}
108017
+ `, { mode: 384 });
108018
+ installedSubscriptionPaths.delete(name);
108019
+ }
108004
108020
  var PULLFROG_DATA_DIR = "/var/lib/pullfrog";
108005
108021
  function isRejectedChain(body) {
108006
108022
  if (!body.refresh_rejected_at) return false;
@@ -108020,6 +108036,7 @@ function installCodexAuth() {
108020
108036
  if (isRejectedChain(body)) return null;
108021
108037
  const expiresMs = decodeJwtExpMs(body.tokens.access_token) ?? 0;
108022
108038
  const xdgDataHome = resolveDataHome();
108039
+ if (!xdgDataHome) return null;
108023
108040
  const opencodeDir = join5(xdgDataHome, "opencode");
108024
108041
  const authPath = join5(opencodeDir, "auth.json");
108025
108042
  writeOpenCodeAuthEntry({
@@ -108069,6 +108086,7 @@ function installXaiAuth() {
108069
108086
  }
108070
108087
  if (isXaiRejectedChain(body)) return null;
108071
108088
  const xdgDataHome = resolveDataHome();
108089
+ if (!xdgDataHome) return null;
108072
108090
  const opencodeDir = join5(xdgDataHome, "opencode");
108073
108091
  const authPath = join5(opencodeDir, "auth.json");
108074
108092
  writeOpenCodeAuthEntry({
@@ -108113,17 +108131,23 @@ function installCodexHome() {
108113
108131
  );
108114
108132
  return null;
108115
108133
  }
108116
- const codexHome = join5(resolveDataHome(), "codex");
108134
+ const root = resolveDataHome();
108135
+ if (!root) return null;
108136
+ const codexHome = join5(root, "codex");
108117
108137
  const authPath = join5(codexHome, "auth.json");
108118
108138
  mkdirSync5(codexHome, { recursive: true });
108119
108139
  writeFileSync4(authPath, stringifyCodexAuthBody(body), { mode: 384 });
108120
108140
  log.info(`\xBB installed Codex auth at ${authPath}`);
108121
108141
  return { codexHome, authPath, originalRefresh: body.tokens.refresh_token };
108122
108142
  }
108143
+ var dataHome;
108144
+ function canInstallSubscription(name) {
108145
+ return name !== CODEX_AUTH_ENV && name !== XAI_AUTH_ENV || resolveDataHome() !== null;
108146
+ }
108123
108147
  function resolveDataHome() {
108124
108148
  if (process.env.CI !== "true") return join5(homedir2(), ".local", "share");
108125
- bootstrapPullfrogDataDir();
108126
- return PULLFROG_DATA_DIR;
108149
+ if (dataHome === void 0) dataHome = bootstrapPullfrogDataDir();
108150
+ return dataHome;
108127
108151
  }
108128
108152
  function bootstrapPullfrogDataDir() {
108129
108153
  const user = userInfo().username;
@@ -108140,10 +108164,14 @@ function bootstrapPullfrogDataDir() {
108140
108164
  });
108141
108165
  execFileSync3("sudo", ["-n", "chmod", "700", PULLFROG_DATA_DIR], { stdio: "pipe" });
108142
108166
  } catch (err) {
108143
- throw new Error(
108144
- `failed to bootstrap ${PULLFROG_DATA_DIR} (required for codex auth in CI): ${err instanceof Error ? err.message : String(err)}. the MCP shell's mount-namespace sandbox cannot protect the auth file when it lives under $HOME, and silently falling back would contradict the "three independent layers" claim in wiki/codex-auth.md. passwordless sudo is required for codex auth on this runner \u2014 either configure it, or remove CODEX_AUTH_JSON from the run.`
108167
+ log.warning(
108168
+ `\xBB subscription auth skipped: could not create ${PULLFROG_DATA_DIR} (${err instanceof Error ? err.message : String(err)}). it must live outside the agent sandbox's reach, which needs passwordless sudo on this runner; runs on a model that needs the subscription will report no key.`
108145
108169
  );
108170
+ delete process.env[CODEX_AUTH_ENV];
108171
+ delete process.env[XAI_AUTH_ENV];
108172
+ return null;
108146
108173
  }
108174
+ return PULLFROG_DATA_DIR;
108147
108175
  }
108148
108176
 
108149
108177
  // utils/oauthWriteback.ts
@@ -116693,6 +116721,9 @@ function captureAuthorizedModels(cliPath) {
116693
116721
  }
116694
116722
  log.debug(`\xBB opencode authorized: ${authorized.size} models`);
116695
116723
  }
116724
+ function authorizeModel(model) {
116725
+ getAuthorizedModels().add(model);
116726
+ }
116696
116727
  function getAuthorizedModels() {
116697
116728
  if (!authorized) {
116698
116729
  throw new Error("getAuthorizedModels called before captureAuthorizedModels");
@@ -130812,8 +130843,8 @@ function liftWireOnlyMaterial(message, kind) {
130812
130843
  };
130813
130844
  }
130814
130845
  function codecResultValidator(codec2, method) {
130815
- const probe = codec2.validateResult(method, void 0);
130816
- if (!probe.ok && probe.reason === "not-in-era") return void 0;
130846
+ const probe2 = codec2.validateResult(method, void 0);
130847
+ if (!probe2.ok && probe2.reason === "not-in-era") return void 0;
130817
130848
  return { "~standard": {
130818
130849
  version: 1,
130819
130850
  vendor: "mcp-wire-codec",
@@ -145189,8 +145220,8 @@ function liftWireOnlyMaterial2(message, kind) {
145189
145220
  };
145190
145221
  }
145191
145222
  function codecResultValidator2(codec2, method) {
145192
- const probe = codec2.validateResult(method, void 0);
145193
- if (!probe.ok && probe.reason === "not-in-era") return void 0;
145223
+ const probe2 = codec2.validateResult(method, void 0);
145224
+ if (!probe2.ok && probe2.reason === "not-in-era") return void 0;
145194
145225
  return { "~standard": {
145195
145226
  version: 1,
145196
145227
  vendor: "mcp-wire-codec",
@@ -188369,10 +188400,10 @@ function CommitChangesTool(ctx) {
188369
188400
  }
188370
188401
  const requested = new Set(params.files);
188371
188402
  const known = new Set(changes.map((c) => c.path));
188372
- const unknown4 = [...requested].filter((p) => !known.has(p));
188373
- if (unknown4.length > 0) {
188403
+ const unknown5 = [...requested].filter((p) => !known.has(p));
188404
+ if (unknown5.length > 0) {
188374
188405
  throw new Error(
188375
- `no detected change at: ${unknown4.join(", ")} \u2014 run git status to list changed paths.`
188406
+ `no detected change at: ${unknown5.join(", ")} \u2014 run git status to list changed paths.`
188376
188407
  );
188377
188408
  }
188378
188409
  changes = changes.filter((c) => requested.has(c.path));
@@ -188765,32 +188796,20 @@ function PushTagsTool(ctx) {
188765
188796
  // utils/buildPullfrogFooter.ts
188766
188797
  var PULLFROG_DIVIDER = "<!-- PULLFROG_DIVIDER_DO_NOT_REMOVE_PLZ -->";
188767
188798
  var FROG_LOGO = `<a href="https://pullfrog.com"><picture><source media="(prefers-color-scheme: dark)" srcset="https://pullfrog.com/logos/frog-white-full-18px.png"><img src="https://pullfrog.com/logos/frog-green-full-18px.png" width="9px" height="9px" style="vertical-align: middle; " alt="Pullfrog"></picture></a>`;
188768
- function providerDisplayName(slug2) {
188769
- try {
188770
- const key = getModelProvider(slug2);
188771
- const meta3 = providers[key];
188772
- return meta3?.displayName ?? key;
188773
- } catch {
188774
- return slug2;
188775
- }
188776
- }
188777
188799
  function formatModelLabel(params) {
188778
- const alias = resolveDisplayAlias(params.model) ?? // reverse-lookup: when the caller passes an effective model (proxy or
188800
+ const slugAlias = resolveDisplayAlias(params.model);
188801
+ const alias = slugAlias ?? // reverse-lookup: when the caller passes an effective model (proxy or
188779
188802
  // resolved target like "openrouter/anthropic/claude-opus-4.7") instead of
188780
- // a stored alias slug, find the alias whose resolve target matches so we
188781
- // still render a friendly display name.
188803
+ // a stored alias slug, find the alias whose resolve target matches.
188782
188804
  modelAliases.find((a) => a.resolve === params.model || a.openRouterResolve === params.model);
188783
- const displayName = alias?.displayName ?? params.model;
188805
+ const modelName = slugAlias?.routing ? slugAlias.displayName : modelIdLabel(slugAlias?.resolve ?? params.model);
188784
188806
  if (params.oss) {
188785
- const ossBase = `\`${displayName}\` (free via [Pullfrog for OSS](https://pullfrog.com/for-oss))`;
188807
+ const ossBase = `\`${modelName}\` (free via [Pullfrog for OSS](https://pullfrog.com/for-oss))`;
188786
188808
  if (params.clamped?.reason !== "oss") return ossBase;
188787
188809
  const configured = isAutoTier(params.clamped.from) ? "the intelligent tier" : `\`${resolveDisplayAlias(params.clamped.from)?.displayName ?? params.clamped.from}\``;
188788
188810
  return `${ossBase} (${configured} not used \u2014 pick one of the [funded models](https://docs.pullfrog.com/models#pullfrog-for-oss) or add a [provider key](https://docs.pullfrog.com/keys) to run your own)`;
188789
188811
  }
188790
- const base = alias?.isFree ? `\`${displayName}\` (free)` : `\`${displayName}\``;
188791
- if (params.fallbackFrom) {
188792
- return `${base} (credentials for ${providerDisplayName(params.fallbackFrom)} were rejected by the provider)`;
188793
- }
188812
+ const base = alias?.isFree ? `\`${modelName}\` (free)` : `\`${modelName}\``;
188794
188813
  if (params.clamped?.reason === "trial") {
188795
188814
  return `${base} (model usage covered by Pullfrog)`;
188796
188815
  }
@@ -188831,7 +188850,6 @@ function buildPullfrogFooter(params) {
188831
188850
  parts.push(
188832
188851
  `Using ${formatModelLabel({
188833
188852
  model: params.model,
188834
- fallbackFrom: params.fallbackFrom,
188835
188853
  clamped: params.clamped,
188836
188854
  unselectedProxyDefault: params.unselectedProxyDefault,
188837
188855
  oss: params.oss
@@ -189031,7 +189049,6 @@ function buildCommentFooter(ctx, customParts) {
189031
189049
  } : void 0,
189032
189050
  customParts,
189033
189051
  model: ctx.toolState.model,
189034
- fallbackFrom: ctx.toolState.modelFallback?.from,
189035
189052
  clamped: ctx.toolState.modelClamped,
189036
189053
  unselectedProxyDefault: ctx.toolState.unselectedProxyDefault,
189037
189054
  oss: ctx.oss,
@@ -190021,7 +190038,6 @@ async function createAndSubmitWithFooter(ctx, params, opts) {
190021
190038
  workflowRun: ctx.runId ? { owner: ctx.repo.owner, repo: ctx.repo.name, runId: ctx.runId, jobId: ctx.jobId } : void 0,
190022
190039
  customParts,
190023
190040
  model: ctx.toolState.model,
190024
- fallbackFrom: ctx.toolState.modelFallback?.from,
190025
190041
  clamped: ctx.toolState.modelClamped,
190026
190042
  unselectedProxyDefault: ctx.toolState.unselectedProxyDefault,
190027
190043
  oss: ctx.oss,
@@ -192397,7 +192413,6 @@ function buildPrBodyWithFooter(ctx, body) {
192397
192413
  triggeredBy: true,
192398
192414
  workflowRun: ctx.runId ? { owner: ctx.repo.owner, repo: ctx.repo.name, runId: ctx.runId, jobId: ctx.jobId } : void 0,
192399
192415
  model: ctx.toolState.model,
192400
- fallbackFrom: ctx.toolState.modelFallback?.from,
192401
192416
  clamped: ctx.toolState.modelClamped,
192402
192417
  unselectedProxyDefault: ctx.toolState.unselectedProxyDefault,
192403
192418
  oss: ctx.oss,
@@ -194546,17 +194561,17 @@ async function verifyCredential(params) {
194546
194561
  const value2 = params.value.trim();
194547
194562
  if (value2.length === 0) return "dead";
194548
194563
  if (params.envVar === "CLAUDE_CODE_OAUTH_TOKEN") return verifyClaudeSubscription(value2);
194549
- const probe = PROBES[params.envVar];
194550
- if (!probe) return "unknown";
194551
- const request2 = probe.request(value2);
194564
+ const probe2 = PROBES[params.envVar];
194565
+ if (!probe2) return "unknown";
194566
+ const request2 = probe2.request(value2);
194552
194567
  try {
194553
194568
  const response = await fetch(request2.url, {
194554
194569
  headers: request2.headers,
194555
194570
  signal: AbortSignal.timeout(5e3)
194556
194571
  });
194557
194572
  if (response.ok) return "alive";
194558
- if (probe.deadWhen) {
194559
- return probe.deadWhen(response.status, await response.text()) ? "dead" : "unknown";
194573
+ if (probe2.deadWhen) {
194574
+ return probe2.deadWhen(response.status, await response.text()) ? "dead" : "unknown";
194560
194575
  }
194561
194576
  return response.status === 401 ? "dead" : "unknown";
194562
194577
  } catch {
@@ -194587,14 +194602,6 @@ async function checkOne(params) {
194587
194602
  const verdict = await verifyCredential({ envVar: params.envVar, value: value2 });
194588
194603
  return verdict === "dead" ? { severity: "dead", reason: void 0 } : null;
194589
194604
  }
194590
- function pickReplacement(params) {
194591
- const candidates = modelAliases.filter(
194592
- (alias) => !alias.hidden && !alias.fallback && params.authorized.has(alias.resolve) && // a model with no known env vars is routable through credentials we can't
194593
- // see (Codex auth lands as an on-disk auth.json), so it stays a candidate.
194594
- !envVarsFor(alias.resolve).some((envVar) => params.deadVars.has(envVar))
194595
- );
194596
- return (candidates.find((alias) => alias.preferred) ?? candidates[0])?.resolve;
194597
- }
194598
194605
  async function checkConfiguredCredentials(params) {
194599
194606
  if (!params.model) return { kind: "ok" };
194600
194607
  const present = envVarsFor(params.model).filter(hasEnvVar4);
@@ -194620,12 +194627,400 @@ async function checkConfiguredCredentials(params) {
194620
194627
  failed.forEach(drop);
194621
194628
  const first = failed[0];
194622
194629
  if (!first) return { kind: "ok" };
194623
- const outcome = { credential: first.envVar, reason: first.failure.reason };
194624
- const replacement = pickReplacement({
194625
- authorized: params.authorized,
194626
- deadVars: new Set(failed.map((entry) => entry.envVar))
194630
+ return { kind: "dead", credential: first.envVar, reason: first.failure.reason };
194631
+ }
194632
+
194633
+ // utils/normalizeEnv.ts
194634
+ function hasControlCharacter(value2) {
194635
+ return [...value2].some((ch) => {
194636
+ const code = ch.charCodeAt(0);
194637
+ return code < 32 || code === 127;
194627
194638
  });
194628
- return replacement ? { kind: "fellBack", ...outcome, replacement } : { kind: "dead", ...outcome };
194639
+ }
194640
+ function sanitizeSecret(key, value2) {
194641
+ const trimmed = value2.trim();
194642
+ if (trimmed.length === 0) {
194643
+ log.warning(
194644
+ `\xBB ${key} is whitespace-only \u2014 leaving env var unchanged. check your secret value.`
194645
+ );
194646
+ return null;
194647
+ }
194648
+ if (key.endsWith("_API_KEY") && hasControlCharacter(trimmed)) {
194649
+ log.warning(
194650
+ `\xBB ${key} contains a line break or control character and cannot be sent as an HTTP header \u2014 leaving env var unchanged. re-save the secret as a single line.`
194651
+ );
194652
+ return null;
194653
+ }
194654
+ if (trimmed !== value2) {
194655
+ log.warning(
194656
+ `\xBB stripped whitespace from ${key} (whitespace in secret values breaks GitHub Actions log masking)`
194657
+ );
194658
+ }
194659
+ maskSecret(trimmed);
194660
+ return trimmed;
194661
+ }
194662
+ function normalizeEnv() {
194663
+ const upperKeys = /* @__PURE__ */ new Map();
194664
+ for (const key of Object.keys(process.env)) {
194665
+ const upper2 = key.toUpperCase();
194666
+ const existing = upperKeys.get(upper2) || [];
194667
+ existing.push(key);
194668
+ upperKeys.set(upper2, existing);
194669
+ }
194670
+ for (const [upperKey, keys] of upperKeys) {
194671
+ if (keys.length === 1) {
194672
+ const key = keys[0];
194673
+ if (key !== upperKey) {
194674
+ process.env[upperKey] = process.env[key];
194675
+ delete process.env[key];
194676
+ }
194677
+ continue;
194678
+ }
194679
+ const values = keys.map((k2) => process.env[k2]);
194680
+ const uniqueValues = new Set(values);
194681
+ if (uniqueValues.size > 1) {
194682
+ log.warning(
194683
+ `env var conflict: ${keys.join(", ")} have different values. using uppercase ${upperKey}.`
194684
+ );
194685
+ }
194686
+ const preferredKey = keys.find((k2) => k2 === upperKey) || keys[0];
194687
+ const preferredValue = process.env[preferredKey];
194688
+ for (const key of keys) {
194689
+ delete process.env[key];
194690
+ }
194691
+ process.env[upperKey] = preferredValue;
194692
+ }
194693
+ for (const key of Object.keys(process.env)) {
194694
+ if (!isSensitiveEnvName(key)) continue;
194695
+ const value2 = process.env[key];
194696
+ if (typeof value2 !== "string" || value2.length === 0) continue;
194697
+ const sanitized = sanitizeSecret(key, value2);
194698
+ if (sanitized !== null) process.env[key] = sanitized;
194699
+ }
194700
+ }
194701
+
194702
+ // utils/subscriptionCredentials.ts
194703
+ var subscriptionNameSchema = external_exports.enum([
194704
+ "CLAUDE_CODE_OAUTH_TOKEN",
194705
+ "CODEX_AUTH_JSON",
194706
+ "GROK_AUTH_JSON"
194707
+ ]);
194708
+ var subscriptionSchema = external_exports.object({
194709
+ id: external_exports.string(),
194710
+ credentialId: external_exports.string(),
194711
+ name: subscriptionNameSchema,
194712
+ label: external_exports.string(),
194713
+ owner: external_exports.string(),
194714
+ inherited: external_exports.boolean(),
194715
+ shared: external_exports.boolean(),
194716
+ registeredAt: external_exports.string(),
194717
+ /** the provider's last verdict on the current value; null = usable or never checked. */
194718
+ status: external_exports.enum(["rejected", "exhausted"]).nullish(),
194719
+ resetAt: external_exports.string().nullish()
194720
+ });
194721
+ var subscriptionListSchema = external_exports.object({
194722
+ subscriptions: external_exports.array(subscriptionSchema),
194723
+ writable: external_exports.boolean()
194724
+ });
194725
+ var subscriptionInputSchema = external_exports.object({
194726
+ name: subscriptionNameSchema,
194727
+ value: external_exports.string().trim().min(1).refine((value2) => Buffer.byteLength(value2, "utf8") <= 49152),
194728
+ replaceId: external_exports.string().optional()
194729
+ });
194730
+ var credentialCandidateSchema = external_exports.object({
194731
+ id: external_exports.string(),
194732
+ name: external_exports.string(),
194733
+ source: external_exports.enum(["repo", "account", "personal"])
194734
+ });
194735
+ var credentialAccessSchema = external_exports.object({
194736
+ token: external_exports.string(),
194737
+ candidates: external_exports.array(credentialCandidateSchema)
194738
+ });
194739
+ var selectedCredentialSchema = external_exports.object({
194740
+ name: external_exports.string(),
194741
+ value: external_exports.string(),
194742
+ receipt: external_exports.string()
194743
+ });
194744
+
194745
+ // utils/subscriptionProbe.ts
194746
+ var identitySchema = external_exports.object({
194747
+ email: external_exports.string().email().optional(),
194748
+ sub: external_exports.string().optional(),
194749
+ user_id: external_exports.string().optional(),
194750
+ account_id: external_exports.string().optional(),
194751
+ email_verified: external_exports.boolean().optional()
194752
+ });
194753
+ var codexWindowSchema = external_exports.object({ used_percent: external_exports.number().optional(), reset_at: external_exports.number().optional() }).nullish();
194754
+ var codexUsageSchema = external_exports.object({
194755
+ rate_limit: external_exports.object({
194756
+ allowed: external_exports.boolean(),
194757
+ limit_reached: external_exports.boolean(),
194758
+ primary_window: codexWindowSchema,
194759
+ secondary_window: codexWindowSchema
194760
+ }).optional()
194761
+ });
194762
+ var unknown4 = { status: "unknown" };
194763
+ async function readSubscriptionEndpoint(input) {
194764
+ const headers = new Headers({ authorization: `Bearer ${input.token}` });
194765
+ if (input.accountId) headers.set("ChatGPT-Account-Id", input.accountId);
194766
+ if (input.grokBilling) headers.set("x-xai-token-auth", "xai-grok-cli");
194767
+ return fetch(input.url, {
194768
+ headers,
194769
+ redirect: "error",
194770
+ signal: AbortSignal.timeout(1e4)
194771
+ }).catch(() => null);
194772
+ }
194773
+ async function probeSubscription(input) {
194774
+ if (input.name === "CLAUDE_CODE_OAUTH_TOKEN") {
194775
+ const result = await preflightClaudeSubscription({ token: input.value, model: input.model });
194776
+ if (result.usable)
194777
+ return result.status !== void 0 && result.status < 300 ? { status: "usable" } : unknown4;
194778
+ return result.status === 429 ? { status: "exhausted", detail: result.reason, resetAt: result.resetAt } : { status: "rejected", detail: result.reason };
194779
+ }
194780
+ const codex2 = input.name === "CODEX_AUTH_JSON" ? parseCodexAuthBody(input.value) : null;
194781
+ const grok = input.name === "GROK_AUTH_JSON" ? parseXaiAuthBody(input.value) : null;
194782
+ const auth2 = codex2 ?? grok;
194783
+ if (!auth2) return { status: "rejected", detail: "the stored credential is malformed" };
194784
+ if (auth2.refresh_rejected_at)
194785
+ return { status: "rejected", detail: `refresh rejected at ${auth2.refresh_rejected_at}` };
194786
+ if (grok && input.model)
194787
+ return probeInference({
194788
+ name: "XAI_API_KEY",
194789
+ value: grok.tokens.access_token,
194790
+ model: input.model
194791
+ });
194792
+ const response = await readSubscriptionEndpoint({
194793
+ url: codex2 ? "https://chatgpt.com/backend-api/wham/usage" : "https://cli-chat-proxy.grok.com/v1/billing?format=credits",
194794
+ token: auth2.tokens.access_token,
194795
+ accountId: codex2?.tokens.account_id,
194796
+ grokBilling: !!grok
194797
+ });
194798
+ if (response?.status === 401) return { status: "rejected", detail: "rejected with 401" };
194799
+ if (!response?.ok) return unknown4;
194800
+ const body = await response.json().catch(() => null);
194801
+ if (!codex2) return unknown4;
194802
+ const parsed2 = codexUsageSchema.safeParse(body);
194803
+ const limit = parsed2.success ? parsed2.data.rate_limit : void 0;
194804
+ if (!limit) return unknown4;
194805
+ if (limit.allowed && !limit.limit_reached) return { status: "usable" };
194806
+ const windows = [limit.primary_window, limit.secondary_window];
194807
+ const full = windows.filter((window2) => (window2?.used_percent ?? 0) >= 100);
194808
+ const resets = (full.length ? full : windows).flatMap(
194809
+ (window2) => window2?.reset_at ? [window2.reset_at] : []
194810
+ );
194811
+ return {
194812
+ status: "exhausted",
194813
+ detail: "the ChatGPT plan's usage limit is reached",
194814
+ resetAt: resets.length ? new Date(Math.max(...resets) * 1e3) : void 0
194815
+ };
194816
+ }
194817
+ async function probeInference(input) {
194818
+ const anthropic = input.name === "ANTHROPIC_API_KEY";
194819
+ const openai = input.name === "OPENAI_API_KEY";
194820
+ if (!anthropic && !openai && input.name !== "XAI_API_KEY") {
194821
+ const result = await verifyCredential({ envVar: input.name, value: input.value });
194822
+ return result === "dead" ? { status: "rejected", detail: "rejected by its provider" } : result === "alive" ? { status: "usable" } : unknown4;
194823
+ }
194824
+ const headers = new Headers({ "content-type": "application/json" });
194825
+ if (anthropic) {
194826
+ headers.set("x-api-key", input.value);
194827
+ headers.set("anthropic-version", "2023-06-01");
194828
+ } else headers.set("authorization", `Bearer ${input.value}`);
194829
+ const response = await fetch(
194830
+ anthropic ? "https://api.anthropic.com/v1/messages" : openai ? "https://api.openai.com/v1/responses" : "https://api.x.ai/v1/chat/completions",
194831
+ {
194832
+ method: "POST",
194833
+ headers,
194834
+ body: JSON.stringify(
194835
+ openai ? { model: input.model, input: "Reply OK.", max_output_tokens: 16, store: false } : {
194836
+ model: input.model,
194837
+ messages: [{ role: "user", content: "Reply OK." }],
194838
+ max_tokens: 1
194839
+ }
194840
+ ),
194841
+ redirect: "error",
194842
+ signal: AbortSignal.timeout(1e4)
194843
+ }
194844
+ ).catch(() => null);
194845
+ if (response?.status === 400 && input.name === "XAI_API_KEY") {
194846
+ const error49 = external_exports.object({ error: external_exports.string() }).safeParse(await response.json().catch(() => null));
194847
+ return error49.success && error49.data.error.startsWith("Incorrect API key provided.") ? { status: "rejected", detail: "rejected with 400: incorrect API key" } : unknown4;
194848
+ }
194849
+ if (response?.status === 429 && openai) {
194850
+ const error49 = external_exports.object({ error: external_exports.object({ code: external_exports.string() }) }).safeParse(await response.json().catch(() => null));
194851
+ return error49.success && error49.data.error.code === "insufficient_quota" ? { status: "exhausted", detail: "refused with 429: insufficient quota", resetAt: void 0 } : unknown4;
194852
+ }
194853
+ await response?.body?.cancel();
194854
+ if (!response) return unknown4;
194855
+ if (response.ok) return { status: "usable" };
194856
+ if (response.status === 401 || response.status === 403)
194857
+ return { status: "rejected", detail: `rejected with ${response.status}` };
194858
+ if (response.status === 402)
194859
+ return { status: "exhausted", detail: "refused with 402", resetAt: void 0 };
194860
+ return unknown4;
194861
+ }
194862
+
194863
+ // utils/credentialPool.ts
194864
+ var receipts = {};
194865
+ var workflowCredentials = {};
194866
+ function saveReceipts() {
194867
+ for (const receipt of Object.values(receipts)) maskSecret(receipt);
194868
+ saveSecretState("credential_receipts", JSON.stringify(receipts));
194869
+ }
194870
+ var select = op(
194871
+ async (input) => {
194872
+ const response = await apiFetch({
194873
+ path: "/api/runtime/credentials",
194874
+ method: "POST",
194875
+ headers: {
194876
+ authorization: `Bearer ${input.access.token}`,
194877
+ "content-type": "application/json"
194878
+ },
194879
+ body: JSON.stringify({ id: input.candidate.id }),
194880
+ signal: AbortSignal.timeout(35e3)
194881
+ });
194882
+ if (response.status === 404) return null;
194883
+ if (!response.ok)
194884
+ throw new Error("could not load a configured credential; no fallback was charged", {
194885
+ cause: response.status
194886
+ });
194887
+ return selectedCredentialSchema.parse(await response.json());
194888
+ },
194889
+ {
194890
+ retries: [250, 1e3],
194891
+ bail: (error49) => error49 instanceof Error && typeof error49.cause === "number" && error49.cause >= 400 && error49.cause < 500
194892
+ }
194893
+ );
194894
+ function subscriptionForModel(model) {
194895
+ if (!model.includes("/")) return null;
194896
+ const provider2 = getModelProvider(model);
194897
+ return provider2 === "anthropic" ? "CLAUDE_CODE_OAUTH_TOKEN" : provider2 === "openai" ? "CODEX_AUTH_JSON" : provider2 === "xai" ? "GROK_AUTH_JSON" : null;
194898
+ }
194899
+ async function initializeCredentialPool(input) {
194900
+ const access = input.ctx.credentialAccess;
194901
+ if (!access) return;
194902
+ const model = discoveryModel(input.slug);
194903
+ maskSecret(access.token);
194904
+ const names = /* @__PURE__ */ new Set([
194905
+ ...access.candidates.map((candidate) => candidate.name),
194906
+ ...subscriptionNameSchema.options,
194907
+ "ANTHROPIC_API_KEY",
194908
+ "OPENAI_API_KEY",
194909
+ "XAI_API_KEY"
194910
+ ]);
194911
+ for (const name of names) {
194912
+ const value2 = process.env[name];
194913
+ if (value2 && !input.injected.includes(name)) workflowCredentials[name] = value2;
194914
+ }
194915
+ for (const name of subscriptionNameSchema.options) {
194916
+ if (model && subscriptionForModel(model) !== name) continue;
194917
+ if (workflowCredentials[name]) continue;
194918
+ const candidate = access.candidates.find((item) => item.name === name);
194919
+ if (!candidate) continue;
194920
+ const selected = await select({ access, candidate }).catch(() => {
194921
+ log.warning(`\xBB could not load ${name} for model discovery; selection will retry if needed`);
194922
+ return null;
194923
+ });
194924
+ if (!selected) continue;
194925
+ const value2 = sanitizeSecret(name, selected.value);
194926
+ if (!value2) continue;
194927
+ process.env[name] = value2;
194928
+ receipts[name] = selected.receipt;
194929
+ }
194930
+ saveReceipts();
194931
+ }
194932
+ function discoveryModel(slug2) {
194933
+ const value2 = process.env.PULLFROG_MODEL?.trim() || slug2?.trim();
194934
+ if (!value2) return void 0;
194935
+ return resolveCliModel(value2) ?? (value2.includes("/") ? value2 : void 0);
194936
+ }
194937
+ function resolvePoolModel(input) {
194938
+ const agent2 = resolveAgent({
194939
+ model: void 0,
194940
+ proxyModel: void 0,
194941
+ codexAgent: input.codexAgent
194942
+ });
194943
+ if (agent2.name === "opencode") return autoSelectModel();
194944
+ const provider2 = agent2.name === "claude" ? "anthropic" : "openai";
194945
+ return modelAliases.find(
194946
+ (alias) => alias.provider === provider2 && alias.preferred && !alias.hidden && !alias.fallback && !alias.routing
194947
+ )?.resolve;
194948
+ }
194949
+ async function selectConfiguredCredential(input) {
194950
+ const access = input.ctx.credentialAccess;
194951
+ const model = input.model;
194952
+ if (!access || !model?.includes("/") || getProviderGatewayUrl(model)) return false;
194953
+ const subscription = subscriptionForModel(model);
194954
+ const names = getModelEnvVars(model).filter((name) => name !== subscription);
194955
+ if (subscription) names.unshift(subscription);
194956
+ const candidates = access.candidates.filter(
194957
+ (candidate) => names.includes(candidate.name) && canInstallSubscription(candidate.name)
194958
+ );
194959
+ if (!candidates.length) return false;
194960
+ const refused = [];
194961
+ for (const name of names.filter(
194962
+ (item) => workflowCredentials[item] && canInstallSubscription(item)
194963
+ )) {
194964
+ const value2 = workflowCredentials[name];
194965
+ const verdict = await probe({ name, value: value2, model });
194966
+ if (usable(verdict)) {
194967
+ activate({ names, name, value: value2, receipt: void 0, model });
194968
+ return true;
194969
+ }
194970
+ refused.push(`${name} from the workflow: ${verdict.detail}`);
194971
+ log.info(`\xBB ${name} from the workflow unavailable; trying the next credential`);
194972
+ }
194973
+ for (const candidate of candidates) {
194974
+ const selected = await select({ access, candidate });
194975
+ if (!selected) continue;
194976
+ const verdict = await probe({ ...selected, model });
194977
+ await reportVerdict({ receipt: selected.receipt, verdict });
194978
+ if (usable(verdict)) {
194979
+ activate({ names, ...selected, model });
194980
+ log.info(`\xBB selected ${candidate.name} from ${candidate.source} scope`);
194981
+ return true;
194982
+ }
194983
+ refused.push(`${candidate.name} from ${candidate.source} scope: ${verdict.detail}`);
194984
+ log.info(
194985
+ `\xBB ${candidate.name} from ${candidate.source} scope unavailable; trying the next credential`
194986
+ );
194987
+ }
194988
+ throw new Error(
194989
+ `all configured credentials for ${model} were rejected or exhausted (${refused.join("; ")}); no other model or provider was selected`
194990
+ );
194991
+ }
194992
+ async function probe(input) {
194993
+ const subscription = subscriptionNameSchema.safeParse(input.name);
194994
+ const model = stripProviderPrefix(input.model);
194995
+ return subscription.success ? probeSubscription({ name: subscription.data, value: input.value, model }) : probeInference({ ...input, model });
194996
+ }
194997
+ function usable(verdict) {
194998
+ return verdict.status !== "rejected" && verdict.status !== "exhausted";
194999
+ }
195000
+ async function reportVerdict(input) {
195001
+ if (input.verdict.status === "unknown") return;
195002
+ await apiFetch({
195003
+ path: "/api/runtime/credentials/status",
195004
+ method: "POST",
195005
+ headers: { authorization: `Bearer ${input.receipt}`, "content-type": "application/json" },
195006
+ body: JSON.stringify(input.verdict),
195007
+ signal: AbortSignal.timeout(5e3)
195008
+ }).catch(() => log.debug("\xBB could not record a credential verdict"));
195009
+ }
195010
+ function activate(input) {
195011
+ for (const name of input.names) {
195012
+ delete process.env[name];
195013
+ delete receipts[name];
195014
+ if (name === "CODEX_AUTH_JSON" || name === "GROK_AUTH_JSON") clearInstalledSubscription(name);
195015
+ }
195016
+ const value2 = sanitizeSecret(input.name, input.value);
195017
+ if (!value2) throw new Error("configured credential was empty");
195018
+ process.env[input.name] = value2;
195019
+ if (input.receipt) receipts[input.name] = input.receipt;
195020
+ installCodexAuth();
195021
+ installXaiAuth();
195022
+ saveReceipts();
195023
+ authorizeModel(input.model);
194629
195024
  }
194630
195025
 
194631
195026
  // utils/gitAuthServer.ts
@@ -195331,75 +195726,6 @@ function buildModelAccessError(input) {
195331
195726
  return [headline, "", why, "", cta].join("\n");
195332
195727
  }
195333
195728
 
195334
- // utils/normalizeEnv.ts
195335
- function hasControlCharacter(value2) {
195336
- return [...value2].some((ch) => {
195337
- const code = ch.charCodeAt(0);
195338
- return code < 32 || code === 127;
195339
- });
195340
- }
195341
- function sanitizeSecret(key, value2) {
195342
- const trimmed = value2.trim();
195343
- if (trimmed.length === 0) {
195344
- log.warning(
195345
- `\xBB ${key} is whitespace-only \u2014 leaving env var unchanged. check your secret value.`
195346
- );
195347
- return null;
195348
- }
195349
- if (key.endsWith("_API_KEY") && hasControlCharacter(trimmed)) {
195350
- log.warning(
195351
- `\xBB ${key} contains a line break or control character and cannot be sent as an HTTP header \u2014 leaving env var unchanged. re-save the secret as a single line.`
195352
- );
195353
- return null;
195354
- }
195355
- if (trimmed !== value2) {
195356
- log.warning(
195357
- `\xBB stripped whitespace from ${key} (whitespace in secret values breaks GitHub Actions log masking)`
195358
- );
195359
- }
195360
- maskSecret(trimmed);
195361
- return trimmed;
195362
- }
195363
- function normalizeEnv() {
195364
- const upperKeys = /* @__PURE__ */ new Map();
195365
- for (const key of Object.keys(process.env)) {
195366
- const upper2 = key.toUpperCase();
195367
- const existing = upperKeys.get(upper2) || [];
195368
- existing.push(key);
195369
- upperKeys.set(upper2, existing);
195370
- }
195371
- for (const [upperKey, keys] of upperKeys) {
195372
- if (keys.length === 1) {
195373
- const key = keys[0];
195374
- if (key !== upperKey) {
195375
- process.env[upperKey] = process.env[key];
195376
- delete process.env[key];
195377
- }
195378
- continue;
195379
- }
195380
- const values = keys.map((k2) => process.env[k2]);
195381
- const uniqueValues = new Set(values);
195382
- if (uniqueValues.size > 1) {
195383
- log.warning(
195384
- `env var conflict: ${keys.join(", ")} have different values. using uppercase ${upperKey}.`
195385
- );
195386
- }
195387
- const preferredKey = keys.find((k2) => k2 === upperKey) || keys[0];
195388
- const preferredValue = process.env[preferredKey];
195389
- for (const key of keys) {
195390
- delete process.env[key];
195391
- }
195392
- process.env[upperKey] = preferredValue;
195393
- }
195394
- for (const key of Object.keys(process.env)) {
195395
- if (!isSensitiveEnvName(key)) continue;
195396
- const value2 = process.env[key];
195397
- if (typeof value2 !== "string" || value2.length === 0) continue;
195398
- const sanitized = sanitizeSecret(key, value2);
195399
- if (sanitized !== null) process.env[key] = sanitized;
195400
- }
195401
- }
195402
-
195403
195729
  // utils/overrides.ts
195404
195730
  var DENIED_OVERRIDE_NAMES = /* @__PURE__ */ new Set([
195405
195731
  "GITHUB_TOKEN",
@@ -195970,7 +196296,6 @@ ${ctx.error}` : ctx.error;
195970
196296
  workflowRun: runId ? { owner: repoContext.owner, repo: repoContext.name, runId } : void 0,
195971
196297
  customParts,
195972
196298
  model: ctx.toolState.model,
195973
- fallbackFrom: ctx.toolState.modelFallback?.from,
195974
196299
  clamped: ctx.toolState.modelClamped,
195975
196300
  unselectedProxyDefault: ctx.toolState.unselectedProxyDefault,
195976
196301
  oss: ctx.toolState.oss,
@@ -196354,7 +196679,8 @@ async function fetchRunContext(params) {
196354
196679
  const timeoutId = setTimeout(() => controller.abort(), timeoutMs);
196355
196680
  try {
196356
196681
  const headers = {
196357
- Authorization: `Bearer ${params.token}`
196682
+ Authorization: `Bearer ${params.token}`,
196683
+ "X-Pullfrog-Credential-Pools": "1"
196358
196684
  };
196359
196685
  if (params.oidcToken) {
196360
196686
  headers["X-GitHub-OIDC-Token"] = params.oidcToken;
@@ -197651,16 +197977,20 @@ async function main() {
197651
197977
  const preIntrospectionDirty = repoDir ? await dirtyTrackedPaths({ cwd: repoDir }) : null;
197652
197978
  const opencodeCliPath = await agents.opencode.install();
197653
197979
  captureBaselineModels(opencodeCliPath);
197980
+ const injected = [];
197654
197981
  if (runContext.dbSecrets) {
197655
197982
  for (const [key, value2] of Object.entries(runContext.dbSecrets)) {
197656
197983
  if (!process.env[key]) {
197657
197984
  const sanitized = sanitizeSecret(key, value2);
197658
- if (sanitized !== null) process.env[key] = sanitized;
197985
+ if (sanitized === null) continue;
197986
+ process.env[key] = sanitized;
197987
+ injected.push(key);
197659
197988
  }
197660
197989
  }
197661
197990
  const count = Object.keys(runContext.dbSecrets).length;
197662
197991
  if (count > 0) log.info(`\xBB ${count} db secret(s) loaded`);
197663
197992
  }
197993
+ await initializeCredentialPool({ ctx: runContext, slug: payload.model, injected });
197664
197994
  installCodexAuth();
197665
197995
  installXaiAuth();
197666
197996
  captureAuthorizedModels(opencodeCliPath);
@@ -197734,13 +198064,23 @@ async function main() {
197734
198064
  }
197735
198065
  const gitAuthServer = __using(_stack2, await startGitAuthServer(tmpdir3), true);
197736
198066
  setGitAuthServer(gitAuthServer);
198067
+ let resolvedModel = resolveModel({ slug: payload.model });
198068
+ if (!resolvedModel && !payload.proxyModel && runContext.credentialAccess?.candidates.length) {
198069
+ resolvedModel = resolvePoolModel({
198070
+ codexAgent: runContext.repoSettings.codexAgent || payload.codexArm === true
198071
+ });
198072
+ }
198073
+ let poolSelected = !payload.proxyModel && await selectConfiguredCredential({
198074
+ ctx: runContext,
198075
+ model: resolvedModel
198076
+ });
197737
198077
  const access = decideModelAccess({
197738
198078
  modelExplicit: payload.modelExplicit ?? false,
197739
198079
  model: payload.model,
197740
198080
  oss: runContext.oss,
197741
198081
  proxyActive: !!payload.proxyModel,
197742
198082
  subsidyTarget: runContext.proxyModel,
197743
- resolvedModel: resolveModel({ slug: payload.model }),
198083
+ resolvedModel,
197744
198084
  authorized: getAuthorizedModels()
197745
198085
  });
197746
198086
  if (access.kind === "error") {
@@ -197754,15 +198094,19 @@ async function main() {
197754
198094
  );
197755
198095
  }
197756
198096
  if (access.kind === "proxy") payload.proxyModel = access.target;
197757
- if (access.kind === "byok") payload.proxyModel = void 0;
198097
+ if (access.kind === "byok") {
198098
+ payload.proxyModel = void 0;
198099
+ if (!poolSelected)
198100
+ poolSelected = await selectConfiguredCredential({
198101
+ ctx: runContext,
198102
+ model: resolvedModel
198103
+ });
198104
+ }
197758
198105
  if (runContext.oss && payload.proxyModel) {
197759
198106
  payload.effort = ossEffortFloor({ payload });
197760
198107
  }
197761
- const configuredModel = payload.proxyModel ? void 0 : resolveModel({ slug: payload.model });
197762
- const credentials = payload.proxyModel ? { kind: "ok" } : await checkConfiguredCredentials({
197763
- model: configuredModel,
197764
- authorized: getAuthorizedModels()
197765
- });
198108
+ if (payload.proxyModel) resolvedModel = void 0;
198109
+ const credentials = payload.proxyModel || poolSelected ? { kind: "ok" } : await checkConfiguredCredentials({ model: resolvedModel });
197766
198110
  if (credentials.kind === "dead") {
197767
198111
  throw new Error(
197768
198112
  buildRejectedCredentialError({
@@ -197774,13 +198118,6 @@ async function main() {
197774
198118
  })
197775
198119
  );
197776
198120
  }
197777
- if (credentials.kind === "fellBack" && configuredModel) {
197778
- log.warning(
197779
- `\xBB ${credentials.credential} was rejected by its provider \u2014 running ${credentials.replacement} instead of ${configuredModel}`
197780
- );
197781
- toolState.modelFallback = { from: configuredModel };
197782
- }
197783
- const resolvedModel = credentials.kind === "fellBack" ? credentials.replacement : configuredModel;
197784
198121
  vertexCredentials = materializeVertexCredentials({ model: resolvedModel });
197785
198122
  let agent2 = resolveAgent({
197786
198123
  model: resolvedModel,
@@ -198090,12 +198427,12 @@ ${instructions.user}` : null,
198090
198427
  if (payload.timeout === TIMEOUT_DISABLED) {
198091
198428
  result = await Promise.race([agentPromise, activityTimeout.promise]);
198092
198429
  } else {
198093
- const usable = resolveTimeoutMs(payload.timeout);
198094
- if (payload.timeout && usable === null) {
198430
+ const usable2 = resolveTimeoutMs(payload.timeout);
198431
+ if (payload.timeout && usable2 === null) {
198095
198432
  log.warning(`invalid timeout "${payload.timeout}" (use --notimeout to disable), using 1h`);
198096
198433
  }
198097
- const timeoutMs = usable ?? 36e5;
198098
- const actualTimeout = usable !== null ? payload.timeout : "1h";
198434
+ const timeoutMs = usable2 ?? 36e5;
198435
+ const actualTimeout = usable2 !== null ? payload.timeout : "1h";
198099
198436
  let timeoutId;
198100
198437
  const timeoutPromise = new Promise((_4, reject) => {
198101
198438
  timeoutId = setTimeout(() => {