pullfrog 0.1.9 → 0.1.11

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
@@ -97475,14 +97475,14 @@ var require_turndown_cjs = __commonJS({
97475
97475
  } else if (node2.nodeType === 1) {
97476
97476
  replacement = replacementForNode.call(self2, node2);
97477
97477
  }
97478
- return join19(output, replacement);
97478
+ return join20(output, replacement);
97479
97479
  }, "");
97480
97480
  }
97481
97481
  function postProcess(output) {
97482
97482
  var self2 = this;
97483
97483
  this.rules.forEach(function(rule) {
97484
97484
  if (typeof rule.append === "function") {
97485
- output = join19(output, rule.append(self2.options));
97485
+ output = join20(output, rule.append(self2.options));
97486
97486
  }
97487
97487
  });
97488
97488
  return output.replace(/^[\t\r\n]+/, "").replace(/[\t\r\n\s]+$/, "");
@@ -97494,7 +97494,7 @@ var require_turndown_cjs = __commonJS({
97494
97494
  if (whitespace.leading || whitespace.trailing) content = content.trim();
97495
97495
  return whitespace.leading + rule.replacement(content, node2, this.options) + whitespace.trailing;
97496
97496
  }
97497
- function join19(output, replacement) {
97497
+ function join20(output, replacement) {
97498
97498
  var s1 = trimTrailingNewlines(output);
97499
97499
  var s2 = trimLeadingNewlines(replacement);
97500
97500
  var nls = Math.max(output.length - s1.length, replacement.length - s2.length);
@@ -98926,7 +98926,7 @@ var require_fast_content_type_parse = __commonJS({
98926
98926
  // main.ts
98927
98927
  import { existsSync as existsSync7, readdirSync } from "node:fs";
98928
98928
  import { readFile as readFile4 } from "node:fs/promises";
98929
- import { join as join18 } from "node:path";
98929
+ import { join as join19 } from "node:path";
98930
98930
 
98931
98931
  // node_modules/.pnpm/@ark+util@0.56.0/node_modules/@ark/util/out/arrays.js
98932
98932
  var liftArray = (data) => Array.isArray(data) ? data : [data];
@@ -108021,6 +108021,25 @@ var providers = {
108021
108021
  }
108022
108022
  }
108023
108023
  }),
108024
+ vertex: provider({
108025
+ displayName: "Google Vertex AI",
108026
+ envVars: [
108027
+ "VERTEX_SERVICE_ACCOUNT_JSON",
108028
+ "GOOGLE_CLOUD_PROJECT",
108029
+ "VERTEX_LOCATION",
108030
+ "VERTEX_MODEL_ID"
108031
+ ],
108032
+ models: {
108033
+ // single routing entry — the actual Vertex AI model ID is read from
108034
+ // VERTEX_MODEL_ID at run time. see ModelRouting docs for why we don't
108035
+ // catalog individual Vertex models.
108036
+ byok: {
108037
+ displayName: "Google Vertex AI",
108038
+ resolve: "vertex",
108039
+ routing: "vertex"
108040
+ }
108041
+ }
108042
+ }),
108024
108043
  openrouter: provider({
108025
108044
  displayName: "OpenRouter",
108026
108045
  envVars: ["OPENROUTER_API_KEY"],
@@ -108184,9 +108203,13 @@ function resolveCliModel(slug2) {
108184
108203
  return resolveDisplayAlias(slug2)?.resolve;
108185
108204
  }
108186
108205
  var BEDROCK_MODEL_ID_ENV = "BEDROCK_MODEL_ID";
108206
+ var VERTEX_MODEL_ID_ENV = "VERTEX_MODEL_ID";
108187
108207
  function isBedrockAnthropicId(bedrockModelId) {
108188
108208
  return bedrockModelId.toLowerCase().split(/[./:]/).includes("anthropic");
108189
108209
  }
108210
+ function isVertexAnthropicId(vertexModelId) {
108211
+ return /^claude-/i.test(vertexModelId.trim());
108212
+ }
108190
108213
 
108191
108214
  // utils/buildPullfrogFooter.ts
108192
108215
  var PULLFROG_DIVIDER = "<!-- PULLFROG_DIVIDER_DO_NOT_REMOVE_PLZ -->";
@@ -142469,7 +142492,7 @@ var import_semver = __toESM(require_semver2(), 1);
142469
142492
  // package.json
142470
142493
  var package_default = {
142471
142494
  name: "pullfrog",
142472
- version: "0.1.9",
142495
+ version: "0.1.11",
142473
142496
  type: "module",
142474
142497
  bin: {
142475
142498
  pullfrog: "dist/cli.mjs",
@@ -147065,8 +147088,8 @@ function initToolState(params) {
147065
147088
 
147066
147089
  // agents/claude.ts
147067
147090
  import { execFileSync as execFileSync3 } from "node:child_process";
147068
- import { mkdirSync as mkdirSync4, writeFileSync as writeFileSync7 } from "node:fs";
147069
- import { join as join10 } from "node:path";
147091
+ import { mkdirSync as mkdirSync5, writeFileSync as writeFileSync8 } from "node:fs";
147092
+ import { join as join11 } from "node:path";
147070
147093
  import { performance as performance6 } from "node:perf_hooks";
147071
147094
 
147072
147095
  // utils/install.ts
@@ -147367,6 +147390,70 @@ var ThinkingTimer = class {
147367
147390
  }
147368
147391
  };
147369
147392
 
147393
+ // utils/vertex.ts
147394
+ import { randomUUID as randomUUID3 } from "node:crypto";
147395
+ import { mkdirSync as mkdirSync4, rmSync, writeFileSync as writeFileSync7 } from "node:fs";
147396
+ import { homedir } from "node:os";
147397
+ import { join as join10 } from "node:path";
147398
+ var VERTEX_SERVICE_ACCOUNT_JSON_ENV = "VERTEX_SERVICE_ACCOUNT_JSON";
147399
+ var GOOGLE_APPLICATION_CREDENTIALS_ENV = "GOOGLE_APPLICATION_CREDENTIALS";
147400
+ var GOOGLE_CLOUD_PROJECT_ENV = "GOOGLE_CLOUD_PROJECT";
147401
+ var VERTEX_LOCATION_ENV = "VERTEX_LOCATION";
147402
+ function hasEnvVar(name) {
147403
+ const value2 = process.env[name];
147404
+ return typeof value2 === "string" && value2.length > 0;
147405
+ }
147406
+ function isVertexRoute(model) {
147407
+ const vertexId = process.env[VERTEX_MODEL_ID_ENV]?.trim();
147408
+ return model !== void 0 && vertexId !== void 0 && vertexId === model;
147409
+ }
147410
+ function readProjectIdFromVertexServiceAccountJson() {
147411
+ const blob = process.env[VERTEX_SERVICE_ACCOUNT_JSON_ENV];
147412
+ if (!blob) return void 0;
147413
+ try {
147414
+ const parsed2 = JSON.parse(blob);
147415
+ if (!parsed2 || typeof parsed2 !== "object" || !("project_id" in parsed2)) {
147416
+ return void 0;
147417
+ }
147418
+ const projectId = parsed2.project_id;
147419
+ return typeof projectId === "string" && projectId.length > 0 ? projectId : void 0;
147420
+ } catch {
147421
+ return void 0;
147422
+ }
147423
+ }
147424
+ function createSecretDir() {
147425
+ const base = process.env.PULLFROG_SECRET_HOME || process.env.HOME || homedir();
147426
+ const secretDir = join10(base, ".pullfrog", "secrets", randomUUID3());
147427
+ mkdirSync4(secretDir, { recursive: true, mode: 448 });
147428
+ return secretDir;
147429
+ }
147430
+ function materializeVertexCredentials(params) {
147431
+ if (!isVertexRoute(params.model)) return void 0;
147432
+ const blob = process.env[VERTEX_SERVICE_ACCOUNT_JSON_ENV];
147433
+ if (!blob) return void 0;
147434
+ const secretDir = createSecretDir();
147435
+ const credentialsPath = join10(secretDir, "vertex-sa.json");
147436
+ writeFileSync7(credentialsPath, blob, { mode: 384 });
147437
+ process.env[GOOGLE_APPLICATION_CREDENTIALS_ENV] = credentialsPath;
147438
+ const projectId = readProjectIdFromVertexServiceAccountJson();
147439
+ if (projectId && !hasEnvVar(GOOGLE_CLOUD_PROJECT_ENV)) {
147440
+ process.env[GOOGLE_CLOUD_PROJECT_ENV] = projectId;
147441
+ }
147442
+ return { credentialsPath, secretDir };
147443
+ }
147444
+ function cleanupVertexCredentials(credentials) {
147445
+ if (!credentials) return;
147446
+ rmSync(credentials.secretDir, { recursive: true, force: true });
147447
+ }
147448
+ function applyClaudeVertexEnv(env2) {
147449
+ env2.CLAUDE_CODE_USE_VERTEX = "1";
147450
+ env2.ANTHROPIC_VERTEX_PROJECT_ID ??= env2[GOOGLE_CLOUD_PROJECT_ENV];
147451
+ env2.CLOUD_ML_REGION ??= env2[VERTEX_LOCATION_ENV];
147452
+ }
147453
+ function resolveVertexOpenCodeModel(model) {
147454
+ return isVertexRoute(model) && model ? `google-vertex/${model}` : void 0;
147455
+ }
147456
+
147370
147457
  // agents/postRun.ts
147371
147458
  import { readFile } from "node:fs/promises";
147372
147459
  function getUnsubmittedReview(toolState) {
@@ -147670,10 +147757,10 @@ async function installClaudeCli() {
147670
147757
  });
147671
147758
  }
147672
147759
  function writeMcpConfig(ctx) {
147673
- const configDir = join10(ctx.tmpdir, ".claude");
147674
- mkdirSync4(configDir, { recursive: true });
147675
- const configPath = join10(configDir, "mcp.json");
147676
- writeFileSync7(
147760
+ const configDir = join11(ctx.tmpdir, ".claude");
147761
+ mkdirSync5(configDir, { recursive: true });
147762
+ const configPath = join11(configDir, "mcp.json");
147763
+ writeFileSync8(
147677
147764
  configPath,
147678
147765
  JSON.stringify({
147679
147766
  mcpServers: {
@@ -148055,34 +148142,48 @@ ${stderrContext}`
148055
148142
  var MANAGED_SETTINGS_DIR = "/etc/claude-code";
148056
148143
  var MANAGED_SETTINGS_PATH = `${MANAGED_SETTINGS_DIR}/managed-settings.json`;
148057
148144
  var CODEX_AUTH_DENY_PATH = "~/.local/share/opencode/auth.json";
148058
- var managedSettings = {
148059
- allowManagedPermissionRulesOnly: true,
148060
- allowManagedHooksOnly: true,
148061
- permissions: {
148062
- deny: [
148063
- "Read(//proc/**)",
148064
- "Read(//sys/**)",
148065
- "Grep(//proc/**)",
148066
- "Grep(//sys/**)",
148067
- "Edit(//proc/**)",
148068
- "Edit(//sys/**)",
148069
- "Glob(//proc/**)",
148070
- "Glob(//sys/**)",
148071
- `Read(${CODEX_AUTH_DENY_PATH})`,
148072
- `Grep(${CODEX_AUTH_DENY_PATH})`,
148073
- `Edit(${CODEX_AUTH_DENY_PATH})`,
148074
- `Glob(${CODEX_AUTH_DENY_PATH})`
148075
- ]
148076
- },
148077
- sandbox: {
148078
- filesystem: {
148079
- denyRead: ["/proc", "/sys", CODEX_AUTH_DENY_PATH]
148145
+ function buildManagedSettings(ctx) {
148146
+ const secretDenyPaths = ctx.secretDenyPaths ?? [];
148147
+ const toolDeny = secretDenyPaths.flatMap((path3) => [
148148
+ `Read(${path3}/**)`,
148149
+ `Read(/${path3}/**)`,
148150
+ `Grep(${path3}/**)`,
148151
+ `Grep(/${path3}/**)`,
148152
+ `Edit(${path3}/**)`,
148153
+ `Edit(/${path3}/**)`,
148154
+ `Glob(${path3}/**)`,
148155
+ `Glob(/${path3}/**)`
148156
+ ]);
148157
+ return {
148158
+ allowManagedPermissionRulesOnly: true,
148159
+ allowManagedHooksOnly: true,
148160
+ permissions: {
148161
+ deny: [
148162
+ "Read(//proc/**)",
148163
+ "Read(//sys/**)",
148164
+ "Grep(//proc/**)",
148165
+ "Grep(//sys/**)",
148166
+ "Edit(//proc/**)",
148167
+ "Edit(//sys/**)",
148168
+ "Glob(//proc/**)",
148169
+ "Glob(//sys/**)",
148170
+ `Read(${CODEX_AUTH_DENY_PATH})`,
148171
+ `Grep(${CODEX_AUTH_DENY_PATH})`,
148172
+ `Edit(${CODEX_AUTH_DENY_PATH})`,
148173
+ `Glob(${CODEX_AUTH_DENY_PATH})`,
148174
+ ...toolDeny
148175
+ ]
148176
+ },
148177
+ sandbox: {
148178
+ filesystem: {
148179
+ denyRead: ["/proc", "/sys", CODEX_AUTH_DENY_PATH, ...secretDenyPaths]
148180
+ }
148080
148181
  }
148081
- }
148082
- };
148083
- function installManagedSettings() {
148182
+ };
148183
+ }
148184
+ function installManagedSettings(ctx) {
148084
148185
  if (process.env.CI !== "true") return;
148085
- const content = JSON.stringify(managedSettings, null, 2);
148186
+ const content = JSON.stringify(buildManagedSettings(ctx), null, 2);
148086
148187
  try {
148087
148188
  execFileSync3("sudo", ["mkdir", "-p", MANAGED_SETTINGS_DIR]);
148088
148189
  execFileSync3("sudo", ["tee", MANAGED_SETTINGS_PATH], {
@@ -148102,12 +148203,14 @@ var claude = agent({
148102
148203
  const specifier = ctx.payload.proxyModel ?? ctx.resolvedModel;
148103
148204
  const bedrockModelId = process.env[BEDROCK_MODEL_ID_ENV]?.trim();
148104
148205
  const isBedrockRoute = specifier !== void 0 && bedrockModelId !== void 0 && bedrockModelId === specifier && isBedrockAnthropicId(specifier);
148105
- const model = !specifier ? void 0 : isBedrockRoute ? specifier : stripProviderPrefix(specifier);
148206
+ const vertexModelId = process.env[VERTEX_MODEL_ID_ENV]?.trim();
148207
+ const isVertexRoute2 = specifier !== void 0 && vertexModelId !== void 0 && vertexModelId === specifier && isVertexAnthropicId(specifier);
148208
+ const model = !specifier ? void 0 : isBedrockRoute ? specifier : isVertexRoute2 ? void 0 : stripProviderPrefix(specifier);
148106
148209
  const homeEnv = {
148107
148210
  HOME: ctx.tmpdir,
148108
- XDG_CONFIG_HOME: join10(ctx.tmpdir, ".config")
148211
+ XDG_CONFIG_HOME: join11(ctx.tmpdir, ".config")
148109
148212
  };
148110
- mkdirSync4(join10(homeEnv.XDG_CONFIG_HOME, "claude"), { recursive: true });
148213
+ mkdirSync5(join11(homeEnv.XDG_CONFIG_HOME, "claude"), { recursive: true });
148111
148214
  const agentBrowserVersion = getDevDependencyVersion("agent-browser");
148112
148215
  addSkill({
148113
148216
  ref: `vercel-labs/agent-browser@v${agentBrowserVersion}`,
@@ -148118,7 +148221,7 @@ var claude = agent({
148118
148221
  installBundledSkills({ home: homeEnv.HOME });
148119
148222
  const mcpConfigPath = writeMcpConfig(ctx);
148120
148223
  const effort = resolveEffort(model);
148121
- installManagedSettings();
148224
+ installManagedSettings(ctx);
148122
148225
  const baseArgs = [
148123
148226
  cliPath,
148124
148227
  "--output-format",
@@ -148146,6 +148249,10 @@ var claude = agent({
148146
148249
  if (isBedrockRoute) {
148147
148250
  env2.CLAUDE_CODE_USE_BEDROCK = "1";
148148
148251
  }
148252
+ if (isVertexRoute2) {
148253
+ applyClaudeVertexEnv(env2);
148254
+ env2.ANTHROPIC_MODEL = specifier;
148255
+ }
148149
148256
  if (env2.CLAUDE_CODE_OAUTH_TOKEN && !isBedrockRoute && env2.ANTHROPIC_API_KEY) {
148150
148257
  log.debug(
148151
148258
  "\xBB CLAUDE_CODE_OAUTH_TOKEN present \u2014 stripping ANTHROPIC_API_KEY from Claude Code env so the OAuth subscription is used"
@@ -148187,8 +148294,8 @@ var claude = agent({
148187
148294
 
148188
148295
  // agents/opencode_v2.ts
148189
148296
  var core2 = __toESM(require_core(), 1);
148190
- import { mkdirSync as mkdirSync6, writeFileSync as writeFileSync9 } from "node:fs";
148191
- import { join as join12 } from "node:path";
148297
+ import { mkdirSync as mkdirSync7, writeFileSync as writeFileSync10 } from "node:fs";
148298
+ import { join as join13 } from "node:path";
148192
148299
  import { performance as performance7 } from "node:perf_hooks";
148193
148300
 
148194
148301
  // utils/agentHangReport.ts
@@ -148287,9 +148394,9 @@ function formatBillingExhaustedBody(diagnostic) {
148287
148394
  }
148288
148395
 
148289
148396
  // utils/codexHome.ts
148290
- import { mkdirSync as mkdirSync5, writeFileSync as writeFileSync8 } from "node:fs";
148291
- import { homedir } from "node:os";
148292
- import { join as join11 } from "node:path";
148397
+ import { mkdirSync as mkdirSync6, writeFileSync as writeFileSync9 } from "node:fs";
148398
+ import { homedir as homedir2 } from "node:os";
148399
+ import { join as join12 } from "node:path";
148293
148400
  var CODEX_AUTH_ENV = "CODEX_AUTH_JSON";
148294
148401
  function installCodexAuth() {
148295
148402
  const raw2 = process.env[CODEX_AUTH_ENV];
@@ -148299,9 +148406,9 @@ function installCodexAuth() {
148299
148406
  log.warning(`\xBB ${CODEX_AUTH_ENV} present but malformed; ignoring`);
148300
148407
  return null;
148301
148408
  }
148302
- const xdgDataHome = join11(homedir(), ".local", "share");
148303
- const opencodeDir = join11(xdgDataHome, "opencode");
148304
- const authPath = join11(opencodeDir, "auth.json");
148409
+ const xdgDataHome = join12(homedir2(), ".local", "share");
148410
+ const opencodeDir = join12(xdgDataHome, "opencode");
148411
+ const authPath = join12(opencodeDir, "auth.json");
148305
148412
  const opencodeAuth = {
148306
148413
  openai: {
148307
148414
  type: "oauth",
@@ -148314,8 +148421,8 @@ function installCodexAuth() {
148314
148421
  ...blob.tokens.account_id ? { accountId: blob.tokens.account_id } : {}
148315
148422
  }
148316
148423
  };
148317
- mkdirSync5(opencodeDir, { recursive: true });
148318
- writeFileSync8(authPath, `${JSON.stringify(opencodeAuth, null, 2)}
148424
+ mkdirSync6(opencodeDir, { recursive: true });
148425
+ writeFileSync9(authPath, `${JSON.stringify(opencodeAuth, null, 2)}
148319
148426
  `, { mode: 384 });
148320
148427
  log.info(`\xBB installed Codex auth at ${authPath}`);
148321
148428
  return { authPath, xdgDataHome, originalRefresh: blob.tokens.refresh_token };
@@ -148971,16 +149078,17 @@ var opencode = agent({
148971
149078
  const rawModel = ctx.payload.proxyModel ?? ctx.resolvedModel ?? autoSelectModel(cliPath);
148972
149079
  const bedrockModelId = process.env[BEDROCK_MODEL_ID_ENV]?.trim();
148973
149080
  const isBedrockRoute = rawModel !== void 0 && bedrockModelId !== void 0 && bedrockModelId === rawModel;
148974
- const model = isBedrockRoute ? `amazon-bedrock/${rawModel}` : rawModel;
149081
+ const vertexModel = resolveVertexOpenCodeModel(rawModel);
149082
+ const model = vertexModel ?? (isBedrockRoute ? `amazon-bedrock/${rawModel}` : rawModel);
148975
149083
  const homeEnv = {
148976
149084
  HOME: ctx.tmpdir,
148977
- XDG_CONFIG_HOME: join12(ctx.tmpdir, ".config")
149085
+ XDG_CONFIG_HOME: join13(ctx.tmpdir, ".config")
148978
149086
  };
148979
- mkdirSync6(join12(homeEnv.XDG_CONFIG_HOME, "opencode"), { recursive: true });
148980
- const opencodePluginDir = join12(homeEnv.XDG_CONFIG_HOME, "opencode", "plugin");
148981
- mkdirSync6(opencodePluginDir, { recursive: true });
148982
- writeFileSync9(
148983
- join12(opencodePluginDir, PULLFROG_OPENCODE_PLUGIN_FILENAME),
149087
+ mkdirSync7(join13(homeEnv.XDG_CONFIG_HOME, "opencode"), { recursive: true });
149088
+ const opencodePluginDir = join13(homeEnv.XDG_CONFIG_HOME, "opencode", "plugin");
149089
+ mkdirSync7(opencodePluginDir, { recursive: true });
149090
+ writeFileSync10(
149091
+ join13(opencodePluginDir, PULLFROG_OPENCODE_PLUGIN_FILENAME),
148984
149092
  PULLFROG_OPENCODE_PLUGIN_SOURCE
148985
149093
  );
148986
149094
  const agentBrowserVersion = getDevDependencyVersion("agent-browser");
@@ -149050,15 +149158,18 @@ var opencode = agent({
149050
149158
  var agents = { claude, opencode };
149051
149159
 
149052
149160
  // utils/agent.ts
149053
- function hasEnvVar(name) {
149161
+ function hasEnvVar2(name) {
149054
149162
  const val = process.env[name];
149055
149163
  return typeof val === "string" && val.length > 0;
149056
149164
  }
149057
149165
  function hasClaudeCodeAuth() {
149058
- return hasEnvVar("CLAUDE_CODE_OAUTH_TOKEN") || hasEnvVar("ANTHROPIC_API_KEY");
149166
+ return hasEnvVar2("CLAUDE_CODE_OAUTH_TOKEN") || hasEnvVar2("ANTHROPIC_API_KEY");
149059
149167
  }
149060
149168
  function hasBedrockAuth() {
149061
- return hasEnvVar("AWS_BEARER_TOKEN_BEDROCK") || hasEnvVar("AWS_ACCESS_KEY_ID") && hasEnvVar("AWS_SECRET_ACCESS_KEY");
149169
+ return hasEnvVar2("AWS_BEARER_TOKEN_BEDROCK") || hasEnvVar2("AWS_ACCESS_KEY_ID") && hasEnvVar2("AWS_SECRET_ACCESS_KEY");
149170
+ }
149171
+ function hasVertexAuth() {
149172
+ return hasEnvVar2(VERTEX_SERVICE_ACCOUNT_JSON_ENV);
149062
149173
  }
149063
149174
  function resolveSlug(slug2) {
149064
149175
  const alias = resolveDisplayAlias(slug2);
@@ -149066,11 +149177,20 @@ function resolveSlug(slug2) {
149066
149177
  const bedrockId = process.env[BEDROCK_MODEL_ID_ENV]?.trim();
149067
149178
  if (!bedrockId) {
149068
149179
  throw new Error(
149069
- `${BEDROCK_MODEL_ID_ENV} env var is required when the model is set to "${slug2}". set it to an AWS Bedrock model ID (e.g. "us.anthropic.claude-opus-4-7", "amazon.nova-pro-v1:0"). see https://docs.pullfrog.com/bedrock for setup.`
149180
+ `${BEDROCK_MODEL_ID_ENV} env var is required when the model is set to "${slug2}". set it to an AWS Bedrock model ID from the Bedrock console. see https://docs.pullfrog.com/bedrock for setup.`
149070
149181
  );
149071
149182
  }
149072
149183
  return bedrockId;
149073
149184
  }
149185
+ if (alias?.routing === "vertex") {
149186
+ const vertexId = process.env[VERTEX_MODEL_ID_ENV]?.trim();
149187
+ if (!vertexId) {
149188
+ throw new Error(
149189
+ `${VERTEX_MODEL_ID_ENV} env var is required when the model is set to "${slug2}". set it to a Google Vertex AI model ID from Model Garden. see https://docs.pullfrog.com/vertex for setup.`
149190
+ );
149191
+ }
149192
+ return vertexId;
149193
+ }
149074
149194
  return resolveCliModel(slug2);
149075
149195
  }
149076
149196
  function resolveModel(ctx) {
@@ -149098,6 +149218,9 @@ function resolveAgent(ctx) {
149098
149218
  if (ctx.model && hasBedrockAuth() && process.env[BEDROCK_MODEL_ID_ENV]?.trim() === ctx.model) {
149099
149219
  return isBedrockAnthropicId(ctx.model) ? agents.claude : agents.opencode;
149100
149220
  }
149221
+ if (ctx.model && hasVertexAuth() && process.env[VERTEX_MODEL_ID_ENV]?.trim() === ctx.model) {
149222
+ return isVertexAnthropicId(ctx.model) ? agents.claude : agents.opencode;
149223
+ }
149101
149224
  if (ctx.model) {
149102
149225
  try {
149103
149226
  const provider2 = getModelProvider(ctx.model);
@@ -149138,26 +149261,51 @@ add the missing secret(s) to your GitHub repository at ${githubSecretsUrl}, then
149138
149261
 
149139
149262
  for full setup instructions, see https://docs.pullfrog.com/bedrock`;
149140
149263
  }
149141
- function hasEnvVar2(name) {
149264
+ function buildVertexSetupError(params) {
149265
+ const githubSecretsUrl = `https://github.com/${params.owner}/${params.name}/settings/secrets/actions`;
149266
+ return `Google Vertex AI model selected but required configuration is missing: ${params.missing.join(", ")}.
149267
+
149268
+ add the missing secret(s) to your GitHub repository at ${githubSecretsUrl}, then reference them in your workflow's \`env:\` block:
149269
+
149270
+ ${VERTEX_SERVICE_ACCOUNT_JSON_ENV}: \${{ secrets.${VERTEX_SERVICE_ACCOUNT_JSON_ENV} }}
149271
+ ${GOOGLE_CLOUD_PROJECT_ENV}: my-project
149272
+ ${VERTEX_LOCATION_ENV}: global
149273
+ ${VERTEX_MODEL_ID_ENV}: <vertex-model-id>
149274
+
149275
+ for full setup instructions, see https://docs.pullfrog.com/vertex`;
149276
+ }
149277
+ function hasEnvVar3(name) {
149142
149278
  const value2 = process.env[name];
149143
149279
  return typeof value2 === "string" && value2.length > 0;
149144
149280
  }
149145
149281
  function hasProviderKey(model) {
149146
149282
  const requiredVars = getModelEnvVars(model);
149147
149283
  if (requiredVars.length === 0) return true;
149148
- return requiredVars.some((v) => hasEnvVar2(v));
149284
+ return requiredVars.some((v) => hasEnvVar3(v));
149149
149285
  }
149150
149286
  function validateBedrockSetup(params) {
149151
- const hasAuth = hasEnvVar2("AWS_BEARER_TOKEN_BEDROCK") || hasEnvVar2("AWS_ACCESS_KEY_ID") && hasEnvVar2("AWS_SECRET_ACCESS_KEY");
149287
+ const hasAuth = hasEnvVar3("AWS_BEARER_TOKEN_BEDROCK") || hasEnvVar3("AWS_ACCESS_KEY_ID") && hasEnvVar3("AWS_SECRET_ACCESS_KEY");
149152
149288
  const missing = [];
149153
149289
  if (!hasAuth)
149154
149290
  missing.push("AWS_BEARER_TOKEN_BEDROCK (or AWS_ACCESS_KEY_ID + AWS_SECRET_ACCESS_KEY)");
149155
- if (!hasEnvVar2("AWS_REGION")) missing.push("AWS_REGION");
149156
- if (!hasEnvVar2(BEDROCK_MODEL_ID_ENV)) missing.push(BEDROCK_MODEL_ID_ENV);
149291
+ if (!hasEnvVar3("AWS_REGION")) missing.push("AWS_REGION");
149292
+ if (!hasEnvVar3(BEDROCK_MODEL_ID_ENV)) missing.push(BEDROCK_MODEL_ID_ENV);
149157
149293
  if (missing.length > 0) {
149158
149294
  throw new Error(buildBedrockSetupError({ owner: params.owner, name: params.name, missing }));
149159
149295
  }
149160
149296
  }
149297
+ function validateVertexSetup(params) {
149298
+ const hasAuth = hasEnvVar3(VERTEX_SERVICE_ACCOUNT_JSON_ENV);
149299
+ const hasProject = hasEnvVar3(GOOGLE_CLOUD_PROJECT_ENV) || readProjectIdFromVertexServiceAccountJson() !== void 0;
149300
+ const missing = [];
149301
+ if (!hasAuth) missing.push(VERTEX_SERVICE_ACCOUNT_JSON_ENV);
149302
+ if (!hasProject) missing.push(GOOGLE_CLOUD_PROJECT_ENV);
149303
+ if (!hasEnvVar3(VERTEX_LOCATION_ENV)) missing.push(VERTEX_LOCATION_ENV);
149304
+ if (!hasEnvVar3(VERTEX_MODEL_ID_ENV)) missing.push(VERTEX_MODEL_ID_ENV);
149305
+ if (missing.length > 0) {
149306
+ throw new Error(buildVertexSetupError({ owner: params.owner, name: params.name, missing }));
149307
+ }
149308
+ }
149161
149309
  function validateAgentApiKey(params) {
149162
149310
  if (params.model) {
149163
149311
  const alias = resolveDisplayAlias(params.model);
@@ -149165,16 +149313,24 @@ function validateAgentApiKey(params) {
149165
149313
  validateBedrockSetup({ owner: params.owner, name: params.name });
149166
149314
  return;
149167
149315
  }
149316
+ if (alias?.routing === "vertex") {
149317
+ validateVertexSetup({ owner: params.owner, name: params.name });
149318
+ return;
149319
+ }
149168
149320
  if (!params.model.includes("/")) {
149321
+ if (process.env[VERTEX_MODEL_ID_ENV]?.trim() === params.model) {
149322
+ validateVertexSetup({ owner: params.owner, name: params.name });
149323
+ return;
149324
+ }
149169
149325
  validateBedrockSetup({ owner: params.owner, name: params.name });
149170
149326
  return;
149171
149327
  }
149172
149328
  const requiredVars = getModelEnvVars(params.model);
149173
149329
  if (requiredVars.length === 0) return;
149174
- if (requiredVars.some((v) => hasEnvVar2(v))) return;
149330
+ if (requiredVars.some((v) => hasEnvVar3(v))) return;
149175
149331
  throw new Error(buildMissingApiKeyError({ owner: params.owner, name: params.name }));
149176
149332
  }
149177
- const hasAnyKey = [...knownApiKeys].some((k) => hasEnvVar2(k));
149333
+ const hasAnyKey = [...knownApiKeys].some((k) => hasEnvVar3(k));
149178
149334
  if (!hasAnyKey) {
149179
149335
  throw new Error(buildMissingApiKeyError({ owner: params.owner, name: params.name }));
149180
149336
  }
@@ -149309,10 +149465,10 @@ function selectFallbackModelIfNeeded(input) {
149309
149465
  }
149310
149466
 
149311
149467
  // utils/gitAuthServer.ts
149312
- import { randomUUID as randomUUID3 } from "node:crypto";
149313
- import { writeFileSync as writeFileSync10 } from "node:fs";
149468
+ import { randomUUID as randomUUID4 } from "node:crypto";
149469
+ import { writeFileSync as writeFileSync11 } from "node:fs";
149314
149470
  import { createServer as createServer2 } from "node:http";
149315
- import { join as join13 } from "node:path";
149471
+ import { join as join14 } from "node:path";
149316
149472
  var CODE_TTL_MS = 5 * 60 * 1e3;
149317
149473
  var TAMPER_WINDOW_MS = 6e4;
149318
149474
  function revokeGitHubToken(token) {
@@ -149372,7 +149528,7 @@ async function startGitAuthServer(tmpdir3) {
149372
149528
  const port = rawAddr.port;
149373
149529
  log.debug(`git auth server listening on 127.0.0.1:${port}`);
149374
149530
  function register4(token) {
149375
- const code = randomUUID3();
149531
+ const code = randomUUID4();
149376
149532
  const timeout = setTimeout(() => {
149377
149533
  codes.delete(code);
149378
149534
  log.debug(`git auth code expired: ${code.slice(0, 8)}...`);
@@ -149382,9 +149538,9 @@ async function startGitAuthServer(tmpdir3) {
149382
149538
  return code;
149383
149539
  }
149384
149540
  function writeAskpassScript(code) {
149385
- const scriptId = randomUUID3();
149541
+ const scriptId = randomUUID4();
149386
149542
  const scriptName = `askpass-${scriptId}.js`;
149387
- const scriptPath = join13(tmpdir3, scriptName);
149543
+ const scriptPath = join14(tmpdir3, scriptName);
149388
149544
  const content = [
149389
149545
  `#!/usr/bin/env node`,
149390
149546
  `var a=process.argv[2]||"";`,
@@ -149399,7 +149555,7 @@ async function startGitAuthServer(tmpdir3) {
149399
149555
  `try{require("fs").unlinkSync("${scriptPath.replace(/\\/g, "\\\\")}")}catch(e){}`,
149400
149556
  `})}).on("error",function(){process.exit(1)})}`
149401
149557
  ].join("\n");
149402
- writeFileSync10(scriptPath, content, { mode: 448 });
149558
+ writeFileSync11(scriptPath, content, { mode: 448 });
149403
149559
  return scriptPath;
149404
149560
  }
149405
149561
  async function close() {
@@ -149423,7 +149579,7 @@ async function startGitAuthServer(tmpdir3) {
149423
149579
  var core3 = __toESM(require_core(), 1);
149424
149580
  import { createSign } from "node:crypto";
149425
149581
  import { rename, writeFile } from "node:fs/promises";
149426
- import { dirname as dirname3, join as join14 } from "node:path";
149582
+ import { dirname as dirname3, join as join15 } from "node:path";
149427
149583
 
149428
149584
  // node_modules/.pnpm/@octokit+plugin-throttling@11.0.3_@octokit+core@7.0.5/node_modules/@octokit/plugin-throttling/dist-bundle/index.js
149429
149585
  var import_light = __toESM(require_light(), 1);
@@ -153281,7 +153437,7 @@ function getGitHubUsageSummary() {
153281
153437
  }
153282
153438
  async function writeGitHubUsageSummaryToFile(path3) {
153283
153439
  const summary2 = getGitHubUsageSummary();
153284
- const tmpPath = join14(dirname3(path3), `.usage-summary-${process.pid}.tmp`);
153440
+ const tmpPath = join15(dirname3(path3), `.usage-summary-${process.pid}.tmp`);
153285
153441
  await writeFile(tmpPath, JSON.stringify(summary2));
153286
153442
  await rename(tmpPath, path3);
153287
153443
  }
@@ -153681,7 +153837,7 @@ function resolveInstructions(ctx) {
153681
153837
 
153682
153838
  // utils/learnings.ts
153683
153839
  import { mkdir, readFile as readFile2, writeFile as writeFile2 } from "node:fs/promises";
153684
- import { dirname as dirname4, join as join15 } from "node:path";
153840
+ import { dirname as dirname4, join as join16 } from "node:path";
153685
153841
 
153686
153842
  // utils/learningsTruncate.ts
153687
153843
  var MAX_LEARNINGS_LENGTH = 1e5;
@@ -153698,7 +153854,7 @@ function truncateAtLineBoundary(body, cap) {
153698
153854
  // utils/learnings.ts
153699
153855
  var LEARNINGS_FILE_NAME = "pullfrog-learnings.md";
153700
153856
  function learningsFilePath(tmpdir3) {
153701
- return join15(tmpdir3, LEARNINGS_FILE_NAME);
153857
+ return join16(tmpdir3, LEARNINGS_FILE_NAME);
153702
153858
  }
153703
153859
  async function seedLearningsFile(params) {
153704
153860
  const path3 = learningsFilePath(params.tmpdir);
@@ -154378,7 +154534,7 @@ async function runProxyResolution(ctx) {
154378
154534
 
154379
154535
  // utils/prSummary.ts
154380
154536
  import { mkdir as mkdir2, readFile as readFile3, writeFile as writeFile3 } from "node:fs/promises";
154381
- import { dirname as dirname5, join as join16 } from "node:path";
154537
+ import { dirname as dirname5, join as join17 } from "node:path";
154382
154538
  var SUMMARY_FILE_NAME = "pullfrog-summary.md";
154383
154539
  var SUMMARY_SCAFFOLD = `# PR summary
154384
154540
 
@@ -154388,7 +154544,7 @@ var SUMMARY_SCAFFOLD = `# PR summary
154388
154544
  var MIN_SNAPSHOT_LENGTH = 60;
154389
154545
  var MAX_SNAPSHOT_LENGTH = 32768;
154390
154546
  function summaryFilePath(tmpdir3) {
154391
- return join16(tmpdir3, SUMMARY_FILE_NAME);
154547
+ return join17(tmpdir3, SUMMARY_FILE_NAME);
154392
154548
  }
154393
154549
  async function seedSummaryFile(params) {
154394
154550
  const path3 = summaryFilePath(params.tmpdir);
@@ -154812,9 +154968,9 @@ function logRunStartup(ctx) {
154812
154968
  import { execFileSync as execFileSync5, execSync as execSync3 } from "node:child_process";
154813
154969
  import { mkdtempSync } from "node:fs";
154814
154970
  import { tmpdir as tmpdir2 } from "node:os";
154815
- import { join as join17 } from "node:path";
154971
+ import { join as join18 } from "node:path";
154816
154972
  function createTempDirectory() {
154817
- const sharedTempDir = mkdtempSync(join17(tmpdir2(), "pullfrog-"));
154973
+ const sharedTempDir = mkdtempSync(join18(tmpdir2(), "pullfrog-"));
154818
154974
  process.env.PULLFROG_TEMP_DIR = sharedTempDir;
154819
154975
  log.info(`\xBB created temp dir at ${sharedTempDir}`);
154820
154976
  return sharedTempDir;
@@ -155146,6 +155302,7 @@ async function main() {
155146
155302
  let toolContext;
155147
155303
  let progressCallbackDisabled = false;
155148
155304
  let todoTracker;
155305
+ let vertexCredentials;
155149
155306
  try {
155150
155307
  var _stack = [];
155151
155308
  try {
@@ -155180,6 +155337,7 @@ async function main() {
155180
155337
  );
155181
155338
  toolState.modelFallback = { from: fallback.from };
155182
155339
  }
155340
+ vertexCredentials = materializeVertexCredentials({ model: resolvedModel });
155183
155341
  const agent2 = resolveAgent({ model: resolvedModel });
155184
155342
  toolState.model = payload.proxyModel ?? resolvedModel ?? effectiveSlug;
155185
155343
  validateAgentApiKey({
@@ -155291,7 +155449,7 @@ ${instructions.user}` : null,
155291
155449
  log.info(instructions.full);
155292
155450
  });
155293
155451
  if (agentId === "opencode") {
155294
- const pluginDir = join18(process.cwd(), ".opencode", "plugin");
155452
+ const pluginDir = join19(process.cwd(), ".opencode", "plugin");
155295
155453
  const hasPlugins = existsSync7(pluginDir) && readdirSync(pluginDir).some((f) => /\.[jt]sx?$/.test(f));
155296
155454
  if (hasPlugins && toolState.dependencyInstallation?.promise) {
155297
155455
  log.info(
@@ -155347,6 +155505,7 @@ ${instructions.user}` : null,
155347
155505
  resolvedModel,
155348
155506
  mcpServerUrl: mcpHttpServer.url,
155349
155507
  tmpdir: tmpdir3,
155508
+ secretDenyPaths: vertexCredentials ? [vertexCredentials.secretDir] : [],
155350
155509
  instructions,
155351
155510
  todoTracker,
155352
155511
  stopScript: runContext.repoSettings.stopScript,
@@ -155450,6 +155609,7 @@ ${instructions.user}` : null,
155450
155609
  await patchWorkflowRunFields(toolContext, patch);
155451
155610
  }
155452
155611
  }
155612
+ cleanupVertexCredentials(vertexCredentials);
155453
155613
  }
155454
155614
  } catch (_2) {
155455
155615
  var _error2 = _2, _hasError2 = true;
package/dist/internal.js CHANGED
@@ -291,6 +291,25 @@ var providers = {
291
291
  }
292
292
  }
293
293
  }),
294
+ vertex: provider({
295
+ displayName: "Google Vertex AI",
296
+ envVars: [
297
+ "VERTEX_SERVICE_ACCOUNT_JSON",
298
+ "GOOGLE_CLOUD_PROJECT",
299
+ "VERTEX_LOCATION",
300
+ "VERTEX_MODEL_ID"
301
+ ],
302
+ models: {
303
+ // single routing entry — the actual Vertex AI model ID is read from
304
+ // VERTEX_MODEL_ID at run time. see ModelRouting docs for why we don't
305
+ // catalog individual Vertex models.
306
+ byok: {
307
+ displayName: "Google Vertex AI",
308
+ resolve: "vertex",
309
+ routing: "vertex"
310
+ }
311
+ }
312
+ }),
294
313
  openrouter: provider({
295
314
  displayName: "OpenRouter",
296
315
  envVars: ["OPENROUTER_API_KEY"],