mirror of
https://github.com/garrytan/gstack.git
synced 2026-05-25 12:44:04 +02:00
feat: design binary core — generate, check, compare commands
Stateless CLI (design/dist/design) wrapping OpenAI Responses API for UI mockup generation. Three working commands: - generate: brief -> PNG mockup via gpt-4o + image_generation tool - check: vision-based quality gate via GPT-4o (text readability, layout completeness, visual coherence) - compare: generates self-contained HTML comparison board with star ratings, radio Pick, per-variant feedback, regenerate controls, and Submit button that writes structured JSON for agent polling Auth reads from ~/.gstack/openai.json (0600), falls back to OPENAI_API_KEY env var. Compiled separately from browse binary (openai added to devDependencies, not runtime deps). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,63 @@
|
||||
/**
|
||||
* Auth resolution for OpenAI API access.
|
||||
*
|
||||
* Resolution order:
|
||||
* 1. ~/.gstack/openai.json → { "api_key": "sk-..." }
|
||||
* 2. OPENAI_API_KEY environment variable
|
||||
* 3. null (caller handles guided setup or fallback)
|
||||
*/
|
||||
|
||||
import fs from "fs";
|
||||
import path from "path";
|
||||
|
||||
const CONFIG_PATH = path.join(process.env.HOME || "~", ".gstack", "openai.json");
|
||||
|
||||
export function resolveApiKey(): string | null {
|
||||
// 1. Check ~/.gstack/openai.json
|
||||
try {
|
||||
if (fs.existsSync(CONFIG_PATH)) {
|
||||
const content = fs.readFileSync(CONFIG_PATH, "utf-8");
|
||||
const config = JSON.parse(content);
|
||||
if (config.api_key && typeof config.api_key === "string") {
|
||||
return config.api_key;
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
// Fall through to env var
|
||||
}
|
||||
|
||||
// 2. Check environment variable
|
||||
if (process.env.OPENAI_API_KEY) {
|
||||
return process.env.OPENAI_API_KEY;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Save an API key to ~/.gstack/openai.json with 0600 permissions.
|
||||
*/
|
||||
export function saveApiKey(key: string): void {
|
||||
const dir = path.dirname(CONFIG_PATH);
|
||||
fs.mkdirSync(dir, { recursive: true });
|
||||
fs.writeFileSync(CONFIG_PATH, JSON.stringify({ api_key: key }, null, 2));
|
||||
fs.chmodSync(CONFIG_PATH, 0o600);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get API key or exit with setup instructions.
|
||||
*/
|
||||
export function requireApiKey(): string {
|
||||
const key = resolveApiKey();
|
||||
if (!key) {
|
||||
console.error("No OpenAI API key found.");
|
||||
console.error("");
|
||||
console.error("Run: $D setup");
|
||||
console.error(" or save to ~/.gstack/openai.json: { \"api_key\": \"sk-...\" }");
|
||||
console.error(" or set OPENAI_API_KEY environment variable");
|
||||
console.error("");
|
||||
console.error("Get a key at: https://platform.openai.com/api-keys");
|
||||
process.exit(1);
|
||||
}
|
||||
return key;
|
||||
}
|
||||
Reference in New Issue
Block a user