From 0fa154c7d6571a8f994a8c8ce59449452b3b96f7 Mon Sep 17 00:00:00 2001 From: Wuesteon Date: Fri, 12 Dec 2025 20:47:43 +0100 Subject: [PATCH 1/4] =?UTF-8?q?=F0=9F=90=9B=20fix(shared-auth):=20add=20au?= =?UTF-8?q?tomatic=20token=20refresh=20on=20401=20responses?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add backendUrl parameter to initializeWebAuth() for interceptor config - Expand isTokenExpiredResponse() to match more error patterns: - "invalid token", "token validation failed", "claim" (jose errors) - ERR_JWT_EXPIRED error code - Update all web apps to pass backendUrl for automatic refresh: - picture (3006), chat (3002), zitare (3007), contacts (3015) - calendar (3014), clock (3017), todo (3018) - Fix API client default port in picture web app This prevents users from being randomly signed out when JWT expires. The interceptor now catches 401 responses and automatically refreshes the token before retrying the request. --- .../apps/web/src/lib/stores/auth.svelte.ts | 15 ++++++++++++++- .../apps/web/src/lib/stores/auth.svelte.ts | 15 ++++++++++++++- .../apps/web/src/lib/stores/auth.svelte.ts | 15 ++++++++++++++- .../apps/web/src/lib/stores/auth.svelte.ts | 7 +++++-- apps/picture/apps/web/src/lib/api/client.ts | 8 +++++--- .../apps/web/src/lib/stores/auth.svelte.ts | 6 +++++- .../apps/web/src/lib/stores/auth.svelte.ts | 15 ++++++++++++++- .../apps/web/src/lib/stores/auth.svelte.ts | 7 +++++-- packages/shared-auth/src/index.ts | 19 +++++++++++++++++-- .../src/interceptors/fetchInterceptor.ts | 17 +++++++++++++---- 10 files changed, 106 insertions(+), 18 deletions(-) diff --git a/apps/calendar/apps/web/src/lib/stores/auth.svelte.ts b/apps/calendar/apps/web/src/lib/stores/auth.svelte.ts index 9ede84176..ced1c1ad4 100644 --- a/apps/calendar/apps/web/src/lib/stores/auth.svelte.ts +++ b/apps/calendar/apps/web/src/lib/stores/auth.svelte.ts @@ -20,6 +20,16 @@ function getAuthUrl(): string { return process.env.PUBLIC_MANA_CORE_AUTH_URL || 'http://localhost:3001'; } +// Get backend URL dynamically at runtime +function getBackendUrl(): string { + if (browser && typeof window !== 'undefined') { + const injectedUrl = (window as unknown as { __PUBLIC_BACKEND_URL__?: string }) + .__PUBLIC_BACKEND_URL__; + return injectedUrl || 'http://localhost:3014'; + } + return process.env.PUBLIC_BACKEND_URL || 'http://localhost:3014'; +} + // Lazy initialization to avoid SSR issues with localStorage let _authService: ReturnType['authService'] | null = null; let _tokenManager: ReturnType['tokenManager'] | null = null; @@ -27,7 +37,10 @@ let _tokenManager: ReturnType['tokenManager'] | null = function getAuthService() { if (!browser) return null; if (!_authService) { - const auth = initializeWebAuth({ baseUrl: getAuthUrl() }); + const auth = initializeWebAuth({ + baseUrl: getAuthUrl(), + backendUrl: getBackendUrl(), // Enables automatic token refresh on 401 responses + }); _authService = auth.authService; _tokenManager = auth.tokenManager; } diff --git a/apps/chat/apps/web/src/lib/stores/auth.svelte.ts b/apps/chat/apps/web/src/lib/stores/auth.svelte.ts index 92064596d..ed412923c 100644 --- a/apps/chat/apps/web/src/lib/stores/auth.svelte.ts +++ b/apps/chat/apps/web/src/lib/stores/auth.svelte.ts @@ -20,6 +20,16 @@ function getAuthUrl(): string { return process.env.PUBLIC_MANA_CORE_AUTH_URL || 'http://localhost:3001'; } +// Get backend URL dynamically at runtime +function getBackendUrl(): string { + if (browser && typeof window !== 'undefined') { + const injectedUrl = (window as unknown as { __PUBLIC_BACKEND_URL__?: string }) + .__PUBLIC_BACKEND_URL__; + return injectedUrl || 'http://localhost:3002'; + } + return process.env.PUBLIC_BACKEND_URL || 'http://localhost:3002'; +} + // Lazy initialization to avoid SSR issues with localStorage let _authService: ReturnType['authService'] | null = null; let _tokenManager: ReturnType['tokenManager'] | null = null; @@ -27,7 +37,10 @@ let _tokenManager: ReturnType['tokenManager'] | null = function getAuthService() { if (!browser) return null; if (!_authService) { - const auth = initializeWebAuth({ baseUrl: getAuthUrl() }); + const auth = initializeWebAuth({ + baseUrl: getAuthUrl(), + backendUrl: getBackendUrl(), // Enables automatic token refresh on 401 responses + }); _authService = auth.authService; _tokenManager = auth.tokenManager; } diff --git a/apps/clock/apps/web/src/lib/stores/auth.svelte.ts b/apps/clock/apps/web/src/lib/stores/auth.svelte.ts index 2613ee0b5..931f6327e 100644 --- a/apps/clock/apps/web/src/lib/stores/auth.svelte.ts +++ b/apps/clock/apps/web/src/lib/stores/auth.svelte.ts @@ -19,6 +19,16 @@ function getAuthUrl(): string { return process.env.PUBLIC_MANA_CORE_AUTH_URL || 'http://localhost:3001'; } +// Get backend URL dynamically at runtime +function getBackendUrl(): string { + if (browser && typeof window !== 'undefined') { + const injectedUrl = (window as unknown as { __PUBLIC_BACKEND_URL__?: string }) + .__PUBLIC_BACKEND_URL__; + return injectedUrl || 'http://localhost:3017'; + } + return process.env.PUBLIC_BACKEND_URL || 'http://localhost:3017'; +} + // Lazy initialization to avoid SSR issues with localStorage let _authService: ReturnType['authService'] | null = null; let _tokenManager: ReturnType['tokenManager'] | null = null; @@ -26,7 +36,10 @@ let _tokenManager: ReturnType['tokenManager'] | null = function getAuthService() { if (!browser) return null; if (!_authService) { - const auth = initializeWebAuth({ baseUrl: getAuthUrl() }); + const auth = initializeWebAuth({ + baseUrl: getAuthUrl(), + backendUrl: getBackendUrl(), // Enables automatic token refresh on 401 responses + }); _authService = auth.authService; _tokenManager = auth.tokenManager; } diff --git a/apps/contacts/apps/web/src/lib/stores/auth.svelte.ts b/apps/contacts/apps/web/src/lib/stores/auth.svelte.ts index 3122bf4f1..3f7320628 100644 --- a/apps/contacts/apps/web/src/lib/stores/auth.svelte.ts +++ b/apps/contacts/apps/web/src/lib/stores/auth.svelte.ts @@ -8,8 +8,8 @@ import { initializeWebAuth } from '@manacore/shared-auth'; import type { UserData } from '@manacore/shared-auth'; // Initialize Mana Core Auth only on the client side -// TODO: Use PUBLIC_MANA_CORE_AUTH_URL from env when available const MANA_AUTH_URL = 'http://localhost:3001'; +const BACKEND_URL = 'http://localhost:3015'; // Lazy initialization to avoid SSR issues with localStorage let _authService: ReturnType['authService'] | null = null; @@ -18,7 +18,10 @@ let _tokenManager: ReturnType['tokenManager'] | null = function getAuthService() { if (!browser) return null; if (!_authService) { - const auth = initializeWebAuth({ baseUrl: MANA_AUTH_URL }); + const auth = initializeWebAuth({ + baseUrl: MANA_AUTH_URL, + backendUrl: BACKEND_URL, // Enables automatic token refresh on 401 responses + }); _authService = auth.authService; _tokenManager = auth.tokenManager; } diff --git a/apps/picture/apps/web/src/lib/api/client.ts b/apps/picture/apps/web/src/lib/api/client.ts index 5e15d3754..3cac5de48 100644 --- a/apps/picture/apps/web/src/lib/api/client.ts +++ b/apps/picture/apps/web/src/lib/api/client.ts @@ -2,14 +2,16 @@ * API Client for Picture Backend * Replaces direct Supabase calls with backend API calls. * - * Token handling: Uses authStore.getValidToken() which automatically - * refreshes expired tokens before making requests. + * Token handling: + * - Uses authStore.getValidToken() which automatically refreshes expired tokens + * - The fetch interceptor (setupFetchInterceptor) handles 401 responses by refreshing and retrying + * - If refresh fails, the request fails and user should be redirected to login */ import { env } from '$env/dynamic/public'; import { authStore } from '$lib/stores/auth.svelte'; -const API_BASE = env.PUBLIC_BACKEND_URL || 'http://localhost:3003'; +const API_BASE = env.PUBLIC_BACKEND_URL || 'http://localhost:3006'; type FetchOptions = { method?: 'GET' | 'POST' | 'PATCH' | 'DELETE'; diff --git a/apps/picture/apps/web/src/lib/stores/auth.svelte.ts b/apps/picture/apps/web/src/lib/stores/auth.svelte.ts index b9ad5b753..b63abb804 100644 --- a/apps/picture/apps/web/src/lib/stores/auth.svelte.ts +++ b/apps/picture/apps/web/src/lib/stores/auth.svelte.ts @@ -7,6 +7,7 @@ import { browser } from '$app/environment'; import { env } from '$env/dynamic/public'; const MANA_AUTH_URL = env.PUBLIC_MANA_CORE_AUTH_URL || 'http://localhost:3001'; +const BACKEND_URL = env.PUBLIC_BACKEND_URL || 'http://localhost:3006'; export interface UserData { id: string; @@ -28,7 +29,10 @@ async function getAuthService() { if (!_authService) { try { const { initializeWebAuth } = await import('@manacore/shared-auth'); - const auth = initializeWebAuth({ baseUrl: MANA_AUTH_URL }); + const auth = initializeWebAuth({ + baseUrl: MANA_AUTH_URL, + backendUrl: BACKEND_URL, // Enables automatic token refresh on 401 responses + }); _authService = auth.authService; _tokenManager = auth.tokenManager; } catch (error) { diff --git a/apps/todo/apps/web/src/lib/stores/auth.svelte.ts b/apps/todo/apps/web/src/lib/stores/auth.svelte.ts index 7c3210601..817c251fd 100644 --- a/apps/todo/apps/web/src/lib/stores/auth.svelte.ts +++ b/apps/todo/apps/web/src/lib/stores/auth.svelte.ts @@ -20,6 +20,16 @@ function getAuthUrl(): string { return process.env.PUBLIC_MANA_CORE_AUTH_URL || 'http://localhost:3001'; } +// Get backend URL dynamically at runtime +function getBackendUrl(): string { + if (browser && typeof window !== 'undefined') { + const injectedUrl = (window as unknown as { __PUBLIC_BACKEND_URL__?: string }) + .__PUBLIC_BACKEND_URL__; + return injectedUrl || 'http://localhost:3018'; + } + return process.env.PUBLIC_BACKEND_URL || 'http://localhost:3018'; +} + // Lazy initialization to avoid SSR issues with localStorage let _authService: ReturnType['authService'] | null = null; let _tokenManager: ReturnType['tokenManager'] | null = null; @@ -27,7 +37,10 @@ let _tokenManager: ReturnType['tokenManager'] | null = function getAuthService() { if (!browser) return null; if (!_authService) { - const auth = initializeWebAuth({ baseUrl: getAuthUrl() }); + const auth = initializeWebAuth({ + baseUrl: getAuthUrl(), + backendUrl: getBackendUrl(), // Enables automatic token refresh on 401 responses + }); _authService = auth.authService; _tokenManager = auth.tokenManager; } diff --git a/apps/zitare/apps/web/src/lib/stores/auth.svelte.ts b/apps/zitare/apps/web/src/lib/stores/auth.svelte.ts index 3122bf4f1..688e7e17f 100644 --- a/apps/zitare/apps/web/src/lib/stores/auth.svelte.ts +++ b/apps/zitare/apps/web/src/lib/stores/auth.svelte.ts @@ -8,8 +8,8 @@ import { initializeWebAuth } from '@manacore/shared-auth'; import type { UserData } from '@manacore/shared-auth'; // Initialize Mana Core Auth only on the client side -// TODO: Use PUBLIC_MANA_CORE_AUTH_URL from env when available const MANA_AUTH_URL = 'http://localhost:3001'; +const BACKEND_URL = 'http://localhost:3007'; // Lazy initialization to avoid SSR issues with localStorage let _authService: ReturnType['authService'] | null = null; @@ -18,7 +18,10 @@ let _tokenManager: ReturnType['tokenManager'] | null = function getAuthService() { if (!browser) return null; if (!_authService) { - const auth = initializeWebAuth({ baseUrl: MANA_AUTH_URL }); + const auth = initializeWebAuth({ + baseUrl: MANA_AUTH_URL, + backendUrl: BACKEND_URL, // Enables automatic token refresh on 401 responses + }); _authService = auth.authService; _tokenManager = auth.tokenManager; } diff --git a/packages/shared-auth/src/index.ts b/packages/shared-auth/src/index.ts index ce6391bb3..77db0d5f9 100644 --- a/packages/shared-auth/src/index.ts +++ b/packages/shared-auth/src/index.ts @@ -81,13 +81,21 @@ export type { ContactsClientConfig, ContactSearchOptions } from './clients/conta * ```typescript * import { initializeWebAuth } from '@manacore/shared-auth'; * + * // Basic setup (interceptor only for auth URL) * const { authService, tokenManager } = initializeWebAuth({ - * baseUrl: 'https://api.example.com', + * baseUrl: 'https://auth.example.com', + * }); + * + * // With backend URL (interceptor for both auth and backend - recommended) + * const { authService, tokenManager } = initializeWebAuth({ + * baseUrl: 'https://auth.example.com', + * backendUrl: 'https://api.example.com', * }); * ``` */ export function initializeWebAuth(config: { baseUrl: string; + backendUrl?: string; storageKeys?: Partial; }) { // Set up adapters @@ -99,8 +107,15 @@ export function initializeWebAuth(config: { const authService = _createAuthService(config); const tokenManager = _createTokenManager(authService); - // Set up interceptor + // Set up interceptor for auth URL _setupFetchInterceptor(authService, tokenManager); + // Set up interceptor for backend URL if provided (for automatic token refresh on 401) + if (config.backendUrl) { + _setupFetchInterceptor(authService, tokenManager, { + backendUrl: config.backendUrl, + }); + } + return { authService, tokenManager }; } diff --git a/packages/shared-auth/src/interceptors/fetchInterceptor.ts b/packages/shared-auth/src/interceptors/fetchInterceptor.ts index 259679352..78736519a 100644 --- a/packages/shared-auth/src/interceptors/fetchInterceptor.ts +++ b/packages/shared-auth/src/interceptors/fetchInterceptor.ts @@ -201,8 +201,9 @@ async function makeRequestWithToken( } /** - * Check if response indicates token expiration - * Only return true for explicit token expiration, not generic unauthorized errors + * Check if response indicates a token issue that warrants a refresh attempt + * Any 401 response should trigger a refresh attempt - if the refresh fails, + * then we know the session is truly invalid */ function isTokenExpiredResponse(responseData: Record): boolean { const error = responseData.error as Record | undefined; @@ -211,13 +212,21 @@ function isTokenExpiredResponse(responseData: Record): boolean ).toLowerCase(); const errorCode = String(responseData.code || error?.code || ''); - // Only trigger refresh for explicit token expiration messages + // Trigger refresh for any token-related auth error + // This includes: + // - Explicit expiration: "jwt expired", "token expired" + // - Generic validation failures: "invalid token", "token validation failed" + // - Backend passthrough errors: "exp claim", "claim timestamp" return ( errorMessage.includes('jwt expired') || errorMessage.includes('token expired') || errorMessage.includes('token has expired') || + errorMessage.includes('invalid token') || + errorMessage.includes('token validation failed') || + errorMessage.includes('claim') || // Catches jose errors like "exp claim timestamp check failed" errorCode === 'PGRST301' || - errorCode === 'TOKEN_EXPIRED' + errorCode === 'TOKEN_EXPIRED' || + errorCode === 'ERR_JWT_EXPIRED' ); } From 3f9bc5761b09644e51e94d2f326760252d86bbfe Mon Sep 17 00:00:00 2001 From: Wuesteon Date: Fri, 12 Dec 2025 20:48:10 +0100 Subject: [PATCH 2/4] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20refactor(chat):=20simp?= =?UTF-8?q?lify=20chat=20service=20and=20update=20seed=20data?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Remove unused code from chat.service.ts - Simplify seed.ts with fewer AI models - Update CLAUDE.md documentation - Update .env.example - Remove unused package dependency - Minor UI fix in chat page --- apps/chat/CLAUDE.md | 98 +++++---- apps/chat/apps/backend/.env.example | 14 +- apps/chat/apps/backend/package.json | 1 - .../apps/backend/src/chat/chat.service.ts | 206 +----------------- apps/chat/apps/backend/src/db/seed.ts | 141 +----------- .../routes/(protected)/chat/[id]/+page.svelte | 8 +- 6 files changed, 75 insertions(+), 393 deletions(-) diff --git a/apps/chat/CLAUDE.md b/apps/chat/CLAUDE.md index 107216de4..0632271fd 100644 --- a/apps/chat/CLAUDE.md +++ b/apps/chat/CLAUDE.md @@ -24,6 +24,7 @@ pnpm dev:chat:mobile # Start mobile app pnpm dev:chat:web # Start web app pnpm dev:chat:landing # Start landing page pnpm dev:chat:backend # Start backend server +pnpm dev:chat:full # Start backend + web + auth together ``` ### Mobile App (chat/apps/mobile) @@ -43,6 +44,9 @@ pnpm build:prod # Build production version pnpm start:dev # Start with hot reload pnpm build # Build for production pnpm start:prod # Start production server +pnpm db:push # Push schema to database +pnpm db:seed # Seed AI models +pnpm db:studio # Open Drizzle Studio ``` ### Web App (chat/apps/web) @@ -66,7 +70,8 @@ pnpm preview # Preview production build - **Mobile**: React Native 0.76.7 + Expo SDK 52, NativeWind, Expo Router - **Web**: SvelteKit 2.x, Svelte 5, Tailwind CSS 4 - **Landing**: Astro 5.16, Tailwind CSS -- **Backend**: NestJS 10, OpenRouter/Gemini AI, Supabase +- **Backend**: NestJS 10, OpenRouter AI, Drizzle ORM, PostgreSQL +- **Auth**: Mana Core Auth (JWT) - **Types**: TypeScript 5.x ## Architecture @@ -75,34 +80,45 @@ pnpm preview # Preview production build | Endpoint | Method | Description | | --------------------------------- | ------ | --------------------------- | -| `/api/health` | GET | Health check | -| `/api/chat/models` | GET | List available AI models | -| `/api/chat/completions` | POST | Create chat completion | -| `/api/conversations` | GET | List user conversations | -| `/api/conversations/:id` | GET | Get conversation details | -| `/api/conversations/:id/messages` | GET | Get conversation messages | -| `/api/conversations` | POST | Create new conversation | -| `/api/conversations/:id/messages` | POST | Add message to conversation | +| `/api/v1/health` | GET | Health check | +| `/api/v1/chat/models` | GET | List available AI models | +| `/api/v1/chat/completions` | POST | Create chat completion | +| `/api/v1/conversations` | GET | List user conversations | +| `/api/v1/conversations/:id` | GET | Get conversation details | +| `/api/v1/conversations/:id/messages` | GET | Get conversation messages | +| `/api/v1/conversations` | POST | Create new conversation | +| `/api/v1/conversations/:id/messages` | POST | Add message to conversation | ### Environment Variables #### Backend (.env) -``` -OPENROUTER_API_KEY=... # Get at https://openrouter.ai/keys -GOOGLE_GENAI_API_KEY=... # Optional: For Gemini models -SUPABASE_URL=https://... -SUPABASE_SERVICE_KEY=... +```env +# Required - All AI models via OpenRouter +OPENROUTER_API_KEY=sk-or-v1-xxx # Get at https://openrouter.ai/keys + +# Database (uses shared Docker PostgreSQL) +DATABASE_URL=postgresql://manacore:devpassword@localhost:5432/chat + +# Auth +MANA_CORE_AUTH_URL=http://localhost:3001 + +# Server PORT=3002 -DEV_BYPASS_AUTH=true # Optional: Skip auth in development ``` #### Mobile (.env) +```env +EXPO_PUBLIC_MANA_CORE_AUTH_URL=http://localhost:3001 +EXPO_PUBLIC_BACKEND_URL=http://localhost:3002 ``` -EXPO_PUBLIC_SUPABASE_URL=https://... -EXPO_PUBLIC_SUPABASE_ANON_KEY=... -EXPO_PUBLIC_BACKEND_URL=http://localhost:3001 + +#### Web (.env) + +```env +PUBLIC_MANA_CORE_AUTH_URL=http://localhost:3001 +PUBLIC_BACKEND_URL=http://localhost:3002 ``` ## Code Style Guidelines @@ -113,39 +129,43 @@ EXPO_PUBLIC_BACKEND_URL=http://localhost:3001 - **Styling**: Tailwind CSS everywhere - **Formatting**: 100 char line limit, 2 space tabs, single quotes -## AI Models Available +## AI Models Available (via OpenRouter) -### OpenRouter Models (Recommended) +All models are accessed through OpenRouter, providing access to 100+ models with a single API key. | Model ID | Name | Price | Best For | | -------- | ---- | ----- | -------- | -| ...440201 | Llama 3.1 8B | $0.05/M | Everyday tasks, cheap | +| ...440201 | Llama 3.1 8B | $0.05/M | Everyday tasks (default) | | ...440202 | Llama 3.1 70B | $0.35/M | Complex reasoning | | ...440203 | DeepSeek V3 | $0.14/M | Reasoning at low cost | | ...440204 | Mistral Small | $0.10/M | General tasks | | ...440205 | Claude 3.5 Sonnet | $3/M | Best quality | | ...440206 | GPT-4o Mini | $0.15/M | Balanced performance | -### Google Gemini Models +## Quick Start -| Model ID | Name | Description | Default | -| -------- | ---- | ----------- | ------- | -| ...440101 | Gemini 2.5 Flash | Fast, efficient responses | Yes | -| ...440102 | Gemini 2.0 Flash-Lite | Ultra-lightweight model | No | -| ...440103 | Gemini 2.5 Pro | Most capable model | No | - -## OpenRouter Setup - -To enable OpenRouter models: - -- [ ] Get API key at https://openrouter.ai/keys -- [ ] Add `OPENROUTER_API_KEY=sk-or-v1-xxx` to `apps/chat/apps/backend/.env` -- [ ] Re-seed database: `pnpm --filter @chat/backend db:seed` -- [ ] Test: `pnpm dev:chat:backend` +1. **Get OpenRouter API key** at https://openrouter.ai/keys +2. **Create `.env`** in `apps/chat/apps/backend/`: + ```env + OPENROUTER_API_KEY=sk-or-v1-xxx + DATABASE_URL=postgresql://manacore:devpassword@localhost:5432/chat + MANA_CORE_AUTH_URL=http://localhost:3001 + PORT=3002 + ``` +3. **Start services**: + ```bash + pnpm docker:up # Start PostgreSQL + pnpm dev:chat:full # Start auth + backend + web + ``` +4. **Seed database** (first time only): + ```bash + pnpm --filter @chat/backend db:push + pnpm --filter @chat/backend db:seed + ``` ## Important Notes 1. **Security**: API keys are stored in the backend only - never in client apps -2. **Authentication**: Uses Supabase Auth, shared with Mana Core ecosystem -3. **Database**: Supabase PostgreSQL with RLS policies -4. **Deployment**: Backend runs on port 3001 by default +2. **Authentication**: Uses Mana Core Auth (JWT tokens) +3. **Database**: PostgreSQL with Drizzle ORM (uses shared Docker container) +4. **Deployment**: Backend runs on port 3002 diff --git a/apps/chat/apps/backend/.env.example b/apps/chat/apps/backend/.env.example index f011962ae..e94bb4010 100644 --- a/apps/chat/apps/backend/.env.example +++ b/apps/chat/apps/backend/.env.example @@ -1,20 +1,14 @@ -# OpenRouter Configuration (Recommended - multi-model access) +# OpenRouter Configuration (Required) # Get your API key at https://openrouter.ai/keys +# All AI models are accessed through OpenRouter OPENROUTER_API_KEY=your-openrouter-api-key -# Google Gemini Configuration -GOOGLE_GENAI_API_KEY=your-google-api-key - -# Azure OpenAI Configuration (Optional) -AZURE_OPENAI_ENDPOINT=https://your-azure-openai-endpoint.openai.azure.com -AZURE_OPENAI_API_KEY=your-api-key-here -AZURE_OPENAI_API_VERSION=2024-12-01-preview - # Mana Core Auth Configuration MANA_CORE_AUTH_URL=http://localhost:3001 # PostgreSQL Database Configuration -DATABASE_URL=postgresql://chat:password@localhost:5432/chat +# Uses shared Docker PostgreSQL with separate 'chat' database +DATABASE_URL=postgresql://manacore:devpassword@localhost:5432/chat # Server Configuration PORT=3002 diff --git a/apps/chat/apps/backend/package.json b/apps/chat/apps/backend/package.json index 6ce38d5e5..c4b909062 100644 --- a/apps/chat/apps/backend/package.json +++ b/apps/chat/apps/backend/package.json @@ -24,7 +24,6 @@ "docker:clean": "docker compose down -v --rmi local" }, "dependencies": { - "@google/generative-ai": "^0.24.1", "@manacore/shared-errors": "workspace:*", "@manacore/shared-nestjs-auth": "workspace:*", "@nestjs/common": "^10.4.15", diff --git a/apps/chat/apps/backend/src/chat/chat.service.ts b/apps/chat/apps/backend/src/chat/chat.service.ts index edc00e97a..20a569d59 100644 --- a/apps/chat/apps/backend/src/chat/chat.service.ts +++ b/apps/chat/apps/backend/src/chat/chat.service.ts @@ -2,7 +2,6 @@ import { Injectable, Inject, Logger } from '@nestjs/common'; import { ConfigService } from '@nestjs/config'; import { eq } from 'drizzle-orm'; import { AsyncResult, ok, err, ValidationError, ServiceError } from '@manacore/shared-errors'; -import { GoogleGenerativeAI } from '@google/generative-ai'; import OpenAI from 'openai'; import { DATABASE_CONNECTION } from '../db/database.module'; import { Database } from '../db/connection'; @@ -14,37 +13,14 @@ import type { ChatCompletionResponseDto } from './dto/chat-completion.dto'; @Injectable() export class ChatService { private readonly logger = new Logger(ChatService.name); - // Azure OpenAI config - private readonly azureApiKey: string; - private readonly azureEndpoint: string; - private readonly azureApiVersion: string; - // Google Gemini config - private readonly geminiClient: GoogleGenerativeAI | null = null; - // OpenRouter config + // OpenRouter config (primary provider) private readonly openRouterClient: OpenAI | null = null; constructor( private configService: ConfigService, @Inject(DATABASE_CONNECTION) private readonly db: Database ) { - // Azure OpenAI setup - this.azureApiKey = this.configService.get('AZURE_OPENAI_API_KEY') || ''; - this.azureEndpoint = - this.configService.get('AZURE_OPENAI_ENDPOINT') || - 'https://memoroseopenai.openai.azure.com'; - this.azureApiVersion = - this.configService.get('AZURE_OPENAI_API_VERSION') || '2024-12-01-preview'; - - // Google Gemini setup - const geminiApiKey = this.configService.get('GOOGLE_GENAI_API_KEY'); - if (geminiApiKey) { - this.geminiClient = new GoogleGenerativeAI(geminiApiKey); - this.logger.log('Google Gemini client initialized'); - } else { - this.logger.warn('GOOGLE_GENAI_API_KEY is not set - Gemini models unavailable'); - } - - // OpenRouter setup + // OpenRouter setup (primary and only provider) const openRouterApiKey = this.configService.get('OPENROUTER_API_KEY'); if (openRouterApiKey) { this.openRouterClient = new OpenAI({ @@ -57,11 +33,7 @@ export class ChatService { }); this.logger.log('OpenRouter client initialized'); } else { - this.logger.warn('OPENROUTER_API_KEY is not set - OpenRouter models unavailable'); - } - - if (!this.azureApiKey) { - this.logger.warn('AZURE_OPENAI_API_KEY is not set - Azure models unavailable'); + this.logger.error('OPENROUTER_API_KEY is not set - Chat will not work!'); } } @@ -100,176 +72,8 @@ export class ChatService { this.logger.log(`User ${userId} creating chat completion with model ${dto.modelId}`); } - // Route to appropriate provider - if (model.provider === 'gemini') { - return this.createGeminiCompletion(model, dto); - } else if (model.provider === 'openrouter') { - return this.createOpenRouterCompletion(model, dto); - } else { - return this.createAzureCompletion(model, dto); - } - } - - private async createGeminiCompletion( - model: Model, - dto: ChatCompletionDto - ): AsyncResult { - if (!this.geminiClient) { - return err(ServiceError.externalError('Google Gemini', 'Gemini client not configured')); - } - - const params = model.parameters as { - model?: string; - temperature?: number; - max_tokens?: number; - } | null; - - const modelName = params?.model || 'gemini-2.5-flash'; - const temperature = dto.temperature ?? params?.temperature ?? 0.7; - const maxTokens = dto.maxTokens ?? params?.max_tokens ?? 8192; - - this.logger.log(`Sending request to Google Gemini model: ${modelName}`); - - try { - const genModel = this.geminiClient.getGenerativeModel({ - model: modelName, - generationConfig: { - temperature, - maxOutputTokens: maxTokens, - }, - }); - - // Convert messages to Gemini format - // Gemini expects alternating user/model messages, with system as first user message - const systemMessages = dto.messages.filter((m) => m.role === 'system'); - const chatMessages = dto.messages.filter((m) => m.role !== 'system'); - - // Build history for chat (all but last message) - const history = chatMessages.slice(0, -1).map((msg) => ({ - role: msg.role === 'user' ? 'user' : 'model', - parts: [{ text: msg.content }], - })); - - // Last message to send - const lastMessage = chatMessages[chatMessages.length - 1]; - let userPrompt = lastMessage?.content || ''; - - // Prepend system instruction if present - if (systemMessages.length > 0) { - const systemPrompt = systemMessages.map((m) => m.content).join('\n'); - userPrompt = `${systemPrompt}\n\n${userPrompt}`; - } - - const chat = genModel.startChat({ history }); - const result = await chat.sendMessage(userPrompt); - const response = result.response; - const messageContent = response.text(); - - if (!messageContent) { - this.logger.warn('No message content in Gemini response'); - return err(ServiceError.generationFailed('Google Gemini', 'No response generated')); - } - - // Gemini provides usage metadata - const usageMetadata = response.usageMetadata; - - return ok({ - content: messageContent, - usage: { - prompt_tokens: usageMetadata?.promptTokenCount || 0, - completion_tokens: usageMetadata?.candidatesTokenCount || 0, - total_tokens: usageMetadata?.totalTokenCount || 0, - }, - }); - } catch (error) { - this.logger.error('Error calling Google Gemini API', error); - return err( - ServiceError.generationFailed( - 'Google Gemini', - error instanceof Error ? error.message : 'Unknown error', - error instanceof Error ? error : undefined - ) - ); - } - } - - private async createAzureCompletion( - model: Model, - dto: ChatCompletionDto - ): AsyncResult { - const params = model.parameters as { - deployment?: string; - temperature?: number; - max_tokens?: number; - } | null; - - const deployment = params?.deployment || 'gpt-4o-mini-se'; - const temperature = dto.temperature ?? params?.temperature ?? 0.7; - const maxTokens = dto.maxTokens ?? params?.max_tokens ?? 1000; - - // Prepare request body - const requestBody: Record = { - messages: dto.messages.map((msg) => ({ - role: msg.role, - content: msg.content, - })), - }; - - // Model-specific parameters - const isGPTOModel = deployment.includes('gpt-o') || deployment.includes('gpt-4o'); - - if (!isGPTOModel) { - requestBody.max_tokens = maxTokens; - requestBody.temperature = temperature; - } - - const url = `${this.azureEndpoint}/openai/deployments/${deployment}/chat/completions?api-version=${this.azureApiVersion}`; - - this.logger.log(`Sending request to Azure OpenAI: ${url}`); - - try { - const response = await fetch(url, { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - 'api-key': this.azureApiKey, - }, - body: JSON.stringify(requestBody), - }); - - if (!response.ok) { - const errorText = await response.text(); - this.logger.error(`API error: ${response.status} - ${errorText}`); - return err(ServiceError.externalError('Azure OpenAI', `API error: ${response.status}`)); - } - - const data = await response.json(); - - const messageContent = data.choices?.[0]?.message?.content; - - if (!messageContent) { - this.logger.warn('No message content in response'); - return err(ServiceError.generationFailed('Azure OpenAI', 'No response generated')); - } - - return ok({ - content: messageContent, - usage: { - prompt_tokens: data.usage?.prompt_tokens || 0, - completion_tokens: data.usage?.completion_tokens || 0, - total_tokens: data.usage?.total_tokens || 0, - }, - }); - } catch (error) { - this.logger.error('Error calling Azure OpenAI API', error); - return err( - ServiceError.generationFailed( - 'Azure OpenAI', - error instanceof Error ? error.message : 'Unknown error', - error instanceof Error ? error : undefined - ) - ); - } + // All models go through OpenRouter + return this.createOpenRouterCompletion(model, dto); } private async createOpenRouterCompletion( diff --git a/apps/chat/apps/backend/src/db/seed.ts b/apps/chat/apps/backend/src/db/seed.ts index c478cac10..109c9b3f3 100644 --- a/apps/chat/apps/backend/src/db/seed.ts +++ b/apps/chat/apps/backend/src/db/seed.ts @@ -33,49 +33,7 @@ async function seed() { const modelData = [ // ============================================ - // Google Gemini Models (Primary - fast & cost-effective) - // ============================================ - { - id: '550e8400-e29b-41d4-a716-446655440101', - name: 'Gemini 2.5 Flash', - description: 'Fastest & most cost-effective - ideal for everyday tasks', - provider: 'gemini', - parameters: { - model: 'gemini-2.5-flash', - temperature: 0.7, - max_tokens: 8192, - }, - isActive: true, - isDefault: true, // Default model - }, - { - id: '550e8400-e29b-41d4-a716-446655440102', - name: 'Gemini 2.0 Flash-Lite', - description: 'Ultra-fast lightweight model - minimal latency', - provider: 'gemini', - parameters: { - model: 'gemini-2.0-flash-lite', - temperature: 0.7, - max_tokens: 4096, - }, - isActive: true, - isDefault: false, - }, - { - id: '550e8400-e29b-41d4-a716-446655440103', - name: 'Gemini 2.5 Pro', - description: 'Most powerful Gemini - complex reasoning & analysis', - provider: 'gemini', - parameters: { - model: 'gemini-2.5-pro', - temperature: 0.7, - max_tokens: 16384, - }, - isActive: true, - isDefault: false, - }, - // ============================================ - // OpenRouter Models (Multi-provider, cost-effective) + // OpenRouter Models (All models via OpenRouter) // ============================================ { id: '550e8400-e29b-41d4-a716-446655440201', @@ -88,7 +46,7 @@ async function seed() { max_tokens: 4096, }, isActive: true, - isDefault: false, + isDefault: true, // Default model - fast and cost-effective }, { id: '550e8400-e29b-41d4-a716-446655440202', @@ -155,101 +113,6 @@ async function seed() { isActive: true, isDefault: false, }, - // ============================================ - // Azure OpenAI GPT-5 Family (Inactive - no deployment) - // ============================================ - { - id: '550e8400-e29b-41d4-a716-446655440001', - name: 'GPT-5 Mini', - description: 'Fast & cost-effective - best for everyday tasks', - provider: 'azure', - parameters: { - temperature: 0.7, - max_tokens: 8192, - deployment: 'gpt-5-mini', - }, - isActive: false, - isDefault: false, - }, - { - id: '550e8400-e29b-41d4-a716-446655440002', - name: 'GPT-5 Nano', - description: 'Ultra-fast responses with low latency', - provider: 'azure', - parameters: { - temperature: 0.7, - max_tokens: 4096, - deployment: 'gpt-5-nano', - }, - isActive: false, - isDefault: false, - }, - { - id: '550e8400-e29b-41d4-a716-446655440003', - name: 'GPT-5 Chat', - description: 'Advanced multimodal conversations with emotional intelligence', - provider: 'azure', - parameters: { - temperature: 0.7, - max_tokens: 16384, - deployment: 'gpt-5-chat', - }, - isActive: false, - isDefault: false, - }, - { - id: '550e8400-e29b-41d4-a716-446655440004', - name: 'GPT-5', - description: 'Most powerful LLM - logic-heavy & multi-step tasks', - provider: 'azure', - parameters: { - temperature: 0.7, - max_tokens: 32768, - deployment: 'gpt-5', - }, - isActive: false, - isDefault: false, - }, - { - id: '550e8400-e29b-41d4-a716-446655440005', - name: 'GPT-5 Codex', - description: 'Optimized for coding & front-end development', - provider: 'azure', - parameters: { - temperature: 0.7, - max_tokens: 32768, - deployment: 'gpt-5-codex', - }, - isActive: false, - isDefault: false, - }, - // O-Series Reasoning Models (Inactive - no deployment) - { - id: '550e8400-e29b-41d4-a716-446655440006', - name: 'o4-mini', - description: 'Latest reasoning model - best for STEM & code', - provider: 'azure', - parameters: { - temperature: 1, // Reasoning models work best with temp=1 - max_tokens: 16384, - deployment: 'o4-mini', - }, - isActive: false, - isDefault: false, - }, - { - id: '550e8400-e29b-41d4-a716-446655440007', - name: 'o3', - description: 'Advanced reasoning - 20% fewer errors than o1', - provider: 'azure', - parameters: { - temperature: 1, - max_tokens: 32768, - deployment: 'o3', - }, - isActive: false, - isDefault: false, - }, ]; await db.insert(models).values(modelData); diff --git a/apps/chat/apps/web/src/routes/(protected)/chat/[id]/+page.svelte b/apps/chat/apps/web/src/routes/(protected)/chat/[id]/+page.svelte index a9b58b72e..5262047bf 100644 --- a/apps/chat/apps/web/src/routes/(protected)/chat/[id]/+page.svelte +++ b/apps/chat/apps/web/src/routes/(protected)/chat/[id]/+page.svelte @@ -28,15 +28,17 @@ let showVersionsModal = $state(false); let showDocumentPanel = $state(true); - // Track current request to prevent race conditions - let currentLoadId = $state(0); + // Track current request to prevent race conditions (not reactive to avoid effect loops) + let currentLoadId = 0; + let lastLoadedConversationId = ''; const conversationId = $derived($page.params.id ?? ''); const isDocumentMode = $derived(conversation?.documentMode ?? false); // React to conversationId changes with race condition protection $effect(() => { - if (conversationId) { + if (conversationId && conversationId !== lastLoadedConversationId) { + lastLoadedConversationId = conversationId; loadData(conversationId); } }); From a9aeffabb19a89708bca7860d200ce279e61a01c Mon Sep 17 00:00:00 2001 From: Wuesteon Date: Fri, 12 Dec 2025 20:49:38 +0100 Subject: [PATCH 3/4] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20refactor(picture):=20s?= =?UTF-8?q?implify=20credit=20system=20to=20production-only?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Remove freemium model (3 free generations per user) - Credit check now only enforced in production (not staging) - Add HTTPS detection for webhook mode fallback - Simplify GenerateResponse (remove freeGenerationsRemaining) - Clean up async generation metadata handling πŸ€– Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- .../backend/src/generate/generate.service.ts | 103 ++++++------------ 1 file changed, 36 insertions(+), 67 deletions(-) diff --git a/apps/picture/apps/backend/src/generate/generate.service.ts b/apps/picture/apps/backend/src/generate/generate.service.ts index 1c87dbb6a..c88f1aff2 100644 --- a/apps/picture/apps/backend/src/generate/generate.service.ts +++ b/apps/picture/apps/backend/src/generate/generate.service.ts @@ -8,7 +8,7 @@ import { Logger, } from '@nestjs/common'; import { ConfigService } from '@nestjs/config'; -import { eq, and, count } from 'drizzle-orm'; +import { eq } from 'drizzle-orm'; import { CreditClientService } from '@mana-core/nestjs-integration'; import { DATABASE_CONNECTION } from '../db/database.module'; import { Database } from '../db/connection'; @@ -18,7 +18,6 @@ import { ReplicateService, GenerationParams } from './replicate.service'; import { StorageService } from '../upload/storage.service'; import { GenerateImageDto } from './dto/generate.dto'; -const FREE_GENERATIONS_LIMIT = 3; const CREDITS_PER_GENERATION = 10; export interface GenerateResponse { @@ -26,14 +25,14 @@ export interface GenerateResponse { status: string; image?: Image; creditsUsed?: number; - freeGenerationsRemaining?: number; } @Injectable() export class GenerateService { private readonly logger = new Logger(GenerateService.name); private readonly webhookBaseUrl: string; - private readonly isStaging: boolean; + private readonly isProduction: boolean; + private readonly canUseWebhooks: boolean; constructor( @Inject(DATABASE_CONNECTION) private readonly db: Database, @@ -44,65 +43,41 @@ export class GenerateService { ) { this.webhookBaseUrl = this.configService.get('WEBHOOK_BASE_URL') || 'http://localhost:3003'; - // Freemium/credit system only enforced in staging - this.isStaging = this.configService.get('NODE_ENV') === 'staging'; + // Credit system only enforced in production + this.isProduction = this.configService.get('NODE_ENV') === 'production'; + // Replicate requires HTTPS webhooks - detect if we can use them + this.canUseWebhooks = this.webhookBaseUrl.startsWith('https://'); + if (!this.canUseWebhooks) { + this.logger.warn( + `Webhook URL is not HTTPS (${this.webhookBaseUrl}). Falling back to sync mode for all generations.` + ); + } } /** - * Get count of completed generations for a user - */ - private async getUserGenerationCount(userId: string): Promise { - const result = await this.db - .select({ count: count() }) - .from(imageGenerations) - .where(and(eq(imageGenerations.userId, userId), eq(imageGenerations.status, 'completed'))); - return result[0]?.count ?? 0; - } - - /** - * Check if user can generate (has free generations or credits) - * Returns: { canGenerate, isFree, freeRemaining, creditsRequired } + * Check if user has enough credits to generate + * Credits are only enforced in production (NODE_ENV=production) */ async checkGenerationAccess(userId: string): Promise<{ canGenerate: boolean; - isFree: boolean; - freeGenerationsRemaining: number; creditsRequired: number; currentBalance?: number; }> { - const completedCount = await this.getUserGenerationCount(userId); - const freeRemaining = Math.max(0, FREE_GENERATIONS_LIMIT - completedCount); - - // If user has free generations, they can proceed - if (freeRemaining > 0) { + // In development, skip credit check (users get 150 free credits on signup anyway) + if (!this.isProduction) { return { canGenerate: true, - isFree: true, - freeGenerationsRemaining: freeRemaining, - creditsRequired: 0, - }; - } - - // No free generations - check credits (only in staging) - if (!this.isStaging) { - // In development/production without credit enforcement, allow generation - return { - canGenerate: true, - isFree: false, - freeGenerationsRemaining: 0, creditsRequired: CREDITS_PER_GENERATION, }; } - // In staging, check actual credit balance + // In production, check actual credit balance try { const balance = await this.creditClient.getBalance(userId); const hasEnoughCredits = balance.balance >= CREDITS_PER_GENERATION; return { canGenerate: hasEnoughCredits, - isFree: false, - freeGenerationsRemaining: 0, creditsRequired: CREDITS_PER_GENERATION, currentBalance: balance.balance, }; @@ -111,8 +86,6 @@ export class GenerateService { // On error, allow generation (fail open for better UX) return { canGenerate: true, - isFree: false, - freeGenerationsRemaining: 0, creditsRequired: CREDITS_PER_GENERATION, }; } @@ -123,7 +96,7 @@ export class GenerateService { */ async generateImage(userId: string, dto: GenerateImageDto): Promise { try { - // Check if user can generate (freemium/credit check) + // Check if user has enough credits (only enforced in production) const access = await this.checkGenerationAccess(userId); if (!access.canGenerate) { @@ -168,7 +141,6 @@ export class GenerateService { .returning(); const generation = generationResult[0]; - const isFreeGeneration = access.isFree; // Build generation params const generationParams: GenerationParams = { @@ -186,25 +158,29 @@ export class GenerateService { style: dto.style, }; - // If waitForResult is true, use synchronous generation with polling - if (dto.waitForResult) { + // Use sync mode if: + // 1. Client explicitly requested waitForResult + // 2. Webhooks are not available (no HTTPS URL) + const useSyncMode = dto.waitForResult || !this.canUseWebhooks; + + if (useSyncMode) { + if (!this.canUseWebhooks && !dto.waitForResult) { + this.logger.debug('Using sync mode because webhooks are not available (no HTTPS)'); + } + const result = await this.generateSync(generation, generationParams); - // Consume credits after successful generation (if not free) - if (result.status === 'completed' && !isFreeGeneration && this.isStaging) { + // Consume credits after successful generation (only in production) + if (result.status === 'completed' && this.isProduction) { await this.consumeCreditsForGeneration(userId, generation.id); result.creditsUsed = CREDITS_PER_GENERATION; } - // Add free generations remaining info - const newAccess = await this.checkGenerationAccess(userId); - result.freeGenerationsRemaining = newAccess.freeGenerationsRemaining; - return result; } // Otherwise use async generation with webhook (credits consumed on webhook completion) - return this.generateAsync(generation, model, generationParams, isFreeGeneration); + return this.generateAsync(generation, model, generationParams); } catch (error) { if (error instanceof NotFoundException || error instanceof HttpException) { throw error; @@ -329,8 +305,7 @@ export class GenerateService { private async generateAsync( generation: ImageGeneration, model: any, - params: GenerationParams, - isFreeGeneration: boolean + params: GenerationParams ): Promise { try { const webhookUrl = `${this.webhookBaseUrl}/api/generate/webhook`; @@ -342,15 +317,12 @@ export class GenerateService { webhookUrl ); - // Update generation with prediction ID and free generation flag (in metadata) + // Update generation with prediction ID await this.db .update(imageGenerations) .set({ replicatePredictionId: prediction.id, status: 'processing', - // Store isFreeGeneration in a way that can be retrieved in webhook - // We'll use the errorMessage field temporarily for metadata (cleared on success) - errorMessage: isFreeGeneration ? 'FREE_GENERATION' : null, }) .where(eq(imageGenerations.id, generation.id)); @@ -514,7 +486,7 @@ export class GenerateService { async handleWebhook(body: any): Promise<{ received: boolean }> { try { - const { id, status, output, error, metrics } = body; + const { id, status, output, error } = body; if (!id) { return { received: false }; @@ -534,14 +506,11 @@ export class GenerateService { const generation = result[0]; - // Check if this was a free generation (stored in errorMessage field temporarily) - const isFreeGeneration = generation.errorMessage === 'FREE_GENERATION'; - if (status === 'succeeded' && output) { await this.processCompletedGeneration(generation, output); - // Consume credits for paid generations in staging - if (!isFreeGeneration && this.isStaging) { + // Consume credits in production + if (this.isProduction) { await this.consumeCreditsForGeneration(generation.userId, generation.id); } } else if (status === 'failed') { From 612f6a10ef460b8738f19a39f9e22e98b849c3de Mon Sep 17 00:00:00 2001 From: Wuesteon Date: Fri, 12 Dec 2025 20:50:03 +0100 Subject: [PATCH 4/4] =?UTF-8?q?=F0=9F=94=A7=20chore:=20switch=20chat=20bac?= =?UTF-8?q?kend=20to=20OpenRouter=20API?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Replace Azure OpenAI and Google Gemini with OpenRouter API - Update generate-env.mjs to use OPENROUTER_API_KEY - Remove temporary doc_nils.md file πŸ€– Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- .env.development | 14 +- doc_nils.md | 973 --------------------------------------- pnpm-lock.yaml | 502 ++++++++++---------- scripts/generate-env.mjs | 5 +- 4 files changed, 260 insertions(+), 1234 deletions(-) delete mode 100644 doc_nils.md diff --git a/.env.development b/.env.development index f2c0b8483..18f715025 100644 --- a/.env.development +++ b/.env.development @@ -67,17 +67,9 @@ CHAT_DATABASE_URL=postgresql://manacore:devpassword@localhost:5432/chat DEV_BYPASS_AUTH=true DEV_USER_ID=00000000-0000-0000-0000-000000000000 -# Google Gemini API (primary - fast & cost-effective) -GOOGLE_GENAI_API_KEY=AIzaSyApsYQXxN6PuXpF8-7j6MonCACwS0ZxNRc - -# Azure OpenAI (fallback) -AZURE_OPENAI_ENDPOINT=https://memoroseopenai.openai.azure.com -AZURE_OPENAI_API_KEY=3082103c9b0d4270a795686ccaa89921 -AZURE_OPENAI_API_VERSION=2024-12-01-preview - -# Chat Supabase (if using Supabase for chat data) -CHAT_SUPABASE_URL=https://your-chat-project.supabase.co -CHAT_SUPABASE_ANON_KEY=your-supabase-anon-key +# OpenRouter API (all AI models via OpenRouter) +# Get your API key at https://openrouter.ai/keys +OPENROUTER_API_KEY=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 # ============================================ # MAERCHENZAUBER PROJECT diff --git a/doc_nils.md b/doc_nils.md deleted file mode 100644 index 80a69a281..000000000 --- a/doc_nils.md +++ /dev/null @@ -1,973 +0,0 @@ -why do i need to set it in envs? - -⏺ Great questions! Let me explain the Docker Compose deployment architecture: - -How This Setup Works - -1. Containers Are Always Running - -Yes! The Docker containers run continuously on your staging server: - -β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” -β”‚ Staging Server (Ubuntu) β”‚ -β”‚ β”‚ -β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ -β”‚ β”‚ Docker Compose Network β”‚ β”‚ -β”‚ β”‚ β”‚ β”‚ -β”‚ β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ β”‚ -β”‚ β”‚ β”‚ Postgres β”‚ β”‚ Redis β”‚ β”‚ β”‚ -β”‚ β”‚ β”‚ :5432 β”‚ β”‚ :6379 β”‚ β”‚ β”‚ -β”‚ β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ β”‚ -β”‚ β”‚ β”‚ β”‚ -β”‚ β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ β”‚ -β”‚ β”‚ β”‚ Auth β”‚ β”‚ Chat β”‚ β”‚ β”‚ -β”‚ β”‚ β”‚ :3001 β”‚ β”‚ :3002 β”‚ β”‚ β”‚ -β”‚ β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ β”‚ -β”‚ β”‚ β”‚ β”‚ -β”‚ β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ β”‚ -β”‚ β”‚ β”‚ ManaDeck β”‚ β”‚ Nginx β”‚ β”‚ β”‚ -β”‚ β”‚ β”‚ :3003 β”‚ β”‚ :80 :443 β”‚ β”‚ β”‚ -β”‚ β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ β”‚ -β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ -β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ -↑ -β”‚ Users access via HTTP/HTTPS -β”‚ Nginx routes to backend services - -2. Data Persistence - YES! - -The postgres data is persistent across redeployments. Look at the -docker-compose.staging.yml: - -postgres: -volumes: - postgres_data:/var/lib/postgresql/data # ← Persistent volume - -volumes: -postgres_data: -name: manacore-postgres-staging # ← Named volume stored on server disk - -What happens on redeploy: - -1. docker compose pull - Downloads new backend images -2. docker compose up -d - Recreates containers with new code -3. Postgres volume stays untouched - Your data survives! - -# First deployment - -docker compose up -d - -# Creates: postgres_data volume β†’ - -/var/lib/docker/volumes/manacore-postgres-staging - -# After redeploy (10 times later) - -docker compose up -d - -# Uses: SAME postgres_data volume β†’ Your data is still there! - -3. Risks - -Current Risks: - -❌ No backups - If the server disk fails, all data is lost❌ No password set - -Postgres won't start without POSTGRES_PASSWORD❌ Single server - If it goes -down, everything is offline⚠️ Docker volume only - Data lives in -/var/lib/docker/volumes/, not easily inspectable - -Recommended Setup: - -# 1. Regular backups (add to server crontab) - -0 2 \* \* \* docker exec manacore-postgres-staging pg*dump -U postgres manacore > -/backup/db*$(date +\%Y\%m\%d).sql - -# 2. Or use Supabase for production data (you're already using it!) - -# Keep postgres in staging for testing, but production uses Supabase's managed - -database - -4. Why You Need POSTGRES_PASSWORD - -The Problem: - -Postgres refuses to start without a password for security reasons. When the -container starts for the first time, it initializes the database and needs to -set the postgres superuser password. - -How to Set It: - -⏺ Read(.github/workflows/cd-staging.yml) -⎿ Read 40 lines - -⏺ The workflow creates the .env file from GitHub Secrets. Here's what you need: - -How to Fix This - -Step 1: Set GitHub Secrets - -Go to your GitHub repository: -Settings β†’ Secrets and variables β†’ Actions β†’ New repository secret - -You need to add these secrets: - -STAGING_POSTGRES_HOST=postgres -STAGING_POSTGRES_PORT=5432 -STAGING_POSTGRES_DB=manacore -STAGING_POSTGRES_USER=postgres -STAGING_POSTGRES_PASSWORD= - -STAGING_REDIS_HOST=redis -STAGING_REDIS_PORT=6379 -STAGING_REDIS_PASSWORD= - -STAGING_MANA_SERVICE_URL=http://mana-core-auth:3001 -STAGING_JWT_SECRET= -STAGING_JWT_PUBLIC_KEY= -STAGING_JWT_PRIVATE_KEY= - -STAGING_SUPABASE_URL= -STAGING_SUPABASE_ANON_KEY= -STAGING_SUPABASE_SERVICE_ROLE_KEY= - -STAGING_AZURE_OPENAI_ENDPOINT= -STAGING_AZURE_OPENAI_API_KEY= - -Step 2: For Testing NOW (Temporary) - -Since you're on the server already, manually create the .env file: - -# You're already here: ~/manacore-staging - -nano .env - -Add this content: - -# Database - -POSTGRES_HOST=postgres -POSTGRES_PORT=5432 -POSTGRES_DB=manacore -POSTGRES_USER=postgres -POSTGRES_PASSWORD=staging_password_123 # ← Use any password you want - -# Redis - -REDIS_HOST=redis -REDIS_PORT=6379 -REDIS_PASSWORD=redis_password_123 - -# Mana Core Auth - -MANA_SERVICE_URL=http://mana-core-auth:3001 -JWT_SECRET=your_jwt_secret_here -JWT_PUBLIC_KEY=your_public_key_here -JWT_PRIVATE_KEY=your_private_key_here - -# Supabase - -SUPABASE_URL=your_supabase_url -SUPABASE_ANON_KEY=your_supabase_anon_key -SUPABASE_SERVICE_ROLE_KEY=your_supabase_service_role_key - -# Azure OpenAI - -AZURE_OPENAI_ENDPOINT=your_azure_endpoint -AZURE_OPENAI_API_KEY=your_azure_api_key -AZURE_OPENAI_API_VERSION=2024-12-01-preview - -# Environment - -NODE_ENV=staging - -Save and exit (Ctrl+X, Y, Enter), then: - -# Restart postgres with the new password - -docker compose down postgres -docker compose up -d postgres - -# Check if it's healthy now - -docker compose ps -docker compose logs --tail=20 postgres - -Once it works, you should see: -βœ… manacore-postgres-staging Up healthy - -Would you like me to help you generate secure passwords for the production -GitHub secrets? - -> Would you like me to help you generate secure passwords for the production -> GitHub secrets? -> yes write me an script which outputs this so i can copy -> paste it to github secrets - -⏺ Write(scripts/generate-staging-secrets.sh) -⎿ Wrote 138 lines to scripts/generate-staging-secrets.sh -#!/bin/bash # Generate Staging Secrets for GitHub # Run this script and copy the output to GitHub Secrets -set -e -echo "================================================" -echo " STAGING SECRETS GENERATOR" -echo "================================================" -… +128 lines (ctrl+o to expand) - -⏺ Bash(chmod +x /Users/wuesteon/dev/mana_universe/manacore-monorepo/scripts/generate- -staging-secrets.sh) -⎿ (No content) - -⏺ Bash(/Users/wuesteon/dev/mana_universe/manacore-monorepo/scripts/generate-staging-s -ecrets.sh) -⎿ ================================================ -STAGING SECRETS GENERATOR -================================================ -… +108 lines (ctrl+o to expand) - -⏺ Perfect! πŸŽ‰ The script has generated secure secrets for you. Here's what to do: - -Quick Copy Guide - -For each secret, go to GitHub β†’ Settings β†’ Secrets and variables β†’ Actions β†’ -New repository secret - -Database Secrets: - -Name: STAGING_POSTGRES_PASSWORD -Value: qUHBDzlc0fMntYPdT0LXwLHnvMUdUFfp - -Name: STAGING_REDIS_PASSWORD -Value: OxYL7XQgPAG4HjS29UFDYEmrkfk5kJoJ - -Name: STAGING_JWT_SECRET -Value: ayNMdsOOlAWIjduJ1aqPe9ep9DtCzvKwgmV0qGrct4WZyXJn1NMxrTIAa6EkiijMfuh5ZyGY -D0TcTGbkWF4g - -Name: STAGING_JWT_PRIVATE_KEY -Value: b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZWQy -NTUxOQAAACDnbEhUMtYMTPhwKuqc8DV+J0i/oyWWJ+wWw7p6YwivmwAAAJjKVMoXylTKFwAAAAtzc2g -tZWQyNTUxOQAAACDnbEhUMtYMTPhwKuqc8DV+J0i/oyWWJ+wWw7p6YwivmwAAAEBjB/hkfSdozgvi/S -JsvbYxA2URarKSVkgVBwqbB1+yQedsSFQy1gxM+HAq6pzwNX4nSL+jJZYn7BbDunpjCK+bAAAAFG1hb -mFjb3JlLXN0YWdpbmctand0AQ== - -Simple Values (copy as-is): - -STAGING_POSTGRES_HOST = postgres -STAGING_POSTGRES_PORT = 5432 -STAGING_POSTGRES_DB = manacore -STAGING_POSTGRES_USER = postgres -STAGING_REDIS_HOST = redis -STAGING_REDIS_PORT = 6379 -STAGING_MANA_SERVICE_URL = http://mana-core-auth:3001 -STAGING_HOST = 46.224.108.214 -STAGING_USER = deploy - -SSH Key (get from your local machine): - -⏺ Write(scripts/get-ssh-key.sh) -⎿ Wrote 19 lines to scripts/get-ssh-key.sh - #!/bin/bash # Get SSH Private Key Content for GitHub Secret -echo "================================================" -echo " SSH PRIVATE KEY FOR STAGING_SSH_KEY" -echo "================================================" -echo "" -echo "Copy the ENTIRE output below (including BEGIN and END lines):" -echo "" -… +9 lines (ctrl+o to expand) - -⏺ Bash(chmod +x -/Users/wuesteon/dev/mana_universe/manacore-monorepo/scripts/get-ssh-key.sh && -/Users/wuesteon/dev/mana_universe/manacore-monorepo/scripts/get-ssh-key.s…) -⎿ ================================================ -SSH PRIVATE KEY FOR STAGING_SSH_KEY -================================================ -… +14 lines (ctrl+o to expand) - -⏺ Perfect! Now for the SSH key: - -Name: STAGING_SSH_KEY -Value: (Copy the entire content above, including -----BEGIN and -----END lines) - ---- - -Still Need to Fill Manually: - -You need to add your Supabase and Azure OpenAI credentials: - -1. STAGING_SUPABASE_URL - Get from your Supabase project settings -2. STAGING_SUPABASE_ANON_KEY - Get from Supabase project settings -3. STAGING_SUPABASE_SERVICE_ROLE_KEY - Get from Supabase project settings -4. STAGING_AZURE_OPENAI_ENDPOINT - Your Azure OpenAI endpoint -5. STAGING_AZURE_OPENAI_API_KEY - Your Azure OpenAI API key - ---- - -Quick Test Locally First - -Before setting all GitHub secrets, let's test with the generated passwords on -your server: - -# On your staging server (you're already SSH'd in) - -nano ~/manacore-staging/.env - -# Add/update just these lines: - -POSTGRES_PASSWORD=qUHBDzlc0fMntYPdT0LXwLHnvMUdUFfp -REDIS_PASSWORD=OxYL7XQgPAG4HjS29UFDYEmrkfk5kJoJ - -# Save and restart - -docker compose down -docker compose up -d - -# Check if postgres starts successfully - -docker compose logs --tail=20 postgres -docker compose ps - -Once postgres is healthy, you'll know the setup works! Then you can add all the -secrets to GitHub for CI/CD deployments. πŸš€ - -Would you like me to also create a script that shows you where to get your -Supabase credentials? - -> do i need to add those -> -> STAGING_POSTGRES_HOST = postgres -> STAGING_POSTGRES_PORT = 5432 -> STAGING_POSTGRES_DB = manacore -> STAGING_POSTGRES_USER = postgres -> STAGING_REDIS_HOST = redis -> STAGING_REDIS_PORT = 6379 -> STAGING_MANA_SERVICE_URL = http://mana-core-auth:3001 -> STAGING_HOST = 46.224.108.214 -> STAGING_USER = deploy -> ? - -⏺ Yes! You need to add ALL of them to GitHub Secrets. - -Even though they're not "secret" values (like postgres, -5432), the workflow reads them from secrets.\*, so GitHub -Actions needs them defined. - -Looking at the workflow file -.github/workflows/cd-staging.yml: - -POSTGRES_HOST=${{ secrets.STAGING_POSTGRES_HOST }} - POSTGRES_PORT=${{ secrets.STAGING_POSTGRES_PORT }} -POSTGRES_DB=${{ secrets.STAGING_POSTGRES_DB }} - POSTGRES_USER=${{ secrets.STAGING_POSTGRES_USER }} -POSTGRES_PASSWORD=${{ secrets.STAGING_POSTGRES_PASSWORD }} - -REDIS_HOST=${{ secrets.STAGING_REDIS_HOST }} - REDIS_PORT=${{ secrets.STAGING_REDIS_PORT }} -REDIS_PASSWORD=${{ secrets.STAGING_REDIS_PASSWORD }} - -MANA_SERVICE_URL=${{ secrets.STAGING_MANA_SERVICE_URL }} - -And for SSH connection: -ssh ${{ secrets.STAGING_USER }}@${{ secrets.STAGING_HOST }} - -All of these must be GitHub secrets or the workflow will fail -with "secret not found" errors. - ---- - -Complete List of Secrets to Add: - -Required (17 total): - -βœ… Database: - -- STAGING_POSTGRES_HOST = postgres -- STAGING_POSTGRES_PORT = 5432 -- STAGING_POSTGRES_DB = manacore -- STAGING_POSTGRES_USER = postgres -- STAGING_POSTGRES_PASSWORD = (generated password) - -βœ… Redis: - -- STAGING_REDIS_HOST = redis -- STAGING_REDIS_PORT = 6379 -- STAGING_REDIS_PASSWORD = (generated password) - -βœ… Mana Core Auth: - -- STAGING_MANA_SERVICE_URL = http://mana-core-auth:3001 -- STAGING_JWT_SECRET = (generated secret) -- STAGING_JWT_PUBLIC_KEY = (empty for now, or generated) -- STAGING_JWT_PRIVATE_KEY = (generated key) - -βœ… Supabase (you fill these): - -- STAGING_SUPABASE_URL -- STAGING_SUPABASE_ANON_KEY -- STAGING_SUPABASE_SERVICE_ROLE_KEY - -βœ… Azure OpenAI (you fill these): - -- STAGING_AZURE_OPENAI_ENDPOINT -- STAGING_AZURE_OPENAI_API_KEY - -βœ… SSH: - -- STAGING_SSH_KEY = (your SSH private key) -- STAGING_HOST = 46.224.108.214 -- STAGING_USER = deploy - ---- - -Why even simple values? - -GitHub Actions doesn't distinguish between "secret" and -"non-secret" values. Everything passed via ${{ secrets.* }} -must be defined in GitHub Secrets, even if it's just postgres -or 5432. - -This is actually good for flexibility - you can change these -values (like switching to a different database name) without -modifying the workflow file. - -> cant i set them in the ci/cd file? - STAGING_POSTGRES_HOST = -> postgres - -- STAGING_POSTGRES_PORT = 5432 -- STAGING_POSTGRES_DB = manacore -- STAGING_POSTGRES_USER = postgres - -⏺ YES! Absolutely! That's actually a better practice. Only -truly sensitive values (passwords, API keys) should be GitHub -Secrets. - -Let me update the workflow to hardcode the non-sensitive -configuration values: - -⏺ Read(.github/workflows/cd-staging.yml) -⎿ Read 40 lines - -⏺ Update(.github/workflows/cd-staging.yml) -⎿ Updated .github/workflows/cd-staging.yml with 11 addition -and 11 removals -61 - 62 - name: Copy environment file -63 run: | -64 - # Create staging env file from - secrets -64 + # Create staging env file (mix + of hardcoded config and secrets) -65 cat > .env.staging << EOF -66 - # Database -67 - POSTGRES_HOST=${{ - - secrets.STAGING_POSTGRES_HOST }} - 68 - POSTGRES_PORT=${{ - - secrets.STAGING_POSTGRES_PORT }} -69 - POSTGRES_DB=${{ - - secrets.STAGING_POSTGRES_DB }} - 70 - POSTGRES_USER=${{ - - secrets.STAGING_POSTGRES_USER }} -66 + # Database - Configuration -67 + POSTGRES_HOST=postgres -68 + POSTGRES_PORT=5432 -69 + POSTGRES_DB=manacore -70 + POSTGRES_USER=postgres -71 POSTGRES_PASSWORD=${{ - secrets.STAGING_POSTGRES_PASSWORD }} - 72 - 73 - # Redis - 74 - REDIS_HOST=${{ - - secrets.STAGING_REDIS_HOST }} -75 - REDIS_PORT=${{ - - secrets.STAGING_REDIS_PORT }} - 73 + # Redis - Configuration - 74 + REDIS_HOST=redis - 75 + REDIS_PORT=6379 - 76 REDIS_PASSWORD=${{ - secrets.STAGING_REDIS_PASSWORD }} -77 - 78 - # Mana Core Auth -79 - MANA_SERVICE_URL=${{ - - secrets.STAGING_MANA_SERVICE_URL }} - 78 + # Mana Core Auth - Configuration - 79 + - + MANA_SERVICE_URL=http://mana-core-auth:3001 - 80 JWT_SECRET=${{ - secrets.STAGING_JWT_SECRET }} -81 JWT_PUBLIC_KEY=${{ - secrets.STAGING_JWT_PUBLIC_KEY }} - 82 JWT_PRIVATE_KEY=${{ - secrets.STAGING_JWT_PRIVATE_KEY }} - -⏺ Perfect! Now let's update the SSH connection parts too: - -⏺ Update(.github/workflows/cd-staging.yml) -⎿ Updated .github/workflows/cd-staging.yml with 7 additions -and 2 removals -39 ssh-private-key: ${{ - secrets.STAGING_SSH_KEY }} - 40 - 41 - name: Add staging server to known hosts - 42 + env: - 43 + STAGING_HOST: 46.224.108.214 - 44 run: | - 45 mkdir -p ~/.ssh - 46 - ssh-keyscan -H ${{ secrets. - - STAGING_HOST }} >> ~/.ssh/known_hosts - 46 + ssh-keyscan -H $STAGING_HOST - + >> ~/.ssh/known_hosts - 47 - 48 - name: Prepare deployment directory - 49 + env: - 50 + STAGING_USER: deploy - 51 + STAGING_HOST: 46.224.108.214 - 52 run: | - 53 - ssh ${{ secrets.STAGING_USER }}@$ - {{ secrets.STAGING_HOST }} << 'EOF' -53 + ssh $STAGING_USER@$STAGING_HOST + << 'EOF' -54 mkdir -p ~/manacore-staging -55 cd ~/manacore-staging -56 - -⏺ Update(.github/workflows/cd-staging.yml) -⎿ Updated .github/workflows/cd-staging.yml with 4 additions -and 1 removal -61 EOF -62 - 63 - name: Copy docker-compose file -64 + env: -65 + STAGING_USER: deploy -66 + STAGING_HOST: 46.224.108.214 -67 run: | -68 - scp docker-compose.staging.yml $ - - {{ secrets.STAGING_USER }}@${{ secrets. - - STAGING_HOST }} - :~/manacore-staging/docker-compose.yml -68 + scp docker-compose.staging.yml $ - + STAGING_USER@$STAGING_HOST + :~/manacore-staging/docker-compose.yml -69 - 70 - name: Copy environment file -71 run: | - -⏺ Update(.github/workflows/cd-staging.yml) -⎿ Updated .github/workflows/cd-staging.yml with 4 additions -and 1 removal -68 scp docker-compose.staging.yml -$STAGING_USER@$STAGING_HOST:~/manacore-staging/d -ocker-compose.yml -69 - 70 - name: Copy environment file -71 + env: -72 + STAGING_USER: deploy -73 + STAGING_HOST: 46.224.108.214 -74 run: | -75 # Create staging env file (mix of -hardcoded config and secrets) -76 cat > .env.staging << EOF -... -103 NODE_ENV=staging -104 EOF -105 - 106 - scp .env.staging ${{ secrets. - - STAGING_USER }}@${{ secrets.STAGING_HOST }} - :~/manacore-staging/.env -106 + scp .env.staging $STAGING_USER@$ + STAGING_HOST:~/manacore-staging/.env -107 rm .env.staging -108 - 109 - name: Login to GitHub Container -Registry on staging server - -⏺ Read(.github/workflows/cd-staging.yml) -⎿ Read 60 lines - -⏺ Update(.github/workflows/cd-staging.yml) -⎿ Updated .github/workflows/cd-staging.yml with 16 addition -and 4 removals -110 rm .env.staging -111 - 112 - name: Login to GitHub Container -Registry on staging server -113 + env: -114 + STAGING_USER: deploy -115 + STAGING_HOST: 46.224.108.214 -116 run: | -117 - ssh ${{ secrets.STAGING_USER }}@$ - {{ secrets.STAGING_HOST }} << EOF -117 + ssh $STAGING_USER@$STAGING_HOST + << EOF -118 # Login to ghcr.io with GitHub -token -119 echo "${{ secrets.GITHUB_TOKEN }}" - | docker login ghcr.io -u ${{ github.actor }} - --password-stdin - 120 EOF - 121 - 122 - name: Pull latest Docker images - 123 + env: - 124 + STAGING_USER: deploy - 125 + STAGING_HOST: 46.224.108.214 - 126 run: | - 127 - ssh ${{ secrets.STAGING_USER }}@$ - {{ secrets.STAGING_HOST }} << 'EOF' -127 + ssh $STAGING_USER@$STAGING_HOST + << 'EOF' -128 cd ~/manacore-staging -129 docker compose pull -130 EOF -131 - 132 - name: Deploy services -133 + env: -134 + STAGING_USER: deploy -135 + STAGING_HOST: 46.224.108.214 -136 run: | -137 SERVICE="${{ - github.event.inputs.service || 'all' }}" - 138 - 139 - ssh ${{ secrets.STAGING_USER }}@$ - {{ secrets.STAGING_HOST }} << EOF -139 + ssh $STAGING_USER@$STAGING_HOST + << EOF -140 cd ~/manacore-staging -141 - 142 # Determine which services to -deploy -... -148 EOF -149 - 150 - name: Run health checks -151 + env: -152 + STAGING_USER: deploy -153 + STAGING_HOST: 46.224.108.214 -154 run: | -155 - ssh ${{ secrets.STAGING_USER }}@$ - {{ secrets.STAGING_HOST }} << 'EOF' -155 + ssh $STAGING_USER@$STAGING_HOST + << 'EOF' -156 cd ~/manacore-staging -157 - 158 # Wait for services to fully start - -⏺ Read(.github/workflows/cd-staging.yml) -⎿ Read 15 lines - -⏺ Update(.github/workflows/cd-staging.yml) -⎿ Updated .github/workflows/cd-staging.yml with 4 additions -and 1 removal -212 EOF -213 - 214 - name: Run database migrations -215 + env: -216 + STAGING_USER: deploy -217 + STAGING_HOST: 46.224.108.214 -218 run: | -219 # Run migrations for services that -need them -220 - ssh ${{ secrets.STAGING_USER }}@$ - {{ secrets.STAGING_HOST }} << 'EOF' -220 + ssh $STAGING_USER@$STAGING_HOST + << 'EOF' -221 cd ~/manacore-staging -222 - 223 # Mana Core Auth migrations - -⏺ Perfect! Now let me update the secrets generation script to -reflect the reduced list: - -⏺ Write(scripts/generate-staging-secrets.sh) -⎿ Updated scripts/generate-staging-secrets.sh with 25 - additions and 38 removals -11 echo "" -12 echo "Copy each value below to GitHub Settings -β†’ Secrets and variables β†’ Actions" -13 echo "" -14 + echo "Note: Configuration values (host, ports, + etc.) are now hardcoded in the workflow" -15 + echo "Only sensitive values (passwords, keys) + need to be added as secrets" -16 + echo "" -17 echo "========================================= -=======" -18 echo "" -19 - ... -32 - 33 # Output all secrets in GitHub format -34 echo "# -============================================" -35 - echo "# DATABASE SECRETS" -35 + echo "# DATABASE SECRETS (2 secrets)" -36 echo "# -============================================" -37 echo "" -38 - echo "STAGING_POSTGRES_HOST" -39 - echo "postgres" -40 - echo "" -41 - echo "STAGING_POSTGRES_PORT" -42 - echo "5432" -43 - echo "" -44 - echo "STAGING_POSTGRES_DB" -45 - echo "manacore" -46 - echo "" -47 - echo "STAGING_POSTGRES_USER" -48 - echo "postgres" -49 - echo "" -38 echo "STAGING_POSTGRES_PASSWORD" -39 echo "$POSTGRES_PASSWORD" - 40 echo "" - 41 - 42 echo "# - ============================================" - 43 - echo "# REDIS SECRETS" - 43 + echo "# REDIS SECRETS (1 secret)" - 44 echo "# - ============================================" - 45 echo "" - 46 - echo "STAGING_REDIS_HOST" - 47 - echo "redis" - 48 - echo "" - 49 - echo "STAGING_REDIS_PORT" - 50 - echo "6379" - 51 - echo "" - 46 echo "STAGING_REDIS_PASSWORD" - 47 echo "$REDIS_PASSWORD" -48 echo "" -49 - 50 echo "# -============================================" -51 - echo "# MANA CORE AUTH SECRETS" -51 + echo "# MANA CORE AUTH SECRETS (3 secrets)" -52 echo "# -============================================" -53 echo "" -54 - echo "STAGING_MANA_SERVICE_URL" -55 - echo "http://mana-core-auth:3001" -56 - echo "" -54 echo "STAGING_JWT_SECRET" -55 echo "$JWT_SECRET" -56 echo "" -... -83 echo "" -84 - 85 echo "# -============================================" -86 - echo "# SUPABASE SECRETS (Fill these manually)" -86 + echo "# SUPABASE SECRETS (Fill these manually + - 3 secrets)" -87 echo "# -============================================" -88 echo "" -89 echo "STAGING_SUPABASE_URL" -... -97 echo "" -98 - 99 echo "# -============================================" -100 - echo "# AZURE OPENAI SECRETS (Fill these - manually)" -100 + echo "# AZURE OPENAI SECRETS (Fill these + manually - 2 secrets)" -101 echo "# -============================================" -102 echo "" -103 echo "STAGING_AZURE_OPENAI_ENDPOINT" -... -108 echo "" -109 - 110 echo "# -============================================" -111 - echo "# SSH DEPLOYMENT SECRETS (Fill these - manually)" -111 + echo "# SSH DEPLOYMENT SECRETS (Fill these + manually - 1 secret)" -112 echo "# -============================================" -113 echo "" -114 echo "STAGING_SSH_KEY" -115 - echo "Contents of: ~/.ssh/hetzner_deploy_key" -115 + echo "Run: cat ~/.ssh/hetzner_deploy_key" -116 + echo "(Copy the ENTIRE output including + -----BEGIN and -----END lines)" -117 echo "" -118 - echo "STAGING_HOST" -119 - echo "46.224.108.214" -120 - echo "" -121 - echo "STAGING_USER" -122 - echo "deploy" -123 - echo "" -118 - 119 echo "======================================== -========" -120 - echo " DONE!" -120 + echo " SUMMARY" -121 echo "======================================== -========" -122 echo "" -123 - echo "IMPORTANT: Save these values securely!" -124 - echo "You will need them to add to GitHub - Secrets." -123 + echo "Total secrets to add: 12" -124 + echo " - Auto-generated: 6 (passwords, JWT + keys)" -125 + echo " - Manual: 6 (Supabase, Azure, SSH + key)" -126 echo "" -127 + echo "The following are now HARDCODED in the + workflow:" -128 + echo " - POSTGRES_HOST, POSTGRES_PORT, + POSTGRES_DB, POSTGRES_USER" -129 + echo " - REDIS_HOST, REDIS_PORT" -130 + echo " - MANA_SERVICE_URL" -131 + echo " - STAGING_HOST (46.224.108.214)" -132 + echo " - STAGING_USER (deploy)" -133 + echo "" -134 + echo "======================================== + ========" -135 + echo "" -136 echo "Next steps:" -137 echo "1. Go to: https://github.com/YOUR_ORG/ma -nacore-monorepo/settings/secrets/actions" -138 echo "2. Click 'New repository secret' for -each value above" -139 echo "3. Copy the secret name (e.g., -STAGING_POSTGRES_PASSWORD)" -140 echo "4. Copy the secret value (the line below -the name)" -141 - echo "5. Fill in Supabase and Azure - values manually" -141 + echo "5. Fill in Supabase, Azure, and SSH key + values manually" -142 echo "" - -⏺ Perfect! Now let's commit these workflow improvements: - -⏺ Bash(git add .github/workflows/cd-staging.yml -scripts/generate-staging-secrets.sh scripts/get-ssh-key.sh) -⎿ (No content) - -⏺ Bash(git commit -m "♻️ refactor(cd): hardcode non-sensitive -config in staging workflow…) -⎿ [main 234703a1] ♻️ refactor(cd): hardcode non-sensitive co -nfig in staging workflow -3 files changed, 188 insertions(+), 20 deletions(-) -… +690 lines (ctrl+o to expand) - -⏺ Bash(git push) -⎿ To https://github.com/Memo-2023/manacore-monorepo.git -cf2b6aaa..234703a1 main -> main - -⏺ Perfect! All changes committed and pushed to GitHub. πŸŽ‰ - -Summary of What We Did - -Reduced GitHub Secrets Requirements: 21 β†’ 12 - -Hardcoded in workflow (no longer secrets): - -- POSTGRES_HOST=postgres -- POSTGRES_PORT=5432 -- POSTGRES_DB=manacore -- POSTGRES_USER=postgres -- REDIS_HOST=redis -- REDIS_PORT=6379 -- MANA_SERVICE_URL=http://mana-core-auth:3001 -- STAGING_HOST=46.224.108.214 -- STAGING_USER=deploy - -Still need as GitHub Secrets (12 total): - -1. Auto-generated (6): - - STAGING_POSTGRES_PASSWORD - - STAGING_REDIS_PASSWORD - - STAGING_JWT_SECRET - - STAGING_JWT_PUBLIC_KEY - - STAGING_JWT_PRIVATE_KEY - - STAGING_SSH_KEY - -2. You provide manually (6): - - STAGING_SUPABASE_URL - - STAGING_SUPABASE_ANON_KEY - - STAGING_SUPABASE_SERVICE_ROLE_KEY - - STAGING_AZURE_OPENAI_ENDPOINT - - STAGING_AZURE_OPENAI_API_KEY - ---- - -Next Steps - -1. Generate secrets (auto-generated ones are already shown - earlier in this chat) -2. Add them to GitHub at: https://github.com/Memo-2023/manaco - re-monorepo/settings/secrets/actions -3. Add your Supabase and Azure credentials manually -4. Test the staging deployment - trigger a manual workflow - run - -Would you like me to re-run the secrets generator script to -get fresh values, or shall we now test the postgres setup on -your server? - -> which secrets am i missing: Repository secrets -> Name - -Last updated -sort ascending -Actions -STAGING_HOST -2 days ago -STAGING_JWT_PRIVATE_KEY -4 minutes ago -STAGING_JWT_SECRET -6 minutes ago -STAGING_POSTGRES_HOST -4 minutes ago -STAGING_POSTGRES_PASSWORD -6 minutes ago -STAGING_POSTGRES_PORT -4 minutes ago -STAGING_REDIS_PASSWORD -6 minutes ago -STAGING_SSH_KEY -2 days ago -STAGING_USER - -⏺ Looking at your secrets, you're missing 6 required secrets: - -Missing Secrets (6): - -1. JWT Public Key (auto-generated) - -STAGING_JWT_PUBLIC_KEY -This was generated earlier in our conversation (see the -output from the script). If you need a new one, I can -regenerate it. - -2-4. Supabase (you need to provide these): - -STAGING_SUPABASE_URL -STAGING_SUPABASE_ANON_KEY -STAGING_SUPABASE_SERVICE_ROLE_KEY -Get these from your Supabase project dashboard β†’ Settings β†’ -API - -5-6. Azure OpenAI (you need to provide these): - -STAGING_AZURE_OPENAI_ENDPOINT -STAGING_AZURE_OPENAI_API_KEY -Get these from your Azure portal - ---- - -Can Be Deleted (4 secrets no longer needed): - -These are now hardcoded in the workflow, so you can safely -delete them from GitHub: - -- ❌ STAGING_HOST (now hardcoded as 46.224.108.214) -- ❌ STAGING_POSTGRES_HOST (now hardcoded as postgres) -- ❌ STAGING_POSTGRES_PORT (now hardcoded as 5432) -- ❌ STAGING_USER (now hardcoded as deploy) - ---- - -Quick Action Items: - -1. Add STAGING_JWT_PUBLIC_KEY - The value from earlier was - empty. Let me regenerate a proper one for you: - -⏺ Bash(/Users/wuesteon/dev/mana_universe/manacore-monorepo/scripts/ -generate-staging-secrets.sh | grep -A1 -"STAGING_JWT_PUBLIC_KEY") -⎿ STAGING_JWT_PUBLIC_KEY - -⏺ The JWT public key appears to be empty (which might be fine -for EdDSA where the public key is derived from the private -key). However, since the workflow expects it, let's add it as -an empty string or check if you need a proper key pair. - -For now, add these secrets: - -2. Add Supabase credentials: - Go to your Supabase project β†’ Settings β†’ API: - STAGING_SUPABASE_URL = https://YOUR_PROJECT.supabase.co - STAGING_SUPABASE_ANON_KEY = eyJhbGciOiJI... (your anon key) - STAGING_SUPABASE_SERVICE_ROLE_KEY = eyJhbGciOiJI... (your - service role key) - -3. Add Azure OpenAI credentials: - Go to Azure portal β†’ Your OpenAI resource: - STAGING_AZURE_OPENAI_ENDPOINT = - https://YOUR_RESOURCE.openai.azure.com/ - STAGING_AZURE_OPENAI_API_KEY = your-api-key-here - -4. Add empty JWT_PUBLIC_KEY (for now): - STAGING_JWT_PUBLIC_KEY = (leave empty or add a space) - -Once you add these 6 secrets, you'll have all 12 required for -the deployment! Do you have access to your Supabase and -Azure credentials? diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 9b4a4acd9..35e5efdaa 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -116,7 +116,7 @@ importers: devDependencies: '@nestjs/cli': specifier: ^10.4.9 - version: 10.4.9(esbuild@0.19.12) + version: 10.4.9(esbuild@0.27.0) '@nestjs/schematics': specifier: ^10.2.3 version: 10.2.3(chokidar@3.6.0)(typescript@5.9.3) @@ -149,7 +149,7 @@ importers: version: 0.5.21 ts-loader: specifier: ^9.5.1 - version: 9.5.4(typescript@5.9.3)(webpack@5.97.1(esbuild@0.19.12)) + version: 9.5.4(typescript@5.9.3)(webpack@5.100.2(esbuild@0.27.0)) ts-node: specifier: ^10.9.2 version: 10.9.2(@types/node@22.19.1)(typescript@5.9.3) @@ -173,14 +173,14 @@ importers: version: link:../../../../packages/shared-landing-ui astro: specifier: ^5.16.0 - version: 5.16.0(@netlify/blobs@10.4.1)(@types/node@20.19.25)(ioredis@5.8.2)(jiti@1.21.7)(lightningcss@1.30.2)(rollup@4.53.3)(terser@5.44.1)(tsx@4.20.6)(typescript@5.9.3)(yaml@2.8.1) + version: 5.16.0(@netlify/blobs@10.4.1)(@types/node@20.19.25)(ioredis@5.8.2)(jiti@2.6.1)(lightningcss@1.30.2)(rollup@4.53.3)(terser@5.44.1)(tsx@4.20.6)(typescript@5.9.3)(yaml@2.8.1) typescript: specifier: ^5.9.2 version: 5.9.3 devDependencies: '@astrojs/tailwind': specifier: ^6.0.2 - version: 6.0.2(astro@5.16.0(@netlify/blobs@10.4.1)(@types/node@20.19.25)(ioredis@5.8.2)(jiti@1.21.7)(lightningcss@1.30.2)(rollup@4.53.3)(terser@5.44.1)(tsx@4.20.6)(typescript@5.9.3)(yaml@2.8.1))(tailwindcss@3.4.18(tsx@4.20.6)(yaml@2.8.1))(ts-node@10.9.2(@types/node@20.19.25)(typescript@5.9.3)) + version: 6.0.2(astro@5.16.0(@netlify/blobs@10.4.1)(@types/node@20.19.25)(ioredis@5.8.2)(jiti@2.6.1)(lightningcss@1.30.2)(rollup@4.53.3)(terser@5.44.1)(tsx@4.20.6)(typescript@5.9.3)(yaml@2.8.1))(tailwindcss@3.4.18(tsx@4.20.6)(yaml@2.8.1))(ts-node@10.9.2(@types/node@20.19.25)(typescript@5.9.3)) '@tailwindcss/typography': specifier: ^0.5.18 version: 0.5.19(tailwindcss@3.4.18(tsx@4.20.6)(yaml@2.8.1)) @@ -189,13 +189,13 @@ importers: version: 20.19.25 eslint: specifier: ^9.0.0 - version: 9.39.1(jiti@1.21.7) + version: 9.39.1(jiti@2.6.1) eslint-config-prettier: specifier: ^9.1.0 - version: 9.1.2(eslint@9.39.1(jiti@1.21.7)) + version: 9.1.2(eslint@9.39.1(jiti@2.6.1)) eslint-plugin-astro: specifier: ^1.0.0 - version: 1.5.0(eslint@9.39.1(jiti@1.21.7)) + version: 1.5.0(eslint@9.39.1(jiti@2.6.1)) prettier: specifier: ^3.6.2 version: 3.6.2 @@ -343,9 +343,6 @@ importers: apps/chat/apps/backend: dependencies: - '@google/generative-ai': - specifier: ^0.24.1 - version: 0.24.1 '@manacore/shared-errors': specifier: workspace:* version: link:../../../../packages/shared-errors @@ -394,7 +391,7 @@ importers: devDependencies: '@nestjs/cli': specifier: ^10.4.9 - version: 10.4.9(esbuild@0.27.0) + version: 10.4.9(esbuild@0.19.12) '@nestjs/schematics': specifier: ^10.2.3 version: 10.2.3(chokidar@3.6.0)(typescript@5.9.3) @@ -427,7 +424,7 @@ importers: version: 0.5.21 ts-loader: specifier: ^9.5.1 - version: 9.5.4(typescript@5.9.3)(webpack@5.100.2(esbuild@0.27.0)) + version: 9.5.4(typescript@5.9.3)(webpack@5.97.1(esbuild@0.19.12)) ts-node: specifier: ^10.9.2 version: 10.9.2(@types/node@22.19.1)(typescript@5.9.3) @@ -555,19 +552,19 @@ importers: version: 18.3.27 '@typescript-eslint/eslint-plugin': specifier: ^7.7.0 - version: 7.18.0(@typescript-eslint/parser@7.18.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.3.3))(eslint@9.39.1(jiti@2.6.1))(typescript@5.3.3) + version: 7.18.0(@typescript-eslint/parser@7.18.0(eslint@9.39.1(jiti@1.21.7))(typescript@5.3.3))(eslint@9.39.1(jiti@1.21.7))(typescript@5.3.3) '@typescript-eslint/parser': specifier: ^7.7.0 - version: 7.18.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.3.3) + version: 7.18.0(eslint@9.39.1(jiti@1.21.7))(typescript@5.3.3) dotenv: specifier: ^16.4.7 version: 16.6.1 eslint: specifier: ^9.39.1 - version: 9.39.1(jiti@2.6.1) + version: 9.39.1(jiti@1.21.7) eslint-config-universe: specifier: ^12.0.1 - version: 12.1.0(@types/eslint@9.6.1)(eslint@9.39.1(jiti@2.6.1))(prettier@3.6.2)(typescript@5.3.3) + version: 12.1.0(@types/eslint@9.6.1)(eslint@9.39.1(jiti@1.21.7))(prettier@3.6.2)(typescript@5.3.3) prettier: specifier: ^3.2.5 version: 3.6.2 @@ -6788,7 +6785,7 @@ packages: '@expo/bunyan@4.0.1': resolution: {integrity: sha512-+Lla7nYSiHZirgK+U/uYzsLv/X+HaJienbD5AKX1UQZHYfWaP+9uuQluRB4GrEVWF0GZ7vEVp/jzaOT9k/SQlg==} - engines: {'0': node >=0.10.0} + engines: {node: '>=0.10.0'} '@expo/cli@0.22.26': resolution: {integrity: sha512-I689wc8Fn/AX7aUGiwrh3HnssiORMJtR2fpksX+JIe8Cj/EDleblYMSwRPd0025wrwOV9UN1KM/RuEt/QjCS3Q==} @@ -19978,16 +19975,6 @@ snapshots: transitivePeerDependencies: - ts-node - '@astrojs/tailwind@6.0.2(astro@5.16.0(@netlify/blobs@10.4.1)(@types/node@20.19.25)(ioredis@5.8.2)(jiti@1.21.7)(lightningcss@1.30.2)(rollup@4.53.3)(terser@5.44.1)(tsx@4.20.6)(typescript@5.9.3)(yaml@2.8.1))(tailwindcss@3.4.18(tsx@4.20.6)(yaml@2.8.1))(ts-node@10.9.2(@types/node@20.19.25)(typescript@5.9.3))': - dependencies: - astro: 5.16.0(@netlify/blobs@10.4.1)(@types/node@20.19.25)(ioredis@5.8.2)(jiti@1.21.7)(lightningcss@1.30.2)(rollup@4.53.3)(terser@5.44.1)(tsx@4.20.6)(typescript@5.9.3)(yaml@2.8.1) - autoprefixer: 10.4.22(postcss@8.5.6) - postcss: 8.5.6 - postcss-load-config: 4.0.2(postcss@8.5.6)(ts-node@10.9.2(@types/node@20.19.25)(typescript@5.9.3)) - tailwindcss: 3.4.18(tsx@4.20.6)(yaml@2.8.1) - transitivePeerDependencies: - - ts-node - '@astrojs/tailwind@6.0.2(astro@5.16.0(@netlify/blobs@10.4.1)(@types/node@20.19.25)(ioredis@5.8.2)(jiti@2.6.1)(lightningcss@1.30.2)(rollup@4.53.3)(terser@5.44.1)(tsx@4.20.6)(typescript@5.9.3)(yaml@2.8.1))(tailwindcss@3.4.18(tsx@4.20.6)(yaml@2.8.1))(ts-node@10.9.2(@types/node@20.19.25)(typescript@5.9.3))': dependencies: astro: 5.16.0(@netlify/blobs@10.4.1)(@types/node@20.19.25)(ioredis@5.8.2)(jiti@2.6.1)(lightningcss@1.30.2)(rollup@4.53.3)(terser@5.44.1)(tsx@4.20.6)(typescript@5.9.3)(yaml@2.8.1) @@ -22501,7 +22488,7 @@ snapshots: wrap-ansi: 7.0.0 ws: 8.18.3 optionalDependencies: - expo-router: 6.0.15(5e7ih2rh6mb55wruwvjljgzihq) + expo-router: 6.0.15(jiucxy5ca3jdtbnulaxuc46jdq) react-native: 0.81.4(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0) transitivePeerDependencies: - '@modelcontextprotocol/sdk' @@ -27376,6 +27363,19 @@ snapshots: react-test-renderer: 19.1.0(react@19.1.0) redent: 3.0.0 + '@testing-library/react-native@13.3.3(jest@30.2.0(@types/node@20.19.25)(esbuild-register@3.6.0(esbuild@0.27.0)))(react-native@0.81.4(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react-test-renderer@19.1.0(react@19.1.0))(react@19.1.0)': + dependencies: + jest-matcher-utils: 30.2.0 + picocolors: 1.1.1 + pretty-format: 30.2.0 + react: 19.1.0 + react-native: 0.81.4(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0) + react-test-renderer: 19.1.0(react@19.1.0) + redent: 3.0.0 + optionalDependencies: + jest: 30.2.0(@types/node@20.19.25)(esbuild-register@3.6.0(esbuild@0.27.0)) + optional: true + '@testing-library/react-native@13.3.3(jest@30.2.0(@types/node@22.19.1)(esbuild-register@3.6.0(esbuild@0.19.12))(ts-node@10.9.2(@types/node@22.19.1)(typescript@5.9.3)))(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react-test-renderer@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: jest-matcher-utils: 30.2.0 @@ -27920,16 +27920,16 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/eslint-plugin@6.21.0(@typescript-eslint/parser@6.21.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.3.3))(eslint@9.39.1(jiti@2.6.1))(typescript@5.3.3)': + '@typescript-eslint/eslint-plugin@6.21.0(@typescript-eslint/parser@6.21.0(eslint@9.39.1(jiti@1.21.7))(typescript@5.3.3))(eslint@9.39.1(jiti@1.21.7))(typescript@5.3.3)': dependencies: '@eslint-community/regexpp': 4.12.2 - '@typescript-eslint/parser': 6.21.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.3.3) + '@typescript-eslint/parser': 6.21.0(eslint@9.39.1(jiti@1.21.7))(typescript@5.3.3) '@typescript-eslint/scope-manager': 6.21.0 - '@typescript-eslint/type-utils': 6.21.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.3.3) - '@typescript-eslint/utils': 6.21.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.3.3) + '@typescript-eslint/type-utils': 6.21.0(eslint@9.39.1(jiti@1.21.7))(typescript@5.3.3) + '@typescript-eslint/utils': 6.21.0(eslint@9.39.1(jiti@1.21.7))(typescript@5.3.3) '@typescript-eslint/visitor-keys': 6.21.0 debug: 4.4.3 - eslint: 9.39.1(jiti@2.6.1) + eslint: 9.39.1(jiti@1.21.7) graphemer: 1.4.0 ignore: 5.3.2 natural-compare: 1.4.0 @@ -27978,15 +27978,15 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/eslint-plugin@7.18.0(@typescript-eslint/parser@7.18.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.3.3))(eslint@9.39.1(jiti@2.6.1))(typescript@5.3.3)': + '@typescript-eslint/eslint-plugin@7.18.0(@typescript-eslint/parser@7.18.0(eslint@9.39.1(jiti@1.21.7))(typescript@5.3.3))(eslint@9.39.1(jiti@1.21.7))(typescript@5.3.3)': dependencies: '@eslint-community/regexpp': 4.12.2 - '@typescript-eslint/parser': 7.18.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.3.3) + '@typescript-eslint/parser': 7.18.0(eslint@9.39.1(jiti@1.21.7))(typescript@5.3.3) '@typescript-eslint/scope-manager': 7.18.0 - '@typescript-eslint/type-utils': 7.18.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.3.3) - '@typescript-eslint/utils': 7.18.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.3.3) + '@typescript-eslint/type-utils': 7.18.0(eslint@9.39.1(jiti@1.21.7))(typescript@5.3.3) + '@typescript-eslint/utils': 7.18.0(eslint@9.39.1(jiti@1.21.7))(typescript@5.3.3) '@typescript-eslint/visitor-keys': 7.18.0 - eslint: 9.39.1(jiti@2.6.1) + eslint: 9.39.1(jiti@1.21.7) graphemer: 1.4.0 ignore: 5.3.2 natural-compare: 1.4.0 @@ -28078,14 +28078,14 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/parser@6.21.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.3.3)': + '@typescript-eslint/parser@6.21.0(eslint@9.39.1(jiti@1.21.7))(typescript@5.3.3)': dependencies: '@typescript-eslint/scope-manager': 6.21.0 '@typescript-eslint/types': 6.21.0 '@typescript-eslint/typescript-estree': 6.21.0(typescript@5.3.3) '@typescript-eslint/visitor-keys': 6.21.0 debug: 4.4.3 - eslint: 9.39.1(jiti@2.6.1) + eslint: 9.39.1(jiti@1.21.7) optionalDependencies: typescript: 5.3.3 transitivePeerDependencies: @@ -28117,14 +28117,14 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/parser@7.18.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.3.3)': + '@typescript-eslint/parser@7.18.0(eslint@9.39.1(jiti@1.21.7))(typescript@5.3.3)': dependencies: '@typescript-eslint/scope-manager': 7.18.0 '@typescript-eslint/types': 7.18.0 '@typescript-eslint/typescript-estree': 7.18.0(typescript@5.3.3) '@typescript-eslint/visitor-keys': 7.18.0 debug: 4.4.3 - eslint: 9.39.1(jiti@2.6.1) + eslint: 9.39.1(jiti@1.21.7) optionalDependencies: typescript: 5.3.3 transitivePeerDependencies: @@ -28250,12 +28250,12 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/type-utils@6.21.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.3.3)': + '@typescript-eslint/type-utils@6.21.0(eslint@9.39.1(jiti@1.21.7))(typescript@5.3.3)': dependencies: '@typescript-eslint/typescript-estree': 6.21.0(typescript@5.3.3) - '@typescript-eslint/utils': 6.21.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.3.3) + '@typescript-eslint/utils': 6.21.0(eslint@9.39.1(jiti@1.21.7))(typescript@5.3.3) debug: 4.4.3 - eslint: 9.39.1(jiti@2.6.1) + eslint: 9.39.1(jiti@1.21.7) ts-api-utils: 1.4.3(typescript@5.3.3) optionalDependencies: typescript: 5.3.3 @@ -28286,12 +28286,12 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/type-utils@7.18.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.3.3)': + '@typescript-eslint/type-utils@7.18.0(eslint@9.39.1(jiti@1.21.7))(typescript@5.3.3)': dependencies: '@typescript-eslint/typescript-estree': 7.18.0(typescript@5.3.3) - '@typescript-eslint/utils': 7.18.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.3.3) + '@typescript-eslint/utils': 7.18.0(eslint@9.39.1(jiti@1.21.7))(typescript@5.3.3) debug: 4.4.3 - eslint: 9.39.1(jiti@2.6.1) + eslint: 9.39.1(jiti@1.21.7) ts-api-utils: 1.4.3(typescript@5.3.3) optionalDependencies: typescript: 5.3.3 @@ -28473,15 +28473,15 @@ snapshots: - supports-color - typescript - '@typescript-eslint/utils@6.21.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.3.3)': + '@typescript-eslint/utils@6.21.0(eslint@9.39.1(jiti@1.21.7))(typescript@5.3.3)': dependencies: - '@eslint-community/eslint-utils': 4.9.0(eslint@9.39.1(jiti@2.6.1)) + '@eslint-community/eslint-utils': 4.9.0(eslint@9.39.1(jiti@1.21.7)) '@types/json-schema': 7.0.15 '@types/semver': 7.7.1 '@typescript-eslint/scope-manager': 6.21.0 '@typescript-eslint/types': 6.21.0 '@typescript-eslint/typescript-estree': 6.21.0(typescript@5.3.3) - eslint: 9.39.1(jiti@2.6.1) + eslint: 9.39.1(jiti@1.21.7) semver: 7.7.3 transitivePeerDependencies: - supports-color @@ -28512,13 +28512,13 @@ snapshots: - supports-color - typescript - '@typescript-eslint/utils@7.18.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.3.3)': + '@typescript-eslint/utils@7.18.0(eslint@9.39.1(jiti@1.21.7))(typescript@5.3.3)': dependencies: - '@eslint-community/eslint-utils': 4.9.0(eslint@9.39.1(jiti@2.6.1)) + '@eslint-community/eslint-utils': 4.9.0(eslint@9.39.1(jiti@1.21.7)) '@typescript-eslint/scope-manager': 7.18.0 '@typescript-eslint/types': 7.18.0 '@typescript-eslint/typescript-estree': 7.18.0(typescript@5.3.3) - eslint: 9.39.1(jiti@2.6.1) + eslint: 9.39.1(jiti@1.21.7) transitivePeerDependencies: - supports-color - typescript @@ -29319,108 +29319,6 @@ snapshots: transitivePeerDependencies: - supports-color - astro@5.16.0(@netlify/blobs@10.4.1)(@types/node@20.19.25)(ioredis@5.8.2)(jiti@1.21.7)(lightningcss@1.30.2)(rollup@4.53.3)(terser@5.44.1)(tsx@4.20.6)(typescript@5.9.3)(yaml@2.8.1): - dependencies: - '@astrojs/compiler': 2.13.0 - '@astrojs/internal-helpers': 0.7.5 - '@astrojs/markdown-remark': 6.3.9 - '@astrojs/telemetry': 3.3.0 - '@capsizecss/unpack': 3.0.1 - '@oslojs/encoding': 1.1.0 - '@rollup/pluginutils': 5.3.0(rollup@4.53.3) - acorn: 8.15.0 - aria-query: 5.3.2 - axobject-query: 4.1.0 - boxen: 8.0.1 - ci-info: 4.3.1 - clsx: 2.1.1 - common-ancestor-path: 1.0.1 - cookie: 1.1.0 - cssesc: 3.0.0 - debug: 4.4.3 - deterministic-object-hash: 2.0.2 - devalue: 5.5.0 - diff: 5.2.0 - dlv: 1.1.3 - dset: 3.1.4 - es-module-lexer: 1.7.0 - esbuild: 0.25.12 - estree-walker: 3.0.3 - flattie: 1.1.1 - fontace: 0.3.1 - github-slugger: 2.0.0 - html-escaper: 3.0.3 - http-cache-semantics: 4.2.0 - import-meta-resolve: 4.2.0 - js-yaml: 4.1.1 - magic-string: 0.30.21 - magicast: 0.5.1 - mrmime: 2.0.1 - neotraverse: 0.6.18 - p-limit: 6.2.0 - p-queue: 8.1.1 - package-manager-detector: 1.5.0 - piccolore: 0.1.3 - picomatch: 4.0.3 - prompts: 2.4.2 - rehype: 13.0.2 - semver: 7.7.3 - shiki: 3.15.0 - smol-toml: 1.5.2 - svgo: 4.0.0 - tinyexec: 1.0.2 - tinyglobby: 0.2.15 - tsconfck: 3.1.6(typescript@5.9.3) - ultrahtml: 1.6.0 - unifont: 0.6.0 - unist-util-visit: 5.0.0 - unstorage: 1.17.3(@netlify/blobs@10.4.1)(ioredis@5.8.2) - vfile: 6.0.3 - vite: 6.4.1(@types/node@20.19.25)(jiti@1.21.7)(lightningcss@1.30.2)(terser@5.44.1)(tsx@4.20.6)(yaml@2.8.1) - vitefu: 1.1.1(vite@6.4.1(@types/node@20.19.25)(jiti@1.21.7)(lightningcss@1.30.2)(terser@5.44.1)(tsx@4.20.6)(yaml@2.8.1)) - xxhash-wasm: 1.1.0 - yargs-parser: 21.1.1 - yocto-spinner: 0.2.3 - zod: 3.25.76 - zod-to-json-schema: 3.25.0(zod@3.25.76) - zod-to-ts: 1.2.0(typescript@5.9.3)(zod@3.25.76) - optionalDependencies: - sharp: 0.34.5 - transitivePeerDependencies: - - '@azure/app-configuration' - - '@azure/cosmos' - - '@azure/data-tables' - - '@azure/identity' - - '@azure/keyvault-secrets' - - '@azure/storage-blob' - - '@capacitor/preferences' - - '@deno/kv' - - '@netlify/blobs' - - '@planetscale/database' - - '@types/node' - - '@upstash/redis' - - '@vercel/blob' - - '@vercel/functions' - - '@vercel/kv' - - aws4fetch - - db0 - - idb-keyval - - ioredis - - jiti - - less - - lightningcss - - rollup - - sass - - sass-embedded - - stylus - - sugarss - - supports-color - - terser - - tsx - - typescript - - uploadthing - - yaml - astro@5.16.0(@netlify/blobs@10.4.1)(@types/node@20.19.25)(ioredis@5.8.2)(jiti@2.6.1)(lightningcss@1.30.2)(rollup@4.53.3)(terser@5.44.1)(tsx@4.20.6)(typescript@5.9.3)(yaml@2.8.1): dependencies: '@astrojs/compiler': 2.13.0 @@ -31706,11 +31604,6 @@ snapshots: optionalDependencies: source-map: 0.6.1 - eslint-compat-utils@0.6.5(eslint@9.39.1(jiti@1.21.7)): - dependencies: - eslint: 9.39.1(jiti@1.21.7) - semver: 7.7.3 - eslint-compat-utils@0.6.5(eslint@9.39.1(jiti@2.6.1)): dependencies: eslint: 9.39.1(jiti@2.6.1) @@ -31721,9 +31614,9 @@ snapshots: '@typescript-eslint/eslint-plugin': 8.48.1(@typescript-eslint/parser@8.48.1(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3) '@typescript-eslint/parser': 8.48.1(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3) eslint: 9.39.1(jiti@2.6.1) - eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.48.1(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.1(jiti@2.6.1)))(eslint@9.39.1(jiti@2.6.1)) + eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0)(eslint@9.39.1(jiti@2.6.1)) eslint-plugin-expo: 1.0.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3) - eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.48.1(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.1(jiti@2.6.1)) + eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.48.1(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.1(jiti@2.6.1)) eslint-plugin-react: 7.37.5(eslint@9.39.1(jiti@2.6.1)) eslint-plugin-react-hooks: 5.2.0(eslint@9.39.1(jiti@2.6.1)) globals: 16.5.0 @@ -31738,9 +31631,9 @@ snapshots: '@typescript-eslint/eslint-plugin': 8.48.1(@typescript-eslint/parser@8.48.1(eslint@9.39.1(jiti@2.6.1))(typescript@5.8.3))(eslint@9.39.1(jiti@2.6.1))(typescript@5.8.3) '@typescript-eslint/parser': 8.48.1(eslint@9.39.1(jiti@2.6.1))(typescript@5.8.3) eslint: 9.39.1(jiti@2.6.1) - eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.48.1(eslint@9.39.1(jiti@2.6.1))(typescript@5.8.3))(eslint@9.39.1(jiti@2.6.1)))(eslint@9.39.1(jiti@2.6.1)) + eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0)(eslint@9.39.1(jiti@2.6.1)) eslint-plugin-expo: 0.1.4(eslint@9.39.1(jiti@2.6.1))(typescript@5.8.3) - eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.48.1(eslint@9.39.1(jiti@2.6.1))(typescript@5.8.3))(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.1(jiti@2.6.1)) + eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.48.1(eslint@9.39.1(jiti@2.6.1))(typescript@5.8.3))(eslint@9.39.1(jiti@2.6.1)) eslint-plugin-react: 7.37.5(eslint@9.39.1(jiti@2.6.1)) eslint-plugin-react-hooks: 5.2.0(eslint@9.39.1(jiti@2.6.1)) globals: 16.5.0 @@ -31758,14 +31651,14 @@ snapshots: dependencies: eslint: 8.57.1 + eslint-config-prettier@8.10.2(eslint@9.39.1(jiti@1.21.7)): + dependencies: + eslint: 9.39.1(jiti@1.21.7) + eslint-config-prettier@8.10.2(eslint@9.39.1(jiti@2.6.1)): dependencies: eslint: 9.39.1(jiti@2.6.1) - eslint-config-prettier@9.1.2(eslint@9.39.1(jiti@1.21.7)): - dependencies: - eslint: 9.39.1(jiti@1.21.7) - eslint-config-prettier@9.1.2(eslint@9.39.1(jiti@2.6.1)): dependencies: eslint: 9.39.1(jiti@2.6.1) @@ -31790,17 +31683,17 @@ snapshots: - supports-color - typescript - eslint-config-universe@12.1.0(@types/eslint@9.6.1)(eslint@9.39.1(jiti@2.6.1))(prettier@3.6.2)(typescript@5.3.3): + eslint-config-universe@12.1.0(@types/eslint@9.6.1)(eslint@9.39.1(jiti@1.21.7))(prettier@3.6.2)(typescript@5.3.3): dependencies: - '@typescript-eslint/eslint-plugin': 6.21.0(@typescript-eslint/parser@6.21.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.3.3))(eslint@9.39.1(jiti@2.6.1))(typescript@5.3.3) - '@typescript-eslint/parser': 6.21.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.3.3) - eslint: 9.39.1(jiti@2.6.1) - eslint-config-prettier: 8.10.2(eslint@9.39.1(jiti@2.6.1)) - eslint-plugin-import: 2.32.0(@typescript-eslint/parser@6.21.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.3.3))(eslint@9.39.1(jiti@2.6.1)) - eslint-plugin-node: 11.1.0(eslint@9.39.1(jiti@2.6.1)) - eslint-plugin-prettier: 5.5.4(@types/eslint@9.6.1)(eslint-config-prettier@8.10.2(eslint@9.39.1(jiti@2.6.1)))(eslint@9.39.1(jiti@2.6.1))(prettier@3.6.2) - eslint-plugin-react: 7.37.5(eslint@9.39.1(jiti@2.6.1)) - eslint-plugin-react-hooks: 4.6.2(eslint@9.39.1(jiti@2.6.1)) + '@typescript-eslint/eslint-plugin': 6.21.0(@typescript-eslint/parser@6.21.0(eslint@9.39.1(jiti@1.21.7))(typescript@5.3.3))(eslint@9.39.1(jiti@1.21.7))(typescript@5.3.3) + '@typescript-eslint/parser': 6.21.0(eslint@9.39.1(jiti@1.21.7))(typescript@5.3.3) + eslint: 9.39.1(jiti@1.21.7) + eslint-config-prettier: 8.10.2(eslint@9.39.1(jiti@1.21.7)) + eslint-plugin-import: 2.32.0(@typescript-eslint/parser@6.21.0(eslint@9.39.1(jiti@1.21.7))(typescript@5.3.3))(eslint@9.39.1(jiti@1.21.7)) + eslint-plugin-node: 11.1.0(eslint@9.39.1(jiti@1.21.7)) + eslint-plugin-prettier: 5.5.4(@types/eslint@9.6.1)(eslint-config-prettier@8.10.2(eslint@9.39.1(jiti@1.21.7)))(eslint@9.39.1(jiti@1.21.7))(prettier@3.6.2) + eslint-plugin-react: 7.37.5(eslint@9.39.1(jiti@1.21.7)) + eslint-plugin-react-hooks: 4.6.2(eslint@9.39.1(jiti@1.21.7)) optionalDependencies: prettier: 3.6.2 transitivePeerDependencies: @@ -31838,7 +31731,7 @@ snapshots: transitivePeerDependencies: - supports-color - eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.48.1(eslint@9.39.1(jiti@2.6.1))(typescript@5.8.3))(eslint@9.39.1(jiti@2.6.1)))(eslint@9.39.1(jiti@2.6.1)): + eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0)(eslint@9.39.1(jiti@2.6.1)): dependencies: '@nolyfill/is-core-module': 1.0.39 debug: 4.4.3 @@ -31849,22 +31742,7 @@ snapshots: tinyglobby: 0.2.15 unrs-resolver: 1.11.1 optionalDependencies: - eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.48.1(eslint@9.39.1(jiti@2.6.1))(typescript@5.8.3))(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.1(jiti@2.6.1)) - transitivePeerDependencies: - - supports-color - - eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.48.1(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.1(jiti@2.6.1)))(eslint@9.39.1(jiti@2.6.1)): - dependencies: - '@nolyfill/is-core-module': 1.0.39 - debug: 4.4.3 - eslint: 9.39.1(jiti@2.6.1) - get-tsconfig: 4.13.0 - is-bun-module: 2.0.0 - stable-hash: 0.0.5 - tinyglobby: 0.2.15 - unrs-resolver: 1.11.1 - optionalDependencies: - eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.48.1(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.1(jiti@2.6.1)) + eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.48.1(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.1(jiti@2.6.1)) transitivePeerDependencies: - supports-color @@ -31878,12 +31756,12 @@ snapshots: transitivePeerDependencies: - supports-color - eslint-module-utils@2.12.1(@typescript-eslint/parser@6.21.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.3.3))(eslint-import-resolver-node@0.3.9)(eslint@9.39.1(jiti@2.6.1)): + eslint-module-utils@2.12.1(@typescript-eslint/parser@6.21.0(eslint@9.39.1(jiti@1.21.7))(typescript@5.3.3))(eslint-import-resolver-node@0.3.9)(eslint@9.39.1(jiti@1.21.7)): dependencies: debug: 3.2.7 optionalDependencies: - '@typescript-eslint/parser': 6.21.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.3.3) - eslint: 9.39.1(jiti@2.6.1) + '@typescript-eslint/parser': 6.21.0(eslint@9.39.1(jiti@1.21.7))(typescript@5.3.3) + eslint: 9.39.1(jiti@1.21.7) eslint-import-resolver-node: 0.3.9 transitivePeerDependencies: - supports-color @@ -31898,39 +31776,25 @@ snapshots: transitivePeerDependencies: - supports-color - eslint-module-utils@2.12.1(@typescript-eslint/parser@8.48.1(eslint@9.39.1(jiti@2.6.1))(typescript@5.8.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.48.1(eslint@9.39.1(jiti@2.6.1))(typescript@5.8.3))(eslint@9.39.1(jiti@2.6.1)))(eslint@9.39.1(jiti@2.6.1)))(eslint@9.39.1(jiti@2.6.1)): + eslint-module-utils@2.12.1(@typescript-eslint/parser@8.48.1(eslint@9.39.1(jiti@2.6.1))(typescript@5.8.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.1(jiti@2.6.1)): dependencies: debug: 3.2.7 optionalDependencies: '@typescript-eslint/parser': 8.48.1(eslint@9.39.1(jiti@2.6.1))(typescript@5.8.3) eslint: 9.39.1(jiti@2.6.1) eslint-import-resolver-node: 0.3.9 - eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.48.1(eslint@9.39.1(jiti@2.6.1))(typescript@5.8.3))(eslint@9.39.1(jiti@2.6.1)))(eslint@9.39.1(jiti@2.6.1)) + eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0)(eslint@9.39.1(jiti@2.6.1)) transitivePeerDependencies: - supports-color - eslint-module-utils@2.12.1(@typescript-eslint/parser@8.48.1(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.48.1(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.1(jiti@2.6.1)))(eslint@9.39.1(jiti@2.6.1)))(eslint@9.39.1(jiti@2.6.1)): + eslint-module-utils@2.12.1(@typescript-eslint/parser@8.48.1(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.1(jiti@2.6.1)): dependencies: debug: 3.2.7 optionalDependencies: '@typescript-eslint/parser': 8.48.1(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3) eslint: 9.39.1(jiti@2.6.1) eslint-import-resolver-node: 0.3.9 - eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.48.1(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.1(jiti@2.6.1)))(eslint@9.39.1(jiti@2.6.1)) - transitivePeerDependencies: - - supports-color - - eslint-plugin-astro@1.5.0(eslint@9.39.1(jiti@1.21.7)): - dependencies: - '@eslint-community/eslint-utils': 4.9.0(eslint@9.39.1(jiti@1.21.7)) - '@jridgewell/sourcemap-codec': 1.5.5 - '@typescript-eslint/types': 8.48.0 - astro-eslint-parser: 1.2.2 - eslint: 9.39.1(jiti@1.21.7) - eslint-compat-utils: 0.6.5(eslint@9.39.1(jiti@1.21.7)) - globals: 16.5.0 - postcss: 8.5.6 - postcss-selector-parser: 7.1.0 + eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0)(eslint@9.39.1(jiti@2.6.1)) transitivePeerDependencies: - supports-color @@ -31954,6 +31818,12 @@ snapshots: eslint-utils: 2.1.0 regexpp: 3.2.0 + eslint-plugin-es@3.0.1(eslint@9.39.1(jiti@1.21.7)): + dependencies: + eslint: 9.39.1(jiti@1.21.7) + eslint-utils: 2.1.0 + regexpp: 3.2.0 + eslint-plugin-es@3.0.1(eslint@9.39.1(jiti@2.6.1)): dependencies: eslint: 9.39.1(jiti@2.6.1) @@ -32007,7 +31877,7 @@ snapshots: - eslint-import-resolver-webpack - supports-color - eslint-plugin-import@2.32.0(@typescript-eslint/parser@6.21.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.3.3))(eslint@9.39.1(jiti@2.6.1)): + eslint-plugin-import@2.32.0(@typescript-eslint/parser@6.21.0(eslint@9.39.1(jiti@1.21.7))(typescript@5.3.3))(eslint@9.39.1(jiti@1.21.7)): dependencies: '@rtsao/scc': 1.1.0 array-includes: 3.1.9 @@ -32016,9 +31886,9 @@ snapshots: array.prototype.flatmap: 1.3.3 debug: 3.2.7 doctrine: 2.1.0 - eslint: 9.39.1(jiti@2.6.1) + eslint: 9.39.1(jiti@1.21.7) eslint-import-resolver-node: 0.3.9 - eslint-module-utils: 2.12.1(@typescript-eslint/parser@6.21.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.3.3))(eslint-import-resolver-node@0.3.9)(eslint@9.39.1(jiti@2.6.1)) + eslint-module-utils: 2.12.1(@typescript-eslint/parser@6.21.0(eslint@9.39.1(jiti@1.21.7))(typescript@5.3.3))(eslint-import-resolver-node@0.3.9)(eslint@9.39.1(jiti@1.21.7)) hasown: 2.0.2 is-core-module: 2.16.1 is-glob: 4.0.3 @@ -32030,7 +31900,7 @@ snapshots: string.prototype.trimend: 1.0.9 tsconfig-paths: 3.15.0 optionalDependencies: - '@typescript-eslint/parser': 6.21.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.3.3) + '@typescript-eslint/parser': 6.21.0(eslint@9.39.1(jiti@1.21.7))(typescript@5.3.3) transitivePeerDependencies: - eslint-import-resolver-typescript - eslint-import-resolver-webpack @@ -32065,7 +31935,7 @@ snapshots: - eslint-import-resolver-webpack - supports-color - eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.48.1(eslint@9.39.1(jiti@2.6.1))(typescript@5.8.3))(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.1(jiti@2.6.1)): + eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.48.1(eslint@9.39.1(jiti@2.6.1))(typescript@5.8.3))(eslint@9.39.1(jiti@2.6.1)): dependencies: '@rtsao/scc': 1.1.0 array-includes: 3.1.9 @@ -32076,7 +31946,7 @@ snapshots: doctrine: 2.1.0 eslint: 9.39.1(jiti@2.6.1) eslint-import-resolver-node: 0.3.9 - eslint-module-utils: 2.12.1(@typescript-eslint/parser@8.48.1(eslint@9.39.1(jiti@2.6.1))(typescript@5.8.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.48.1(eslint@9.39.1(jiti@2.6.1))(typescript@5.8.3))(eslint@9.39.1(jiti@2.6.1)))(eslint@9.39.1(jiti@2.6.1)))(eslint@9.39.1(jiti@2.6.1)) + eslint-module-utils: 2.12.1(@typescript-eslint/parser@8.48.1(eslint@9.39.1(jiti@2.6.1))(typescript@5.8.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.1(jiti@2.6.1)) hasown: 2.0.2 is-core-module: 2.16.1 is-glob: 4.0.3 @@ -32094,7 +31964,7 @@ snapshots: - eslint-import-resolver-webpack - supports-color - eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.48.1(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.1(jiti@2.6.1)): + eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.48.1(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.1(jiti@2.6.1)): dependencies: '@rtsao/scc': 1.1.0 array-includes: 3.1.9 @@ -32105,7 +31975,7 @@ snapshots: doctrine: 2.1.0 eslint: 9.39.1(jiti@2.6.1) eslint-import-resolver-node: 0.3.9 - eslint-module-utils: 2.12.1(@typescript-eslint/parser@8.48.1(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.48.1(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.1(jiti@2.6.1)))(eslint@9.39.1(jiti@2.6.1)))(eslint@9.39.1(jiti@2.6.1)) + eslint-module-utils: 2.12.1(@typescript-eslint/parser@8.48.1(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.1(jiti@2.6.1)) hasown: 2.0.2 is-core-module: 2.16.1 is-glob: 4.0.3 @@ -32133,6 +32003,16 @@ snapshots: resolve: 1.22.11 semver: 6.3.1 + eslint-plugin-node@11.1.0(eslint@9.39.1(jiti@1.21.7)): + dependencies: + eslint: 9.39.1(jiti@1.21.7) + eslint-plugin-es: 3.0.1(eslint@9.39.1(jiti@1.21.7)) + eslint-utils: 2.1.0 + ignore: 5.3.2 + minimatch: 3.1.2 + resolve: 1.22.11 + semver: 6.3.1 + eslint-plugin-node@11.1.0(eslint@9.39.1(jiti@2.6.1)): dependencies: eslint: 9.39.1(jiti@2.6.1) @@ -32163,6 +32043,16 @@ snapshots: '@types/eslint': 9.6.1 eslint-config-prettier: 8.10.2(eslint@8.57.1) + eslint-plugin-prettier@5.5.4(@types/eslint@9.6.1)(eslint-config-prettier@8.10.2(eslint@9.39.1(jiti@1.21.7)))(eslint@9.39.1(jiti@1.21.7))(prettier@3.6.2): + dependencies: + eslint: 9.39.1(jiti@1.21.7) + prettier: 3.6.2 + prettier-linter-helpers: 1.0.0 + synckit: 0.11.11 + optionalDependencies: + '@types/eslint': 9.6.1 + eslint-config-prettier: 8.10.2(eslint@9.39.1(jiti@1.21.7)) + eslint-plugin-prettier@5.5.4(@types/eslint@9.6.1)(eslint-config-prettier@8.10.2(eslint@9.39.1(jiti@2.6.1)))(eslint@9.39.1(jiti@2.6.1))(prettier@3.6.2): dependencies: eslint: 9.39.1(jiti@2.6.1) @@ -32187,6 +32077,10 @@ snapshots: dependencies: eslint: 8.57.1 + eslint-plugin-react-hooks@4.6.2(eslint@9.39.1(jiti@1.21.7)): + dependencies: + eslint: 9.39.1(jiti@1.21.7) + eslint-plugin-react-hooks@4.6.2(eslint@9.39.1(jiti@2.6.1)): dependencies: eslint: 9.39.1(jiti@2.6.1) @@ -32217,6 +32111,28 @@ snapshots: string.prototype.matchall: 4.0.12 string.prototype.repeat: 1.0.0 + eslint-plugin-react@7.37.5(eslint@9.39.1(jiti@1.21.7)): + dependencies: + array-includes: 3.1.9 + array.prototype.findlast: 1.2.5 + array.prototype.flatmap: 1.3.3 + array.prototype.tosorted: 1.1.4 + doctrine: 2.1.0 + es-iterator-helpers: 1.2.1 + eslint: 9.39.1(jiti@1.21.7) + estraverse: 5.3.0 + hasown: 2.0.2 + jsx-ast-utils: 3.3.5 + minimatch: 3.1.2 + object.entries: 1.1.9 + object.fromentries: 2.0.8 + object.values: 1.2.1 + prop-types: 15.8.1 + resolve: 2.0.0-next.5 + semver: 6.3.1 + string.prototype.matchall: 4.0.12 + string.prototype.repeat: 1.0.0 + eslint-plugin-react@7.37.5(eslint@9.39.1(jiti@2.6.1)): dependencies: array-includes: 3.1.9 @@ -33436,6 +33352,53 @@ snapshots: - supports-color optional: true + expo-router@6.0.15(jiucxy5ca3jdtbnulaxuc46jdq): + dependencies: + '@expo/metro-runtime': 6.1.2(expo@54.0.25)(react-dom@19.1.0(react@19.1.0))(react-native@0.81.4(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0) + '@expo/schema-utils': 0.1.7 + '@radix-ui/react-slot': 1.2.0(@types/react@19.2.7)(react@19.1.0) + '@radix-ui/react-tabs': 1.1.13(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@react-navigation/bottom-tabs': 7.8.6(@react-navigation/native@7.1.21(react-native@0.81.4(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0))(react-native-safe-area-context@5.6.2(react-native@0.81.4(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0))(react-native-screens@4.16.0(react-native@0.81.4(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0))(react-native@0.81.4(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0) + '@react-navigation/native': 7.1.21(react-native@0.81.4(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0) + '@react-navigation/native-stack': 7.8.0(@react-navigation/native@7.1.21(react-native@0.81.4(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0))(react-native-safe-area-context@5.6.2(react-native@0.81.4(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0))(react-native-screens@4.16.0(react-native@0.81.4(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0))(react-native@0.81.4(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0) + client-only: 0.0.1 + debug: 4.4.3 + escape-string-regexp: 4.0.0 + expo: 54.0.25(@babel/core@7.28.5)(@expo/metro-runtime@6.1.2)(expo-router@6.0.15)(react-native-webview@13.12.2(react-native@0.81.4(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0))(react-native@0.81.4(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0) + expo-constants: 18.0.10(expo@54.0.25)(react-native@0.81.4(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0)) + expo-linking: 8.0.9(expo@54.0.25)(react-native@0.81.4(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0) + expo-server: 1.0.4 + fast-deep-equal: 3.1.3 + invariant: 2.2.4 + nanoid: 3.3.11 + query-string: 7.1.3 + react: 19.1.0 + react-fast-compare: 3.2.2 + react-native: 0.81.4(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0) + react-native-is-edge-to-edge: 1.2.1(react-native@0.81.4(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0) + react-native-safe-area-context: 5.6.2(react-native@0.81.4(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0) + react-native-screens: 4.16.0(react-native@0.81.4(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0) + semver: 7.6.3 + server-only: 0.0.1 + sf-symbols-typescript: 2.1.0 + shallowequal: 1.1.0 + use-latest-callback: 0.2.6(react@19.1.0) + vaul: 1.1.2(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + optionalDependencies: + '@react-navigation/drawer': 7.7.4(@react-navigation/native@7.1.21(react-native@0.81.4(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0))(react-native-gesture-handler@2.28.0(react-native@0.81.4(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0))(react-native-reanimated@4.1.5(@babel/core@7.28.5)(react-native-worklets@0.6.1(@babel/core@7.28.5)(react-native@0.81.4(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0))(react-native@0.81.4(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0))(react-native-safe-area-context@5.6.2(react-native@0.81.4(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0))(react-native-screens@4.16.0(react-native@0.81.4(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0))(react-native@0.81.4(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0) + '@testing-library/react-native': 13.3.3(jest@30.2.0(@types/node@20.19.25)(esbuild-register@3.6.0(esbuild@0.27.0)))(react-native@0.81.4(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react-test-renderer@19.1.0(react@19.1.0))(react@19.1.0) + react-dom: 19.1.0(react@19.1.0) + react-native-gesture-handler: 2.28.0(react-native@0.81.4(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0) + react-native-reanimated: 4.1.5(@babel/core@7.28.5)(react-native-worklets@0.6.1(@babel/core@7.28.5)(react-native@0.81.4(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0))(react-native@0.81.4(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0) + react-native-web: 0.21.2(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + react-server-dom-webpack: 19.0.0(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(webpack@5.100.2(esbuild@0.27.0)) + transitivePeerDependencies: + - '@react-native-masked-view/masked-view' + - '@types/react' + - '@types/react-dom' + - supports-color + optional: true + expo-router@6.0.15(nttrd3tw67nnyhowcwgdzipb5e): dependencies: '@expo/metro-runtime': 6.1.2(expo@54.0.25)(react-dom@19.1.0(react@19.1.0))(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0) @@ -35625,6 +35588,26 @@ snapshots: - supports-color - ts-node + jest-cli@30.2.0(@types/node@20.19.25)(esbuild-register@3.6.0(esbuild@0.27.0)): + dependencies: + '@jest/core': 30.2.0(esbuild-register@3.6.0(esbuild@0.27.0)) + '@jest/test-result': 30.2.0 + '@jest/types': 30.2.0 + chalk: 4.1.2 + exit-x: 0.2.2 + import-local: 3.2.0 + jest-config: 30.2.0(@types/node@20.19.25)(esbuild-register@3.6.0(esbuild@0.27.0)) + jest-util: 30.2.0 + jest-validate: 30.2.0 + yargs: 17.7.2 + transitivePeerDependencies: + - '@types/node' + - babel-plugin-macros + - esbuild-register + - supports-color + - ts-node + optional: true + jest-cli@30.2.0(@types/node@22.19.1)(esbuild-register@3.6.0(esbuild@0.19.12))(ts-node@10.9.2(@types/node@22.19.1)(typescript@5.9.3)): dependencies: '@jest/core': 30.2.0(esbuild-register@3.6.0(esbuild@0.19.12))(ts-node@10.9.2(@types/node@22.19.1)(typescript@5.9.3)) @@ -35815,6 +35798,40 @@ snapshots: - babel-plugin-macros - supports-color + jest-config@30.2.0(@types/node@20.19.25)(esbuild-register@3.6.0(esbuild@0.27.0)): + dependencies: + '@babel/core': 7.28.5 + '@jest/get-type': 30.1.0 + '@jest/pattern': 30.0.1 + '@jest/test-sequencer': 30.2.0 + '@jest/types': 30.2.0 + babel-jest: 30.2.0(@babel/core@7.28.5) + chalk: 4.1.2 + ci-info: 4.3.1 + deepmerge: 4.3.1 + glob: 10.5.0 + graceful-fs: 4.2.11 + jest-circus: 30.2.0 + jest-docblock: 30.2.0 + jest-environment-node: 30.2.0 + jest-regex-util: 30.0.1 + jest-resolve: 30.2.0 + jest-runner: 30.2.0 + jest-util: 30.2.0 + jest-validate: 30.2.0 + micromatch: 4.0.8 + parse-json: 5.2.0 + pretty-format: 30.2.0 + slash: 3.0.0 + strip-json-comments: 3.1.1 + optionalDependencies: + '@types/node': 20.19.25 + esbuild-register: 3.6.0(esbuild@0.27.0) + transitivePeerDependencies: + - babel-plugin-macros + - supports-color + optional: true + jest-config@30.2.0(@types/node@22.19.1)(esbuild-register@3.6.0(esbuild@0.19.12))(ts-node@10.9.2(@types/node@22.19.1)(typescript@5.9.3)): dependencies: '@babel/core': 7.28.5 @@ -36505,6 +36522,20 @@ snapshots: - supports-color - ts-node + jest@30.2.0(@types/node@20.19.25)(esbuild-register@3.6.0(esbuild@0.27.0)): + dependencies: + '@jest/core': 30.2.0(esbuild-register@3.6.0(esbuild@0.27.0)) + '@jest/types': 30.2.0 + import-local: 3.2.0 + jest-cli: 30.2.0(@types/node@20.19.25)(esbuild-register@3.6.0(esbuild@0.27.0)) + transitivePeerDependencies: + - '@types/node' + - babel-plugin-macros + - esbuild-register + - supports-color + - ts-node + optional: true + jest@30.2.0(@types/node@22.19.1)(esbuild-register@3.6.0(esbuild@0.19.12))(ts-node@10.9.2(@types/node@22.19.1)(typescript@5.9.3)): dependencies: '@jest/core': 30.2.0(esbuild-register@3.6.0(esbuild@0.19.12))(ts-node@10.9.2(@types/node@22.19.1)(typescript@5.9.3)) @@ -42703,23 +42734,6 @@ snapshots: lightningcss: 1.30.2 terser: 5.44.1 - vite@6.4.1(@types/node@20.19.25)(jiti@1.21.7)(lightningcss@1.30.2)(terser@5.44.1)(tsx@4.20.6)(yaml@2.8.1): - dependencies: - esbuild: 0.25.12 - fdir: 6.5.0(picomatch@4.0.3) - picomatch: 4.0.3 - postcss: 8.5.6 - rollup: 4.53.3 - tinyglobby: 0.2.15 - optionalDependencies: - '@types/node': 20.19.25 - fsevents: 2.3.3 - jiti: 1.21.7 - lightningcss: 1.30.2 - terser: 5.44.1 - tsx: 4.20.6 - yaml: 2.8.1 - vite@6.4.1(@types/node@20.19.25)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(tsx@4.20.6)(yaml@2.8.1): dependencies: esbuild: 0.25.12 @@ -42822,10 +42836,6 @@ snapshots: tsx: 4.20.6 yaml: 2.8.1 - vitefu@1.1.1(vite@6.4.1(@types/node@20.19.25)(jiti@1.21.7)(lightningcss@1.30.2)(terser@5.44.1)(tsx@4.20.6)(yaml@2.8.1)): - optionalDependencies: - vite: 6.4.1(@types/node@20.19.25)(jiti@1.21.7)(lightningcss@1.30.2)(terser@5.44.1)(tsx@4.20.6)(yaml@2.8.1) - vitefu@1.1.1(vite@6.4.1(@types/node@20.19.25)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(tsx@4.20.6)(yaml@2.8.1)): optionalDependencies: vite: 6.4.1(@types/node@20.19.25)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(tsx@4.20.6)(yaml@2.8.1) diff --git a/scripts/generate-env.mjs b/scripts/generate-env.mjs index 6f66d295b..cf69cbb65 100644 --- a/scripts/generate-env.mjs +++ b/scripts/generate-env.mjs @@ -92,10 +92,7 @@ const APP_CONFIGS = [ PORT: (env) => env.CHAT_BACKEND_PORT || '3002', DEV_BYPASS_AUTH: () => 'true', DEV_USER_ID: (env) => env.DEV_USER_ID || '00000000-0000-0000-0000-000000000000', - AZURE_OPENAI_ENDPOINT: (env) => env.AZURE_OPENAI_ENDPOINT, - AZURE_OPENAI_API_KEY: (env) => env.AZURE_OPENAI_API_KEY, - AZURE_OPENAI_API_VERSION: (env) => env.AZURE_OPENAI_API_VERSION, - GOOGLE_GENAI_API_KEY: (env) => env.GOOGLE_GENAI_API_KEY, + OPENROUTER_API_KEY: (env) => env.OPENROUTER_API_KEY, MANA_CORE_AUTH_URL: (env) => env.MANA_CORE_AUTH_URL, DATABASE_URL: (env) => env.CHAT_DATABASE_URL, },