From d0d6e4b42ca620ff4f94c9051d3b72c98174c1e8 Mon Sep 17 00:00:00 2001 From: Max Farrell Date: Thu, 11 Dec 2025 17:17:55 -0500 Subject: [PATCH] init simpler --- .env.example | 4 +- AGENTS.md | 33 ++++++++++-- README.md | 31 ++++++++++-- index.ts | 4 ++ package.json | 3 +- scripts/secrets.ts | 124 +++++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 188 insertions(+), 11 deletions(-) create mode 100644 scripts/secrets.ts diff --git a/.env.example b/.env.example index e76726b..7ff9ce0 100644 --- a/.env.example +++ b/.env.example @@ -1 +1,3 @@ -VERCEL_OIDC_TOKEN="create with vercel cli" \ No newline at end of file +# API keys (set via 'bun run scripts/secrets.ts set VERCEL_OIDC_TOKEN') +# Note: API keys are stored in OS credential manager for security +# No .env file needed - use the secrets CLI instead \ No newline at end of file diff --git a/AGENTS.md b/AGENTS.md index b38b6cb..21f6a7a 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -8,7 +8,7 @@ AI SDK benchmarking tool built with Vercel AI SDK and Bun runtime. Tests AI agen # Install dependencies (runs patch-package automatically) bun install -# Run the main benchmark (discovers and runs all tests) +# Run main benchmark (discovers and runs all tests) bun run index.ts # Verify reference implementations against test suites @@ -22,6 +22,11 @@ bun run generate-report.ts results/result-2024-12-07-14-30-45.json # Run TypeScript type checking bun tsc --noEmit + +# Secrets management +bun run secrets # Show token status +bun run secrets set VERCEL_OIDC_TOKEN # Store Vercel token +bun run secrets get VERCEL_OIDC_TOKEN # Get Vercel token ``` ## Environment Variables @@ -106,10 +111,28 @@ MCP_SERVER_URL= ### Required API Keys -- `ANTHROPIC_API_KEY`: Required when using `anthropic/*` models -- `OPENAI_API_KEY`: Required when using `openai/*` models (get at https://platform.openai.com/api-keys) -- `OPENROUTER_API_KEY`: Required when using `openrouter/*` models (get at https://openrouter.ai/keys) -- No API key required for `lmstudio/*` models (runs locally) +- `VERCEL_OIDC_TOKEN`: Required for Vercel AI Gateway (stored in bun.secrets) +- Other API keys (Anthropic, OpenAI, OpenRouter) are configured in Vercel dashboard when using AI Gateway + +### Secrets Management + +The tool uses Bun's secure credential storage for the Vercel OIDC token: + +```bash +# Store Vercel OIDC token +bun run secrets set VERCEL_OIDC_TOKEN your_token_here + +# Check if token is stored +bun run secrets + +# Get the stored token +bun run secrets get VERCEL_OIDC_TOKEN +``` + +**Security Benefits:** +- Encrypted storage using OS credential manager (Keychain, libsecret, Windows Credential Manager) +- No plaintext tokens in files +- User-level access control ### Provider Routing diff --git a/README.md b/README.md index 2dbb337..0432e7a 100644 --- a/README.md +++ b/README.md @@ -12,17 +12,40 @@ bun install ## Setup -Configure your API keys in `.env`: +Configure your Vercel OIDC token using bun.secrets: 1. Install Vercel CLI if you haven't already 2. Run `bun run vercel:link` and link the benchmark to a project that has AI Gateway enabled -3. Run the benchmark with "bun run dev" +3. Store your VERCEL_OIDC_TOKEN securely: + ```bash + # Get your token from Vercel project settings + bun run secrets set VERCEL_OIDC_TOKEN your_token_here + ``` ### Required API Keys -You'll need at least one API key for the providers you want to test: +- `VERCEL_OIDC_TOKEN`: Required for Vercel AI Gateway (stored in bun.secrets) +- Other API keys (Anthropic, OpenAI, OpenRouter) are configured in the Vercel dashboard when using AI Gateway -- `VERCEL_OIDC_TOKEN`: The OIDC token for vercel AI gateway +## Secrets Management + +API keys are stored securely using your OS credential manager: + +```bash +# Check if token is set +bun run secrets + +# Set token +bun run secrets set VERCEL_OIDC_TOKEN your_token_here + +# Get token +bun run secrets get VERCEL_OIDC_TOKEN +``` + +**Security Benefits:** +- Encrypted storage using OS credential manager (Keychain, libsecret, Windows Credential Manager) +- No plaintext API keys in files +- User-level access control ## Usage diff --git a/index.ts b/index.ts index 8e32d8f..43cde4f 100644 --- a/index.ts +++ b/index.ts @@ -43,6 +43,7 @@ import { note, } from "@clack/prompts"; import { gateway } from "ai"; +import { loadTokenToEnv } from "./scripts/secrets.ts"; interface PricingValidationResult { enabled: boolean; @@ -500,6 +501,9 @@ async function runSingleTest( // Main execution async function main() { + // Load VERCEL_OIDC_TOKEN from bun.secrets + await loadTokenToEnv(); + const { models, mcp, testingTool, pricing } = await selectOptions(); // Get MCP server URL/command from environment (optional) diff --git a/package.json b/package.json index 8dc581f..a012e49 100644 --- a/package.json +++ b/package.json @@ -13,7 +13,8 @@ "tsc": "tsc --noEmit", "test:self": "bun vitest run lib/*.test.ts", "vercel:link": "vercel link", - "vercel:env:pull": "vercel env pull .env.local --yes" + "vercel:env:pull": "vercel env pull .env.local --yes", + "secrets": "bun run scripts/secrets.ts" }, "devDependencies": { "@sveltejs/vite-plugin-svelte": "^5.0.3", diff --git a/scripts/secrets.ts b/scripts/secrets.ts new file mode 100644 index 0000000..a0a44b7 --- /dev/null +++ b/scripts/secrets.ts @@ -0,0 +1,124 @@ +#!/usr/bin/env bun +/** + * Simple CLI tool for managing VERCEL_OIDC_TOKEN using bun.secrets + * + * Usage: + * bun run secrets set VERCEL_OIDC_TOKEN # Set the token + * bun run secrets get VERCEL_OIDC_TOKEN # Get the token + * bun run secrets # Show status + */ + +import { secrets } from "bun"; + +async function showStatus() { + console.log("\nšŸ” Vercel OIDC Token Status"); + console.log("─".repeat(40)); + + const token = await secrets.get({ + service: "svelte-ai", + name: "VERCEL_OIDC_TOKEN" + }); + + if (token) { + const masked = token.slice(0, 12) + "..." + token.slice(-8); + console.log(`āœ… VERCEL_OIDC_TOKEN: ${masked}`); + } else { + console.log("āŒ VERCEL_OIDC_TOKEN: Not set"); + console.log("\nšŸ’” Run 'bun run secrets set VERCEL_OIDC_TOKEN ' to set it"); + } +} + +async function setToken(value: string) { + if (!value) { + // Empty value means delete the token + await secrets.delete({ + service: "svelte-ai", + name: "VERCEL_OIDC_TOKEN" + }); + console.log("šŸ—‘ļø VERCEL_OIDC_TOKEN deleted from OS credential manager"); + return; + } + + if (value.length < 20) { + console.error("āŒ Invalid token: VERCEL_OIDC_TOKEN appears to be too short"); + process.exit(1); + } + + await secrets.set({ + service: "svelte-ai", + name: "VERCEL_OIDC_TOKEN", + value, + }); + + console.log("āœ… VERCEL_OIDC_TOKEN stored securely in OS credential manager"); +} + +async function getToken() { + const token = await secrets.get({ + service: "svelte-ai", + name: "VERCEL_OIDC_TOKEN" + }); + + if (token) { + console.log(`VERCEL_OIDC_TOKEN: ${token}`); + } else { + console.log("āŒ VERCEL_OIDC_TOKEN not found"); + process.exit(1); + } +} + +async function loadTokenToEnv() { + const token = await secrets.get({ + service: "svelte-ai", + name: "VERCEL_OIDC_TOKEN" + }); + + if (token) { + process.env.VERCEL_OIDC_TOKEN = token; + console.log("āœ… VERCEL_OIDC_TOKEN loaded from bun.secrets"); + } +} + +// Export for use in main application +export { loadTokenToEnv }; + +// CLI logic +if (import.meta.main) { + const command = process.argv[2]; + const arg1 = process.argv[3]; + const arg2 = process.argv[4]; + + switch (command) { + case "set": + if (arg1 !== "VERCEL_OIDC_TOKEN") { + console.error("Usage: bun run secrets set VERCEL_OIDC_TOKEN "); + process.exit(1); + } + // Empty arg2 means delete, otherwise set the value + await setToken(arg2 || ""); + break; + + case "get": + if (arg1 !== "VERCEL_OIDC_TOKEN") { + console.error("Usage: bun run secrets get VERCEL_OIDC_TOKEN"); + process.exit(1); + } + await getToken(); + break; + + case "load": + // Internal command used by main app + await loadTokenToEnv(); + break; + + case undefined: + case "status": + await showStatus(); + break; + + default: + console.error(`Unknown command: ${command}`); + console.error("Available commands: status, set VERCEL_OIDC_TOKEN , get VERCEL_OIDC_TOKEN"); + process.exit(1); + } +} \ No newline at end of file