All checks were successful
Deploy to Production / deploy (push) Successful in 53s
The free tier was hemorrhaging Anthropic cost with no abuse cap (no rate limit on /preview, Opus default in the build worker, 5-min cache TTL that made cache-miss the common case). This switches free users to GLM, paid users to Claude tiers, and tightens every leak found in the audit. Backend: - @bmm/llm: GLM provider via Zhipu's OpenAI-compatible endpoint, pickPreviewModel + pickBuildModel helpers, plan-aware ModelChoice - preview-cache TTL 5min -> 24h (kills the cache-miss path) - /v1/servers/preview: picks model from caller's plan, returns model name to UI - /v1/servers POST: enforces SERVER_LIMITS per plan (402), rate-limits builds - daily rate-limit on preview (5/40/150/1000) and build (3/20/100/500) - /v1/auth/me returns plan so the wizard can show the right model name - generator worker: GLM default, Anthropic Sonnet fallback if GLM errors Frontend: - Wizard fetches plan, shows "<model> is drafting the tool spec" pre-emptively, upgrade hint for hobby users, friendly errors for 402 / 429 - Pricing page: AI-model line per tier (Open-tier / Haiku / Sonnet / Opus), Team €149 -> €199, Enterprise €499 -> €999, daily-preview limit per tier - Privacy + Security: explicit subprocessor disclosure for Anthropic (US) / Zhipu (CN) and which tier uses which Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
19 lines
735 B
TypeScript
19 lines
735 B
TypeScript
import { z } from 'zod';
|
|
|
|
const Env = z.object({
|
|
DATABASE_URL: z.string(),
|
|
REDIS_URL: z.string().default('redis://localhost:6379'),
|
|
ANTHROPIC_API_KEY: z.string().optional(),
|
|
GLM_API_KEY: z.string().optional(),
|
|
RUNNER_HOST: z.string().default('localhost'),
|
|
RUNNER_PORT_RANGE_START: z.coerce.number().default(4100),
|
|
RUNNER_PORT_RANGE_END: z.coerce.number().default(4999),
|
|
CONTROL_PLANE_URL: z.string().default('http://host.docker.internal:4000'),
|
|
CONTROL_PLANE_PUBLIC_URL: z.string().default('http://localhost:4000'),
|
|
OAUTH_ISSUER: z.string().optional(),
|
|
MODEL_GENERATE: z.string().default('glm-4.5'),
|
|
MODEL_FIX: z.string().default('claude-haiku-4-5-20251001'),
|
|
});
|
|
|
|
export const config = Env.parse(process.env);
|