d3b5340583
* fix: 9 unaddressed log-audit / run-audit findings Co-authored-by: Cursor <cursoragent@cursor.com> #815 entryPost stdlib-only imports; #823 MCP timeoutMs on checkout_pr/shell; #816 FREE_FALLBACK → opencode/big-pickle; #822 chunk GraphQL nodes ≤100; #817/#821 vip_audit 404 skip paths; #813 longer serializable retries; #818 run-context handler-entered log; #805 audit severity template. * fix: update footer test for big-pickle fallback slug Co-authored-by: Cursor <cursoragent@cursor.com> * fix: anneal round 1 — ghaCore getState casing, post-hook timeout Match @actions/core STATE_ key semantics (no uppercasing), cap postApiFetch at 30s, trim serializable retries to stay under GitHub's 10s webhook window, log Clerk failures in getUserTokenByGithubLogin. Co-authored-by: Cursor <cursoragent@cursor.com> * revert: drop run-context handler log (#818 deferred) The #692 client-side fix is already on main; residual SyntaxError hits are old action pins. Per-request log added noise without fixing anything. Co-authored-by: Cursor <cursoragent@cursor.com> * document per-issue Closes syntax for audit PRs GitHub only auto-closes the first issue when numbers are comma-separated; /audits and AGENTS.md now require Closes before each issue number. Co-authored-by: Cursor <cursoragent@cursor.com> * fix: anneal round 2 — outreach privacy, alert resilience, vertex cleanup Filter private repos from VIP authority output, harden console alert lines against DB failures, drop spoofable changesets body check, and unset GOOGLE_APPLICATION_CREDENTIALS after vertex credential cleanup. Co-authored-by: Cursor <cursoragent@cursor.com> * refactor: drop codexHome re-export of detectCodexRefresh Import detectCodexRefresh directly from codexRefreshDetect.ts everywhere; rename the unit test file to match. codexHome.ts stays install-only. Co-authored-by: Cursor <cursoragent@cursor.com> * fix: drop deprecated minimax-m2.5-free; add paid minimax-m2.5 Remove the deprecated free MiniMax promo from the catalog, docs, and tests. BYOK fallback and picker copy stay on opencode/big-pickle. Add opencode/minimax-m2.5 and openrouter/minimax-m2.5 for Zen BYOK and Router. Pin #816 regressions with freeFallbackCatalog and runErrorRenderer unit tests. Co-authored-by: Cursor <cursoragent@cursor.com> * fix: hidden minimax-m2.5-free fallback for stored slugs Re-add opencode/minimax-m2.5-free as a hidden fallback alias to big-pickle so repos with the legacy slug still resolve as free. Drop live Zen API experiment tests in freeFallbackCatalog.test.ts. Co-authored-by: Cursor <cursoragent@cursor.com> --------- Co-authored-by: Cursor <cursoragent@cursor.com>
86 lines
3.0 KiB
TypeScript
86 lines
3.0 KiB
TypeScript
import { randomUUID } from "node:crypto";
|
|
import { mkdirSync, rmSync, writeFileSync } from "node:fs";
|
|
import { homedir } from "node:os";
|
|
import { join } from "node:path";
|
|
import { VERTEX_MODEL_ID_ENV } from "../models.ts";
|
|
|
|
export const VERTEX_SERVICE_ACCOUNT_JSON_ENV = "VERTEX_SERVICE_ACCOUNT_JSON";
|
|
export const GOOGLE_APPLICATION_CREDENTIALS_ENV = "GOOGLE_APPLICATION_CREDENTIALS";
|
|
export const GOOGLE_CLOUD_PROJECT_ENV = "GOOGLE_CLOUD_PROJECT";
|
|
export const VERTEX_LOCATION_ENV = "VERTEX_LOCATION";
|
|
|
|
export type VertexCredentials = {
|
|
credentialsPath: string;
|
|
secretDir: string;
|
|
};
|
|
|
|
function hasEnvVar(name: string): boolean {
|
|
const value = process.env[name];
|
|
return typeof value === "string" && value.length > 0;
|
|
}
|
|
|
|
export function isVertexRoute(model: string | undefined): boolean {
|
|
const vertexId = process.env[VERTEX_MODEL_ID_ENV]?.trim();
|
|
return model !== undefined && vertexId !== undefined && vertexId === model;
|
|
}
|
|
|
|
export function readProjectIdFromVertexServiceAccountJson(): string | undefined {
|
|
const blob = process.env[VERTEX_SERVICE_ACCOUNT_JSON_ENV];
|
|
if (!blob) return undefined;
|
|
|
|
try {
|
|
const parsed: unknown = JSON.parse(blob);
|
|
if (!parsed || typeof parsed !== "object" || !("project_id" in parsed)) {
|
|
return undefined;
|
|
}
|
|
const projectId = parsed.project_id;
|
|
return typeof projectId === "string" && projectId.length > 0 ? projectId : undefined;
|
|
} catch {
|
|
return undefined;
|
|
}
|
|
}
|
|
|
|
function createSecretDir(): string {
|
|
const base = process.env.PULLFROG_SECRET_HOME || process.env.HOME || homedir();
|
|
const secretDir = join(base, ".pullfrog", "secrets", randomUUID());
|
|
mkdirSync(secretDir, { recursive: true, mode: 0o700 });
|
|
return secretDir;
|
|
}
|
|
|
|
export function materializeVertexCredentials(params: {
|
|
model: string | undefined;
|
|
}): VertexCredentials | undefined {
|
|
if (!isVertexRoute(params.model)) return undefined;
|
|
|
|
const blob = process.env[VERTEX_SERVICE_ACCOUNT_JSON_ENV];
|
|
if (!blob) return undefined;
|
|
|
|
const secretDir = createSecretDir();
|
|
const credentialsPath = join(secretDir, "vertex-sa.json");
|
|
writeFileSync(credentialsPath, blob, { mode: 0o600 });
|
|
process.env[GOOGLE_APPLICATION_CREDENTIALS_ENV] = credentialsPath;
|
|
|
|
const projectId = readProjectIdFromVertexServiceAccountJson();
|
|
if (projectId && !hasEnvVar(GOOGLE_CLOUD_PROJECT_ENV)) {
|
|
process.env[GOOGLE_CLOUD_PROJECT_ENV] = projectId;
|
|
}
|
|
|
|
return { credentialsPath, secretDir };
|
|
}
|
|
|
|
export function cleanupVertexCredentials(credentials: VertexCredentials | undefined): void {
|
|
if (!credentials) return;
|
|
rmSync(credentials.secretDir, { recursive: true, force: true });
|
|
delete process.env[GOOGLE_APPLICATION_CREDENTIALS_ENV];
|
|
}
|
|
|
|
export function applyClaudeVertexEnv(env: Record<string, string | undefined>): void {
|
|
env.CLAUDE_CODE_USE_VERTEX = "1";
|
|
env.ANTHROPIC_VERTEX_PROJECT_ID ??= env[GOOGLE_CLOUD_PROJECT_ENV];
|
|
env.CLOUD_ML_REGION ??= env[VERTEX_LOCATION_ENV];
|
|
}
|
|
|
|
export function resolveVertexOpenCodeModel(model: string | undefined): string | undefined {
|
|
return isVertexRoute(model) && model ? `google-vertex/${model}` : undefined;
|
|
}
|