import { writeEnvironmentAssets, type EnvironmentAssetEntry } from './environment-assets.js'; import { resolveWorkspaceRootFromEnv } from './workspace-root.js'; import { ensureProjectWorkspace } from './workspace.js'; // Go Bananas image generation for seamless empty environment/location plates — // the location half of the Asset-First Principle, mirroring character-auto-create. const DEFAULT_BASE_URL = 'https://gobananasai.com/api'; const BROWSER_UA = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) ' + 'AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36'; export interface EnvironmentAutoCreateInput { name: string; description: string; style?: string; } export interface EnvironmentAutoCreateResult { imageUrl: string; plateRef: string; created: boolean; persisted: boolean; } function authHeaders(apiKey: string): Record { return { 'X-API-Key': apiKey, 'Content-Type': 'application/json', Accept: 'application/json', 'User-Agent': BROWSER_UA, }; } export function buildPlatePrompt(input: EnvironmentAutoCreateInput): string { const stylePrefix = input.style?.trim() || 'photoreal cinematic establishing plate'; return `${stylePrefix} of ${input.description.trim()}, empty environment, no people, no characters, distraction-free, even neutral lighting, deep focus, high detail, consistent location design. No text, no watermarks.`; } async function generatePlateUrl( input: EnvironmentAutoCreateInput, apiKey: string, apiUrl: string, ): Promise { const response = await fetch(`${apiUrl}/images`, { method: 'POST', headers: authHeaders(apiKey), body: JSON.stringify({ prompt: buildPlatePrompt(input), aspect_ratio: '16:9', model_id: 'gemini-pro-image', enhance_prompt: false, negative_prompt: 'people, person, face, text, watermark, logo, blurry, deformed', }), }); if (!response.ok) { throw new Error(`POST /images failed: ${response.status} ${(await response.text()).slice(0, 200)}`); } const payload = await response.json() as { url?: string; image_url?: string; data?: { url?: string; images?: Array<{ full_url?: string; url?: string }> }; images?: Array<{ full_url?: string; url?: string }>; }; const imageUrl = payload.url ?? payload.image_url ?? payload.data?.url ?? payload.data?.images?.[0]?.full_url ?? payload.data?.images?.[0]?.url ?? payload.images?.[0]?.full_url ?? payload.images?.[0]?.url; if (!imageUrl) { throw new Error('POST /images succeeded but returned no image URL'); } return imageUrl; } function plateRefFor(name: string): string { const slug = name.trim().toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-+|-+$/g, ''); return `gobananas://environment/${slug}`; } export async function autoCreateEnvironments( inputs: EnvironmentAutoCreateInput[], options?: { projectSlug?: string; root?: string; apiKey?: string; apiUrl?: string; dryRun?: boolean }, ): Promise> { const apiKey = options?.apiKey ?? process.env.GO_BANANAS_API_KEY ?? ''; if (!apiKey) { throw new Error('GO_BANANAS_API_KEY is required for environment auto-create'); } const apiUrl = (options?.apiUrl ?? process.env.GO_BANANAS_API_URL ?? DEFAULT_BASE_URL).trim(); const workspace = options?.projectSlug ? await ensureProjectWorkspace(options.projectSlug, options.root ?? resolveWorkspaceRootFromEnv()) : null; const results: Record = {}; const persistedEntries: EnvironmentAssetEntry[] = []; for (const input of inputs) { const name = input.name.trim(); const description = input.description.trim(); if (!name || !description) { continue; } if (options?.dryRun) { results[name] = { imageUrl: '', plateRef: plateRefFor(name), created: true, persisted: false }; continue; } const imageUrl = await generatePlateUrl(input, apiKey, apiUrl); const plateRef = plateRefFor(name); persistedEntries.push({ name, description, plateUrl: imageUrl, plateRef }); results[name] = { imageUrl, plateRef, created: true, persisted: Boolean(workspace) }; } if (workspace && persistedEntries.length > 0 && options?.projectSlug) { await writeEnvironmentAssets(options.root ?? resolveWorkspaceRootFromEnv(), options.projectSlug, { schemaVersion: 1, projectSlug: options.projectSlug, generatedAt: new Date().toISOString(), environments: persistedEntries, }); } return results; }