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.
@@ -90,6 +90,8 @@ export interface AgentRunContext {
90
90
  resolvedModel?: string | undefined;
91
91
  mcpServerUrl: string;
92
92
  tmpdir: string;
93
+ /** harness-owned secret paths that agent filesystem tools must never read. */
94
+ secretDenyPaths?: string[] | undefined;
93
95
  instructions: ResolvedInstructions;
94
96
  todoTracker?: TodoTracker | undefined;
95
97
  /**
package/dist/cli.mjs CHANGED
@@ -97748,14 +97748,14 @@ var require_turndown_cjs = __commonJS({
97748
97748
  } else if (node2.nodeType === 1) {
97749
97749
  replacement = replacementForNode.call(self2, node2);
97750
97750
  }
97751
- return join20(output, replacement);
97751
+ return join21(output, replacement);
97752
97752
  }, "");
97753
97753
  }
97754
97754
  function postProcess(output) {
97755
97755
  var self2 = this;
97756
97756
  this.rules.forEach(function(rule) {
97757
97757
  if (typeof rule.append === "function") {
97758
- output = join20(output, rule.append(self2.options));
97758
+ output = join21(output, rule.append(self2.options));
97759
97759
  }
97760
97760
  });
97761
97761
  return output.replace(/^[\t\r\n]+/, "").replace(/[\t\r\n\s]+$/, "");
@@ -97767,7 +97767,7 @@ var require_turndown_cjs = __commonJS({
97767
97767
  if (whitespace.leading || whitespace.trailing) content = content.trim();
97768
97768
  return whitespace.leading + rule.replacement(content, node2, this.options) + whitespace.trailing;
97769
97769
  }
97770
- function join20(output, replacement) {
97770
+ function join21(output, replacement) {
97771
97771
  var s1 = trimTrailingNewlines(output);
97772
97772
  var s2 = trimLeadingNewlines(replacement);
97773
97773
  var nls = Math.max(output.length - s1.length, replacement.length - s2.length);
@@ -100666,7 +100666,7 @@ import { dirname as dirname6 } from "node:path";
100666
100666
  // main.ts
100667
100667
  import { existsSync as existsSync7, readdirSync } from "node:fs";
100668
100668
  import { readFile as readFile4 } from "node:fs/promises";
100669
- import { join as join19 } from "node:path";
100669
+ import { join as join20 } from "node:path";
100670
100670
 
100671
100671
  // node_modules/.pnpm/@ark+util@0.56.0/node_modules/@ark/util/out/arrays.js
100672
100672
  var liftArray = (data) => Array.isArray(data) ? data : [data];
@@ -109761,6 +109761,25 @@ var providers = {
109761
109761
  }
109762
109762
  }
109763
109763
  }),
109764
+ vertex: provider({
109765
+ displayName: "Google Vertex AI",
109766
+ envVars: [
109767
+ "VERTEX_SERVICE_ACCOUNT_JSON",
109768
+ "GOOGLE_CLOUD_PROJECT",
109769
+ "VERTEX_LOCATION",
109770
+ "VERTEX_MODEL_ID"
109771
+ ],
109772
+ models: {
109773
+ // single routing entry — the actual Vertex AI model ID is read from
109774
+ // VERTEX_MODEL_ID at run time. see ModelRouting docs for why we don't
109775
+ // catalog individual Vertex models.
109776
+ byok: {
109777
+ displayName: "Google Vertex AI",
109778
+ resolve: "vertex",
109779
+ routing: "vertex"
109780
+ }
109781
+ }
109782
+ }),
109764
109783
  openrouter: provider({
109765
109784
  displayName: "OpenRouter",
109766
109785
  envVars: ["OPENROUTER_API_KEY"],
@@ -109924,9 +109943,13 @@ function resolveCliModel(slug2) {
109924
109943
  return resolveDisplayAlias(slug2)?.resolve;
109925
109944
  }
109926
109945
  var BEDROCK_MODEL_ID_ENV = "BEDROCK_MODEL_ID";
109946
+ var VERTEX_MODEL_ID_ENV = "VERTEX_MODEL_ID";
109927
109947
  function isBedrockAnthropicId(bedrockModelId) {
109928
109948
  return bedrockModelId.toLowerCase().split(/[./:]/).includes("anthropic");
109929
109949
  }
109950
+ function isVertexAnthropicId(vertexModelId) {
109951
+ return /^claude-/i.test(vertexModelId.trim());
109952
+ }
109930
109953
 
109931
109954
  // utils/buildPullfrogFooter.ts
109932
109955
  var PULLFROG_DIVIDER = "<!-- PULLFROG_DIVIDER_DO_NOT_REMOVE_PLZ -->";
@@ -144209,7 +144232,7 @@ var import_semver = __toESM(require_semver2(), 1);
144209
144232
  // package.json
144210
144233
  var package_default = {
144211
144234
  name: "pullfrog",
144212
- version: "0.1.9",
144235
+ version: "0.1.11",
144213
144236
  type: "module",
144214
144237
  bin: {
144215
144238
  pullfrog: "dist/cli.mjs",
@@ -148805,8 +148828,8 @@ function initToolState(params) {
148805
148828
 
148806
148829
  // agents/claude.ts
148807
148830
  import { execFileSync as execFileSync4 } from "node:child_process";
148808
- import { mkdirSync as mkdirSync4, writeFileSync as writeFileSync8 } from "node:fs";
148809
- import { join as join11 } from "node:path";
148831
+ import { mkdirSync as mkdirSync5, writeFileSync as writeFileSync9 } from "node:fs";
148832
+ import { join as join12 } from "node:path";
148810
148833
  import { performance as performance6 } from "node:perf_hooks";
148811
148834
 
148812
148835
  // utils/install.ts
@@ -149107,6 +149130,70 @@ var ThinkingTimer = class {
149107
149130
  }
149108
149131
  };
149109
149132
 
149133
+ // utils/vertex.ts
149134
+ import { randomUUID as randomUUID3 } from "node:crypto";
149135
+ import { mkdirSync as mkdirSync4, rmSync as rmSync2, writeFileSync as writeFileSync8 } from "node:fs";
149136
+ import { homedir } from "node:os";
149137
+ import { join as join11 } from "node:path";
149138
+ var VERTEX_SERVICE_ACCOUNT_JSON_ENV = "VERTEX_SERVICE_ACCOUNT_JSON";
149139
+ var GOOGLE_APPLICATION_CREDENTIALS_ENV = "GOOGLE_APPLICATION_CREDENTIALS";
149140
+ var GOOGLE_CLOUD_PROJECT_ENV = "GOOGLE_CLOUD_PROJECT";
149141
+ var VERTEX_LOCATION_ENV = "VERTEX_LOCATION";
149142
+ function hasEnvVar(name) {
149143
+ const value2 = process.env[name];
149144
+ return typeof value2 === "string" && value2.length > 0;
149145
+ }
149146
+ function isVertexRoute(model) {
149147
+ const vertexId = process.env[VERTEX_MODEL_ID_ENV]?.trim();
149148
+ return model !== void 0 && vertexId !== void 0 && vertexId === model;
149149
+ }
149150
+ function readProjectIdFromVertexServiceAccountJson() {
149151
+ const blob = process.env[VERTEX_SERVICE_ACCOUNT_JSON_ENV];
149152
+ if (!blob) return void 0;
149153
+ try {
149154
+ const parsed2 = JSON.parse(blob);
149155
+ if (!parsed2 || typeof parsed2 !== "object" || !("project_id" in parsed2)) {
149156
+ return void 0;
149157
+ }
149158
+ const projectId = parsed2.project_id;
149159
+ return typeof projectId === "string" && projectId.length > 0 ? projectId : void 0;
149160
+ } catch {
149161
+ return void 0;
149162
+ }
149163
+ }
149164
+ function createSecretDir() {
149165
+ const base = process.env.PULLFROG_SECRET_HOME || process.env.HOME || homedir();
149166
+ const secretDir = join11(base, ".pullfrog", "secrets", randomUUID3());
149167
+ mkdirSync4(secretDir, { recursive: true, mode: 448 });
149168
+ return secretDir;
149169
+ }
149170
+ function materializeVertexCredentials(params) {
149171
+ if (!isVertexRoute(params.model)) return void 0;
149172
+ const blob = process.env[VERTEX_SERVICE_ACCOUNT_JSON_ENV];
149173
+ if (!blob) return void 0;
149174
+ const secretDir = createSecretDir();
149175
+ const credentialsPath = join11(secretDir, "vertex-sa.json");
149176
+ writeFileSync8(credentialsPath, blob, { mode: 384 });
149177
+ process.env[GOOGLE_APPLICATION_CREDENTIALS_ENV] = credentialsPath;
149178
+ const projectId = readProjectIdFromVertexServiceAccountJson();
149179
+ if (projectId && !hasEnvVar(GOOGLE_CLOUD_PROJECT_ENV)) {
149180
+ process.env[GOOGLE_CLOUD_PROJECT_ENV] = projectId;
149181
+ }
149182
+ return { credentialsPath, secretDir };
149183
+ }
149184
+ function cleanupVertexCredentials(credentials) {
149185
+ if (!credentials) return;
149186
+ rmSync2(credentials.secretDir, { recursive: true, force: true });
149187
+ }
149188
+ function applyClaudeVertexEnv(env2) {
149189
+ env2.CLAUDE_CODE_USE_VERTEX = "1";
149190
+ env2.ANTHROPIC_VERTEX_PROJECT_ID ??= env2[GOOGLE_CLOUD_PROJECT_ENV];
149191
+ env2.CLOUD_ML_REGION ??= env2[VERTEX_LOCATION_ENV];
149192
+ }
149193
+ function resolveVertexOpenCodeModel(model) {
149194
+ return isVertexRoute(model) && model ? `google-vertex/${model}` : void 0;
149195
+ }
149196
+
149110
149197
  // agents/postRun.ts
149111
149198
  import { readFile } from "node:fs/promises";
149112
149199
  function getUnsubmittedReview(toolState) {
@@ -149410,10 +149497,10 @@ async function installClaudeCli() {
149410
149497
  });
149411
149498
  }
149412
149499
  function writeMcpConfig(ctx) {
149413
- const configDir = join11(ctx.tmpdir, ".claude");
149414
- mkdirSync4(configDir, { recursive: true });
149415
- const configPath = join11(configDir, "mcp.json");
149416
- writeFileSync8(
149500
+ const configDir = join12(ctx.tmpdir, ".claude");
149501
+ mkdirSync5(configDir, { recursive: true });
149502
+ const configPath = join12(configDir, "mcp.json");
149503
+ writeFileSync9(
149417
149504
  configPath,
149418
149505
  JSON.stringify({
149419
149506
  mcpServers: {
@@ -149795,34 +149882,48 @@ ${stderrContext}`
149795
149882
  var MANAGED_SETTINGS_DIR = "/etc/claude-code";
149796
149883
  var MANAGED_SETTINGS_PATH = `${MANAGED_SETTINGS_DIR}/managed-settings.json`;
149797
149884
  var CODEX_AUTH_DENY_PATH = "~/.local/share/opencode/auth.json";
149798
- var managedSettings = {
149799
- allowManagedPermissionRulesOnly: true,
149800
- allowManagedHooksOnly: true,
149801
- permissions: {
149802
- deny: [
149803
- "Read(//proc/**)",
149804
- "Read(//sys/**)",
149805
- "Grep(//proc/**)",
149806
- "Grep(//sys/**)",
149807
- "Edit(//proc/**)",
149808
- "Edit(//sys/**)",
149809
- "Glob(//proc/**)",
149810
- "Glob(//sys/**)",
149811
- `Read(${CODEX_AUTH_DENY_PATH})`,
149812
- `Grep(${CODEX_AUTH_DENY_PATH})`,
149813
- `Edit(${CODEX_AUTH_DENY_PATH})`,
149814
- `Glob(${CODEX_AUTH_DENY_PATH})`
149815
- ]
149816
- },
149817
- sandbox: {
149818
- filesystem: {
149819
- denyRead: ["/proc", "/sys", CODEX_AUTH_DENY_PATH]
149885
+ function buildManagedSettings(ctx) {
149886
+ const secretDenyPaths = ctx.secretDenyPaths ?? [];
149887
+ const toolDeny = secretDenyPaths.flatMap((path3) => [
149888
+ `Read(${path3}/**)`,
149889
+ `Read(/${path3}/**)`,
149890
+ `Grep(${path3}/**)`,
149891
+ `Grep(/${path3}/**)`,
149892
+ `Edit(${path3}/**)`,
149893
+ `Edit(/${path3}/**)`,
149894
+ `Glob(${path3}/**)`,
149895
+ `Glob(/${path3}/**)`
149896
+ ]);
149897
+ return {
149898
+ allowManagedPermissionRulesOnly: true,
149899
+ allowManagedHooksOnly: true,
149900
+ permissions: {
149901
+ deny: [
149902
+ "Read(//proc/**)",
149903
+ "Read(//sys/**)",
149904
+ "Grep(//proc/**)",
149905
+ "Grep(//sys/**)",
149906
+ "Edit(//proc/**)",
149907
+ "Edit(//sys/**)",
149908
+ "Glob(//proc/**)",
149909
+ "Glob(//sys/**)",
149910
+ `Read(${CODEX_AUTH_DENY_PATH})`,
149911
+ `Grep(${CODEX_AUTH_DENY_PATH})`,
149912
+ `Edit(${CODEX_AUTH_DENY_PATH})`,
149913
+ `Glob(${CODEX_AUTH_DENY_PATH})`,
149914
+ ...toolDeny
149915
+ ]
149916
+ },
149917
+ sandbox: {
149918
+ filesystem: {
149919
+ denyRead: ["/proc", "/sys", CODEX_AUTH_DENY_PATH, ...secretDenyPaths]
149920
+ }
149820
149921
  }
149821
- }
149822
- };
149823
- function installManagedSettings() {
149922
+ };
149923
+ }
149924
+ function installManagedSettings(ctx) {
149824
149925
  if (process.env.CI !== "true") return;
149825
- const content = JSON.stringify(managedSettings, null, 2);
149926
+ const content = JSON.stringify(buildManagedSettings(ctx), null, 2);
149826
149927
  try {
149827
149928
  execFileSync4("sudo", ["mkdir", "-p", MANAGED_SETTINGS_DIR]);
149828
149929
  execFileSync4("sudo", ["tee", MANAGED_SETTINGS_PATH], {
@@ -149842,12 +149943,14 @@ var claude = agent({
149842
149943
  const specifier = ctx.payload.proxyModel ?? ctx.resolvedModel;
149843
149944
  const bedrockModelId = process.env[BEDROCK_MODEL_ID_ENV]?.trim();
149844
149945
  const isBedrockRoute = specifier !== void 0 && bedrockModelId !== void 0 && bedrockModelId === specifier && isBedrockAnthropicId(specifier);
149845
- const model = !specifier ? void 0 : isBedrockRoute ? specifier : stripProviderPrefix(specifier);
149946
+ const vertexModelId = process.env[VERTEX_MODEL_ID_ENV]?.trim();
149947
+ const isVertexRoute2 = specifier !== void 0 && vertexModelId !== void 0 && vertexModelId === specifier && isVertexAnthropicId(specifier);
149948
+ const model = !specifier ? void 0 : isBedrockRoute ? specifier : isVertexRoute2 ? void 0 : stripProviderPrefix(specifier);
149846
149949
  const homeEnv = {
149847
149950
  HOME: ctx.tmpdir,
149848
- XDG_CONFIG_HOME: join11(ctx.tmpdir, ".config")
149951
+ XDG_CONFIG_HOME: join12(ctx.tmpdir, ".config")
149849
149952
  };
149850
- mkdirSync4(join11(homeEnv.XDG_CONFIG_HOME, "claude"), { recursive: true });
149953
+ mkdirSync5(join12(homeEnv.XDG_CONFIG_HOME, "claude"), { recursive: true });
149851
149954
  const agentBrowserVersion = getDevDependencyVersion("agent-browser");
149852
149955
  addSkill({
149853
149956
  ref: `vercel-labs/agent-browser@v${agentBrowserVersion}`,
@@ -149858,7 +149961,7 @@ var claude = agent({
149858
149961
  installBundledSkills({ home: homeEnv.HOME });
149859
149962
  const mcpConfigPath = writeMcpConfig(ctx);
149860
149963
  const effort = resolveEffort(model);
149861
- installManagedSettings();
149964
+ installManagedSettings(ctx);
149862
149965
  const baseArgs = [
149863
149966
  cliPath,
149864
149967
  "--output-format",
@@ -149886,6 +149989,10 @@ var claude = agent({
149886
149989
  if (isBedrockRoute) {
149887
149990
  env2.CLAUDE_CODE_USE_BEDROCK = "1";
149888
149991
  }
149992
+ if (isVertexRoute2) {
149993
+ applyClaudeVertexEnv(env2);
149994
+ env2.ANTHROPIC_MODEL = specifier;
149995
+ }
149889
149996
  if (env2.CLAUDE_CODE_OAUTH_TOKEN && !isBedrockRoute && env2.ANTHROPIC_API_KEY) {
149890
149997
  log.debug(
149891
149998
  "\xBB CLAUDE_CODE_OAUTH_TOKEN present \u2014 stripping ANTHROPIC_API_KEY from Claude Code env so the OAuth subscription is used"
@@ -149927,8 +150034,8 @@ var claude = agent({
149927
150034
 
149928
150035
  // agents/opencode_v2.ts
149929
150036
  var core2 = __toESM(require_core(), 1);
149930
- import { mkdirSync as mkdirSync6, writeFileSync as writeFileSync10 } from "node:fs";
149931
- import { join as join13 } from "node:path";
150037
+ import { mkdirSync as mkdirSync7, writeFileSync as writeFileSync11 } from "node:fs";
150038
+ import { join as join14 } from "node:path";
149932
150039
  import { performance as performance7 } from "node:perf_hooks";
149933
150040
 
149934
150041
  // utils/agentHangReport.ts
@@ -150027,9 +150134,9 @@ function formatBillingExhaustedBody(diagnostic) {
150027
150134
  }
150028
150135
 
150029
150136
  // utils/codexHome.ts
150030
- import { mkdirSync as mkdirSync5, writeFileSync as writeFileSync9 } from "node:fs";
150031
- import { homedir } from "node:os";
150032
- import { join as join12 } from "node:path";
150137
+ import { mkdirSync as mkdirSync6, writeFileSync as writeFileSync10 } from "node:fs";
150138
+ import { homedir as homedir2 } from "node:os";
150139
+ import { join as join13 } from "node:path";
150033
150140
  var CODEX_AUTH_ENV = "CODEX_AUTH_JSON";
150034
150141
  function installCodexAuth() {
150035
150142
  const raw2 = process.env[CODEX_AUTH_ENV];
@@ -150039,9 +150146,9 @@ function installCodexAuth() {
150039
150146
  log.warning(`\xBB ${CODEX_AUTH_ENV} present but malformed; ignoring`);
150040
150147
  return null;
150041
150148
  }
150042
- const xdgDataHome = join12(homedir(), ".local", "share");
150043
- const opencodeDir = join12(xdgDataHome, "opencode");
150044
- const authPath = join12(opencodeDir, "auth.json");
150149
+ const xdgDataHome = join13(homedir2(), ".local", "share");
150150
+ const opencodeDir = join13(xdgDataHome, "opencode");
150151
+ const authPath = join13(opencodeDir, "auth.json");
150045
150152
  const opencodeAuth = {
150046
150153
  openai: {
150047
150154
  type: "oauth",
@@ -150054,8 +150161,8 @@ function installCodexAuth() {
150054
150161
  ...blob.tokens.account_id ? { accountId: blob.tokens.account_id } : {}
150055
150162
  }
150056
150163
  };
150057
- mkdirSync5(opencodeDir, { recursive: true });
150058
- writeFileSync9(authPath, `${JSON.stringify(opencodeAuth, null, 2)}
150164
+ mkdirSync6(opencodeDir, { recursive: true });
150165
+ writeFileSync10(authPath, `${JSON.stringify(opencodeAuth, null, 2)}
150059
150166
  `, { mode: 384 });
150060
150167
  log.info(`\xBB installed Codex auth at ${authPath}`);
150061
150168
  return { authPath, xdgDataHome, originalRefresh: blob.tokens.refresh_token };
@@ -150711,16 +150818,17 @@ var opencode = agent({
150711
150818
  const rawModel = ctx.payload.proxyModel ?? ctx.resolvedModel ?? autoSelectModel(cliPath);
150712
150819
  const bedrockModelId = process.env[BEDROCK_MODEL_ID_ENV]?.trim();
150713
150820
  const isBedrockRoute = rawModel !== void 0 && bedrockModelId !== void 0 && bedrockModelId === rawModel;
150714
- const model = isBedrockRoute ? `amazon-bedrock/${rawModel}` : rawModel;
150821
+ const vertexModel = resolveVertexOpenCodeModel(rawModel);
150822
+ const model = vertexModel ?? (isBedrockRoute ? `amazon-bedrock/${rawModel}` : rawModel);
150715
150823
  const homeEnv = {
150716
150824
  HOME: ctx.tmpdir,
150717
- XDG_CONFIG_HOME: join13(ctx.tmpdir, ".config")
150825
+ XDG_CONFIG_HOME: join14(ctx.tmpdir, ".config")
150718
150826
  };
150719
- mkdirSync6(join13(homeEnv.XDG_CONFIG_HOME, "opencode"), { recursive: true });
150720
- const opencodePluginDir = join13(homeEnv.XDG_CONFIG_HOME, "opencode", "plugin");
150721
- mkdirSync6(opencodePluginDir, { recursive: true });
150722
- writeFileSync10(
150723
- join13(opencodePluginDir, PULLFROG_OPENCODE_PLUGIN_FILENAME),
150827
+ mkdirSync7(join14(homeEnv.XDG_CONFIG_HOME, "opencode"), { recursive: true });
150828
+ const opencodePluginDir = join14(homeEnv.XDG_CONFIG_HOME, "opencode", "plugin");
150829
+ mkdirSync7(opencodePluginDir, { recursive: true });
150830
+ writeFileSync11(
150831
+ join14(opencodePluginDir, PULLFROG_OPENCODE_PLUGIN_FILENAME),
150724
150832
  PULLFROG_OPENCODE_PLUGIN_SOURCE
150725
150833
  );
150726
150834
  const agentBrowserVersion = getDevDependencyVersion("agent-browser");
@@ -150790,15 +150898,18 @@ var opencode = agent({
150790
150898
  var agents = { claude, opencode };
150791
150899
 
150792
150900
  // utils/agent.ts
150793
- function hasEnvVar(name) {
150901
+ function hasEnvVar2(name) {
150794
150902
  const val = process.env[name];
150795
150903
  return typeof val === "string" && val.length > 0;
150796
150904
  }
150797
150905
  function hasClaudeCodeAuth() {
150798
- return hasEnvVar("CLAUDE_CODE_OAUTH_TOKEN") || hasEnvVar("ANTHROPIC_API_KEY");
150906
+ return hasEnvVar2("CLAUDE_CODE_OAUTH_TOKEN") || hasEnvVar2("ANTHROPIC_API_KEY");
150799
150907
  }
150800
150908
  function hasBedrockAuth() {
150801
- return hasEnvVar("AWS_BEARER_TOKEN_BEDROCK") || hasEnvVar("AWS_ACCESS_KEY_ID") && hasEnvVar("AWS_SECRET_ACCESS_KEY");
150909
+ return hasEnvVar2("AWS_BEARER_TOKEN_BEDROCK") || hasEnvVar2("AWS_ACCESS_KEY_ID") && hasEnvVar2("AWS_SECRET_ACCESS_KEY");
150910
+ }
150911
+ function hasVertexAuth() {
150912
+ return hasEnvVar2(VERTEX_SERVICE_ACCOUNT_JSON_ENV);
150802
150913
  }
150803
150914
  function resolveSlug(slug2) {
150804
150915
  const alias = resolveDisplayAlias(slug2);
@@ -150806,11 +150917,20 @@ function resolveSlug(slug2) {
150806
150917
  const bedrockId = process.env[BEDROCK_MODEL_ID_ENV]?.trim();
150807
150918
  if (!bedrockId) {
150808
150919
  throw new Error(
150809
- `${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.`
150920
+ `${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.`
150810
150921
  );
150811
150922
  }
150812
150923
  return bedrockId;
150813
150924
  }
150925
+ if (alias?.routing === "vertex") {
150926
+ const vertexId = process.env[VERTEX_MODEL_ID_ENV]?.trim();
150927
+ if (!vertexId) {
150928
+ throw new Error(
150929
+ `${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.`
150930
+ );
150931
+ }
150932
+ return vertexId;
150933
+ }
150814
150934
  return resolveCliModel(slug2);
150815
150935
  }
150816
150936
  function resolveModel(ctx) {
@@ -150838,6 +150958,9 @@ function resolveAgent(ctx) {
150838
150958
  if (ctx.model && hasBedrockAuth() && process.env[BEDROCK_MODEL_ID_ENV]?.trim() === ctx.model) {
150839
150959
  return isBedrockAnthropicId(ctx.model) ? agents.claude : agents.opencode;
150840
150960
  }
150961
+ if (ctx.model && hasVertexAuth() && process.env[VERTEX_MODEL_ID_ENV]?.trim() === ctx.model) {
150962
+ return isVertexAnthropicId(ctx.model) ? agents.claude : agents.opencode;
150963
+ }
150841
150964
  if (ctx.model) {
150842
150965
  try {
150843
150966
  const provider2 = getModelProvider(ctx.model);
@@ -150878,26 +151001,51 @@ add the missing secret(s) to your GitHub repository at ${githubSecretsUrl}, then
150878
151001
 
150879
151002
  for full setup instructions, see https://docs.pullfrog.com/bedrock`;
150880
151003
  }
150881
- function hasEnvVar2(name) {
151004
+ function buildVertexSetupError(params) {
151005
+ const githubSecretsUrl = `https://github.com/${params.owner}/${params.name}/settings/secrets/actions`;
151006
+ return `Google Vertex AI model selected but required configuration is missing: ${params.missing.join(", ")}.
151007
+
151008
+ add the missing secret(s) to your GitHub repository at ${githubSecretsUrl}, then reference them in your workflow's \`env:\` block:
151009
+
151010
+ ${VERTEX_SERVICE_ACCOUNT_JSON_ENV}: \${{ secrets.${VERTEX_SERVICE_ACCOUNT_JSON_ENV} }}
151011
+ ${GOOGLE_CLOUD_PROJECT_ENV}: my-project
151012
+ ${VERTEX_LOCATION_ENV}: global
151013
+ ${VERTEX_MODEL_ID_ENV}: <vertex-model-id>
151014
+
151015
+ for full setup instructions, see https://docs.pullfrog.com/vertex`;
151016
+ }
151017
+ function hasEnvVar3(name) {
150882
151018
  const value2 = process.env[name];
150883
151019
  return typeof value2 === "string" && value2.length > 0;
150884
151020
  }
150885
151021
  function hasProviderKey(model) {
150886
151022
  const requiredVars = getModelEnvVars(model);
150887
151023
  if (requiredVars.length === 0) return true;
150888
- return requiredVars.some((v) => hasEnvVar2(v));
151024
+ return requiredVars.some((v) => hasEnvVar3(v));
150889
151025
  }
150890
151026
  function validateBedrockSetup(params) {
150891
- const hasAuth = hasEnvVar2("AWS_BEARER_TOKEN_BEDROCK") || hasEnvVar2("AWS_ACCESS_KEY_ID") && hasEnvVar2("AWS_SECRET_ACCESS_KEY");
151027
+ const hasAuth = hasEnvVar3("AWS_BEARER_TOKEN_BEDROCK") || hasEnvVar3("AWS_ACCESS_KEY_ID") && hasEnvVar3("AWS_SECRET_ACCESS_KEY");
150892
151028
  const missing = [];
150893
151029
  if (!hasAuth)
150894
151030
  missing.push("AWS_BEARER_TOKEN_BEDROCK (or AWS_ACCESS_KEY_ID + AWS_SECRET_ACCESS_KEY)");
150895
- if (!hasEnvVar2("AWS_REGION")) missing.push("AWS_REGION");
150896
- if (!hasEnvVar2(BEDROCK_MODEL_ID_ENV)) missing.push(BEDROCK_MODEL_ID_ENV);
151031
+ if (!hasEnvVar3("AWS_REGION")) missing.push("AWS_REGION");
151032
+ if (!hasEnvVar3(BEDROCK_MODEL_ID_ENV)) missing.push(BEDROCK_MODEL_ID_ENV);
150897
151033
  if (missing.length > 0) {
150898
151034
  throw new Error(buildBedrockSetupError({ owner: params.owner, name: params.name, missing }));
150899
151035
  }
150900
151036
  }
151037
+ function validateVertexSetup(params) {
151038
+ const hasAuth = hasEnvVar3(VERTEX_SERVICE_ACCOUNT_JSON_ENV);
151039
+ const hasProject = hasEnvVar3(GOOGLE_CLOUD_PROJECT_ENV) || readProjectIdFromVertexServiceAccountJson() !== void 0;
151040
+ const missing = [];
151041
+ if (!hasAuth) missing.push(VERTEX_SERVICE_ACCOUNT_JSON_ENV);
151042
+ if (!hasProject) missing.push(GOOGLE_CLOUD_PROJECT_ENV);
151043
+ if (!hasEnvVar3(VERTEX_LOCATION_ENV)) missing.push(VERTEX_LOCATION_ENV);
151044
+ if (!hasEnvVar3(VERTEX_MODEL_ID_ENV)) missing.push(VERTEX_MODEL_ID_ENV);
151045
+ if (missing.length > 0) {
151046
+ throw new Error(buildVertexSetupError({ owner: params.owner, name: params.name, missing }));
151047
+ }
151048
+ }
150901
151049
  function validateAgentApiKey(params) {
150902
151050
  if (params.model) {
150903
151051
  const alias = resolveDisplayAlias(params.model);
@@ -150905,16 +151053,24 @@ function validateAgentApiKey(params) {
150905
151053
  validateBedrockSetup({ owner: params.owner, name: params.name });
150906
151054
  return;
150907
151055
  }
151056
+ if (alias?.routing === "vertex") {
151057
+ validateVertexSetup({ owner: params.owner, name: params.name });
151058
+ return;
151059
+ }
150908
151060
  if (!params.model.includes("/")) {
151061
+ if (process.env[VERTEX_MODEL_ID_ENV]?.trim() === params.model) {
151062
+ validateVertexSetup({ owner: params.owner, name: params.name });
151063
+ return;
151064
+ }
150909
151065
  validateBedrockSetup({ owner: params.owner, name: params.name });
150910
151066
  return;
150911
151067
  }
150912
151068
  const requiredVars = getModelEnvVars(params.model);
150913
151069
  if (requiredVars.length === 0) return;
150914
- if (requiredVars.some((v) => hasEnvVar2(v))) return;
151070
+ if (requiredVars.some((v) => hasEnvVar3(v))) return;
150915
151071
  throw new Error(buildMissingApiKeyError({ owner: params.owner, name: params.name }));
150916
151072
  }
150917
- const hasAnyKey = [...knownApiKeys].some((k) => hasEnvVar2(k));
151073
+ const hasAnyKey = [...knownApiKeys].some((k) => hasEnvVar3(k));
150918
151074
  if (!hasAnyKey) {
150919
151075
  throw new Error(buildMissingApiKeyError({ owner: params.owner, name: params.name }));
150920
151076
  }
@@ -151049,10 +151205,10 @@ function selectFallbackModelIfNeeded(input) {
151049
151205
  }
151050
151206
 
151051
151207
  // utils/gitAuthServer.ts
151052
- import { randomUUID as randomUUID3 } from "node:crypto";
151053
- import { writeFileSync as writeFileSync11 } from "node:fs";
151208
+ import { randomUUID as randomUUID4 } from "node:crypto";
151209
+ import { writeFileSync as writeFileSync12 } from "node:fs";
151054
151210
  import { createServer as createServer2 } from "node:http";
151055
- import { join as join14 } from "node:path";
151211
+ import { join as join15 } from "node:path";
151056
151212
  var CODE_TTL_MS = 5 * 60 * 1e3;
151057
151213
  var TAMPER_WINDOW_MS = 6e4;
151058
151214
  function revokeGitHubToken(token) {
@@ -151112,7 +151268,7 @@ async function startGitAuthServer(tmpdir4) {
151112
151268
  const port = rawAddr.port;
151113
151269
  log.debug(`git auth server listening on 127.0.0.1:${port}`);
151114
151270
  function register4(token) {
151115
- const code = randomUUID3();
151271
+ const code = randomUUID4();
151116
151272
  const timeout = setTimeout(() => {
151117
151273
  codes.delete(code);
151118
151274
  log.debug(`git auth code expired: ${code.slice(0, 8)}...`);
@@ -151122,9 +151278,9 @@ async function startGitAuthServer(tmpdir4) {
151122
151278
  return code;
151123
151279
  }
151124
151280
  function writeAskpassScript(code) {
151125
- const scriptId = randomUUID3();
151281
+ const scriptId = randomUUID4();
151126
151282
  const scriptName = `askpass-${scriptId}.js`;
151127
- const scriptPath = join14(tmpdir4, scriptName);
151283
+ const scriptPath = join15(tmpdir4, scriptName);
151128
151284
  const content = [
151129
151285
  `#!/usr/bin/env node`,
151130
151286
  `var a=process.argv[2]||"";`,
@@ -151139,7 +151295,7 @@ async function startGitAuthServer(tmpdir4) {
151139
151295
  `try{require("fs").unlinkSync("${scriptPath.replace(/\\/g, "\\\\")}")}catch(e){}`,
151140
151296
  `})}).on("error",function(){process.exit(1)})}`
151141
151297
  ].join("\n");
151142
- writeFileSync11(scriptPath, content, { mode: 448 });
151298
+ writeFileSync12(scriptPath, content, { mode: 448 });
151143
151299
  return scriptPath;
151144
151300
  }
151145
151301
  async function close() {
@@ -151163,7 +151319,7 @@ async function startGitAuthServer(tmpdir4) {
151163
151319
  var core3 = __toESM(require_core(), 1);
151164
151320
  import { createSign } from "node:crypto";
151165
151321
  import { rename, writeFile } from "node:fs/promises";
151166
- import { dirname as dirname3, join as join15 } from "node:path";
151322
+ import { dirname as dirname3, join as join16 } from "node:path";
151167
151323
 
151168
151324
  // node_modules/.pnpm/@octokit+plugin-throttling@11.0.3_@octokit+core@7.0.5/node_modules/@octokit/plugin-throttling/dist-bundle/index.js
151169
151325
  var import_light = __toESM(require_light(), 1);
@@ -155021,7 +155177,7 @@ function getGitHubUsageSummary() {
155021
155177
  }
155022
155178
  async function writeGitHubUsageSummaryToFile(path3) {
155023
155179
  const summary2 = getGitHubUsageSummary();
155024
- const tmpPath = join15(dirname3(path3), `.usage-summary-${process.pid}.tmp`);
155180
+ const tmpPath = join16(dirname3(path3), `.usage-summary-${process.pid}.tmp`);
155025
155181
  await writeFile(tmpPath, JSON.stringify(summary2));
155026
155182
  await rename(tmpPath, path3);
155027
155183
  }
@@ -155421,7 +155577,7 @@ function resolveInstructions(ctx) {
155421
155577
 
155422
155578
  // utils/learnings.ts
155423
155579
  import { mkdir, readFile as readFile2, writeFile as writeFile2 } from "node:fs/promises";
155424
- import { dirname as dirname4, join as join16 } from "node:path";
155580
+ import { dirname as dirname4, join as join17 } from "node:path";
155425
155581
 
155426
155582
  // utils/learningsTruncate.ts
155427
155583
  var MAX_LEARNINGS_LENGTH = 1e5;
@@ -155438,7 +155594,7 @@ function truncateAtLineBoundary(body, cap) {
155438
155594
  // utils/learnings.ts
155439
155595
  var LEARNINGS_FILE_NAME = "pullfrog-learnings.md";
155440
155596
  function learningsFilePath(tmpdir4) {
155441
- return join16(tmpdir4, LEARNINGS_FILE_NAME);
155597
+ return join17(tmpdir4, LEARNINGS_FILE_NAME);
155442
155598
  }
155443
155599
  async function seedLearningsFile(params) {
155444
155600
  const path3 = learningsFilePath(params.tmpdir);
@@ -156118,7 +156274,7 @@ async function runProxyResolution(ctx) {
156118
156274
 
156119
156275
  // utils/prSummary.ts
156120
156276
  import { mkdir as mkdir2, readFile as readFile3, writeFile as writeFile3 } from "node:fs/promises";
156121
- import { dirname as dirname5, join as join17 } from "node:path";
156277
+ import { dirname as dirname5, join as join18 } from "node:path";
156122
156278
  var SUMMARY_FILE_NAME = "pullfrog-summary.md";
156123
156279
  var SUMMARY_SCAFFOLD = `# PR summary
156124
156280
 
@@ -156128,7 +156284,7 @@ var SUMMARY_SCAFFOLD = `# PR summary
156128
156284
  var MIN_SNAPSHOT_LENGTH = 60;
156129
156285
  var MAX_SNAPSHOT_LENGTH = 32768;
156130
156286
  function summaryFilePath(tmpdir4) {
156131
- return join17(tmpdir4, SUMMARY_FILE_NAME);
156287
+ return join18(tmpdir4, SUMMARY_FILE_NAME);
156132
156288
  }
156133
156289
  async function seedSummaryFile(params) {
156134
156290
  const path3 = summaryFilePath(params.tmpdir);
@@ -156552,9 +156708,9 @@ function logRunStartup(ctx) {
156552
156708
  import { execFileSync as execFileSync6, execSync as execSync3 } from "node:child_process";
156553
156709
  import { mkdtempSync as mkdtempSync2 } from "node:fs";
156554
156710
  import { tmpdir as tmpdir3 } from "node:os";
156555
- import { join as join18 } from "node:path";
156711
+ import { join as join19 } from "node:path";
156556
156712
  function createTempDirectory() {
156557
- const sharedTempDir = mkdtempSync2(join18(tmpdir3(), "pullfrog-"));
156713
+ const sharedTempDir = mkdtempSync2(join19(tmpdir3(), "pullfrog-"));
156558
156714
  process.env.PULLFROG_TEMP_DIR = sharedTempDir;
156559
156715
  log.info(`\xBB created temp dir at ${sharedTempDir}`);
156560
156716
  return sharedTempDir;
@@ -156886,6 +157042,7 @@ async function main() {
156886
157042
  let toolContext;
156887
157043
  let progressCallbackDisabled = false;
156888
157044
  let todoTracker;
157045
+ let vertexCredentials;
156889
157046
  try {
156890
157047
  var _stack = [];
156891
157048
  try {
@@ -156920,6 +157077,7 @@ async function main() {
156920
157077
  );
156921
157078
  toolState.modelFallback = { from: fallback.from };
156922
157079
  }
157080
+ vertexCredentials = materializeVertexCredentials({ model: resolvedModel });
156923
157081
  const agent2 = resolveAgent({ model: resolvedModel });
156924
157082
  toolState.model = payload.proxyModel ?? resolvedModel ?? effectiveSlug;
156925
157083
  validateAgentApiKey({
@@ -157031,7 +157189,7 @@ ${instructions.user}` : null,
157031
157189
  log.info(instructions.full);
157032
157190
  });
157033
157191
  if (agentId === "opencode") {
157034
- const pluginDir = join19(process.cwd(), ".opencode", "plugin");
157192
+ const pluginDir = join20(process.cwd(), ".opencode", "plugin");
157035
157193
  const hasPlugins = existsSync7(pluginDir) && readdirSync(pluginDir).some((f) => /\.[jt]sx?$/.test(f));
157036
157194
  if (hasPlugins && toolState.dependencyInstallation?.promise) {
157037
157195
  log.info(
@@ -157087,6 +157245,7 @@ ${instructions.user}` : null,
157087
157245
  resolvedModel,
157088
157246
  mcpServerUrl: mcpHttpServer.url,
157089
157247
  tmpdir: tmpdir4,
157248
+ secretDenyPaths: vertexCredentials ? [vertexCredentials.secretDir] : [],
157090
157249
  instructions,
157091
157250
  todoTracker,
157092
157251
  stopScript: runContext.repoSettings.stopScript,
@@ -157190,6 +157349,7 @@ ${instructions.user}` : null,
157190
157349
  await patchWorkflowRunFields(toolContext, patch);
157191
157350
  }
157192
157351
  }
157352
+ cleanupVertexCredentials(vertexCredentials);
157193
157353
  }
157194
157354
  } catch (_3) {
157195
157355
  var _error2 = _3, _hasError2 = true;
@@ -158078,7 +158238,7 @@ async function run2() {
158078
158238
  }
158079
158239
 
158080
158240
  // cli.ts
158081
- var VERSION10 = "0.1.9";
158241
+ var VERSION10 = "0.1.11";
158082
158242
  var bin = basename2(process.argv[1] || "");
158083
158243
  var PROG = bin === "pf" || bin === "pullfrog" ? bin : "pullfrog";
158084
158244
  var rawArgs = process.argv.slice(2);