diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index d48ddd60..f2f2b8cd 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -14,16 +14,17 @@ jobs:
- name: Checkout repository
uses: actions/checkout@v4
- - name: Setup Bun
- uses: oven-sh/setup-bun@v1
+ - name: Setup Node
+ uses: actions/setup-node@v4
with:
- bun-version: latest
+ node-version: '22.x'
+ cache: 'npm'
- name: Install dependencies
- run: bun install --frozen-lockfile
+ run: npm ci
- name: Run build
- run: bun run build
+ run: npm run build
# - name: Run linter
# run: bun run lint
diff --git a/.gitignore b/.gitignore
index b1ea1715..a0fb83be 100644
--- a/.gitignore
+++ b/.gitignore
@@ -41,10 +41,12 @@ temp/
templates
debugging.ipynb
-#bun.lock
+bun.lock
#*.lock
debug-tools/*.json
# Sentry Config File
.env.sentry-build-plugin
-data-dump
\ No newline at end of file
+data-dump
+# Local history (do not commit)
+.history/
\ No newline at end of file
diff --git a/body-60909.json b/body-60909.json
new file mode 100644
index 00000000..f72cabea
--- /dev/null
+++ b/body-60909.json
@@ -0,0 +1 @@
+{"success":true,"data":{"token":"977680572f4a19a8754f198073026f3703b2a6d68807aa00925343c00f3205af","headerName":"X-CSRF-Token","expiresIn":7200}}
\ No newline at end of file
diff --git a/body-8787.json b/body-8787.json
new file mode 100644
index 00000000..2fd6f193
--- /dev/null
+++ b/body-8787.json
@@ -0,0 +1 @@
+{"success":true,"data":{"token":"f692bb6007704315c85d5cd8a98c03305e7250c5feb40e4f7b685e6fb7deab66","headerName":"X-CSRF-Token","expiresIn":7200}}
\ No newline at end of file
diff --git a/body-health-8787.json b/body-health-8787.json
new file mode 100644
index 00000000..1a36cf5f
--- /dev/null
+++ b/body-health-8787.json
@@ -0,0 +1 @@
+{"status":"ok"}
\ No newline at end of file
diff --git a/body-register-8787.json b/body-register-8787.json
new file mode 100644
index 00000000..0a18c0c4
--- /dev/null
+++ b/body-register-8787.json
@@ -0,0 +1 @@
+{"success":false,"error":{"message":"Registration failed","name":"Error"},"message":"An error occurred"}
\ No newline at end of file
diff --git a/cookies-8787.txt b/cookies-8787.txt
new file mode 100644
index 00000000..950b13e0
--- /dev/null
+++ b/cookies-8787.txt
@@ -0,0 +1,5 @@
+# Netscape HTTP Cookie File
+# https://curl.se/docs/http-cookies.html
+# This file was generated by libcurl! Edit at your own risk.
+
+#HttpOnly_localhost FALSE / TRUE 1760329085 csrf-token %7B%22token%22%3A%22f692bb6007704315c85d5cd8a98c03305e7250c5feb40e4f7b685e6fb7deab66%22%2C%22timestamp%22%3A1760321885725%7D
diff --git a/cookies-prod.txt b/cookies-prod.txt
new file mode 100644
index 00000000..3c26a256
--- /dev/null
+++ b/cookies-prod.txt
@@ -0,0 +1,5 @@
+# Netscape HTTP Cookie File
+# https://curl.se/docs/http-cookies.html
+# This file was generated by libcurl! Edit at your own risk.
+
+#HttpOnly_vibesdk-production.isaactrinidadllc.workers.dev FALSE / TRUE 1760315017 CF_AppSession ca7ae1cd14326343
diff --git a/cookies.txt b/cookies.txt
new file mode 100644
index 00000000..a9bd533f
--- /dev/null
+++ b/cookies.txt
@@ -0,0 +1,5 @@
+# Netscape HTTP Cookie File
+# https://curl.se/docs/http-cookies.html
+# This file was generated by libcurl! Edit at your own risk.
+
+#HttpOnly_localhost FALSE / TRUE 1760329216 csrf-token %7B%22token%22%3A%22977680572f4a19a8754f198073026f3703b2a6d68807aa00925343c00f3205af%22%2C%22timestamp%22%3A1760322016202%7D
diff --git a/external/drpower-vibe-production/.dev.vars.example b/external/drpower-vibe-production/.dev.vars.example
new file mode 100644
index 00000000..2d29072b
--- /dev/null
+++ b/external/drpower-vibe-production/.dev.vars.example
@@ -0,0 +1,36 @@
+# Security Configuration
+#CUSTOM_DOMAIN="your-domain.com" # Your custom domain for CORS
+#ENVIRONMENT="prod" # Options: dev, staging, prod
+
+# Essential Secrets:
+#CLOUDFLARE_AI_GATEWAY_TOKEN="" # If this has read and edit permissions, the AI Gateway will be created automatically. run is required at the least
+
+# Provider specific secrets:
+#ANTHROPIC_API_KEY=""
+#OPENAI_API_KEY=""
+GOOGLE_AI_STUDIO_API_KEY=""
+#OPENROUTER_API_KEY="default"
+#GROQ_API_KEY="default"
+
+# Stuff for Oauths:
+#GOOGLE_CLIENT_SECRET=""
+#GOOGLE_CLIENT_ID=""
+
+# GitHub OAuth
+#GITHUB_CLIENT_ID=""
+#GITHUB_CLIENT_SECRET=""
+
+# Github Export OAuth
+# GITHUB_EXPORTER_CLIENT_SECRET=""
+# GITHUB_EXPORTER_CLIENT_ID=""
+
+# Secrets for various internal services of this platform
+JWT_SECRET=""
+WEBHOOK_SECRET=""
+
+# AI Gateway URL Override (Leave as-is if not going to set it)
+#CLOUDFLARE_AI_GATEWAY_URL=""
+
+# Cloudflare API Key and Account ID. This is important
+# CLOUDFLARE_API_TOKEN=""
+# CLOUDFLARE_ACCOUNT_ID=""
\ No newline at end of file
diff --git a/external/drpower-vibe-production/.editorconfig b/external/drpower-vibe-production/.editorconfig
new file mode 100644
index 00000000..69ad037c
--- /dev/null
+++ b/external/drpower-vibe-production/.editorconfig
@@ -0,0 +1,15 @@
+root = true
+
+[*]
+charset = utf-8
+end_of_line = lf
+indent_style = tab
+indent_size = 4
+insert_final_newline = true
+trim_trailing_whitespace = true
+
+[*.md]
+trim_trailing_whitespace = false
+
+[*.{yml,yaml}]
+indent_size = 4
\ No newline at end of file
diff --git a/external/drpower-vibe-production/.gitignore b/external/drpower-vibe-production/.gitignore
new file mode 100644
index 00000000..b1ea1715
--- /dev/null
+++ b/external/drpower-vibe-production/.gitignore
@@ -0,0 +1,50 @@
+# Logs
+logs
+*.log*
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+pnpm-debug.log*
+lerna-debug.log*
+
+node_modules
+dist
+dist-ssr
+.tmp
+*.local
+
+# Editor directories and files
+.vscode/*
+!.vscode/extensions.json
+.idea
+.DS_Store
+*.suo
+*.ntvs*
+*.njsproj
+*.sln
+*.sw?
+
+# wrangler files
+.wrangler
+.dev.vars
+.prod.vars
+.env
+.env.*
+
+runner/downloads
+/generated/prisma
+
+/generated/prisma
+test.ts
+temp/
+
+templates
+debugging.ipynb
+
+#bun.lock
+#*.lock
+
+debug-tools/*.json
+# Sentry Config File
+.env.sentry-build-plugin
+data-dump
\ No newline at end of file
diff --git a/external/drpower-vibe-production/CLAUDE.md b/external/drpower-vibe-production/CLAUDE.md
new file mode 100644
index 00000000..b1043778
--- /dev/null
+++ b/external/drpower-vibe-production/CLAUDE.md
@@ -0,0 +1,140 @@
+# CLAUDE.md
+
+This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
+
+## Project Overview
+This is an official Cloudflare product - a React+Vite frontend with Cloudflare Workers backend that features a Durable Object-based AI agent capable of building webapps phase-wise from user prompts.
+
+**Important Context:**
+- Core functionality: AI-powered webapp generation via Durable Objects
+- Authentication system and database schemas are currently under development (existing code is AI-generated and needs review/rewrite)
+- Full Cloudflare stack: Workers, D1, Durable Objects, R2 (planned)
+- All tests in the project are AI-generated and need replacement
+
+## Development Commands
+
+### Frontend Development
+```bash
+npm run dev # Start Vite dev server with hot reload
+npm run build # Build production frontend
+npm run lint # Run ESLint
+npm run preview # Preview production build
+```
+
+### Worker Development
+```bash
+npm run local # Run Worker locally with Wrangler
+npm run cf-typegen # Generate TypeScript types for CF bindings
+npm run deploy # Deploy to Cloudflare Workers + secrets
+```
+
+### Database (D1) - Under Development
+```bash
+npm run db:setup # Initial database setup
+npm run db:generate # Generate migrations (local)
+npm run db:migrate:local # Apply migrations locally
+npm run db:migrate:remote # Apply migrations to production
+npm run db:studio # Open Drizzle Studio for local DB
+```
+
+### Testing - Needs Rewrite
+```bash
+npm run test # Run Jest tests (current tests need replacement)
+```
+
+## Core Architecture: AI Code Generation
+
+### Phase-wise Generation System (`worker/agents/codegen/`)
+The heart of the system is the `CodeGeneratorAgent` Durable Object that implements sophisticated code generation:
+
+1. **Blueprint Phase**: Analyzes user requirements and creates project blueprint
+2. **Incremental Generation**: Generates code phase-by-phase with specific files per phase
+3. **SCOF Protocol**: Structured Code Output Format for streaming generated code
+4. **Review Cycles**: Multiple automated review passes including:
+ - Static analysis (linting, type checking)
+ - Runtime validation via Runner Service
+ - AI-powered error detection and fixes
+5. **Diff Support**: Efficient file updates using unified diff format
+
+### Key Components
+- **Durable Object**: `worker/agents/codegen/phasewiseGenerator.ts` - Stateful code generation
+- **State Management**: `worker/agents/codegen/state.ts` - Generation state tracking
+- **WebSocket Protocol**: Real-time streaming of generation progress
+- **Runner Service**: External service for code execution and validation
+
+### Frontend-Worker Communication
+- **Initial Request**: POST `/api/agent`
+- **WebSocket Connection**: `/api/agent/:agentId/ws` for real-time updates
+- **Message Types**: Typed protocol for file updates, errors, phase transitions
+
+## Areas Under Development
+
+### Authentication System (Needs Review/Rewrite)
+Current implementation in `worker/auth/` and `worker/api/controllers/authController.ts`:
+- OAuth providers (Google, GitHub) - needs production hardening
+- JWT session management - requires security review
+- Database schema for users/sessions - needs optimization
+
+### Database Architecture (In Progress)
+- Currently using Drizzle ORM with D1
+- Schema in `worker/database/schema.ts` - under active development
+- Migration system needs refinement
+
+### Testing Strategy (Needs Implementation)
+- All current tests are AI-generated placeholders
+- Need proper unit tests for core generation logic
+- Integration tests for Durable Objects
+- E2E tests for generation workflow
+
+## Working with the Codebase
+
+### Adding Features to Code Generation
+1. Modify agent logic in `worker/agents/codegen/phasewiseGenerator.ts`
+2. Update state types in `worker/agents/codegen/state.ts`
+3. Add new message types for WebSocket protocol
+4. Update frontend handler in `src/routes/chat/hooks/use-chat.ts`
+
+### Cloudflare-Specific Patterns
+- **Durable Objects**: Used for stateful, long-running operations
+- **D1 Database**: SQLite-based, use batch operations for performance
+- **Environment Bindings**: Access via `env` parameter (AI, DB, CodeGenObject)
+- **Service Bindings**: Runner service accessed via `env.RUNNER_SERVICE`
+
+### Environment Variables
+Required in `.dev.vars` for local development:
+- `JWT_SECRET` - For session management (under development)
+- `ANTHROPIC_API_KEY`, `OPENAI_API_KEY`, `GOOGLE_AI_STUDIO_API_KEY` - AI providers
+- `RUNNER_SERVICE_API_KEY` - For code execution service
+- OAuth credentials (being redesigned)
+
+## Important Notes
+- Focus on core AI generation functionality when making changes
+- Prioritize Cloudflare-native solutions (D1, Durable Objects, R2)
+- Always **strictly** follow DRY principles
+- Keep code quality high and maintainability in mind
+- Always research and understand the codebase before making changes
+- Never use 'any' type. If you see 'any', Find the proper appropriate type in the project and then replace it. If nothing is found, then write a type for it.
+- Never use dynamic imports. If you see dynamic imports, Correct it!
+- Implement everything the 'right' and 'correct' way instead of 'fast' and 'quick'.
+- Don't add comments for explaining your changes to me. Comments should be professional, to the point and should be there to explain the code, not your changes
+- Don't start writing new 'corrected' versions of files instead of working on fixing the existing ones
+
+## Common Tasks
+
+### Debugging Code Generation
+1. Monitor Durable Object logs: `npm run local`
+2. Check WebSocket messages in browser DevTools
+3. Verify Runner Service connectivity
+4. Review generation state in `CodeGeneratorAgent`
+
+### Working with Durable Objects
+- Class: `worker/agents/codegen/phasewiseGenerator.ts`
+- Binding: `env.CodeGenObject`
+- ID Generation: Based on session/user context
+- State Persistence: Automatic via Cloudflare
+
+### Runner Service Integration
+- Executes generated code in isolated environment
+- Provides runtime error feedback
+- Returns preview URLs for generated apps
+- Configuration in `wrangler.jsonc`
\ No newline at end of file
diff --git a/external/drpower-vibe-production/LICENSE b/external/drpower-vibe-production/LICENSE
new file mode 100644
index 00000000..f0d05814
--- /dev/null
+++ b/external/drpower-vibe-production/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2025 Cloudflare
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/external/drpower-vibe-production/README.md b/external/drpower-vibe-production/README.md
new file mode 100644
index 00000000..6bdf3085
--- /dev/null
+++ b/external/drpower-vibe-production/README.md
@@ -0,0 +1,471 @@
+# ๐งก Cloudflare Vibe SDK
+
+> **An open source full-stack AI webapp generator** โ Deploy your own instance of Cloudflare VibeSDK, an AI vibe coding platform that you can run and customize yourself.
+
+
+
+
+## ๐ Live Demo
+
+**[build.cloudflare.dev](https://build.cloudflare.dev)**
+
+*Explore VibeSDK Build before deploying your own stack.*
+
+[](https://deploy.workers.cloudflare.com/?url=https://github.com/cloudflare/vibesdk)
+
+**๐ Click to deploy your own instance!**
+
+*Follow the setup guide below to configure required services*
+
+
+
+## Star History
+
+[](https://www.star-history.com/#cloudflare/vibesdk&Date)
+
+---
+
+## โจ What is Cloudflare VibeSDK?
+
+Cloudflare VibeSDK is an open source AI vibe coding platform built on Cloudflare's developer platform. If you're building an AI-powered platform for building applications, this is a great example that you can deploy and customize to build the whole platform yourself. Once the platform is deployed, users can say what they want to build in natural language, and the AI agent will create and deploy the application.
+
+**๐ [Experience it live at build.cloudflare.dev](https://build.cloudflare.dev)** โ Try it out before deploying your own instance!
+
+## ๐ฏ Perfect For
+
+### Companies building AI-powered platforms
+Run your own solution that allows users to build applications in natural language. Customize the AI behavior, control the generated code patterns, integrate your own component libraries, and keep all customer data within your infrastructure. Perfect for startups wanting to enter the AI development space or established companies adding AI capabilities to their existing developer tools.
+
+### Internal development
+Enable non-technical teams to create the tools they need without waiting for engineering resources. Marketing can build landing pages, sales can create custom dashboards, and operations can automate workflows, all by describing what they want.
+
+### SaaS platforms
+Let your customers extend your product's functionality without learning your API or writing code. They can describe custom integrations, build specialized workflows, or create tailored interfaces specific to their business needs.
+
+---
+
+### ๐ฏ Key Features
+
+๐ค **AI Code Generation** โ Phase-wise development with intelligent error correction
+โก **Live Previews** โ App previews running in sandboxed containers
+๐ฌ **Interactive Chat** โ Guide development through natural conversation
+๐ฑ **Modern Stack** โ Generates React + TypeScript + Tailwind apps
+๐ **One-Click Deploy** โ Deploy generated apps to Workers for Platforms
+๐ฆ **GitHub Integration** โ Export code directly to your repositories
+
+### ๐๏ธ Built on Cloudflare's Platform
+
+Cloudflare VibeSDK Build utilizes the full Cloudflare developer ecosystem:
+
+- **Frontend**: React + Vite with modern UI components
+- **Backend**: Workers with Durable Objects for AI agents
+- **Database**: D1 (SQLite) with Drizzle ORM
+- **AI**: Multiple LLM providers via AI Gateway
+- **Containers**: Sandboxed app previews and execution
+- **Storage**: R2 buckets for templates, KV for sessions
+- **Deployment**: Workers for Platforms with dispatch namespaces
+
+## ๐ Quick Deploy Checklist
+
+Before clicking "Deploy to Cloudflare", have these ready:
+
+### โ
Prerequisites
+- Cloudflare Workers Paid Plan
+- Workers for Platforms subscription
+- Advanced Certificate Manager (needed when you map a first-level subdomain such as `abc.xyz.com` so Cloudflare can issue the required wildcard certificate for preview apps on `*.abc.xyz.com`)
+
+### ๐ Required API Key
+- **Google Gemini API Key** - Get from [ai.google.dev](https://ai.google.dev)
+
+Once you click "Deploy to Cloudflare", you'll be taken to your Cloudflare dashboard where you can configure your VibeSDK deployment with these variables.
+
+[](https://deploy.workers.cloudflare.com/?url=https://github.com/cloudflare/vibesdk)
+
+### ๐ What you'll configure
+
+- `GOOGLE_AI_STUDIO_API_KEY` - Your Google Gemini API key for Gemini models
+- `JWT_SECRET` - Secure random string for session management
+- `WEBHOOK_SECRET` - Webhook authentication secret
+- `SECRETS_ENCRYPTION_KEY` - Encryption key for secrets
+- `SANDBOX_INSTANCE_TYPE` - Container performance tier (optional, see section below)
+- `ALLOWED_EMAIL` - Email address of the user allowed to use the app. This is used to verify the user's identity and prevent unauthorized access.
+- `CUSTOM_DOMAIN` - Custom domain for your app that you have configured in Cloudflare (**Required**). If you use a first-level subdomain such as `abc.xyz.com`, make sure the Advanced Certificate Manager add-on is active on that zone.
+
+### Custom domain DNS setup
+
+To serve preview apps correctly, add the following DNS record in the zone that hosts `CUSTOM_DOMAIN`:
+
+- Type: `CNAME`
+- Name: `*.abc`
+- Target: `abc.xyz.com` (replace with your base custom domain or another appropriate origin)
+- Proxy status: **Proxied** (orange cloud)
+
+Adjust the placeholder `abc`/`xyz` parts to match your domain. DNS propagation can take timeโexpect it to take up to an hour before previews resolve. This step may be automated in a future release, but it is required today.
+
+### ๐๏ธ Sandbox Instance Configuration (Optional)
+
+VibeSDK uses Cloudflare Containers to run generated applications in isolated environments. You can configure the container performance tier based on your needs and Cloudflare plan.
+
+#### Available Instance Types
+
+> **๐ข Updated Oct 2025**: Cloudflare now offers [larger container instance types](https://developers.cloudflare.com/changelog/2025-10-01-new-container-instance-types/) with more resources!
+
+| Instance Type | Memory | CPU | Disk | Use Case | Availability |
+|---------------|--------|-----|------|----------|--------------|
+| `lite` (alias: `dev`) | 256 MiB | 1/16 vCPU | 2 GB | Development/testing | All plans |
+| `standard-1` (alias: `standard`) | 4 GiB | 1/2 vCPU | 8 GB | Light production apps | All plans |
+| `standard-2` | 8 GiB | 1 vCPU | 12 GB | Medium workloads | All plans |
+| `standard-3` | 12 GiB | 2 vCPU | 16 GB | Production apps | All plans (**Default**) |
+| `standard-4` | 12 GiB | 4 vCPU | 20 GB | High-performance apps | All plans |
+
+#### Configuration Options
+
+**Option A: Via Deploy Button (Recommended)**
+During the "Deploy to Cloudflare" flow, you can set the instance type as a **build variable**:
+- Variable name: `SANDBOX_INSTANCE_TYPE`
+- Recommended values:
+ - **Standard/Paid users**: `standard-3` (default, best balance)
+ - **High-performance needs**: `standard-4`
+
+**Option B: Via Environment Variable**
+For local deployment or CI/CD, set the environment variable:
+```bash
+export SANDBOX_INSTANCE_TYPE=standard-3 # or standard-4, standard-2, standard-1, lite
+bun run deploy
+```
+
+#### Instance Type Selection Guide
+
+**For All Users:**
+- **`standard-3`** (Recommended) - Best balance for production apps with 2 vCPU and 12 GiB memory
+- **`standard-4`** - Maximum performance with 4 vCPU for compute-intensive applications
+
+#### What This Affects
+
+The `SANDBOX_INSTANCE_TYPE` controls:
+- **App Preview Performance** - How fast generated applications run during development
+- **Build Process Speed** - Container compile and build times
+- **Concurrent App Capacity** - How many apps can run simultaneously
+- **Resource Availability** - Memory and disk space for complex applications
+
+> **๐ก Pro Tip**: Start with `standard-3` (the new default) for the best balance of performance and resources. Upgrade to `standard-4` if you need maximum CPU performance for compute-intensive applications.
+
+### ๐ Post-Deployment: OAuth Setup (Optional)
+
+OAuth configuration is **not** shown on the initial deploy page. If you want user login features, you'll need to set this up after deployment:
+
+**How to Add OAuth After Deployment:**
+1. **Find your repository** in your GitHub/GitLab account (created by "Deploy to Cloudflare" flow)
+2. **Clone locally** and run `bun install`
+3. **Create `.dev.vars` and `.prod.vars` files** (see below for OAuth configuration)
+4. **Run `bun run deploy`** to update your deployment
+
+**Google OAuth Setup:**
+1. [Google Cloud Console](https://console.cloud.google.com) โ Create Project
+2. Enable **Google+ API**
+3. Create **OAuth 2.0 Client ID**
+4. Add authorized origins: `https://your-custom-domain.`
+5. Add redirect URI: `https://your-worker-name.workers.dev/api/auth/callback/google`
+6. Add to **both** `.dev.vars` (for local development) and `.prod.vars` (for deployment):
+ ```bash
+ GOOGLE_CLIENT_ID="your-google-client-id"
+ GOOGLE_CLIENT_SECRET="your-google-client-secret"
+ ```
+
+**GitHub OAuth Setup:**
+1. GitHub โ **Settings** โ **Developer settings** โ **OAuth Apps**
+2. Click **New OAuth App**
+3. Application name: `Cloudflare VibeSDK`
+4. Homepage URL: `https://your-worker-name.workers.dev`
+5. Authorization callback URL: `https://your-worker-name.workers.dev/api/auth/callback/github`
+6. Add to **both** `.dev.vars` (for local development) and `.prod.vars` (for deployment):
+ ```bash
+ GITHUB_CLIENT_ID="your-github-client-id"
+ GITHUB_CLIENT_SECRET="your-github-client-secret"
+ ```
+
+**GitHub Export OAuth Setup:**
+1. Create a separate GitHub OAuth app (e.g., `VibeSDK Export`)โdo not reuse the login app above.
+2. Authorization callback URL: `https://your-worker-name.workers.dev/api/github-exporter/callback` (or your custom domain equivalent).
+3. Add to **both** `.dev.vars` and `.prod.vars`:
+ ```bash
+ GITHUB_EXPORTER_CLIENT_ID="your-export-client-id"
+ GITHUB_EXPORTER_CLIENT_SECRET="your-export-client-secret"
+ ```
+4. Redeploy or restart local development so the new variables take effect.
+
+
+---
+## ๐จ How It Works
+
+```mermaid
+graph TD
+ A[User Describes App] --> B[AI Agent Analyzes Request]
+ B --> C[Generate Blueprint & Plan]
+ C --> D[Phase-wise Code Generation]
+ D --> E[Live Preview in Container]
+ E --> F[User Feedback & Iteration]
+ F --> D
+ D --> G[Deploy to Workers for Platforms]
+```
+
+### How It Works
+
+1. **๐ง AI Analysis**: Language models process your description
+2. **๐ Blueprint Creation**: System architecture and file structure planned
+3. **โก Phase Generation**: Code generated incrementally with dependency management
+4. **๐ Quality Assurance**: Automated linting, type checking, and error correction
+5. **๐ฑ Live Preview**: App execution in isolated Cloudflare Containers
+6. **๐ Real-time Iteration**: Chat interface enables continuous refinements
+7. **๐ One-Click Deploy**: Generated apps deploy to Workers for Platforms
+
+## ๐ก Try These Example Prompts
+
+Want to see these prompts in action? **[Visit the live demo at build.cloudflare.dev](https://build.cloudflare.dev)** first, then try them on your own instance once deployed:
+
+**๐ฎ Fun Apps**
+> "Create a todo list with drag and drop and dark mode"
+
+> "Build a simple drawing app with different brush sizes and colors"
+
+> "Make a memory card game with emojis"
+
+**๐ Productivity Apps**
+> "Create an expense tracker with charts and categories"
+
+> "Build a pomodoro timer with task management"
+
+> "Make a habit tracker with streak counters"
+
+**๐จ Creative Tools**
+> "Build a color palette generator from images"
+
+> "Create a markdown editor with live preview"
+
+> "Make a meme generator with text overlays"
+
+**๐ ๏ธ Utility Apps**
+> "Create a QR code generator and scanner"
+
+> "Build a password generator with custom options"
+
+> "Make a URL shortener with click analytics"
+
+---
+
+## ๐ Architecture Deep Dive
+
+### Durable Objects for Stateful AI Agents
+```typescript
+class CodeGeneratorAgent extends DurableObject {
+ async generateCode(prompt: string) {
+ // Persistent state across WebSocket connections
+ // Phase-wise generation with error recovery
+ // Real-time progress streaming to frontend
+ }
+}
+```
+
+### Workers for Platforms Deployment
+```javascript
+// Generated apps deployed to dispatch namespace
+export default {
+ async fetch(request, env) {
+ const appId = extractAppId(request);
+ const userApp = env.DISPATCHER.get(appId);
+ return await userApp.fetch(request);
+ }
+};
+```
+
+### Iteration-based Code Generation
+Cloudflare VibeSDK generates apps in intelligent phases:
+
+1. **Planning Phase**: Analyzes requirements, creates file structure
+2. **Foundation Phase**: Generates package.json, basic setup files
+3. **Core Phase**: Creates main components and logic
+4. **Styling Phase**: Adds CSS and visual design
+5. **Integration Phase**: Connects APIs and external services
+6. **Optimization Phase**: Performance improvements and error fixes
+
+---
+
+## After Deployment
+
+- The "Deploy to Cloudflare" button provisions the worker and also creates a GitHub repository in your account. Clone that repository to work locally.
+- Pushes to the `main` branch trigger automatic deployments; CI/CD is already wired up for you.
+- For a manual deployment, copy `.dev.vars.example` to `.prod.vars`, fill in production-only secrets, and run `bun run deploy`. The deploy script reads from `.prod.vars`.
+
+DNS updates made during setup, including the wildcard CNAME record described above, can take a while to propagate. Wait until the record resolves before testing preview apps.
+
+---
+
+## ๐ Local Development
+
+### Quick Setup
+
+You can run VibeSDK locally by following these steps:
+
+```bash
+# Clone the repository
+git clone https://github.com/cloudflare/vibesdk.git
+cd vibesdk
+
+# Install dependencies
+npm install # or: bun install, yarn install, pnpm install
+
+# Run automated setup
+npm run setup # or: bun run setup
+```
+
+The setup script will guide you through:
+- Installing Bun for better performance
+- Configuring Cloudflare credentials and resources
+- Setting up AI providers and OAuth
+- Creating development and production environments
+- Database setup and migrations
+- Template deployment
+
+**[๐ Complete Setup Guide](docs/setup.md)** - Detailed setup instructions and troubleshooting
+
+### Development Server
+
+After setup, start the development server:
+
+```bash
+bun run dev
+```
+
+Visit `http://localhost:5173` to access VibSDK locally.
+
+### Production Deployment
+
+Deploy to Cloudflare Workers:
+
+```bash
+bun run deploy # Builds and deploys automatically (includes remote DB migration)
+```
+
+---
+
+### Manually Deploying the Platform
+
+#### For Local Development (.dev.vars)
+1. Copy the example file: `cp .dev.vars.example .dev.vars`
+2. Fill in your API keys and tokens
+3. Leave optional values as `"default"` if not needed
+
+#### For Production Deployment
+1. **Build Variables**: Set in your deployment platform (GitHub Actions, etc.)
+2. **Worker Secrets**: Automatically handled by deployment script or set manually:
+ ```bash
+ wrangler secret put ANTHROPIC_API_KEY
+ wrangler secret put OPENAI_API_KEY
+ wrangler secret put GOOGLE_AI_STUDIO_API_KEY
+ # ... etc
+ ```
+
+#### Environment Variable Priority
+The deployment system follows this priority order:
+1. **Environment Variables** (highest priority)
+2. **wrangler.jsonc vars**
+3. **Default values** (lowest priority)
+
+Example: If `MAX_SANDBOX_INSTANCES` is set both as an environment variable (`export MAX_SANDBOX_INSTANCES=5`) and in wrangler.jsonc (`"MAX_SANDBOX_INSTANCES": "2"`), the environment variable value (`5`) will be used.
+
+---
+
+## ๐ Security & Privacy
+
+Cloudflare VibeSDK implements enterprise-grade security:
+
+- ๐ **Encrypted Secrets**: All API keys stored with Cloudflare encryption
+- ๐ฐ **Sandboxed Execution**: Generated apps run in completely isolated containers
+- ๐ก๏ธ **Input Validation**: All user inputs sanitized and validated
+- ๐จ **Rate Limiting**: Prevents abuse and ensures fair usage
+- ๐ **Content Filtering**: AI-powered detection of inappropriate content
+- ๐ **Audit Logs**: Complete tracking of all generation activities
+
+---
+
+## โ Troubleshooting
+
+### Common Deploy Issues
+
+**๐ซ "Insufficient Permissions" Error**
+- Authentication is handled automatically during deployment
+- If you see this error, try redeploying - permissions are auto-granted
+- Contact Cloudflare support if the issue persists
+
+**๐ค "AI Gateway Authentication Failed"**
+- Confirm AI Gateway is set to **Authenticated** mode
+- Verify the authentication token has **Run** permissions
+- Check that gateway URL format is correct
+
+**๐๏ธ "Database Migration Failed"**
+- D1 resources may take time to provision automatically
+- Wait a few minutes and retry - resource creation is handled automatically
+- Check that your account has D1 access enabled
+
+**๐ "Missing Required Variables"**
+- **Worker Secrets**: Verify all required secrets are set: `ANTHROPIC_API_KEY`, `OPENAI_API_KEY`, `GOOGLE_AI_STUDIO_API_KEY`, `JWT_SECRET`
+- **AI Gateway Token**: `CLOUDFLARE_AI_GATEWAY_TOKEN` should be set as BOTH build variable and worker secret
+- **Environment Variables**: These are automatically loaded from wrangler.jsonc - no manual setup needed
+- **Authentication**: API tokens and account IDs are automatically provided by Workers Builds
+
+**๐ค "AI Gateway Not Found"**
+- **With AI Gateway Token**: The deployment script should automatically create the gateway. Check that your token has Read, Edit, and **Run** permissions.
+- **Without AI Gateway Token**: You must manually create an AI Gateway before deployment:
+ 1. Go to [AI Gateway Dashboard](https://dash.cloudflare.com/ai/ai-gateway)
+ 2. Create gateway named `vibesdk-gateway` (or your custom name)
+ 3. Enable authentication and create a token with **Run** permissions
+
+**๐๏ธ "Container Instance Type Issues"**
+- **Slow app previews**: Try upgrading from `lite`/`standard-1` to `standard-3` (default) or `standard-4` instance type
+- **Out of memory errors**: Upgrade to a higher instance type (e.g., from `standard-2` to `standard-3` or `standard-4`) or check for memory leaks in generated apps
+- **Build timeouts**: Use `standard-3` or `standard-4` for faster build times with more CPU cores
+- **Using legacy types**: The `dev` and `standard` aliases still work but map to `lite` and `standard-1` respectively
+
+### Need Help?
+
+- ๐ Check [Cloudflare Workers Docs](https://developers.cloudflare.com/workers/)
+- ๐ฌ Join [Cloudflare Discord](https://discord.gg/cloudflaredev)
+- ๐ Report issues on [GitHub](https://github.com/your-org/cloudflare-vibecoding-starter-kit/issues)
+
+---
+
+## ๐ค Contributing
+
+Want to contribute to Cloudflare VibeSDK? Here's how:
+
+1. **๐ด Fork** via the Deploy button (creates your own instance!)
+2. **๐ป Develop** new features or improvements
+3. **โ
Test** thoroughly with `bun run test`
+4. **๐ค Submit** Pull Request to the main repository
+
+---
+
+## ๐ Resources
+
+### ๐ ๏ธ **Cloudflare Platform**
+- [Workers](https://developers.cloudflare.com/workers/) - Serverless compute platform
+- [Durable Objects](https://developers.cloudflare.com/durable-objects/) - Stateful serverless objects
+- [D1](https://developers.cloudflare.com/d1/) - SQLite database at the edge
+- [R2](https://developers.cloudflare.com/r2/) - Object storage without egress fees
+- [AI Gateway](https://developers.cloudflare.com/ai-gateway/) - Unified AI API gateway
+
+### ๐ฌ **Community**
+- [Discord](https://discord.gg/cloudflaredev) - Real-time chat and support
+- [Community Forum](https://community.cloudflare.com/) - Technical discussions
+- [GitHub Discussions](https://github.com/your-org/cloudflare-vibecoding-starter-kit/discussions) - Feature requests and ideas
+
+### ๐ **Learning Resources**
+- [Workers Learning Path](https://developers.cloudflare.com/learning-paths/workers/) - Master Workers development
+- [Full-Stack Guide](https://developers.cloudflare.com/pages/tutorials/build-a-blog-using-nuxt-and-sanity/) - Build complete applications
+- [AI Integration](https://developers.cloudflare.com/workers-ai/) - Add AI to your apps
+
+---
+
+## ๐ License
+
+MIT License - see [LICENSE](LICENSE) for details.
diff --git a/external/drpower-vibe-production/SandboxDockerfile b/external/drpower-vibe-production/SandboxDockerfile
new file mode 100644
index 00000000..ccb0d032
--- /dev/null
+++ b/external/drpower-vibe-production/SandboxDockerfile
@@ -0,0 +1,56 @@
+# FROM docker.io/cloudflare/sandbox:0.1.3
+FROM docker.io/cloudflare/sandbox:0.1.3
+# FROM --platform=linux/arm64 docker.io/cloudflare/sandbox:0.1.3
+
+# Install cloudflared and system dependencies in a single layer
+ARG TARGETARCH
+RUN apt-get update && \
+ apt-get install -y git ca-certificates curl procps net-tools && \
+ update-ca-certificates && \
+ case ${TARGETARCH} in \
+ "amd64") ARCH="amd64" ;; \
+ "arm64") ARCH="arm64" ;; \
+ *) echo "Unsupported architecture: ${TARGETARCH}"; exit 1 ;; \
+ esac && \
+ curl -L -k --output cloudflared "https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-${ARCH}" && \
+ chmod +x cloudflared && \
+ mv cloudflared /usr/local/bin/ && \
+ rm -rf /var/lib/apt/lists/* && \
+ git config --global user.email "vibesdk-bot@cloudflare.com" && \
+ git config --global user.name "Cloudflare Vibesdk bot"
+
+# Create directory for error monitoring system
+RUN mkdir -p /app/container /app/data
+
+# Copy the process monitoring system
+COPY container/ /app/container/
+
+# Install dependencies for the monitoring system
+WORKDIR /app/container
+RUN bun install && bun run build
+
+# Make scripts executable
+RUN chmod +x /app/container/cli-tools.ts
+
+# Create symlinks for easier CLI usage
+RUN ln -sf /app/container/cli-tools.ts /usr/local/bin/monitor-cli
+
+# Setup common templates packages cache
+WORKDIR /app/container/packages-cache
+ENV BUN_INSTALL_CACHE_DIR=/app/container/packages-cache
+RUN bun install
+
+# Set proper permissions for data directory
+RUN chmod 755 /app/data
+
+# Set environment variable to indicate Docker container environment
+ENV CONTAINER_ENV=docker
+ENV VITE_LOGGER_TYPE=json
+
+# Reset workdir
+WORKDIR /app
+
+EXPOSE 3000
+
+# Run the same command as the original image
+CMD ["bun", "index.ts"]
\ No newline at end of file
diff --git a/bun.lock b/external/drpower-vibe-production/bun.lock
similarity index 100%
rename from bun.lock
rename to external/drpower-vibe-production/bun.lock
diff --git a/external/drpower-vibe-production/components.json b/external/drpower-vibe-production/components.json
new file mode 100644
index 00000000..73afbdbc
--- /dev/null
+++ b/external/drpower-vibe-production/components.json
@@ -0,0 +1,21 @@
+{
+ "$schema": "https://ui.shadcn.com/schema.json",
+ "style": "new-york",
+ "rsc": false,
+ "tsx": true,
+ "tailwind": {
+ "config": "",
+ "css": "src/index.css",
+ "baseColor": "neutral",
+ "cssVariables": true,
+ "prefix": ""
+ },
+ "aliases": {
+ "components": "@/components",
+ "utils": "@/lib/utils",
+ "ui": "@/components/ui",
+ "lib": "@/lib",
+ "hooks": "@/hooks"
+ },
+ "iconLibrary": "lucide"
+}
\ No newline at end of file
diff --git a/external/drpower-vibe-production/container/bun-sqlite.d.ts b/external/drpower-vibe-production/container/bun-sqlite.d.ts
new file mode 100644
index 00000000..053c25a4
--- /dev/null
+++ b/external/drpower-vibe-production/container/bun-sqlite.d.ts
@@ -0,0 +1,18 @@
+// Type declarations for Bun's built-in SQLite module
+declare module 'bun:sqlite' {
+ export interface Statement {
+ all(...params: Params): T[];
+ get(...params: Params): T | null;
+ run(...params: Params): { changes: number };
+ }
+
+ export class Database {
+ constructor(filename: string);
+
+ query(sql: string): Statement;
+ prepare(sql: string): Statement;
+ exec(sql: string): void;
+ close(): void;
+ transaction(fn: (...args: TArgs) => TResult): (...args: TArgs) => TResult;
+ }
+}
diff --git a/external/drpower-vibe-production/container/cli-tools.ts b/external/drpower-vibe-production/container/cli-tools.ts
new file mode 100755
index 00000000..d860f92c
--- /dev/null
+++ b/external/drpower-vibe-production/container/cli-tools.ts
@@ -0,0 +1,1315 @@
+#!/usr/bin/env bun
+import { parseArgs } from 'util';
+import { StorageManager } from './storage.js';
+import { ProcessMonitor } from './process-monitor.js';
+import {
+ ProcessRunnerConfig,
+ ProcessInfo,
+ MonitoringOptions,
+ LogStoreOptions as LogStoreOptionsType,
+ ErrorStoreOptions as ErrorStoreOptionsType,
+ LogFilter,
+ LogCursor,
+ LogLevel,
+ StoredError,
+ StoredLog,
+ SimpleError,
+ Result,
+ DEFAULT_MONITORING_OPTIONS,
+ DEFAULT_STORAGE_OPTIONS,
+ DEFAULT_LOG_STORE_OPTIONS,
+ getDataDirectory,
+ getErrorDbPath,
+ getLogDbPath
+} from './types.js';
+
+class SafeJSON {
+ static stringify(data: unknown, space?: number): string {
+ try {
+ const seen = new WeakSet();
+ const json = JSON.stringify(data, (_key, value) => {
+ if (typeof value === 'object' && value !== null) {
+ if (seen.has(value)) {
+ return '[Circular Reference]';
+ }
+ seen.add(value);
+ }
+ return value;
+ }, space);
+ return json;
+ } catch (error) {
+ // Fallback for any stringify errors
+ return JSON.stringify({
+ error: 'Failed to serialize data',
+ type: typeof data,
+ string: String(data).substring(0, 200)
+ }, null, space);
+ }
+ }
+
+ static parse(text: string): unknown {
+ try {
+ return JSON.parse(text);
+ } catch (error) {
+ // Return error object for failed parsing
+ return {
+ success: false,
+ error: 'Invalid JSON format',
+ rawText: text.substring(0, 200)
+ };
+ }
+ }
+}
+
+class SafeCleanup {
+ static async closeStorage(storage: StorageManager | null, timeoutMs: number = 2000): Promise {
+ if (!storage) return;
+
+ try {
+ const timeout = setTimeout(() => {
+ console.warn('Storage close timeout, forcing cleanup');
+ }, timeoutMs);
+
+ storage.close();
+ clearTimeout(timeout);
+ } catch (error) {
+ console.warn('Storage close error:', error);
+ // Don't throw - we're in cleanup
+ }
+ }
+}
+
+class OutputFormatter {
+ static formatOutput(data: unknown, format: 'json' | 'table' | 'raw' = 'json'): void {
+ switch (format) {
+ case 'json':
+ console.log(SafeJSON.stringify(data, 2));
+ break;
+ case 'raw':
+ if (typeof data === 'string') {
+ console.log(data);
+ } else {
+ console.log(String(data));
+ }
+ break;
+ case 'table':
+ // Table formatting is handled by specific formatters
+ console.log(SafeJSON.stringify(data, 2));
+ break;
+ }
+ }
+
+ static formatError(error: string, additionalData?: Record): void {
+ const errorResponse = {
+ success: false,
+ error,
+ ...additionalData
+ };
+ console.log(SafeJSON.stringify(errorResponse, 2));
+ }
+
+ static formatSuccess(message: string, data?: unknown): void {
+ const successResponse: Record = {
+ success: true,
+ message
+ };
+ if (data) {
+ successResponse.data = data;
+ }
+ console.log(SafeJSON.stringify(successResponse, 2));
+ }
+
+ static printErrorsTable(errors: readonly StoredError[]): void {
+ if (errors.length === 0) {
+ console.log('No errors found.');
+ return;
+ }
+
+ console.log('Timestamp'.padEnd(20) + 'Level'.padEnd(10) + 'Message');
+ console.log('-'.repeat(80));
+
+ for (const error of errors) {
+ const timestamp = new Date(error.timestamp).toISOString().slice(0, 16).replace('T', ' ');
+ const level = `L${error.level}`.padEnd(9);
+ const message = error.message.length > 50 ? error.message.substring(0, 47) + '...' : error.message;
+
+ console.log(`${timestamp} ${level} ${message}`);
+
+ if (error.occurrenceCount > 1) {
+ console.log(''.padEnd(30) + `(occurred ${error.occurrenceCount} times)`);
+ }
+ }
+ }
+
+ static printLogsTable(logs: readonly StoredLog[]): void {
+ if (logs.length === 0) {
+ console.log('No logs found.');
+ return;
+ }
+
+ console.log('Timestamp'.padEnd(20) + 'Level'.padEnd(8) + 'Stream'.padEnd(8) + 'Source'.padEnd(15) + 'Message');
+ console.log('-'.repeat(100));
+
+ for (const log of logs) {
+ const timestamp = new Date(log.timestamp).toISOString().slice(0, 16).replace('T', ' ');
+ const level = log.level.padEnd(7);
+ const stream = log.stream.padEnd(7);
+ const source = (log.source || 'unknown').padEnd(14);
+ const message = log.message.length > 50 ? log.message.substring(0, 47) + '...' : log.message;
+
+ console.log(`${timestamp} ${level} ${stream} ${source} ${message}`);
+ }
+ }
+}
+
+class ProcessCommands {
+ private static activeRunners = new Map();
+
+ static async start(options: {
+ instanceId: string;
+ command: string;
+ args: string[];
+ cwd?: string;
+ port?: string;
+ maxRestarts?: number;
+ restartDelay?: number;
+ maxErrors?: number;
+ retentionDays?: number;
+ logRetentionHours?: number;
+ }): Promise {
+ try {
+ // Check if already running
+ if (this.activeRunners.has(options.instanceId)) {
+ OutputFormatter.formatError(`Process ${options.instanceId} is already running`);
+ process.exit(1);
+ }
+
+ // Set PORT environment variable if provided
+ if (options.port) {
+ process.env.PORT = options.port;
+ }
+
+ // Build configuration
+ const envVars: Record = {};
+ if (options.port) {
+ envVars.PORT = options.port;
+ }
+
+ const monitoring: MonitoringOptions = {
+ ...DEFAULT_MONITORING_OPTIONS,
+ maxRestarts: options.maxRestarts ?? DEFAULT_MONITORING_OPTIONS.maxRestarts,
+ restartDelay: options.restartDelay ?? DEFAULT_MONITORING_OPTIONS.restartDelay,
+ env: envVars
+ };
+
+ const errorStorage: ErrorStoreOptionsType = {
+ ...DEFAULT_STORAGE_OPTIONS,
+ maxErrors: options.maxErrors ?? DEFAULT_STORAGE_OPTIONS.maxErrors,
+ retentionDays: options.retentionDays ?? DEFAULT_STORAGE_OPTIONS.retentionDays
+ };
+
+ const logStorage: LogStoreOptionsType = {
+ ...DEFAULT_LOG_STORE_OPTIONS,
+ retentionHours: options.logRetentionHours ?? DEFAULT_LOG_STORE_OPTIONS.retentionHours
+ };
+
+ const config: ProcessRunnerConfig = {
+ instanceId: options.instanceId,
+ command: options.command,
+ args: options.args,
+ cwd: options.cwd || process.cwd(),
+ monitoring,
+ storage: {
+ error: errorStorage,
+ log: logStorage
+ }
+ };
+
+ console.log('Starting Process Monitor:');
+ console.log(` Instance ID: ${options.instanceId}`);
+ console.log(` Command: ${options.command} ${options.args.join(' ')}`);
+ console.log(` Working Directory: ${config.cwd}`);
+ console.log(` Max Restarts: ${monitoring.maxRestarts}`);
+ console.log(` Restart Delay: ${monitoring.restartDelay}ms`);
+
+ // Create and start ProcessRunner with storage options
+ const runner = new ProcessRunner(config, {
+ error: errorStorage,
+ log: logStorage
+ });
+ const startResult = await runner.start();
+
+ if (!startResult.success) {
+ OutputFormatter.formatError(`Failed to start process: ${startResult.error.message}`);
+ process.exit(1);
+ }
+
+ // Store active runner
+ this.activeRunners.set(options.instanceId, runner);
+
+ if (startResult.success) {
+ console.log(`Process monitoring started successfully. PID: ${startResult.data.pid}`);
+ console.log('Process is active. Press Ctrl+C to stop.');
+ }
+
+ // Setup periodic status reporting
+ const statusInterval = setInterval(() => {
+ const processInfo = runner.getProcessInfo();
+ if (processInfo && processInfo.startTime) {
+ const uptime = Math.floor((Date.now() - processInfo.startTime.getTime()) / 1000);
+ console.log(`[STATUS] Process ${processInfo.id} running for ${uptime}s (restarts: ${processInfo.restartCount})`);
+ } else if (processInfo) {
+ console.log(`[STATUS] Process ${processInfo.id} running (restarts: ${processInfo.restartCount})`);
+ }
+ }, 60000); // Every minute
+
+ // Setup graceful shutdown with race condition protection
+ let isShuttingDown = false;
+ const gracefulShutdown = async (signal: string) => {
+ if (isShuttingDown) {
+ console.log(`\nAlready shutting down, ignoring ${signal}`);
+ return;
+ }
+ isShuttingDown = true;
+
+ console.log(`\nReceived ${signal}. Initiating graceful shutdown...`);
+ clearInterval(statusInterval);
+
+ try {
+ await runner.stop();
+ this.activeRunners.delete(options.instanceId);
+ console.log('Graceful shutdown completed');
+ } catch (shutdownError) {
+ console.error('Error during shutdown:', shutdownError);
+ }
+
+ process.exit(0);
+ };
+
+ process.on('SIGTERM', () => gracefulShutdown('SIGTERM'));
+ process.on('SIGINT', () => gracefulShutdown('SIGINT'));
+ process.on('SIGHUP', () => gracefulShutdown('SIGHUP'));
+
+ // Keep alive (process will exit via signal handlers)
+ await new Promise(() => {}); // Never resolves
+
+ } catch (error) {
+ OutputFormatter.formatError(`Process start failed: ${error instanceof Error ? error.message : 'Unknown error'}`);
+ process.exit(1);
+ }
+ }
+
+ static async stop(options: { instanceId: string; force?: boolean }): Promise {
+ try {
+ const runner = this.activeRunners.get(options.instanceId);
+ if (!runner) {
+ OutputFormatter.formatError(`Process ${options.instanceId} is not running`);
+ process.exit(1);
+ }
+
+ if (runner) {
+ const stopResult = await runner.stop(options.force);
+ if (!stopResult.success) {
+ OutputFormatter.formatError(`Failed to stop process: ${stopResult.error.message}`);
+ process.exit(1);
+ }
+
+ this.activeRunners.delete(options.instanceId);
+ OutputFormatter.formatSuccess(`Process ${options.instanceId} stopped successfully`);
+ }
+
+ } catch (error) {
+ OutputFormatter.formatError(`Process stop failed: ${error instanceof Error ? error.message : 'Unknown error'}`);
+ process.exit(1);
+ }
+ }
+
+ static async status(options: { instanceId?: string }): Promise {
+ try {
+ if (options.instanceId) {
+ const runner = this.activeRunners.get(options.instanceId);
+ if (!runner) {
+ OutputFormatter.formatError(`Process ${options.instanceId} is not running`);
+ return;
+ }
+
+ const stats = runner.getStats();
+ OutputFormatter.formatOutput({
+ success: true,
+ instanceId: options.instanceId,
+ status: 'running',
+ ...stats
+ });
+ } else {
+ // List all active processes
+ const processes = Array.from(this.activeRunners.entries()).map(([instanceId, runner]) => ({
+ instanceId,
+ stats: runner.getStats()
+ }));
+
+ OutputFormatter.formatOutput({
+ success: true,
+ activeProcesses: processes.length,
+ processes
+ });
+ }
+ } catch (error) {
+ OutputFormatter.formatError(`Status check failed: ${error instanceof Error ? error.message : 'Unknown error'}`);
+ }
+ }
+}
+
+class ProcessRunner {
+ private config: ProcessRunnerConfig;
+ private storage: StorageManager;
+ private monitor?: ProcessMonitor;
+ private isRunning = false;
+
+ constructor(config: ProcessRunnerConfig, storageOptions?: { error?: ErrorStoreOptionsType; log?: LogStoreOptionsType }) {
+ this.config = config;
+ const options = storageOptions || config.storage || {};
+ this.storage = new StorageManager(undefined, undefined, options);
+ }
+
+ async start(): Promise> {
+ try {
+ if (this.isRunning) {
+ return { success: false, error: new Error('ProcessRunner is already running') };
+ }
+
+ const processInfo: ProcessInfo = {
+ id: `proc-${this.config.instanceId}-${Date.now()}`,
+ instanceId: this.config.instanceId,
+ command: this.config.command,
+ args: this.config.args,
+ cwd: this.config.cwd,
+ status: 'starting',
+ startTime: new Date(),
+ restartCount: 0
+ };
+
+ this.monitor = new ProcessMonitor(processInfo, this.storage, this.config.monitoring);
+ this.setupMonitorEventHandlers();
+
+ const startResult = await this.monitor.start();
+ if (!startResult.success) {
+ return startResult;
+ }
+
+ this.isRunning = true;
+ return startResult;
+ } catch (error) {
+ return {
+ success: false,
+ error: error instanceof Error ? error : new Error('Unknown error starting ProcessRunner')
+ };
+ }
+ }
+
+ async stop(force = false): Promise> {
+ try {
+ if (!this.isRunning || !this.monitor) {
+ return { success: true, data: true };
+ }
+
+ const stopResult = await this.monitor.stop();
+ this.isRunning = false;
+
+ await this.monitor.cleanup();
+ this.monitor = undefined;
+ this.storage.close();
+
+ // Convert Result to Result
+ if (stopResult.success) {
+ return { success: true, data: true };
+ } else {
+ return { success: false, error: stopResult.error };
+ }
+ } catch (error) {
+ return {
+ success: false,
+ error: error instanceof Error ? error : new Error('Unknown error stopping ProcessRunner')
+ };
+ }
+ }
+
+ getProcessInfo(): ProcessInfo | null {
+ return this.monitor?.getProcessInfo() || null;
+ }
+
+ getStats(): Record {
+ if (!this.monitor) {
+ return { error: 'Monitor not initialized' };
+ }
+
+ const processInfo = this.monitor.getProcessInfo();
+ return {
+ processId: processInfo.id,
+ instanceId: processInfo.instanceId,
+ pid: processInfo.pid,
+ status: processInfo.status,
+ startTime: processInfo.startTime,
+ restartCount: processInfo.restartCount,
+ config: {
+ instanceId: this.config.instanceId,
+ command: this.config.command,
+ args: this.config.args,
+ cwd: this.config.cwd
+ }
+ };
+ }
+
+ private setupMonitorEventHandlers(): void {
+ if (!this.monitor) return;
+
+ this.monitor.on('process_started', (event) => {
+ console.log(`[${event.timestamp.toISOString()}] Process started: PID ${event.pid}`);
+ });
+
+ this.monitor.on('process_stopped', (event) => {
+ console.log(`[${event.timestamp.toISOString()}] Process stopped: ${event.reason}`);
+ });
+
+ this.monitor.on('error_detected', (event) => {
+ const { error } = event;
+ console.error(`[${event.timestamp.toISOString()}] Error detected [Level ${error.level}]: ${error.message}`);
+ });
+
+ this.monitor.on('process_crashed', (event) => {
+ console.error(`[${event.timestamp.toISOString()}] Process crashed: Exit code ${event.exitCode}, Signal: ${event.signal}`);
+ if (event.willRestart) {
+ console.log('Process will be restarted automatically');
+ }
+ });
+ }
+}
+
+class ErrorCommands {
+ static async list(options: {
+ instanceId: string;
+ minLevel?: number;
+ maxLevel?: number;
+ since?: string;
+ until?: string;
+ limit?: number;
+ offset?: number;
+ format?: 'json' | 'table' | 'raw';
+ dbPath?: string;
+ reset?: boolean;
+ }): Promise {
+ let storage: StorageManager | null = null;
+
+ try {
+ storage = new StorageManager(options.dbPath);
+ const result = storage.getErrors(options.instanceId);
+
+ if (!result.success) {
+ throw result.error;
+ }
+
+ let filteredErrors = result.data;
+
+ // Apply filters
+ if (options.minLevel !== undefined) {
+ filteredErrors = filteredErrors.filter(error =>
+ error.level >= options.minLevel!
+ );
+ }
+
+ if (options.maxLevel !== undefined) {
+ filteredErrors = filteredErrors.filter(error =>
+ error.level <= options.maxLevel!
+ );
+ }
+
+ if (options.since) {
+ const sinceDate = new Date(options.since);
+ filteredErrors = filteredErrors.filter(error =>
+ new Date(error.timestamp) >= sinceDate
+ );
+ }
+
+ if (options.until) {
+ const untilDate = new Date(options.until);
+ filteredErrors = filteredErrors.filter(error =>
+ new Date(error.timestamp) <= untilDate
+ );
+ }
+
+ // Apply pagination
+ const offset = options.offset || 0;
+ const limit = options.limit || 100;
+ const paginatedErrors = filteredErrors.slice(offset, offset + limit);
+
+ const response = {
+ success: true,
+ errors: paginatedErrors,
+ summary: {
+ totalErrors: filteredErrors.length,
+ errorsByLevel: this.countByField(filteredErrors, 'level'),
+ hasMore: offset + paginatedErrors.length < filteredErrors.length
+ }
+ };
+
+ let resetInfo: { clearedCount: number } | undefined;
+ if (options.reset) {
+ if (!storage) {
+ throw new Error('Storage not initialized for reset operation');
+ }
+
+ const clearResult = storage.clearErrors(options.instanceId);
+ if (!clearResult.success) {
+ throw clearResult.error;
+ }
+
+ resetInfo = { clearedCount: clearResult.data.clearedCount };
+ }
+
+ if (options.format === 'table') {
+ OutputFormatter.printErrorsTable(paginatedErrors);
+ if (resetInfo) {
+ console.log(`\nCleared ${resetInfo.clearedCount} stored errors.`);
+ }
+ } else {
+ const outputPayload = resetInfo ? { ...response, reset: resetInfo } : response;
+ OutputFormatter.formatOutput(outputPayload, options.format);
+ }
+
+ // Explicit exit after successful execution
+ process.exit(0);
+
+ } catch (error) {
+ try {
+ OutputFormatter.formatError(
+ error instanceof Error ? error.message : String(error),
+ { instanceId: options.instanceId }
+ );
+ } catch (formatError) {
+ // Fallback if formatting fails
+ console.error(SafeJSON.stringify({ success: false, error: String(error) }));
+ }
+
+ // Ensure cleanup before exit
+ if (storage) {
+ try {
+ storage.close();
+ } catch (closeError) {
+ // Ignore close errors
+ }
+ }
+ process.exit(1);
+ } finally {
+ await SafeCleanup.closeStorage(storage);
+ }
+ }
+
+ static async stats(options: { instanceId: string; dbPath?: string }): Promise {
+ const storage = new StorageManager(options.dbPath);
+
+ try {
+ const result = storage.getErrorSummary(options.instanceId);
+ if (!result.success) {
+ throw result.error;
+ }
+
+ const response = {
+ success: true,
+ instanceId: options.instanceId,
+ ...result.data
+ };
+
+ OutputFormatter.formatOutput(response);
+
+ // Explicit exit after successful execution
+ process.exit(0);
+
+ } catch (error) {
+ OutputFormatter.formatError(
+ error instanceof Error ? error.message : String(error),
+ { instanceId: options.instanceId }
+ );
+ process.exit(1);
+ } finally {
+ await SafeCleanup.closeStorage(storage);
+ }
+ }
+
+ static async clear(options: { instanceId: string; confirm: boolean; dbPath?: string }): Promise {
+ if (!options.confirm) {
+ OutputFormatter.formatError('--confirm flag required to clear errors');
+ process.exit(1);
+ }
+
+ const storage = new StorageManager(options.dbPath);
+
+ try {
+ const result = storage.clearErrors(options.instanceId);
+ if (!result.success) {
+ throw result.error;
+ }
+
+ const response = {
+ success: true,
+ message: `Cleared ${result.data.clearedCount} errors for instance ${options.instanceId}`,
+ clearedCount: result.data.clearedCount
+ };
+
+ OutputFormatter.formatOutput(response);
+
+ // Explicit exit after successful execution
+ process.exit(0);
+
+ } catch (error) {
+ OutputFormatter.formatError(
+ error instanceof Error ? error.message : String(error),
+ { instanceId: options.instanceId }
+ );
+ process.exit(1);
+ } finally {
+ await SafeCleanup.closeStorage(storage);
+ }
+ }
+
+ private static countByField(errors: readonly StoredError[], field: keyof StoredError): Record {
+ const counts: Record = {};
+ for (const error of errors) {
+ const value = String(error[field]);
+ counts[value] = (counts[value] || 0) + 1;
+ }
+ return counts;
+ }
+}
+
+class LogCommands {
+ static async list(options: {
+ instanceId: string;
+ levels?: LogLevel[];
+ streams?: ('stdout' | 'stderr')[];
+ since?: string;
+ until?: string;
+ limit?: number;
+ offset?: number;
+ format?: 'json' | 'table' | 'raw';
+ dbPath?: string;
+ }): Promise {
+ const storage = new StorageManager(undefined, options.dbPath);
+
+ try {
+ const filter: LogFilter = {
+ instanceId: options.instanceId,
+ levels: options.levels,
+ streams: options.streams,
+ since: options.since ? new Date(options.since) : undefined,
+ until: options.until ? new Date(options.until) : undefined,
+ limit: options.limit || 100,
+ offset: options.offset || 0,
+ sortOrder: 'desc'
+ };
+
+ const result = storage.getLogs(filter);
+ if (!result.success) {
+ throw result.error;
+ }
+
+ const response = result.data;
+
+ if (options.format === 'table') {
+ OutputFormatter.printLogsTable(response.logs);
+ } else if (options.format === 'raw') {
+ response.logs.forEach(log => console.log(log.message));
+ } else {
+ OutputFormatter.formatOutput(response, options.format);
+ }
+
+ // Explicit exit after successful execution
+ process.exit(0);
+
+ } catch (error) {
+ OutputFormatter.formatError(
+ error instanceof Error ? error.message : String(error),
+ { instanceId: options.instanceId }
+ );
+ process.exit(1);
+ } finally {
+ await SafeCleanup.closeStorage(storage);
+ }
+ }
+
+
+
+ static async get(options: {
+ instanceId: string;
+ format?: 'json' | 'raw';
+ reset?: boolean;
+ }): Promise {
+ try {
+ const { promises: fs } = require('fs');
+ const { join } = require('path');
+
+ const logFilePath = join(getDataDirectory(), `${options.instanceId}-process.log`);
+ const lockFilePath = `${logFilePath}.lock`;
+ const tempPath = `${logFilePath}.tmp.${Date.now()}.${process.pid}`;
+
+ let logs = '';
+
+ // Simple file-based locking to prevent concurrent access
+ const acquireLock = async (): Promise => {
+ try {
+ await fs.writeFile(lockFilePath, process.pid.toString(), { flag: 'wx' });
+ return true;
+ } catch (error) {
+ return false;
+ }
+ };
+
+ const releaseLock = async (): Promise => {
+ try {
+ await fs.unlink(lockFilePath);
+ } catch (error) {
+ // Ignore lock release errors
+ }
+ };
+
+ // Try to acquire lock with retry
+ let lockAcquired = false;
+ for (let attempt = 0; attempt < 5; attempt++) {
+ lockAcquired = await acquireLock();
+ if (lockAcquired) break;
+
+ // Wait briefly before retry
+ await new Promise(resolve => setTimeout(resolve, 50 + Math.random() * 50));
+ }
+
+ if (!lockAcquired) {
+ throw new Error('Could not acquire file lock for log reset operation');
+ }
+
+ try {
+ if (options.reset) {
+ // Reset mode: Atomic operation to read and clear the file
+ try {
+ await fs.rename(logFilePath, tempPath);
+
+ // Create new empty log file immediately
+ await fs.writeFile(logFilePath, '', 'utf8').catch(() => {});
+
+ // Read from temp file and clean up
+ try {
+ logs = await fs.readFile(tempPath, 'utf8');
+ await fs.unlink(tempPath).catch(() => {}); // Clean up temp file
+ } catch (error) {
+ // If we can't read temp file, at least clean it up
+ await fs.unlink(tempPath).catch(() => {});
+ logs = '';
+ }
+ } catch (error) {
+ // File doesn't exist yet, return empty
+ if ((error as any).code === 'ENOENT') {
+ logs = '';
+ } else {
+ throw error;
+ }
+ }
+ } else {
+ // Read-only mode: Just read the file without resetting
+ try {
+ logs = await fs.readFile(logFilePath, 'utf8');
+ } catch (error) {
+ // File doesn't exist yet, return empty
+ if ((error as any).code === 'ENOENT') {
+ logs = '';
+ } else {
+ throw error;
+ }
+ }
+ }
+ } finally {
+ await releaseLock();
+ }
+
+ if (options.format === 'raw') {
+ console.log(logs);
+ } else {
+ const response = {
+ success: true,
+ logs: logs,
+ instanceId: options.instanceId
+ };
+ OutputFormatter.formatOutput(response, options.format);
+ }
+
+ // Explicit exit after successful execution
+ process.exit(0);
+
+ } catch (error) {
+ OutputFormatter.formatError(
+ error instanceof Error ? error.message : String(error),
+ { instanceId: options.instanceId }
+ );
+ process.exit(1);
+ }
+ }
+
+ static async stats(options: { instanceId: string; dbPath?: string }): Promise {
+ const storage = new StorageManager(undefined, options.dbPath);
+
+ try {
+ const result = storage.getLogStats(options.instanceId);
+ if (!result.success) {
+ throw result.error;
+ }
+
+ const response = {
+ success: true,
+ instanceId: options.instanceId,
+ ...result.data
+ };
+
+ OutputFormatter.formatOutput(response);
+
+ // Explicit exit after successful execution
+ process.exit(0);
+
+ } catch (error) {
+ OutputFormatter.formatError(
+ error instanceof Error ? error.message : String(error),
+ { instanceId: options.instanceId }
+ );
+ process.exit(1);
+ } finally {
+ await SafeCleanup.closeStorage(storage);
+ }
+ }
+
+ static async clear(options: { instanceId: string; confirm: boolean; dbPath?: string }): Promise {
+ if (!options.confirm) {
+ OutputFormatter.formatError('--confirm flag required to clear logs');
+ process.exit(1);
+ }
+
+ const storage = new StorageManager(undefined, options.dbPath);
+
+ try {
+ const result = storage.clearLogs(options.instanceId);
+ if (!result.success) {
+ throw result.error;
+ }
+
+ const response = {
+ success: true,
+ message: `Cleared ${result.data.clearedCount} logs for instance ${options.instanceId}`,
+ clearedCount: result.data.clearedCount
+ };
+
+ OutputFormatter.formatOutput(response);
+
+ // Explicit exit after successful execution
+ process.exit(0);
+
+ } catch (error) {
+ OutputFormatter.formatError(
+ error instanceof Error ? error.message : String(error),
+ { instanceId: options.instanceId }
+ );
+ process.exit(1);
+ } finally {
+ await SafeCleanup.closeStorage(storage);
+ }
+ }
+}
+
+function showHelp() {
+ console.log(`
+Unified Process Monitoring CLI - Comprehensive management for containerized processes
+
+Usage: bun run cli-tools.ts [subcommand] [options]
+
+COMMANDS:
+
+ process Process lifecycle management
+ start Start process monitoring
+ stop Stop process monitoring
+ status Get process status
+
+ errors Error management
+ list List runtime errors (optional reset)
+ stats Get error statistics
+ clear Clear stored errors
+
+ logs Log management
+ list List process logs
+ get Get process logs with optional reset
+ stats Get log statistics
+ clear Clear stored logs
+
+PROCESS COMMANDS:
+
+ # Start monitoring a development server
+ bun run cli-tools.ts process start --instance-id my-app --port 8080 -- bun run dev
+
+ # Start with custom restart policy
+ bun run cli-tools.ts process start -i my-app --max-restarts 5 --restart-delay 2000 -- npm start
+
+ # Stop monitoring
+ bun run cli-tools.ts process stop --instance-id my-app
+
+ # Get process status
+ bun run cli-tools.ts process status --instance-id my-app
+
+ERROR COMMANDS:
+
+ # List recent errors
+ bun run cli-tools.ts errors list --instance-id my-app --limit 50
+
+ # List errors and clear them after retrieval
+ bun run cli-tools.ts errors list --instance-id my-app --reset
+
+ # Filter by log level (50=error, 60=fatal)
+ bun run cli-tools.ts errors list -i my-app --min-level 50 --max-level 60
+
+ # Get error statistics
+ bun run cli-tools.ts errors stats --instance-id my-app
+
+ # Clear all errors
+ bun run cli-tools.ts errors clear --instance-id my-app --confirm
+
+LOG COMMANDS:
+
+ # List recent logs
+ bun run cli-tools.ts logs list --instance-id my-app --limit 100
+
+ # Filter by log level and stream
+ bun run cli-tools.ts logs list -i my-app --levels error,warn --streams stderr
+
+ # Get all process logs (for SandboxSdkClient)
+ bun run cli-tools.ts logs get --instance-id my-app --format raw
+
+ # Get all process logs and reset log file
+ bun run cli-tools.ts logs get --instance-id my-app --format raw --reset
+
+ # Get log statistics
+ bun run cli-tools.ts logs stats --instance-id my-app
+
+GLOBAL OPTIONS:
+
+ --instance-id, -i Instance identifier (required for most commands)
+ --format Output format: json (default), table, raw
+ --db-path Custom database path
+ --help, -h Show help message
+
+PROCESS START OPTIONS:
+
+ --port, -p Set PORT environment variable
+ --cwd, -c Working directory (default: current directory)
+ --max-restarts Maximum restart attempts (default: 3)
+ --restart-delay Delay between restarts in ms (default: 1000)
+ --max-errors Maximum errors to store (default: 1000)
+ --retention-days Error retention period (default: 7)
+ --log-retention-hours Log retention period in hours (default: 168)
+
+FILTER OPTIONS:
+
+ --levels Filter by log levels (comma-separated)
+ --streams Filter by streams (comma-separated: stdout,stderr)
+ --categories Filter by error categories (comma-separated)
+ --severities Filter by error severities (comma-separated)
+ --since Filter since date (ISO format)
+ --until Filter until date (ISO format)
+ --limit Limit number of results (default: 100)
+ --offset Offset for pagination (default: 0)
+
+STATE OPTIONS:
+
+ --reset Clear stored data after retrieval where supported
+
+EXAMPLES:
+
+ # Complete workflow: start monitoring, check errors, view logs
+ bun run cli-tools.ts process start -i vite-app -- bun run dev
+ bun run cli-tools.ts errors list -i vite-app --format table
+ bun run cli-tools.ts logs get -i vite-app --format raw
+
+ # Monitor production deployment
+ bun run cli-tools.ts process start -i prod-api --max-restarts 10 -- node server.js
+
+ # Debug specific error patterns
+ bun run cli-tools.ts errors list -i my-app --severities fatal,error --since 2024-01-01
+
+Environment Variables:
+ INSTANCE_ID Default instance identifier
+ PORT Port for the application
+ CLI_DATA_DIR Data directory path (default: ./data)
+ CLI_ERROR_DB_PATH Error database path (default: /errors.db)
+ CLI_LOG_DB_PATH Log database path (default: /logs.db)
+
+Database Storage:
+ Errors: ${getErrorDbPath()} (configurable via CLI_ERROR_DB_PATH)
+ Logs: ${getLogDbPath()} (configurable via CLI_LOG_DB_PATH)
+ Data: ${getDataDirectory()} (configurable via CLI_DATA_DIR)
+`);
+}
+
+function initializeDataDirectory(): void {
+ const fs = require('fs');
+ const dataDir = getDataDirectory();
+
+ try {
+ if (!fs.existsSync(dataDir)) {
+ fs.mkdirSync(dataDir, { recursive: true });
+ console.log(`Created data directory: ${dataDir}`);
+ }
+ } catch (error) {
+ console.warn(`Failed to create data directory ${dataDir}:`, error);
+ }
+}
+
+async function main() {
+ try {
+ // Initialize data directory
+ initializeDataDirectory();
+
+ const { values: args, positionals } = parseArgs({
+ args: process.argv.slice(2),
+ options: {
+ // Global options
+ 'instance-id': { type: 'string', short: 'i' },
+ 'format': { type: 'string' },
+ 'db-path': { type: 'string' },
+ 'help': { type: 'boolean', short: 'h' },
+
+ // Process options
+ 'cwd': { type: 'string', short: 'c' },
+ 'port': { type: 'string', short: 'p' },
+ 'max-restarts': { type: 'string' },
+ 'restart-delay': { type: 'string' },
+ 'max-errors': { type: 'string' },
+ 'retention-days': { type: 'string' },
+ 'log-retention-hours': { type: 'string' },
+ 'force': { type: 'boolean' },
+
+ // Filter options
+ 'levels': { type: 'string' },
+ 'streams': { type: 'string' },
+ 'categories': { type: 'string' },
+ 'severities': { type: 'string' },
+ 'since': { type: 'string' },
+ 'until': { type: 'string' },
+ 'limit': { type: 'string' },
+ 'offset': { type: 'string' },
+ 'last-sequence': { type: 'string' },
+ 'count': { type: 'string' },
+ 'confirm': { type: 'boolean' },
+ 'reset': { type: 'boolean' }
+ },
+ allowPositionals: true
+ });
+
+ const command = positionals[0];
+ const subcommand = positionals[1];
+
+ if (args.help || !command) {
+ showHelp();
+ process.exit(0);
+ }
+
+ // Route to appropriate command handler
+ switch (command) {
+ case 'process':
+ await handleProcessCommand(subcommand, args, positionals.slice(2));
+ break;
+
+ case 'errors':
+ await handleErrorCommand(subcommand, args);
+ break;
+
+ case 'logs':
+ await handleLogCommand(subcommand, args);
+ break;
+
+ default:
+ OutputFormatter.formatError(`Unknown command: ${command}`);
+ showHelp();
+ process.exit(1);
+ }
+
+ } catch (error) {
+ OutputFormatter.formatError(`CLI failed: ${error instanceof Error ? error.message : 'Unknown error'}`);
+ process.exit(1);
+ }
+}
+
+async function handleProcessCommand(subcommand: string, args: Record, remainingArgs: string[]) {
+ switch (subcommand) {
+ case 'start':
+ if (remainingArgs.length === 0) {
+ OutputFormatter.formatError('No command specified to monitor');
+ process.exit(1);
+ }
+
+ const instanceId = String(args['instance-id'] || process.env.INSTANCE_ID || `instance-${Date.now()}`);
+
+ await ProcessCommands.start({
+ instanceId,
+ command: remainingArgs[0],
+ args: remainingArgs.slice(1),
+ cwd: args.cwd ? String(args.cwd) : undefined,
+ port: args.port ? String(args.port) : undefined,
+ maxRestarts: args['max-restarts'] ? parseInt(String(args['max-restarts'])) : undefined,
+ restartDelay: args['restart-delay'] ? parseInt(String(args['restart-delay'])) : undefined,
+ maxErrors: args['max-errors'] ? parseInt(String(args['max-errors'])) : undefined,
+ retentionDays: args['retention-days'] ? parseInt(String(args['retention-days'])) : undefined,
+ logRetentionHours: args['log-retention-hours'] ? parseInt(String(args['log-retention-hours'])) : undefined
+ });
+ break;
+
+ case 'stop':
+ if (!args['instance-id']) {
+ OutputFormatter.formatError('--instance-id is required for stop command');
+ process.exit(1);
+ }
+
+ await ProcessCommands.stop({
+ instanceId: String(args['instance-id']),
+ force: Boolean(args.force)
+ });
+ break;
+
+ case 'status':
+ await ProcessCommands.status({
+ instanceId: args['instance-id'] ? String(args['instance-id']) : undefined
+ });
+ break;
+
+ default:
+ OutputFormatter.formatError(`Unknown process subcommand: ${subcommand}`);
+ process.exit(1);
+ }
+}
+
+async function handleErrorCommand(subcommand: string, args: Record) {
+ switch (subcommand) {
+ case 'list':
+ if (!args['instance-id']) {
+ OutputFormatter.formatError('--instance-id is required for list command');
+ process.exit(1);
+ }
+
+ await ErrorCommands.list({
+ instanceId: String(args['instance-id']),
+ minLevel: args['min-level'] ? parseInt(String(args['min-level'])) : undefined,
+ maxLevel: args['max-level'] ? parseInt(String(args['max-level'])) : undefined,
+ since: args.since ? String(args.since) : undefined,
+ until: args.until ? String(args.until) : undefined,
+ limit: args.limit ? parseInt(String(args.limit)) : undefined,
+ offset: args.offset ? parseInt(String(args.offset)) : undefined,
+ format: args.format as 'json' | 'table' | 'raw',
+ dbPath: args['db-path'] ? String(args['db-path']) : undefined,
+ reset: Boolean(args.reset)
+ });
+ break;
+
+ case 'stats':
+ if (!args['instance-id']) {
+ OutputFormatter.formatError('--instance-id is required for stats command');
+ process.exit(1);
+ }
+
+ await ErrorCommands.stats({
+ instanceId: String(args['instance-id']),
+ dbPath: args['db-path'] ? String(args['db-path']) : undefined
+ });
+ break;
+
+ case 'clear':
+ if (!args['instance-id']) {
+ OutputFormatter.formatError('--instance-id is required for clear command');
+ process.exit(1);
+ }
+
+ await ErrorCommands.clear({
+ instanceId: String(args['instance-id']),
+ confirm: Boolean(args.confirm),
+ dbPath: args['db-path'] ? String(args['db-path']) : undefined
+ });
+ break;
+
+ default:
+ OutputFormatter.formatError(`Unknown error subcommand: ${subcommand}`);
+ process.exit(1);
+ }
+}
+
+async function handleLogCommand(subcommand: string, args: Record) {
+ switch (subcommand) {
+ case 'list':
+ if (!args['instance-id']) {
+ OutputFormatter.formatError('--instance-id is required for list command');
+ process.exit(1);
+ }
+
+ await LogCommands.list({
+ instanceId: String(args['instance-id']),
+ levels: args.levels ? String(args.levels).split(',') as LogLevel[] : undefined,
+ streams: args.streams ? String(args.streams).split(',') as ('stdout' | 'stderr')[] : undefined,
+ since: args.since ? String(args.since) : undefined,
+ until: args.until ? String(args.until) : undefined,
+ limit: args.limit ? parseInt(String(args.limit)) : undefined,
+ offset: args.offset ? parseInt(String(args.offset)) : undefined,
+ format: args.format as 'json' | 'table' | 'raw',
+ dbPath: args['db-path'] ? String(args['db-path']) : undefined
+ });
+ break;
+
+ case 'get':
+ if (!args['instance-id']) {
+ OutputFormatter.formatError('--instance-id is required for get command');
+ process.exit(1);
+ }
+
+ await LogCommands.get({
+ instanceId: String(args['instance-id']),
+ format: args.format as 'json' | 'raw',
+ reset: Boolean(args.reset)
+ });
+ break;
+
+ case 'stats':
+ if (!args['instance-id']) {
+ OutputFormatter.formatError('--instance-id is required for stats command');
+ process.exit(1);
+ }
+
+ await LogCommands.stats({
+ instanceId: String(args['instance-id']),
+ dbPath: args['db-path'] ? String(args['db-path']) : undefined
+ });
+ break;
+
+ case 'clear':
+ if (!args['instance-id']) {
+ OutputFormatter.formatError('--instance-id is required for clear command');
+ process.exit(1);
+ }
+
+ await LogCommands.clear({
+ instanceId: String(args['instance-id']),
+ confirm: Boolean(args.confirm),
+ dbPath: args['db-path'] ? String(args['db-path']) : undefined
+ });
+ break;
+
+ default:
+ OutputFormatter.formatError(`Unknown log subcommand: ${subcommand}`);
+ process.exit(1);
+ }
+}
+
+// Run CLI if this file is executed directly
+if (process.argv[1] && process.argv[1].endsWith('cli-tools.ts')) {
+ main().catch(error => {
+ OutputFormatter.formatError(`Unhandled error: ${error instanceof Error ? error.message : 'Unknown error'}`);
+ process.exit(1);
+ });
+}
+
+export { ProcessCommands, ErrorCommands, LogCommands, OutputFormatter };
diff --git a/external/drpower-vibe-production/container/example-usage.sh b/external/drpower-vibe-production/container/example-usage.sh
new file mode 100755
index 00000000..020c92dd
--- /dev/null
+++ b/external/drpower-vibe-production/container/example-usage.sh
@@ -0,0 +1,64 @@
+#!/bin/bash
+
+# Example usage of CLI tools outside Docker environment
+# This script demonstrates how to use the CLI tools in any directory
+
+echo "=== CLI Tools Configuration Example ==="
+echo
+
+# Set custom data directory (optional)
+export CLI_DATA_DIR="./monitoring-data"
+
+# Set custom database paths (optional)
+export CLI_ERROR_DB_PATH="./monitoring-data/runtime-errors.db"
+export CLI_LOG_DB_PATH="./monitoring-data/process-logs.db"
+
+echo "Data directory: $CLI_DATA_DIR"
+echo "Error database: $CLI_ERROR_DB_PATH"
+echo "Log database: $CLI_LOG_DB_PATH"
+echo
+
+# Example: Start monitoring a process
+echo "=== Starting Process Monitor ==="
+bun run cli-tools.ts process start --instance-id "my-app" --port 3000 -- npm run dev &
+MONITOR_PID=$!
+
+# Wait a bit for the process to start
+sleep 2
+
+# Example: Check process status
+echo "=== Process Status ==="
+bun run cli-tools.ts process status --instance-id "my-app"
+echo
+
+# Example: List recent errors
+echo "=== Recent Errors ==="
+bun run cli-tools.ts errors list --instance-id "my-app" --limit 10 --format table
+echo
+
+# Example: Get recent logs
+echo "=== Recent Logs ==="
+bun run cli-tools.ts logs get --instance-id "my-app" --format raw
+echo
+
+# Example: Get error statistics
+echo "=== Error Statistics ==="
+bun run cli-tools.ts errors stats --instance-id "my-app"
+echo
+
+# Example: Get log statistics
+echo "=== Log Statistics ==="
+bun run cli-tools.ts logs stats --instance-id "my-app"
+echo
+
+# Example: Get all logs and reset (useful for periodic log collection)
+echo "=== All Logs (and reset) ==="
+bun run cli-tools.ts logs get --instance-id "my-app" --format raw --reset > collected-logs.txt
+echo "Logs saved to collected-logs.txt"
+echo
+
+# Cleanup: Stop the monitor
+echo "=== Stopping Monitor ==="
+bun run cli-tools.ts process stop --instance-id "my-app"
+
+echo "=== Example Complete ==="
\ No newline at end of file
diff --git a/external/drpower-vibe-production/container/package.json b/external/drpower-vibe-production/container/package.json
new file mode 100644
index 00000000..8baa5387
--- /dev/null
+++ b/external/drpower-vibe-production/container/package.json
@@ -0,0 +1,19 @@
+{
+ "name": "process-monitoring-system",
+ "version": "2.0.0",
+ "description": "Unified process monitoring system for Cloudflare sandbox containers",
+ "main": "cli-tools.ts",
+ "scripts": {
+ "build": "bun build cli-tools.ts --outdir ./dist --target bun",
+ "start": "bun run cli-tools.ts process start",
+ "monitor": "bun run cli-tools.ts",
+ "errors": "bun run cli-tools.ts errors",
+ "logs": "bun run cli-tools.ts logs",
+ "test": "bun test",
+ "clean": "rm -f ./data/*.db"
+ },
+ "dependencies": {
+ "zod": "^3.22.4"
+ },
+ "type": "module"
+}
\ No newline at end of file
diff --git a/external/drpower-vibe-production/container/packages-cache/bun.lock b/external/drpower-vibe-production/container/packages-cache/bun.lock
new file mode 100644
index 00000000..f198b3cc
--- /dev/null
+++ b/external/drpower-vibe-production/container/packages-cache/bun.lock
@@ -0,0 +1,1711 @@
+{
+ "lockfileVersion": 1,
+ "workspaces": {
+ "": {
+ "name": "vite-cf-do-runner",
+ "dependencies": {
+ "@dnd-kit/core": "^6.3.1",
+ "@dnd-kit/sortable": "^10.0.0",
+ "@headlessui/react": "^2.2.4",
+ "@hookform/resolvers": "^5.1.1",
+ "@radix-ui/react-accordion": "^1.2.11",
+ "@radix-ui/react-alert-dialog": "^1.1.14",
+ "@radix-ui/react-aspect-ratio": "^1.1.7",
+ "@radix-ui/react-avatar": "^1.1.10",
+ "@radix-ui/react-checkbox": "^1.3.2",
+ "@radix-ui/react-collapsible": "^1.1.11",
+ "@radix-ui/react-context-menu": "^2.2.15",
+ "@radix-ui/react-dialog": "^1.1.14",
+ "@radix-ui/react-dropdown-menu": "^2.1.15",
+ "@radix-ui/react-hover-card": "^1.1.14",
+ "@radix-ui/react-label": "^2.1.7",
+ "@radix-ui/react-menubar": "^1.1.15",
+ "@radix-ui/react-navigation-menu": "^1.2.13",
+ "@radix-ui/react-popover": "^1.1.14",
+ "@radix-ui/react-progress": "^1.1.7",
+ "@radix-ui/react-radio-group": "^1.3.7",
+ "@radix-ui/react-scroll-area": "^1.2.9",
+ "@radix-ui/react-select": "^2.2.5",
+ "@radix-ui/react-separator": "^1.1.7",
+ "@radix-ui/react-slider": "^1.3.5",
+ "@radix-ui/react-slot": "^1.2.3",
+ "@radix-ui/react-switch": "^1.2.5",
+ "@radix-ui/react-tabs": "^1.1.12",
+ "@radix-ui/react-toast": "^1.2.14",
+ "@radix-ui/react-toggle": "^1.1.9",
+ "@radix-ui/react-toggle-group": "^1.1.10",
+ "@radix-ui/react-tooltip": "^1.2.7",
+ "@tanstack/react-query": "^5.83.0",
+ "@typescript-eslint/eslint-plugin": "^8.38.0",
+ "@typescript-eslint/parser": "^8.38.0",
+ "class-variance-authority": "^0.7.1",
+ "cloudflare": "^5.0.0",
+ "clsx": "^2.1.1",
+ "cmdk": "^1.1.1",
+ "date-fns": "^4.1.0",
+ "embla-carousel-react": "^8.6.0",
+ "eslint-import-resolver-typescript": "^4.4.4",
+ "eslint-plugin-import": "^2.32.0",
+ "framer-motion": "^12.23.0",
+ "hono": "^4.8.5",
+ "immer": "^10.1.1",
+ "input-otp": "^1.4.2",
+ "lucide-react": "^0.525.0",
+ "next-themes": "^0.4.6",
+ "pino": "^9.11.0",
+ "react": "^18.3.1",
+ "react-day-picker": "^9.8.0",
+ "react-dom": "^18.3.1",
+ "react-flow": "^1.0.3",
+ "react-hook-form": "^7.60.0",
+ "react-hotkeys-hook": "^5.1.0",
+ "react-resizable-panels": "^3.0.3",
+ "react-router-dom": "6.30.0",
+ "react-select": "^5.10.2",
+ "react-swipeable": "^7.0.2",
+ "react-use": "^17.6.0",
+ "recharts": "2.15.4",
+ "sonner": "^2.0.6",
+ "tailwind-merge": "^3.3.1",
+ "tailwindcss-animate": "^1.0.7",
+ "tw-animate-css": "^1.3.5",
+ "uuid": "^11.1.0",
+ "vaul": "^1.1.2",
+ "zod": "^4.0.5",
+ "zustand": "^5.0.6",
+ },
+ "devDependencies": {
+ "@cloudflare/vite-plugin": "^1.9.4",
+ "@cloudflare/workers-types": "^4.20250807.0",
+ "@eslint/js": "^9.22.0",
+ "@types/node": "^22.15.3",
+ "@types/react": "^18.3.1",
+ "@types/react-dom": "^18.3.1",
+ "@vitejs/plugin-react": "^4.3.4",
+ "autoprefixer": "^10.4.21",
+ "eslint": "^9.31.0",
+ "eslint-plugin-react-hooks": "^5.2.0",
+ "eslint-plugin-react-refresh": "^0.4.19",
+ "globals": "^16.0.0",
+ "postcss": "^8.5.3",
+ "tailwindcss": "^3.4.17",
+ "typescript": "5.8",
+ "typescript-eslint": "^8.26.1",
+ "vite": "^6.3.1",
+ },
+ },
+ },
+ "packages": {
+ "@alloc/quick-lru": ["@alloc/quick-lru@5.2.0", "", {}, "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw=="],
+
+ "@babel/code-frame": ["@babel/code-frame@7.27.1", "", { "dependencies": { "@babel/helper-validator-identifier": "^7.27.1", "js-tokens": "^4.0.0", "picocolors": "^1.1.1" } }, "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg=="],
+
+ "@babel/compat-data": ["@babel/compat-data@7.28.4", "", {}, "sha512-YsmSKC29MJwf0gF8Rjjrg5LQCmyh+j/nD8/eP7f+BeoQTKYqs9RoWbjGOdy0+1Ekr68RJZMUOPVQaQisnIo4Rw=="],
+
+ "@babel/core": ["@babel/core@7.28.4", "", { "dependencies": { "@babel/code-frame": "^7.27.1", "@babel/generator": "^7.28.3", "@babel/helper-compilation-targets": "^7.27.2", "@babel/helper-module-transforms": "^7.28.3", "@babel/helpers": "^7.28.4", "@babel/parser": "^7.28.4", "@babel/template": "^7.27.2", "@babel/traverse": "^7.28.4", "@babel/types": "^7.28.4", "@jridgewell/remapping": "^2.3.5", "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", "json5": "^2.2.3", "semver": "^6.3.1" } }, "sha512-2BCOP7TN8M+gVDj7/ht3hsaO/B/n5oDbiAyyvnRlNOs+u1o+JWNYTQrmpuNp1/Wq2gcFrI01JAW+paEKDMx/CA=="],
+
+ "@babel/generator": ["@babel/generator@7.28.3", "", { "dependencies": { "@babel/parser": "^7.28.3", "@babel/types": "^7.28.2", "@jridgewell/gen-mapping": "^0.3.12", "@jridgewell/trace-mapping": "^0.3.28", "jsesc": "^3.0.2" } }, "sha512-3lSpxGgvnmZznmBkCRnVREPUFJv2wrv9iAoFDvADJc0ypmdOxdUtcLeBgBJ6zE0PMeTKnxeQzyk0xTBq4Ep7zw=="],
+
+ "@babel/helper-compilation-targets": ["@babel/helper-compilation-targets@7.27.2", "", { "dependencies": { "@babel/compat-data": "^7.27.2", "@babel/helper-validator-option": "^7.27.1", "browserslist": "^4.24.0", "lru-cache": "^5.1.1", "semver": "^6.3.1" } }, "sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ=="],
+
+ "@babel/helper-globals": ["@babel/helper-globals@7.28.0", "", {}, "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw=="],
+
+ "@babel/helper-module-imports": ["@babel/helper-module-imports@7.27.1", "", { "dependencies": { "@babel/traverse": "^7.27.1", "@babel/types": "^7.27.1" } }, "sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w=="],
+
+ "@babel/helper-module-transforms": ["@babel/helper-module-transforms@7.28.3", "", { "dependencies": { "@babel/helper-module-imports": "^7.27.1", "@babel/helper-validator-identifier": "^7.27.1", "@babel/traverse": "^7.28.3" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-gytXUbs8k2sXS9PnQptz5o0QnpLL51SwASIORY6XaBKF88nsOT0Zw9szLqlSGQDP/4TljBAD5y98p2U1fqkdsw=="],
+
+ "@babel/helper-plugin-utils": ["@babel/helper-plugin-utils@7.27.1", "", {}, "sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw=="],
+
+ "@babel/helper-string-parser": ["@babel/helper-string-parser@7.27.1", "", {}, "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA=="],
+
+ "@babel/helper-validator-identifier": ["@babel/helper-validator-identifier@7.27.1", "", {}, "sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow=="],
+
+ "@babel/helper-validator-option": ["@babel/helper-validator-option@7.27.1", "", {}, "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg=="],
+
+ "@babel/helpers": ["@babel/helpers@7.28.4", "", { "dependencies": { "@babel/template": "^7.27.2", "@babel/types": "^7.28.4" } }, "sha512-HFN59MmQXGHVyYadKLVumYsA9dBFun/ldYxipEjzA4196jpLZd8UjEEBLkbEkvfYreDqJhZxYAWFPtrfhNpj4w=="],
+
+ "@babel/parser": ["@babel/parser@7.28.4", "", { "dependencies": { "@babel/types": "^7.28.4" }, "bin": "./bin/babel-parser.js" }, "sha512-yZbBqeM6TkpP9du/I2pUZnJsRMGGvOuIrhjzC1AwHwW+6he4mni6Bp/m8ijn0iOuZuPI2BfkCoSRunpyjnrQKg=="],
+
+ "@babel/plugin-transform-react-jsx-self": ["@babel/plugin-transform-react-jsx-self@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-6UzkCs+ejGdZ5mFFC/OCUrv028ab2fp1znZmCZjAOBKiBK2jXD1O+BPSfX8X2qjJ75fZBMSnQn3Rq2mrBJK2mw=="],
+
+ "@babel/plugin-transform-react-jsx-source": ["@babel/plugin-transform-react-jsx-source@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-zbwoTsBruTeKB9hSq73ha66iFeJHuaFkUbwvqElnygoNbj/jHRsSeokowZFN3CZ64IvEqcmmkVe89OPXc7ldAw=="],
+
+ "@babel/runtime": ["@babel/runtime@7.28.4", "", {}, "sha512-Q/N6JNWvIvPnLDvjlE1OUBLPQHH6l3CltCEsHIujp45zQUSSh8K+gHnaEX45yAT1nyngnINhvWtzN+Nb9D8RAQ=="],
+
+ "@babel/template": ["@babel/template@7.27.2", "", { "dependencies": { "@babel/code-frame": "^7.27.1", "@babel/parser": "^7.27.2", "@babel/types": "^7.27.1" } }, "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw=="],
+
+ "@babel/traverse": ["@babel/traverse@7.28.4", "", { "dependencies": { "@babel/code-frame": "^7.27.1", "@babel/generator": "^7.28.3", "@babel/helper-globals": "^7.28.0", "@babel/parser": "^7.28.4", "@babel/template": "^7.27.2", "@babel/types": "^7.28.4", "debug": "^4.3.1" } }, "sha512-YEzuboP2qvQavAcjgQNVgsvHIDv6ZpwXvcvjmyySP2DIMuByS/6ioU5G9pYrWHM6T2YDfc7xga9iNzYOs12CFQ=="],
+
+ "@babel/types": ["@babel/types@7.28.4", "", { "dependencies": { "@babel/helper-string-parser": "^7.27.1", "@babel/helper-validator-identifier": "^7.27.1" } }, "sha512-bkFqkLhh3pMBUQQkpVgWDWq/lqzc2678eUyDlTBhRqhCHFguYYGM0Efga7tYk4TogG/3x0EEl66/OQ+WGbWB/Q=="],
+
+ "@cloudflare/kv-asset-handler": ["@cloudflare/kv-asset-handler@0.4.0", "", { "dependencies": { "mime": "^3.0.0" } }, "sha512-+tv3z+SPp+gqTIcImN9o0hqE9xyfQjI1XD9pL6NuKjua9B1y7mNYv0S9cP+QEbA4ppVgGZEmKOvHX5G5Ei1CVA=="],
+
+ "@cloudflare/unenv-preset": ["@cloudflare/unenv-preset@2.7.3", "", { "peerDependencies": { "unenv": "2.0.0-rc.21", "workerd": "^1.20250828.1" }, "optionalPeers": ["workerd"] }, "sha512-tsQQagBKjvpd9baa6nWVIv399ejiqcrUBBW6SZx6Z22+ymm+Odv5+cFimyuCsD/fC1fQTwfRmwXBNpzvHSeGCw=="],
+
+ "@cloudflare/vite-plugin": ["@cloudflare/vite-plugin@1.12.4", "", { "dependencies": { "@cloudflare/unenv-preset": "2.7.3", "@remix-run/node-fetch-server": "^0.8.0", "get-port": "^7.1.0", "miniflare": "4.20250906.0", "picocolors": "^1.1.1", "tinyglobby": "^0.2.12", "unenv": "2.0.0-rc.21", "wrangler": "4.35.0", "ws": "8.18.0" }, "peerDependencies": { "vite": "^6.1.0 || ^7.0.0" } }, "sha512-hyvTk/uBk5g9nUFwmjLzSyE+1/WABGulbAA6QXK1wDY5/Xxz2w59yKl2vNV2R7/+/jsHC29n0DKEA4pb/QTKPQ=="],
+
+ "@cloudflare/workerd-darwin-64": ["@cloudflare/workerd-darwin-64@1.20250906.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-E+X/YYH9BmX0ew2j/mAWFif2z05NMNuhCTlNYEGLkqMe99K15UewBqajL9pMcMUKxylnlrEoK3VNxl33DkbnPA=="],
+
+ "@cloudflare/workerd-darwin-arm64": ["@cloudflare/workerd-darwin-arm64@1.20250906.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-X5apsZ1SFW4FYTM19ISHf8005FJMPfrcf4U5rO0tdj+TeJgQgXuZ57IG0WeW7SpLVeBo8hM6WC8CovZh41AfnA=="],
+
+ "@cloudflare/workerd-linux-64": ["@cloudflare/workerd-linux-64@1.20250906.0", "", { "os": "linux", "cpu": "x64" }, "sha512-rlKzWgsLnlQ5Nt9W69YBJKcmTmZbOGu0edUsenXPmc6wzULUxoQpi7ZE9k3TfTonJx4WoQsQlzCUamRYFsX+0Q=="],
+
+ "@cloudflare/workerd-linux-arm64": ["@cloudflare/workerd-linux-arm64@1.20250906.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-DdedhiQ+SeLzpg7BpcLrIPEZ33QKioJQ1wvL4X7nuLzEB9rWzS37NNNahQzc1+44rhG4fyiHbXBPOeox4B9XVA=="],
+
+ "@cloudflare/workerd-windows-64": ["@cloudflare/workerd-windows-64@1.20250906.0", "", { "os": "win32", "cpu": "x64" }, "sha512-Q8Qjfs8jGVILnZL6vUpQ90q/8MTCYaGR3d1LGxZMBqte8Vr7xF3KFHPEy7tFs0j0mMjnqCYzlofmPNY+9ZaDRg=="],
+
+ "@cloudflare/workers-types": ["@cloudflare/workers-types@4.20250912.0", "", {}, "sha512-1IGAf2zlxBPW1pyf0g7f87xCL+CGPwmdtrteqHX3Cr+lBXKKIoLD8ctWdAs3Fi6rUeS5inAwttSQFscR9euLkA=="],
+
+ "@cspotcode/source-map-support": ["@cspotcode/source-map-support@0.8.1", "", { "dependencies": { "@jridgewell/trace-mapping": "0.3.9" } }, "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw=="],
+
+ "@date-fns/tz": ["@date-fns/tz@1.4.1", "", {}, "sha512-P5LUNhtbj6YfI3iJjw5EL9eUAG6OitD0W3fWQcpQjDRc/QIsL0tRNuO1PcDvPccWL1fSTXXdE1ds+l95DV/OFA=="],
+
+ "@dnd-kit/accessibility": ["@dnd-kit/accessibility@3.1.1", "", { "dependencies": { "tslib": "^2.0.0" }, "peerDependencies": { "react": ">=16.8.0" } }, "sha512-2P+YgaXF+gRsIihwwY1gCsQSYnu9Zyj2py8kY5fFvUM1qm2WA2u639R6YNVfU4GWr+ZM5mqEsfHZZLoRONbemw=="],
+
+ "@dnd-kit/core": ["@dnd-kit/core@6.3.1", "", { "dependencies": { "@dnd-kit/accessibility": "^3.1.1", "@dnd-kit/utilities": "^3.2.2", "tslib": "^2.0.0" }, "peerDependencies": { "react": ">=16.8.0", "react-dom": ">=16.8.0" } }, "sha512-xkGBRQQab4RLwgXxoqETICr6S5JlogafbhNsidmrkVv2YRs5MLwpjoF2qpiGjQt8S9AoxtIV603s0GIUpY5eYQ=="],
+
+ "@dnd-kit/sortable": ["@dnd-kit/sortable@10.0.0", "", { "dependencies": { "@dnd-kit/utilities": "^3.2.2", "tslib": "^2.0.0" }, "peerDependencies": { "@dnd-kit/core": "^6.3.0", "react": ">=16.8.0" } }, "sha512-+xqhmIIzvAYMGfBYYnbKuNicfSsk4RksY2XdmJhT+HAC01nix6fHCztU68jooFiMUB01Ky3F0FyOvhG/BZrWkg=="],
+
+ "@dnd-kit/utilities": ["@dnd-kit/utilities@3.2.2", "", { "dependencies": { "tslib": "^2.0.0" }, "peerDependencies": { "react": ">=16.8.0" } }, "sha512-+MKAJEOfaBe5SmV6t34p80MMKhjvUz0vRrvVJbPT0WElzaOJ/1xs+D+KDv+tD/NE5ujfrChEcshd4fLn0wpiqg=="],
+
+ "@emnapi/core": ["@emnapi/core@1.5.0", "", { "dependencies": { "@emnapi/wasi-threads": "1.1.0", "tslib": "^2.4.0" } }, "sha512-sbP8GzB1WDzacS8fgNPpHlp6C9VZe+SJP3F90W9rLemaQj2PzIuTEl1qDOYQf58YIpyjViI24y9aPWCjEzY2cg=="],
+
+ "@emnapi/runtime": ["@emnapi/runtime@1.5.0", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-97/BJ3iXHww3djw6hYIfErCZFee7qCtrneuLa20UXFCOTCfBM2cvQHjWJ2EG0s0MtdNwInarqCTz35i4wWXHsQ=="],
+
+ "@emnapi/wasi-threads": ["@emnapi/wasi-threads@1.1.0", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-WI0DdZ8xFSbgMjR1sFsKABJ/C5OnRrjT06JXbZKexJGrDuPTzZdDYfFlsgcCXCyf+suG5QU2e/y1Wo2V/OapLQ=="],
+
+ "@emotion/babel-plugin": ["@emotion/babel-plugin@11.13.5", "", { "dependencies": { "@babel/helper-module-imports": "^7.16.7", "@babel/runtime": "^7.18.3", "@emotion/hash": "^0.9.2", "@emotion/memoize": "^0.9.0", "@emotion/serialize": "^1.3.3", "babel-plugin-macros": "^3.1.0", "convert-source-map": "^1.5.0", "escape-string-regexp": "^4.0.0", "find-root": "^1.1.0", "source-map": "^0.5.7", "stylis": "4.2.0" } }, "sha512-pxHCpT2ex+0q+HH91/zsdHkw/lXd468DIN2zvfvLtPKLLMo6gQj7oLObq8PhkrxOZb/gGCq03S3Z7PDhS8pduQ=="],
+
+ "@emotion/cache": ["@emotion/cache@11.14.0", "", { "dependencies": { "@emotion/memoize": "^0.9.0", "@emotion/sheet": "^1.4.0", "@emotion/utils": "^1.4.2", "@emotion/weak-memoize": "^0.4.0", "stylis": "4.2.0" } }, "sha512-L/B1lc/TViYk4DcpGxtAVbx0ZyiKM5ktoIyafGkH6zg/tj+mA+NE//aPYKG0k8kCHSHVJrpLpcAlOBEXQ3SavA=="],
+
+ "@emotion/hash": ["@emotion/hash@0.9.2", "", {}, "sha512-MyqliTZGuOm3+5ZRSaaBGP3USLw6+EGykkwZns2EPC5g8jJ4z9OrdZY9apkl3+UP9+sdz76YYkwCKP5gh8iY3g=="],
+
+ "@emotion/memoize": ["@emotion/memoize@0.9.0", "", {}, "sha512-30FAj7/EoJ5mwVPOWhAyCX+FPfMDrVecJAM+Iw9NRoSl4BBAQeqj4cApHHUXOVvIPgLVDsCFoz/hGD+5QQD1GQ=="],
+
+ "@emotion/react": ["@emotion/react@11.14.0", "", { "dependencies": { "@babel/runtime": "^7.18.3", "@emotion/babel-plugin": "^11.13.5", "@emotion/cache": "^11.14.0", "@emotion/serialize": "^1.3.3", "@emotion/use-insertion-effect-with-fallbacks": "^1.2.0", "@emotion/utils": "^1.4.2", "@emotion/weak-memoize": "^0.4.0", "hoist-non-react-statics": "^3.3.1" }, "peerDependencies": { "react": ">=16.8.0" } }, "sha512-O000MLDBDdk/EohJPFUqvnp4qnHeYkVP5B0xEG0D/L7cOKP9kefu2DXn8dj74cQfsEzUqh+sr1RzFqiL1o+PpA=="],
+
+ "@emotion/serialize": ["@emotion/serialize@1.3.3", "", { "dependencies": { "@emotion/hash": "^0.9.2", "@emotion/memoize": "^0.9.0", "@emotion/unitless": "^0.10.0", "@emotion/utils": "^1.4.2", "csstype": "^3.0.2" } }, "sha512-EISGqt7sSNWHGI76hC7x1CksiXPahbxEOrC5RjmFRJTqLyEK9/9hZvBbiYn70dw4wuwMKiEMCUlR6ZXTSWQqxA=="],
+
+ "@emotion/sheet": ["@emotion/sheet@1.4.0", "", {}, "sha512-fTBW9/8r2w3dXWYM4HCB1Rdp8NLibOw2+XELH5m5+AkWiL/KqYX6dc0kKYlaYyKjrQ6ds33MCdMPEwgs2z1rqg=="],
+
+ "@emotion/unitless": ["@emotion/unitless@0.10.0", "", {}, "sha512-dFoMUuQA20zvtVTuxZww6OHoJYgrzfKM1t52mVySDJnMSEa08ruEvdYQbhvyu6soU+NeLVd3yKfTfT0NeV6qGg=="],
+
+ "@emotion/use-insertion-effect-with-fallbacks": ["@emotion/use-insertion-effect-with-fallbacks@1.2.0", "", { "peerDependencies": { "react": ">=16.8.0" } }, "sha512-yJMtVdH59sxi/aVJBpk9FQq+OR8ll5GT8oWd57UpeaKEVGab41JWaCFA7FRLoMLloOZF/c/wsPoe+bfGmRKgDg=="],
+
+ "@emotion/utils": ["@emotion/utils@1.4.2", "", {}, "sha512-3vLclRofFziIa3J2wDh9jjbkUz9qk5Vi3IZ/FSTKViB0k+ef0fPV7dYrUIugbgupYDx7v9ud/SjrtEP8Y4xLoA=="],
+
+ "@emotion/weak-memoize": ["@emotion/weak-memoize@0.4.0", "", {}, "sha512-snKqtPW01tN0ui7yu9rGv69aJXr/a/Ywvl11sUjNtEcRc+ng/mQriFL0wLXMef74iHa/EkftbDzU9F8iFbH+zg=="],
+
+ "@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.25.9", "", { "os": "aix", "cpu": "ppc64" }, "sha512-OaGtL73Jck6pBKjNIe24BnFE6agGl+6KxDtTfHhy1HmhthfKouEcOhqpSL64K4/0WCtbKFLOdzD/44cJ4k9opA=="],
+
+ "@esbuild/android-arm": ["@esbuild/android-arm@0.25.9", "", { "os": "android", "cpu": "arm" }, "sha512-5WNI1DaMtxQ7t7B6xa572XMXpHAaI/9Hnhk8lcxF4zVN4xstUgTlvuGDorBguKEnZO70qwEcLpfifMLoxiPqHQ=="],
+
+ "@esbuild/android-arm64": ["@esbuild/android-arm64@0.25.9", "", { "os": "android", "cpu": "arm64" }, "sha512-IDrddSmpSv51ftWslJMvl3Q2ZT98fUSL2/rlUXuVqRXHCs5EUF1/f+jbjF5+NG9UffUDMCiTyh8iec7u8RlTLg=="],
+
+ "@esbuild/android-x64": ["@esbuild/android-x64@0.25.9", "", { "os": "android", "cpu": "x64" }, "sha512-I853iMZ1hWZdNllhVZKm34f4wErd4lMyeV7BLzEExGEIZYsOzqDWDf+y082izYUE8gtJnYHdeDpN/6tUdwvfiw=="],
+
+ "@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.25.9", "", { "os": "darwin", "cpu": "arm64" }, "sha512-XIpIDMAjOELi/9PB30vEbVMs3GV1v2zkkPnuyRRURbhqjyzIINwj+nbQATh4H9GxUgH1kFsEyQMxwiLFKUS6Rg=="],
+
+ "@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.25.9", "", { "os": "darwin", "cpu": "x64" }, "sha512-jhHfBzjYTA1IQu8VyrjCX4ApJDnH+ez+IYVEoJHeqJm9VhG9Dh2BYaJritkYK3vMaXrf7Ogr/0MQ8/MeIefsPQ=="],
+
+ "@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.25.9", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-z93DmbnY6fX9+KdD4Ue/H6sYs+bhFQJNCPZsi4XWJoYblUqT06MQUdBCpcSfuiN72AbqeBFu5LVQTjfXDE2A6Q=="],
+
+ "@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.25.9", "", { "os": "freebsd", "cpu": "x64" }, "sha512-mrKX6H/vOyo5v71YfXWJxLVxgy1kyt1MQaD8wZJgJfG4gq4DpQGpgTB74e5yBeQdyMTbgxp0YtNj7NuHN0PoZg=="],
+
+ "@esbuild/linux-arm": ["@esbuild/linux-arm@0.25.9", "", { "os": "linux", "cpu": "arm" }, "sha512-HBU2Xv78SMgaydBmdor38lg8YDnFKSARg1Q6AT0/y2ezUAKiZvc211RDFHlEZRFNRVhcMamiToo7bDx3VEOYQw=="],
+
+ "@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.25.9", "", { "os": "linux", "cpu": "arm64" }, "sha512-BlB7bIcLT3G26urh5Dmse7fiLmLXnRlopw4s8DalgZ8ef79Jj4aUcYbk90g8iCa2467HX8SAIidbL7gsqXHdRw=="],
+
+ "@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.25.9", "", { "os": "linux", "cpu": "ia32" }, "sha512-e7S3MOJPZGp2QW6AK6+Ly81rC7oOSerQ+P8L0ta4FhVi+/j/v2yZzx5CqqDaWjtPFfYz21Vi1S0auHrap3Ma3A=="],
+
+ "@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.25.9", "", { "os": "linux", "cpu": "none" }, "sha512-Sbe10Bnn0oUAB2AalYztvGcK+o6YFFA/9829PhOCUS9vkJElXGdphz0A3DbMdP8gmKkqPmPcMJmJOrI3VYB1JQ=="],
+
+ "@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.25.9", "", { "os": "linux", "cpu": "none" }, "sha512-YcM5br0mVyZw2jcQeLIkhWtKPeVfAerES5PvOzaDxVtIyZ2NUBZKNLjC5z3/fUlDgT6w89VsxP2qzNipOaaDyA=="],
+
+ "@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.25.9", "", { "os": "linux", "cpu": "ppc64" }, "sha512-++0HQvasdo20JytyDpFvQtNrEsAgNG2CY1CLMwGXfFTKGBGQT3bOeLSYE2l1fYdvML5KUuwn9Z8L1EWe2tzs1w=="],
+
+ "@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.25.9", "", { "os": "linux", "cpu": "none" }, "sha512-uNIBa279Y3fkjV+2cUjx36xkx7eSjb8IvnL01eXUKXez/CBHNRw5ekCGMPM0BcmqBxBcdgUWuUXmVWwm4CH9kg=="],
+
+ "@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.25.9", "", { "os": "linux", "cpu": "s390x" }, "sha512-Mfiphvp3MjC/lctb+7D287Xw1DGzqJPb/J2aHHcHxflUo+8tmN/6d4k6I2yFR7BVo5/g7x2Monq4+Yew0EHRIA=="],
+
+ "@esbuild/linux-x64": ["@esbuild/linux-x64@0.25.9", "", { "os": "linux", "cpu": "x64" }, "sha512-iSwByxzRe48YVkmpbgoxVzn76BXjlYFXC7NvLYq+b+kDjyyk30J0JY47DIn8z1MO3K0oSl9fZoRmZPQI4Hklzg=="],
+
+ "@esbuild/netbsd-arm64": ["@esbuild/netbsd-arm64@0.25.9", "", { "os": "none", "cpu": "arm64" }, "sha512-9jNJl6FqaUG+COdQMjSCGW4QiMHH88xWbvZ+kRVblZsWrkXlABuGdFJ1E9L7HK+T0Yqd4akKNa/lO0+jDxQD4Q=="],
+
+ "@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.25.9", "", { "os": "none", "cpu": "x64" }, "sha512-RLLdkflmqRG8KanPGOU7Rpg829ZHu8nFy5Pqdi9U01VYtG9Y0zOG6Vr2z4/S+/3zIyOxiK6cCeYNWOFR9QP87g=="],
+
+ "@esbuild/openbsd-arm64": ["@esbuild/openbsd-arm64@0.25.9", "", { "os": "openbsd", "cpu": "arm64" }, "sha512-YaFBlPGeDasft5IIM+CQAhJAqS3St3nJzDEgsgFixcfZeyGPCd6eJBWzke5piZuZ7CtL656eOSYKk4Ls2C0FRQ=="],
+
+ "@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.25.9", "", { "os": "openbsd", "cpu": "x64" }, "sha512-1MkgTCuvMGWuqVtAvkpkXFmtL8XhWy+j4jaSO2wxfJtilVCi0ZE37b8uOdMItIHz4I6z1bWWtEX4CJwcKYLcuA=="],
+
+ "@esbuild/openharmony-arm64": ["@esbuild/openharmony-arm64@0.25.9", "", { "os": "none", "cpu": "arm64" }, "sha512-4Xd0xNiMVXKh6Fa7HEJQbrpP3m3DDn43jKxMjxLLRjWnRsfxjORYJlXPO4JNcXtOyfajXorRKY9NkOpTHptErg=="],
+
+ "@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.25.9", "", { "os": "sunos", "cpu": "x64" }, "sha512-WjH4s6hzo00nNezhp3wFIAfmGZ8U7KtrJNlFMRKxiI9mxEK1scOMAaa9i4crUtu+tBr+0IN6JCuAcSBJZfnphw=="],
+
+ "@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.25.9", "", { "os": "win32", "cpu": "arm64" }, "sha512-mGFrVJHmZiRqmP8xFOc6b84/7xa5y5YvR1x8djzXpJBSv/UsNK6aqec+6JDjConTgvvQefdGhFDAs2DLAds6gQ=="],
+
+ "@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.25.9", "", { "os": "win32", "cpu": "ia32" }, "sha512-b33gLVU2k11nVx1OhX3C8QQP6UHQK4ZtN56oFWvVXvz2VkDoe6fbG8TOgHFxEvqeqohmRnIHe5A1+HADk4OQww=="],
+
+ "@esbuild/win32-x64": ["@esbuild/win32-x64@0.25.9", "", { "os": "win32", "cpu": "x64" }, "sha512-PPOl1mi6lpLNQxnGoyAfschAodRFYXJ+9fs6WHXz7CSWKbOqiMZsubC+BQsVKuul+3vKLuwTHsS2c2y9EoKwxQ=="],
+
+ "@eslint-community/eslint-utils": ["@eslint-community/eslint-utils@4.9.0", "", { "dependencies": { "eslint-visitor-keys": "^3.4.3" }, "peerDependencies": { "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" } }, "sha512-ayVFHdtZ+hsq1t2Dy24wCmGXGe4q9Gu3smhLYALJrr473ZH27MsnSL+LKUlimp4BWJqMDMLmPpx/Q9R3OAlL4g=="],
+
+ "@eslint-community/regexpp": ["@eslint-community/regexpp@4.12.1", "", {}, "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ=="],
+
+ "@eslint/config-array": ["@eslint/config-array@0.21.0", "", { "dependencies": { "@eslint/object-schema": "^2.1.6", "debug": "^4.3.1", "minimatch": "^3.1.2" } }, "sha512-ENIdc4iLu0d93HeYirvKmrzshzofPw6VkZRKQGe9Nv46ZnWUzcF1xV01dcvEg/1wXUR61OmmlSfyeyO7EvjLxQ=="],
+
+ "@eslint/config-helpers": ["@eslint/config-helpers@0.3.1", "", {}, "sha512-xR93k9WhrDYpXHORXpxVL5oHj3Era7wo6k/Wd8/IsQNnZUTzkGS29lyn3nAT05v6ltUuTFVCCYDEGfy2Or/sPA=="],
+
+ "@eslint/core": ["@eslint/core@0.15.2", "", { "dependencies": { "@types/json-schema": "^7.0.15" } }, "sha512-78Md3/Rrxh83gCxoUc0EiciuOHsIITzLy53m3d9UyiW8y9Dj2D29FeETqyKA+BRK76tnTp6RXWb3pCay8Oyomg=="],
+
+ "@eslint/eslintrc": ["@eslint/eslintrc@3.3.1", "", { "dependencies": { "ajv": "^6.12.4", "debug": "^4.3.2", "espree": "^10.0.1", "globals": "^14.0.0", "ignore": "^5.2.0", "import-fresh": "^3.2.1", "js-yaml": "^4.1.0", "minimatch": "^3.1.2", "strip-json-comments": "^3.1.1" } }, "sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ=="],
+
+ "@eslint/js": ["@eslint/js@9.35.0", "", {}, "sha512-30iXE9whjlILfWobBkNerJo+TXYsgVM5ERQwMcMKCHckHflCmf7wXDAHlARoWnh0s1U72WqlbeyE7iAcCzuCPw=="],
+
+ "@eslint/object-schema": ["@eslint/object-schema@2.1.6", "", {}, "sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA=="],
+
+ "@eslint/plugin-kit": ["@eslint/plugin-kit@0.3.5", "", { "dependencies": { "@eslint/core": "^0.15.2", "levn": "^0.4.1" } }, "sha512-Z5kJ+wU3oA7MMIqVR9tyZRtjYPr4OC004Q4Rw7pgOKUOKkJfZ3O24nz3WYfGRpMDNmcOi3TwQOmgm7B7Tpii0w=="],
+
+ "@floating-ui/core": ["@floating-ui/core@1.7.3", "", { "dependencies": { "@floating-ui/utils": "^0.2.10" } }, "sha512-sGnvb5dmrJaKEZ+LDIpguvdX3bDlEllmv4/ClQ9awcmCZrlx5jQyyMWFM5kBI+EyNOCDDiKk8il0zeuX3Zlg/w=="],
+
+ "@floating-ui/dom": ["@floating-ui/dom@1.7.4", "", { "dependencies": { "@floating-ui/core": "^1.7.3", "@floating-ui/utils": "^0.2.10" } }, "sha512-OOchDgh4F2CchOX94cRVqhvy7b3AFb+/rQXyswmzmGakRfkMgoWVjfnLWkRirfLEfuD4ysVW16eXzwt3jHIzKA=="],
+
+ "@floating-ui/react": ["@floating-ui/react@0.26.28", "", { "dependencies": { "@floating-ui/react-dom": "^2.1.2", "@floating-ui/utils": "^0.2.8", "tabbable": "^6.0.0" }, "peerDependencies": { "react": ">=16.8.0", "react-dom": ">=16.8.0" } }, "sha512-yORQuuAtVpiRjpMhdc0wJj06b9JFjrYF4qp96j++v2NBpbi6SEGF7donUJ3TMieerQ6qVkAv1tgr7L4r5roTqw=="],
+
+ "@floating-ui/react-dom": ["@floating-ui/react-dom@2.1.6", "", { "dependencies": { "@floating-ui/dom": "^1.7.4" }, "peerDependencies": { "react": ">=16.8.0", "react-dom": ">=16.8.0" } }, "sha512-4JX6rEatQEvlmgU80wZyq9RT96HZJa88q8hp0pBd+LrczeDI4o6uA2M+uvxngVHo4Ihr8uibXxH6+70zhAFrVw=="],
+
+ "@floating-ui/utils": ["@floating-ui/utils@0.2.10", "", {}, "sha512-aGTxbpbg8/b5JfU1HXSrbH3wXZuLPJcNEcZQFMxLs3oSzgtVu6nFPkbbGGUvBcUjKV2YyB9Wxxabo+HEH9tcRQ=="],
+
+ "@headlessui/react": ["@headlessui/react@2.2.7", "", { "dependencies": { "@floating-ui/react": "^0.26.16", "@react-aria/focus": "^3.20.2", "@react-aria/interactions": "^3.25.0", "@tanstack/react-virtual": "^3.13.9", "use-sync-external-store": "^1.5.0" }, "peerDependencies": { "react": "^18 || ^19 || ^19.0.0-rc", "react-dom": "^18 || ^19 || ^19.0.0-rc" } }, "sha512-WKdTymY8Y49H8/gUc/lIyYK1M+/6dq0Iywh4zTZVAaiTDprRfioxSgD0wnXTQTBpjpGJuTL1NO/mqEvc//5SSg=="],
+
+ "@hookform/resolvers": ["@hookform/resolvers@5.2.1", "", { "dependencies": { "@standard-schema/utils": "^0.3.0" }, "peerDependencies": { "react-hook-form": "^7.55.0" } }, "sha512-u0+6X58gkjMcxur1wRWokA7XsiiBJ6aK17aPZxhkoYiK5J+HcTx0Vhu9ovXe6H+dVpO6cjrn2FkJTryXEMlryQ=="],
+
+ "@humanfs/core": ["@humanfs/core@0.19.1", "", {}, "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA=="],
+
+ "@humanfs/node": ["@humanfs/node@0.16.7", "", { "dependencies": { "@humanfs/core": "^0.19.1", "@humanwhocodes/retry": "^0.4.0" } }, "sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ=="],
+
+ "@humanwhocodes/module-importer": ["@humanwhocodes/module-importer@1.0.1", "", {}, "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA=="],
+
+ "@humanwhocodes/retry": ["@humanwhocodes/retry@0.4.3", "", {}, "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ=="],
+
+ "@img/sharp-darwin-arm64": ["@img/sharp-darwin-arm64@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-darwin-arm64": "1.0.4" }, "os": "darwin", "cpu": "arm64" }, "sha512-UT4p+iz/2H4twwAoLCqfA9UH5pI6DggwKEGuaPy7nCVQ8ZsiY5PIcrRvD1DzuY3qYL07NtIQcWnBSY/heikIFQ=="],
+
+ "@img/sharp-darwin-x64": ["@img/sharp-darwin-x64@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-darwin-x64": "1.0.4" }, "os": "darwin", "cpu": "x64" }, "sha512-fyHac4jIc1ANYGRDxtiqelIbdWkIuQaI84Mv45KvGRRxSAa7o7d1ZKAOBaYbnepLC1WqxfpimdeWfvqqSGwR2Q=="],
+
+ "@img/sharp-libvips-darwin-arm64": ["@img/sharp-libvips-darwin-arm64@1.0.4", "", { "os": "darwin", "cpu": "arm64" }, "sha512-XblONe153h0O2zuFfTAbQYAX2JhYmDHeWikp1LM9Hul9gVPjFY427k6dFEcOL72O01QxQsWi761svJ/ev9xEDg=="],
+
+ "@img/sharp-libvips-darwin-x64": ["@img/sharp-libvips-darwin-x64@1.0.4", "", { "os": "darwin", "cpu": "x64" }, "sha512-xnGR8YuZYfJGmWPvmlunFaWJsb9T/AO2ykoP3Fz/0X5XV2aoYBPkX6xqCQvUTKKiLddarLaxpzNe+b1hjeWHAQ=="],
+
+ "@img/sharp-libvips-linux-arm": ["@img/sharp-libvips-linux-arm@1.0.5", "", { "os": "linux", "cpu": "arm" }, "sha512-gvcC4ACAOPRNATg/ov8/MnbxFDJqf/pDePbBnuBDcjsI8PssmjoKMAz4LtLaVi+OnSb5FK/yIOamqDwGmXW32g=="],
+
+ "@img/sharp-libvips-linux-arm64": ["@img/sharp-libvips-linux-arm64@1.0.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-9B+taZ8DlyyqzZQnoeIvDVR/2F4EbMepXMc/NdVbkzsJbzkUjhXv/70GQJ7tdLA4YJgNP25zukcxpX2/SueNrA=="],
+
+ "@img/sharp-libvips-linux-s390x": ["@img/sharp-libvips-linux-s390x@1.0.4", "", { "os": "linux", "cpu": "s390x" }, "sha512-u7Wz6ntiSSgGSGcjZ55im6uvTrOxSIS8/dgoVMoiGE9I6JAfU50yH5BoDlYA1tcuGS7g/QNtetJnxA6QEsCVTA=="],
+
+ "@img/sharp-libvips-linux-x64": ["@img/sharp-libvips-linux-x64@1.0.4", "", { "os": "linux", "cpu": "x64" }, "sha512-MmWmQ3iPFZr0Iev+BAgVMb3ZyC4KeFc3jFxnNbEPas60e1cIfevbtuyf9nDGIzOaW9PdnDciJm+wFFaTlj5xYw=="],
+
+ "@img/sharp-libvips-linuxmusl-arm64": ["@img/sharp-libvips-linuxmusl-arm64@1.0.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-9Ti+BbTYDcsbp4wfYib8Ctm1ilkugkA/uscUn6UXK1ldpC1JjiXbLfFZtRlBhjPZ5o1NCLiDbg8fhUPKStHoTA=="],
+
+ "@img/sharp-libvips-linuxmusl-x64": ["@img/sharp-libvips-linuxmusl-x64@1.0.4", "", { "os": "linux", "cpu": "x64" }, "sha512-viYN1KX9m+/hGkJtvYYp+CCLgnJXwiQB39damAO7WMdKWlIhmYTfHjwSbQeUK/20vY154mwezd9HflVFM1wVSw=="],
+
+ "@img/sharp-linux-arm": ["@img/sharp-linux-arm@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-arm": "1.0.5" }, "os": "linux", "cpu": "arm" }, "sha512-JTS1eldqZbJxjvKaAkxhZmBqPRGmxgu+qFKSInv8moZ2AmT5Yib3EQ1c6gp493HvrvV8QgdOXdyaIBrhvFhBMQ=="],
+
+ "@img/sharp-linux-arm64": ["@img/sharp-linux-arm64@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-arm64": "1.0.4" }, "os": "linux", "cpu": "arm64" }, "sha512-JMVv+AMRyGOHtO1RFBiJy/MBsgz0x4AWrT6QoEVVTyh1E39TrCUpTRI7mx9VksGX4awWASxqCYLCV4wBZHAYxA=="],
+
+ "@img/sharp-linux-s390x": ["@img/sharp-linux-s390x@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-s390x": "1.0.4" }, "os": "linux", "cpu": "s390x" }, "sha512-y/5PCd+mP4CA/sPDKl2961b+C9d+vPAveS33s6Z3zfASk2j5upL6fXVPZi7ztePZ5CuH+1kW8JtvxgbuXHRa4Q=="],
+
+ "@img/sharp-linux-x64": ["@img/sharp-linux-x64@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-x64": "1.0.4" }, "os": "linux", "cpu": "x64" }, "sha512-opC+Ok5pRNAzuvq1AG0ar+1owsu842/Ab+4qvU879ippJBHvyY5n2mxF1izXqkPYlGuP/M556uh53jRLJmzTWA=="],
+
+ "@img/sharp-linuxmusl-arm64": ["@img/sharp-linuxmusl-arm64@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-linuxmusl-arm64": "1.0.4" }, "os": "linux", "cpu": "arm64" }, "sha512-XrHMZwGQGvJg2V/oRSUfSAfjfPxO+4DkiRh6p2AFjLQztWUuY/o8Mq0eMQVIY7HJ1CDQUJlxGGZRw1a5bqmd1g=="],
+
+ "@img/sharp-linuxmusl-x64": ["@img/sharp-linuxmusl-x64@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-linuxmusl-x64": "1.0.4" }, "os": "linux", "cpu": "x64" }, "sha512-WT+d/cgqKkkKySYmqoZ8y3pxx7lx9vVejxW/W4DOFMYVSkErR+w7mf2u8m/y4+xHe7yY9DAXQMWQhpnMuFfScw=="],
+
+ "@img/sharp-wasm32": ["@img/sharp-wasm32@0.33.5", "", { "dependencies": { "@emnapi/runtime": "^1.2.0" }, "cpu": "none" }, "sha512-ykUW4LVGaMcU9lu9thv85CbRMAwfeadCJHRsg2GmeRa/cJxsVY9Rbd57JcMxBkKHag5U/x7TSBpScF4U8ElVzg=="],
+
+ "@img/sharp-win32-ia32": ["@img/sharp-win32-ia32@0.33.5", "", { "os": "win32", "cpu": "ia32" }, "sha512-T36PblLaTwuVJ/zw/LaH0PdZkRz5rd3SmMHX8GSmR7vtNSP5Z6bQkExdSK7xGWyxLw4sUknBuugTelgw2faBbQ=="],
+
+ "@img/sharp-win32-x64": ["@img/sharp-win32-x64@0.33.5", "", { "os": "win32", "cpu": "x64" }, "sha512-MpY/o8/8kj+EcnxwvrP4aTJSWw/aZ7JIGR4aBeZkZw5B7/Jn+tY9/VNwtcoGmdT7GfggGIU4kygOMSbYnOrAbg=="],
+
+ "@isaacs/cliui": ["@isaacs/cliui@8.0.2", "", { "dependencies": { "string-width": "^5.1.2", "string-width-cjs": "npm:string-width@^4.2.0", "strip-ansi": "^7.0.1", "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", "wrap-ansi": "^8.1.0", "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" } }, "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA=="],
+
+ "@jridgewell/gen-mapping": ["@jridgewell/gen-mapping@0.3.13", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.0", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA=="],
+
+ "@jridgewell/remapping": ["@jridgewell/remapping@2.3.5", "", { "dependencies": { "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ=="],
+
+ "@jridgewell/resolve-uri": ["@jridgewell/resolve-uri@3.1.2", "", {}, "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw=="],
+
+ "@jridgewell/sourcemap-codec": ["@jridgewell/sourcemap-codec@1.5.5", "", {}, "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og=="],
+
+ "@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.9", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.0.3", "@jridgewell/sourcemap-codec": "^1.4.10" } }, "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ=="],
+
+ "@napi-rs/wasm-runtime": ["@napi-rs/wasm-runtime@0.2.12", "", { "dependencies": { "@emnapi/core": "^1.4.3", "@emnapi/runtime": "^1.4.3", "@tybys/wasm-util": "^0.10.0" } }, "sha512-ZVWUcfwY4E/yPitQJl481FjFo3K22D6qF0DuFH6Y/nbnE11GY5uguDxZMGXPQ8WQ0128MXQD7TnfHyK4oWoIJQ=="],
+
+ "@nodelib/fs.scandir": ["@nodelib/fs.scandir@2.1.5", "", { "dependencies": { "@nodelib/fs.stat": "2.0.5", "run-parallel": "^1.1.9" } }, "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g=="],
+
+ "@nodelib/fs.stat": ["@nodelib/fs.stat@2.0.5", "", {}, "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A=="],
+
+ "@nodelib/fs.walk": ["@nodelib/fs.walk@1.2.8", "", { "dependencies": { "@nodelib/fs.scandir": "2.1.5", "fastq": "^1.6.0" } }, "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg=="],
+
+ "@pkgjs/parseargs": ["@pkgjs/parseargs@0.11.0", "", {}, "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg=="],
+
+ "@poppinss/colors": ["@poppinss/colors@4.1.5", "", { "dependencies": { "kleur": "^4.1.5" } }, "sha512-FvdDqtcRCtz6hThExcFOgW0cWX+xwSMWcRuQe5ZEb2m7cVQOAVZOIMt+/v9RxGiD9/OY16qJBXK4CVKWAPalBw=="],
+
+ "@poppinss/dumper": ["@poppinss/dumper@0.6.4", "", { "dependencies": { "@poppinss/colors": "^4.1.5", "@sindresorhus/is": "^7.0.2", "supports-color": "^10.0.0" } }, "sha512-iG0TIdqv8xJ3Lt9O8DrPRxw1MRLjNpoqiSGU03P/wNLP/s0ra0udPJ1J2Tx5M0J3H/cVyEgpbn8xUKRY9j59kQ=="],
+
+ "@poppinss/exception": ["@poppinss/exception@1.2.2", "", {}, "sha512-m7bpKCD4QMlFCjA/nKTs23fuvoVFoA83brRKmObCUNmi/9tVu8Ve3w4YQAnJu4q3Tjf5fr685HYIC/IA2zHRSg=="],
+
+ "@radix-ui/number": ["@radix-ui/number@1.1.1", "", {}, "sha512-MkKCwxlXTgz6CFoJx3pCwn07GKp36+aZyu/u2Ln2VrA5DcdyCZkASEDBTd8x5whTQQL5CiYf4prXKLcgQdv29g=="],
+
+ "@radix-ui/primitive": ["@radix-ui/primitive@1.1.3", "", {}, "sha512-JTF99U/6XIjCBo0wqkU5sK10glYe27MRRsfwoiq5zzOEZLHU3A3KCMa5X/azekYRCJ0HlwI0crAXS/5dEHTzDg=="],
+
+ "@radix-ui/react-accordion": ["@radix-ui/react-accordion@1.2.12", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-collapsible": "1.1.12", "@radix-ui/react-collection": "1.1.7", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-direction": "1.1.1", "@radix-ui/react-id": "1.1.1", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-controllable-state": "1.2.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-T4nygeh9YE9dLRPhAHSeOZi7HBXo+0kYIPJXayZfvWOWA0+n3dESrZbjfDPUABkUNym6Hd+f2IR113To8D2GPA=="],
+
+ "@radix-ui/react-alert-dialog": ["@radix-ui/react-alert-dialog@1.1.15", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-dialog": "1.1.15", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-oTVLkEw5GpdRe29BqJ0LSDFWI3qu0vR1M0mUkOQWDIUnY/QIkLpgDMWuKxP94c2NAC2LGcgVhG1ImF3jkZ5wXw=="],
+
+ "@radix-ui/react-arrow": ["@radix-ui/react-arrow@1.1.7", "", { "dependencies": { "@radix-ui/react-primitive": "2.1.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-F+M1tLhO+mlQaOWspE8Wstg+z6PwxwRd8oQ8IXceWz92kfAmalTRf0EjrouQeo7QssEPfCn05B4Ihs1K9WQ/7w=="],
+
+ "@radix-ui/react-aspect-ratio": ["@radix-ui/react-aspect-ratio@1.1.7", "", { "dependencies": { "@radix-ui/react-primitive": "2.1.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-Yq6lvO9HQyPwev1onK1daHCHqXVLzPhSVjmsNjCa2Zcxy2f7uJD2itDtxknv6FzAKCwD1qQkeVDmX/cev13n/g=="],
+
+ "@radix-ui/react-avatar": ["@radix-ui/react-avatar@1.1.10", "", { "dependencies": { "@radix-ui/react-context": "1.1.2", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-callback-ref": "1.1.1", "@radix-ui/react-use-is-hydrated": "0.1.0", "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-V8piFfWapM5OmNCXTzVQY+E1rDa53zY+MQ4Y7356v4fFz6vqCyUtIz2rUD44ZEdwg78/jKmMJHj07+C/Z/rcog=="],
+
+ "@radix-ui/react-checkbox": ["@radix-ui/react-checkbox@1.3.3", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-presence": "1.1.5", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-controllable-state": "1.2.2", "@radix-ui/react-use-previous": "1.1.1", "@radix-ui/react-use-size": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-wBbpv+NQftHDdG86Qc0pIyXk5IR3tM8Vd0nWLKDcX8nNn4nXFOFwsKuqw2okA/1D/mpaAkmuyndrPJTYDNZtFw=="],
+
+ "@radix-ui/react-collapsible": ["@radix-ui/react-collapsible@1.1.12", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-id": "1.1.1", "@radix-ui/react-presence": "1.1.5", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-controllable-state": "1.2.2", "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-Uu+mSh4agx2ib1uIGPP4/CKNULyajb3p92LsVXmH2EHVMTfZWpll88XJ0j4W0z3f8NK1eYl1+Mf/szHPmcHzyA=="],
+
+ "@radix-ui/react-collection": ["@radix-ui/react-collection@1.1.7", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-Fh9rGN0MoI4ZFUNyfFVNU4y9LUz93u9/0K+yLgA2bwRojxM8JU1DyvvMBabnZPBgMWREAJvU2jjVzq+LrFUglw=="],
+
+ "@radix-ui/react-compose-refs": ["@radix-ui/react-compose-refs@1.1.2", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg=="],
+
+ "@radix-ui/react-context": ["@radix-ui/react-context@1.1.2", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-jCi/QKUM2r1Ju5a3J64TH2A5SpKAgh0LpknyqdQ4m6DCV0xJ2HG1xARRwNGPQfi1SLdLWZ1OJz6F4OMBBNiGJA=="],
+
+ "@radix-ui/react-context-menu": ["@radix-ui/react-context-menu@2.2.16", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-menu": "2.1.16", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-callback-ref": "1.1.1", "@radix-ui/react-use-controllable-state": "1.2.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-O8morBEW+HsVG28gYDZPTrT9UUovQUlJue5YO836tiTJhuIWBm/zQHc7j388sHWtdH/xUZurK9olD2+pcqx5ww=="],
+
+ "@radix-ui/react-dialog": ["@radix-ui/react-dialog@1.1.15", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-dismissable-layer": "1.1.11", "@radix-ui/react-focus-guards": "1.1.3", "@radix-ui/react-focus-scope": "1.1.7", "@radix-ui/react-id": "1.1.1", "@radix-ui/react-portal": "1.1.9", "@radix-ui/react-presence": "1.1.5", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-slot": "1.2.3", "@radix-ui/react-use-controllable-state": "1.2.2", "aria-hidden": "^1.2.4", "react-remove-scroll": "^2.6.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-TCglVRtzlffRNxRMEyR36DGBLJpeusFcgMVD9PZEzAKnUs1lKCgX5u9BmC2Yg+LL9MgZDugFFs1Vl+Jp4t/PGw=="],
+
+ "@radix-ui/react-direction": ["@radix-ui/react-direction@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-1UEWRX6jnOA2y4H5WczZ44gOOjTEmlqv1uNW4GAJEO5+bauCBhv8snY65Iw5/VOS/ghKN9gr2KjnLKxrsvoMVw=="],
+
+ "@radix-ui/react-dismissable-layer": ["@radix-ui/react-dismissable-layer@1.1.11", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-callback-ref": "1.1.1", "@radix-ui/react-use-escape-keydown": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-Nqcp+t5cTB8BinFkZgXiMJniQH0PsUt2k51FUhbdfeKvc4ACcG2uQniY/8+h1Yv6Kza4Q7lD7PQV0z0oicE0Mg=="],
+
+ "@radix-ui/react-dropdown-menu": ["@radix-ui/react-dropdown-menu@2.1.16", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-id": "1.1.1", "@radix-ui/react-menu": "2.1.16", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-controllable-state": "1.2.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-1PLGQEynI/3OX/ftV54COn+3Sud/Mn8vALg2rWnBLnRaGtJDduNW/22XjlGgPdpcIbiQxjKtb7BkcjP00nqfJw=="],
+
+ "@radix-ui/react-focus-guards": ["@radix-ui/react-focus-guards@1.1.3", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-0rFg/Rj2Q62NCm62jZw0QX7a3sz6QCQU0LpZdNrJX8byRGaGVTqbrW9jAoIAHyMQqsNpeZ81YgSizOt5WXq0Pw=="],
+
+ "@radix-ui/react-focus-scope": ["@radix-ui/react-focus-scope@1.1.7", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-callback-ref": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-t2ODlkXBQyn7jkl6TNaw/MtVEVvIGelJDCG41Okq/KwUsJBwQ4XVZsHAVUkK4mBv3ewiAS3PGuUWuY2BoK4ZUw=="],
+
+ "@radix-ui/react-hover-card": ["@radix-ui/react-hover-card@1.1.15", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-dismissable-layer": "1.1.11", "@radix-ui/react-popper": "1.2.8", "@radix-ui/react-portal": "1.1.9", "@radix-ui/react-presence": "1.1.5", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-controllable-state": "1.2.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-qgTkjNT1CfKMoP0rcasmlH2r1DAiYicWsDsufxl940sT2wHNEWWv6FMWIQXWhVdmC1d/HYfbhQx60KYyAtKxjg=="],
+
+ "@radix-ui/react-id": ["@radix-ui/react-id@1.1.1", "", { "dependencies": { "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-kGkGegYIdQsOb4XjsfM97rXsiHaBwco+hFI66oO4s9LU+PLAC5oJ7khdOVFxkhsmlbpUqDAvXw11CluXP+jkHg=="],
+
+ "@radix-ui/react-label": ["@radix-ui/react-label@2.1.7", "", { "dependencies": { "@radix-ui/react-primitive": "2.1.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-YT1GqPSL8kJn20djelMX7/cTRp/Y9w5IZHvfxQTVHrOqa2yMl7i/UfMqKRU5V7mEyKTrUVgJXhNQPVCG8PBLoQ=="],
+
+ "@radix-ui/react-menu": ["@radix-ui/react-menu@2.1.16", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-collection": "1.1.7", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-direction": "1.1.1", "@radix-ui/react-dismissable-layer": "1.1.11", "@radix-ui/react-focus-guards": "1.1.3", "@radix-ui/react-focus-scope": "1.1.7", "@radix-ui/react-id": "1.1.1", "@radix-ui/react-popper": "1.2.8", "@radix-ui/react-portal": "1.1.9", "@radix-ui/react-presence": "1.1.5", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-roving-focus": "1.1.11", "@radix-ui/react-slot": "1.2.3", "@radix-ui/react-use-callback-ref": "1.1.1", "aria-hidden": "^1.2.4", "react-remove-scroll": "^2.6.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-72F2T+PLlphrqLcAotYPp0uJMr5SjP5SL01wfEspJbru5Zs5vQaSHb4VB3ZMJPimgHHCHG7gMOeOB9H3Hdmtxg=="],
+
+ "@radix-ui/react-menubar": ["@radix-ui/react-menubar@1.1.16", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-collection": "1.1.7", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-direction": "1.1.1", "@radix-ui/react-id": "1.1.1", "@radix-ui/react-menu": "2.1.16", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-roving-focus": "1.1.11", "@radix-ui/react-use-controllable-state": "1.2.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-EB1FktTz5xRRi2Er974AUQZWg2yVBb1yjip38/lgwtCVRd3a+maUoGHN/xs9Yv8SY8QwbSEb+YrxGadVWbEutA=="],
+
+ "@radix-ui/react-navigation-menu": ["@radix-ui/react-navigation-menu@1.2.14", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-collection": "1.1.7", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-direction": "1.1.1", "@radix-ui/react-dismissable-layer": "1.1.11", "@radix-ui/react-id": "1.1.1", "@radix-ui/react-presence": "1.1.5", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-callback-ref": "1.1.1", "@radix-ui/react-use-controllable-state": "1.2.2", "@radix-ui/react-use-layout-effect": "1.1.1", "@radix-ui/react-use-previous": "1.1.1", "@radix-ui/react-visually-hidden": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-YB9mTFQvCOAQMHU+C/jVl96WmuWeltyUEpRJJky51huhds5W2FQr1J8D/16sQlf0ozxkPK8uF3niQMdUwZPv5w=="],
+
+ "@radix-ui/react-popover": ["@radix-ui/react-popover@1.1.15", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-dismissable-layer": "1.1.11", "@radix-ui/react-focus-guards": "1.1.3", "@radix-ui/react-focus-scope": "1.1.7", "@radix-ui/react-id": "1.1.1", "@radix-ui/react-popper": "1.2.8", "@radix-ui/react-portal": "1.1.9", "@radix-ui/react-presence": "1.1.5", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-slot": "1.2.3", "@radix-ui/react-use-controllable-state": "1.2.2", "aria-hidden": "^1.2.4", "react-remove-scroll": "^2.6.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-kr0X2+6Yy/vJzLYJUPCZEc8SfQcf+1COFoAqauJm74umQhta9M7lNJHP7QQS3vkvcGLQUbWpMzwrXYwrYztHKA=="],
+
+ "@radix-ui/react-popper": ["@radix-ui/react-popper@1.2.8", "", { "dependencies": { "@floating-ui/react-dom": "^2.0.0", "@radix-ui/react-arrow": "1.1.7", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-callback-ref": "1.1.1", "@radix-ui/react-use-layout-effect": "1.1.1", "@radix-ui/react-use-rect": "1.1.1", "@radix-ui/react-use-size": "1.1.1", "@radix-ui/rect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-0NJQ4LFFUuWkE7Oxf0htBKS6zLkkjBH+hM1uk7Ng705ReR8m/uelduy1DBo0PyBXPKVnBA6YBlU94MBGXrSBCw=="],
+
+ "@radix-ui/react-portal": ["@radix-ui/react-portal@1.1.9", "", { "dependencies": { "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-bpIxvq03if6UNwXZ+HTK71JLh4APvnXntDc6XOX8UVq4XQOVl7lwok0AvIl+b8zgCw3fSaVTZMpAPPagXbKmHQ=="],
+
+ "@radix-ui/react-presence": ["@radix-ui/react-presence@1.1.5", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-/jfEwNDdQVBCNvjkGit4h6pMOzq8bHkopq458dPt2lMjx+eBQUohZNG9A7DtO/O5ukSbxuaNGXMjHicgwy6rQQ=="],
+
+ "@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.3", "", { "dependencies": { "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ=="],
+
+ "@radix-ui/react-progress": ["@radix-ui/react-progress@1.1.7", "", { "dependencies": { "@radix-ui/react-context": "1.1.2", "@radix-ui/react-primitive": "2.1.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-vPdg/tF6YC/ynuBIJlk1mm7Le0VgW6ub6J2UWnTQ7/D23KXcPI1qy+0vBkgKgd38RCMJavBXpB83HPNFMTb0Fg=="],
+
+ "@radix-ui/react-radio-group": ["@radix-ui/react-radio-group@1.3.8", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-direction": "1.1.1", "@radix-ui/react-presence": "1.1.5", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-roving-focus": "1.1.11", "@radix-ui/react-use-controllable-state": "1.2.2", "@radix-ui/react-use-previous": "1.1.1", "@radix-ui/react-use-size": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-VBKYIYImA5zsxACdisNQ3BjCBfmbGH3kQlnFVqlWU4tXwjy7cGX8ta80BcrO+WJXIn5iBylEH3K6ZTlee//lgQ=="],
+
+ "@radix-ui/react-roving-focus": ["@radix-ui/react-roving-focus@1.1.11", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-collection": "1.1.7", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-direction": "1.1.1", "@radix-ui/react-id": "1.1.1", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-callback-ref": "1.1.1", "@radix-ui/react-use-controllable-state": "1.2.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-7A6S9jSgm/S+7MdtNDSb+IU859vQqJ/QAtcYQcfFC6W8RS4IxIZDldLR0xqCFZ6DCyrQLjLPsxtTNch5jVA4lA=="],
+
+ "@radix-ui/react-scroll-area": ["@radix-ui/react-scroll-area@1.2.10", "", { "dependencies": { "@radix-ui/number": "1.1.1", "@radix-ui/primitive": "1.1.3", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-direction": "1.1.1", "@radix-ui/react-presence": "1.1.5", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-callback-ref": "1.1.1", "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-tAXIa1g3sM5CGpVT0uIbUx/U3Gs5N8T52IICuCtObaos1S8fzsrPXG5WObkQN3S6NVl6wKgPhAIiBGbWnvc97A=="],
+
+ "@radix-ui/react-select": ["@radix-ui/react-select@2.2.6", "", { "dependencies": { "@radix-ui/number": "1.1.1", "@radix-ui/primitive": "1.1.3", "@radix-ui/react-collection": "1.1.7", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-direction": "1.1.1", "@radix-ui/react-dismissable-layer": "1.1.11", "@radix-ui/react-focus-guards": "1.1.3", "@radix-ui/react-focus-scope": "1.1.7", "@radix-ui/react-id": "1.1.1", "@radix-ui/react-popper": "1.2.8", "@radix-ui/react-portal": "1.1.9", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-slot": "1.2.3", "@radix-ui/react-use-callback-ref": "1.1.1", "@radix-ui/react-use-controllable-state": "1.2.2", "@radix-ui/react-use-layout-effect": "1.1.1", "@radix-ui/react-use-previous": "1.1.1", "@radix-ui/react-visually-hidden": "1.2.3", "aria-hidden": "^1.2.4", "react-remove-scroll": "^2.6.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-I30RydO+bnn2PQztvo25tswPH+wFBjehVGtmagkU78yMdwTwVf12wnAOF+AeP8S2N8xD+5UPbGhkUfPyvT+mwQ=="],
+
+ "@radix-ui/react-separator": ["@radix-ui/react-separator@1.1.7", "", { "dependencies": { "@radix-ui/react-primitive": "2.1.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-0HEb8R9E8A+jZjvmFCy/J4xhbXy3TV+9XSnGJ3KvTtjlIUy/YQ/p6UYZvi7YbeoeXdyU9+Y3scizK6hkY37baA=="],
+
+ "@radix-ui/react-slider": ["@radix-ui/react-slider@1.3.6", "", { "dependencies": { "@radix-ui/number": "1.1.1", "@radix-ui/primitive": "1.1.3", "@radix-ui/react-collection": "1.1.7", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-direction": "1.1.1", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-controllable-state": "1.2.2", "@radix-ui/react-use-layout-effect": "1.1.1", "@radix-ui/react-use-previous": "1.1.1", "@radix-ui/react-use-size": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-JPYb1GuM1bxfjMRlNLE+BcmBC8onfCi60Blk7OBqi2MLTFdS+8401U4uFjnwkOr49BLmXxLC6JHkvAsx5OJvHw=="],
+
+ "@radix-ui/react-slot": ["@radix-ui/react-slot@1.2.3", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A=="],
+
+ "@radix-ui/react-switch": ["@radix-ui/react-switch@1.2.6", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-controllable-state": "1.2.2", "@radix-ui/react-use-previous": "1.1.1", "@radix-ui/react-use-size": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-bByzr1+ep1zk4VubeEVViV592vu2lHE2BZY5OnzehZqOOgogN80+mNtCqPkhn2gklJqOpxWgPoYTSnhBCqpOXQ=="],
+
+ "@radix-ui/react-tabs": ["@radix-ui/react-tabs@1.1.13", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-direction": "1.1.1", "@radix-ui/react-id": "1.1.1", "@radix-ui/react-presence": "1.1.5", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-roving-focus": "1.1.11", "@radix-ui/react-use-controllable-state": "1.2.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-7xdcatg7/U+7+Udyoj2zodtI9H/IIopqo+YOIcZOq1nJwXWBZ9p8xiu5llXlekDbZkca79a/fozEYQXIA4sW6A=="],
+
+ "@radix-ui/react-toast": ["@radix-ui/react-toast@1.2.15", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-collection": "1.1.7", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-dismissable-layer": "1.1.11", "@radix-ui/react-portal": "1.1.9", "@radix-ui/react-presence": "1.1.5", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-callback-ref": "1.1.1", "@radix-ui/react-use-controllable-state": "1.2.2", "@radix-ui/react-use-layout-effect": "1.1.1", "@radix-ui/react-visually-hidden": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-3OSz3TacUWy4WtOXV38DggwxoqJK4+eDkNMl5Z/MJZaoUPaP4/9lf81xXMe1I2ReTAptverZUpbPY4wWwWyL5g=="],
+
+ "@radix-ui/react-toggle": ["@radix-ui/react-toggle@1.1.10", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-controllable-state": "1.2.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-lS1odchhFTeZv3xwHH31YPObmJn8gOg7Lq12inrr0+BH/l3Tsq32VfjqH1oh80ARM3mlkfMic15n0kg4sD1poQ=="],
+
+ "@radix-ui/react-toggle-group": ["@radix-ui/react-toggle-group@1.1.11", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-direction": "1.1.1", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-roving-focus": "1.1.11", "@radix-ui/react-toggle": "1.1.10", "@radix-ui/react-use-controllable-state": "1.2.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-5umnS0T8JQzQT6HbPyO7Hh9dgd82NmS36DQr+X/YJ9ctFNCiiQd6IJAYYZ33LUwm8M+taCz5t2ui29fHZc4Y6Q=="],
+
+ "@radix-ui/react-tooltip": ["@radix-ui/react-tooltip@1.2.8", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-dismissable-layer": "1.1.11", "@radix-ui/react-id": "1.1.1", "@radix-ui/react-popper": "1.2.8", "@radix-ui/react-portal": "1.1.9", "@radix-ui/react-presence": "1.1.5", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-slot": "1.2.3", "@radix-ui/react-use-controllable-state": "1.2.2", "@radix-ui/react-visually-hidden": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-tY7sVt1yL9ozIxvmbtN5qtmH2krXcBCfjEiCgKGLqunJHvgvZG2Pcl2oQ3kbcZARb1BGEHdkLzcYGO8ynVlieg=="],
+
+ "@radix-ui/react-use-callback-ref": ["@radix-ui/react-use-callback-ref@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-FkBMwD+qbGQeMu1cOHnuGB6x4yzPjho8ap5WtbEJ26umhgqVXbhekKUQO+hZEL1vU92a3wHwdp0HAcqAUF5iDg=="],
+
+ "@radix-ui/react-use-controllable-state": ["@radix-ui/react-use-controllable-state@1.2.2", "", { "dependencies": { "@radix-ui/react-use-effect-event": "0.0.2", "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-BjasUjixPFdS+NKkypcyyN5Pmg83Olst0+c6vGov0diwTEo6mgdqVR6hxcEgFuh4QrAs7Rc+9KuGJ9TVCj0Zzg=="],
+
+ "@radix-ui/react-use-effect-event": ["@radix-ui/react-use-effect-event@0.0.2", "", { "dependencies": { "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-Qp8WbZOBe+blgpuUT+lw2xheLP8q0oatc9UpmiemEICxGvFLYmHm9QowVZGHtJlGbS6A6yJ3iViad/2cVjnOiA=="],
+
+ "@radix-ui/react-use-escape-keydown": ["@radix-ui/react-use-escape-keydown@1.1.1", "", { "dependencies": { "@radix-ui/react-use-callback-ref": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-Il0+boE7w/XebUHyBjroE+DbByORGR9KKmITzbR7MyQ4akpORYP/ZmbhAr0DG7RmmBqoOnZdy2QlvajJ2QA59g=="],
+
+ "@radix-ui/react-use-is-hydrated": ["@radix-ui/react-use-is-hydrated@0.1.0", "", { "dependencies": { "use-sync-external-store": "^1.5.0" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-U+UORVEq+cTnRIaostJv9AGdV3G6Y+zbVd+12e18jQ5A3c0xL03IhnHuiU4UV69wolOQp5GfR58NW/EgdQhwOA=="],
+
+ "@radix-ui/react-use-layout-effect": ["@radix-ui/react-use-layout-effect@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ=="],
+
+ "@radix-ui/react-use-previous": ["@radix-ui/react-use-previous@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-2dHfToCj/pzca2Ck724OZ5L0EVrr3eHRNsG/b3xQJLA2hZpVCS99bLAX+hm1IHXDEnzU6by5z/5MIY794/a8NQ=="],
+
+ "@radix-ui/react-use-rect": ["@radix-ui/react-use-rect@1.1.1", "", { "dependencies": { "@radix-ui/rect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-QTYuDesS0VtuHNNvMh+CjlKJ4LJickCMUAqjlE3+j8w+RlRpwyX3apEQKGFzbZGdo7XNG1tXa+bQqIE7HIXT2w=="],
+
+ "@radix-ui/react-use-size": ["@radix-ui/react-use-size@1.1.1", "", { "dependencies": { "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-ewrXRDTAqAXlkl6t/fkXWNAhFX9I+CkKlw6zjEwk86RSPKwZr3xpBRso655aqYafwtnbpHLj6toFzmd6xdVptQ=="],
+
+ "@radix-ui/react-visually-hidden": ["@radix-ui/react-visually-hidden@1.2.3", "", { "dependencies": { "@radix-ui/react-primitive": "2.1.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-pzJq12tEaaIhqjbzpCuv/OypJY/BPavOofm+dbab+MHLajy277+1lLm6JFcGgF5eskJ6mquGirhXY2GD/8u8Ug=="],
+
+ "@radix-ui/rect": ["@radix-ui/rect@1.1.1", "", {}, "sha512-HPwpGIzkl28mWyZqG52jiqDJ12waP11Pa1lGoiyUkIEuMLBP0oeK/C89esbXrxsky5we7dfd8U58nm0SgAWpVw=="],
+
+ "@react-aria/focus": ["@react-aria/focus@3.21.1", "", { "dependencies": { "@react-aria/interactions": "^3.25.5", "@react-aria/utils": "^3.30.1", "@react-types/shared": "^3.32.0", "@swc/helpers": "^0.5.0", "clsx": "^2.0.0" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1", "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, "sha512-hmH1IhHlcQ2lSIxmki1biWzMbGgnhdxJUM0MFfzc71Rv6YAzhlx4kX3GYn4VNcjCeb6cdPv4RZ5vunV4kgMZYQ=="],
+
+ "@react-aria/interactions": ["@react-aria/interactions@3.25.5", "", { "dependencies": { "@react-aria/ssr": "^3.9.10", "@react-aria/utils": "^3.30.1", "@react-stately/flags": "^3.1.2", "@react-types/shared": "^3.32.0", "@swc/helpers": "^0.5.0" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1", "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, "sha512-EweYHOEvMwef/wsiEqV73KurX/OqnmbzKQa2fLxdULbec5+yDj6wVGaRHIzM4NiijIDe+bldEl5DG05CAKOAHA=="],
+
+ "@react-aria/ssr": ["@react-aria/ssr@3.9.10", "", { "dependencies": { "@swc/helpers": "^0.5.0" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, "sha512-hvTm77Pf+pMBhuBm760Li0BVIO38jv1IBws1xFm1NoL26PU+fe+FMW5+VZWyANR6nYL65joaJKZqOdTQMkO9IQ=="],
+
+ "@react-aria/utils": ["@react-aria/utils@3.30.1", "", { "dependencies": { "@react-aria/ssr": "^3.9.10", "@react-stately/flags": "^3.1.2", "@react-stately/utils": "^3.10.8", "@react-types/shared": "^3.32.0", "@swc/helpers": "^0.5.0", "clsx": "^2.0.0" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1", "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, "sha512-zETcbDd6Vf9GbLndO6RiWJadIZsBU2MMm23rBACXLmpRztkrIqPEb2RVdlLaq1+GklDx0Ii6PfveVjx+8S5U6A=="],
+
+ "@react-stately/flags": ["@react-stately/flags@3.1.2", "", { "dependencies": { "@swc/helpers": "^0.5.0" } }, "sha512-2HjFcZx1MyQXoPqcBGALwWWmgFVUk2TuKVIQxCbRq7fPyWXIl6VHcakCLurdtYC2Iks7zizvz0Idv48MQ38DWg=="],
+
+ "@react-stately/utils": ["@react-stately/utils@3.10.8", "", { "dependencies": { "@swc/helpers": "^0.5.0" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, "sha512-SN3/h7SzRsusVQjQ4v10LaVsDc81jyyR0DD5HnsQitm/I5WDpaSr2nRHtyloPFU48jlql1XX/S04T2DLQM7Y3g=="],
+
+ "@react-types/shared": ["@react-types/shared@3.32.0", "", { "peerDependencies": { "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, "sha512-t+cligIJsZYFMSPFMvsJMjzlzde06tZMOIOFa1OV5Z0BcMowrb2g4mB57j/9nP28iJIRYn10xCniQts+qadrqQ=="],
+
+ "@remix-run/node-fetch-server": ["@remix-run/node-fetch-server@0.8.1", "", {}, "sha512-J1dev372wtJqmqn9U/qbpbZxbJSQrogNN2+Qv1lKlpATpe/WQ9aCZfl/xSb9d2Rgh1IyLSvNxZAXPZxruO6Xig=="],
+
+ "@remix-run/router": ["@remix-run/router@1.23.0", "", {}, "sha512-O3rHJzAQKamUz1fvE0Qaw0xSFqsA/yafi2iqeE0pvdFtCO1viYx8QL6f3Ln/aCCTLxs68SLf0KPM9eSeM8yBnA=="],
+
+ "@rolldown/pluginutils": ["@rolldown/pluginutils@1.0.0-beta.27", "", {}, "sha512-+d0F4MKMCbeVUJwG96uQ4SgAznZNSq93I3V+9NHA4OpvqG8mRCpGdKmK8l/dl02h2CCDHwW2FqilnTyDcAnqjA=="],
+
+ "@rollup/rollup-android-arm-eabi": ["@rollup/rollup-android-arm-eabi@4.50.1", "", { "os": "android", "cpu": "arm" }, "sha512-HJXwzoZN4eYTdD8bVV22DN8gsPCAj3V20NHKOs8ezfXanGpmVPR7kalUHd+Y31IJp9stdB87VKPFbsGY3H/2ag=="],
+
+ "@rollup/rollup-android-arm64": ["@rollup/rollup-android-arm64@4.50.1", "", { "os": "android", "cpu": "arm64" }, "sha512-PZlsJVcjHfcH53mOImyt3bc97Ep3FJDXRpk9sMdGX0qgLmY0EIWxCag6EigerGhLVuL8lDVYNnSo8qnTElO4xw=="],
+
+ "@rollup/rollup-darwin-arm64": ["@rollup/rollup-darwin-arm64@4.50.1", "", { "os": "darwin", "cpu": "arm64" }, "sha512-xc6i2AuWh++oGi4ylOFPmzJOEeAa2lJeGUGb4MudOtgfyyjr4UPNK+eEWTPLvmPJIY/pgw6ssFIox23SyrkkJw=="],
+
+ "@rollup/rollup-darwin-x64": ["@rollup/rollup-darwin-x64@4.50.1", "", { "os": "darwin", "cpu": "x64" }, "sha512-2ofU89lEpDYhdLAbRdeyz/kX3Y2lpYc6ShRnDjY35bZhd2ipuDMDi6ZTQ9NIag94K28nFMofdnKeHR7BT0CATw=="],
+
+ "@rollup/rollup-freebsd-arm64": ["@rollup/rollup-freebsd-arm64@4.50.1", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-wOsE6H2u6PxsHY/BeFHA4VGQN3KUJFZp7QJBmDYI983fgxq5Th8FDkVuERb2l9vDMs1D5XhOrhBrnqcEY6l8ZA=="],
+
+ "@rollup/rollup-freebsd-x64": ["@rollup/rollup-freebsd-x64@4.50.1", "", { "os": "freebsd", "cpu": "x64" }, "sha512-A/xeqaHTlKbQggxCqispFAcNjycpUEHP52mwMQZUNqDUJFFYtPHCXS1VAG29uMlDzIVr+i00tSFWFLivMcoIBQ=="],
+
+ "@rollup/rollup-linux-arm-gnueabihf": ["@rollup/rollup-linux-arm-gnueabihf@4.50.1", "", { "os": "linux", "cpu": "arm" }, "sha512-54v4okehwl5TaSIkpp97rAHGp7t3ghinRd/vyC1iXqXMfjYUTm7TfYmCzXDoHUPTTf36L8pr0E7YsD3CfB3ZDg=="],
+
+ "@rollup/rollup-linux-arm-musleabihf": ["@rollup/rollup-linux-arm-musleabihf@4.50.1", "", { "os": "linux", "cpu": "arm" }, "sha512-p/LaFyajPN/0PUHjv8TNyxLiA7RwmDoVY3flXHPSzqrGcIp/c2FjwPPP5++u87DGHtw+5kSH5bCJz0mvXngYxw=="],
+
+ "@rollup/rollup-linux-arm64-gnu": ["@rollup/rollup-linux-arm64-gnu@4.50.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-2AbMhFFkTo6Ptna1zO7kAXXDLi7H9fGTbVaIq2AAYO7yzcAsuTNWPHhb2aTA6GPiP+JXh85Y8CiS54iZoj4opw=="],
+
+ "@rollup/rollup-linux-arm64-musl": ["@rollup/rollup-linux-arm64-musl@4.50.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-Cgef+5aZwuvesQNw9eX7g19FfKX5/pQRIyhoXLCiBOrWopjo7ycfB292TX9MDcDijiuIJlx1IzJz3IoCPfqs9w=="],
+
+ "@rollup/rollup-linux-loongarch64-gnu": ["@rollup/rollup-linux-loongarch64-gnu@4.50.1", "", { "os": "linux", "cpu": "none" }, "sha512-RPhTwWMzpYYrHrJAS7CmpdtHNKtt2Ueo+BlLBjfZEhYBhK00OsEqM08/7f+eohiF6poe0YRDDd8nAvwtE/Y62Q=="],
+
+ "@rollup/rollup-linux-ppc64-gnu": ["@rollup/rollup-linux-ppc64-gnu@4.50.1", "", { "os": "linux", "cpu": "ppc64" }, "sha512-eSGMVQw9iekut62O7eBdbiccRguuDgiPMsw++BVUg+1K7WjZXHOg/YOT9SWMzPZA+w98G+Fa1VqJgHZOHHnY0Q=="],
+
+ "@rollup/rollup-linux-riscv64-gnu": ["@rollup/rollup-linux-riscv64-gnu@4.50.1", "", { "os": "linux", "cpu": "none" }, "sha512-S208ojx8a4ciIPrLgazF6AgdcNJzQE4+S9rsmOmDJkusvctii+ZvEuIC4v/xFqzbuP8yDjn73oBlNDgF6YGSXQ=="],
+
+ "@rollup/rollup-linux-riscv64-musl": ["@rollup/rollup-linux-riscv64-musl@4.50.1", "", { "os": "linux", "cpu": "none" }, "sha512-3Ag8Ls1ggqkGUvSZWYcdgFwriy2lWo+0QlYgEFra/5JGtAd6C5Hw59oojx1DeqcA2Wds2ayRgvJ4qxVTzCHgzg=="],
+
+ "@rollup/rollup-linux-s390x-gnu": ["@rollup/rollup-linux-s390x-gnu@4.50.1", "", { "os": "linux", "cpu": "s390x" }, "sha512-t9YrKfaxCYe7l7ldFERE1BRg/4TATxIg+YieHQ966jwvo7ddHJxPj9cNFWLAzhkVsbBvNA4qTbPVNsZKBO4NSg=="],
+
+ "@rollup/rollup-linux-x64-gnu": ["@rollup/rollup-linux-x64-gnu@4.50.1", "", { "os": "linux", "cpu": "x64" }, "sha512-MCgtFB2+SVNuQmmjHf+wfI4CMxy3Tk8XjA5Z//A0AKD7QXUYFMQcns91K6dEHBvZPCnhJSyDWLApk40Iq/H3tA=="],
+
+ "@rollup/rollup-linux-x64-musl": ["@rollup/rollup-linux-x64-musl@4.50.1", "", { "os": "linux", "cpu": "x64" }, "sha512-nEvqG+0jeRmqaUMuwzlfMKwcIVffy/9KGbAGyoa26iu6eSngAYQ512bMXuqqPrlTyfqdlB9FVINs93j534UJrg=="],
+
+ "@rollup/rollup-openharmony-arm64": ["@rollup/rollup-openharmony-arm64@4.50.1", "", { "os": "none", "cpu": "arm64" }, "sha512-RDsLm+phmT3MJd9SNxA9MNuEAO/J2fhW8GXk62G/B4G7sLVumNFbRwDL6v5NrESb48k+QMqdGbHgEtfU0LCpbA=="],
+
+ "@rollup/rollup-win32-arm64-msvc": ["@rollup/rollup-win32-arm64-msvc@4.50.1", "", { "os": "win32", "cpu": "arm64" }, "sha512-hpZB/TImk2FlAFAIsoElM3tLzq57uxnGYwplg6WDyAxbYczSi8O2eQ+H2Lx74504rwKtZ3N2g4bCUkiamzS6TQ=="],
+
+ "@rollup/rollup-win32-ia32-msvc": ["@rollup/rollup-win32-ia32-msvc@4.50.1", "", { "os": "win32", "cpu": "ia32" }, "sha512-SXjv8JlbzKM0fTJidX4eVsH+Wmnp0/WcD8gJxIZyR6Gay5Qcsmdbi9zVtnbkGPG8v2vMR1AD06lGWy5FLMcG7A=="],
+
+ "@rollup/rollup-win32-x64-msvc": ["@rollup/rollup-win32-x64-msvc@4.50.1", "", { "os": "win32", "cpu": "x64" }, "sha512-StxAO/8ts62KZVRAm4JZYq9+NqNsV7RvimNK+YM7ry//zebEH6meuugqW/P5OFUCjyQgui+9fUxT6d5NShvMvA=="],
+
+ "@rtsao/scc": ["@rtsao/scc@1.1.0", "", {}, "sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g=="],
+
+ "@sindresorhus/is": ["@sindresorhus/is@7.0.2", "", {}, "sha512-d9xRovfKNz1SKieM0qJdO+PQonjnnIfSNWfHYnBSJ9hkjm0ZPw6HlxscDXYstp3z+7V2GOFHc+J0CYrYTjqCJw=="],
+
+ "@speed-highlight/core": ["@speed-highlight/core@1.2.7", "", {}, "sha512-0dxmVj4gxg3Jg879kvFS/msl4s9F3T9UXC1InxgOf7t5NvcPD97u/WTA5vL/IxWHMn7qSxBozqrnnE2wvl1m8g=="],
+
+ "@standard-schema/utils": ["@standard-schema/utils@0.3.0", "", {}, "sha512-e7Mew686owMaPJVNNLs55PUvgz371nKgwsc4vxE49zsODpJEnxgxRo2y/OKrqueavXgZNMDVj3DdHFlaSAeU8g=="],
+
+ "@swc/helpers": ["@swc/helpers@0.5.17", "", { "dependencies": { "tslib": "^2.8.0" } }, "sha512-5IKx/Y13RsYd+sauPb2x+U/xZikHjolzfuDgTAl/Tdf3Q8rslRvC19NKDLgAJQ6wsqADk10ntlv08nPFw/gO/A=="],
+
+ "@tanstack/query-core": ["@tanstack/query-core@5.87.4", "", {}, "sha512-uNsg6zMxraEPDVO2Bn+F3/ctHi+Zsk+MMpcN8h6P7ozqD088F6mFY5TfGM7zuyIrL7HKpDyu6QHfLWiDxh3cuw=="],
+
+ "@tanstack/react-query": ["@tanstack/react-query@5.87.4", "", { "dependencies": { "@tanstack/query-core": "5.87.4" }, "peerDependencies": { "react": "^18 || ^19" } }, "sha512-T5GT/1ZaNsUXf5I3RhcYuT17I4CPlbZgyLxc/ZGv7ciS6esytlbjb3DgUFO6c8JWYMDpdjSWInyGZUErgzqhcA=="],
+
+ "@tanstack/react-virtual": ["@tanstack/react-virtual@3.13.12", "", { "dependencies": { "@tanstack/virtual-core": "3.13.12" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-Gd13QdxPSukP8ZrkbgS2RwoZseTTbQPLnQEn7HY/rqtM+8Zt95f7xKC7N0EsKs7aoz0WzZ+fditZux+F8EzYxA=="],
+
+ "@tanstack/virtual-core": ["@tanstack/virtual-core@3.13.12", "", {}, "sha512-1YBOJfRHV4sXUmWsFSf5rQor4Ss82G8dQWLRbnk3GA4jeP8hQt1hxXh0tmflpC0dz3VgEv/1+qwPyLeWkQuPFA=="],
+
+ "@tybys/wasm-util": ["@tybys/wasm-util@0.10.1", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg=="],
+
+ "@types/babel__core": ["@types/babel__core@7.20.5", "", { "dependencies": { "@babel/parser": "^7.20.7", "@babel/types": "^7.20.7", "@types/babel__generator": "*", "@types/babel__template": "*", "@types/babel__traverse": "*" } }, "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA=="],
+
+ "@types/babel__generator": ["@types/babel__generator@7.27.0", "", { "dependencies": { "@babel/types": "^7.0.0" } }, "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg=="],
+
+ "@types/babel__template": ["@types/babel__template@7.4.4", "", { "dependencies": { "@babel/parser": "^7.1.0", "@babel/types": "^7.0.0" } }, "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A=="],
+
+ "@types/babel__traverse": ["@types/babel__traverse@7.28.0", "", { "dependencies": { "@babel/types": "^7.28.2" } }, "sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q=="],
+
+ "@types/d3-array": ["@types/d3-array@3.2.1", "", {}, "sha512-Y2Jn2idRrLzUfAKV2LyRImR+y4oa2AntrgID95SHJxuMUrkNXmanDSed71sRNZysveJVt1hLLemQZIady0FpEg=="],
+
+ "@types/d3-color": ["@types/d3-color@3.1.3", "", {}, "sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A=="],
+
+ "@types/d3-ease": ["@types/d3-ease@3.0.2", "", {}, "sha512-NcV1JjO5oDzoK26oMzbILE6HW7uVXOHLQvHshBUW4UMdZGfiY6v5BeQwh9a9tCzv+CeefZQHJt5SRgK154RtiA=="],
+
+ "@types/d3-interpolate": ["@types/d3-interpolate@3.0.4", "", { "dependencies": { "@types/d3-color": "*" } }, "sha512-mgLPETlrpVV1YRJIglr4Ez47g7Yxjl1lj7YKsiMCb27VJH9W8NVM6Bb9d8kkpG/uAQS5AmbA48q2IAolKKo1MA=="],
+
+ "@types/d3-path": ["@types/d3-path@3.1.1", "", {}, "sha512-VMZBYyQvbGmWyWVea0EHs/BwLgxc+MKi1zLDCONksozI4YJMcTt8ZEuIR4Sb1MMTE8MMW49v0IwI5+b7RmfWlg=="],
+
+ "@types/d3-scale": ["@types/d3-scale@4.0.9", "", { "dependencies": { "@types/d3-time": "*" } }, "sha512-dLmtwB8zkAeO/juAMfnV+sItKjlsw2lKdZVVy6LRr0cBmegxSABiLEpGVmSJJ8O08i4+sGR6qQtb6WtuwJdvVw=="],
+
+ "@types/d3-shape": ["@types/d3-shape@3.1.7", "", { "dependencies": { "@types/d3-path": "*" } }, "sha512-VLvUQ33C+3J+8p+Daf+nYSOsjB4GXp19/S/aGo60m9h1v6XaxjiT82lKVWJCfzhtuZ3yD7i/TPeC/fuKLLOSmg=="],
+
+ "@types/d3-time": ["@types/d3-time@3.0.4", "", {}, "sha512-yuzZug1nkAAaBlBBikKZTgzCeA+k1uy4ZFwWANOfKw5z5LRhV0gNA7gNkKm7HoK+HRN0wX3EkxGk0fpbWhmB7g=="],
+
+ "@types/d3-timer": ["@types/d3-timer@3.0.2", "", {}, "sha512-Ps3T8E8dZDam6fUyNiMkekK3XUsaUEik+idO9/YjPtfj2qruF8tFBXS7XhtE4iIXBLxhmLjP3SXpLhVf21I9Lw=="],
+
+ "@types/estree": ["@types/estree@1.0.8", "", {}, "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w=="],
+
+ "@types/js-cookie": ["@types/js-cookie@2.2.7", "", {}, "sha512-aLkWa0C0vO5b4Sr798E26QgOkss68Un0bLjs7u9qxzPT5CG+8DuNTffWES58YzJs3hrVAOs1wonycqEBqNJubA=="],
+
+ "@types/json-schema": ["@types/json-schema@7.0.15", "", {}, "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA=="],
+
+ "@types/json5": ["@types/json5@0.0.29", "", {}, "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ=="],
+
+ "@types/node": ["@types/node@22.18.1", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-rzSDyhn4cYznVG+PCzGe1lwuMYJrcBS1fc3JqSa2PvtABwWo+dZ1ij5OVok3tqfpEBCBoaR4d7upFJk73HRJDw=="],
+
+ "@types/node-fetch": ["@types/node-fetch@2.6.13", "", { "dependencies": { "@types/node": "*", "form-data": "^4.0.4" } }, "sha512-QGpRVpzSaUs30JBSGPjOg4Uveu384erbHBoT1zeONvyCfwQxIkUshLAOqN/k9EjGviPRmWTTe6aH2qySWKTVSw=="],
+
+ "@types/parse-json": ["@types/parse-json@4.0.2", "", {}, "sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw=="],
+
+ "@types/prop-types": ["@types/prop-types@15.7.15", "", {}, "sha512-F6bEyamV9jKGAFBEmlQnesRPGOQqS2+Uwi0Em15xenOxHaf2hv6L8YCVn3rPdPJOiJfPiCnLIRyvwVaqMY3MIw=="],
+
+ "@types/react": ["@types/react@18.3.24", "", { "dependencies": { "@types/prop-types": "*", "csstype": "^3.0.2" } }, "sha512-0dLEBsA1kI3OezMBF8nSsb7Nk19ZnsyE1LLhB8r27KbgU5H4pvuqZLdtE+aUkJVoXgTVuA+iLIwmZ0TuK4tx6A=="],
+
+ "@types/react-dom": ["@types/react-dom@18.3.7", "", { "peerDependencies": { "@types/react": "^18.0.0" } }, "sha512-MEe3UeoENYVFXzoXEWsvcpg6ZvlrFNlOQ7EOsvhI3CfAXwzPfO8Qwuxd40nepsYKqyyVQnTdEfv68q91yLcKrQ=="],
+
+ "@types/react-transition-group": ["@types/react-transition-group@4.4.12", "", { "peerDependencies": { "@types/react": "*" } }, "sha512-8TV6R3h2j7a91c+1DXdJi3Syo69zzIZbz7Lg5tORM5LEJG7X/E6a1V3drRyBRZq7/utz7A+c4OgYLiLcYGHG6w=="],
+
+ "@typescript-eslint/eslint-plugin": ["@typescript-eslint/eslint-plugin@8.43.0", "", { "dependencies": { "@eslint-community/regexpp": "^4.10.0", "@typescript-eslint/scope-manager": "8.43.0", "@typescript-eslint/type-utils": "8.43.0", "@typescript-eslint/utils": "8.43.0", "@typescript-eslint/visitor-keys": "8.43.0", "graphemer": "^1.4.0", "ignore": "^7.0.0", "natural-compare": "^1.4.0", "ts-api-utils": "^2.1.0" }, "peerDependencies": { "@typescript-eslint/parser": "^8.43.0", "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-8tg+gt7ENL7KewsKMKDHXR1vm8tt9eMxjJBYINf6swonlWgkYn5NwyIgXpbbDxTNU5DgpDFfj95prcTq2clIQQ=="],
+
+ "@typescript-eslint/parser": ["@typescript-eslint/parser@8.43.0", "", { "dependencies": { "@typescript-eslint/scope-manager": "8.43.0", "@typescript-eslint/types": "8.43.0", "@typescript-eslint/typescript-estree": "8.43.0", "@typescript-eslint/visitor-keys": "8.43.0", "debug": "^4.3.4" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-B7RIQiTsCBBmY+yW4+ILd6mF5h1FUwJsVvpqkrgpszYifetQ2Ke+Z4u6aZh0CblkUGIdR59iYVyXqqZGkZ3aBw=="],
+
+ "@typescript-eslint/project-service": ["@typescript-eslint/project-service@8.43.0", "", { "dependencies": { "@typescript-eslint/tsconfig-utils": "^8.43.0", "@typescript-eslint/types": "^8.43.0", "debug": "^4.3.4" }, "peerDependencies": { "typescript": ">=4.8.4 <6.0.0" } }, "sha512-htB/+D/BIGoNTQYffZw4uM4NzzuolCoaA/BusuSIcC8YjmBYQioew5VUZAYdAETPjeed0hqCaW7EHg+Robq8uw=="],
+
+ "@typescript-eslint/scope-manager": ["@typescript-eslint/scope-manager@8.43.0", "", { "dependencies": { "@typescript-eslint/types": "8.43.0", "@typescript-eslint/visitor-keys": "8.43.0" } }, "sha512-daSWlQ87ZhsjrbMLvpuuMAt3y4ba57AuvadcR7f3nl8eS3BjRc8L9VLxFLk92RL5xdXOg6IQ+qKjjqNEimGuAg=="],
+
+ "@typescript-eslint/tsconfig-utils": ["@typescript-eslint/tsconfig-utils@8.43.0", "", { "peerDependencies": { "typescript": ">=4.8.4 <6.0.0" } }, "sha512-ALC2prjZcj2YqqL5X/bwWQmHA2em6/94GcbB/KKu5SX3EBDOsqztmmX1kMkvAJHzxk7TazKzJfFiEIagNV3qEA=="],
+
+ "@typescript-eslint/type-utils": ["@typescript-eslint/type-utils@8.43.0", "", { "dependencies": { "@typescript-eslint/types": "8.43.0", "@typescript-eslint/typescript-estree": "8.43.0", "@typescript-eslint/utils": "8.43.0", "debug": "^4.3.4", "ts-api-utils": "^2.1.0" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-qaH1uLBpBuBBuRf8c1mLJ6swOfzCXryhKND04Igr4pckzSEW9JX5Aw9AgW00kwfjWJF0kk0ps9ExKTfvXfw4Qg=="],
+
+ "@typescript-eslint/types": ["@typescript-eslint/types@8.43.0", "", {}, "sha512-vQ2FZaxJpydjSZJKiSW/LJsabFFvV7KgLC5DiLhkBcykhQj8iK9BOaDmQt74nnKdLvceM5xmhaTF+pLekrxEkw=="],
+
+ "@typescript-eslint/typescript-estree": ["@typescript-eslint/typescript-estree@8.43.0", "", { "dependencies": { "@typescript-eslint/project-service": "8.43.0", "@typescript-eslint/tsconfig-utils": "8.43.0", "@typescript-eslint/types": "8.43.0", "@typescript-eslint/visitor-keys": "8.43.0", "debug": "^4.3.4", "fast-glob": "^3.3.2", "is-glob": "^4.0.3", "minimatch": "^9.0.4", "semver": "^7.6.0", "ts-api-utils": "^2.1.0" }, "peerDependencies": { "typescript": ">=4.8.4 <6.0.0" } }, "sha512-7Vv6zlAhPb+cvEpP06WXXy/ZByph9iL6BQRBDj4kmBsW98AqEeQHlj/13X+sZOrKSo9/rNKH4Ul4f6EICREFdw=="],
+
+ "@typescript-eslint/utils": ["@typescript-eslint/utils@8.43.0", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.7.0", "@typescript-eslint/scope-manager": "8.43.0", "@typescript-eslint/types": "8.43.0", "@typescript-eslint/typescript-estree": "8.43.0" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-S1/tEmkUeeswxd0GGcnwuVQPFWo8NzZTOMxCvw8BX7OMxnNae+i8Tm7REQen/SwUIPoPqfKn7EaZ+YLpiB3k9g=="],
+
+ "@typescript-eslint/visitor-keys": ["@typescript-eslint/visitor-keys@8.43.0", "", { "dependencies": { "@typescript-eslint/types": "8.43.0", "eslint-visitor-keys": "^4.2.1" } }, "sha512-T+S1KqRD4sg/bHfLwrpF/K3gQLBM1n7Rp7OjjikjTEssI2YJzQpi5WXoynOaQ93ERIuq3O8RBTOUYDKszUCEHw=="],
+
+ "@unrs/resolver-binding-android-arm-eabi": ["@unrs/resolver-binding-android-arm-eabi@1.11.1", "", { "os": "android", "cpu": "arm" }, "sha512-ppLRUgHVaGRWUx0R0Ut06Mjo9gBaBkg3v/8AxusGLhsIotbBLuRk51rAzqLC8gq6NyyAojEXglNjzf6R948DNw=="],
+
+ "@unrs/resolver-binding-android-arm64": ["@unrs/resolver-binding-android-arm64@1.11.1", "", { "os": "android", "cpu": "arm64" }, "sha512-lCxkVtb4wp1v+EoN+HjIG9cIIzPkX5OtM03pQYkG+U5O/wL53LC4QbIeazgiKqluGeVEeBlZahHalCaBvU1a2g=="],
+
+ "@unrs/resolver-binding-darwin-arm64": ["@unrs/resolver-binding-darwin-arm64@1.11.1", "", { "os": "darwin", "cpu": "arm64" }, "sha512-gPVA1UjRu1Y/IsB/dQEsp2V1pm44Of6+LWvbLc9SDk1c2KhhDRDBUkQCYVWe6f26uJb3fOK8saWMgtX8IrMk3g=="],
+
+ "@unrs/resolver-binding-darwin-x64": ["@unrs/resolver-binding-darwin-x64@1.11.1", "", { "os": "darwin", "cpu": "x64" }, "sha512-cFzP7rWKd3lZaCsDze07QX1SC24lO8mPty9vdP+YVa3MGdVgPmFc59317b2ioXtgCMKGiCLxJ4HQs62oz6GfRQ=="],
+
+ "@unrs/resolver-binding-freebsd-x64": ["@unrs/resolver-binding-freebsd-x64@1.11.1", "", { "os": "freebsd", "cpu": "x64" }, "sha512-fqtGgak3zX4DCB6PFpsH5+Kmt/8CIi4Bry4rb1ho6Av2QHTREM+47y282Uqiu3ZRF5IQioJQ5qWRV6jduA+iGw=="],
+
+ "@unrs/resolver-binding-linux-arm-gnueabihf": ["@unrs/resolver-binding-linux-arm-gnueabihf@1.11.1", "", { "os": "linux", "cpu": "arm" }, "sha512-u92mvlcYtp9MRKmP+ZvMmtPN34+/3lMHlyMj7wXJDeXxuM0Vgzz0+PPJNsro1m3IZPYChIkn944wW8TYgGKFHw=="],
+
+ "@unrs/resolver-binding-linux-arm-musleabihf": ["@unrs/resolver-binding-linux-arm-musleabihf@1.11.1", "", { "os": "linux", "cpu": "arm" }, "sha512-cINaoY2z7LVCrfHkIcmvj7osTOtm6VVT16b5oQdS4beibX2SYBwgYLmqhBjA1t51CarSaBuX5YNsWLjsqfW5Cw=="],
+
+ "@unrs/resolver-binding-linux-arm64-gnu": ["@unrs/resolver-binding-linux-arm64-gnu@1.11.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-34gw7PjDGB9JgePJEmhEqBhWvCiiWCuXsL9hYphDF7crW7UgI05gyBAi6MF58uGcMOiOqSJ2ybEeCvHcq0BCmQ=="],
+
+ "@unrs/resolver-binding-linux-arm64-musl": ["@unrs/resolver-binding-linux-arm64-musl@1.11.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-RyMIx6Uf53hhOtJDIamSbTskA99sPHS96wxVE/bJtePJJtpdKGXO1wY90oRdXuYOGOTuqjT8ACccMc4K6QmT3w=="],
+
+ "@unrs/resolver-binding-linux-ppc64-gnu": ["@unrs/resolver-binding-linux-ppc64-gnu@1.11.1", "", { "os": "linux", "cpu": "ppc64" }, "sha512-D8Vae74A4/a+mZH0FbOkFJL9DSK2R6TFPC9M+jCWYia/q2einCubX10pecpDiTmkJVUH+y8K3BZClycD8nCShA=="],
+
+ "@unrs/resolver-binding-linux-riscv64-gnu": ["@unrs/resolver-binding-linux-riscv64-gnu@1.11.1", "", { "os": "linux", "cpu": "none" }, "sha512-frxL4OrzOWVVsOc96+V3aqTIQl1O2TjgExV4EKgRY09AJ9leZpEg8Ak9phadbuX0BA4k8U5qtvMSQQGGmaJqcQ=="],
+
+ "@unrs/resolver-binding-linux-riscv64-musl": ["@unrs/resolver-binding-linux-riscv64-musl@1.11.1", "", { "os": "linux", "cpu": "none" }, "sha512-mJ5vuDaIZ+l/acv01sHoXfpnyrNKOk/3aDoEdLO/Xtn9HuZlDD6jKxHlkN8ZhWyLJsRBxfv9GYM2utQ1SChKew=="],
+
+ "@unrs/resolver-binding-linux-s390x-gnu": ["@unrs/resolver-binding-linux-s390x-gnu@1.11.1", "", { "os": "linux", "cpu": "s390x" }, "sha512-kELo8ebBVtb9sA7rMe1Cph4QHreByhaZ2QEADd9NzIQsYNQpt9UkM9iqr2lhGr5afh885d/cB5QeTXSbZHTYPg=="],
+
+ "@unrs/resolver-binding-linux-x64-gnu": ["@unrs/resolver-binding-linux-x64-gnu@1.11.1", "", { "os": "linux", "cpu": "x64" }, "sha512-C3ZAHugKgovV5YvAMsxhq0gtXuwESUKc5MhEtjBpLoHPLYM+iuwSj3lflFwK3DPm68660rZ7G8BMcwSro7hD5w=="],
+
+ "@unrs/resolver-binding-linux-x64-musl": ["@unrs/resolver-binding-linux-x64-musl@1.11.1", "", { "os": "linux", "cpu": "x64" }, "sha512-rV0YSoyhK2nZ4vEswT/QwqzqQXw5I6CjoaYMOX0TqBlWhojUf8P94mvI7nuJTeaCkkds3QE4+zS8Ko+GdXuZtA=="],
+
+ "@unrs/resolver-binding-wasm32-wasi": ["@unrs/resolver-binding-wasm32-wasi@1.11.1", "", { "dependencies": { "@napi-rs/wasm-runtime": "^0.2.11" }, "cpu": "none" }, "sha512-5u4RkfxJm+Ng7IWgkzi3qrFOvLvQYnPBmjmZQ8+szTK/b31fQCnleNl1GgEt7nIsZRIf5PLhPwT0WM+q45x/UQ=="],
+
+ "@unrs/resolver-binding-win32-arm64-msvc": ["@unrs/resolver-binding-win32-arm64-msvc@1.11.1", "", { "os": "win32", "cpu": "arm64" }, "sha512-nRcz5Il4ln0kMhfL8S3hLkxI85BXs3o8EYoattsJNdsX4YUU89iOkVn7g0VHSRxFuVMdM4Q1jEpIId1Ihim/Uw=="],
+
+ "@unrs/resolver-binding-win32-ia32-msvc": ["@unrs/resolver-binding-win32-ia32-msvc@1.11.1", "", { "os": "win32", "cpu": "ia32" }, "sha512-DCEI6t5i1NmAZp6pFonpD5m7i6aFrpofcp4LA2i8IIq60Jyo28hamKBxNrZcyOwVOZkgsRp9O2sXWBWP8MnvIQ=="],
+
+ "@unrs/resolver-binding-win32-x64-msvc": ["@unrs/resolver-binding-win32-x64-msvc@1.11.1", "", { "os": "win32", "cpu": "x64" }, "sha512-lrW200hZdbfRtztbygyaq/6jP6AKE8qQN2KvPcJ+x7wiD038YtnYtZ82IMNJ69GJibV7bwL3y9FgK+5w/pYt6g=="],
+
+ "@vitejs/plugin-react": ["@vitejs/plugin-react@4.7.0", "", { "dependencies": { "@babel/core": "^7.28.0", "@babel/plugin-transform-react-jsx-self": "^7.27.1", "@babel/plugin-transform-react-jsx-source": "^7.27.1", "@rolldown/pluginutils": "1.0.0-beta.27", "@types/babel__core": "^7.20.5", "react-refresh": "^0.17.0" }, "peerDependencies": { "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0" } }, "sha512-gUu9hwfWvvEDBBmgtAowQCojwZmJ5mcLn3aufeCsitijs3+f2NsrPtlAWIR6OPiqljl96GVCUbLe0HyqIpVaoA=="],
+
+ "@xobotyi/scrollbar-width": ["@xobotyi/scrollbar-width@1.9.5", "", {}, "sha512-N8tkAACJx2ww8vFMneJmaAgmjAG1tnVBZJRLRcx061tmsLRZHSEZSLuGWnwPtunsSLvSqXQ2wfp7Mgqg1I+2dQ=="],
+
+ "abort-controller": ["abort-controller@3.0.0", "", { "dependencies": { "event-target-shim": "^5.0.0" } }, "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg=="],
+
+ "acorn": ["acorn@8.14.0", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA=="],
+
+ "acorn-jsx": ["acorn-jsx@5.3.2", "", { "peerDependencies": { "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ=="],
+
+ "acorn-walk": ["acorn-walk@8.3.2", "", {}, "sha512-cjkyv4OtNCIeqhHrfS81QWXoCBPExR/J62oyEqepVw8WaQeSqpW2uhuLPh1m9eWhDuOo/jUXVTlifvesOWp/4A=="],
+
+ "agentkeepalive": ["agentkeepalive@4.6.0", "", { "dependencies": { "humanize-ms": "^1.2.1" } }, "sha512-kja8j7PjmncONqaTsB8fQ+wE2mSU2DJ9D4XKoJ5PFWIdRMa6SLSN1ff4mOr4jCbfRSsxR4keIiySJU0N9T5hIQ=="],
+
+ "ajv": ["ajv@6.12.6", "", { "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", "json-schema-traverse": "^0.4.1", "uri-js": "^4.2.2" } }, "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g=="],
+
+ "ansi-regex": ["ansi-regex@6.2.2", "", {}, "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg=="],
+
+ "ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="],
+
+ "any-promise": ["any-promise@1.3.0", "", {}, "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A=="],
+
+ "anymatch": ["anymatch@3.1.3", "", { "dependencies": { "normalize-path": "^3.0.0", "picomatch": "^2.0.4" } }, "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw=="],
+
+ "arg": ["arg@5.0.2", "", {}, "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg=="],
+
+ "argparse": ["argparse@2.0.1", "", {}, "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="],
+
+ "aria-hidden": ["aria-hidden@1.2.6", "", { "dependencies": { "tslib": "^2.0.0" } }, "sha512-ik3ZgC9dY/lYVVM++OISsaYDeg1tb0VtP5uL3ouh1koGOaUMDPpbFIei4JkFimWUFPn90sbMNMXQAIVOlnYKJA=="],
+
+ "array-buffer-byte-length": ["array-buffer-byte-length@1.0.2", "", { "dependencies": { "call-bound": "^1.0.3", "is-array-buffer": "^3.0.5" } }, "sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw=="],
+
+ "array-includes": ["array-includes@3.1.9", "", { "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.4", "define-properties": "^1.2.1", "es-abstract": "^1.24.0", "es-object-atoms": "^1.1.1", "get-intrinsic": "^1.3.0", "is-string": "^1.1.1", "math-intrinsics": "^1.1.0" } }, "sha512-FmeCCAenzH0KH381SPT5FZmiA/TmpndpcaShhfgEN9eCVjnFBqq3l1xrI42y8+PPLI6hypzou4GXw00WHmPBLQ=="],
+
+ "array.prototype.findlastindex": ["array.prototype.findlastindex@1.2.6", "", { "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.4", "define-properties": "^1.2.1", "es-abstract": "^1.23.9", "es-errors": "^1.3.0", "es-object-atoms": "^1.1.1", "es-shim-unscopables": "^1.1.0" } }, "sha512-F/TKATkzseUExPlfvmwQKGITM3DGTK+vkAsCZoDc5daVygbJBnjEUCbgkAvVFsgfXfX4YIqZ/27G3k3tdXrTxQ=="],
+
+ "array.prototype.flat": ["array.prototype.flat@1.3.3", "", { "dependencies": { "call-bind": "^1.0.8", "define-properties": "^1.2.1", "es-abstract": "^1.23.5", "es-shim-unscopables": "^1.0.2" } }, "sha512-rwG/ja1neyLqCuGZ5YYrznA62D4mZXg0i1cIskIUKSiqF3Cje9/wXAls9B9s1Wa2fomMsIv8czB8jZcPmxCXFg=="],
+
+ "array.prototype.flatmap": ["array.prototype.flatmap@1.3.3", "", { "dependencies": { "call-bind": "^1.0.8", "define-properties": "^1.2.1", "es-abstract": "^1.23.5", "es-shim-unscopables": "^1.0.2" } }, "sha512-Y7Wt51eKJSyi80hFrJCePGGNo5ktJCslFuboqJsbf57CCPcm5zztluPlc4/aD8sWsKvlwatezpV4U1efk8kpjg=="],
+
+ "arraybuffer.prototype.slice": ["arraybuffer.prototype.slice@1.0.4", "", { "dependencies": { "array-buffer-byte-length": "^1.0.1", "call-bind": "^1.0.8", "define-properties": "^1.2.1", "es-abstract": "^1.23.5", "es-errors": "^1.3.0", "get-intrinsic": "^1.2.6", "is-array-buffer": "^3.0.4" } }, "sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ=="],
+
+ "async-function": ["async-function@1.0.0", "", {}, "sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA=="],
+
+ "asynckit": ["asynckit@0.4.0", "", {}, "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="],
+
+ "atomic-sleep": ["atomic-sleep@1.0.0", "", {}, "sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ=="],
+
+ "autoprefixer": ["autoprefixer@10.4.21", "", { "dependencies": { "browserslist": "^4.24.4", "caniuse-lite": "^1.0.30001702", "fraction.js": "^4.3.7", "normalize-range": "^0.1.2", "picocolors": "^1.1.1", "postcss-value-parser": "^4.2.0" }, "peerDependencies": { "postcss": "^8.1.0" }, "bin": { "autoprefixer": "bin/autoprefixer" } }, "sha512-O+A6LWV5LDHSJD3LjHYoNi4VLsj/Whi7k6zG12xTYaU4cQ8oxQGckXNX8cRHK5yOZ/ppVHe0ZBXGzSV9jXdVbQ=="],
+
+ "available-typed-arrays": ["available-typed-arrays@1.0.7", "", { "dependencies": { "possible-typed-array-names": "^1.0.0" } }, "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ=="],
+
+ "babel-plugin-macros": ["babel-plugin-macros@3.1.0", "", { "dependencies": { "@babel/runtime": "^7.12.5", "cosmiconfig": "^7.0.0", "resolve": "^1.19.0" } }, "sha512-Cg7TFGpIr01vOQNODXOOaGz2NpCU5gl8x1qJFbb6hbZxR7XrcE2vtbAsTAbJ7/xwJtUuJEw8K8Zr/AE0LHlesg=="],
+
+ "balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="],
+
+ "binary-extensions": ["binary-extensions@2.3.0", "", {}, "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw=="],
+
+ "blake3-wasm": ["blake3-wasm@2.1.5", "", {}, "sha512-F1+K8EbfOZE49dtoPtmxUQrpXaBIl3ICvasLh+nJta0xkz+9kF/7uet9fLnwKqhDrmj6g+6K3Tw9yQPUg2ka5g=="],
+
+ "brace-expansion": ["brace-expansion@1.1.12", "", { "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg=="],
+
+ "braces": ["braces@3.0.3", "", { "dependencies": { "fill-range": "^7.1.1" } }, "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA=="],
+
+ "browserslist": ["browserslist@4.25.4", "", { "dependencies": { "caniuse-lite": "^1.0.30001737", "electron-to-chromium": "^1.5.211", "node-releases": "^2.0.19", "update-browserslist-db": "^1.1.3" }, "bin": { "browserslist": "cli.js" } }, "sha512-4jYpcjabC606xJ3kw2QwGEZKX0Aw7sgQdZCvIK9dhVSPh76BKo+C+btT1RRofH7B+8iNpEbgGNVWiLki5q93yg=="],
+
+ "call-bind": ["call-bind@1.0.8", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.0", "es-define-property": "^1.0.0", "get-intrinsic": "^1.2.4", "set-function-length": "^1.2.2" } }, "sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww=="],
+
+ "call-bind-apply-helpers": ["call-bind-apply-helpers@1.0.2", "", { "dependencies": { "es-errors": "^1.3.0", "function-bind": "^1.1.2" } }, "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ=="],
+
+ "call-bound": ["call-bound@1.0.4", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.2", "get-intrinsic": "^1.3.0" } }, "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg=="],
+
+ "callsites": ["callsites@3.1.0", "", {}, "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ=="],
+
+ "camelcase-css": ["camelcase-css@2.0.1", "", {}, "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA=="],
+
+ "caniuse-lite": ["caniuse-lite@1.0.30001741", "", {}, "sha512-QGUGitqsc8ARjLdgAfxETDhRbJ0REsP6O3I96TAth/mVjh2cYzN2u+3AzPP3aVSm2FehEItaJw1xd+IGBXWeSw=="],
+
+ "chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="],
+
+ "chokidar": ["chokidar@3.6.0", "", { "dependencies": { "anymatch": "~3.1.2", "braces": "~3.0.2", "glob-parent": "~5.1.2", "is-binary-path": "~2.1.0", "is-glob": "~4.0.1", "normalize-path": "~3.0.0", "readdirp": "~3.6.0" }, "optionalDependencies": { "fsevents": "~2.3.2" } }, "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw=="],
+
+ "class-variance-authority": ["class-variance-authority@0.7.1", "", { "dependencies": { "clsx": "^2.1.1" } }, "sha512-Ka+9Trutv7G8M6WT6SeiRWz792K5qEqIGEGzXKhAE6xOWAY6pPH8U+9IY3oCMv6kqTmLsv7Xh/2w2RigkePMsg=="],
+
+ "cloudflare": ["cloudflare@5.0.0", "", { "dependencies": { "@types/node": "^18.11.18", "@types/node-fetch": "^2.6.4", "abort-controller": "^3.0.0", "agentkeepalive": "^4.2.1", "form-data-encoder": "1.7.2", "formdata-node": "^4.3.2", "node-fetch": "^2.6.7" } }, "sha512-aBCEQKZUABNBP8ELDWvxLRRw3qFJDZhYCLywOVubmaD0QZlfagiK7wiAfWJK0N+pMpQJyGufhqQzQHPW+ypHdw=="],
+
+ "clsx": ["clsx@2.1.1", "", {}, "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA=="],
+
+ "cmdk": ["cmdk@1.1.1", "", { "dependencies": { "@radix-ui/react-compose-refs": "^1.1.1", "@radix-ui/react-dialog": "^1.1.6", "@radix-ui/react-id": "^1.1.0", "@radix-ui/react-primitive": "^2.0.2" }, "peerDependencies": { "react": "^18 || ^19 || ^19.0.0-rc", "react-dom": "^18 || ^19 || ^19.0.0-rc" } }, "sha512-Vsv7kFaXm+ptHDMZ7izaRsP70GgrW9NBNGswt9OZaVBLlE0SNpDq8eu/VGXyF9r7M0azK3Wy7OlYXsuyYLFzHg=="],
+
+ "color": ["color@4.2.3", "", { "dependencies": { "color-convert": "^2.0.1", "color-string": "^1.9.0" } }, "sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A=="],
+
+ "color-convert": ["color-convert@2.0.1", "", { "dependencies": { "color-name": "~1.1.4" } }, "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ=="],
+
+ "color-name": ["color-name@1.1.4", "", {}, "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="],
+
+ "color-string": ["color-string@1.9.1", "", { "dependencies": { "color-name": "^1.0.0", "simple-swizzle": "^0.2.2" } }, "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg=="],
+
+ "combined-stream": ["combined-stream@1.0.8", "", { "dependencies": { "delayed-stream": "~1.0.0" } }, "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg=="],
+
+ "commander": ["commander@4.1.1", "", {}, "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA=="],
+
+ "concat-map": ["concat-map@0.0.1", "", {}, "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg=="],
+
+ "convert-source-map": ["convert-source-map@2.0.0", "", {}, "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg=="],
+
+ "cookie": ["cookie@1.0.2", "", {}, "sha512-9Kr/j4O16ISv8zBBhJoi4bXOYNTkFLOqSL3UDB0njXxCXNezjeyVrJyGOWtgfs/q2km1gwBcfH8q1yEGoMYunA=="],
+
+ "copy-to-clipboard": ["copy-to-clipboard@3.3.3", "", { "dependencies": { "toggle-selection": "^1.0.6" } }, "sha512-2KV8NhB5JqC3ky0r9PMCAZKbUHSwtEo4CwCs0KXgruG43gX5PMqDEBbVU4OUzw2MuAWUfsuFmWvEKG5QRfSnJA=="],
+
+ "cosmiconfig": ["cosmiconfig@7.1.0", "", { "dependencies": { "@types/parse-json": "^4.0.0", "import-fresh": "^3.2.1", "parse-json": "^5.0.0", "path-type": "^4.0.0", "yaml": "^1.10.0" } }, "sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA=="],
+
+ "cross-spawn": ["cross-spawn@7.0.6", "", { "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", "which": "^2.0.1" } }, "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA=="],
+
+ "css-in-js-utils": ["css-in-js-utils@3.1.0", "", { "dependencies": { "hyphenate-style-name": "^1.0.3" } }, "sha512-fJAcud6B3rRu+KHYk+Bwf+WFL2MDCJJ1XG9x137tJQ0xYxor7XziQtuGFbWNdqrvF4Tk26O3H73nfVqXt/fW1A=="],
+
+ "css-tree": ["css-tree@1.1.3", "", { "dependencies": { "mdn-data": "2.0.14", "source-map": "^0.6.1" } }, "sha512-tRpdppF7TRazZrjJ6v3stzv93qxRcSsFmW6cX0Zm2NVKpxE1WV1HblnghVv9TreireHkqI/VDEsfolRF1p6y7Q=="],
+
+ "cssesc": ["cssesc@3.0.0", "", { "bin": { "cssesc": "bin/cssesc" } }, "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg=="],
+
+ "csstype": ["csstype@3.1.3", "", {}, "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw=="],
+
+ "d3-array": ["d3-array@3.2.4", "", { "dependencies": { "internmap": "1 - 2" } }, "sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg=="],
+
+ "d3-color": ["d3-color@3.1.0", "", {}, "sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA=="],
+
+ "d3-ease": ["d3-ease@3.0.1", "", {}, "sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w=="],
+
+ "d3-format": ["d3-format@3.1.0", "", {}, "sha512-YyUI6AEuY/Wpt8KWLgZHsIU86atmikuoOmCfommt0LYHiQSPjvX2AcFc38PX0CBpr2RCyZhjex+NS/LPOv6YqA=="],
+
+ "d3-interpolate": ["d3-interpolate@3.0.1", "", { "dependencies": { "d3-color": "1 - 3" } }, "sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g=="],
+
+ "d3-path": ["d3-path@3.1.0", "", {}, "sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ=="],
+
+ "d3-scale": ["d3-scale@4.0.2", "", { "dependencies": { "d3-array": "2.10.0 - 3", "d3-format": "1 - 3", "d3-interpolate": "1.2.0 - 3", "d3-time": "2.1.1 - 3", "d3-time-format": "2 - 4" } }, "sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ=="],
+
+ "d3-shape": ["d3-shape@3.2.0", "", { "dependencies": { "d3-path": "^3.1.0" } }, "sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA=="],
+
+ "d3-time": ["d3-time@3.1.0", "", { "dependencies": { "d3-array": "2 - 3" } }, "sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q=="],
+
+ "d3-time-format": ["d3-time-format@4.1.0", "", { "dependencies": { "d3-time": "1 - 3" } }, "sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg=="],
+
+ "d3-timer": ["d3-timer@3.0.1", "", {}, "sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA=="],
+
+ "data-view-buffer": ["data-view-buffer@1.0.2", "", { "dependencies": { "call-bound": "^1.0.3", "es-errors": "^1.3.0", "is-data-view": "^1.0.2" } }, "sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ=="],
+
+ "data-view-byte-length": ["data-view-byte-length@1.0.2", "", { "dependencies": { "call-bound": "^1.0.3", "es-errors": "^1.3.0", "is-data-view": "^1.0.2" } }, "sha512-tuhGbE6CfTM9+5ANGf+oQb72Ky/0+s3xKUpHvShfiz2RxMFgFPjsXuRLBVMtvMs15awe45SRb83D6wH4ew6wlQ=="],
+
+ "data-view-byte-offset": ["data-view-byte-offset@1.0.1", "", { "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", "is-data-view": "^1.0.1" } }, "sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ=="],
+
+ "date-fns": ["date-fns@4.1.0", "", {}, "sha512-Ukq0owbQXxa/U3EGtsdVBkR1w7KOQ5gIBqdH2hkvknzZPYvBxb/aa6E8L7tmjFtkwZBu3UXBbjIgPo/Ez4xaNg=="],
+
+ "date-fns-jalali": ["date-fns-jalali@4.1.0-0", "", {}, "sha512-hTIP/z+t+qKwBDcmmsnmjWTduxCg+5KfdqWQvb2X/8C9+knYY6epN/pfxdDuyVlSVeFz0sM5eEfwIUQ70U4ckg=="],
+
+ "debug": ["debug@4.4.1", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ=="],
+
+ "decimal.js-light": ["decimal.js-light@2.5.1", "", {}, "sha512-qIMFpTMZmny+MMIitAB6D7iVPEorVw6YQRWkvarTkT4tBeSLLiHzcwj6q0MmYSFCiVpiqPJTJEYIrpcPzVEIvg=="],
+
+ "deep-is": ["deep-is@0.1.4", "", {}, "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ=="],
+
+ "define-data-property": ["define-data-property@1.1.4", "", { "dependencies": { "es-define-property": "^1.0.0", "es-errors": "^1.3.0", "gopd": "^1.0.1" } }, "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A=="],
+
+ "define-properties": ["define-properties@1.2.1", "", { "dependencies": { "define-data-property": "^1.0.1", "has-property-descriptors": "^1.0.0", "object-keys": "^1.1.1" } }, "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg=="],
+
+ "defu": ["defu@6.1.4", "", {}, "sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg=="],
+
+ "delayed-stream": ["delayed-stream@1.0.0", "", {}, "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ=="],
+
+ "detect-libc": ["detect-libc@2.0.4", "", {}, "sha512-3UDv+G9CsCKO1WKMGw9fwq/SWJYbI0c5Y7LU1AXYoDdbhE2AHQ6N6Nb34sG8Fj7T5APy8qXDCKuuIHd1BR0tVA=="],
+
+ "detect-node-es": ["detect-node-es@1.1.0", "", {}, "sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ=="],
+
+ "didyoumean": ["didyoumean@1.2.2", "", {}, "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw=="],
+
+ "dlv": ["dlv@1.1.3", "", {}, "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA=="],
+
+ "doctrine": ["doctrine@2.1.0", "", { "dependencies": { "esutils": "^2.0.2" } }, "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw=="],
+
+ "dom-helpers": ["dom-helpers@5.2.1", "", { "dependencies": { "@babel/runtime": "^7.8.7", "csstype": "^3.0.2" } }, "sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA=="],
+
+ "dunder-proto": ["dunder-proto@1.0.1", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.1", "es-errors": "^1.3.0", "gopd": "^1.2.0" } }, "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A=="],
+
+ "eastasianwidth": ["eastasianwidth@0.2.0", "", {}, "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA=="],
+
+ "electron-to-chromium": ["electron-to-chromium@1.5.218", "", {}, "sha512-uwwdN0TUHs8u6iRgN8vKeWZMRll4gBkz+QMqdS7DDe49uiK68/UX92lFb61oiFPrpYZNeZIqa4bA7O6Aiasnzg=="],
+
+ "embla-carousel": ["embla-carousel@8.6.0", "", {}, "sha512-SjWyZBHJPbqxHOzckOfo8lHisEaJWmwd23XppYFYVh10bU66/Pn5tkVkbkCMZVdbUE5eTCI2nD8OyIP4Z+uwkA=="],
+
+ "embla-carousel-react": ["embla-carousel-react@8.6.0", "", { "dependencies": { "embla-carousel": "8.6.0", "embla-carousel-reactive-utils": "8.6.0" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.1 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" } }, "sha512-0/PjqU7geVmo6F734pmPqpyHqiM99olvyecY7zdweCw+6tKEXnrE90pBiBbMMU8s5tICemzpQ3hi5EpxzGW+JA=="],
+
+ "embla-carousel-reactive-utils": ["embla-carousel-reactive-utils@8.6.0", "", { "peerDependencies": { "embla-carousel": "8.6.0" } }, "sha512-fMVUDUEx0/uIEDM0Mz3dHznDhfX+znCCDCeIophYb1QGVM7YThSWX+wz11zlYwWFOr74b4QLGg0hrGPJeG2s4A=="],
+
+ "emoji-regex": ["emoji-regex@9.2.2", "", {}, "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg=="],
+
+ "error-ex": ["error-ex@1.3.2", "", { "dependencies": { "is-arrayish": "^0.2.1" } }, "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g=="],
+
+ "error-stack-parser": ["error-stack-parser@2.1.4", "", { "dependencies": { "stackframe": "^1.3.4" } }, "sha512-Sk5V6wVazPhq5MhpO+AUxJn5x7XSXGl1R93Vn7i+zS15KDVxQijejNCrz8340/2bgLBjR9GtEG8ZVKONDjcqGQ=="],
+
+ "error-stack-parser-es": ["error-stack-parser-es@1.0.5", "", {}, "sha512-5qucVt2XcuGMcEGgWI7i+yZpmpByQ8J1lHhcL7PwqCwu9FPP3VUXzT4ltHe5i2z9dePwEHcDVOAfSnHsOlCXRA=="],
+
+ "es-abstract": ["es-abstract@1.24.0", "", { "dependencies": { "array-buffer-byte-length": "^1.0.2", "arraybuffer.prototype.slice": "^1.0.4", "available-typed-arrays": "^1.0.7", "call-bind": "^1.0.8", "call-bound": "^1.0.4", "data-view-buffer": "^1.0.2", "data-view-byte-length": "^1.0.2", "data-view-byte-offset": "^1.0.1", "es-define-property": "^1.0.1", "es-errors": "^1.3.0", "es-object-atoms": "^1.1.1", "es-set-tostringtag": "^2.1.0", "es-to-primitive": "^1.3.0", "function.prototype.name": "^1.1.8", "get-intrinsic": "^1.3.0", "get-proto": "^1.0.1", "get-symbol-description": "^1.1.0", "globalthis": "^1.0.4", "gopd": "^1.2.0", "has-property-descriptors": "^1.0.2", "has-proto": "^1.2.0", "has-symbols": "^1.1.0", "hasown": "^2.0.2", "internal-slot": "^1.1.0", "is-array-buffer": "^3.0.5", "is-callable": "^1.2.7", "is-data-view": "^1.0.2", "is-negative-zero": "^2.0.3", "is-regex": "^1.2.1", "is-set": "^2.0.3", "is-shared-array-buffer": "^1.0.4", "is-string": "^1.1.1", "is-typed-array": "^1.1.15", "is-weakref": "^1.1.1", "math-intrinsics": "^1.1.0", "object-inspect": "^1.13.4", "object-keys": "^1.1.1", "object.assign": "^4.1.7", "own-keys": "^1.0.1", "regexp.prototype.flags": "^1.5.4", "safe-array-concat": "^1.1.3", "safe-push-apply": "^1.0.0", "safe-regex-test": "^1.1.0", "set-proto": "^1.0.0", "stop-iteration-iterator": "^1.1.0", "string.prototype.trim": "^1.2.10", "string.prototype.trimend": "^1.0.9", "string.prototype.trimstart": "^1.0.8", "typed-array-buffer": "^1.0.3", "typed-array-byte-length": "^1.0.3", "typed-array-byte-offset": "^1.0.4", "typed-array-length": "^1.0.7", "unbox-primitive": "^1.1.0", "which-typed-array": "^1.1.19" } }, "sha512-WSzPgsdLtTcQwm4CROfS5ju2Wa1QQcVeT37jFjYzdFz1r9ahadC8B8/a4qxJxM+09F18iumCdRmlr96ZYkQvEg=="],
+
+ "es-define-property": ["es-define-property@1.0.1", "", {}, "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g=="],
+
+ "es-errors": ["es-errors@1.3.0", "", {}, "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw=="],
+
+ "es-object-atoms": ["es-object-atoms@1.1.1", "", { "dependencies": { "es-errors": "^1.3.0" } }, "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA=="],
+
+ "es-set-tostringtag": ["es-set-tostringtag@2.1.0", "", { "dependencies": { "es-errors": "^1.3.0", "get-intrinsic": "^1.2.6", "has-tostringtag": "^1.0.2", "hasown": "^2.0.2" } }, "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA=="],
+
+ "es-shim-unscopables": ["es-shim-unscopables@1.1.0", "", { "dependencies": { "hasown": "^2.0.2" } }, "sha512-d9T8ucsEhh8Bi1woXCf+TIKDIROLG5WCkxg8geBCbvk22kzwC5G2OnXVMO6FUsvQlgUUXQ2itephWDLqDzbeCw=="],
+
+ "es-to-primitive": ["es-to-primitive@1.3.0", "", { "dependencies": { "is-callable": "^1.2.7", "is-date-object": "^1.0.5", "is-symbol": "^1.0.4" } }, "sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g=="],
+
+ "esbuild": ["esbuild@0.25.9", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.25.9", "@esbuild/android-arm": "0.25.9", "@esbuild/android-arm64": "0.25.9", "@esbuild/android-x64": "0.25.9", "@esbuild/darwin-arm64": "0.25.9", "@esbuild/darwin-x64": "0.25.9", "@esbuild/freebsd-arm64": "0.25.9", "@esbuild/freebsd-x64": "0.25.9", "@esbuild/linux-arm": "0.25.9", "@esbuild/linux-arm64": "0.25.9", "@esbuild/linux-ia32": "0.25.9", "@esbuild/linux-loong64": "0.25.9", "@esbuild/linux-mips64el": "0.25.9", "@esbuild/linux-ppc64": "0.25.9", "@esbuild/linux-riscv64": "0.25.9", "@esbuild/linux-s390x": "0.25.9", "@esbuild/linux-x64": "0.25.9", "@esbuild/netbsd-arm64": "0.25.9", "@esbuild/netbsd-x64": "0.25.9", "@esbuild/openbsd-arm64": "0.25.9", "@esbuild/openbsd-x64": "0.25.9", "@esbuild/openharmony-arm64": "0.25.9", "@esbuild/sunos-x64": "0.25.9", "@esbuild/win32-arm64": "0.25.9", "@esbuild/win32-ia32": "0.25.9", "@esbuild/win32-x64": "0.25.9" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-CRbODhYyQx3qp7ZEwzxOk4JBqmD/seJrzPa/cGjY1VtIn5E09Oi9/dB4JwctnfZ8Q8iT7rioVv5k/FNT/uf54g=="],
+
+ "escalade": ["escalade@3.2.0", "", {}, "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA=="],
+
+ "escape-string-regexp": ["escape-string-regexp@4.0.0", "", {}, "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA=="],
+
+ "eslint": ["eslint@9.35.0", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.1", "@eslint/config-array": "^0.21.0", "@eslint/config-helpers": "^0.3.1", "@eslint/core": "^0.15.2", "@eslint/eslintrc": "^3.3.1", "@eslint/js": "9.35.0", "@eslint/plugin-kit": "^0.3.5", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", "@humanwhocodes/retry": "^0.4.2", "@types/estree": "^1.0.6", "@types/json-schema": "^7.0.15", "ajv": "^6.12.4", "chalk": "^4.0.0", "cross-spawn": "^7.0.6", "debug": "^4.3.2", "escape-string-regexp": "^4.0.0", "eslint-scope": "^8.4.0", "eslint-visitor-keys": "^4.2.1", "espree": "^10.4.0", "esquery": "^1.5.0", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", "file-entry-cache": "^8.0.0", "find-up": "^5.0.0", "glob-parent": "^6.0.2", "ignore": "^5.2.0", "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", "json-stable-stringify-without-jsonify": "^1.0.1", "lodash.merge": "^4.6.2", "minimatch": "^3.1.2", "natural-compare": "^1.4.0", "optionator": "^0.9.3" }, "peerDependencies": { "jiti": "*" }, "optionalPeers": ["jiti"], "bin": { "eslint": "bin/eslint.js" } }, "sha512-QePbBFMJFjgmlE+cXAlbHZbHpdFVS2E/6vzCy7aKlebddvl1vadiC4JFV5u/wqTkNUwEV8WrQi257jf5f06hrg=="],
+
+ "eslint-import-context": ["eslint-import-context@0.1.9", "", { "dependencies": { "get-tsconfig": "^4.10.1", "stable-hash-x": "^0.2.0" }, "peerDependencies": { "unrs-resolver": "^1.0.0" }, "optionalPeers": ["unrs-resolver"] }, "sha512-K9Hb+yRaGAGUbwjhFNHvSmmkZs9+zbuoe3kFQ4V1wYjrepUFYM2dZAfNtjbbj3qsPfUfsA68Bx/ICWQMi+C8Eg=="],
+
+ "eslint-import-resolver-node": ["eslint-import-resolver-node@0.3.9", "", { "dependencies": { "debug": "^3.2.7", "is-core-module": "^2.13.0", "resolve": "^1.22.4" } }, "sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g=="],
+
+ "eslint-import-resolver-typescript": ["eslint-import-resolver-typescript@4.4.4", "", { "dependencies": { "debug": "^4.4.1", "eslint-import-context": "^0.1.8", "get-tsconfig": "^4.10.1", "is-bun-module": "^2.0.0", "stable-hash-x": "^0.2.0", "tinyglobby": "^0.2.14", "unrs-resolver": "^1.7.11" }, "peerDependencies": { "eslint": "*", "eslint-plugin-import": "*", "eslint-plugin-import-x": "*" }, "optionalPeers": ["eslint-plugin-import", "eslint-plugin-import-x"] }, "sha512-1iM2zeBvrYmUNTj2vSC/90JTHDth+dfOfiNKkxApWRsTJYNrc8rOdxxIf5vazX+BiAXTeOT0UvWpGI/7qIWQOw=="],
+
+ "eslint-module-utils": ["eslint-module-utils@2.12.1", "", { "dependencies": { "debug": "^3.2.7" } }, "sha512-L8jSWTze7K2mTg0vos/RuLRS5soomksDPoJLXIslC7c8Wmut3bx7CPpJijDcBZtxQ5lrbUdM+s0OlNbz0DCDNw=="],
+
+ "eslint-plugin-import": ["eslint-plugin-import@2.32.0", "", { "dependencies": { "@rtsao/scc": "^1.1.0", "array-includes": "^3.1.9", "array.prototype.findlastindex": "^1.2.6", "array.prototype.flat": "^1.3.3", "array.prototype.flatmap": "^1.3.3", "debug": "^3.2.7", "doctrine": "^2.1.0", "eslint-import-resolver-node": "^0.3.9", "eslint-module-utils": "^2.12.1", "hasown": "^2.0.2", "is-core-module": "^2.16.1", "is-glob": "^4.0.3", "minimatch": "^3.1.2", "object.fromentries": "^2.0.8", "object.groupby": "^1.0.3", "object.values": "^1.2.1", "semver": "^6.3.1", "string.prototype.trimend": "^1.0.9", "tsconfig-paths": "^3.15.0" }, "peerDependencies": { "eslint": "^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8 || ^9" } }, "sha512-whOE1HFo/qJDyX4SnXzP4N6zOWn79WhnCUY/iDR0mPfQZO8wcYE4JClzI2oZrhBnnMUCBCHZhO6VQyoBU95mZA=="],
+
+ "eslint-plugin-react-hooks": ["eslint-plugin-react-hooks@5.2.0", "", { "peerDependencies": { "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0" } }, "sha512-+f15FfK64YQwZdJNELETdn5ibXEUQmW1DZL6KXhNnc2heoy/sg9VJJeT7n8TlMWouzWqSWavFkIhHyIbIAEapg=="],
+
+ "eslint-plugin-react-refresh": ["eslint-plugin-react-refresh@0.4.20", "", { "peerDependencies": { "eslint": ">=8.40" } }, "sha512-XpbHQ2q5gUF8BGOX4dHe+71qoirYMhApEPZ7sfhF/dNnOF1UXnCMGZf79SFTBO7Bz5YEIT4TMieSlJBWhP9WBA=="],
+
+ "eslint-scope": ["eslint-scope@8.4.0", "", { "dependencies": { "esrecurse": "^4.3.0", "estraverse": "^5.2.0" } }, "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg=="],
+
+ "eslint-visitor-keys": ["eslint-visitor-keys@4.2.1", "", {}, "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ=="],
+
+ "espree": ["espree@10.4.0", "", { "dependencies": { "acorn": "^8.15.0", "acorn-jsx": "^5.3.2", "eslint-visitor-keys": "^4.2.1" } }, "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ=="],
+
+ "esquery": ["esquery@1.6.0", "", { "dependencies": { "estraverse": "^5.1.0" } }, "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg=="],
+
+ "esrecurse": ["esrecurse@4.3.0", "", { "dependencies": { "estraverse": "^5.2.0" } }, "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag=="],
+
+ "estraverse": ["estraverse@5.3.0", "", {}, "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA=="],
+
+ "esutils": ["esutils@2.0.3", "", {}, "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g=="],
+
+ "event-target-shim": ["event-target-shim@5.0.1", "", {}, "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ=="],
+
+ "eventemitter3": ["eventemitter3@4.0.7", "", {}, "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw=="],
+
+ "exit-hook": ["exit-hook@2.2.1", "", {}, "sha512-eNTPlAD67BmP31LDINZ3U7HSF8l57TxOY2PmBJ1shpCvpnxBF93mWCE8YHBnXs8qiUZJc9WDcWIeC3a2HIAMfw=="],
+
+ "exsolve": ["exsolve@1.0.7", "", {}, "sha512-VO5fQUzZtI6C+vx4w/4BWJpg3s/5l+6pRQEHzFRM8WFi4XffSP1Z+4qi7GbjWbvRQEbdIco5mIMq+zX4rPuLrw=="],
+
+ "fast-deep-equal": ["fast-deep-equal@3.1.3", "", {}, "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="],
+
+ "fast-equals": ["fast-equals@5.2.2", "", {}, "sha512-V7/RktU11J3I36Nwq2JnZEM7tNm17eBJz+u25qdxBZeCKiX6BkVSZQjwWIr+IobgnZy+ag73tTZgZi7tr0LrBw=="],
+
+ "fast-glob": ["fast-glob@3.3.3", "", { "dependencies": { "@nodelib/fs.stat": "^2.0.2", "@nodelib/fs.walk": "^1.2.3", "glob-parent": "^5.1.2", "merge2": "^1.3.0", "micromatch": "^4.0.8" } }, "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg=="],
+
+ "fast-json-stable-stringify": ["fast-json-stable-stringify@2.1.0", "", {}, "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw=="],
+
+ "fast-levenshtein": ["fast-levenshtein@2.0.6", "", {}, "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw=="],
+
+ "fast-redact": ["fast-redact@3.5.0", "", {}, "sha512-dwsoQlS7h9hMeYUq1W++23NDcBLV4KqONnITDV9DjfS3q1SgDGVrBdvvTLUotWtPSD7asWDV9/CmsZPy8Hf70A=="],
+
+ "fast-shallow-equal": ["fast-shallow-equal@1.0.0", "", {}, "sha512-HPtaa38cPgWvaCFmRNhlc6NG7pv6NUHqjPgVAkWGoB9mQMwYB27/K0CvOM5Czy+qpT3e8XJ6Q4aPAnzpNpzNaw=="],
+
+ "fastest-stable-stringify": ["fastest-stable-stringify@2.0.2", "", {}, "sha512-bijHueCGd0LqqNK9b5oCMHc0MluJAx0cwqASgbWMvkO01lCYgIhacVRLcaDz3QnyYIRNJRDwMb41VuT6pHJ91Q=="],
+
+ "fastq": ["fastq@1.19.1", "", { "dependencies": { "reusify": "^1.0.4" } }, "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ=="],
+
+ "fdir": ["fdir@6.5.0", "", { "peerDependencies": { "picomatch": "^3 || ^4" }, "optionalPeers": ["picomatch"] }, "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg=="],
+
+ "file-entry-cache": ["file-entry-cache@8.0.0", "", { "dependencies": { "flat-cache": "^4.0.0" } }, "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ=="],
+
+ "fill-range": ["fill-range@7.1.1", "", { "dependencies": { "to-regex-range": "^5.0.1" } }, "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg=="],
+
+ "find-root": ["find-root@1.1.0", "", {}, "sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng=="],
+
+ "find-up": ["find-up@5.0.0", "", { "dependencies": { "locate-path": "^6.0.0", "path-exists": "^4.0.0" } }, "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng=="],
+
+ "flat-cache": ["flat-cache@4.0.1", "", { "dependencies": { "flatted": "^3.2.9", "keyv": "^4.5.4" } }, "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw=="],
+
+ "flatted": ["flatted@3.3.3", "", {}, "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg=="],
+
+ "for-each": ["for-each@0.3.5", "", { "dependencies": { "is-callable": "^1.2.7" } }, "sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg=="],
+
+ "foreground-child": ["foreground-child@3.3.1", "", { "dependencies": { "cross-spawn": "^7.0.6", "signal-exit": "^4.0.1" } }, "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw=="],
+
+ "form-data": ["form-data@4.0.4", "", { "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", "es-set-tostringtag": "^2.1.0", "hasown": "^2.0.2", "mime-types": "^2.1.12" } }, "sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow=="],
+
+ "form-data-encoder": ["form-data-encoder@1.7.2", "", {}, "sha512-qfqtYan3rxrnCk1VYaA4H+Ms9xdpPqvLZa6xmMgFvhO32x7/3J/ExcTd6qpxM0vH2GdMI+poehyBZvqfMTto8A=="],
+
+ "formdata-node": ["formdata-node@4.4.1", "", { "dependencies": { "node-domexception": "1.0.0", "web-streams-polyfill": "4.0.0-beta.3" } }, "sha512-0iirZp3uVDjVGt9p49aTaqjk84TrglENEDuqfdlZQ1roC9CWlPk6Avf8EEnZNcAqPonwkG35x4n3ww/1THYAeQ=="],
+
+ "fraction.js": ["fraction.js@4.3.7", "", {}, "sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew=="],
+
+ "framer-motion": ["framer-motion@12.23.12", "", { "dependencies": { "motion-dom": "^12.23.12", "motion-utils": "^12.23.6", "tslib": "^2.4.0" }, "peerDependencies": { "@emotion/is-prop-valid": "*", "react": "^18.0.0 || ^19.0.0", "react-dom": "^18.0.0 || ^19.0.0" }, "optionalPeers": ["@emotion/is-prop-valid", "react", "react-dom"] }, "sha512-6e78rdVtnBvlEVgu6eFEAgG9v3wLnYEboM8I5O5EXvfKC8gxGQB8wXJdhkMy10iVcn05jl6CNw7/HTsTCfwcWg=="],
+
+ "fsevents": ["fsevents@2.3.3", "", { "os": "darwin" }, "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="],
+
+ "function-bind": ["function-bind@1.1.2", "", {}, "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA=="],
+
+ "function.prototype.name": ["function.prototype.name@1.1.8", "", { "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.3", "define-properties": "^1.2.1", "functions-have-names": "^1.2.3", "hasown": "^2.0.2", "is-callable": "^1.2.7" } }, "sha512-e5iwyodOHhbMr/yNrc7fDYG4qlbIvI5gajyzPnb5TCwyhjApznQh1BMFou9b30SevY43gCJKXycoCBjMbsuW0Q=="],
+
+ "functions-have-names": ["functions-have-names@1.2.3", "", {}, "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ=="],
+
+ "gensync": ["gensync@1.0.0-beta.2", "", {}, "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg=="],
+
+ "get-intrinsic": ["get-intrinsic@1.3.0", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.2", "es-define-property": "^1.0.1", "es-errors": "^1.3.0", "es-object-atoms": "^1.1.1", "function-bind": "^1.1.2", "get-proto": "^1.0.1", "gopd": "^1.2.0", "has-symbols": "^1.1.0", "hasown": "^2.0.2", "math-intrinsics": "^1.1.0" } }, "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ=="],
+
+ "get-nonce": ["get-nonce@1.0.1", "", {}, "sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q=="],
+
+ "get-port": ["get-port@7.1.0", "", {}, "sha512-QB9NKEeDg3xxVwCCwJQ9+xycaz6pBB6iQ76wiWMl1927n0Kir6alPiP+yuiICLLU4jpMe08dXfpebuQppFA2zw=="],
+
+ "get-proto": ["get-proto@1.0.1", "", { "dependencies": { "dunder-proto": "^1.0.1", "es-object-atoms": "^1.0.0" } }, "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g=="],
+
+ "get-symbol-description": ["get-symbol-description@1.1.0", "", { "dependencies": { "call-bound": "^1.0.3", "es-errors": "^1.3.0", "get-intrinsic": "^1.2.6" } }, "sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg=="],
+
+ "get-tsconfig": ["get-tsconfig@4.10.1", "", { "dependencies": { "resolve-pkg-maps": "^1.0.0" } }, "sha512-auHyJ4AgMz7vgS8Hp3N6HXSmlMdUyhSUrfBF16w153rxtLIEOE+HGqaBppczZvnHLqQJfiHotCYpNhl0lUROFQ=="],
+
+ "glob": ["glob@10.4.5", "", { "dependencies": { "foreground-child": "^3.1.0", "jackspeak": "^3.1.2", "minimatch": "^9.0.4", "minipass": "^7.1.2", "package-json-from-dist": "^1.0.0", "path-scurry": "^1.11.1" }, "bin": { "glob": "dist/esm/bin.mjs" } }, "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg=="],
+
+ "glob-parent": ["glob-parent@6.0.2", "", { "dependencies": { "is-glob": "^4.0.3" } }, "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A=="],
+
+ "glob-to-regexp": ["glob-to-regexp@0.4.1", "", {}, "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw=="],
+
+ "globals": ["globals@16.4.0", "", {}, "sha512-ob/2LcVVaVGCYN+r14cnwnoDPUufjiYgSqRhiFD0Q1iI4Odora5RE8Iv1D24hAz5oMophRGkGz+yuvQmmUMnMw=="],
+
+ "globalthis": ["globalthis@1.0.4", "", { "dependencies": { "define-properties": "^1.2.1", "gopd": "^1.0.1" } }, "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ=="],
+
+ "gopd": ["gopd@1.2.0", "", {}, "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg=="],
+
+ "graphemer": ["graphemer@1.4.0", "", {}, "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag=="],
+
+ "has-bigints": ["has-bigints@1.1.0", "", {}, "sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg=="],
+
+ "has-flag": ["has-flag@4.0.0", "", {}, "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ=="],
+
+ "has-property-descriptors": ["has-property-descriptors@1.0.2", "", { "dependencies": { "es-define-property": "^1.0.0" } }, "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg=="],
+
+ "has-proto": ["has-proto@1.2.0", "", { "dependencies": { "dunder-proto": "^1.0.0" } }, "sha512-KIL7eQPfHQRC8+XluaIw7BHUwwqL19bQn4hzNgdr+1wXoU0KKj6rufu47lhY7KbJR2C6T6+PfyN0Ea7wkSS+qQ=="],
+
+ "has-symbols": ["has-symbols@1.1.0", "", {}, "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ=="],
+
+ "has-tostringtag": ["has-tostringtag@1.0.2", "", { "dependencies": { "has-symbols": "^1.0.3" } }, "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw=="],
+
+ "hasown": ["hasown@2.0.2", "", { "dependencies": { "function-bind": "^1.1.2" } }, "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ=="],
+
+ "hoist-non-react-statics": ["hoist-non-react-statics@3.3.2", "", { "dependencies": { "react-is": "^16.7.0" } }, "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw=="],
+
+ "hono": ["hono@4.9.6", "", {}, "sha512-doVjXhSFvYZ7y0dNokjwwSahcrAfdz+/BCLvAMa/vHLzjj8+CFyV5xteThGUsKdkaasgN+gF2mUxao+SGLpUeA=="],
+
+ "humanize-ms": ["humanize-ms@1.2.1", "", { "dependencies": { "ms": "^2.0.0" } }, "sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ=="],
+
+ "hyphenate-style-name": ["hyphenate-style-name@1.1.0", "", {}, "sha512-WDC/ui2VVRrz3jOVi+XtjqkDjiVjTtFaAGiW37k6b+ohyQ5wYDOGkvCZa8+H0nx3gyvv0+BST9xuOgIyGQ00gw=="],
+
+ "ignore": ["ignore@5.3.2", "", {}, "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g=="],
+
+ "immer": ["immer@10.1.3", "", {}, "sha512-tmjF/k8QDKydUlm3mZU+tjM6zeq9/fFpPqH9SzWmBnVVKsPBg/V66qsMwb3/Bo90cgUN+ghdVBess+hPsxUyRw=="],
+
+ "import-fresh": ["import-fresh@3.3.1", "", { "dependencies": { "parent-module": "^1.0.0", "resolve-from": "^4.0.0" } }, "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ=="],
+
+ "imurmurhash": ["imurmurhash@0.1.4", "", {}, "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA=="],
+
+ "inline-style-prefixer": ["inline-style-prefixer@7.0.1", "", { "dependencies": { "css-in-js-utils": "^3.1.0" } }, "sha512-lhYo5qNTQp3EvSSp3sRvXMbVQTLrvGV6DycRMJ5dm2BLMiJ30wpXKdDdgX+GmJZ5uQMucwRKHamXSst3Sj/Giw=="],
+
+ "input-otp": ["input-otp@1.4.2", "", { "peerDependencies": { "react": "^16.8 || ^17.0 || ^18.0 || ^19.0.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0.0 || ^19.0.0-rc" } }, "sha512-l3jWwYNvrEa6NTCt7BECfCm48GvwuZzkoeG3gBL2w4CHeOXW3eKFmf9UNYkNfYc3mxMrthMnxjIE07MT0zLBQA=="],
+
+ "internal-slot": ["internal-slot@1.1.0", "", { "dependencies": { "es-errors": "^1.3.0", "hasown": "^2.0.2", "side-channel": "^1.1.0" } }, "sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw=="],
+
+ "internmap": ["internmap@2.0.3", "", {}, "sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg=="],
+
+ "is-array-buffer": ["is-array-buffer@3.0.5", "", { "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.3", "get-intrinsic": "^1.2.6" } }, "sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A=="],
+
+ "is-arrayish": ["is-arrayish@0.3.2", "", {}, "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ=="],
+
+ "is-async-function": ["is-async-function@2.1.1", "", { "dependencies": { "async-function": "^1.0.0", "call-bound": "^1.0.3", "get-proto": "^1.0.1", "has-tostringtag": "^1.0.2", "safe-regex-test": "^1.1.0" } }, "sha512-9dgM/cZBnNvjzaMYHVoxxfPj2QXt22Ev7SuuPrs+xav0ukGB0S6d4ydZdEiM48kLx5kDV+QBPrpVnFyefL8kkQ=="],
+
+ "is-bigint": ["is-bigint@1.1.0", "", { "dependencies": { "has-bigints": "^1.0.2" } }, "sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ=="],
+
+ "is-binary-path": ["is-binary-path@2.1.0", "", { "dependencies": { "binary-extensions": "^2.0.0" } }, "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw=="],
+
+ "is-boolean-object": ["is-boolean-object@1.2.2", "", { "dependencies": { "call-bound": "^1.0.3", "has-tostringtag": "^1.0.2" } }, "sha512-wa56o2/ElJMYqjCjGkXri7it5FbebW5usLw/nPmCMs5DeZ7eziSYZhSmPRn0txqeW4LnAmQQU7FgqLpsEFKM4A=="],
+
+ "is-bun-module": ["is-bun-module@2.0.0", "", { "dependencies": { "semver": "^7.7.1" } }, "sha512-gNCGbnnnnFAUGKeZ9PdbyeGYJqewpmc2aKHUEMO5nQPWU9lOmv7jcmQIv+qHD8fXW6W7qfuCwX4rY9LNRjXrkQ=="],
+
+ "is-callable": ["is-callable@1.2.7", "", {}, "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA=="],
+
+ "is-core-module": ["is-core-module@2.16.1", "", { "dependencies": { "hasown": "^2.0.2" } }, "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w=="],
+
+ "is-data-view": ["is-data-view@1.0.2", "", { "dependencies": { "call-bound": "^1.0.2", "get-intrinsic": "^1.2.6", "is-typed-array": "^1.1.13" } }, "sha512-RKtWF8pGmS87i2D6gqQu/l7EYRlVdfzemCJN/P3UOs//x1QE7mfhvzHIApBTRf7axvT6DMGwSwBXYCT0nfB9xw=="],
+
+ "is-date-object": ["is-date-object@1.1.0", "", { "dependencies": { "call-bound": "^1.0.2", "has-tostringtag": "^1.0.2" } }, "sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg=="],
+
+ "is-extglob": ["is-extglob@2.1.1", "", {}, "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ=="],
+
+ "is-finalizationregistry": ["is-finalizationregistry@1.1.1", "", { "dependencies": { "call-bound": "^1.0.3" } }, "sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg=="],
+
+ "is-fullwidth-code-point": ["is-fullwidth-code-point@3.0.0", "", {}, "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg=="],
+
+ "is-generator-function": ["is-generator-function@1.1.0", "", { "dependencies": { "call-bound": "^1.0.3", "get-proto": "^1.0.0", "has-tostringtag": "^1.0.2", "safe-regex-test": "^1.1.0" } }, "sha512-nPUB5km40q9e8UfN/Zc24eLlzdSf9OfKByBw9CIdw4H1giPMeA0OIJvbchsCu4npfI2QcMVBsGEBHKZ7wLTWmQ=="],
+
+ "is-glob": ["is-glob@4.0.3", "", { "dependencies": { "is-extglob": "^2.1.1" } }, "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg=="],
+
+ "is-map": ["is-map@2.0.3", "", {}, "sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw=="],
+
+ "is-negative-zero": ["is-negative-zero@2.0.3", "", {}, "sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw=="],
+
+ "is-number": ["is-number@7.0.0", "", {}, "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng=="],
+
+ "is-number-object": ["is-number-object@1.1.1", "", { "dependencies": { "call-bound": "^1.0.3", "has-tostringtag": "^1.0.2" } }, "sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw=="],
+
+ "is-regex": ["is-regex@1.2.1", "", { "dependencies": { "call-bound": "^1.0.2", "gopd": "^1.2.0", "has-tostringtag": "^1.0.2", "hasown": "^2.0.2" } }, "sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g=="],
+
+ "is-set": ["is-set@2.0.3", "", {}, "sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg=="],
+
+ "is-shared-array-buffer": ["is-shared-array-buffer@1.0.4", "", { "dependencies": { "call-bound": "^1.0.3" } }, "sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A=="],
+
+ "is-string": ["is-string@1.1.1", "", { "dependencies": { "call-bound": "^1.0.3", "has-tostringtag": "^1.0.2" } }, "sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA=="],
+
+ "is-symbol": ["is-symbol@1.1.1", "", { "dependencies": { "call-bound": "^1.0.2", "has-symbols": "^1.1.0", "safe-regex-test": "^1.1.0" } }, "sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w=="],
+
+ "is-typed-array": ["is-typed-array@1.1.15", "", { "dependencies": { "which-typed-array": "^1.1.16" } }, "sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ=="],
+
+ "is-weakmap": ["is-weakmap@2.0.2", "", {}, "sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w=="],
+
+ "is-weakref": ["is-weakref@1.1.1", "", { "dependencies": { "call-bound": "^1.0.3" } }, "sha512-6i9mGWSlqzNMEqpCp93KwRS1uUOodk2OJ6b+sq7ZPDSy2WuI5NFIxp/254TytR8ftefexkWn5xNiHUNpPOfSew=="],
+
+ "is-weakset": ["is-weakset@2.0.4", "", { "dependencies": { "call-bound": "^1.0.3", "get-intrinsic": "^1.2.6" } }, "sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ=="],
+
+ "isarray": ["isarray@2.0.5", "", {}, "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw=="],
+
+ "isexe": ["isexe@2.0.0", "", {}, "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="],
+
+ "jackspeak": ["jackspeak@3.4.3", "", { "dependencies": { "@isaacs/cliui": "^8.0.2" }, "optionalDependencies": { "@pkgjs/parseargs": "^0.11.0" } }, "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw=="],
+
+ "jiti": ["jiti@1.21.7", "", { "bin": { "jiti": "bin/jiti.js" } }, "sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A=="],
+
+ "js-cookie": ["js-cookie@2.2.1", "", {}, "sha512-HvdH2LzI/EAZcUwA8+0nKNtWHqS+ZmijLA30RwZA0bo7ToCckjK5MkGhjED9KoRcXO6BaGI3I9UIzSA1FKFPOQ=="],
+
+ "js-tokens": ["js-tokens@4.0.0", "", {}, "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="],
+
+ "js-yaml": ["js-yaml@4.1.0", "", { "dependencies": { "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA=="],
+
+ "jsesc": ["jsesc@3.1.0", "", { "bin": { "jsesc": "bin/jsesc" } }, "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA=="],
+
+ "json-buffer": ["json-buffer@3.0.1", "", {}, "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ=="],
+
+ "json-parse-even-better-errors": ["json-parse-even-better-errors@2.3.1", "", {}, "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w=="],
+
+ "json-schema-traverse": ["json-schema-traverse@0.4.1", "", {}, "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg=="],
+
+ "json-stable-stringify-without-jsonify": ["json-stable-stringify-without-jsonify@1.0.1", "", {}, "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw=="],
+
+ "json5": ["json5@2.2.3", "", { "bin": { "json5": "lib/cli.js" } }, "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg=="],
+
+ "keyv": ["keyv@4.5.4", "", { "dependencies": { "json-buffer": "3.0.1" } }, "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw=="],
+
+ "kleur": ["kleur@4.1.5", "", {}, "sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ=="],
+
+ "levn": ["levn@0.4.1", "", { "dependencies": { "prelude-ls": "^1.2.1", "type-check": "~0.4.0" } }, "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ=="],
+
+ "lilconfig": ["lilconfig@3.1.3", "", {}, "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw=="],
+
+ "lines-and-columns": ["lines-and-columns@1.2.4", "", {}, "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg=="],
+
+ "locate-path": ["locate-path@6.0.0", "", { "dependencies": { "p-locate": "^5.0.0" } }, "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw=="],
+
+ "lodash": ["lodash@4.17.21", "", {}, "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="],
+
+ "lodash.merge": ["lodash.merge@4.6.2", "", {}, "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ=="],
+
+ "loose-envify": ["loose-envify@1.4.0", "", { "dependencies": { "js-tokens": "^3.0.0 || ^4.0.0" }, "bin": { "loose-envify": "cli.js" } }, "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q=="],
+
+ "lru-cache": ["lru-cache@5.1.1", "", { "dependencies": { "yallist": "^3.0.2" } }, "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w=="],
+
+ "lucide-react": ["lucide-react@0.525.0", "", { "peerDependencies": { "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-Tm1txJ2OkymCGkvwoHt33Y2JpN5xucVq1slHcgE6Lk0WjDfjgKWor5CdVER8U6DvcfMwh4M8XxmpTiyzfmfDYQ=="],
+
+ "math-intrinsics": ["math-intrinsics@1.1.0", "", {}, "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g=="],
+
+ "mdn-data": ["mdn-data@2.0.14", "", {}, "sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow=="],
+
+ "memoize-one": ["memoize-one@6.0.0", "", {}, "sha512-rkpe71W0N0c0Xz6QD0eJETuWAJGnJ9afsl1srmwPrI+yBCkge5EycXXbYRyvL29zZVUWQCY7InPRCv3GDXuZNw=="],
+
+ "merge2": ["merge2@1.4.1", "", {}, "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg=="],
+
+ "micromatch": ["micromatch@4.0.8", "", { "dependencies": { "braces": "^3.0.3", "picomatch": "^2.3.1" } }, "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA=="],
+
+ "mime": ["mime@3.0.0", "", { "bin": { "mime": "cli.js" } }, "sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A=="],
+
+ "mime-db": ["mime-db@1.52.0", "", {}, "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg=="],
+
+ "mime-types": ["mime-types@2.1.35", "", { "dependencies": { "mime-db": "1.52.0" } }, "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw=="],
+
+ "miniflare": ["miniflare@4.20250906.0", "", { "dependencies": { "@cspotcode/source-map-support": "0.8.1", "acorn": "8.14.0", "acorn-walk": "8.3.2", "exit-hook": "2.2.1", "glob-to-regexp": "0.4.1", "sharp": "^0.33.5", "stoppable": "1.1.0", "undici": "^7.10.0", "workerd": "1.20250906.0", "ws": "8.18.0", "youch": "4.1.0-beta.10", "zod": "3.22.3" }, "bin": { "miniflare": "bootstrap.js" } }, "sha512-T/RWn1sa0ien80s6NjU+Un/tj12gR6wqScZoiLeMJDD4/fK0UXfnbWXJDubnUED8Xjm7RPQ5ESYdE+mhPmMtuQ=="],
+
+ "minimatch": ["minimatch@3.1.2", "", { "dependencies": { "brace-expansion": "^1.1.7" } }, "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw=="],
+
+ "minimist": ["minimist@1.2.8", "", {}, "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA=="],
+
+ "minipass": ["minipass@7.1.2", "", {}, "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw=="],
+
+ "motion-dom": ["motion-dom@12.23.12", "", { "dependencies": { "motion-utils": "^12.23.6" } }, "sha512-RcR4fvMCTESQBD/uKQe49D5RUeDOokkGRmz4ceaJKDBgHYtZtntC/s2vLvY38gqGaytinij/yi3hMcWVcEF5Kw=="],
+
+ "motion-utils": ["motion-utils@12.23.6", "", {}, "sha512-eAWoPgr4eFEOFfg2WjIsMoqJTW6Z8MTUCgn/GZ3VRpClWBdnbjryiA3ZSNLyxCTmCQx4RmYX6jX1iWHbenUPNQ=="],
+
+ "ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="],
+
+ "mz": ["mz@2.7.0", "", { "dependencies": { "any-promise": "^1.0.0", "object-assign": "^4.0.1", "thenify-all": "^1.0.0" } }, "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q=="],
+
+ "nano-css": ["nano-css@5.6.2", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.4.15", "css-tree": "^1.1.2", "csstype": "^3.1.2", "fastest-stable-stringify": "^2.0.2", "inline-style-prefixer": "^7.0.1", "rtl-css-js": "^1.16.1", "stacktrace-js": "^2.0.2", "stylis": "^4.3.0" }, "peerDependencies": { "react": "*", "react-dom": "*" } }, "sha512-+6bHaC8dSDGALM1HJjOHVXpuastdu2xFoZlC77Jh4cg+33Zcgm+Gxd+1xsnpZK14eyHObSp82+ll5y3SX75liw=="],
+
+ "nanoid": ["nanoid@3.3.11", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w=="],
+
+ "napi-postinstall": ["napi-postinstall@0.3.3", "", { "bin": { "napi-postinstall": "lib/cli.js" } }, "sha512-uTp172LLXSxuSYHv/kou+f6KW3SMppU9ivthaVTXian9sOt3XM/zHYHpRZiLgQoxeWfYUnslNWQHF1+G71xcow=="],
+
+ "natural-compare": ["natural-compare@1.4.0", "", {}, "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw=="],
+
+ "next-themes": ["next-themes@0.4.6", "", { "peerDependencies": { "react": "^16.8 || ^17 || ^18 || ^19 || ^19.0.0-rc", "react-dom": "^16.8 || ^17 || ^18 || ^19 || ^19.0.0-rc" } }, "sha512-pZvgD5L0IEvX5/9GWyHMf3m8BKiVQwsCMHfoFosXtXBMnaS0ZnIJ9ST4b4NqLVKDEm8QBxoNNGNaBv2JNF6XNA=="],
+
+ "node-domexception": ["node-domexception@1.0.0", "", {}, "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ=="],
+
+ "node-fetch": ["node-fetch@2.7.0", "", { "dependencies": { "whatwg-url": "^5.0.0" }, "peerDependencies": { "encoding": "^0.1.0" }, "optionalPeers": ["encoding"] }, "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A=="],
+
+ "node-releases": ["node-releases@2.0.20", "", {}, "sha512-7gK6zSXEH6neM212JgfYFXe+GmZQM+fia5SsusuBIUgnPheLFBmIPhtFoAQRj8/7wASYQnbDlHPVwY0BefoFgA=="],
+
+ "normalize-path": ["normalize-path@3.0.0", "", {}, "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA=="],
+
+ "normalize-range": ["normalize-range@0.1.2", "", {}, "sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA=="],
+
+ "object-assign": ["object-assign@4.1.1", "", {}, "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg=="],
+
+ "object-hash": ["object-hash@3.0.0", "", {}, "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw=="],
+
+ "object-inspect": ["object-inspect@1.13.4", "", {}, "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew=="],
+
+ "object-keys": ["object-keys@1.1.1", "", {}, "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA=="],
+
+ "object.assign": ["object.assign@4.1.7", "", { "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.3", "define-properties": "^1.2.1", "es-object-atoms": "^1.0.0", "has-symbols": "^1.1.0", "object-keys": "^1.1.1" } }, "sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw=="],
+
+ "object.fromentries": ["object.fromentries@2.0.8", "", { "dependencies": { "call-bind": "^1.0.7", "define-properties": "^1.2.1", "es-abstract": "^1.23.2", "es-object-atoms": "^1.0.0" } }, "sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ=="],
+
+ "object.groupby": ["object.groupby@1.0.3", "", { "dependencies": { "call-bind": "^1.0.7", "define-properties": "^1.2.1", "es-abstract": "^1.23.2" } }, "sha512-+Lhy3TQTuzXI5hevh8sBGqbmurHbbIjAi0Z4S63nthVLmLxfbj4T54a4CfZrXIrt9iP4mVAPYMo/v99taj3wjQ=="],
+
+ "object.values": ["object.values@1.2.1", "", { "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.3", "define-properties": "^1.2.1", "es-object-atoms": "^1.0.0" } }, "sha512-gXah6aZrcUxjWg2zR2MwouP2eHlCBzdV4pygudehaKXSGW4v2AsRQUK+lwwXhii6KFZcunEnmSUoYp5CXibxtA=="],
+
+ "ohash": ["ohash@2.0.11", "", {}, "sha512-RdR9FQrFwNBNXAr4GixM8YaRZRJ5PUWbKYbE5eOsrwAjJW0q2REGcf79oYPsLyskQCZG1PLN+S/K1V00joZAoQ=="],
+
+ "on-exit-leak-free": ["on-exit-leak-free@2.1.2", "", {}, "sha512-0eJJY6hXLGf1udHwfNftBqH+g73EU4B504nZeKpz1sYRKafAghwxEJunB2O7rDZkL4PGfsMVnTXZ2EjibbqcsA=="],
+
+ "optionator": ["optionator@0.9.4", "", { "dependencies": { "deep-is": "^0.1.3", "fast-levenshtein": "^2.0.6", "levn": "^0.4.1", "prelude-ls": "^1.2.1", "type-check": "^0.4.0", "word-wrap": "^1.2.5" } }, "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g=="],
+
+ "own-keys": ["own-keys@1.0.1", "", { "dependencies": { "get-intrinsic": "^1.2.6", "object-keys": "^1.1.1", "safe-push-apply": "^1.0.0" } }, "sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg=="],
+
+ "p-limit": ["p-limit@3.1.0", "", { "dependencies": { "yocto-queue": "^0.1.0" } }, "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ=="],
+
+ "p-locate": ["p-locate@5.0.0", "", { "dependencies": { "p-limit": "^3.0.2" } }, "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw=="],
+
+ "package-json-from-dist": ["package-json-from-dist@1.0.1", "", {}, "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw=="],
+
+ "parent-module": ["parent-module@1.0.1", "", { "dependencies": { "callsites": "^3.0.0" } }, "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g=="],
+
+ "parse-json": ["parse-json@5.2.0", "", { "dependencies": { "@babel/code-frame": "^7.0.0", "error-ex": "^1.3.1", "json-parse-even-better-errors": "^2.3.0", "lines-and-columns": "^1.1.6" } }, "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg=="],
+
+ "path-exists": ["path-exists@4.0.0", "", {}, "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w=="],
+
+ "path-key": ["path-key@3.1.1", "", {}, "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q=="],
+
+ "path-parse": ["path-parse@1.0.7", "", {}, "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw=="],
+
+ "path-scurry": ["path-scurry@1.11.1", "", { "dependencies": { "lru-cache": "^10.2.0", "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" } }, "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA=="],
+
+ "path-to-regexp": ["path-to-regexp@6.3.0", "", {}, "sha512-Yhpw4T9C6hPpgPeA28us07OJeqZ5EzQTkbfwuhsUg0c237RomFoETJgmp2sa3F/41gfLE6G5cqcYwznmeEeOlQ=="],
+
+ "path-type": ["path-type@4.0.0", "", {}, "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw=="],
+
+ "pathe": ["pathe@2.0.3", "", {}, "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w=="],
+
+ "picocolors": ["picocolors@1.1.1", "", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="],
+
+ "picomatch": ["picomatch@4.0.3", "", {}, "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q=="],
+
+ "pify": ["pify@2.3.0", "", {}, "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog=="],
+
+ "pino": ["pino@9.11.0", "", { "dependencies": { "atomic-sleep": "^1.0.0", "fast-redact": "^3.1.1", "on-exit-leak-free": "^2.1.0", "pino-abstract-transport": "^2.0.0", "pino-std-serializers": "^7.0.0", "process-warning": "^5.0.0", "quick-format-unescaped": "^4.0.3", "real-require": "^0.2.0", "safe-stable-stringify": "^2.3.1", "sonic-boom": "^4.0.1", "thread-stream": "^3.0.0" }, "bin": { "pino": "bin.js" } }, "sha512-+YIodBB9sxcWeR8PrXC2K3gEDyfkUuVEITOcbqrfcj+z5QW4ioIcqZfYFbrLTYLsmAwunbS7nfU/dpBB6PZc1g=="],
+
+ "pino-abstract-transport": ["pino-abstract-transport@2.0.0", "", { "dependencies": { "split2": "^4.0.0" } }, "sha512-F63x5tizV6WCh4R6RHyi2Ml+M70DNRXt/+HANowMflpgGFMAym/VKm6G7ZOQRjqN7XbGxK1Lg9t6ZrtzOaivMw=="],
+
+ "pino-std-serializers": ["pino-std-serializers@7.0.0", "", {}, "sha512-e906FRY0+tV27iq4juKzSYPbUj2do2X2JX4EzSca1631EB2QJQUqGbDuERal7LCtOpxl6x3+nvo9NPZcmjkiFA=="],
+
+ "pirates": ["pirates@4.0.7", "", {}, "sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA=="],
+
+ "possible-typed-array-names": ["possible-typed-array-names@1.1.0", "", {}, "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg=="],
+
+ "postcss": ["postcss@8.5.6", "", { "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg=="],
+
+ "postcss-import": ["postcss-import@15.1.0", "", { "dependencies": { "postcss-value-parser": "^4.0.0", "read-cache": "^1.0.0", "resolve": "^1.1.7" }, "peerDependencies": { "postcss": "^8.0.0" } }, "sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew=="],
+
+ "postcss-js": ["postcss-js@4.0.1", "", { "dependencies": { "camelcase-css": "^2.0.1" }, "peerDependencies": { "postcss": "^8.4.21" } }, "sha512-dDLF8pEO191hJMtlHFPRa8xsizHaM82MLfNkUHdUtVEV3tgTp5oj+8qbEqYM57SLfc74KSbw//4SeJma2LRVIw=="],
+
+ "postcss-load-config": ["postcss-load-config@4.0.2", "", { "dependencies": { "lilconfig": "^3.0.0", "yaml": "^2.3.4" }, "peerDependencies": { "postcss": ">=8.0.9", "ts-node": ">=9.0.0" }, "optionalPeers": ["postcss", "ts-node"] }, "sha512-bSVhyJGL00wMVoPUzAVAnbEoWyqRxkjv64tUl427SKnPrENtq6hJwUojroMz2VB+Q1edmi4IfrAPpami5VVgMQ=="],
+
+ "postcss-nested": ["postcss-nested@6.2.0", "", { "dependencies": { "postcss-selector-parser": "^6.1.1" }, "peerDependencies": { "postcss": "^8.2.14" } }, "sha512-HQbt28KulC5AJzG+cZtj9kvKB93CFCdLvog1WFLf1D+xmMvPGlBstkpTEZfK5+AN9hfJocyBFCNiqyS48bpgzQ=="],
+
+ "postcss-selector-parser": ["postcss-selector-parser@6.1.2", "", { "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" } }, "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg=="],
+
+ "postcss-value-parser": ["postcss-value-parser@4.2.0", "", {}, "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ=="],
+
+ "prelude-ls": ["prelude-ls@1.2.1", "", {}, "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g=="],
+
+ "process-warning": ["process-warning@5.0.0", "", {}, "sha512-a39t9ApHNx2L4+HBnQKqxxHNs1r7KF+Intd8Q/g1bUh6q0WIp9voPXJ/x0j+ZL45KF1pJd9+q2jLIRMfvEshkA=="],
+
+ "prop-types": ["prop-types@15.8.1", "", { "dependencies": { "loose-envify": "^1.4.0", "object-assign": "^4.1.1", "react-is": "^16.13.1" } }, "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg=="],
+
+ "punycode": ["punycode@2.3.1", "", {}, "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg=="],
+
+ "queue-microtask": ["queue-microtask@1.2.3", "", {}, "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A=="],
+
+ "quick-format-unescaped": ["quick-format-unescaped@4.0.4", "", {}, "sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg=="],
+
+ "react": ["react@18.3.1", "", { "dependencies": { "loose-envify": "^1.1.0" } }, "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ=="],
+
+ "react-day-picker": ["react-day-picker@9.9.0", "", { "dependencies": { "@date-fns/tz": "^1.4.1", "date-fns": "^4.1.0", "date-fns-jalali": "^4.1.0-0" }, "peerDependencies": { "react": ">=16.8.0" } }, "sha512-NtkJbuX6cl/VaGNb3sVVhmMA6LSMnL5G3xNL+61IyoZj0mUZFWTg4hmj7PHjIQ8MXN9dHWhUHFoJWG6y60DKSg=="],
+
+ "react-dom": ["react-dom@18.3.1", "", { "dependencies": { "loose-envify": "^1.1.0", "scheduler": "^0.23.2" }, "peerDependencies": { "react": "^18.3.1" } }, "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw=="],
+
+ "react-flow": ["react-flow@1.0.3", "", {}, "sha512-Rx4Oqqbe9y7NyrUGzCrmtNvuGuMJBVjfIeMFuwbtbMc9f22sUSmxMjIA0c8r6Z7iMtB1zOWoGkTgfpmmpH9ReQ=="],
+
+ "react-hook-form": ["react-hook-form@7.62.0", "", { "peerDependencies": { "react": "^16.8.0 || ^17 || ^18 || ^19" } }, "sha512-7KWFejc98xqG/F4bAxpL41NB3o1nnvQO1RWZT3TqRZYL8RryQETGfEdVnJN2fy1crCiBLLjkRBVK05j24FxJGA=="],
+
+ "react-hotkeys-hook": ["react-hotkeys-hook@5.1.0", "", { "peerDependencies": { "react": ">=16.8.0", "react-dom": ">=16.8.0" } }, "sha512-GCNGXjBzV9buOS3REoQFmSmE4WTvBhYQ0YrAeeMZI83bhXg3dRWsLHXDutcVDdEjwJqJCxk5iewWYX5LtFUd7g=="],
+
+ "react-is": ["react-is@18.3.1", "", {}, "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg=="],
+
+ "react-refresh": ["react-refresh@0.17.0", "", {}, "sha512-z6F7K9bV85EfseRCp2bzrpyQ0Gkw1uLoCel9XBVWPg/TjRj94SkJzUTGfOa4bs7iJvBWtQG0Wq7wnI0syw3EBQ=="],
+
+ "react-remove-scroll": ["react-remove-scroll@2.7.1", "", { "dependencies": { "react-remove-scroll-bar": "^2.3.7", "react-style-singleton": "^2.2.3", "tslib": "^2.1.0", "use-callback-ref": "^1.3.3", "use-sidecar": "^1.1.3" }, "peerDependencies": { "@types/react": "*", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-HpMh8+oahmIdOuS5aFKKY6Pyog+FNaZV/XyJOq7b4YFwsFHe5yYfdbIalI4k3vU2nSDql7YskmUseHsRrJqIPA=="],
+
+ "react-remove-scroll-bar": ["react-remove-scroll-bar@2.3.8", "", { "dependencies": { "react-style-singleton": "^2.2.2", "tslib": "^2.0.0" }, "peerDependencies": { "@types/react": "*", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" }, "optionalPeers": ["@types/react"] }, "sha512-9r+yi9+mgU33AKcj6IbT9oRCO78WriSj6t/cF8DWBZJ9aOGPOTEDvdUDz1FwKim7QXWwmHqtdHnRJfhAxEG46Q=="],
+
+ "react-resizable-panels": ["react-resizable-panels@3.0.5", "", { "peerDependencies": { "react": "^16.14.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc", "react-dom": "^16.14.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" } }, "sha512-3z1yN25DMTXLg2wfyFrW32r5k4WEcUa3F7cJ2EgtNK07lnOs4mpM8yWLGunCpkhcQRwJX4fqoLcIh/pHPxzlmQ=="],
+
+ "react-router": ["react-router@6.30.0", "", { "dependencies": { "@remix-run/router": "1.23.0" }, "peerDependencies": { "react": ">=16.8" } }, "sha512-D3X8FyH9nBcTSHGdEKurK7r8OYE1kKFn3d/CF+CoxbSHkxU7o37+Uh7eAHRXr6k2tSExXYO++07PeXJtA/dEhQ=="],
+
+ "react-router-dom": ["react-router-dom@6.30.0", "", { "dependencies": { "@remix-run/router": "1.23.0", "react-router": "6.30.0" }, "peerDependencies": { "react": ">=16.8", "react-dom": ">=16.8" } }, "sha512-x30B78HV5tFk8ex0ITwzC9TTZMua4jGyA9IUlH1JLQYQTFyxr/ZxwOJq7evg1JX1qGVUcvhsmQSKdPncQrjTgA=="],
+
+ "react-select": ["react-select@5.10.2", "", { "dependencies": { "@babel/runtime": "^7.12.0", "@emotion/cache": "^11.4.0", "@emotion/react": "^11.8.1", "@floating-ui/dom": "^1.0.1", "@types/react-transition-group": "^4.4.0", "memoize-one": "^6.0.0", "prop-types": "^15.6.0", "react-transition-group": "^4.3.0", "use-isomorphic-layout-effect": "^1.2.0" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-Z33nHdEFWq9tfnfVXaiM12rbJmk+QjFEztWLtmXqQhz6Al4UZZ9xc0wiatmGtUOCCnHN0WizL3tCMYRENX4rVQ=="],
+
+ "react-smooth": ["react-smooth@4.0.4", "", { "dependencies": { "fast-equals": "^5.0.1", "prop-types": "^15.8.1", "react-transition-group": "^4.4.5" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-gnGKTpYwqL0Iii09gHobNolvX4Kiq4PKx6eWBCYYix+8cdw+cGo3do906l1NBPKkSWx1DghC1dlWG9L2uGd61Q=="],
+
+ "react-style-singleton": ["react-style-singleton@2.2.3", "", { "dependencies": { "get-nonce": "^1.0.0", "tslib": "^2.0.0" }, "peerDependencies": { "@types/react": "*", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-b6jSvxvVnyptAiLjbkWLE/lOnR4lfTtDAl+eUC7RZy+QQWc6wRzIV2CE6xBuMmDxc2qIihtDCZD5NPOFl7fRBQ=="],
+
+ "react-swipeable": ["react-swipeable@7.0.2", "", { "peerDependencies": { "react": "^16.8.3 || ^17 || ^18 || ^19.0.0 || ^19.0.0-rc" } }, "sha512-v1Qx1l+aC2fdxKa9aKJiaU/ZxmJ5o98RMoFwUqAAzVWUcxgfHFXDDruCKXhw6zIYXm6V64JiHgP9f6mlME5l8w=="],
+
+ "react-transition-group": ["react-transition-group@4.4.5", "", { "dependencies": { "@babel/runtime": "^7.5.5", "dom-helpers": "^5.0.1", "loose-envify": "^1.4.0", "prop-types": "^15.6.2" }, "peerDependencies": { "react": ">=16.6.0", "react-dom": ">=16.6.0" } }, "sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g=="],
+
+ "react-universal-interface": ["react-universal-interface@0.6.2", "", { "peerDependencies": { "react": "*", "tslib": "*" } }, "sha512-dg8yXdcQmvgR13RIlZbTRQOoUrDciFVoSBZILwjE2LFISxZZ8loVJKAkuzswl5js8BHda79bIb2b84ehU8IjXw=="],
+
+ "react-use": ["react-use@17.6.0", "", { "dependencies": { "@types/js-cookie": "^2.2.6", "@xobotyi/scrollbar-width": "^1.9.5", "copy-to-clipboard": "^3.3.1", "fast-deep-equal": "^3.1.3", "fast-shallow-equal": "^1.0.0", "js-cookie": "^2.2.1", "nano-css": "^5.6.2", "react-universal-interface": "^0.6.2", "resize-observer-polyfill": "^1.5.1", "screenfull": "^5.1.0", "set-harmonic-interval": "^1.0.1", "throttle-debounce": "^3.0.1", "ts-easing": "^0.2.0", "tslib": "^2.1.0" }, "peerDependencies": { "react": "*", "react-dom": "*" } }, "sha512-OmedEScUMKFfzn1Ir8dBxiLLSOzhKe/dPZwVxcujweSj45aNM7BEGPb9BEVIgVEqEXx6f3/TsXzwIktNgUR02g=="],
+
+ "read-cache": ["read-cache@1.0.0", "", { "dependencies": { "pify": "^2.3.0" } }, "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA=="],
+
+ "readdirp": ["readdirp@3.6.0", "", { "dependencies": { "picomatch": "^2.2.1" } }, "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA=="],
+
+ "real-require": ["real-require@0.2.0", "", {}, "sha512-57frrGM/OCTLqLOAh0mhVA9VBMHd+9U7Zb2THMGdBUoZVOtGbJzjxsYGDJ3A9AYYCP4hn6y1TVbaOfzWtm5GFg=="],
+
+ "recharts": ["recharts@2.15.4", "", { "dependencies": { "clsx": "^2.0.0", "eventemitter3": "^4.0.1", "lodash": "^4.17.21", "react-is": "^18.3.1", "react-smooth": "^4.0.4", "recharts-scale": "^0.4.4", "tiny-invariant": "^1.3.1", "victory-vendor": "^36.6.8" }, "peerDependencies": { "react": "^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", "react-dom": "^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-UT/q6fwS3c1dHbXv2uFgYJ9BMFHu3fwnd7AYZaEQhXuYQ4hgsxLvsUXzGdKeZrW5xopzDCvuA2N41WJ88I7zIw=="],
+
+ "recharts-scale": ["recharts-scale@0.4.5", "", { "dependencies": { "decimal.js-light": "^2.4.1" } }, "sha512-kivNFO+0OcUNu7jQquLXAxz1FIwZj8nrj+YkOKc5694NbjCvcT6aSZiIzNzd2Kul4o4rTto8QVR9lMNtxD4G1w=="],
+
+ "reflect.getprototypeof": ["reflect.getprototypeof@1.0.10", "", { "dependencies": { "call-bind": "^1.0.8", "define-properties": "^1.2.1", "es-abstract": "^1.23.9", "es-errors": "^1.3.0", "es-object-atoms": "^1.0.0", "get-intrinsic": "^1.2.7", "get-proto": "^1.0.1", "which-builtin-type": "^1.2.1" } }, "sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw=="],
+
+ "regexp.prototype.flags": ["regexp.prototype.flags@1.5.4", "", { "dependencies": { "call-bind": "^1.0.8", "define-properties": "^1.2.1", "es-errors": "^1.3.0", "get-proto": "^1.0.1", "gopd": "^1.2.0", "set-function-name": "^2.0.2" } }, "sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA=="],
+
+ "resize-observer-polyfill": ["resize-observer-polyfill@1.5.1", "", {}, "sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg=="],
+
+ "resolve": ["resolve@1.22.10", "", { "dependencies": { "is-core-module": "^2.16.0", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, "bin": { "resolve": "bin/resolve" } }, "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w=="],
+
+ "resolve-from": ["resolve-from@4.0.0", "", {}, "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g=="],
+
+ "resolve-pkg-maps": ["resolve-pkg-maps@1.0.0", "", {}, "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw=="],
+
+ "reusify": ["reusify@1.1.0", "", {}, "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw=="],
+
+ "rollup": ["rollup@4.50.1", "", { "dependencies": { "@types/estree": "1.0.8" }, "optionalDependencies": { "@rollup/rollup-android-arm-eabi": "4.50.1", "@rollup/rollup-android-arm64": "4.50.1", "@rollup/rollup-darwin-arm64": "4.50.1", "@rollup/rollup-darwin-x64": "4.50.1", "@rollup/rollup-freebsd-arm64": "4.50.1", "@rollup/rollup-freebsd-x64": "4.50.1", "@rollup/rollup-linux-arm-gnueabihf": "4.50.1", "@rollup/rollup-linux-arm-musleabihf": "4.50.1", "@rollup/rollup-linux-arm64-gnu": "4.50.1", "@rollup/rollup-linux-arm64-musl": "4.50.1", "@rollup/rollup-linux-loongarch64-gnu": "4.50.1", "@rollup/rollup-linux-ppc64-gnu": "4.50.1", "@rollup/rollup-linux-riscv64-gnu": "4.50.1", "@rollup/rollup-linux-riscv64-musl": "4.50.1", "@rollup/rollup-linux-s390x-gnu": "4.50.1", "@rollup/rollup-linux-x64-gnu": "4.50.1", "@rollup/rollup-linux-x64-musl": "4.50.1", "@rollup/rollup-openharmony-arm64": "4.50.1", "@rollup/rollup-win32-arm64-msvc": "4.50.1", "@rollup/rollup-win32-ia32-msvc": "4.50.1", "@rollup/rollup-win32-x64-msvc": "4.50.1", "fsevents": "~2.3.2" }, "bin": { "rollup": "dist/bin/rollup" } }, "sha512-78E9voJHwnXQMiQdiqswVLZwJIzdBKJ1GdI5Zx6XwoFKUIk09/sSrr+05QFzvYb8q6Y9pPV45zzDuYa3907TZA=="],
+
+ "rtl-css-js": ["rtl-css-js@1.16.1", "", { "dependencies": { "@babel/runtime": "^7.1.2" } }, "sha512-lRQgou1mu19e+Ya0LsTvKrVJ5TYUbqCVPAiImX3UfLTenarvPUl1QFdvu5Z3PYmHT9RCcwIfbjRQBntExyj3Zg=="],
+
+ "run-parallel": ["run-parallel@1.2.0", "", { "dependencies": { "queue-microtask": "^1.2.2" } }, "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA=="],
+
+ "safe-array-concat": ["safe-array-concat@1.1.3", "", { "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.2", "get-intrinsic": "^1.2.6", "has-symbols": "^1.1.0", "isarray": "^2.0.5" } }, "sha512-AURm5f0jYEOydBj7VQlVvDrjeFgthDdEF5H1dP+6mNpoXOMo1quQqJ4wvJDyRZ9+pO3kGWoOdmV08cSv2aJV6Q=="],
+
+ "safe-push-apply": ["safe-push-apply@1.0.0", "", { "dependencies": { "es-errors": "^1.3.0", "isarray": "^2.0.5" } }, "sha512-iKE9w/Z7xCzUMIZqdBsp6pEQvwuEebH4vdpjcDWnyzaI6yl6O9FHvVpmGelvEHNsoY6wGblkxR6Zty/h00WiSA=="],
+
+ "safe-regex-test": ["safe-regex-test@1.1.0", "", { "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", "is-regex": "^1.2.1" } }, "sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw=="],
+
+ "safe-stable-stringify": ["safe-stable-stringify@2.5.0", "", {}, "sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA=="],
+
+ "scheduler": ["scheduler@0.23.2", "", { "dependencies": { "loose-envify": "^1.1.0" } }, "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ=="],
+
+ "screenfull": ["screenfull@5.2.0", "", {}, "sha512-9BakfsO2aUQN2K9Fdbj87RJIEZ82Q9IGim7FqM5OsebfoFC6ZHXgDq/KvniuLTPdeM8wY2o6Dj3WQ7KeQCj3cA=="],
+
+ "semver": ["semver@6.3.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="],
+
+ "set-function-length": ["set-function-length@1.2.2", "", { "dependencies": { "define-data-property": "^1.1.4", "es-errors": "^1.3.0", "function-bind": "^1.1.2", "get-intrinsic": "^1.2.4", "gopd": "^1.0.1", "has-property-descriptors": "^1.0.2" } }, "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg=="],
+
+ "set-function-name": ["set-function-name@2.0.2", "", { "dependencies": { "define-data-property": "^1.1.4", "es-errors": "^1.3.0", "functions-have-names": "^1.2.3", "has-property-descriptors": "^1.0.2" } }, "sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ=="],
+
+ "set-harmonic-interval": ["set-harmonic-interval@1.0.1", "", {}, "sha512-AhICkFV84tBP1aWqPwLZqFvAwqEoVA9kxNMniGEUvzOlm4vLmOFLiTT3UZ6bziJTy4bOVpzWGTfSCbmaayGx8g=="],
+
+ "set-proto": ["set-proto@1.0.0", "", { "dependencies": { "dunder-proto": "^1.0.1", "es-errors": "^1.3.0", "es-object-atoms": "^1.0.0" } }, "sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw=="],
+
+ "sharp": ["sharp@0.33.5", "", { "dependencies": { "color": "^4.2.3", "detect-libc": "^2.0.3", "semver": "^7.6.3" }, "optionalDependencies": { "@img/sharp-darwin-arm64": "0.33.5", "@img/sharp-darwin-x64": "0.33.5", "@img/sharp-libvips-darwin-arm64": "1.0.4", "@img/sharp-libvips-darwin-x64": "1.0.4", "@img/sharp-libvips-linux-arm": "1.0.5", "@img/sharp-libvips-linux-arm64": "1.0.4", "@img/sharp-libvips-linux-s390x": "1.0.4", "@img/sharp-libvips-linux-x64": "1.0.4", "@img/sharp-libvips-linuxmusl-arm64": "1.0.4", "@img/sharp-libvips-linuxmusl-x64": "1.0.4", "@img/sharp-linux-arm": "0.33.5", "@img/sharp-linux-arm64": "0.33.5", "@img/sharp-linux-s390x": "0.33.5", "@img/sharp-linux-x64": "0.33.5", "@img/sharp-linuxmusl-arm64": "0.33.5", "@img/sharp-linuxmusl-x64": "0.33.5", "@img/sharp-wasm32": "0.33.5", "@img/sharp-win32-ia32": "0.33.5", "@img/sharp-win32-x64": "0.33.5" } }, "sha512-haPVm1EkS9pgvHrQ/F3Xy+hgcuMV0Wm9vfIBSiwZ05k+xgb0PkBQpGsAA/oWdDobNaZTH5ppvHtzCFbnSEwHVw=="],
+
+ "shebang-command": ["shebang-command@2.0.0", "", { "dependencies": { "shebang-regex": "^3.0.0" } }, "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA=="],
+
+ "shebang-regex": ["shebang-regex@3.0.0", "", {}, "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A=="],
+
+ "side-channel": ["side-channel@1.1.0", "", { "dependencies": { "es-errors": "^1.3.0", "object-inspect": "^1.13.3", "side-channel-list": "^1.0.0", "side-channel-map": "^1.0.1", "side-channel-weakmap": "^1.0.2" } }, "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw=="],
+
+ "side-channel-list": ["side-channel-list@1.0.0", "", { "dependencies": { "es-errors": "^1.3.0", "object-inspect": "^1.13.3" } }, "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA=="],
+
+ "side-channel-map": ["side-channel-map@1.0.1", "", { "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", "get-intrinsic": "^1.2.5", "object-inspect": "^1.13.3" } }, "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA=="],
+
+ "side-channel-weakmap": ["side-channel-weakmap@1.0.2", "", { "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", "get-intrinsic": "^1.2.5", "object-inspect": "^1.13.3", "side-channel-map": "^1.0.1" } }, "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A=="],
+
+ "signal-exit": ["signal-exit@4.1.0", "", {}, "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw=="],
+
+ "simple-swizzle": ["simple-swizzle@0.2.2", "", { "dependencies": { "is-arrayish": "^0.3.1" } }, "sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg=="],
+
+ "sonic-boom": ["sonic-boom@4.2.0", "", { "dependencies": { "atomic-sleep": "^1.0.0" } }, "sha512-INb7TM37/mAcsGmc9hyyI6+QR3rR1zVRu36B0NeGXKnOOLiZOfER5SA+N7X7k3yUYRzLWafduTDvJAfDswwEww=="],
+
+ "sonner": ["sonner@2.0.7", "", { "peerDependencies": { "react": "^18.0.0 || ^19.0.0 || ^19.0.0-rc", "react-dom": "^18.0.0 || ^19.0.0 || ^19.0.0-rc" } }, "sha512-W6ZN4p58k8aDKA4XPcx2hpIQXBRAgyiWVkYhT7CvK6D3iAu7xjvVyhQHg2/iaKJZ1XVJ4r7XuwGL+WGEK37i9w=="],
+
+ "source-map": ["source-map@0.5.7", "", {}, "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ=="],
+
+ "source-map-js": ["source-map-js@1.2.1", "", {}, "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA=="],
+
+ "split2": ["split2@4.2.0", "", {}, "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg=="],
+
+ "stable-hash-x": ["stable-hash-x@0.2.0", "", {}, "sha512-o3yWv49B/o4QZk5ZcsALc6t0+eCelPc44zZsLtCQnZPDwFpDYSWcDnrv2TtMmMbQ7uKo3J0HTURCqckw23czNQ=="],
+
+ "stack-generator": ["stack-generator@2.0.10", "", { "dependencies": { "stackframe": "^1.3.4" } }, "sha512-mwnua/hkqM6pF4k8SnmZ2zfETsRUpWXREfA/goT8SLCV4iOFa4bzOX2nDipWAZFPTjLvQB82f5yaodMVhK0yJQ=="],
+
+ "stackframe": ["stackframe@1.3.4", "", {}, "sha512-oeVtt7eWQS+Na6F//S4kJ2K2VbRlS9D43mAlMyVpVWovy9o+jfgH8O9agzANzaiLjclA0oYzUXEM4PurhSUChw=="],
+
+ "stacktrace-gps": ["stacktrace-gps@3.1.2", "", { "dependencies": { "source-map": "0.5.6", "stackframe": "^1.3.4" } }, "sha512-GcUgbO4Jsqqg6RxfyTHFiPxdPqF+3LFmQhm7MgCuYQOYuWyqxo5pwRPz5d/u6/WYJdEnWfK4r+jGbyD8TSggXQ=="],
+
+ "stacktrace-js": ["stacktrace-js@2.0.2", "", { "dependencies": { "error-stack-parser": "^2.0.6", "stack-generator": "^2.0.5", "stacktrace-gps": "^3.0.4" } }, "sha512-Je5vBeY4S1r/RnLydLl0TBTi3F2qdfWmYsGvtfZgEI+SCprPppaIhQf5nGcal4gI4cGpCV/duLcAzT1np6sQqg=="],
+
+ "stop-iteration-iterator": ["stop-iteration-iterator@1.1.0", "", { "dependencies": { "es-errors": "^1.3.0", "internal-slot": "^1.1.0" } }, "sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ=="],
+
+ "stoppable": ["stoppable@1.1.0", "", {}, "sha512-KXDYZ9dszj6bzvnEMRYvxgeTHU74QBFL54XKtP3nyMuJ81CFYtABZ3bAzL2EdFUaEwJOBOgENyFj3R7oTzDyyw=="],
+
+ "string-width": ["string-width@5.1.2", "", { "dependencies": { "eastasianwidth": "^0.2.0", "emoji-regex": "^9.2.2", "strip-ansi": "^7.0.1" } }, "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA=="],
+
+ "string-width-cjs": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="],
+
+ "string.prototype.trim": ["string.prototype.trim@1.2.10", "", { "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.2", "define-data-property": "^1.1.4", "define-properties": "^1.2.1", "es-abstract": "^1.23.5", "es-object-atoms": "^1.0.0", "has-property-descriptors": "^1.0.2" } }, "sha512-Rs66F0P/1kedk5lyYyH9uBzuiI/kNRmwJAR9quK6VOtIpZ2G+hMZd+HQbbv25MgCA6gEffoMZYxlTod4WcdrKA=="],
+
+ "string.prototype.trimend": ["string.prototype.trimend@1.0.9", "", { "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.2", "define-properties": "^1.2.1", "es-object-atoms": "^1.0.0" } }, "sha512-G7Ok5C6E/j4SGfyLCloXTrngQIQU3PWtXGst3yM7Bea9FRURf1S42ZHlZZtsNque2FN2PoUhfZXYLNWwEr4dLQ=="],
+
+ "string.prototype.trimstart": ["string.prototype.trimstart@1.0.8", "", { "dependencies": { "call-bind": "^1.0.7", "define-properties": "^1.2.1", "es-object-atoms": "^1.0.0" } }, "sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg=="],
+
+ "strip-ansi": ["strip-ansi@7.1.2", "", { "dependencies": { "ansi-regex": "^6.0.1" } }, "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA=="],
+
+ "strip-ansi-cjs": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="],
+
+ "strip-bom": ["strip-bom@3.0.0", "", {}, "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA=="],
+
+ "strip-json-comments": ["strip-json-comments@3.1.1", "", {}, "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig=="],
+
+ "stylis": ["stylis@4.2.0", "", {}, "sha512-Orov6g6BB1sDfYgzWfTHDOxamtX1bE/zo104Dh9e6fqJ3PooipYyfJ0pUmrZO2wAvO8YbEyeFrkV91XTsGMSrw=="],
+
+ "sucrase": ["sucrase@3.35.0", "", { "dependencies": { "@jridgewell/gen-mapping": "^0.3.2", "commander": "^4.0.0", "glob": "^10.3.10", "lines-and-columns": "^1.1.6", "mz": "^2.7.0", "pirates": "^4.0.1", "ts-interface-checker": "^0.1.9" }, "bin": { "sucrase": "bin/sucrase", "sucrase-node": "bin/sucrase-node" } }, "sha512-8EbVDiu9iN/nESwxeSxDKe0dunta1GOlHufmSSXxMD2z2/tMZpDMpvXQGsc+ajGo8y2uYUmixaSRUc/QPoQ0GA=="],
+
+ "supports-color": ["supports-color@7.2.0", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw=="],
+
+ "supports-preserve-symlinks-flag": ["supports-preserve-symlinks-flag@1.0.0", "", {}, "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w=="],
+
+ "tabbable": ["tabbable@6.2.0", "", {}, "sha512-Cat63mxsVJlzYvN51JmVXIgNoUokrIaT2zLclCXjRd8boZ0004U4KCs/sToJ75C6sdlByWxpYnb5Boif1VSFew=="],
+
+ "tailwind-merge": ["tailwind-merge@3.3.1", "", {}, "sha512-gBXpgUm/3rp1lMZZrM/w7D8GKqshif0zAymAhbCyIt8KMe+0v9DQ7cdYLR4FHH/cKpdTXb+A/tKKU3eolfsI+g=="],
+
+ "tailwindcss": ["tailwindcss@3.4.17", "", { "dependencies": { "@alloc/quick-lru": "^5.2.0", "arg": "^5.0.2", "chokidar": "^3.6.0", "didyoumean": "^1.2.2", "dlv": "^1.1.3", "fast-glob": "^3.3.2", "glob-parent": "^6.0.2", "is-glob": "^4.0.3", "jiti": "^1.21.6", "lilconfig": "^3.1.3", "micromatch": "^4.0.8", "normalize-path": "^3.0.0", "object-hash": "^3.0.0", "picocolors": "^1.1.1", "postcss": "^8.4.47", "postcss-import": "^15.1.0", "postcss-js": "^4.0.1", "postcss-load-config": "^4.0.2", "postcss-nested": "^6.2.0", "postcss-selector-parser": "^6.1.2", "resolve": "^1.22.8", "sucrase": "^3.35.0" }, "bin": { "tailwind": "lib/cli.js", "tailwindcss": "lib/cli.js" } }, "sha512-w33E2aCvSDP0tW9RZuNXadXlkHXqFzSkQew/aIa2i/Sj8fThxwovwlXHSPXTbAHwEIhBFXAedUhP2tueAKP8Og=="],
+
+ "tailwindcss-animate": ["tailwindcss-animate@1.0.7", "", { "peerDependencies": { "tailwindcss": ">=3.0.0 || insiders" } }, "sha512-bl6mpH3T7I3UFxuvDEXLxy/VuFxBk5bbzplh7tXI68mwMokNYd1t9qPBHlnyTwfa4JGC4zP516I1hYYtQ/vspA=="],
+
+ "thenify": ["thenify@3.3.1", "", { "dependencies": { "any-promise": "^1.0.0" } }, "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw=="],
+
+ "thenify-all": ["thenify-all@1.6.0", "", { "dependencies": { "thenify": ">= 3.1.0 < 4" } }, "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA=="],
+
+ "thread-stream": ["thread-stream@3.1.0", "", { "dependencies": { "real-require": "^0.2.0" } }, "sha512-OqyPZ9u96VohAyMfJykzmivOrY2wfMSf3C5TtFJVgN+Hm6aj+voFhlK+kZEIv2FBh1X6Xp3DlnCOfEQ3B2J86A=="],
+
+ "throttle-debounce": ["throttle-debounce@3.0.1", "", {}, "sha512-dTEWWNu6JmeVXY0ZYoPuH5cRIwc0MeGbJwah9KUNYSJwommQpCzTySTpEe8Gs1J23aeWEuAobe4Ag7EHVt/LOg=="],
+
+ "tiny-invariant": ["tiny-invariant@1.3.3", "", {}, "sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg=="],
+
+ "tinyglobby": ["tinyglobby@0.2.15", "", { "dependencies": { "fdir": "^6.5.0", "picomatch": "^4.0.3" } }, "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ=="],
+
+ "to-regex-range": ["to-regex-range@5.0.1", "", { "dependencies": { "is-number": "^7.0.0" } }, "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ=="],
+
+ "toggle-selection": ["toggle-selection@1.0.6", "", {}, "sha512-BiZS+C1OS8g/q2RRbJmy59xpyghNBqrr6k5L/uKBGRsTfxmu3ffiRnd8mlGPUVayg8pvfi5urfnu8TU7DVOkLQ=="],
+
+ "tr46": ["tr46@0.0.3", "", {}, "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw=="],
+
+ "ts-api-utils": ["ts-api-utils@2.1.0", "", { "peerDependencies": { "typescript": ">=4.8.4" } }, "sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ=="],
+
+ "ts-easing": ["ts-easing@0.2.0", "", {}, "sha512-Z86EW+fFFh/IFB1fqQ3/+7Zpf9t2ebOAxNI/V6Wo7r5gqiqtxmgTlQ1qbqQcjLKYeSHPTsEmvlJUDg/EuL0uHQ=="],
+
+ "ts-interface-checker": ["ts-interface-checker@0.1.13", "", {}, "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA=="],
+
+ "tsconfig-paths": ["tsconfig-paths@3.15.0", "", { "dependencies": { "@types/json5": "^0.0.29", "json5": "^1.0.2", "minimist": "^1.2.6", "strip-bom": "^3.0.0" } }, "sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg=="],
+
+ "tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
+
+ "tw-animate-css": ["tw-animate-css@1.3.8", "", {}, "sha512-Qrk3PZ7l7wUcGYhwZloqfkWCmaXZAoqjkdbIDvzfGshwGtexa/DAs9koXxIkrpEasyevandomzCBAV1Yyop5rw=="],
+
+ "type-check": ["type-check@0.4.0", "", { "dependencies": { "prelude-ls": "^1.2.1" } }, "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew=="],
+
+ "typed-array-buffer": ["typed-array-buffer@1.0.3", "", { "dependencies": { "call-bound": "^1.0.3", "es-errors": "^1.3.0", "is-typed-array": "^1.1.14" } }, "sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw=="],
+
+ "typed-array-byte-length": ["typed-array-byte-length@1.0.3", "", { "dependencies": { "call-bind": "^1.0.8", "for-each": "^0.3.3", "gopd": "^1.2.0", "has-proto": "^1.2.0", "is-typed-array": "^1.1.14" } }, "sha512-BaXgOuIxz8n8pIq3e7Atg/7s+DpiYrxn4vdot3w9KbnBhcRQq6o3xemQdIfynqSeXeDrF32x+WvfzmOjPiY9lg=="],
+
+ "typed-array-byte-offset": ["typed-array-byte-offset@1.0.4", "", { "dependencies": { "available-typed-arrays": "^1.0.7", "call-bind": "^1.0.8", "for-each": "^0.3.3", "gopd": "^1.2.0", "has-proto": "^1.2.0", "is-typed-array": "^1.1.15", "reflect.getprototypeof": "^1.0.9" } }, "sha512-bTlAFB/FBYMcuX81gbL4OcpH5PmlFHqlCCpAl8AlEzMz5k53oNDvN8p1PNOWLEmI2x4orp3raOFB51tv9X+MFQ=="],
+
+ "typed-array-length": ["typed-array-length@1.0.7", "", { "dependencies": { "call-bind": "^1.0.7", "for-each": "^0.3.3", "gopd": "^1.0.1", "is-typed-array": "^1.1.13", "possible-typed-array-names": "^1.0.0", "reflect.getprototypeof": "^1.0.6" } }, "sha512-3KS2b+kL7fsuk/eJZ7EQdnEmQoaho/r6KUef7hxvltNA5DR8NAUM+8wJMbJyZ4G9/7i3v5zPBIMN5aybAh2/Jg=="],
+
+ "typescript": ["typescript@5.8.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ=="],
+
+ "typescript-eslint": ["typescript-eslint@8.43.0", "", { "dependencies": { "@typescript-eslint/eslint-plugin": "8.43.0", "@typescript-eslint/parser": "8.43.0", "@typescript-eslint/typescript-estree": "8.43.0", "@typescript-eslint/utils": "8.43.0" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-FyRGJKUGvcFekRRcBKFBlAhnp4Ng8rhe8tuvvkR9OiU0gfd4vyvTRQHEckO6VDlH57jbeUQem2IpqPq9kLJH+w=="],
+
+ "ufo": ["ufo@1.6.1", "", {}, "sha512-9a4/uxlTWJ4+a5i0ooc1rU7C7YOw3wT+UGqdeNNHWnOF9qcMBgLRS+4IYUqbczewFx4mLEig6gawh7X6mFlEkA=="],
+
+ "unbox-primitive": ["unbox-primitive@1.1.0", "", { "dependencies": { "call-bound": "^1.0.3", "has-bigints": "^1.0.2", "has-symbols": "^1.1.0", "which-boxed-primitive": "^1.1.1" } }, "sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw=="],
+
+ "undici": ["undici@7.16.0", "", {}, "sha512-QEg3HPMll0o3t2ourKwOeUAZ159Kn9mx5pnzHRQO8+Wixmh88YdZRiIwat0iNzNNXn0yoEtXJqFpyW7eM8BV7g=="],
+
+ "undici-types": ["undici-types@6.21.0", "", {}, "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ=="],
+
+ "unenv": ["unenv@2.0.0-rc.21", "", { "dependencies": { "defu": "^6.1.4", "exsolve": "^1.0.7", "ohash": "^2.0.11", "pathe": "^2.0.3", "ufo": "^1.6.1" } }, "sha512-Wj7/AMtE9MRnAXa6Su3Lk0LNCfqDYgfwVjwRFVum9U7wsto1imuHqk4kTm7Jni+5A0Hn7dttL6O/zjvUvoo+8A=="],
+
+ "unrs-resolver": ["unrs-resolver@1.11.1", "", { "dependencies": { "napi-postinstall": "^0.3.0" }, "optionalDependencies": { "@unrs/resolver-binding-android-arm-eabi": "1.11.1", "@unrs/resolver-binding-android-arm64": "1.11.1", "@unrs/resolver-binding-darwin-arm64": "1.11.1", "@unrs/resolver-binding-darwin-x64": "1.11.1", "@unrs/resolver-binding-freebsd-x64": "1.11.1", "@unrs/resolver-binding-linux-arm-gnueabihf": "1.11.1", "@unrs/resolver-binding-linux-arm-musleabihf": "1.11.1", "@unrs/resolver-binding-linux-arm64-gnu": "1.11.1", "@unrs/resolver-binding-linux-arm64-musl": "1.11.1", "@unrs/resolver-binding-linux-ppc64-gnu": "1.11.1", "@unrs/resolver-binding-linux-riscv64-gnu": "1.11.1", "@unrs/resolver-binding-linux-riscv64-musl": "1.11.1", "@unrs/resolver-binding-linux-s390x-gnu": "1.11.1", "@unrs/resolver-binding-linux-x64-gnu": "1.11.1", "@unrs/resolver-binding-linux-x64-musl": "1.11.1", "@unrs/resolver-binding-wasm32-wasi": "1.11.1", "@unrs/resolver-binding-win32-arm64-msvc": "1.11.1", "@unrs/resolver-binding-win32-ia32-msvc": "1.11.1", "@unrs/resolver-binding-win32-x64-msvc": "1.11.1" } }, "sha512-bSjt9pjaEBnNiGgc9rUiHGKv5l4/TGzDmYw3RhnkJGtLhbnnA/5qJj7x3dNDCRx/PJxu774LlH8lCOlB4hEfKg=="],
+
+ "update-browserslist-db": ["update-browserslist-db@1.1.3", "", { "dependencies": { "escalade": "^3.2.0", "picocolors": "^1.1.1" }, "peerDependencies": { "browserslist": ">= 4.21.0" }, "bin": { "update-browserslist-db": "cli.js" } }, "sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw=="],
+
+ "uri-js": ["uri-js@4.4.1", "", { "dependencies": { "punycode": "^2.1.0" } }, "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg=="],
+
+ "use-callback-ref": ["use-callback-ref@1.3.3", "", { "dependencies": { "tslib": "^2.0.0" }, "peerDependencies": { "@types/react": "*", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-jQL3lRnocaFtu3V00JToYz/4QkNWswxijDaCVNZRiRTO3HQDLsdu1ZtmIUvV4yPp+rvWm5j0y0TG/S61cuijTg=="],
+
+ "use-isomorphic-layout-effect": ["use-isomorphic-layout-effect@1.2.1", "", { "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-tpZZ+EX0gaghDAiFR37hj5MgY6ZN55kLiPkJsKxBMZ6GZdOSPJXiOzPM984oPYZ5AnehYx5WQp1+ME8I/P/pRA=="],
+
+ "use-sidecar": ["use-sidecar@1.1.3", "", { "dependencies": { "detect-node-es": "^1.1.0", "tslib": "^2.0.0" }, "peerDependencies": { "@types/react": "*", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-Fedw0aZvkhynoPYlA5WXrMCAMm+nSWdZt6lzJQ7Ok8S6Q+VsHmHpRWndVRJ8Be0ZbkfPc5LRYH+5XrzXcEeLRQ=="],
+
+ "use-sync-external-store": ["use-sync-external-store@1.5.0", "", { "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-Rb46I4cGGVBmjamjphe8L/UnvJD+uPPtTkNvX5mZgqdbavhI4EbgIWJiIHXJ8bc/i9EQGPRh4DwEURJ552Do0A=="],
+
+ "util-deprecate": ["util-deprecate@1.0.2", "", {}, "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="],
+
+ "uuid": ["uuid@11.1.0", "", { "bin": { "uuid": "dist/esm/bin/uuid" } }, "sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A=="],
+
+ "vaul": ["vaul@1.1.2", "", { "dependencies": { "@radix-ui/react-dialog": "^1.1.1" }, "peerDependencies": { "react": "^16.8 || ^17.0 || ^18.0 || ^19.0.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0.0 || ^19.0.0-rc" } }, "sha512-ZFkClGpWyI2WUQjdLJ/BaGuV6AVQiJ3uELGk3OYtP+B6yCO7Cmn9vPFXVJkRaGkOJu3m8bQMgtyzNHixULceQA=="],
+
+ "victory-vendor": ["victory-vendor@36.9.2", "", { "dependencies": { "@types/d3-array": "^3.0.3", "@types/d3-ease": "^3.0.0", "@types/d3-interpolate": "^3.0.1", "@types/d3-scale": "^4.0.2", "@types/d3-shape": "^3.1.0", "@types/d3-time": "^3.0.0", "@types/d3-timer": "^3.0.0", "d3-array": "^3.1.6", "d3-ease": "^3.0.1", "d3-interpolate": "^3.0.1", "d3-scale": "^4.0.2", "d3-shape": "^3.1.0", "d3-time": "^3.0.0", "d3-timer": "^3.0.1" } }, "sha512-PnpQQMuxlwYdocC8fIJqVXvkeViHYzotI+NJrCuav0ZYFoq912ZHBk3mCeuj+5/VpodOjPe1z0Fk2ihgzlXqjQ=="],
+
+ "vite": ["vite@6.3.6", "", { "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.4.4", "picomatch": "^4.0.2", "postcss": "^8.5.3", "rollup": "^4.34.9", "tinyglobby": "^0.2.13" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", "jiti": ">=1.21.0", "less": "*", "lightningcss": "^1.21.0", "sass": "*", "sass-embedded": "*", "stylus": "*", "sugarss": "*", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-0msEVHJEScQbhkbVTb/4iHZdJ6SXp/AvxL2sjwYQFfBqleHtnCqv1J3sa9zbWz/6kW1m9Tfzn92vW+kZ1WV6QA=="],
+
+ "web-streams-polyfill": ["web-streams-polyfill@4.0.0-beta.3", "", {}, "sha512-QW95TCTaHmsYfHDybGMwO5IJIM93I/6vTRk+daHTWFPhwh+C8Cg7j7XyKrwrj8Ib6vYXe0ocYNrmzY4xAAN6ug=="],
+
+ "webidl-conversions": ["webidl-conversions@3.0.1", "", {}, "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ=="],
+
+ "whatwg-url": ["whatwg-url@5.0.0", "", { "dependencies": { "tr46": "~0.0.3", "webidl-conversions": "^3.0.0" } }, "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw=="],
+
+ "which": ["which@2.0.2", "", { "dependencies": { "isexe": "^2.0.0" }, "bin": { "node-which": "./bin/node-which" } }, "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA=="],
+
+ "which-boxed-primitive": ["which-boxed-primitive@1.1.1", "", { "dependencies": { "is-bigint": "^1.1.0", "is-boolean-object": "^1.2.1", "is-number-object": "^1.1.1", "is-string": "^1.1.1", "is-symbol": "^1.1.1" } }, "sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA=="],
+
+ "which-builtin-type": ["which-builtin-type@1.2.1", "", { "dependencies": { "call-bound": "^1.0.2", "function.prototype.name": "^1.1.6", "has-tostringtag": "^1.0.2", "is-async-function": "^2.0.0", "is-date-object": "^1.1.0", "is-finalizationregistry": "^1.1.0", "is-generator-function": "^1.0.10", "is-regex": "^1.2.1", "is-weakref": "^1.0.2", "isarray": "^2.0.5", "which-boxed-primitive": "^1.1.0", "which-collection": "^1.0.2", "which-typed-array": "^1.1.16" } }, "sha512-6iBczoX+kDQ7a3+YJBnh3T+KZRxM/iYNPXicqk66/Qfm1b93iu+yOImkg0zHbj5LNOcNv1TEADiZ0xa34B4q6Q=="],
+
+ "which-collection": ["which-collection@1.0.2", "", { "dependencies": { "is-map": "^2.0.3", "is-set": "^2.0.3", "is-weakmap": "^2.0.2", "is-weakset": "^2.0.3" } }, "sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw=="],
+
+ "which-typed-array": ["which-typed-array@1.1.19", "", { "dependencies": { "available-typed-arrays": "^1.0.7", "call-bind": "^1.0.8", "call-bound": "^1.0.4", "for-each": "^0.3.5", "get-proto": "^1.0.1", "gopd": "^1.2.0", "has-tostringtag": "^1.0.2" } }, "sha512-rEvr90Bck4WZt9HHFC4DJMsjvu7x+r6bImz0/BrbWb7A2djJ8hnZMrWnHo9F8ssv0OMErasDhftrfROTyqSDrw=="],
+
+ "word-wrap": ["word-wrap@1.2.5", "", {}, "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA=="],
+
+ "workerd": ["workerd@1.20250906.0", "", { "optionalDependencies": { "@cloudflare/workerd-darwin-64": "1.20250906.0", "@cloudflare/workerd-darwin-arm64": "1.20250906.0", "@cloudflare/workerd-linux-64": "1.20250906.0", "@cloudflare/workerd-linux-arm64": "1.20250906.0", "@cloudflare/workerd-windows-64": "1.20250906.0" }, "bin": { "workerd": "bin/workerd" } }, "sha512-ryVyEaqXPPsr/AxccRmYZZmDAkfQVjhfRqrNTlEeN8aftBk6Ca1u7/VqmfOayjCXrA+O547TauebU+J3IpvFXw=="],
+
+ "wrangler": ["wrangler@4.35.0", "", { "dependencies": { "@cloudflare/kv-asset-handler": "0.4.0", "@cloudflare/unenv-preset": "2.7.3", "blake3-wasm": "2.1.5", "esbuild": "0.25.4", "miniflare": "4.20250906.0", "path-to-regexp": "6.3.0", "unenv": "2.0.0-rc.21", "workerd": "1.20250906.0" }, "optionalDependencies": { "fsevents": "~2.3.2" }, "peerDependencies": { "@cloudflare/workers-types": "^4.20250906.0" }, "optionalPeers": ["@cloudflare/workers-types"], "bin": { "wrangler": "bin/wrangler.js", "wrangler2": "bin/wrangler.js" } }, "sha512-HbyXtbrh4Fi3mU8ussY85tVdQ74qpVS1vctUgaPc+bPrXBTqfDLkZ6VRtHAVF/eBhz4SFmhJtCQpN1caY2Ak8A=="],
+
+ "wrap-ansi": ["wrap-ansi@8.1.0", "", { "dependencies": { "ansi-styles": "^6.1.0", "string-width": "^5.0.1", "strip-ansi": "^7.0.1" } }, "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ=="],
+
+ "wrap-ansi-cjs": ["wrap-ansi@7.0.0", "", { "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0" } }, "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q=="],
+
+ "ws": ["ws@8.18.0", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw=="],
+
+ "yallist": ["yallist@3.1.1", "", {}, "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g=="],
+
+ "yaml": ["yaml@2.8.1", "", { "bin": { "yaml": "bin.mjs" } }, "sha512-lcYcMxX2PO9XMGvAJkJ3OsNMw+/7FKes7/hgerGUYWIoWu5j/+YQqcZr5JnPZWzOsEBgMbSbiSTn/dv/69Mkpw=="],
+
+ "yocto-queue": ["yocto-queue@0.1.0", "", {}, "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q=="],
+
+ "youch": ["youch@4.1.0-beta.10", "", { "dependencies": { "@poppinss/colors": "^4.1.5", "@poppinss/dumper": "^0.6.4", "@speed-highlight/core": "^1.2.7", "cookie": "^1.0.2", "youch-core": "^0.3.3" } }, "sha512-rLfVLB4FgQneDr0dv1oddCVZmKjcJ6yX6mS4pU82Mq/Dt9a3cLZQ62pDBL4AUO+uVrCvtWz3ZFUL2HFAFJ/BXQ=="],
+
+ "youch-core": ["youch-core@0.3.3", "", { "dependencies": { "@poppinss/exception": "^1.2.2", "error-stack-parser-es": "^1.0.5" } }, "sha512-ho7XuGjLaJ2hWHoK8yFnsUGy2Y5uDpqSTq1FkHLK4/oqKtyUU1AFbOOxY4IpC9f0fTLjwYbslUz0Po5BpD1wrA=="],
+
+ "zod": ["zod@4.1.8", "", {}, "sha512-5R1P+WwQqmmMIEACyzSvo4JXHY5WiAFHRMg+zBZKgKS+Q1viRa0C1hmUKtHltoIFKtIdki3pRxkmpP74jnNYHQ=="],
+
+ "zustand": ["zustand@5.0.8", "", { "peerDependencies": { "@types/react": ">=18.0.0", "immer": ">=9.0.6", "react": ">=18.0.0", "use-sync-external-store": ">=1.2.0" }, "optionalPeers": ["@types/react", "immer", "react", "use-sync-external-store"] }, "sha512-gyPKpIaxY9XcO2vSMrLbiER7QMAMGOQZVRdJ6Zi782jkbzZygq5GI9nG8g+sMgitRtndwaBSl7uiqC49o1SSiw=="],
+
+ "@babel/generator/@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.31", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw=="],
+
+ "@emotion/babel-plugin/convert-source-map": ["convert-source-map@1.9.0", "", {}, "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A=="],
+
+ "@eslint-community/eslint-utils/eslint-visitor-keys": ["eslint-visitor-keys@3.4.3", "", {}, "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag=="],
+
+ "@eslint/eslintrc/globals": ["globals@14.0.0", "", {}, "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ=="],
+
+ "@jridgewell/gen-mapping/@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.31", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw=="],
+
+ "@jridgewell/remapping/@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.31", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw=="],
+
+ "@poppinss/dumper/supports-color": ["supports-color@10.2.2", "", {}, "sha512-SS+jx45GF1QjgEXQx4NJZV9ImqmO2NPz5FNsIHrsDjh2YsHnawpan7SNQ1o8NuhrbHZy9AZhIoCUiCeaW/C80g=="],
+
+ "@typescript-eslint/eslint-plugin/ignore": ["ignore@7.0.5", "", {}, "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg=="],
+
+ "@typescript-eslint/typescript-estree/minimatch": ["minimatch@9.0.5", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow=="],
+
+ "@typescript-eslint/typescript-estree/semver": ["semver@7.7.2", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA=="],
+
+ "anymatch/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="],
+
+ "chokidar/glob-parent": ["glob-parent@5.1.2", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="],
+
+ "cloudflare/@types/node": ["@types/node@18.19.124", "", { "dependencies": { "undici-types": "~5.26.4" } }, "sha512-hY4YWZFLs3ku6D2Gqo3RchTd9VRCcrjqp/I0mmohYeUVA5Y8eCXKJEasHxLAJVZRJuQogfd1GiJ9lgogBgKeuQ=="],
+
+ "cosmiconfig/yaml": ["yaml@1.10.2", "", {}, "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg=="],
+
+ "css-tree/source-map": ["source-map@0.6.1", "", {}, "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="],
+
+ "error-ex/is-arrayish": ["is-arrayish@0.2.1", "", {}, "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg=="],
+
+ "eslint-import-resolver-node/debug": ["debug@3.2.7", "", { "dependencies": { "ms": "^2.1.1" } }, "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ=="],
+
+ "eslint-module-utils/debug": ["debug@3.2.7", "", { "dependencies": { "ms": "^2.1.1" } }, "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ=="],
+
+ "eslint-plugin-import/debug": ["debug@3.2.7", "", { "dependencies": { "ms": "^2.1.1" } }, "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ=="],
+
+ "espree/acorn": ["acorn@8.15.0", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg=="],
+
+ "fast-glob/glob-parent": ["glob-parent@5.1.2", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="],
+
+ "glob/minimatch": ["minimatch@9.0.5", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow=="],
+
+ "hoist-non-react-statics/react-is": ["react-is@16.13.1", "", {}, "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="],
+
+ "is-bun-module/semver": ["semver@7.7.2", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA=="],
+
+ "micromatch/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="],
+
+ "miniflare/zod": ["zod@3.22.3", "", {}, "sha512-EjIevzuJRiRPbVH4mGc8nApb/lVLKVpmUhAaR5R5doKGfAnGJ6Gr3CViAVjP+4FWSxCsybeWQdcgCtbX+7oZug=="],
+
+ "nano-css/stylis": ["stylis@4.3.6", "", {}, "sha512-yQ3rwFWRfwNUY7H5vpU0wfdkNSnvnJinhF9830Swlaxl03zsOjCfmX0ugac+3LtK0lYSgwL/KXc8oYL3mG4YFQ=="],
+
+ "path-scurry/lru-cache": ["lru-cache@10.4.3", "", {}, "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ=="],
+
+ "prop-types/react-is": ["react-is@16.13.1", "", {}, "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="],
+
+ "readdirp/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="],
+
+ "sharp/semver": ["semver@7.7.2", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA=="],
+
+ "stacktrace-gps/source-map": ["source-map@0.5.6", "", {}, "sha512-MjZkVp0NHr5+TPihLcadqnlVoGIoWo4IBHptutGh9wI3ttUYvCG26HkSuDi+K6lsZ25syXJXcctwgyVCt//xqA=="],
+
+ "string-width-cjs/emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="],
+
+ "string-width-cjs/strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="],
+
+ "strip-ansi-cjs/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="],
+
+ "tsconfig-paths/json5": ["json5@1.0.2", "", { "dependencies": { "minimist": "^1.2.0" }, "bin": { "json5": "lib/cli.js" } }, "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA=="],
+
+ "wrangler/esbuild": ["esbuild@0.25.4", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.25.4", "@esbuild/android-arm": "0.25.4", "@esbuild/android-arm64": "0.25.4", "@esbuild/android-x64": "0.25.4", "@esbuild/darwin-arm64": "0.25.4", "@esbuild/darwin-x64": "0.25.4", "@esbuild/freebsd-arm64": "0.25.4", "@esbuild/freebsd-x64": "0.25.4", "@esbuild/linux-arm": "0.25.4", "@esbuild/linux-arm64": "0.25.4", "@esbuild/linux-ia32": "0.25.4", "@esbuild/linux-loong64": "0.25.4", "@esbuild/linux-mips64el": "0.25.4", "@esbuild/linux-ppc64": "0.25.4", "@esbuild/linux-riscv64": "0.25.4", "@esbuild/linux-s390x": "0.25.4", "@esbuild/linux-x64": "0.25.4", "@esbuild/netbsd-arm64": "0.25.4", "@esbuild/netbsd-x64": "0.25.4", "@esbuild/openbsd-arm64": "0.25.4", "@esbuild/openbsd-x64": "0.25.4", "@esbuild/sunos-x64": "0.25.4", "@esbuild/win32-arm64": "0.25.4", "@esbuild/win32-ia32": "0.25.4", "@esbuild/win32-x64": "0.25.4" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-8pgjLUcUjcgDg+2Q4NYXnPbo/vncAY4UmyaCm0jZevERqCHZIaWwdJHkf8XQtu4AxSKCdvrUbT0XUr1IdZzI8Q=="],
+
+ "wrap-ansi/ansi-styles": ["ansi-styles@6.2.3", "", {}, "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg=="],
+
+ "wrap-ansi-cjs/string-width": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="],
+
+ "wrap-ansi-cjs/strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="],
+
+ "@typescript-eslint/typescript-estree/minimatch/brace-expansion": ["brace-expansion@2.0.2", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ=="],
+
+ "cloudflare/@types/node/undici-types": ["undici-types@5.26.5", "", {}, "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA=="],
+
+ "glob/minimatch/brace-expansion": ["brace-expansion@2.0.2", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ=="],
+
+ "string-width-cjs/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="],
+
+ "wrangler/esbuild/@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.25.4", "", { "os": "aix", "cpu": "ppc64" }, "sha512-1VCICWypeQKhVbE9oW/sJaAmjLxhVqacdkvPLEjwlttjfwENRSClS8EjBz0KzRyFSCPDIkuXW34Je/vk7zdB7Q=="],
+
+ "wrangler/esbuild/@esbuild/android-arm": ["@esbuild/android-arm@0.25.4", "", { "os": "android", "cpu": "arm" }, "sha512-QNdQEps7DfFwE3hXiU4BZeOV68HHzYwGd0Nthhd3uCkkEKK7/R6MTgM0P7H7FAs5pU/DIWsviMmEGxEoxIZ+ZQ=="],
+
+ "wrangler/esbuild/@esbuild/android-arm64": ["@esbuild/android-arm64@0.25.4", "", { "os": "android", "cpu": "arm64" }, "sha512-bBy69pgfhMGtCnwpC/x5QhfxAz/cBgQ9enbtwjf6V9lnPI/hMyT9iWpR1arm0l3kttTr4L0KSLpKmLp/ilKS9A=="],
+
+ "wrangler/esbuild/@esbuild/android-x64": ["@esbuild/android-x64@0.25.4", "", { "os": "android", "cpu": "x64" }, "sha512-TVhdVtQIFuVpIIR282btcGC2oGQoSfZfmBdTip2anCaVYcqWlZXGcdcKIUklfX2wj0JklNYgz39OBqh2cqXvcQ=="],
+
+ "wrangler/esbuild/@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.25.4", "", { "os": "darwin", "cpu": "arm64" }, "sha512-Y1giCfM4nlHDWEfSckMzeWNdQS31BQGs9/rouw6Ub91tkK79aIMTH3q9xHvzH8d0wDru5Ci0kWB8b3up/nl16g=="],
+
+ "wrangler/esbuild/@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.25.4", "", { "os": "darwin", "cpu": "x64" }, "sha512-CJsry8ZGM5VFVeyUYB3cdKpd/H69PYez4eJh1W/t38vzutdjEjtP7hB6eLKBoOdxcAlCtEYHzQ/PJ/oU9I4u0A=="],
+
+ "wrangler/esbuild/@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.25.4", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-yYq+39NlTRzU2XmoPW4l5Ifpl9fqSk0nAJYM/V/WUGPEFfek1epLHJIkTQM6bBs1swApjO5nWgvr843g6TjxuQ=="],
+
+ "wrangler/esbuild/@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.25.4", "", { "os": "freebsd", "cpu": "x64" }, "sha512-0FgvOJ6UUMflsHSPLzdfDnnBBVoCDtBTVyn/MrWloUNvq/5SFmh13l3dvgRPkDihRxb77Y17MbqbCAa2strMQQ=="],
+
+ "wrangler/esbuild/@esbuild/linux-arm": ["@esbuild/linux-arm@0.25.4", "", { "os": "linux", "cpu": "arm" }, "sha512-kro4c0P85GMfFYqW4TWOpvmF8rFShbWGnrLqlzp4X1TNWjRY3JMYUfDCtOxPKOIY8B0WC8HN51hGP4I4hz4AaQ=="],
+
+ "wrangler/esbuild/@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.25.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-+89UsQTfXdmjIvZS6nUnOOLoXnkUTB9hR5QAeLrQdzOSWZvNSAXAtcRDHWtqAUtAmv7ZM1WPOOeSxDzzzMogiQ=="],
+
+ "wrangler/esbuild/@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.25.4", "", { "os": "linux", "cpu": "ia32" }, "sha512-yTEjoapy8UP3rv8dB0ip3AfMpRbyhSN3+hY8mo/i4QXFeDxmiYbEKp3ZRjBKcOP862Ua4b1PDfwlvbuwY7hIGQ=="],
+
+ "wrangler/esbuild/@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.25.4", "", { "os": "linux", "cpu": "none" }, "sha512-NeqqYkrcGzFwi6CGRGNMOjWGGSYOpqwCjS9fvaUlX5s3zwOtn1qwg1s2iE2svBe4Q/YOG1q6875lcAoQK/F4VA=="],
+
+ "wrangler/esbuild/@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.25.4", "", { "os": "linux", "cpu": "none" }, "sha512-IcvTlF9dtLrfL/M8WgNI/qJYBENP3ekgsHbYUIzEzq5XJzzVEV/fXY9WFPfEEXmu3ck2qJP8LG/p3Q8f7Zc2Xg=="],
+
+ "wrangler/esbuild/@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.25.4", "", { "os": "linux", "cpu": "ppc64" }, "sha512-HOy0aLTJTVtoTeGZh4HSXaO6M95qu4k5lJcH4gxv56iaycfz1S8GO/5Jh6X4Y1YiI0h7cRyLi+HixMR+88swag=="],
+
+ "wrangler/esbuild/@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.25.4", "", { "os": "linux", "cpu": "none" }, "sha512-i8JUDAufpz9jOzo4yIShCTcXzS07vEgWzyX3NH2G7LEFVgrLEhjwL3ajFE4fZI3I4ZgiM7JH3GQ7ReObROvSUA=="],
+
+ "wrangler/esbuild/@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.25.4", "", { "os": "linux", "cpu": "s390x" }, "sha512-jFnu+6UbLlzIjPQpWCNh5QtrcNfMLjgIavnwPQAfoGx4q17ocOU9MsQ2QVvFxwQoWpZT8DvTLooTvmOQXkO51g=="],
+
+ "wrangler/esbuild/@esbuild/linux-x64": ["@esbuild/linux-x64@0.25.4", "", { "os": "linux", "cpu": "x64" }, "sha512-6e0cvXwzOnVWJHq+mskP8DNSrKBr1bULBvnFLpc1KY+d+irZSgZ02TGse5FsafKS5jg2e4pbvK6TPXaF/A6+CA=="],
+
+ "wrangler/esbuild/@esbuild/netbsd-arm64": ["@esbuild/netbsd-arm64@0.25.4", "", { "os": "none", "cpu": "arm64" }, "sha512-vUnkBYxZW4hL/ie91hSqaSNjulOnYXE1VSLusnvHg2u3jewJBz3YzB9+oCw8DABeVqZGg94t9tyZFoHma8gWZQ=="],
+
+ "wrangler/esbuild/@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.25.4", "", { "os": "none", "cpu": "x64" }, "sha512-XAg8pIQn5CzhOB8odIcAm42QsOfa98SBeKUdo4xa8OvX8LbMZqEtgeWE9P/Wxt7MlG2QqvjGths+nq48TrUiKw=="],
+
+ "wrangler/esbuild/@esbuild/openbsd-arm64": ["@esbuild/openbsd-arm64@0.25.4", "", { "os": "openbsd", "cpu": "arm64" }, "sha512-Ct2WcFEANlFDtp1nVAXSNBPDxyU+j7+tId//iHXU2f/lN5AmO4zLyhDcpR5Cz1r08mVxzt3Jpyt4PmXQ1O6+7A=="],
+
+ "wrangler/esbuild/@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.25.4", "", { "os": "openbsd", "cpu": "x64" }, "sha512-xAGGhyOQ9Otm1Xu8NT1ifGLnA6M3sJxZ6ixylb+vIUVzvvd6GOALpwQrYrtlPouMqd/vSbgehz6HaVk4+7Afhw=="],
+
+ "wrangler/esbuild/@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.25.4", "", { "os": "sunos", "cpu": "x64" }, "sha512-Mw+tzy4pp6wZEK0+Lwr76pWLjrtjmJyUB23tHKqEDP74R3q95luY/bXqXZeYl4NYlvwOqoRKlInQialgCKy67Q=="],
+
+ "wrangler/esbuild/@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.25.4", "", { "os": "win32", "cpu": "arm64" }, "sha512-AVUP428VQTSddguz9dO9ngb+E5aScyg7nOeJDrF1HPYu555gmza3bDGMPhmVXL8svDSoqPCsCPjb265yG/kLKQ=="],
+
+ "wrangler/esbuild/@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.25.4", "", { "os": "win32", "cpu": "ia32" }, "sha512-i1sW+1i+oWvQzSgfRcxxG2k4I9n3O9NRqy8U+uugaT2Dy7kLO9Y7wI72haOahxceMX8hZAzgGou1FhndRldxRg=="],
+
+ "wrangler/esbuild/@esbuild/win32-x64": ["@esbuild/win32-x64@0.25.4", "", { "os": "win32", "cpu": "x64" }, "sha512-nOT2vZNw6hJ+z43oP1SPea/G/6AbN6X+bGNhNuq8NtRHy4wsMhw765IKLNmnjek7GvjWBYQ8Q5VBoYTFg9y1UQ=="],
+
+ "wrap-ansi-cjs/string-width/emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="],
+
+ "wrap-ansi-cjs/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="],
+ }
+}
diff --git a/external/drpower-vibe-production/container/packages-cache/package.json b/external/drpower-vibe-production/container/packages-cache/package.json
new file mode 100644
index 00000000..a01c85b7
--- /dev/null
+++ b/external/drpower-vibe-production/container/packages-cache/package.json
@@ -0,0 +1,104 @@
+{
+ "name": "vite-cf-do-v2-runner",
+ "private": true,
+ "version": "0.0.0",
+ "type": "module",
+ "scripts": {
+ "dev": "vite --host 0.0.0.0 --port ${PORT:-3000}",
+ "build": "vite build",
+ "lint": "eslint --cache -f json --quiet .",
+ "preview": "bun run build && vite preview --host 0.0.0.0 --port ${PORT:-4173}",
+ "deploy": "bun run build && wrangler deploy",
+ "cf-typegen": "wrangler types"
+ },
+ "dependencies": {
+ "@dnd-kit/core": "^6.3.1",
+ "@dnd-kit/sortable": "^10.0.0",
+ "@headlessui/react": "^2.2.4",
+ "@hookform/resolvers": "^5.1.1",
+ "@radix-ui/react-accordion": "^1.2.11",
+ "@radix-ui/react-alert-dialog": "^1.1.14",
+ "@radix-ui/react-aspect-ratio": "^1.1.7",
+ "@radix-ui/react-avatar": "^1.1.10",
+ "@radix-ui/react-checkbox": "^1.3.2",
+ "@radix-ui/react-collapsible": "^1.1.11",
+ "@radix-ui/react-context-menu": "^2.2.15",
+ "@radix-ui/react-dialog": "^1.1.14",
+ "@radix-ui/react-dropdown-menu": "^2.1.15",
+ "@radix-ui/react-hover-card": "^1.1.14",
+ "@radix-ui/react-label": "^2.1.7",
+ "@radix-ui/react-menubar": "^1.1.15",
+ "@radix-ui/react-navigation-menu": "^1.2.13",
+ "@radix-ui/react-popover": "^1.1.14",
+ "@radix-ui/react-progress": "^1.1.7",
+ "@radix-ui/react-radio-group": "^1.3.7",
+ "@radix-ui/react-scroll-area": "^1.2.9",
+ "@radix-ui/react-select": "^2.2.5",
+ "@radix-ui/react-separator": "^1.1.7",
+ "@radix-ui/react-slider": "^1.3.5",
+ "@radix-ui/react-slot": "^1.2.3",
+ "@radix-ui/react-switch": "^1.2.5",
+ "@radix-ui/react-tabs": "^1.1.12",
+ "@radix-ui/react-toast": "^1.2.14",
+ "@radix-ui/react-toggle": "^1.1.9",
+ "@radix-ui/react-toggle-group": "^1.1.10",
+ "@radix-ui/react-tooltip": "^1.2.7",
+ "@tanstack/react-query": "^5.83.0",
+ "@typescript-eslint/eslint-plugin": "^8.38.0",
+ "@typescript-eslint/parser": "^8.38.0",
+ "class-variance-authority": "^0.7.1",
+ "cloudflare": "^5.0.0",
+ "clsx": "^2.1.1",
+ "cmdk": "^1.1.1",
+ "date-fns": "^4.1.0",
+ "embla-carousel-react": "^8.6.0",
+ "eslint-import-resolver-typescript": "^4.4.4",
+ "eslint-plugin-import": "^2.32.0",
+ "framer-motion": "^12.23.0",
+ "hono": "^4.8.5",
+ "immer": "^10.1.1",
+ "input-otp": "^1.4.2",
+ "lucide-react": "^0.525.0",
+ "next-themes": "^0.4.6",
+ "pino": "^9.11.0",
+ "react": "^18.3.1",
+ "react-day-picker": "^9.8.0",
+ "react-dom": "^18.3.1",
+ "react-flow": "^1.0.3",
+ "react-hook-form": "^7.60.0",
+ "react-hotkeys-hook": "^5.1.0",
+ "react-resizable-panels": "^3.0.3",
+ "react-router-dom": "6.30.0",
+ "react-select": "^5.10.2",
+ "react-swipeable": "^7.0.2",
+ "react-use": "^17.6.0",
+ "recharts": "2.15.4",
+ "sonner": "^2.0.6",
+ "tailwind-merge": "^3.3.1",
+ "tailwindcss-animate": "^1.0.7",
+ "tw-animate-css": "^1.3.5",
+ "uuid": "^11.1.0",
+ "vaul": "^1.1.2",
+ "zod": "^4.0.5",
+ "zustand": "^5.0.6"
+ },
+ "devDependencies": {
+ "@cloudflare/vite-plugin": "^1.9.4",
+ "@cloudflare/workers-types": "^4.20250807.0",
+ "@eslint/js": "^9.22.0",
+ "@types/node": "^22.15.3",
+ "@types/react": "^18.3.1",
+ "@types/react-dom": "^18.3.1",
+ "@vitejs/plugin-react": "^4.3.4",
+ "autoprefixer": "^10.4.21",
+ "eslint": "^9.31.0",
+ "eslint-plugin-react-hooks": "^5.2.0",
+ "eslint-plugin-react-refresh": "^0.4.19",
+ "globals": "^16.0.0",
+ "postcss": "^8.5.3",
+ "tailwindcss": "^3.4.17",
+ "typescript": "5.8",
+ "typescript-eslint": "^8.26.1",
+ "vite": "^6.3.1"
+ }
+}
diff --git a/external/drpower-vibe-production/container/process-monitor.ts b/external/drpower-vibe-production/container/process-monitor.ts
new file mode 100644
index 00000000..5109b11d
--- /dev/null
+++ b/external/drpower-vibe-production/container/process-monitor.ts
@@ -0,0 +1,615 @@
+import { EventEmitter } from 'events';
+import { spawn, ChildProcess } from 'child_process';
+import { StorageManager } from './storage.js';
+import { promises as fs } from 'fs';
+import { join } from 'path';
+import {
+ ProcessInfo,
+ ProcessState,
+ MonitoringOptions,
+ MonitoringEvent,
+ LogLine,
+ Result,
+ SimpleError,
+ getDataDirectory
+} from './types.js';
+class SimpleLogManager {
+ private logFilePath: string;
+ private maxLines: number;
+ private maxFileSize: number; // in bytes
+ private appendCount = 0;
+ private static readonly CHECK_INTERVAL = 100; // Check file size every 100 appends
+
+ constructor(instanceId: string, maxLines: number = 1000, maxFileSize: number = 1024 * 1024) { // 1MB default
+ this.logFilePath = join(getDataDirectory(), `${instanceId}-process.log`);
+ this.maxLines = maxLines;
+ this.maxFileSize = maxFileSize;
+ }
+
+ async appendLog(content: string, stream: 'stdout' | 'stderr'): Promise {
+ try {
+ const timestamp = new Date().toISOString();
+ const logLine = `[${timestamp}] [${stream}] ${content}\n`;
+
+ await fs.appendFile(this.logFilePath, logLine, 'utf8');
+
+ // Only check file size periodically to reduce I/O overhead
+ if (++this.appendCount % SimpleLogManager.CHECK_INTERVAL === 0) {
+ await this.trimLogIfNeeded();
+ }
+ } catch (error) {
+ console.warn('Failed to append to log file:', error);
+ }
+ }
+
+ async getAllLogsAndReset(): Promise {
+ try {
+ const tempPath = `${this.logFilePath}.tmp.${Date.now()}`;
+
+ try {
+ await fs.rename(this.logFilePath, tempPath);
+ } catch (error: any) {
+ if (error?.code === 'ENOENT') {
+ return '';
+ }
+ throw error;
+ }
+
+ await fs.writeFile(this.logFilePath, '', 'utf8').catch(() => {});
+
+ try {
+ const logs = await fs.readFile(tempPath, 'utf8');
+ await fs.unlink(tempPath).catch(() => {});
+ return logs;
+ } catch (error) {
+ await fs.unlink(tempPath).catch(() => {});
+ return '';
+ }
+ } catch (error) {
+ console.warn('Failed to read/reset log file:', error);
+ return '';
+ }
+ }
+
+ private async trimLogIfNeeded(): Promise {
+ try {
+ const stats = await fs.stat(this.logFilePath).catch(() => null);
+ if (!stats) return;
+
+ if (stats.size > this.maxFileSize) {
+ await this.trimLogFile();
+ return;
+ }
+
+ if (stats.size > 50000) {
+ const content = await fs.readFile(this.logFilePath, 'utf8');
+ const lines = content.split('\n');
+
+ if (lines.length > this.maxLines) {
+ await this.trimLogFile();
+ }
+ }
+ } catch (error) {
+ console.warn('Failed to check/trim log file:', error);
+ }
+ }
+
+ private async trimLogFile(): Promise {
+ try {
+ const content = await fs.readFile(this.logFilePath, 'utf8');
+ const lines = content.split('\n');
+
+ const keepLines = Math.floor(this.maxLines * 0.7);
+ const trimmedContent = lines.slice(-keepLines).join('\n');
+
+ await fs.writeFile(this.logFilePath, trimmedContent, 'utf8');
+ } catch (error) {
+ console.warn('Failed to trim log file:', error);
+ }
+ }
+
+ async cleanup(): Promise {
+ try {
+ await fs.unlink(this.logFilePath).catch(() => {});
+ } catch (error) {
+ console.warn('Failed to cleanup log file:', error);
+ }
+ }
+}
+
+const DEFAULT_MONITORING_OPTIONS: Required = {
+ autoRestart: true,
+ maxRestarts: 3,
+ restartDelay: 1000,
+ healthCheckInterval: 30000,
+ errorBufferSize: 100,
+ enableMetrics: false,
+ env: {},
+ killTimeout: 10000
+};
+
+export class ProcessMonitor extends EventEmitter {
+ private processInfo: ProcessInfo;
+ private childProcess?: ChildProcess;
+ private options: Required;
+ private storage: StorageManager;
+ private simpleLogManager: SimpleLogManager;
+ private state: ProcessState = 'stopped';
+ private restartCount = 0;
+ private restartTimer?: NodeJS.Timeout;
+ private healthCheckTimer?: NodeJS.Timeout;
+ private lastActivity = new Date();
+ private logBuffer: LogLine[] = [];
+
+ constructor(
+ processInfo: ProcessInfo,
+ storage: StorageManager,
+ options: MonitoringOptions = {}
+ ) {
+ super();
+
+ this.processInfo = { ...processInfo };
+ this.options = { ...DEFAULT_MONITORING_OPTIONS, ...options } as Required;
+ this.storage = storage;
+ this.simpleLogManager = new SimpleLogManager(this.processInfo.instanceId);
+
+ this.startHealthMonitoring();
+ }
+
+ public async start(): Promise> {
+ try {
+ if (this.state === 'running') {
+ return { success: false, error: new Error('Process is already running') };
+ }
+
+ this.setState('starting');
+
+ this.childProcess = spawn(this.processInfo.command, this.processInfo.args || [], {
+ cwd: this.processInfo.cwd,
+ env: { ...process.env, ...this.options.env },
+ stdio: ['pipe', 'pipe', 'pipe'],
+ detached: false,
+ shell: false // Don't use shell to avoid escaping issues
+ });
+
+ this.processInfo.pid = this.childProcess.pid;
+
+ if (!this.childProcess.pid) {
+ throw new Error('Failed to start process - no PID assigned');
+ }
+
+ // Update process info
+ this.processInfo = {
+ ...this.processInfo,
+ pid: this.childProcess.pid,
+ startTime: new Date(),
+ endTime: undefined,
+ exitCode: undefined,
+ status: 'running'
+ };
+
+ this.setupProcessMonitoring();
+ this.setupStreamMonitoring();
+
+ this.setState('running');
+ this.lastActivity = new Date();
+
+ await this.simpleLogManager.appendLog(`Process started: ${this.processInfo.command} ${this.processInfo.args?.join(' ') || ''}`, 'stdout').catch(() => {});
+
+ this.emit('process_started', {
+ type: 'process_started',
+ processId: this.processInfo.id,
+ instanceId: this.processInfo.instanceId,
+ pid: this.processInfo.pid,
+ timestamp: new Date()
+ } as MonitoringEvent);
+
+ console.log(`Process started: ${this.processInfo.command}`);
+
+ return {
+ success: true,
+ data: this.processInfo
+ };
+ } catch (error) {
+ this.setState('stopped');
+ const errorMessage = error instanceof Error ? error.message : 'Failed to start process';
+ console.error(`Failed to start process: ${errorMessage}`);
+
+ return {
+ success: false,
+ error: new Error(errorMessage)
+ };
+ }
+ }
+
+ public async stop(): Promise> {
+ try {
+ if (this.state === 'stopped') {
+ return { success: true, data: undefined };
+ }
+
+ this.setState('stopping');
+
+ if (this.restartTimer) {
+ clearTimeout(this.restartTimer);
+ this.restartTimer = undefined;
+ }
+
+ if (this.childProcess && !this.childProcess.killed) {
+ await this.killProcess(false);
+ }
+
+ this.setState('stopped');
+
+ // Emit stop event
+ this.emit('process_stopped', {
+ type: 'process_stopped',
+ processId: this.processInfo.id,
+ instanceId: this.processInfo.instanceId,
+ timestamp: new Date()
+ } as MonitoringEvent);
+
+ console.log(`Process stopped: ${this.processInfo.command}`);
+
+ return { success: true, data: undefined };
+ } catch (error) {
+ const errorMessage = error instanceof Error ? error.message : 'Failed to stop process';
+ console.error(`Failed to stop process: ${errorMessage}`);
+
+ return {
+ success: false,
+ error: new Error(errorMessage)
+ };
+ }
+ }
+
+ private setupProcessMonitoring(): void {
+ if (!this.childProcess) return;
+
+ this.childProcess.on('exit', (code, signal) => {
+ // Update process info
+ this.processInfo = {
+ ...this.processInfo,
+ exitCode: code ?? undefined,
+ endTime: new Date()
+ };
+
+ const wasUnexpected = this.state === 'running';
+ const wasStopping = this.state === 'stopping';
+
+ this.setState('stopped');
+
+ this.simpleLogManager.appendLog(`Process exited with code ${code}${signal ? ` (signal: ${signal})` : ''}`, 'stdout').catch(() => {});
+
+ // Emit stop event
+ this.emit('process_stopped', {
+ type: 'process_stopped',
+ processId: this.processInfo.id,
+ instanceId: this.processInfo.instanceId,
+ exitCode: code,
+ reason: signal ? `Signal: ${signal}` : `Exit code: ${code}`,
+ timestamp: new Date()
+ } as MonitoringEvent);
+
+ if (wasUnexpected) {
+ console.log(`Process exited unexpectedly: code=${code}, signal=${signal}`);
+
+ const shouldRestart = this.options.autoRestart && this.shouldRestartAfterExit(code, signal, wasStopping);
+
+ if (code !== 0 || shouldRestart) {
+ this.setState('crashed');
+
+ this.emit('process_crashed', {
+ type: 'process_crashed',
+ processId: this.processInfo.id,
+ instanceId: this.processInfo.instanceId,
+ exitCode: code,
+ signal: signal,
+ willRestart: shouldRestart,
+ timestamp: new Date()
+ } as MonitoringEvent);
+
+ if (shouldRestart) {
+ this.scheduleRestart();
+ }
+ }
+ }
+ });
+
+ this.childProcess.on('error', (error) => {
+ console.error(`Process ${this.processInfo.id} error:`, error);
+
+ this.processInfo = {
+ ...this.processInfo,
+ lastError: error.message
+ };
+ this.setState('crashed');
+
+ this.simpleLogManager.appendLog(`Process error: ${error.message}`, 'stderr').catch(() => {});
+
+ const simpleError: SimpleError = {
+ timestamp: new Date().toISOString(),
+ level: 60, // fatal
+ message: `Process error: ${error.message}`,
+ rawOutput: error.stack || error.message
+ };
+
+ this.storage.storeError(
+ this.processInfo.instanceId,
+ this.processInfo.id,
+ simpleError
+ );
+ });
+ }
+
+ private setupStreamMonitoring(): void {
+ if (!this.childProcess) return;
+
+ this.childProcess.stdout?.on('data', (data: Buffer) => {
+ this.processStreamData(data, 'stdout');
+ });
+
+ this.childProcess.stderr?.on('data', (data: Buffer) => {
+ this.processStreamData(data, 'stderr');
+ });
+ }
+
+ private processStreamData(data: Buffer, stream: 'stdout' | 'stderr'): void {
+ const content = data.toString('utf8');
+ const lines = content.split('\n');
+
+ this.lastActivity = new Date();
+
+ for (const line of lines) {
+ const trimmedLine = line.trim();
+ if (!trimmedLine) continue;
+
+ this.simpleLogManager.appendLog(trimmedLine, stream).catch(() => {});
+
+ const logLine: LogLine = {
+ content: trimmedLine,
+ timestamp: new Date(),
+ stream,
+ processId: this.processInfo.id
+ };
+ this.logBuffer.push(logLine);
+
+ if (this.logBuffer.length > this.options.errorBufferSize) {
+ this.logBuffer.shift();
+ }
+
+ this.parseJsonLog(trimmedLine);
+ }
+ }
+
+ private parseJsonLog(line: string): void {
+ try {
+ if (!line.startsWith('{')) return;
+
+ const logData = JSON.parse(line);
+
+ if (logData.level && logData.level >= 50) {
+ const message = logData.msg || 'Unknown error';
+
+ const simpleError: SimpleError = {
+ timestamp: logData.time ? new Date(logData.time).toISOString() : new Date().toISOString(),
+ level: logData.level,
+ message: message,
+ rawOutput: line
+ };
+
+ const storeResult = this.storage.storeError(
+ this.processInfo.instanceId,
+ this.processInfo.id,
+ simpleError
+ );
+
+ if (storeResult.success) {
+ console.log(`Error detected (level ${logData.level}): ${message.substring(0, 100)}...`);
+
+ // Emit error event
+ this.emit('error_detected', {
+ type: 'error_detected',
+ processId: this.processInfo.id,
+ instanceId: this.processInfo.instanceId,
+ error: simpleError,
+ timestamp: new Date()
+ } as MonitoringEvent);
+
+ if (this.isFatalError(message, logData.level)) {
+ this.handleFatalError(simpleError);
+ }
+ }
+ }
+ } catch (e) {
+ }
+ }
+
+ private isFatalError(message: string, level: number): boolean {
+ if (level >= 60) return true;
+
+ const fatalPatterns = [
+ /fatal error/i,
+ /out of memory/i,
+ /maximum call stack/i,
+ /segmentation fault/i,
+ /EADDRINUSE/i,
+ /cannot find module/i,
+ /module not found/i,
+ /failed to compile/i
+ ];
+
+ return fatalPatterns.some(pattern => pattern.test(message));
+ }
+
+ private handleFatalError(error: SimpleError): void {
+ console.error(`Fatal error detected: ${error.message}`);
+
+ if (this.childProcess && !this.childProcess.killed) {
+ console.log('Killing process due to fatal error...');
+ this.childProcess.kill('SIGTERM');
+ }
+ }
+
+ private shouldRestartAfterExit(exitCode: number | null, signal: NodeJS.Signals | null, wasStopping: boolean): boolean {
+ if (wasStopping) {
+ console.log('Process was explicitly stopped, not restarting');
+ return false;
+ }
+
+ if (this.restartCount >= this.options.maxRestarts) {
+ console.error(`Max restart attempts (${this.options.maxRestarts}) reached`);
+ return false;
+ }
+
+ if (signal) {
+ console.log(`Process killed by signal ${signal}, will restart`);
+ return true;
+ }
+
+ if (exitCode === 0) {
+ const timeSinceLastActivity = Date.now() - this.lastActivity.getTime();
+
+ if (timeSinceLastActivity > 30000) { // More than 30 seconds
+ console.log(`Process exited with code 0 but was unresponsive for ${Math.round(timeSinceLastActivity/1000)}s, assuming killed, will restart`);
+ return true;
+ }
+
+ console.log('Process exited cleanly with code 0, not restarting');
+ return false;
+ }
+
+ if (exitCode !== 0) {
+ console.log(`Process exited with code ${exitCode}, will restart`);
+ return true;
+ }
+
+ return false;
+ }
+
+ private scheduleRestart(): void {
+ this.restartCount++;
+
+ console.log(`Scheduling restart ${this.restartCount}/${this.options.maxRestarts} in ${this.options.restartDelay}ms...`);
+
+ this.restartTimer = setTimeout(async () => {
+ console.log(`Restarting process (attempt ${this.restartCount}/${this.options.maxRestarts})...`);
+
+ const result = await this.start();
+ if (!result.success) {
+ console.error(`Failed to restart process: ${result.error?.message}`);
+
+ // Emit restart failed event
+ this.emit('restart_failed', {
+ type: 'restart_failed',
+ processId: this.processInfo.id,
+ instanceId: this.processInfo.instanceId,
+ attempt: this.restartCount,
+ error: result.error?.message,
+ timestamp: new Date()
+ } as MonitoringEvent);
+ }
+ }, this.options.restartDelay);
+ }
+
+ private async killProcess(force: boolean = false): Promise {
+ if (!this.childProcess || this.childProcess.killed) return;
+
+ return new Promise((resolve) => {
+ const killTimeout = this.options.killTimeout || 10000;
+
+ if (force) {
+ this.childProcess!.kill('SIGKILL');
+ resolve();
+ return;
+ }
+
+ const timeout = setTimeout(() => {
+ if (this.childProcess && !this.childProcess.killed) {
+ console.log('Process did not exit gracefully, force killing...');
+ this.childProcess.kill('SIGKILL');
+ }
+ resolve();
+ }, killTimeout);
+
+ this.childProcess!.once('exit', () => {
+ clearTimeout(timeout);
+ resolve();
+ });
+
+ this.childProcess!.kill('SIGTERM');
+ });
+ }
+
+ private startHealthMonitoring(): void {
+ if (this.options.healthCheckInterval <= 0) return;
+
+ this.healthCheckTimer = setInterval(() => {
+ if (this.state === 'running') {
+ const now = new Date();
+ const timeSinceLastActivity = now.getTime() - this.lastActivity.getTime();
+
+ if (timeSinceLastActivity > this.options.healthCheckInterval * 2) {
+ console.warn(`Process appears unresponsive (no activity for ${timeSinceLastActivity}ms)`);
+
+ this.emit('health_check_failed', {
+ type: 'health_check_failed',
+ processId: this.processInfo.id,
+ instanceId: this.processInfo.instanceId,
+ lastActivity: this.lastActivity,
+ timestamp: now
+ } as MonitoringEvent);
+ }
+ }
+ }, this.options.healthCheckInterval);
+ }
+
+ private setState(newState: ProcessState): void {
+ const oldState = this.state;
+ this.state = newState;
+
+ if (oldState !== newState) {
+ this.emit('state_changed', {
+ type: 'state_changed',
+ processId: this.processInfo.id,
+ instanceId: this.processInfo.instanceId,
+ oldState,
+ newState,
+ timestamp: new Date()
+ } as MonitoringEvent);
+ }
+ }
+
+ public getState(): ProcessState {
+ return this.state;
+ }
+
+ public getProcessInfo(): ProcessInfo {
+ return { ...this.processInfo };
+ }
+
+ public getRecentLogs(limit: number = 50): LogLine[] {
+ return this.logBuffer.slice(-limit);
+ }
+
+ public async getAllLogsAndReset(): Promise {
+ return await this.simpleLogManager.getAllLogsAndReset();
+ }
+
+ public async cleanup(): Promise {
+ if (this.restartTimer) {
+ clearTimeout(this.restartTimer);
+ this.restartTimer = undefined;
+ }
+ if (this.healthCheckTimer) {
+ clearInterval(this.healthCheckTimer);
+ this.healthCheckTimer = undefined;
+ }
+
+ await this.stop();
+
+ await this.simpleLogManager.cleanup();
+
+ this.removeAllListeners();
+ }
+}
diff --git a/external/drpower-vibe-production/container/storage.ts b/external/drpower-vibe-production/container/storage.ts
new file mode 100644
index 00000000..6dc95c12
--- /dev/null
+++ b/external/drpower-vibe-production/container/storage.ts
@@ -0,0 +1,725 @@
+import { Database } from 'bun:sqlite';
+import { createHash } from 'crypto';
+import {
+ SimpleError,
+ StoredError,
+ ProcessInfo,
+ StoredLog,
+ LogLevel,
+ ErrorSummary,
+ ErrorStoreOptions,
+ LogStoreOptions,
+ LogFilter,
+ LogCursor,
+ LogRetrievalResponse,
+ Result,
+ getErrorDbPath,
+ getLogDbPath,
+ ERROR_HASH_ALGORITHM,
+ DEFAULT_STORAGE_OPTIONS,
+ DEFAULT_LOG_STORE_OPTIONS
+} from './types.js';
+
+export interface ProcessLog {
+ readonly instanceId: string;
+ readonly processId: string;
+ readonly level: LogLevel;
+ readonly message: string;
+ readonly stream: 'stdout' | 'stderr';
+ readonly source?: string;
+ readonly metadata?: Record;
+}
+
+/**
+ * Unified storage manager with shared database connections and optimized operations
+ */
+export class StorageManager {
+ private errorDb: Database;
+ private logDb: Database;
+ private errorStorage: ErrorStorage;
+ private logStorage: LogStorage;
+ private options: {
+ error: Required;
+ log: Required;
+ };
+
+ constructor(
+ errorDbPath: string = getErrorDbPath(),
+ logDbPath: string = getLogDbPath(),
+ options: { error?: ErrorStoreOptions; log?: LogStoreOptions } = {}
+ ) {
+ this.options = {
+ error: { ...DEFAULT_STORAGE_OPTIONS, ...options.error } as Required,
+ log: { ...DEFAULT_LOG_STORE_OPTIONS, ...options.log } as Required
+ };
+
+ this.ensureDataDirectory(errorDbPath);
+ if (errorDbPath !== logDbPath) {
+ this.ensureDataDirectory(logDbPath);
+ }
+
+ this.errorDb = this.initializeDatabase(errorDbPath);
+ this.logDb = errorDbPath === logDbPath ? this.errorDb : this.initializeDatabase(logDbPath);
+
+ this.errorStorage = new ErrorStorage(this.errorDb, this.options.error);
+ this.logStorage = new LogStorage(this.logDb, this.options.log);
+
+ this.setupMaintenanceTasks();
+ }
+
+ private ensureDataDirectory(dbPath: string): void {
+ const fs = require('fs');
+ const path = require('path');
+ const dir = path.dirname(dbPath);
+
+ if (!fs.existsSync(dir)) {
+ fs.mkdirSync(dir, { recursive: true });
+ }
+ }
+
+ private initializeDatabase(dbPath: string): Database {
+ const fs = require('fs');
+
+ try {
+ const dbExists = fs.existsSync(dbPath);
+
+ const db = new Database(dbPath);
+
+ if (!dbExists) {
+ try {
+ db.exec('PRAGMA journal_mode = WAL');
+ db.exec('PRAGMA synchronous = NORMAL');
+ db.exec('PRAGMA cache_size = 10000');
+ db.exec('PRAGMA temp_store = memory');
+ } catch (error) {
+ console.warn('Database pragma setup failed (this is okay if database already initialized):', error);
+ }
+ }
+
+ return db;
+ } catch (error) {
+ console.error('Failed to initialize database at', dbPath, error);
+ throw new Error(`Failed to initialize database: ${error}`);
+ }
+ }
+
+ private setupMaintenanceTasks(): void {
+ setInterval(() => {
+ if (this.errorStorage) {
+ // Maintenance tasks if needed
+ }
+ }, 60 * 60 * 1000);
+ }
+
+ private toError(error: unknown, defaultMessage = 'Unknown error'): Error {
+ return error instanceof Error ? error : new Error(String(error) || defaultMessage);
+ }
+
+ /**
+ * Wrapper for retry operations
+ */
+ private retryOperation(operation: () => Result, maxRetries: number = 3): Result {
+ let attempt = 0;
+ let lastResult: Result = operation();
+
+ while (!lastResult.success && attempt < maxRetries - 1) {
+ attempt += 1;
+ lastResult = operation();
+ }
+
+ return lastResult;
+ }
+
+ private wrapRetryOperation(operation: () => Result): Result {
+ try {
+ return this.retryOperation(operation);
+ } catch (error) {
+ return { success: false, error: this.toError(error) };
+ }
+ }
+
+ public storeProcessInfo(processInfo: ProcessInfo): Result {
+ try {
+ return { success: true, data: true };
+ } catch (error) {
+ return { success: false, error: this.toError(error) };
+ }
+ }
+
+ public storeError(instanceId: string, processId: string, error: SimpleError): Result {
+ return this.wrapRetryOperation(() => this.errorStorage.storeError(instanceId, processId, error));
+ }
+
+ public getErrors(instanceId: string): Result {
+ return this.errorStorage.getErrors(instanceId);
+ }
+
+ public getErrorSummary(instanceId: string): Result {
+ return this.errorStorage.getErrorSummary(instanceId);
+ }
+
+ public clearErrors(instanceId: string): Result<{ clearedCount: number }> {
+ return this.errorStorage.clearErrors(instanceId);
+ }
+
+ public storeLogs(logs: ProcessLog[]): Result {
+ return this.logStorage.storeLogs(logs);
+ }
+
+ public getLogs(filter: LogFilter = {}): Result {
+ return this.logStorage.getLogs(filter);
+ }
+
+ public clearLogs(instanceId: string): Result<{ clearedCount: number }> {
+ return this.logStorage.clearLogs(instanceId);
+ }
+
+ public getLogStats(instanceId: string): Result<{
+ totalLogs: number;
+ logsByLevel: Record;
+ logsByStream: Record<'stdout' | 'stderr', number>;
+ oldestLog?: Date;
+ newestLog?: Date;
+ }> {
+ return this.logStorage.getLogStats(instanceId);
+ }
+
+ public transaction(operation: () => T): T {
+ // Use error database for transaction coordination
+ const transaction = this.errorDb.transaction(operation);
+ return transaction();
+ }
+
+ /**
+ * Close all database connections and cleanup
+ */
+ public close(): void {
+ try {
+ this.errorStorage.close();
+ this.logStorage.close();
+
+ if (this.errorDb !== this.logDb) {
+ this.logDb.close();
+ }
+ this.errorDb.close();
+ } catch (error) {
+ console.error('Error closing storage manager:', error);
+ }
+ }
+}
+
+class ErrorStorage {
+ private db: Database;
+ private options: Required;
+
+ // Prepared statements
+ private insertErrorStmt: ReturnType;
+ private updateErrorStmt: ReturnType;
+ private selectErrorsStmt: ReturnType;
+ private countErrorsStmt: ReturnType;
+ private deleteErrorsStmt: ReturnType;
+ private deleteOldErrorsStmt: ReturnType;
+
+ private errorResult(error: unknown, defaultMessage: string): Result {
+ return {
+ success: false,
+ error: error instanceof Error ? error : new Error(defaultMessage)
+ };
+ }
+
+ private successResult(data: T): Result {
+ return { success: true, data };
+ }
+
+ constructor(db: Database, options: Required) {
+ if (!db) {
+ throw new Error('Database instance is required for ErrorStorage');
+ }
+ this.db = db;
+ this.options = options;
+ this.initializeSchema();
+ this.prepareStatements();
+ }
+
+ private initializeSchema(): void {
+ this.db.exec(`
+ CREATE TABLE IF NOT EXISTS simple_errors (
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
+ instance_id TEXT NOT NULL,
+ process_id TEXT NOT NULL,
+ error_hash TEXT NOT NULL,
+ timestamp TEXT NOT NULL,
+ level INTEGER NOT NULL,
+ message TEXT NOT NULL,
+ raw_output TEXT NOT NULL,
+ occurrence_count INTEGER DEFAULT 1,
+ created_at TEXT DEFAULT (datetime('now'))
+ );
+
+ CREATE INDEX IF NOT EXISTS idx_simple_instance ON simple_errors(instance_id);
+ CREATE INDEX IF NOT EXISTS idx_simple_hash ON simple_errors(error_hash);
+ CREATE INDEX IF NOT EXISTS idx_simple_timestamp ON simple_errors(timestamp DESC);
+ CREATE INDEX IF NOT EXISTS idx_simple_level ON simple_errors(level);
+ `);
+ }
+
+ private prepareStatements(): void {
+ this.insertErrorStmt = this.db.query(`
+ INSERT INTO simple_errors (
+ instance_id, process_id, error_hash, timestamp, level, message, raw_output
+ ) VALUES (?, ?, ?, ?, ?, ?, ?)
+ `);
+
+ this.updateErrorStmt = this.db.query(`
+ UPDATE simple_errors
+ SET occurrence_count = occurrence_count + 1, timestamp = ?
+ WHERE error_hash = ? AND instance_id = ?
+ `);
+
+ this.selectErrorsStmt = this.db.query(`
+ WITH deduplicated AS (
+ SELECT
+ MAX(id) as id,
+ instance_id,
+ process_id,
+ error_hash,
+ MAX(timestamp) as latest_timestamp,
+ level,
+ message,
+ MAX(raw_output) as raw_output,
+ SUM(occurrence_count) AS total_occurrences,
+ MIN(created_at) AS first_seen
+ FROM simple_errors
+ WHERE instance_id = ?
+ GROUP BY error_hash
+ )
+ SELECT
+ id,
+ instance_id,
+ process_id,
+ error_hash AS errorHash,
+ latest_timestamp as timestamp,
+ level,
+ message,
+ raw_output AS rawOutput,
+ total_occurrences AS occurrenceCount,
+ first_seen AS createdAt
+ FROM deduplicated
+ ORDER BY latest_timestamp DESC
+ `);
+
+ this.countErrorsStmt = this.db.query(`
+ SELECT COUNT(*) as count FROM simple_errors WHERE instance_id = ?
+ `);
+
+ this.deleteErrorsStmt = this.db.query(`
+ DELETE FROM simple_errors WHERE instance_id = ?
+ `);
+
+ this.deleteOldErrorsStmt = this.db.query(`
+ DELETE FROM simple_errors
+ WHERE datetime(created_at) < datetime('now', '-' || ? || ' days')
+ `);
+ }
+
+ public storeError(instanceId: string, processId: string, error: SimpleError): Result {
+ try {
+ const cleanedMessage = this.cleanMessageForHashing(error.message);
+
+ const errorHash = createHash(ERROR_HASH_ALGORITHM)
+ .update(cleanedMessage)
+ .update(String(error.level))
+ .digest('hex');
+
+ const existing = this.db.query(`
+ SELECT id, occurrence_count FROM simple_errors
+ WHERE error_hash = ? AND instance_id = ?
+ ORDER BY timestamp DESC
+ LIMIT 1
+ `).get(errorHash, instanceId) as { id: number; occurrence_count: number } | null;
+
+ if (existing) {
+ this.db.query(`
+ UPDATE simple_errors
+ SET
+ occurrence_count = occurrence_count + 1,
+ timestamp = ?,
+ raw_output = ?
+ WHERE id = ?
+ `).run(error.timestamp, error.rawOutput, existing.id);
+ } else {
+ this.insertErrorStmt.run(
+ instanceId, processId, errorHash, error.timestamp,
+ error.level, error.message, error.rawOutput
+ );
+ }
+
+ return this.successResult(true);
+ } catch (error) {
+ return this.errorResult(error, 'Unknown error storing error');
+ }
+ }
+
+ public getErrors(instanceId: string): Result {
+ try {
+ const errors = this.selectErrorsStmt.all(instanceId) as StoredError[];
+ return this.successResult(errors);
+ } catch (error) {
+ return this.errorResult(error, 'Unknown error retrieving errors');
+ }
+ }
+
+ public getErrorSummary(instanceId: string): Result {
+ try {
+ const errors = this.selectErrorsStmt.all(instanceId) as StoredError[];
+
+ if (errors.length === 0) {
+ return {
+ success: true,
+ data: {
+ totalErrors: 0,
+ errorsByLevel: {} as Record,
+ uniqueErrors: 0,
+ repeatedErrors: 0,
+ latestError: undefined,
+ oldestError: undefined
+ }
+ };
+ }
+
+ const errorsByLevel = {} as Record;
+ const uniqueHashes = new Set();
+ let totalOccurrences = 0;
+
+ for (const error of errors) {
+ errorsByLevel[error.level] = (errorsByLevel[error.level] || 0) + error.occurrenceCount;
+ uniqueHashes.add(error.errorHash);
+ totalOccurrences += error.occurrenceCount;
+ }
+
+ const summary: ErrorSummary = {
+ totalErrors: totalOccurrences,
+ uniqueErrors: uniqueHashes.size,
+ repeatedErrors: totalOccurrences - errors.length,
+ errorsByLevel,
+ latestError: new Date(errors[0].timestamp),
+ oldestError: new Date(errors[errors.length - 1].timestamp)
+ };
+
+ return this.successResult(summary);
+ } catch (error) {
+ return this.errorResult(error, 'Unknown error getting summary');
+ }
+ }
+
+ public clearErrors(instanceId: string): Result<{ clearedCount: number }> {
+ try {
+ const countResult = this.countErrorsStmt.get(instanceId) as { count: number };
+ const clearedCount = countResult?.count || 0;
+
+ this.deleteErrorsStmt.run(instanceId);
+
+ return this.successResult({ clearedCount });
+ } catch (error) {
+ return this.errorResult<{ clearedCount: number }>(error, 'Unknown error clearing errors');
+ }
+ }
+
+ private cleanMessageForHashing(message: string): string {
+ let cleaned = message;
+
+ cleaned = cleaned.replace(/\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z/g, 'TIMESTAMP');
+
+ cleaned = cleaned.replace(/\b\d{13}\b/g, 'UNIX_TIME');
+
+ cleaned = cleaned.replace(/:\d{4,5}\b/g, ':PORT');
+
+ cleaned = cleaned.replace(/(:\d+):(\d+)/g, ':LINE:COL');
+
+ cleaned = cleaned.replace(/\?v=[a-f0-9]+/g, '?v=HASH');
+
+ cleaned = cleaned.replace(/\s+/g, ' ').trim();
+
+ if (cleaned.length > 500) {
+ cleaned = cleaned.substring(0, 500);
+ }
+
+ return cleaned;
+ }
+
+ public close(): void {
+ // Prepared statements are automatically cleaned up
+ }
+}
+
+class LogStorage {
+ private db: Database;
+ private options: Required;
+
+ // Prepared statements
+ private insertLogStmt: ReturnType;
+ private selectLogsStmt: ReturnType;
+ private selectLogsSinceStmt: ReturnType;
+ private countLogsStmt: ReturnType;
+ private deleteOldLogsStmt: ReturnType;
+ private getLastSequenceStmt: ReturnType;
+ private deleteAllLogsStmt: ReturnType;
+
+ private sequenceCounter = 0;
+
+ constructor(db: Database, options: Required) {
+ this.db = db;
+ this.options = options;
+ this.initializeSchema();
+ this.prepareStatements();
+ this.initializeSequenceCounter();
+ }
+
+ private initializeSchema(): void {
+ this.db.exec(`
+ CREATE TABLE IF NOT EXISTS process_logs (
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
+ instance_id TEXT NOT NULL,
+ process_id TEXT NOT NULL,
+ level TEXT NOT NULL,
+ message TEXT NOT NULL,
+ timestamp TEXT NOT NULL,
+ stream TEXT NOT NULL,
+ source TEXT,
+ metadata TEXT,
+ sequence INTEGER UNIQUE NOT NULL,
+ created_at TEXT DEFAULT (datetime('now'))
+ );
+
+ CREATE INDEX IF NOT EXISTS idx_instance_logs ON process_logs(instance_id);
+ CREATE INDEX IF NOT EXISTS idx_sequence ON process_logs(sequence);
+ CREATE INDEX IF NOT EXISTS idx_timestamp ON process_logs(timestamp DESC);
+ CREATE INDEX IF NOT EXISTS idx_level ON process_logs(level);
+ CREATE INDEX IF NOT EXISTS idx_instance_sequence ON process_logs(instance_id, sequence);
+ `);
+ }
+
+ private prepareStatements(): void {
+ this.insertLogStmt = this.db.query(`
+ INSERT INTO process_logs (
+ instance_id, process_id, level, message, timestamp,
+ stream, source, metadata, sequence
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
+ `);
+
+ this.selectLogsStmt = this.db.query(`
+ SELECT * FROM process_logs
+ WHERE instance_id = ?
+ ORDER BY sequence DESC
+ LIMIT ? OFFSET ?
+ `);
+
+ this.selectLogsSinceStmt = this.db.query(`
+ SELECT * FROM process_logs
+ WHERE instance_id = ? AND sequence > ?
+ ORDER BY sequence ASC
+ LIMIT ?
+ `);
+
+ this.countLogsStmt = this.db.query(`
+ SELECT COUNT(*) as count FROM process_logs WHERE instance_id = ?
+ `);
+
+ this.deleteOldLogsStmt = this.db.query(`
+ DELETE FROM process_logs
+ WHERE datetime(timestamp) < datetime('now', '-' || ? || ' hours')
+ `);
+
+ this.getLastSequenceStmt = this.db.query(`
+ SELECT MAX(sequence) as maxSequence FROM process_logs
+ `);
+
+ this.deleteAllLogsStmt = this.db.query(`
+ DELETE FROM process_logs WHERE instance_id = ?
+ `);
+ }
+
+ private initializeSequenceCounter(): void {
+ const result = this.getLastSequenceStmt.get() as { maxSequence: number | null };
+ this.sequenceCounter = (result?.maxSequence || 0) + 1;
+ }
+
+ public storeLog(log: ProcessLog): Result {
+ try {
+ const sequence = this.sequenceCounter++;
+ const now = new Date().toISOString();
+
+ this.insertLogStmt.run(
+ log.instanceId, log.processId, log.level, log.message, now,
+ log.stream, log.source || null,
+ log.metadata ? JSON.stringify(log.metadata) : null, sequence
+ );
+
+
+ return { success: true, data: sequence };
+ } catch (error) {
+ return {
+ success: false,
+ error: error instanceof Error ? error : new Error('Unknown error storing log')
+ };
+ }
+ }
+
+ public storeLogs(logs: ProcessLog[]): Result {
+ try {
+ const sequences: number[] = [];
+ const transaction = this.db.transaction(() => {
+ for (const log of logs) {
+ const result = this.storeLog(log);
+ if (!result.success) {
+ if ('error' in result) {
+ throw result.error;
+ }
+ throw new Error('Unknown error storing log');
+ }
+ sequences.push(result.data);
+ }
+ });
+
+ transaction();
+ return { success: true, data: sequences };
+ } catch (error) {
+ return {
+ success: false,
+ error: error instanceof Error ? error : new Error('Unknown error storing logs')
+ };
+ }
+ }
+
+ public getLogs(filter: LogFilter = {}): Result {
+ try {
+ const instanceId = filter.instanceId || '';
+ const limit = filter.limit || 100;
+ const offset = filter.offset || 0;
+
+ const logs = this.selectLogsStmt.all(instanceId, limit, offset) as StoredLog[];
+ const countResult = this.countLogsStmt.get(instanceId) as { count: number };
+ const totalCount = countResult?.count || 0;
+
+ const lastSequence = logs.length > 0 ? Math.max(...logs.map(l => l.sequence)) : 0;
+ const cursor: LogCursor = {
+ instanceId,
+ lastSequence,
+ lastRetrieved: new Date()
+ };
+
+ const hasMore = offset + logs.length < totalCount;
+
+ return {
+ success: true,
+ data: {
+ success: true,
+ logs,
+ cursor,
+ hasMore,
+ totalCount
+ }
+ };
+ } catch (error) {
+ return {
+ success: false,
+ error: error instanceof Error ? error : new Error('Unknown error retrieving logs')
+ };
+ }
+ }
+
+
+ public clearLogs(instanceId: string): Result<{ clearedCount: number }> {
+ try {
+ const countResult = this.countLogsStmt.get(instanceId) as { count: number };
+ const clearedCount = countResult?.count || 0;
+
+ this.deleteAllLogsStmt.run(instanceId);
+
+ return { success: true, data: { clearedCount } };
+ } catch (error) {
+ return {
+ success: false,
+ error: error instanceof Error ? error : new Error('Unknown error clearing logs')
+ };
+ }
+ }
+
+ public getLogStats(instanceId: string): Result<{
+ totalLogs: number;
+ logsByLevel: Record;
+ logsByStream: Record<'stdout' | 'stderr', number>;
+ oldestLog?: Date;
+ newestLog?: Date;
+ }> {
+ try {
+ const stats = this.db.query(`
+ SELECT
+ COUNT(*) as total,
+ level,
+ stream,
+ MIN(timestamp) as oldest,
+ MAX(timestamp) as newest
+ FROM process_logs
+ WHERE instance_id = ?
+ GROUP BY level, stream
+ `).all(instanceId) as Array<{
+ total: number;
+ level: LogLevel;
+ stream: 'stdout' | 'stderr';
+ oldest: string;
+ newest: string;
+ }>;
+
+ const logsByLevel: Record = {};
+ const logsByStream: Record = {};
+ let totalLogs = 0;
+ let oldestLog: Date | undefined;
+ let newestLog: Date | undefined;
+
+ for (const stat of stats) {
+ totalLogs += stat.total;
+ logsByLevel[stat.level] = (logsByLevel[stat.level] || 0) + stat.total;
+ logsByStream[stat.stream] = (logsByStream[stat.stream] || 0) + stat.total;
+
+ const oldest = new Date(stat.oldest);
+ const newest = new Date(stat.newest);
+
+ if (!oldestLog || oldest < oldestLog) oldestLog = oldest;
+ if (!newestLog || newest > newestLog) newestLog = newest;
+ }
+
+ return {
+ success: true,
+ data: {
+ totalLogs,
+ logsByLevel: logsByLevel as Record,
+ logsByStream: logsByStream as Record<'stdout' | 'stderr', number>,
+ oldestLog,
+ newestLog
+ }
+ };
+ } catch (error) {
+ return {
+ success: false,
+ error: error instanceof Error ? error : new Error('Unknown error getting log stats')
+ };
+ }
+ }
+
+ public cleanupOldLogs(): Result {
+ try {
+ const result = this.deleteOldLogsStmt.run(this.options.retentionHours);
+ return { success: true, data: result.changes };
+ } catch (error) {
+ return {
+ success: false,
+ error: error instanceof Error ? error : new Error('Unknown error cleaning up logs')
+ };
+ }
+ }
+
+ public close(): void {
+ // Database connection is managed by StorageManager
+ }
+}
diff --git a/external/drpower-vibe-production/container/types.ts b/external/drpower-vibe-production/container/types.ts
new file mode 100644
index 00000000..f4811284
--- /dev/null
+++ b/external/drpower-vibe-production/container/types.ts
@@ -0,0 +1,368 @@
+import { z } from 'zod';
+
+// ==========================================
+// COMMON SCHEMAS AND TYPES
+// ==========================================
+
+export const StreamTypeSchema = z.enum(['stdout', 'stderr']);
+export type StreamType = z.infer;
+
+export const LogLevelSchema = z.enum([
+ 'debug', // Detailed diagnostic information
+ 'info', // General informational messages
+ 'warn', // Warning messages (non-error issues)
+ 'error', // Error messages (already handled by error system)
+ 'output' // Raw process output (stdout/stderr)
+]);
+export type LogLevel = z.infer;
+
+// ==========================================
+// SIMPLIFIED ERROR TYPE FOR JSON LOGS
+// ==========================================
+
+export const SimpleErrorSchema = z.object({
+ timestamp: z.string(), // ISO timestamp
+ level: z.number(), // Pino log level (50=error, 60=fatal)
+ message: z.string(), // The 'msg' field from JSON log
+ rawOutput: z.string() // The complete raw JSON log line
+});
+export type SimpleError = z.infer;
+
+// ==========================================
+// LOG TYPES
+// ==========================================
+
+export interface LogLine {
+ readonly content: string;
+ readonly timestamp: Date;
+ readonly stream: StreamType;
+ readonly processId: string;
+}
+
+// ==========================================
+// STORAGE SCHEMAS - Extend base types
+// ==========================================
+
+// StoredError extends SimpleError with storage-specific fields
+export const StoredErrorSchema = SimpleErrorSchema.extend({
+ id: z.number(),
+ instanceId: z.string(),
+ processId: z.string(),
+ errorHash: z.string(),
+ occurrenceCount: z.number(),
+ createdAt: z.string()
+});
+export type StoredError = z.infer;
+
+// Base fields shared by stored entities
+const StoredEntityBaseSchema = z.object({
+ id: z.number(),
+ instanceId: z.string(),
+ processId: z.string(),
+ timestamp: z.string(),
+ createdAt: z.string()
+});
+
+// StoredLog extends base with log-specific fields
+export const StoredLogSchema = StoredEntityBaseSchema.extend({
+ level: LogLevelSchema,
+ message: z.string(),
+ stream: StreamTypeSchema,
+ source: z.string().optional(),
+ metadata: z.string().nullable(),
+ sequence: z.number()
+});
+export type StoredLog = z.infer;
+
+// ==========================================
+// PROCESS MONITORING TYPES
+// ==========================================
+
+export const ProcessStateSchema = z.enum([
+ 'starting',
+ 'running',
+ 'stopping',
+ 'stopped',
+ 'crashed',
+ 'restarting'
+]);
+export type ProcessState = z.infer;
+
+export interface ProcessInfo {
+ readonly id: string;
+ readonly instanceId: string;
+ readonly command: string;
+ readonly args?: readonly string[];
+ readonly cwd: string;
+ pid?: number;
+ readonly env?: Record;
+ readonly startTime?: Date;
+ readonly status?: ProcessState;
+ readonly endTime?: Date;
+ readonly exitCode?: number;
+ readonly restartCount: number;
+ readonly lastError?: string;
+}
+
+export interface MonitoringOptions {
+ readonly autoRestart?: boolean;
+ readonly maxRestarts?: number;
+ readonly restartDelay?: number;
+ readonly healthCheckInterval?: number;
+ readonly errorBufferSize?: number;
+ readonly enableMetrics?: boolean;
+ readonly env?: Record;
+ readonly killTimeout?: number;
+}
+
+// ==========================================
+// STORAGE OPTIONS
+// ==========================================
+
+// Base storage options shared by error and log stores
+interface BaseStoreOptions {
+ readonly vacuumInterval?: number; // Hours between cleanup runs
+}
+
+export interface ErrorStoreOptions extends BaseStoreOptions {
+ readonly maxErrors?: number;
+ readonly retentionDays?: number;
+}
+
+export interface LogStoreOptions extends BaseStoreOptions {
+ readonly maxLogs?: number;
+ readonly retentionHours?: number;
+ readonly bufferSize?: number;
+}
+
+// ==========================================
+// FILTER & CURSOR TYPES
+// ==========================================
+
+// Base filter options shared by all filters
+interface BaseFilter {
+ readonly instanceId?: string;
+ readonly since?: Date;
+ readonly until?: Date;
+ readonly limit?: number;
+ readonly offset?: number;
+ readonly sortOrder?: 'asc' | 'desc';
+}
+
+export interface ErrorFilter extends BaseFilter {
+ readonly level?: number;
+ readonly includeRaw?: boolean;
+ readonly sortBy?: 'timestamp' | 'occurrenceCount';
+}
+
+export interface LogFilter extends BaseFilter {
+ readonly levels?: readonly LogLevel[];
+ readonly streams?: readonly StreamType[];
+ readonly includeMetadata?: boolean;
+ readonly afterSequence?: number;
+}
+
+export interface LogCursor {
+ readonly instanceId: string;
+ readonly lastSequence: number;
+ readonly lastRetrieved: Date;
+}
+
+// ==========================================
+// SUMMARY TYPES
+// ==========================================
+
+export interface ErrorSummary {
+ readonly totalErrors: number;
+ readonly errorsByLevel: Record;
+ readonly latestError?: Date;
+ readonly oldestError?: Date;
+ readonly uniqueErrors: number;
+ readonly repeatedErrors: number;
+}
+
+export interface LogRetrievalResponse {
+ readonly success: boolean;
+ readonly logs: readonly StoredLog[];
+ readonly cursor: LogCursor;
+ readonly hasMore: boolean;
+ readonly totalCount?: number;
+ readonly error?: string;
+}
+
+// ==========================================
+// MONITORING EVENTS
+// ==========================================
+
+export type MonitoringEvent =
+ | {
+ type: 'process_started';
+ processId: string;
+ instanceId: string;
+ pid?: number;
+ command?: string;
+ timestamp: Date;
+ }
+ | {
+ type: 'process_stopped';
+ processId: string;
+ instanceId: string;
+ exitCode?: number | null;
+ reason?: string;
+ timestamp: Date;
+ }
+ | {
+ type: 'process_exited';
+ processId: string;
+ instanceId: string;
+ code: number | null;
+ signal: NodeJS.Signals | null;
+ timestamp: Date;
+ }
+ | {
+ type: 'process_error';
+ processId: string;
+ instanceId: string;
+ error: string;
+ timestamp: Date;
+ }
+ | {
+ type: 'error_detected';
+ processId: string;
+ instanceId: string;
+ error: SimpleError;
+ timestamp: Date;
+ }
+ | {
+ type: 'process_crashed';
+ processId: string;
+ instanceId: string;
+ exitCode?: number | null;
+ signal?: string | null;
+ willRestart?: boolean;
+ timestamp: Date;
+ }
+ | {
+ type: 'restart_failed';
+ processId: string;
+ instanceId: string;
+ attempt: number;
+ error?: string;
+ timestamp: Date;
+ }
+ | {
+ type: 'health_check_failed';
+ processId: string;
+ instanceId: string;
+ lastActivity: Date;
+ timestamp: Date;
+ }
+ | {
+ type: 'state_changed';
+ processId: string;
+ instanceId: string;
+ oldState: ProcessState;
+ newState: ProcessState;
+ timestamp: Date;
+ };
+// ==========================================
+// CONFIGURATION TYPES
+// ==========================================
+
+// Combines ProcessInfo with monitoring and storage config
+export interface ProcessRunnerConfig {
+ readonly instanceId: string;
+ readonly command: string;
+ readonly args: readonly string[];
+ readonly cwd: string;
+ readonly monitoring?: MonitoringOptions;
+ readonly storage?: {
+ readonly error?: ErrorStoreOptions;
+ readonly log?: LogStoreOptions;
+ };
+}
+
+// ==========================================
+// UTILITY TYPES
+// ==========================================
+
+export type Result =
+ | { readonly success: true; readonly data: T }
+ | { readonly success: false; readonly error: E };
+
+// ==========================================
+// CONSTANTS
+// ==========================================
+
+export const DEFAULT_MONITORING_OPTIONS: MonitoringOptions = {
+ autoRestart: true,
+ maxRestarts: 6,
+ restartDelay: 1000,
+ errorBufferSize: 300,
+ healthCheckInterval: 10000,
+ enableMetrics: false
+} as const;
+
+export const DEFAULT_STORAGE_OPTIONS: ErrorStoreOptions = {
+ maxErrors: 1000,
+ retentionDays: 7,
+ vacuumInterval: 24
+} as const;
+
+export const DEFAULT_LOG_STORE_OPTIONS: LogStoreOptions = {
+ maxLogs: 10000,
+ retentionHours: 168, // 7 days
+ bufferSize: 1000
+} as const;
+
+// Configurable paths - use environment variables or default to ./data directory
+export const getDataDirectory = (): string => {
+ return process.env.CLI_DATA_DIR || './.data';
+};
+
+export const getErrorDbPath = (): string => {
+ return process.env.CLI_ERROR_DB_PATH || `${getDataDirectory()}/errors.db`;
+};
+
+export const getLogDbPath = (): string => {
+ return process.env.CLI_LOG_DB_PATH || `${getDataDirectory()}/logs.db`;
+};
+
+// CLI tools path resolution for different environments
+export const getCliToolsPath = (): string => {
+ // In Docker container, use absolute path
+ if (process.env.CONTAINER_ENV === 'docker') {
+ return '/app/container/cli-tools.ts';
+ }
+
+ // For local development, try to find the cli-tools.ts file
+ const path = require('path');
+ const fs = require('fs');
+
+ // Common locations to check
+ const possiblePaths = [
+ './cli-tools.ts',
+ './container/cli-tools.ts',
+ '../container/cli-tools.ts',
+ path.join(__dirname, 'cli-tools.ts'),
+ path.join(process.cwd(), 'container/cli-tools.ts')
+ ];
+
+ for (const possiblePath of possiblePaths) {
+ try {
+ if (fs.existsSync(possiblePath)) {
+ return path.resolve(possiblePath);
+ }
+ } catch (error) {
+ // Continue checking other paths
+ }
+ }
+
+ // Fallback to relative path
+ return './cli-tools.ts';
+};
+
+// Legacy constants for backward compatibility
+export const ERROR_DB_PATH = getErrorDbPath();
+export const LOG_DB_PATH = getLogDbPath();
+export const ERROR_HASH_ALGORITHM = 'sha256' as const;
\ No newline at end of file
diff --git a/external/drpower-vibe-production/debug-tools/ai_request_analyzer_v2.py b/external/drpower-vibe-production/debug-tools/ai_request_analyzer_v2.py
new file mode 100644
index 00000000..6f7e0d94
--- /dev/null
+++ b/external/drpower-vibe-production/debug-tools/ai_request_analyzer_v2.py
@@ -0,0 +1,987 @@
+#!/usr/bin/env python3
+"""
+AI Request Analyzer v2.0 - Enhanced Type-Safe Phase Implementation Analysis
+
+This comprehensive, type-safe analyzer examines AI inference requests from the
+PhaseImplementation operation with proper SCOF format parsing and strict adherence
+to DRY principles.
+
+Features:
+- Type-safe data structures with proper validation
+- Accurate SCOF format parsing with file extraction
+- Dependencies list analysis with package breakdown
+- Blueprint schema parsing with section details
+- Template variable tracking and overhead calculation
+- Cross-message duplication detection
+- Compression potential estimation with precise calculations
+
+Usage:
+ python ai_request_analyzer_v2.py path/to/sample-request.json --detailed
+"""
+
+import json
+import sys
+import re
+from dataclasses import dataclass, field
+from typing import Dict, List, Any, Optional, Tuple, Union, TypedDict, Protocol
+from pathlib import Path
+import argparse
+from collections import defaultdict, Counter
+import math
+from enum import Enum
+from abc import ABC, abstractmethod
+
+
+class ContentType(Enum):
+ """Enumeration for content types."""
+ SOURCE_CODE = "source_code"
+ JSON_DATA = "json_data"
+ MARKDOWN_STRUCTURED = "markdown_structured"
+ LARGE_TEXT = "large_text"
+ METADATA = "metadata"
+ PROSE = "prose"
+
+
+class ComponentName(Enum):
+ """Enumeration for component names."""
+ ROLE_SECTION = "role_section"
+ GOAL_SECTION = "goal_section"
+ CONTEXT_SECTION = "context_section"
+ CLIENT_REQUEST = "client_request"
+ BLUEPRINT = "blueprint"
+ DEPENDENCIES = "dependencies"
+ UI_GUIDELINES = "ui_guidelines"
+ STRATEGY = "strategy"
+ TEMPLATE_DETAILS = "template_details"
+ PROJECT_CONTEXT = "project_context"
+ COMPLETED_PHASES = "completed_phases"
+ CODEBASE = "codebase"
+ RUNTIME_ERRORS = "runtime_errors"
+ CURRENT_PHASE = "current_phase"
+ INSTRUCTIONS = "instructions"
+ COMMON_PITFALLS = "common_pitfalls"
+ REACT_PREVENTION = "react_prevention"
+ OUTPUT_FORMAT = "output_format"
+
+
+@dataclass(frozen=True)
+class SCOFFile:
+ """Type-safe representation of a file in SCOF format."""
+ path: str
+ purpose: str
+ content: str
+ content_size: int
+ format_type: str # 'full_content' or 'unified_diff'
+
+ def __post_init__(self):
+ """Validate file data."""
+ if not self.path:
+ raise ValueError("File path cannot be empty")
+ if self.content_size != len(self.content):
+ raise ValueError("Content size mismatch")
+
+ @property
+ def purpose_preview(self) -> str:
+ """Get first 50 chars of purpose."""
+ return self.purpose[:50] + "..." if len(self.purpose) > 50 else self.purpose
+
+ @property
+ def content_preview(self) -> str:
+ """Get first 50 chars of content."""
+ content_clean = self.content.strip()[:50]
+ return content_clean + "..." if len(self.content) > 50 else content_clean
+
+ @property
+ def file_extension(self) -> str:
+ """Get file extension."""
+ return Path(self.path).suffix.lower()
+
+ @property
+ def is_test_file(self) -> bool:
+ """Check if this is a test file."""
+ test_patterns = ['test', 'spec', '.test.', '.spec.', '__tests__', 'example', 'demo', 'sample', '.stories.', 'mock']
+ return any(pattern in self.path.lower() for pattern in test_patterns)
+
+
+@dataclass(frozen=True)
+class Dependency:
+ """Type-safe representation of a package dependency."""
+ name: str
+ version: str
+ category: str # 'runtime', 'dev', 'peer'
+
+ def __post_init__(self):
+ """Validate dependency data."""
+ if not self.name or not self.version:
+ raise ValueError("Dependency name and version are required")
+
+ @property
+ def is_dev_dependency(self) -> bool:
+ """Check if this is a dev dependency."""
+ dev_indicators = ['@types/', 'eslint', 'typescript', 'vite', '@vitejs/', 'autoprefixer', 'postcss', 'globals']
+ return any(indicator in self.name for indicator in dev_indicators)
+
+ @property
+ def size_estimate(self) -> int:
+ """Estimate package size contribution in chars."""
+ return len(f'"{self.name}":"{self.version}",')
+
+
+@dataclass(frozen=True)
+class PromptComponent:
+ """Type-safe representation of a prompt component."""
+ name: ComponentName
+ content: str
+ start_marker: str
+ end_marker: str
+ size_chars: int
+ content_type: ContentType
+
+ def __post_init__(self):
+ """Validate component data."""
+ if self.size_chars != len(self.content):
+ raise ValueError("Size mismatch in PromptComponent")
+
+ @property
+ def size_tokens_approx(self) -> int:
+ """Approximate token count."""
+ return math.ceil(self.size_chars / 4)
+
+ @property
+ def percentage_of_request(self) -> float:
+ """Percentage of total request (set externally)."""
+ return 0.0 # Will be calculated by analyzer
+
+
+@dataclass
+class MessageAnalysis:
+ """Type-safe analysis of a single message."""
+ role: str
+ content: str
+ size_chars: int
+ size_tokens_approx: int
+ components: List[PromptComponent] = field(default_factory=list)
+
+ def __post_init__(self):
+ """Validate message data."""
+ if self.size_chars != len(self.content):
+ raise ValueError("Size mismatch in MessageAnalysis")
+
+ def add_component(self, component: PromptComponent) -> None:
+ """Add a component to this message."""
+ self.components.append(component)
+
+
+@dataclass
+class SCOFAnalysis:
+ """Comprehensive SCOF format analysis."""
+ files: List[SCOFFile]
+ total_files: int
+ total_content_size: int
+ total_metadata_overhead: int
+ file_type_distribution: Dict[str, int]
+ test_files: List[SCOFFile]
+
+ def __post_init__(self):
+ """Validate SCOF analysis."""
+ if self.total_files != len(self.files):
+ raise ValueError("File count mismatch")
+
+ @property
+ def overhead_percentage(self) -> float:
+ """Calculate SCOF overhead as percentage."""
+ return (self.total_metadata_overhead / max(self.total_content_size, 1)) * 100
+
+ @property
+ def filterable_files_count(self) -> int:
+ """Count of files that can be filtered out."""
+ return len(self.test_files)
+
+ @property
+ def filterable_size_savings(self) -> int:
+ """Estimated size savings from filtering test files."""
+ return sum(f.content_size + 50 for f in self.test_files) # +50 for SCOF overhead per file
+
+
+@dataclass
+class DependencyAnalysis:
+ """Analysis of project dependencies."""
+ dependencies: List[Dependency]
+ total_count: int
+ total_serialized_size: int # Just the JSON object size
+ dev_dependencies: List[Dependency]
+ runtime_dependencies: List[Dependency]
+ full_component_size: int = 0 # Full component including all text
+ blueprint_dependencies_text: str = "" # The blueprint.frameworks text
+
+ def __post_init__(self):
+ """Validate dependency analysis."""
+ if self.total_count != len(self.dependencies):
+ raise ValueError("Dependency count mismatch")
+
+ @property
+ def dev_dependency_overhead(self) -> int:
+ """Size of dev dependencies that could be excluded."""
+ return sum(dep.size_estimate for dep in self.dev_dependencies)
+
+ @property
+ def optimization_potential(self) -> float:
+ """Potential size reduction percentage."""
+ return (self.dev_dependency_overhead / max(self.total_serialized_size, 1)) * 100
+
+ @property
+ def template_bloat(self) -> int:
+ """Size of template text vs actual data."""
+ return self.full_component_size - self.total_serialized_size - len(self.blueprint_dependencies_text)
+
+ @property
+ def bloat_percentage(self) -> float:
+ """Percentage of component that is template bloat."""
+ return (self.template_bloat / max(self.full_component_size, 1)) * 100
+
+
+@dataclass
+class TemplateAnalysis:
+ """Analysis of template serialization efficiency."""
+ template_variables: List[str]
+ substitution_overhead: int
+ markdown_sections: int
+ markdown_overhead: int
+ unused_sections: List[str]
+ total_template_size: int
+
+ @property
+ def efficiency_score(self) -> float:
+ """Calculate template efficiency score (0-100)."""
+ total_overhead = self.substitution_overhead + self.markdown_overhead
+ return max(0, 100 - (total_overhead / max(self.total_template_size, 1) * 100))
+
+
+class BaseAnalyzer(ABC):
+ """Abstract base class for analyzers to ensure consistent interface."""
+
+ @abstractmethod
+ def analyze(self, content: str) -> Any:
+ """Analyze content and return results."""
+ pass
+
+
+class SCOFParser(BaseAnalyzer):
+ """Type-safe SCOF format parser."""
+
+ SCOF_FILE_PATTERN = re.compile(
+ r'# Creating new file: ([^\n]+)\n'
+ r'# File Purpose: ([^\n]*(?:\n# [^\n]*)*)\n*'
+ r'cat > [^\n]+ << \'EOF\'\n'
+ r'(.*?)\n'
+ r'EOF',
+ re.DOTALL | re.MULTILINE
+ )
+
+ SCOF_DIFF_PATTERN = re.compile(
+ r'# Applying diff to file: ([^\n]+)\n'
+ r'# File Purpose: ([^\n]*(?:\n# [^\n]*)*)\n*'
+ r'cat << \'EOF\' \| patch [^\n]+\n'
+ r'(.*?)\n'
+ r'EOF',
+ re.DOTALL | re.MULTILINE
+ )
+
+ def analyze(self, content: str) -> SCOFAnalysis:
+ """Parse SCOF content and extract files."""
+ files = []
+
+ # Parse full content files
+ for match in self.SCOF_FILE_PATTERN.finditer(content):
+ file_path, purpose_raw, file_content = match.groups()
+ purpose = self._clean_purpose(purpose_raw)
+
+ scof_file = SCOFFile(
+ path=file_path.strip(),
+ purpose=purpose,
+ content=file_content,
+ content_size=len(file_content),
+ format_type='full_content'
+ )
+ files.append(scof_file)
+
+ # Parse diff files
+ for match in self.SCOF_DIFF_PATTERN.finditer(content):
+ file_path, purpose_raw, diff_content = match.groups()
+ purpose = self._clean_purpose(purpose_raw)
+
+ scof_file = SCOFFile(
+ path=file_path.strip(),
+ purpose=purpose,
+ content=diff_content,
+ content_size=len(diff_content),
+ format_type='unified_diff'
+ )
+ files.append(scof_file)
+
+ return self._build_analysis(files, content)
+
+ def _clean_purpose(self, purpose_raw: str) -> str:
+ """Clean purpose text from SCOF comments."""
+ return re.sub(r'\n# ', ' ', purpose_raw).strip()
+
+ def _build_analysis(self, files: List[SCOFFile], content: str) -> SCOFAnalysis:
+ """Build comprehensive SCOF analysis."""
+ file_type_dist = defaultdict(int)
+ test_files = []
+ total_content_size = 0
+ total_metadata_overhead = 0
+
+ for file in files:
+ file_type_dist[file.file_extension] += 1
+ total_content_size += file.content_size
+
+ if file.is_test_file:
+ test_files.append(file)
+
+ # Calculate SCOF metadata overhead
+ metadata_size = len(f"# Creating new file: {file.path}\n# File Purpose: {file.purpose}\n\ncat > {file.path} << 'EOF'\nEOF\n\n")
+ total_metadata_overhead += metadata_size
+
+ return SCOFAnalysis(
+ files=files,
+ total_files=len(files),
+ total_content_size=total_content_size,
+ total_metadata_overhead=total_metadata_overhead,
+ file_type_distribution=dict(file_type_dist),
+ test_files=test_files
+ )
+
+
+class DependencyParser(BaseAnalyzer):
+ """Type-safe dependency parser."""
+
+ def analyze(self, content: str) -> DependencyAnalysis:
+ """Parse dependencies from the full component content."""
+ # Find the JSON dependencies object
+ json_match = re.search(r'\{("[^"]+":"[^"]+",?\s*)+\}', content)
+ if not json_match:
+ return self._empty_analysis()
+
+ try:
+ deps_json = json.loads(json_match.group(0))
+ except json.JSONDecodeError:
+ return self._empty_analysis()
+
+ dependencies = []
+ dev_deps = []
+ runtime_deps = []
+
+ for name, version in deps_json.items():
+ dep = Dependency(name=name, version=version, category='runtime')
+ dependencies.append(dep)
+
+ if dep.is_dev_dependency:
+ dev_deps.append(dep)
+ else:
+ runtime_deps.append(dep)
+
+ # JSON object size (the actual dependencies data)
+ json_size = len(json_match.group(0))
+
+ # Find blueprint dependencies (comma-separated frameworks)
+ blueprint_deps_match = re.search(r'additional dependencies/frameworks.*?provided:\s*([^\n]+)', content, re.DOTALL)
+ blueprint_deps_text = blueprint_deps_match.group(1).strip() if blueprint_deps_match else ""
+
+ return DependencyAnalysis(
+ dependencies=dependencies,
+ total_count=len(dependencies),
+ total_serialized_size=json_size, # Just the JSON object size
+ dev_dependencies=dev_deps,
+ runtime_dependencies=runtime_deps,
+ full_component_size=len(content), # Full component size
+ blueprint_dependencies_text=blueprint_deps_text
+ )
+
+ def _empty_analysis(self) -> DependencyAnalysis:
+ """Return empty dependency analysis."""
+ return DependencyAnalysis(
+ dependencies=[],
+ total_count=0,
+ total_serialized_size=0,
+ dev_dependencies=[],
+ runtime_dependencies=[],
+ full_component_size=0,
+ blueprint_dependencies_text=""
+ )
+
+
+class TemplateParser(BaseAnalyzer):
+ """Type-safe template analysis parser."""
+
+ TEMPLATE_VAR_PATTERN = re.compile(r'\{\{(\w+)\}\}')
+ MARKDOWN_SECTION_PATTERN = re.compile(r'^#{2,6}\s+(.+)$', re.MULTILINE)
+
+ def analyze(self, content: str) -> TemplateAnalysis:
+ """Analyze template usage and efficiency."""
+ # Find template variables
+ template_vars = list(set(self.TEMPLATE_VAR_PATTERN.findall(content)))
+
+ # Count markdown sections
+ markdown_sections = len(self.MARKDOWN_SECTION_PATTERN.findall(content))
+
+ # Calculate overheads
+ substitution_overhead = len(template_vars) * 20 # Estimated overhead per variable
+ markdown_overhead = markdown_sections * 50 # Estimated overhead per section
+
+ # Detect unused sections (simplified heuristic)
+ unused_sections = []
+ if 'placeholder' in content.lower() or 'example' in content.lower():
+ unused_sections.append('example_content')
+
+ return TemplateAnalysis(
+ template_variables=template_vars,
+ substitution_overhead=substitution_overhead,
+ markdown_sections=markdown_sections,
+ markdown_overhead=markdown_overhead,
+ unused_sections=unused_sections,
+ total_template_size=len(content)
+ )
+
+
+@dataclass
+class OptimizationRecommendation:
+ """Type-safe optimization recommendation."""
+ title: str
+ description: str
+ estimated_savings_chars: int
+ estimated_savings_percentage: float
+ implementation_difficulty: str # 'easy', 'medium', 'hard'
+ code_location: Optional[str] = None
+
+ @property
+ def estimated_savings_tokens(self) -> int:
+ """Estimated token savings."""
+ return math.ceil(self.estimated_savings_chars / 4)
+
+
+@dataclass
+class RequestAnalysis:
+ """Complete type-safe request analysis."""
+ model: str
+ total_size_chars: int
+ total_size_tokens_approx: int
+ total_messages: int
+ messages: List[MessageAnalysis]
+ scof_analysis: Optional[SCOFAnalysis] = None
+ dependency_analysis: Optional[DependencyAnalysis] = None
+ template_analysis: Optional[TemplateAnalysis] = None
+ recommendations: List[OptimizationRecommendation] = field(default_factory=list)
+
+ def __post_init__(self):
+ """Validate request analysis."""
+ if self.total_messages != len(self.messages):
+ raise ValueError("Message count mismatch")
+ if self.total_size_chars != sum(msg.size_chars for msg in self.messages):
+ raise ValueError("Total size mismatch")
+
+
+class PhaseImplementationAnalyzer:
+ """Main type-safe analyzer for Phase Implementation requests."""
+
+ def __init__(self):
+ self.scof_parser = SCOFParser()
+ self.dependency_parser = DependencyParser()
+ self.template_parser = TemplateParser()
+
+ self.prompt_patterns = self._get_prompt_patterns()
+
+ def _get_prompt_patterns(self) -> Dict[ComponentName, Tuple[str, str]]:
+ """Get prompt component patterns."""
+ return {
+ ComponentName.ROLE_SECTION: ('', ' '),
+ ComponentName.GOAL_SECTION: ('', ' '),
+ ComponentName.CONTEXT_SECTION: ('', ' '),
+ ComponentName.CLIENT_REQUEST: ('', ' '),
+ ComponentName.BLUEPRINT: ('', ' '),
+ # Use more specific pattern for DEPENDENCIES to avoid matching references
+ ComponentName.DEPENDENCIES: ('\n**Available Dependencies:**', ' '),
+ ComponentName.STRATEGY: ('', ' '),
+ ComponentName.PROJECT_CONTEXT: ('', ' '),
+ ComponentName.COMPLETED_PHASES: ('', ' '),
+ ComponentName.CODEBASE: ('', ' '),
+ ComponentName.CURRENT_PHASE: ('', ' '),
+ ComponentName.INSTRUCTIONS: ('', ' '),
+ }
+
+ def analyze_request(self, json_path: str) -> RequestAnalysis:
+ """Analyze AI request with full type safety."""
+ print(f"๐ Analyzing AI request: {json_path}")
+
+ with open(json_path, 'r', encoding='utf-8') as f:
+ request_data = json.load(f)
+
+ messages = []
+ total_size = 0
+
+ # Analyze each message
+ for msg_data in request_data.get('messages', []):
+ content = msg_data.get('content', '')
+ size_chars = len(content)
+ total_size += size_chars
+
+ message_analysis = MessageAnalysis(
+ role=msg_data.get('role', 'unknown'),
+ content=content,
+ size_chars=size_chars,
+ size_tokens_approx=math.ceil(size_chars / 4)
+ )
+
+ # Extract components
+ components = self._extract_components(content, total_size)
+ for component in components:
+ message_analysis.add_component(component)
+
+ messages.append(message_analysis)
+
+ # Create base analysis
+ analysis = RequestAnalysis(
+ model=request_data.get('model', 'unknown'),
+ total_size_chars=total_size,
+ total_size_tokens_approx=math.ceil(total_size / 4),
+ total_messages=len(messages),
+ messages=messages
+ )
+
+ # Enhanced analysis
+ print("๐ฌ Running enhanced analysis...")
+ print(" ๐ก File fetching mechanism: sandboxSdkClient.getFiles() reads .important_files.json")
+ analysis.scof_analysis = self._analyze_scof(analysis)
+ analysis.dependency_analysis = self._analyze_dependencies(analysis)
+ analysis.template_analysis = self._analyze_templates(analysis)
+ analysis.recommendations = self._generate_recommendations(analysis)
+
+ return analysis
+
+ def _extract_components(self, content: str, total_size: int) -> List[PromptComponent]:
+ """Extract components from message content."""
+ components = []
+
+ for component_name, (start_marker, end_marker) in self.prompt_patterns.items():
+ pattern = re.escape(start_marker) + r'(.*?)' + re.escape(end_marker)
+ matches = re.finditer(pattern, content, re.DOTALL)
+
+ for match in matches:
+ component_content = match.group(1).strip()
+ content_type = self._classify_content_type(component_content)
+
+
+ component = PromptComponent(
+ name=component_name,
+ content=component_content,
+ start_marker=start_marker,
+ end_marker=end_marker,
+ size_chars=len(component_content),
+ content_type=content_type
+ )
+ components.append(component)
+
+ return components
+
+ def _classify_content_type(self, content: str) -> ContentType:
+ """Classify content type."""
+ content_lower = content.lower()
+
+ if content.count('"') > 10 and ':' in content:
+ return ContentType.JSON_DATA
+ elif re.search(r'^#{2,6}', content, re.MULTILINE):
+ return ContentType.MARKDOWN_STRUCTURED
+ elif 'import' in content_lower and ('export' in content_lower or 'function' in content_lower):
+ return ContentType.SOURCE_CODE
+ elif content.count('\n') > 50:
+ return ContentType.LARGE_TEXT
+ elif len(content) < 100:
+ return ContentType.METADATA
+ else:
+ return ContentType.PROSE
+
+ def _find_component_content(self, analysis: RequestAnalysis, component_name: ComponentName) -> str:
+ """DRY helper: Find content of a specific component across all messages."""
+ for message in analysis.messages:
+ for component in message.components:
+ if component.name == component_name:
+ return component.content
+ return ""
+
+ def _analyze_scof(self, analysis: RequestAnalysis) -> Optional[SCOFAnalysis]:
+ """Analyze SCOF format in the request."""
+ print(" ๐ Analyzing SCOF format...")
+
+ codebase_content = self._find_component_content(analysis, ComponentName.CODEBASE)
+ if codebase_content:
+ return self.scof_parser.analyze(codebase_content)
+ return None
+
+ def _analyze_dependencies(self, analysis: RequestAnalysis) -> Optional[DependencyAnalysis]:
+ """Analyze dependencies in the request."""
+ print(" ๐ฆ Analyzing dependencies...")
+
+ deps_content = self._find_component_content(analysis, ComponentName.DEPENDENCIES)
+ if deps_content:
+ return self.dependency_parser.analyze(deps_content)
+ return None
+
+ def _analyze_templates(self, analysis: RequestAnalysis) -> Optional[TemplateAnalysis]:
+ """Analyze template usage."""
+ print(" ๐๏ธ Analyzing templates...")
+
+ # Combine all content for template analysis
+ all_content = " ".join(msg.content for msg in analysis.messages)
+ return self.template_parser.analyze(all_content)
+
+ def _generate_recommendations(self, analysis: RequestAnalysis) -> List[OptimizationRecommendation]:
+ """Generate type-safe optimization recommendations."""
+ print(" ๐ก Generating recommendations...")
+
+ recommendations = []
+
+ # Critical size warning
+ if analysis.total_size_chars > 200_000:
+ recommendations.append(OptimizationRecommendation(
+ title="Critical Size Reduction",
+ description=f"Request size ({analysis.total_size_chars:,} chars) exceeds optimal LLM limits",
+ estimated_savings_chars=analysis.total_size_chars - 150_000,
+ estimated_savings_percentage=((analysis.total_size_chars - 150_000) / analysis.total_size_chars) * 100,
+ implementation_difficulty="medium"
+ ))
+
+ # SCOF optimizations
+ if analysis.scof_analysis:
+ scof = analysis.scof_analysis
+ if scof.filterable_files_count > 0:
+ recommendations.append(OptimizationRecommendation(
+ title="SCOF File Filtering",
+ description=f"Remove {scof.filterable_files_count} test/demo files from codebase serialization",
+ estimated_savings_chars=scof.filterable_size_savings,
+ estimated_savings_percentage=(scof.filterable_size_savings / analysis.total_size_chars) * 100,
+ implementation_difficulty="easy",
+ code_location="worker/agents/operations/common.ts:getSystemPromptWithProjectContext()"
+ ))
+
+ # Dependency optimizations
+ if analysis.dependency_analysis:
+ deps = analysis.dependency_analysis
+ if deps.optimization_potential > 10:
+ recommendations.append(OptimizationRecommendation(
+ title="Dependency List Optimization",
+ description=f"Exclude dev dependencies from prompt context",
+ estimated_savings_chars=deps.dev_dependency_overhead,
+ estimated_savings_percentage=deps.optimization_potential,
+ implementation_difficulty="easy",
+ code_location="worker/agents/prompts.ts:PROMPT_UTILS.serializeTemplate()"
+ ))
+
+ # Template optimizations
+ if analysis.template_analysis:
+ template = analysis.template_analysis
+ if template.efficiency_score < 70:
+ savings = int(template.total_template_size * 0.2) # Conservative 20% reduction
+ recommendations.append(OptimizationRecommendation(
+ title="Template Serialization Optimization",
+ description="Optimize markdown schema serialization and remove unused sections",
+ estimated_savings_chars=savings,
+ estimated_savings_percentage=(savings / analysis.total_size_chars) * 100,
+ implementation_difficulty="medium",
+ code_location="worker/agents/inferutils/schemaFormatters.ts:TemplateRegistry.markdown.serialize()"
+ ))
+
+ return recommendations
+
+ def _print_component_breakdown_table(self, analysis: RequestAnalysis) -> None:
+ """Print detailed table of all prompt components with accurate sizes."""
+ # Collect all components from all messages
+ all_components = []
+ for msg_idx, message in enumerate(analysis.messages):
+ for component in message.components:
+ all_components.append((msg_idx, message.role, component))
+
+ # Sort by size (descending)
+ all_components.sort(key=lambda x: x[2].size_chars, reverse=True)
+
+ # Print table header
+ print(f" {'Section':<25} {'Message':<8} {'Size':<10} {'%':<6} {'Type':<12} {'Description':<50}")
+ print(" " + "-" * 111)
+
+ # Print all components
+ for msg_idx, msg_role, component in all_components:
+ percentage = (component.size_chars / analysis.total_size_chars) * 100
+ section_name = component.name.value.replace('_', ' ').title()
+ description = self._get_component_description_short(component.name)
+
+ print(f" {section_name[:24]:<25} {msg_role:<8} {component.size_chars:>8,} {percentage:>5.1f}% {component.content_type.value:<12} {description[:49]:<50}")
+
+ # Calculate non-overlapping total by handling nested components
+ total_non_overlapping = self._calculate_non_overlapping_total(all_components, analysis)
+ unidentified = analysis.total_size_chars - total_non_overlapping
+
+ print(" " + "-" * 111)
+ print(f" {'NON-OVERLAPPING TOTAL':<25} {'ALL':<8} {total_non_overlapping:>8,} {(total_non_overlapping/analysis.total_size_chars)*100:>5.1f}% {'MIXED':<12} {'Adjusted for nested components':<50}")
+ if unidentified > 0:
+ print(f" {'UNIDENTIFIED/OTHER':<25} {'ALL':<8} {unidentified:>8,} {(unidentified/analysis.total_size_chars)*100:>5.1f}% {'UNKNOWN':<12} {'Headers, formatting, assistant messages':<50}")
+ elif unidentified < 0:
+ print(f" {'NOTE: NESTED OVERLAP':<25} {'ALL':<8} {abs(unidentified):>8,} {'chars'} {'WARNING':<12} {'Some components are nested within others':<50}")
+
+ # Show component-specific breakdowns for complex sections
+ self._print_special_component_breakdowns(analysis)
+
+ def _get_component_description_short(self, component_name: ComponentName) -> str:
+ """Get short description for component."""
+ descriptions = {
+ ComponentName.ROLE_SECTION: 'Agent role and identity',
+ ComponentName.GOAL_SECTION: 'Primary objectives',
+ ComponentName.CONTEXT_SECTION: 'Operational constraints',
+ ComponentName.CLIENT_REQUEST: 'Original user query',
+ ComponentName.BLUEPRINT: 'Project specifications',
+ ComponentName.DEPENDENCIES: 'Available packages',
+ ComponentName.STRATEGY: 'Development approach',
+ ComponentName.PROJECT_CONTEXT: 'Current project state',
+ ComponentName.COMPLETED_PHASES: 'Previous phases',
+ ComponentName.CODEBASE: 'Source code files (SCOF)',
+ ComponentName.CURRENT_PHASE: 'Phase to implement',
+ ComponentName.INSTRUCTIONS: 'Code quality standards',
+ }
+ return descriptions.get(component_name, component_name.value)
+
+ def _calculate_non_overlapping_total(self, all_components: List, analysis: RequestAnalysis) -> int:
+ """Calculate total size avoiding double-counting nested components."""
+
+ # Known nested relationships based on prompt structure:
+ # PROJECT_CONTEXT contains COMPLETED_PHASES and CODEBASE
+ nested_components = {
+ ComponentName.PROJECT_CONTEXT: [ComponentName.COMPLETED_PHASES, ComponentName.CODEBASE]
+ }
+
+ total = 0
+ parent_components = set()
+ nested_in_parent = set()
+
+ # First, identify parent components and their nested children
+ for _, _, component in all_components:
+ if component.name in nested_components:
+ parent_components.add(component.name)
+ for nested_name in nested_components[component.name]:
+ nested_in_parent.add(nested_name)
+
+ # Sum non-nested components + parent components (which include nested content)
+ for _, _, component in all_components:
+ if component.name in parent_components:
+ # Include parent component (it contains the nested ones)
+ total += component.size_chars
+ elif component.name not in nested_in_parent:
+ # Include non-nested component
+ total += component.size_chars
+ # Skip nested components to avoid double counting
+
+ return total
+
+ def _print_special_component_breakdowns(self, analysis: RequestAnalysis) -> None:
+ """Print detailed breakdowns for complex components."""
+
+ # Dependencies breakdown
+ if analysis.dependency_analysis:
+ deps = analysis.dependency_analysis
+ print(f"\n ๐ฆ DEPENDENCIES COMPONENT BREAKDOWN ({deps.full_component_size:,} chars):")
+ print(f" JSON object (84 packages): {deps.total_serialized_size:,} chars ({(deps.total_serialized_size/deps.full_component_size)*100:.1f}%)")
+ print(f" Blueprint frameworks: {len(deps.blueprint_dependencies_text):,} chars ({(len(deps.blueprint_dependencies_text)/deps.full_component_size)*100:.1f}%)")
+ print(f" Headers/formatting: {deps.template_bloat:,} chars ({deps.bloat_percentage:.1f}%)")
+ print(f" โโ Dev dependencies removable: {deps.dev_dependency_overhead:,} chars ({deps.optimization_potential:.1f}% of JSON)")
+
+ # SCOF breakdown
+ if analysis.scof_analysis:
+ scof = analysis.scof_analysis
+ print(f"\n ๐ CODEBASE COMPONENT BREAKDOWN (SCOF format):")
+ print(f" File content (40 files): {scof.total_content_size:,} chars ({(scof.total_content_size/(scof.total_content_size+scof.total_metadata_overhead))*100:.1f}%)")
+ print(f" SCOF metadata overhead: {scof.total_metadata_overhead:,} chars ({scof.overhead_percentage:.1f}%)")
+ print(f" โโ Test files removable: {scof.filterable_size_savings:,} chars ({len(scof.test_files)} file)")
+
+ def print_detailed_analysis(self, analysis: RequestAnalysis) -> None:
+ """Print comprehensive analysis results."""
+ print("\n" + "=" * 80)
+ print("๐ฌ AI REQUEST ANALYSIS - Type-Safe & SCOF-Accurate")
+ print("=" * 80)
+
+ # First show comprehensive component breakdown table
+ print(f"\n๐ COMPLETE PROMPT SECTIONS BREAKDOWN:")
+ self._print_component_breakdown_table(analysis)
+
+ # Then show file list from SCOF
+ if analysis.scof_analysis:
+ print(f"\n๐ COMPLETE FILE LIST FROM SCOF DUMP:")
+ print(f" {'#':<3} {'File Path':<40} {'Size':<8} {'Type':<6} {'Test?':<6} {'Purpose (truncated)'}")
+ print(" " + "-" * 100)
+
+ for i, file in enumerate(analysis.scof_analysis.files, 1):
+ is_test = "YES" if file.is_test_file else "NO"
+ file_type = file.file_extension or "none"
+ print(f" {i:<3} {file.path[:39]:<40} {file.content_size:>6,} {file_type:<6} {is_test:<6} {file.purpose_preview}")
+
+ print(f"\n Total files: {len(analysis.scof_analysis.files)}")
+ print(f" Test files (filterable): {len([f for f in analysis.scof_analysis.files if f.is_test_file])}")
+ print(f" Total content size: {sum(f.content_size for f in analysis.scof_analysis.files):,} chars")
+ print(f" SCOF metadata overhead: {analysis.scof_analysis.total_metadata_overhead:,} chars")
+
+ # Overview
+ print(f"\n๐ REQUEST OVERVIEW:")
+ print(f" Model: {analysis.model}")
+ print(f" Total Size: {analysis.total_size_chars:,} characters")
+ print(f" Token Estimate: ~{analysis.total_size_tokens_approx:,} tokens")
+ print(f" Messages: {analysis.total_messages}")
+
+ # Message breakdown
+ print(f"\n๐จ MESSAGE BREAKDOWN:")
+ for i, message in enumerate(analysis.messages):
+ print(f" Message {i+1} ({message.role:>9}): {message.size_chars:>8,} chars ({message.size_tokens_approx:>6,} tokens)")
+ for component in sorted(message.components, key=lambda x: x.size_chars, reverse=True)[:2]:
+ print(f" โณ {component.name.value}: {component.size_chars:,} chars")
+
+ # SCOF Analysis
+ if analysis.scof_analysis:
+ print(f"\n๐ SCOF FORMAT ANALYSIS:")
+ scof = analysis.scof_analysis
+ print(f" Total files: {scof.total_files}")
+ print(f" Content size: {scof.total_content_size:,} chars")
+ print(f" Metadata overhead: {scof.total_metadata_overhead:,} chars ({scof.overhead_percentage:.1f}%)")
+ print(f" Test/filterable files: {scof.filterable_files_count}")
+ print(f" Potential savings: {scof.filterable_size_savings:,} chars")
+
+ if scof.files:
+ print(f"\n ๐ FILES BREAKDOWN:")
+ print(f" {'File Path':<30} {'Purpose (50 chars)':<52} {'Content (50 chars)':<52} {'Size':<8}")
+ print(" " + "-" * 142)
+
+ for file in sorted(scof.files, key=lambda f: f.content_size, reverse=True)[:10]:
+ print(f" {file.path[:29]:<30} {file.purpose_preview:<52} {file.content_preview:<52} {file.content_size:>6,}")
+
+ # Dependencies Analysis
+ if analysis.dependency_analysis:
+ print(f"\n๐ฆ DEPENDENCIES ANALYSIS (CORRECTED):")
+ deps = analysis.dependency_analysis
+
+ print(f" Total dependencies: {deps.total_count}")
+ print(f" Runtime dependencies: {len(deps.runtime_dependencies)}")
+ print(f" Dev dependencies: {len(deps.dev_dependencies)}")
+ print(f" JSON object size: {deps.total_serialized_size:,} chars (actual data)")
+ print(f" Full component size: {deps.full_component_size:,} chars")
+ print(f" Blueprint dependencies: '{deps.blueprint_dependencies_text}' ({len(deps.blueprint_dependencies_text)} chars)")
+ print(f" Template bloat: {deps.template_bloat:,} chars ({deps.bloat_percentage:.1f}% of component)")
+ print(f" Dev dependency overhead: {deps.dev_dependency_overhead:,} chars ({deps.optimization_potential:.1f}% of JSON)")
+
+ print(f"\n ๐ TOP DEPENDENCIES BY SIZE:")
+ sorted_deps = sorted(deps.dependencies, key=lambda d: d.size_estimate, reverse=True)
+ for dep in sorted_deps[:10]:
+ dep_type = "DEV" if dep.is_dev_dependency else "RUN"
+ print(f" {dep.name:<40} {dep.version:<12} {dep_type:<4} {dep.size_estimate:>4} chars")
+
+ # Template Analysis
+ if analysis.template_analysis:
+ print(f"\n๐๏ธ TEMPLATE ANALYSIS:")
+ template = analysis.template_analysis
+ print(f" Template variables: {len(template.template_variables)}")
+ print(f" Markdown sections: {template.markdown_sections}")
+ print(f" Efficiency score: {template.efficiency_score:.1f}%")
+ print(f" Total template size: {template.total_template_size:,} chars")
+ print(f" Overhead: {template.substitution_overhead + template.markdown_overhead:,} chars")
+
+ if template.template_variables:
+ print(f" Variables: {', '.join(template.template_variables[:10])}{'...' if len(template.template_variables) > 10 else ''}")
+
+ # Recommendations
+ if analysis.recommendations:
+ print(f"\n๐ก OPTIMIZATION RECOMMENDATIONS:")
+ total_potential_savings = sum(r.estimated_savings_chars for r in analysis.recommendations)
+ print(f" Total potential savings: {total_potential_savings:,} chars ({(total_potential_savings/analysis.total_size_chars)*100:.1f}%)")
+ print()
+
+ for i, rec in enumerate(analysis.recommendations, 1):
+ print(f" {i}. {rec.title}")
+ print(f" Description: {rec.description}")
+ print(f" Savings: {rec.estimated_savings_chars:,} chars ({rec.estimated_savings_percentage:.1f}%)")
+ print(f" Difficulty: {rec.implementation_difficulty}")
+ if rec.code_location:
+ print(f" Location: {rec.code_location}")
+ print()
+
+
+def main():
+ """Main CLI entry point with proper error handling."""
+ parser = argparse.ArgumentParser(
+ description="Type-safe AI Gateway request analyzer for PhaseImplementation",
+ formatter_class=argparse.RawDescriptionHelpFormatter,
+ epilog="""
+Examples:
+ python ai_request_analyzer_v2.py sample-request.json --detailed
+ python ai_request_analyzer_v2.py sample-request.json --export analysis.json
+ """
+ )
+
+ parser.add_argument('request_file', help='Path to the JSON request file')
+ parser.add_argument('--detailed', '-d', action='store_true',
+ help='Print detailed analysis')
+ parser.add_argument('--export', '-e', help='Export analysis to JSON file')
+
+ args = parser.parse_args()
+
+ # Validate input
+ if not Path(args.request_file).exists():
+ print(f"โ Error: Request file not found: {args.request_file}")
+ sys.exit(1)
+
+ try:
+ # Run analysis
+ analyzer = PhaseImplementationAnalyzer()
+ analysis = analyzer.analyze_request(args.request_file)
+
+ # Print results
+ if args.detailed:
+ analyzer.print_detailed_analysis(analysis)
+ else:
+ print(f"\n๐ ANALYSIS SUMMARY:")
+ print(f" Request size: {analysis.total_size_chars:,} chars")
+ print(f" SCOF files: {analysis.scof_analysis.total_files if analysis.scof_analysis else 0}")
+ print(f" Dependencies: {analysis.dependency_analysis.total_count if analysis.dependency_analysis else 0}")
+ print(f" Optimization opportunities: {len(analysis.recommendations)}")
+
+ if analysis.recommendations:
+ print(f"\n๐ก TOP RECOMMENDATIONS:")
+ for rec in analysis.recommendations[:3]:
+ print(f" โข {rec.title}: {rec.estimated_savings_chars:,} chars")
+
+ # Export if requested
+ if args.export:
+ export_data = {
+ 'overview': {
+ 'model': analysis.model,
+ 'total_size_chars': analysis.total_size_chars,
+ 'total_messages': analysis.total_messages
+ },
+ 'scof_analysis': {
+ 'total_files': analysis.scof_analysis.total_files if analysis.scof_analysis else 0,
+ 'files': [{'path': f.path, 'size': f.content_size} for f in analysis.scof_analysis.files] if analysis.scof_analysis else []
+ } if analysis.scof_analysis else None,
+ 'recommendations': [
+ {
+ 'title': r.title,
+ 'savings_chars': r.estimated_savings_chars,
+ 'difficulty': r.implementation_difficulty
+ } for r in analysis.recommendations
+ ]
+ }
+
+ with open(args.export, 'w') as f:
+ json.dump(export_data, f, indent=2)
+ print(f"\n๐พ Analysis exported to: {args.export}")
+
+ print(f"\nโ
Analysis complete!")
+
+ except Exception as e:
+ print(f"โ Analysis failed: {e}")
+ sys.exit(1)
+
+
+if __name__ == '__main__':
+ main()
\ No newline at end of file
diff --git a/external/drpower-vibe-production/debug-tools/conversation_analyzer.py b/external/drpower-vibe-production/debug-tools/conversation_analyzer.py
new file mode 100644
index 00000000..b71ec42f
--- /dev/null
+++ b/external/drpower-vibe-production/debug-tools/conversation_analyzer.py
@@ -0,0 +1,261 @@
+#!/usr/bin/env python3
+"""
+Conversation Messages Analyzer
+
+This script analyzes the conversationMessages property to understand
+why it's so large and provide specific recommendations for cleanup.
+
+Usage: python conversation_analyzer.py
+"""
+
+import json
+import os
+from typing import List, Dict, Any
+from dataclasses import dataclass
+from collections import Counter, defaultdict
+
+@dataclass
+class ConversationAnalysis:
+ total_messages: int
+ total_size: int
+ avg_message_size: int
+ message_types: Dict[str, int]
+ largest_messages: List[Dict[str, Any]]
+ size_by_type: Dict[str, int]
+ recommendations: List[str]
+
+class ConversationAnalyzer:
+ def __init__(self):
+ self.size_thresholds = {
+ 'small': 1000, # 1KB
+ 'medium': 5000, # 5KB
+ 'large': 20000, # 20KB
+ 'huge': 100000 # 100KB
+ }
+
+ def analyze_conversation_messages(self, messages: List[Dict[str, Any]]) -> ConversationAnalysis:
+ """Analyze conversation messages for size and content"""
+ print(f"๐ Analyzing {len(messages)} conversation messages...")
+
+ total_size = 0
+ message_types = Counter()
+ size_by_type = defaultdict(int)
+ largest_messages = []
+
+ for i, msg in enumerate(messages):
+ # Calculate message size
+ msg_size = len(json.dumps(msg, default=str))
+ total_size += msg_size
+
+ # Categorize by type/role
+ msg_type = msg.get('role', msg.get('type', 'unknown'))
+ message_types[msg_type] += 1
+ size_by_type[msg_type] += msg_size
+
+ # Track largest messages
+ msg_info = {
+ 'index': i,
+ 'size': msg_size,
+ 'type': msg_type,
+ 'content_preview': str(msg.get('content', ''))[:100],
+ 'timestamp': msg.get('timestamp', 'unknown')
+ }
+ largest_messages.append(msg_info)
+
+ # Sort by size for analysis
+ largest_messages.sort(key=lambda x: x['size'], reverse=True)
+
+ avg_size = total_size // len(messages) if messages else 0
+
+ # Generate recommendations
+ recommendations = self._generate_recommendations(
+ messages, total_size, avg_size, message_types, largest_messages
+ )
+
+ return ConversationAnalysis(
+ total_messages=len(messages),
+ total_size=total_size,
+ avg_message_size=avg_size,
+ message_types=dict(message_types),
+ largest_messages=largest_messages[:20], # Top 20
+ size_by_type=dict(size_by_type),
+ recommendations=recommendations
+ )
+
+ def _generate_recommendations(self, messages, total_size, avg_size, message_types, largest_messages):
+ recommendations = []
+
+ # Size-based recommendations
+ if total_size > 1000000: # 1MB
+ recommendations.append("๐จ CRITICAL: Conversation messages exceed 1MB - implement immediate cleanup")
+ recommendations.append(" Implement a maximum message history limit (e.g., 50-100 messages)")
+
+ if avg_size > 10000: # 10KB average
+ recommendations.append("โ ๏ธ Average message size is very large - check for data bloat in messages")
+
+ # Check for huge individual messages
+ huge_messages = [m for m in largest_messages if m['size'] > self.size_thresholds['huge']]
+ if huge_messages:
+ recommendations.append(f"๐ Found {len(huge_messages)} messages larger than 100KB each")
+ recommendations.append(" Consider truncating or summarizing very long messages")
+
+ # Type-based recommendations
+ if 'assistant' in message_types and message_types['assistant'] > 100:
+ recommendations.append("๐ High number of assistant messages - consider keeping only recent ones")
+
+ if 'user' in message_types and message_types['user'] > 50:
+ recommendations.append("๐ค High number of user messages - implement conversation pruning")
+
+ # Specific code recommendations
+ total_messages = len(messages)
+ if total_messages > 100:
+ keep_recent = min(50, total_messages // 2)
+ recommendations.append(f"๐ก SUGGESTED FIX: Keep only the {keep_recent} most recent messages")
+ recommendations.append(" Implementation: conversationMessages = conversationMessages.slice(-{keep_recent})")
+
+ return recommendations
+
+ def generate_report(self, analysis: ConversationAnalysis) -> str:
+ """Generate detailed conversation analysis report"""
+ report = ["Conversation Messages Analysis Report", "=" * 50, ""]
+
+ # Summary
+ report.append("๐ CONVERSATION SUMMARY")
+ report.append("-" * 30)
+ report.append(f"Total messages: {analysis.total_messages:,}")
+ report.append(f"Total size: {analysis.total_size:,} characters ({analysis.total_size/1024:.1f} KB)")
+ report.append(f"Average message size: {analysis.avg_message_size:,} characters")
+ report.append("")
+
+ # Message types breakdown
+ report.append("๐ MESSAGE TYPES")
+ report.append("-" * 20)
+ for msg_type, count in sorted(analysis.message_types.items(), key=lambda x: x[1], reverse=True):
+ percentage = (count / analysis.total_messages * 100)
+ avg_size_type = analysis.size_by_type[msg_type] // count if count > 0 else 0
+ report.append(f"{msg_type:>15}: {count:>4} messages ({percentage:4.1f}%) - avg {avg_size_type:,} chars each")
+ report.append("")
+
+ # Largest messages
+ report.append("๐ฏ LARGEST MESSAGES (Top 20)")
+ report.append("-" * 40)
+ report.append(f"{'Index':>5} {'Size':>8} {'Type':>12} {'Preview'}")
+ report.append("-" * 70)
+
+ for msg in analysis.largest_messages[:20]:
+ preview = msg['content_preview'].replace('\n', ' ')[:50]
+ report.append(f"{msg['index']:>5} {msg['size']:>8} {msg['type']:>12} {preview}")
+ report.append("")
+
+ # Size distribution
+ report.append("๐ SIZE DISTRIBUTION")
+ report.append("-" * 25)
+ size_buckets = {
+ 'tiny (<1KB)': 0,
+ 'small (1-5KB)': 0,
+ 'medium (5-20KB)': 0,
+ 'large (20-100KB)': 0,
+ 'huge (>100KB)': 0
+ }
+
+ for msg in analysis.largest_messages:
+ size = msg['size']
+ if size < 1000:
+ size_buckets['tiny (<1KB)'] += 1
+ elif size < 5000:
+ size_buckets['small (1-5KB)'] += 1
+ elif size < 20000:
+ size_buckets['medium (5-20KB)'] += 1
+ elif size < 100000:
+ size_buckets['large (20-100KB)'] += 1
+ else:
+ size_buckets['huge (>100KB)'] += 1
+
+ for bucket, count in size_buckets.items():
+ percentage = (count / analysis.total_messages * 100) if analysis.total_messages > 0 else 0
+ report.append(f"{bucket:>20}: {count:>4} messages ({percentage:4.1f}%)")
+ report.append("")
+
+ # Recommendations
+ report.append("๐ก RECOMMENDATIONS")
+ report.append("-" * 20)
+ for rec in analysis.recommendations:
+ report.append(rec)
+ report.append("")
+
+ # Code suggestions
+ report.append("๐ง CODE IMPLEMENTATION SUGGESTIONS")
+ report.append("-" * 40)
+ if analysis.total_messages > 50:
+ keep_messages = min(50, analysis.total_messages // 2)
+ report.append("// Add this to your setState calls to limit conversation history:")
+ report.append(f"const MAX_CONVERSATION_MESSAGES = {keep_messages};")
+ report.append("if (conversationMessages.length > MAX_CONVERSATION_MESSAGES) {")
+ report.append(" conversationMessages = conversationMessages.slice(-MAX_CONVERSATION_MESSAGES);")
+ report.append("}")
+ report.append("")
+
+ # Calculate potential savings
+ messages_to_remove = analysis.total_messages - keep_messages
+ estimated_savings = (messages_to_remove / analysis.total_messages) * analysis.total_size
+ report.append(f"// Estimated size reduction: ~{estimated_savings:,.0f} characters ({estimated_savings/1024:.1f}KB)")
+
+ return "\n".join(report)
+
+def main():
+ # Check if we have debug files from the main analyzer
+ conversation_file = "debug_output/conversationMessages_new.json"
+
+ if not os.path.exists(conversation_file):
+ print("โ Conversation messages debug file not found!")
+ print(" Please run the main state analyzer first: python state_analyzer.py errorfile.json")
+ print(" This will generate the required debug files in debug_output/")
+ return
+
+ print("๐ Starting conversation messages analysis...")
+ print(f"๐ Reading conversation data from: {conversation_file}")
+
+ try:
+ with open(conversation_file, 'r') as f:
+ messages = json.load(f)
+
+ print(f"๐ Loaded {len(messages)} conversation messages")
+
+ analyzer = ConversationAnalyzer()
+ analysis = analyzer.analyze_conversation_messages(messages)
+
+ # Generate report
+ report = analyzer.generate_report(analysis)
+
+ # Save report
+ report_file = "conversation_analysis_report.txt"
+ with open(report_file, 'w') as f:
+ f.write(report)
+
+ print(f"๐ Conversation analysis saved to: {report_file}")
+
+ # Print key findings
+ print("\n" + "="*60)
+ print("KEY FINDINGS")
+ print("="*60)
+ print(f"Total messages: {analysis.total_messages:,}")
+ print(f"Total size: {analysis.total_size:,} chars ({analysis.total_size/1024:.1f}KB)")
+ print(f"Average per message: {analysis.avg_message_size:,} chars")
+
+ if analysis.largest_messages:
+ largest = analysis.largest_messages[0]
+ print(f"Largest single message: {largest['size']:,} chars ({largest['type']})")
+
+ print(f"\n๐ก Top recommendation:")
+ if analysis.recommendations:
+ print(f" {analysis.recommendations[0]}")
+
+ print(f"\n๐ Full analysis available in: {report_file}")
+
+ except Exception as e:
+ print(f"โ Error during conversation analysis: {e}")
+ import traceback
+ traceback.print_exc()
+
+if __name__ == "__main__":
+ main()
diff --git a/external/drpower-vibe-production/debug-tools/migration_tester.py b/external/drpower-vibe-production/debug-tools/migration_tester.py
new file mode 100644
index 00000000..d9cd585c
--- /dev/null
+++ b/external/drpower-vibe-production/debug-tools/migration_tester.py
@@ -0,0 +1,363 @@
+#!/usr/bin/env python3
+"""
+Migration Algorithm Tester & Enhanced Analytics
+
+This script tests the migration algorithm from the TypeScript code and provides
+comprehensive analytics about conversation messages before and after migration.
+
+Usage: python migration_tester.py
+"""
+
+import json
+import os
+from typing import List, Dict, Any, Tuple
+from dataclasses import dataclass
+from collections import Counter, defaultdict
+
+@dataclass
+class MigrationResult:
+ original_count: int
+ deduplicated_count: int
+ final_count: int
+ duplicates_removed: int
+ old_messages_removed: int
+ unique_conversation_ids: List[str]
+ longest_message: Dict[str, Any]
+ final_messages: List[Dict[str, Any]]
+
+class MigrationTester:
+ """Tests the exact migration algorithm from TypeScript code"""
+
+ def __init__(self):
+ self.MAX_CONVERSATION_MESSAGES = 50
+
+ def extract_timestamp_from_id(self, conversation_id: str) -> int:
+ """Extract timestamp from conversationId (format: conv-{timestamp}-{random})"""
+ if conversation_id and conversation_id.startswith('conv-'):
+ parts = conversation_id.split('-')
+ if len(parts) >= 2:
+ try:
+ return int(parts[1])
+ except ValueError:
+ return 0
+ return 0
+
+ def apply_migration_algorithm(self, messages: List[Dict[str, Any]]) -> MigrationResult:
+ """Apply the exact migration algorithm from TypeScript code"""
+ print(f"๐งช Testing updated migration algorithm on {len(messages)} messages...")
+
+ original_count = len(messages)
+ MIN_MESSAGES_FOR_CLEANUP = 25
+
+ # Deduplicate messages by conversationId
+ seen = set()
+ unique_messages = []
+
+ for message in messages:
+ # Use conversationId as primary unique key since it should be unique per message
+ key = message.get('conversationId')
+ if not key:
+ # Fallback for messages without conversationId
+ content = message.get('content', '')
+ if isinstance(content, str):
+ content_str = content[:100]
+ else:
+ content_str = json.dumps(content, default=str)[:100]
+ key = f"{message.get('role', 'unknown')}_{content_str}_{self.extract_timestamp_from_id('')}"
+
+ if key not in seen:
+ seen.add(key)
+ unique_messages.append(message)
+
+ # Sort messages by timestamp (extracted from conversationId) to maintain chronological order
+ unique_messages.sort(key=lambda msg: self.extract_timestamp_from_id(msg.get('conversationId', '')))
+
+ # Smart filtering: if we have more than MIN_MESSAGES_FOR_CLEANUP, remove internal memos but keep actual conversations
+ final_messages = unique_messages
+ internal_memos_removed = 0
+
+ if len(unique_messages) > MIN_MESSAGES_FOR_CLEANUP:
+ real_conversations = []
+ internal_memos = []
+
+ for message in unique_messages:
+ content = message.get('content', '')
+ if isinstance(content, str):
+ content_str = content
+ else:
+ content_str = json.dumps(content, default=str)
+
+ is_internal_memo = '****' in content_str or 'Project Updates:' in content_str
+
+ if is_internal_memo:
+ internal_memos.append(message)
+ else:
+ real_conversations.append(message)
+
+ print(f" ๐ Smart filtering analysis:")
+ print(f" Real conversations: {len(real_conversations)}")
+ print(f" Internal memos: {len(internal_memos)}")
+ print(f" Will remove internal memos: {len(unique_messages) > MIN_MESSAGES_FOR_CLEANUP}")
+
+ # Keep all real conversations, remove internal memos if we exceed the threshold
+ final_messages = real_conversations
+ internal_memos_removed = len(internal_memos)
+
+ # Find longest message
+ longest_message = max(messages, key=lambda m: len(json.dumps(m, default=str))) if messages else {}
+
+ # Get unique conversation IDs
+ unique_ids = list(set(msg.get('conversationId') for msg in unique_messages if msg.get('conversationId')))
+
+ return MigrationResult(
+ original_count=original_count,
+ deduplicated_count=len(unique_messages),
+ final_count=len(final_messages),
+ duplicates_removed=original_count - len(unique_messages),
+ old_messages_removed=internal_memos_removed,
+ unique_conversation_ids=unique_ids,
+ longest_message=longest_message,
+ final_messages=final_messages
+ )
+
+ def analyze_unique_conversations(self, messages: List[Dict[str, Any]]) -> Dict[str, Any]:
+ """Analyze all unique conversation IDs and their patterns"""
+ print("๐ Analyzing unique conversation patterns...")
+
+ # Group messages by conversationId
+ conversations = defaultdict(list)
+ for msg in messages:
+ conv_id = msg.get('conversationId', 'no-id')
+ conversations[conv_id].append(msg)
+
+ # Analyze each unique conversation
+ conversation_analysis = {}
+ for conv_id, msgs in conversations.items():
+ if conv_id == 'no-id':
+ continue
+
+ timestamps = [self.extract_timestamp_from_id(conv_id)]
+ roles = Counter(msg.get('role', 'unknown') for msg in msgs)
+ total_content_length = sum(len(json.dumps(msg.get('content', ''), default=str)) for msg in msgs)
+
+ # Get sample content
+ sample_content = msgs[0].get('content', '') if msgs else ''
+ if isinstance(sample_content, str):
+ sample_preview = sample_content[:100]
+ else:
+ sample_preview = json.dumps(sample_content, default=str)[:100]
+
+ conversation_analysis[conv_id] = {
+ 'message_count': len(msgs),
+ 'timestamp': timestamps[0],
+ 'roles': dict(roles),
+ 'total_content_length': total_content_length,
+ 'sample_content': sample_preview,
+ 'duplicate_count': len(msgs)
+ }
+
+ return conversation_analysis
+
+ def generate_enhanced_report(self, result: MigrationResult, conversation_analysis: Dict[str, Any]) -> str:
+ """Generate enhanced analysis report with detailed insights"""
+ report = ["Enhanced Migration Test & Analysis Report", "=" * 60, ""]
+
+ # Migration Results
+ report.append("๐งช MIGRATION ALGORITHM TEST RESULTS")
+ report.append("-" * 45)
+ report.append(f"Original message count: {result.original_count:,}")
+ report.append(f"After deduplication: {result.deduplicated_count:,}")
+ report.append(f"Final count (after limit): {result.final_count:,}")
+ report.append(f"Duplicates removed: {result.duplicates_removed:,}")
+ report.append(f"Old messages trimmed: {result.old_messages_removed:,}")
+ report.append(f"Size reduction: {((result.original_count - result.final_count) / result.original_count * 100):.1f}%")
+ report.append("")
+
+ # Longest Message Analysis
+ if result.longest_message:
+ longest_content = result.longest_message.get('content', '')
+ if isinstance(longest_content, str):
+ content_preview = longest_content[:500]
+ else:
+ content_preview = json.dumps(longest_content, default=str)[:500]
+
+ longest_size = len(json.dumps(result.longest_message, default=str))
+ report.append("๐ LONGEST MESSAGE ANALYSIS")
+ report.append("-" * 35)
+ report.append(f"Size: {longest_size:,} characters")
+ report.append(f"Role: {result.longest_message.get('role', 'unknown')}")
+ report.append(f"Conversation ID: {result.longest_message.get('conversationId', 'none')}")
+ report.append(f"Content preview (first 500 chars):")
+ report.append(f" {content_preview}")
+ report.append("")
+
+ # Unique Conversation IDs Analysis
+ report.append("๐ UNIQUE CONVERSATION IDs ANALYSIS")
+ report.append("-" * 40)
+ report.append(f"Total unique conversation IDs: {len(conversation_analysis)}")
+ report.append("")
+
+ # Sort conversations by timestamp for chronological view
+ sorted_conversations = sorted(
+ conversation_analysis.items(),
+ key=lambda x: x[1]['timestamp']
+ )
+
+ report.append("๐ CONVERSATION ID DETAILS (chronological order)")
+ report.append("-" * 55)
+ report.append(f"{'Conversation ID':<30} {'Count':<6} {'Roles':<20} {'Sample Content'}")
+ report.append("-" * 100)
+
+ for conv_id, details in sorted_conversations[:20]: # Show first 20
+ roles_str = ', '.join(f"{role}:{count}" for role, count in details['roles'].items())
+ sample = details['sample_content'][:40]
+ report.append(f"{conv_id:<30} {details['duplicate_count']:<6} {roles_str:<20} {sample}")
+
+ if len(sorted_conversations) > 20:
+ report.append(f"... and {len(sorted_conversations) - 20} more conversation IDs")
+
+ report.append("")
+
+ # Most Duplicated Conversations
+ most_duplicated = sorted(
+ conversation_analysis.items(),
+ key=lambda x: x[1]['duplicate_count'],
+ reverse=True
+ )
+
+ report.append("๐ฏ MOST DUPLICATED CONVERSATIONS")
+ report.append("-" * 35)
+ for conv_id, details in most_duplicated[:10]:
+ report.append(f"{conv_id}: {details['duplicate_count']} duplicates")
+ report.append(f" Sample: {details['sample_content'][:80]}")
+ report.append("")
+
+ # Timeline Analysis
+ timestamps = [details['timestamp'] for details in conversation_analysis.values() if details['timestamp'] > 0]
+ if timestamps:
+ timestamps.sort()
+ duration_ms = timestamps[-1] - timestamps[0] if len(timestamps) > 1 else 0
+ duration_minutes = duration_ms / 1000 / 60
+
+ report.append("โฑ๏ธ TIMELINE ANALYSIS")
+ report.append("-" * 20)
+ report.append(f"Conversation span: {duration_minutes:.1f} minutes")
+ report.append(f"First message: {timestamps[0]} ({self.format_timestamp(timestamps[0])})")
+ report.append(f"Last message: {timestamps[-1]} ({self.format_timestamp(timestamps[-1])})")
+ report.append("")
+
+ return "\n".join(report)
+
+ def format_timestamp(self, timestamp: int) -> str:
+ """Format timestamp for human readability"""
+ import datetime
+ try:
+ dt = datetime.datetime.fromtimestamp(timestamp / 1000)
+ return dt.strftime("%Y-%m-%d %H:%M:%S")
+ except:
+ return "Invalid timestamp"
+
+ def save_final_conversation(self, final_messages: List[Dict[str, Any]]):
+ """Save the final conversation after migration for inspection"""
+ print("๐พ Saving final conversation after migration...")
+
+ os.makedirs("migration_test_output", exist_ok=True)
+
+ # Save full final conversation
+ with open("migration_test_output/final_conversation.json", 'w') as f:
+ json.dump(final_messages, f, indent=2, default=str)
+
+ # Save readable conversation format
+ readable_conversation = []
+ for i, msg in enumerate(final_messages):
+ content = msg.get('content', '')
+ if isinstance(content, str):
+ content_str = content
+ else:
+ content_str = json.dumps(content, default=str)
+
+ readable_conversation.append({
+ 'index': i,
+ 'role': msg.get('role', 'unknown'),
+ 'conversationId': msg.get('conversationId', 'none'),
+ 'timestamp': self.extract_timestamp_from_id(msg.get('conversationId', '')),
+ 'content_length': len(content_str),
+ 'content_preview': content_str[:200]
+ })
+
+ with open("migration_test_output/final_conversation_readable.json", 'w') as f:
+ json.dump(readable_conversation, f, indent=2, default=str)
+
+ print(f" Final conversation saved: {len(final_messages)} messages")
+ print(f" Files: migration_test_output/final_conversation.json")
+ print(f" migration_test_output/final_conversation_readable.json")
+
+def main():
+ # Check if we have the conversation messages
+ conversation_file = "debug_output/conversationMessages_new.json"
+
+ if not os.path.exists(conversation_file):
+ print("โ Conversation messages file not found!")
+ print(" Please run: python state_analyzer.py errorfile.json")
+ print(" This will generate the required debug files.")
+ return
+
+ print("๐ Starting enhanced migration test and analysis...")
+ print(f"๐ Loading conversation data from: {conversation_file}")
+
+ try:
+ with open(conversation_file, 'r') as f:
+ messages = json.load(f)
+
+ print(f"๐ Loaded {len(messages)} conversation messages")
+
+ tester = MigrationTester()
+
+ # Analyze unique conversations before migration
+ conversation_analysis = tester.analyze_unique_conversations(messages)
+
+ # Apply migration algorithm
+ result = tester.apply_migration_algorithm(messages)
+
+ # Generate enhanced report
+ report = tester.generate_enhanced_report(result, conversation_analysis)
+
+ # Save report
+ report_file = "migration_test_report.txt"
+ with open(report_file, 'w') as f:
+ f.write(report)
+
+ # Save final conversation
+ tester.save_final_conversation(result.final_messages)
+
+ print(f"๐ Enhanced analysis saved to: {report_file}")
+
+ # Print key findings to console
+ print("\n" + "="*70)
+ print("๐ฏ KEY MIGRATION TEST RESULTS")
+ print("="*70)
+ print(f"โ
Original messages: {result.original_count:,}")
+ print(f"โ
After deduplication: {result.deduplicated_count:,}")
+ print(f"โ
Final count: {result.final_count:,}")
+ print(f"๐๏ธ Duplicates removed: {result.duplicates_removed:,}")
+ print(f"๐๏ธ Old messages trimmed: {result.old_messages_removed:,}")
+
+ size_reduction = ((result.original_count - result.final_count) / result.original_count * 100)
+ print(f"๐ Size reduction: {size_reduction:.1f}%")
+
+ if result.longest_message:
+ longest_size = len(json.dumps(result.longest_message, default=str))
+ print(f"๐ Longest message: {longest_size:,} chars ({result.longest_message.get('role', 'unknown')})")
+
+ print(f"๐ Unique conversation IDs: {len(conversation_analysis)}")
+
+ print(f"\n๐ Full analysis: {report_file}")
+ print(f"๐ Final conversation: migration_test_output/")
+
+ except Exception as e:
+ print(f"โ Error during migration test: {e}")
+ import traceback
+ traceback.print_exc()
+
+if __name__ == "__main__":
+ main()
diff --git a/external/drpower-vibe-production/debug-tools/state_analyzer.py b/external/drpower-vibe-production/debug-tools/state_analyzer.py
new file mode 100644
index 00000000..4e1bb6d9
--- /dev/null
+++ b/external/drpower-vibe-production/debug-tools/state_analyzer.py
@@ -0,0 +1,539 @@
+#!/usr/bin/env python3
+"""
+State Analyzer for SimpleGeneratorAgent setState debugging
+
+This script parses error messages from setState failures and analyzes:
+1. Size of each state property when serialized
+2. Differences between old and new states
+3. Main contributors to state growth
+4. Detailed breakdown for debugging SQL storage issues
+
+Usage: python state_analyzer.py
+"""
+
+import json
+import sys
+import re
+import os
+from typing import Dict, Any, List, Tuple, Union
+from dataclasses import dataclass
+from collections import defaultdict
+import difflib
+
+
+@dataclass
+class PropertyAnalysis:
+ """Analysis results for a single property"""
+ name: str
+ old_size: int
+ new_size: int
+ old_serialized_length: int
+ new_serialized_length: int
+ growth_bytes: int
+ growth_chars: int
+ has_changed: bool
+ old_type: str
+ new_type: str
+
+
+@dataclass
+class StateAnalysis:
+ """Complete analysis of state comparison"""
+ total_old_size: int
+ total_new_size: int
+ total_old_serialized_length: int
+ total_new_serialized_length: int
+ total_growth_bytes: int
+ total_growth_chars: int
+ property_analyses: List[PropertyAnalysis]
+ top_contributors: List[PropertyAnalysis]
+ new_properties: List[str]
+ removed_properties: List[str]
+ changed_properties: List[str]
+
+
+class StateAnalyzer:
+ """Main analyzer class for state debugging"""
+
+ def __init__(self):
+ self.known_large_properties = {
+ 'generatedFilesMap', 'templateDetails', 'conversationMessages',
+ 'generatedPhases', 'blueprint', 'commandsHistory'
+ }
+
+ def extract_states_from_error(self, error_content: str) -> Tuple[Dict[str, Any], Dict[str, Any]]:
+ """Extract original and new states from WebSocket error message"""
+ print("๐ Extracting states from WebSocket error message...")
+
+ # First, try to parse as WebSocket JSON message
+ websocket_message = None
+ try:
+ websocket_message = json.loads(error_content)
+ print("โ
Successfully parsed WebSocket message")
+ except json.JSONDecodeError:
+ print("โ ๏ธ Not a JSON WebSocket message, trying as plain text...")
+
+ # Extract the error text
+ if websocket_message and isinstance(websocket_message, dict):
+ # Handle WebSocket message format: {"type": "error", "error": "..."}
+ if 'error' in websocket_message:
+ error_text = websocket_message['error']
+ print(f"๐ Extracted error text from WebSocket message: {len(error_text):,} chars")
+ else:
+ # Maybe the whole message is the error text
+ error_text = str(websocket_message)
+ print(f"๐ Using whole WebSocket message as error text: {len(error_text):,} chars")
+ else:
+ # Use the raw content as error text
+ error_text = error_content
+ print(f"๐ Using raw content as error text: {len(error_text):,} chars")
+
+ # Now extract states from the error text
+ # Error format: "Error setting state: ; Original state: ; New state: "
+
+ # Find the original state JSON - more robust pattern matching
+ original_match = re.search(r'Original state:\s*(\{.*?);\s*New state:', error_text, re.DOTALL)
+ if not original_match:
+ # Try without the semicolon requirement
+ original_match = re.search(r'Original state:\s*(\{.*?)(?=New state:)', error_text, re.DOTALL)
+
+ # Find the new state JSON - match to end or to next semicolon
+ new_match = re.search(r'New state:\s*(\{.*?)(?:$|\s*$)', error_text, re.DOTALL)
+ if not new_match:
+ # Try more flexible pattern
+ new_match = re.search(r'New state:\s*(\{.*)', error_text, re.DOTALL)
+
+ if not original_match or not new_match:
+ # Print some debug info to help diagnose
+ print("โ Could not find state patterns in error text")
+ print(f"๐ Error text sample (first 500 chars):")
+ print(error_text[:500])
+ print(f"๐ Error text sample (last 500 chars):")
+ print(error_text[-500:])
+
+ # Look for any occurrence of "Original state:" and "New state:"
+ orig_pos = error_text.find("Original state:")
+ new_pos = error_text.find("New state:")
+ print(f"๐ 'Original state:' found at position: {orig_pos}")
+ print(f"๐ 'New state:' found at position: {new_pos}")
+
+ if orig_pos >= 0:
+ print(f"๐ Context around 'Original state:': {error_text[max(0, orig_pos-50):orig_pos+100]}")
+ if new_pos >= 0:
+ print(f"๐ Context around 'New state:': {error_text[max(0, new_pos-50):new_pos+100]}")
+
+ raise ValueError("Could not extract state objects from error message. Expected format: 'Original state: {...}; New state: {...}'")
+
+ original_json = original_match.group(1).strip()
+ new_json = new_match.group(1).strip()
+
+ print(f"๐ Extracted original state JSON length: {len(original_json):,} chars")
+ print(f"๐ Extracted new state JSON length: {len(new_json):,} chars")
+
+ # Clean up the JSON strings - remove any trailing content that's not part of JSON
+ original_json = self.clean_json_string(original_json)
+ new_json = self.clean_json_string(new_json)
+
+ try:
+ original_state = json.loads(original_json)
+ print("โ
Successfully parsed original state")
+ except json.JSONDecodeError as e:
+ print(f"โ Error parsing original state JSON: {e}")
+ print(f"๐ First 200 chars: {original_json[:200]}...")
+ print(f"๐ Last 200 chars: ...{original_json[-200:]}")
+ raise
+
+ try:
+ new_state = json.loads(new_json)
+ print("โ
Successfully parsed new state")
+ except json.JSONDecodeError as e:
+ print(f"โ Error parsing new state JSON: {e}")
+ print(f"๐ First 200 chars: {new_json[:200]}...")
+ print(f"๐ Last 200 chars: ...{new_json[-200:]}")
+ raise
+
+ return original_state, new_state
+
+ def clean_json_string(self, json_str: str) -> str:
+ """Clean up JSON string by removing trailing non-JSON content"""
+ # Find the last closing brace that matches the first opening brace
+ brace_count = 0
+ last_valid_pos = -1
+
+ for i, char in enumerate(json_str):
+ if char == '{':
+ brace_count += 1
+ elif char == '}':
+ brace_count -= 1
+ if brace_count == 0:
+ last_valid_pos = i + 1
+ break
+
+ if last_valid_pos > 0:
+ cleaned = json_str[:last_valid_pos]
+ if len(cleaned) != len(json_str):
+ print(f"๐งน Cleaned JSON string: {len(json_str)} โ {len(cleaned)} chars")
+ return cleaned
+
+ return json_str
+
+ def get_object_size_estimate(self, obj: Any) -> int:
+ """Estimate memory size of an object in bytes"""
+ if obj is None:
+ return 0
+ elif isinstance(obj, bool):
+ return 1
+ elif isinstance(obj, int):
+ return 8
+ elif isinstance(obj, float):
+ return 8
+ elif isinstance(obj, str):
+ return len(obj.encode('utf-8'))
+ elif isinstance(obj, (list, tuple)):
+ return sum(self.get_object_size_estimate(item) for item in obj) + 8
+ elif isinstance(obj, dict):
+ return sum(
+ self.get_object_size_estimate(k) + self.get_object_size_estimate(v)
+ for k, v in obj.items()
+ ) + 8
+ else:
+ # For other types, estimate based on string representation
+ return len(str(obj).encode('utf-8'))
+
+ def get_serialized_length(self, obj: Any) -> int:
+ """Get the length of JSON-serialized object"""
+ try:
+ return len(json.dumps(obj, default=str))
+ except Exception:
+ return len(str(obj))
+
+ def get_type_description(self, obj: Any) -> str:
+ """Get detailed type description of object"""
+ if obj is None:
+ return "null"
+ elif isinstance(obj, bool):
+ return "boolean"
+ elif isinstance(obj, int):
+ return "integer"
+ elif isinstance(obj, float):
+ return "float"
+ elif isinstance(obj, str):
+ return f"string({len(obj)})"
+ elif isinstance(obj, list):
+ return f"array({len(obj)})"
+ elif isinstance(obj, dict):
+ return f"object({len(obj)} keys)"
+ else:
+ return str(type(obj).__name__)
+
+ def analyze_property(self, prop_name: str, old_value: Any, new_value: Any) -> PropertyAnalysis:
+ """Analyze a single property comparison"""
+ old_size = self.get_object_size_estimate(old_value)
+ new_size = self.get_object_size_estimate(new_value)
+ old_serialized = self.get_serialized_length(old_value)
+ new_serialized = self.get_serialized_length(new_value)
+
+ has_changed = old_value != new_value
+
+ return PropertyAnalysis(
+ name=prop_name,
+ old_size=old_size,
+ new_size=new_size,
+ old_serialized_length=old_serialized,
+ new_serialized_length=new_serialized,
+ growth_bytes=new_size - old_size,
+ growth_chars=new_serialized - old_serialized,
+ has_changed=has_changed,
+ old_type=self.get_type_description(old_value),
+ new_type=self.get_type_description(new_value)
+ )
+
+ def analyze_states(self, original_state: Dict[str, Any], new_state: Dict[str, Any]) -> StateAnalysis:
+ """Perform comprehensive analysis of state comparison"""
+ print("\n๐ฌ Analyzing state differences...")
+
+ # Get all properties from both states
+ all_props = set(original_state.keys()) | set(new_state.keys())
+ property_analyses = []
+
+ # Analyze each property
+ for prop_name in all_props:
+ old_value = original_state.get(prop_name)
+ new_value = new_state.get(prop_name)
+
+ analysis = self.analyze_property(prop_name, old_value, new_value)
+ property_analyses.append(analysis)
+
+ # Calculate totals
+ total_old_size = sum(p.old_size for p in property_analyses)
+ total_new_size = sum(p.new_size for p in property_analyses)
+ total_old_serialized = sum(p.old_serialized_length for p in property_analyses)
+ total_new_serialized = sum(p.new_serialized_length for p in property_analyses)
+
+ # Find top contributors (by serialized length growth)
+ top_contributors = sorted(
+ [p for p in property_analyses if p.growth_chars > 0],
+ key=lambda p: p.growth_chars,
+ reverse=True
+ )[:10]
+
+ # Categorize changes
+ new_properties = [p.name for p in property_analyses if p.name not in original_state]
+ removed_properties = [p.name for p in property_analyses if p.name not in new_state]
+ changed_properties = [p.name for p in property_analyses if p.has_changed and p.name in original_state and p.name in new_state]
+
+ return StateAnalysis(
+ total_old_size=total_old_size,
+ total_new_size=total_new_size,
+ total_old_serialized_length=total_old_serialized,
+ total_new_serialized_length=total_new_serialized,
+ total_growth_bytes=total_new_size - total_old_size,
+ total_growth_chars=total_new_serialized - total_old_serialized,
+ property_analyses=property_analyses,
+ top_contributors=top_contributors,
+ new_properties=new_properties,
+ removed_properties=removed_properties,
+ changed_properties=changed_properties
+ )
+
+ def analyze_specific_property(self, prop_name: str, old_value: Any, new_value: Any) -> str:
+ """Detailed analysis of a specific property that changed significantly"""
+ report = [f"\n๐ Detailed Analysis of '{prop_name}'"]
+ report.append("=" * 60)
+
+ if isinstance(old_value, dict) and isinstance(new_value, dict):
+ # Analyze dictionary changes
+ old_keys = set(old_value.keys())
+ new_keys = set(new_value.keys())
+
+ added_keys = new_keys - old_keys
+ removed_keys = old_keys - new_keys
+ common_keys = old_keys & new_keys
+
+ if added_keys:
+ report.append(f"โ Added keys ({len(added_keys)}): {list(added_keys)[:10]}...")
+ for key in list(added_keys)[:5]:
+ size = self.get_serialized_length(new_value[key])
+ report.append(f" - {key}: {size:,} chars")
+
+ if removed_keys:
+ report.append(f"โ Removed keys ({len(removed_keys)}): {list(removed_keys)[:10]}...")
+
+ if common_keys:
+ changed_common = []
+ for key in common_keys:
+ if old_value[key] != new_value[key]:
+ old_size = self.get_serialized_length(old_value[key])
+ new_size = self.get_serialized_length(new_value[key])
+ growth = new_size - old_size
+ changed_common.append((key, growth, new_size))
+
+ if changed_common:
+ changed_common.sort(key=lambda x: x[1], reverse=True)
+ report.append(f"๐ Changed existing keys (top 10):")
+ for key, growth, new_size in changed_common[:10]:
+ report.append(f" - {key}: +{growth:,} chars (total: {new_size:,})")
+
+ elif isinstance(old_value, list) and isinstance(new_value, list):
+ # Analyze list changes
+ report.append(f"๐ List size change: {len(old_value)} โ {len(new_value)} items")
+ if len(new_value) > len(old_value):
+ added_items = len(new_value) - len(old_value)
+ if added_items > 0 and len(new_value) > 0:
+ avg_item_size = self.get_serialized_length(new_value[-1]) if new_value else 0
+ report.append(f" Average size of new items: ~{avg_item_size:,} chars")
+
+ return "\n".join(report)
+
+ def generate_report(self, analysis: StateAnalysis, original_state: Dict[str, Any], new_state: Dict[str, Any]) -> str:
+ """Generate comprehensive analysis report"""
+ report = ["State Size Analysis Report", "=" * 50, ""]
+
+ # Executive Summary
+ report.append("๐ EXECUTIVE SUMMARY")
+ report.append("-" * 30)
+ report.append(f"Total serialized size growth: {analysis.total_growth_chars:,} characters")
+ report.append(f"Memory size growth estimate: {analysis.total_growth_bytes:,} bytes")
+ report.append(f"Growth percentage: {(analysis.total_growth_chars / analysis.total_old_serialized_length * 100):.1f}%")
+ report.append(f"New total serialized size: {analysis.total_new_serialized_length:,} characters")
+ report.append("")
+
+ # Quick Stats
+ report.append("๐ QUICK STATS")
+ report.append("-" * 20)
+ report.append(f"Properties changed: {len(analysis.changed_properties)}")
+ report.append(f"Properties added: {len(analysis.new_properties)}")
+ report.append(f"Properties removed: {len(analysis.removed_properties)}")
+ report.append("")
+
+ # Top Contributors
+ report.append("๐ฏ TOP CONTRIBUTORS TO SIZE GROWTH")
+ report.append("-" * 40)
+ for i, prop in enumerate(analysis.top_contributors, 1):
+ percentage = (prop.growth_chars / analysis.total_growth_chars * 100) if analysis.total_growth_chars > 0 else 0
+ report.append(f"{i:2d}. {prop.name}")
+ report.append(f" Growth: +{prop.growth_chars:,} chars ({percentage:.1f}%)")
+ report.append(f" New size: {prop.new_serialized_length:,} chars")
+ report.append(f" Type: {prop.old_type} โ {prop.new_type}")
+ report.append("")
+
+ # All Properties Summary
+ report.append("๐ ALL PROPERTIES SUMMARY")
+ report.append("-" * 30)
+ report.append(f"{'Property':<25} {'Old Size':<12} {'New Size':<12} {'Growth':<12} {'Changed':<8}")
+ report.append("-" * 75)
+
+ # Sort by new size (largest first)
+ sorted_props = sorted(analysis.property_analyses, key=lambda p: p.new_serialized_length, reverse=True)
+
+ for prop in sorted_props:
+ growth_str = f"+{prop.growth_chars:,}" if prop.growth_chars > 0 else str(prop.growth_chars)
+ changed_str = "Yes" if prop.has_changed else "No"
+ report.append(f"{prop.name[:24]:<25} {prop.old_serialized_length:>11,} {prop.new_serialized_length:>11,} {growth_str:>11} {changed_str:<8}")
+
+ report.append("")
+
+ # Detailed Analysis for Top Contributors
+ report.append("๐ฌ DETAILED ANALYSIS OF TOP CONTRIBUTORS")
+ report.append("-" * 50)
+
+ for prop in analysis.top_contributors[:5]: # Top 5 only
+ if prop.growth_chars > 1000: # Only for significant growth
+ old_value = original_state.get(prop.name)
+ new_value = new_state.get(prop.name)
+ detailed = self.analyze_specific_property(prop.name, old_value, new_value)
+ report.append(detailed)
+ report.append("")
+
+ # Recommendations
+ report.append("๐ก RECOMMENDATIONS")
+ report.append("-" * 20)
+
+ largest_prop = analysis.top_contributors[0] if analysis.top_contributors else None
+ if largest_prop and largest_prop.growth_chars > 10000:
+ report.append(f"๐จ CRITICAL: '{largest_prop.name}' grew by {largest_prop.growth_chars:,} chars")
+ report.append(" Consider implementing size limits or cleanup mechanisms")
+ report.append("")
+
+ if analysis.total_new_serialized_length > 1000000: # 1MB
+ report.append("โ ๏ธ WARNING: Total state size exceeds 1MB when serialized")
+ report.append(" This may cause database storage issues")
+ report.append("")
+
+ # Check for known problematic patterns
+ for prop in analysis.property_analyses:
+ if prop.name in self.known_large_properties and prop.new_serialized_length > 100000:
+ report.append(f"๐ '{prop.name}' is unusually large ({prop.new_serialized_length:,} chars)")
+ if prop.name == 'generatedFilesMap':
+ report.append(" Consider implementing file content compression or chunking")
+ elif prop.name == 'conversationMessages':
+ report.append(" Consider implementing message history limits")
+ elif prop.name == 'commandsHistory':
+ report.append(" Consider cleaning up old command history")
+ report.append("")
+
+ return "\n".join(report)
+
+ def save_debug_files(self, original_state: Dict[str, Any], new_state: Dict[str, Any], analysis: StateAnalysis):
+ """Save debug files for further analysis"""
+ print("๐พ Saving debug files...")
+
+ # Save individual property files for large contributors
+ os.makedirs("debug_output", exist_ok=True)
+
+ for prop in analysis.top_contributors[:3]:
+ if prop.growth_chars > 1000:
+ prop_name_safe = re.sub(r'[^\w\-_]', '_', prop.name)
+
+ # Save old value
+ old_value = original_state.get(prop.name)
+ if old_value:
+ with open(f"debug_output/{prop_name_safe}_old.json", 'w') as f:
+ json.dump(old_value, f, indent=2, default=str)
+
+ # Save new value
+ new_value = new_state.get(prop.name)
+ if new_value:
+ with open(f"debug_output/{prop_name_safe}_new.json", 'w') as f:
+ json.dump(new_value, f, indent=2, default=str)
+
+ print(f" Saved {prop.name} debug files")
+
+ # Save full states
+ with open("debug_output/original_state_full.json", 'w') as f:
+ json.dump(original_state, f, indent=2, default=str)
+
+ with open("debug_output/new_state_full.json", 'w') as f:
+ json.dump(new_state, f, indent=2, default=str)
+
+ print("โ
Debug files saved to debug_output/ directory")
+
+
+def main():
+ if len(sys.argv) != 2:
+ print("Usage: python state_analyzer.py ")
+ print("\nThis script analyzes setState error dumps to identify size issues.")
+ print("The error file should contain the WebSocket error message with original and new states.")
+ sys.exit(1)
+
+ error_file_path = sys.argv[1]
+
+ if not os.path.exists(error_file_path):
+ print(f"โ Error file not found: {error_file_path}")
+ sys.exit(1)
+
+ print(f"๐ Starting state analysis of: {error_file_path}")
+ print(f"๐ File size: {os.path.getsize(error_file_path):,} bytes")
+ print()
+
+ analyzer = StateAnalyzer()
+
+ try:
+ # Read the error file
+ with open(error_file_path, 'r', encoding='utf-8') as f:
+ error_content = f.read()
+
+ print(f"๐ Read {len(error_content):,} characters from error file")
+
+ # Extract states
+ original_state, new_state = analyzer.extract_states_from_error(error_content)
+
+ # Perform analysis
+ analysis = analyzer.analyze_states(original_state, new_state)
+
+ # Generate report
+ report = analyzer.generate_report(analysis, original_state, new_state)
+
+ # Save report
+ report_file = error_file_path.replace('.json', '_analysis.txt').replace('.txt', '_analysis.txt')
+ with open(report_file, 'w') as f:
+ f.write(report)
+
+ print(f"๐ Analysis report saved to: {report_file}")
+
+ # Save debug files
+ analyzer.save_debug_files(original_state, new_state, analysis)
+
+ # Print summary to console
+ print("\n" + "="*60)
+ print("ANALYSIS SUMMARY")
+ print("="*60)
+ print(f"Total growth: {analysis.total_growth_chars:,} characters")
+ print(f"Largest contributor: {analysis.top_contributors[0].name if analysis.top_contributors else 'None'}")
+ if analysis.top_contributors:
+ top = analysis.top_contributors[0]
+ percentage = (top.growth_chars / analysis.total_growth_chars * 100) if analysis.total_growth_chars > 0 else 0
+ print(f" Growth: +{top.growth_chars:,} chars ({percentage:.1f}%)")
+
+ print(f"\n๐ Full report available in: {report_file}")
+ print("๐ Debug files available in: debug_output/")
+
+ except Exception as e:
+ print(f"โ Error during analysis: {e}")
+ import traceback
+ traceback.print_exc()
+ sys.exit(1)
+
+
+if __name__ == "__main__":
+ main()
diff --git a/external/drpower-vibe-production/debug-tools/test-ai-gateway-analytics.ts b/external/drpower-vibe-production/debug-tools/test-ai-gateway-analytics.ts
new file mode 100755
index 00000000..9edc3093
--- /dev/null
+++ b/external/drpower-vibe-production/debug-tools/test-ai-gateway-analytics.ts
@@ -0,0 +1,1116 @@
+#!/usr/bin/env bun
+
+/**
+ * Cloudflare AI Gateway Analytics Testing Script
+ *
+ * Tests the Cloudflare AI Gateway GraphQL Analytics API and displays comprehensive
+ * analytics data including costs, tokens, requests, errors, and response times.
+ *
+ * Usage:
+ * bun --env-file .dev.vars scripts/test-ai-gateway-analytics.ts
+ * bun --env-file .dev.vars scripts/test-ai-gateway-analytics.ts --user-id abc123
+ * bun --env-file .dev.vars scripts/test-ai-gateway-analytics.ts --chat-id xyz789
+ * bun --env-file .dev.vars scripts/test-ai-gateway-analytics.ts --days 7
+ * bun --env-file .dev.vars scripts/test-ai-gateway-analytics.ts --show-models --show-providers
+ * bun --env-file .dev.vars scripts/test-ai-gateway-analytics.ts --granularity minute --top-models 5
+ */
+
+// Types
+interface AnalyticsResponse {
+ data: {
+ viewer: {
+ scope: Array<{
+ totalRequests: Array<{
+ count: number;
+ sum: {
+ cost: number;
+ cachedRequests: number;
+ erroredRequests: number;
+ uncachedTokensIn: number;
+ uncachedTokensOut: number;
+ cachedTokensIn: number;
+ cachedTokensOut: number;
+ };
+ }>;
+ lastRequest: Array<{
+ dimensions: {
+ ts: string;
+ };
+ }>;
+ latestRequests: Array<{
+ count: number;
+ dimensions: {
+ ts: string;
+ };
+ }>;
+ }>;
+ };
+ };
+ errors?: any[];
+}
+
+// Enhanced types for provider and model analytics
+interface ProviderRequestAnalytics {
+ data: {
+ viewer: {
+ accounts: Array<{
+ data: Array<{
+ count: number;
+ dimensions: {
+ ts: string;
+ provider: string;
+ };
+ }>;
+ }>;
+ };
+ };
+ errors?: any[];
+}
+
+interface ModelTokenAnalytics {
+ data: {
+ viewer: {
+ accounts: Array<{
+ data: Array<{
+ count: number;
+ sum: {
+ uncachedTokensIn: number;
+ uncachedTokensOut: number;
+ cost: number;
+ };
+ dimensions: {
+ ts: string;
+ provider: string;
+ model: string;
+ };
+ }>;
+ }>;
+ };
+ };
+ errors?: any[];
+}
+
+// Provider summary data structure
+interface ProviderSummary {
+ name: string;
+ totalRequests: number;
+ totalTokensIn: number;
+ totalTokensOut: number;
+ totalCost: number;
+ models: ModelSummary[];
+}
+
+// Model summary data structure
+interface ModelSummary {
+ name: string;
+ provider: string;
+ requests: number;
+ tokensIn: number;
+ tokensOut: number;
+ cost: number;
+ firstSeen: string;
+ lastSeen: string;
+}
+
+// Enhanced query result types
+interface QueryResult {
+ name: string;
+ responseTime: number;
+ data: AnalyticsResponse | ProviderRequestAnalytics | ModelTokenAnalytics | null;
+ error?: string;
+}
+
+// Configuration (will be updated from env vars)
+let CONFIG = {
+ ACCOUNT_TAG: '',
+ GATEWAY: '',
+ GRAPHQL_ENDPOINT: 'https://api.cloudflare.com/client/v4/graphql',
+ IS_STAGING: false,
+ API_TOKEN: '',
+};
+
+// Parse AI Gateway URL to extract account ID and gateway name
+function parseGatewayUrl(url: string): { accountId: string; gateway: string; isStaging: boolean } {
+ try {
+ const parsedUrl = new URL(url);
+ const isStaging = url.includes('staging');
+
+ // URL format: https://staging.gateway.ai.cfdata.org/v1/{account_id}/{gateway_name}
+ const pathParts = parsedUrl.pathname.split('/').filter(part => part);
+
+ if (pathParts.length >= 3 && pathParts[0] === 'v1') {
+ return {
+ accountId: pathParts[1],
+ gateway: pathParts[2],
+ isStaging
+ };
+ }
+
+ throw new Error('Invalid gateway URL format');
+ } catch (error) {
+ throw new Error(`Failed to parse gateway URL: ${error}`);
+ }
+}
+
+// Initialize configuration from environment variables
+function initializeConfig(): void {
+ const {
+ CLOUDFLARE_ACCOUNT_ID,
+ CLOUDFLARE_AI_GATEWAY,
+ CLOUDFLARE_API_TOKEN,
+ CLOUDFLARE_AI_GATEWAY_TOKEN,
+ CLOUDFLARE_AI_GATEWAY_URL
+ } = process.env;
+
+ // If CLOUDFLARE_AI_GATEWAY_URL is set, parse it and use staging settings
+ if (CLOUDFLARE_AI_GATEWAY_URL) {
+ const { accountId, gateway, isStaging } = parseGatewayUrl(CLOUDFLARE_AI_GATEWAY_URL);
+
+ CONFIG.ACCOUNT_TAG = accountId;
+ CONFIG.GATEWAY = gateway;
+ CONFIG.IS_STAGING = isStaging;
+
+ if (isStaging) {
+ CONFIG.GRAPHQL_ENDPOINT = 'https://api.staging.cloudflare.com/client/v4/graphql';
+ CONFIG.API_TOKEN = CLOUDFLARE_AI_GATEWAY_TOKEN || '';
+ } else {
+ CONFIG.API_TOKEN = CLOUDFLARE_API_TOKEN || '';
+ }
+ } else {
+ // Use direct environment variables
+ CONFIG.ACCOUNT_TAG = CLOUDFLARE_ACCOUNT_ID || '';
+ CONFIG.GATEWAY = CLOUDFLARE_AI_GATEWAY || '';
+ CONFIG.API_TOKEN = CLOUDFLARE_API_TOKEN || '';
+ }
+
+ // Validate required configuration
+ if (!CONFIG.ACCOUNT_TAG || !CONFIG.GATEWAY || !CONFIG.API_TOKEN) {
+ console.error('โ Missing required environment variables:');
+ if (!CONFIG.ACCOUNT_TAG) console.error(' - Account ID not found');
+ if (!CONFIG.GATEWAY) console.error(' - Gateway name not found');
+ if (!CONFIG.API_TOKEN) console.error(' - API token not found');
+ process.exit(1);
+ }
+}
+
+// GraphQL Queries
+const QUERIES = {
+ totalGateway: (start: string, end: string) => ({
+ operationName: null,
+ variables: {
+ accountTag: CONFIG.ACCOUNT_TAG,
+ gateway: CONFIG.GATEWAY,
+ start,
+ end,
+ limit: 1
+ },
+ query: `{
+ viewer {
+ scope: accounts(filter: {accountTag: $accountTag}) {
+ latestRequests: aiGatewayRequestsAdaptiveGroups(limit: $limit, filter: {gateway: $gateway, datetimeHour_geq: $start, datetimeHour_leq: $end}) {
+ count
+ dimensions {
+ ts: datetimeHour
+ __typename
+ }
+ __typename
+ }
+ lastRequest: aiGatewayRequestsAdaptiveGroups(limit: 1, orderBy: [datetimeMinute_DESC], filter: {gateway: $gateway, datetimeHour_geq: $start, datetimeHour_leq: $end}) {
+ dimensions {
+ ts: datetimeMinute
+ __typename
+ }
+ __typename
+ }
+ totalRequests: aiGatewayRequestsAdaptiveGroups(limit: $limit, filter: {gateway: $gateway, datetimeHour_geq: $start, datetimeHour_leq: $end}) {
+ count
+ sum {
+ cost
+ cachedRequests
+ erroredRequests
+ uncachedTokensIn
+ uncachedTokensOut
+ cachedTokensIn
+ cachedTokensOut
+ __typename
+ }
+ __typename
+ }
+ __typename
+ }
+ __typename
+ }
+ }`
+ }),
+
+ // Provider-level request analytics with minute-level precision
+ providerRequests: (start: string, end: string, granularity: 'minute' | 'hour' = 'minute') => ({
+ operationName: 'GetAIRequests',
+ variables: {
+ accountTag: CONFIG.ACCOUNT_TAG,
+ gateway: CONFIG.GATEWAY,
+ start,
+ end,
+ limit: 10000,
+ orderBy: granularity === 'minute' ? 'datetimeMinute_ASC' : 'datetimeHour_ASC'
+ },
+ query: `query GetAIRequests($accountTag: string, $gateway: string, $start: string, $end: string, $limit: Int, $orderBy: [String!]) {
+ viewer {
+ accounts(filter: {accountTag: $accountTag}) {
+ data: aiGatewayRequestsAdaptiveGroups(
+ filter: {
+ gateway: $gateway,
+ metadataKeys_has: "userId",
+ error: 0,
+ ${granularity === 'minute' ? 'datetimeMinute_geq: $start, datetimeMinute_leq: $end' : 'datetimeHour_geq: $start, datetimeHour_leq: $end'}
+ },
+ orderBy: [$orderBy],
+ limit: $limit
+ ) {
+ count
+ dimensions {
+ ts: ${granularity === 'minute' ? 'datetimeMinute' : 'datetimeHour'}
+ provider
+ __typename
+ }
+ __typename
+ }
+ __typename
+ }
+ __typename
+ }
+ }`
+ }),
+
+ // Model-level token analytics with minute-level precision
+ modelTokens: (start: string, end: string, granularity: 'minute' | 'hour' = 'minute') => ({
+ operationName: 'GetAITokens',
+ variables: {
+ accountTag: CONFIG.ACCOUNT_TAG,
+ gateway: CONFIG.GATEWAY,
+ start,
+ end,
+ limit: 10000,
+ orderBy: granularity === 'minute' ? 'datetimeMinute_ASC' : 'datetimeHour_ASC'
+ },
+ query: `query GetAITokens($accountTag: string, $gateway: string, $start: string, $end: string, $limit: Int, $orderBy: [String!]) {
+ viewer {
+ accounts(filter: {accountTag: $accountTag}) {
+ data: aiGatewayRequestsAdaptiveGroups(
+ filter: {
+ gateway: $gateway,
+ metadataKeys_has: "userId",
+ error: 0,
+ ${granularity === 'minute' ? 'datetimeMinute_geq: $start, datetimeMinute_leq: $end' : 'datetimeHour_geq: $start, datetimeHour_leq: $end'}
+ },
+ orderBy: [$orderBy],
+ limit: $limit
+ ) {
+ count
+ sum {
+ uncachedTokensIn
+ uncachedTokensOut
+ cost
+ __typename
+ }
+ dimensions {
+ ts: ${granularity === 'minute' ? 'datetimeMinute' : 'datetimeHour'}
+ provider
+ model
+ __typename
+ }
+ __typename
+ }
+ __typename
+ }
+ __typename
+ }
+ }`
+ }),
+
+ // Chat-specific provider request analytics
+ chatProviderRequests: (start: string, end: string, chatId: string, granularity: 'minute' | 'hour' = 'minute') => ({
+ operationName: 'GetChatAIRequests',
+ variables: {
+ accountTag: CONFIG.ACCOUNT_TAG,
+ gateway: CONFIG.GATEWAY,
+ start,
+ end,
+ limit: 10000,
+ orderBy: granularity === 'minute' ? 'datetimeMinute_ASC' : 'datetimeHour_ASC'
+ },
+ query: `query GetChatAIRequests($accountTag: string, $gateway: string, $start: string, $end: string, $limit: Int, $orderBy: [String!]) {
+ viewer {
+ accounts(filter: {accountTag: $accountTag}) {
+ data: aiGatewayRequestsAdaptiveGroups(
+ filter: {
+ gateway: $gateway,
+ metadataValues_has: "${chatId}",
+ error: 0,
+ ${granularity === 'minute' ? 'datetimeMinute_geq: $start, datetimeMinute_leq: $end' : 'datetimeHour_geq: $start, datetimeHour_leq: $end'}
+ },
+ orderBy: [$orderBy],
+ limit: $limit
+ ) {
+ count
+ dimensions {
+ ts: ${granularity === 'minute' ? 'datetimeMinute' : 'datetimeHour'}
+ provider
+ __typename
+ }
+ __typename
+ }
+ __typename
+ }
+ __typename
+ }
+ }`
+ }),
+
+ // Chat-specific model token analytics
+ chatModelTokens: (start: string, end: string, chatId: string, granularity: 'minute' | 'hour' = 'minute') => ({
+ operationName: 'GetChatAITokens',
+ variables: {
+ accountTag: CONFIG.ACCOUNT_TAG,
+ gateway: CONFIG.GATEWAY,
+ start,
+ end,
+ limit: 10000,
+ orderBy: granularity === 'minute' ? 'datetimeMinute_ASC' : 'datetimeHour_ASC'
+ },
+ query: `query GetChatAITokens($accountTag: string, $gateway: string, $start: string, $end: string, $limit: Int, $orderBy: [String!]) {
+ viewer {
+ accounts(filter: {accountTag: $accountTag}) {
+ data: aiGatewayRequestsAdaptiveGroups(
+ filter: {
+ gateway: $gateway,
+ metadataValues_has: "${chatId}",
+ error: 0,
+ ${granularity === 'minute' ? 'datetimeMinute_geq: $start, datetimeMinute_leq: $end' : 'datetimeHour_geq: $start, datetimeHour_leq: $end'}
+ },
+ orderBy: [$orderBy],
+ limit: $limit
+ ) {
+ count
+ sum {
+ uncachedTokensIn
+ uncachedTokensOut
+ cost
+ __typename
+ }
+ dimensions {
+ ts: ${granularity === 'minute' ? 'datetimeMinute' : 'datetimeHour'}
+ provider
+ model
+ __typename
+ }
+ __typename
+ }
+ __typename
+ }
+ __typename
+ }
+ }`
+ }),
+
+ userFiltered: (start: string, end: string) => ({
+ operationName: null,
+ variables: {
+ accountTag: CONFIG.ACCOUNT_TAG,
+ gateway: CONFIG.GATEWAY,
+ start,
+ end,
+ limit: 1
+ },
+ query: `{
+ viewer {
+ scope: accounts(filter: {accountTag: $accountTag}) {
+ latestRequests: aiGatewayRequestsAdaptiveGroups(limit: $limit, filter: {gateway: $gateway, metadataKeys_has: "userId", datetimeHour_geq: $start, datetimeHour_leq: $end}) {
+ count
+ dimensions {
+ ts: datetimeHour
+ __typename
+ }
+ __typename
+ }
+ lastRequest: aiGatewayRequestsAdaptiveGroups(limit: 1, orderBy: [datetimeMinute_DESC], filter: {gateway: $gateway, metadataKeys_has: "userId", datetimeHour_geq: $start, datetimeHour_leq: $end}) {
+ dimensions {
+ ts: datetimeMinute
+ __typename
+ }
+ __typename
+ }
+ totalRequests: aiGatewayRequestsAdaptiveGroups(limit: $limit, filter: {gateway: $gateway, metadataKeys_has: "userId", datetimeHour_geq: $start, datetimeHour_leq: $end}) {
+ count
+ sum {
+ cost
+ cachedRequests
+ erroredRequests
+ uncachedTokensIn
+ uncachedTokensOut
+ cachedTokensIn
+ cachedTokensOut
+ __typename
+ }
+ __typename
+ }
+ __typename
+ }
+ __typename
+ }
+ }`
+ }),
+
+ chatFiltered: (start: string, end: string) => ({
+ operationName: null,
+ variables: {
+ accountTag: CONFIG.ACCOUNT_TAG,
+ gateway: CONFIG.GATEWAY,
+ start,
+ end,
+ limit: 1
+ },
+ query: `{
+ viewer {
+ scope: accounts(filter: {accountTag: $accountTag}) {
+ latestRequests: aiGatewayRequestsAdaptiveGroups(limit: $limit, filter: {gateway: $gateway, metadataKeys_has: "chatId", datetimeHour_geq: $start, datetimeHour_leq: $end}) {
+ count
+ dimensions {
+ ts: datetimeHour
+ __typename
+ }
+ __typename
+ }
+ lastRequest: aiGatewayRequestsAdaptiveGroups(limit: 1, orderBy: [datetimeMinute_DESC], filter: {gateway: $gateway, metadataKeys_has: "chatId", datetimeHour_geq: $start, datetimeHour_leq: $end}) {
+ dimensions {
+ ts: datetimeMinute
+ __typename
+ }
+ __typename
+ }
+ totalRequests: aiGatewayRequestsAdaptiveGroups(limit: $limit, filter: {gateway: $gateway, metadataKeys_has: "chatId", datetimeHour_geq: $start, datetimeHour_leq: $end}) {
+ count
+ sum {
+ cost
+ cachedRequests
+ erroredRequests
+ uncachedTokensIn
+ uncachedTokensOut
+ cachedTokensIn
+ cachedTokensOut
+ __typename
+ }
+ __typename
+ }
+ __typename
+ }
+ __typename
+ }
+ }`
+ }),
+
+ specificId: (start: string, end: string, metadataValue: string) => ({
+ operationName: null,
+ variables: {
+ accountTag: CONFIG.ACCOUNT_TAG,
+ gateway: CONFIG.GATEWAY,
+ start,
+ end,
+ limit: 1
+ },
+ query: `{
+ viewer {
+ scope: accounts(filter: {accountTag: $accountTag}) {
+ latestRequests: aiGatewayRequestsAdaptiveGroups(limit: $limit, filter: {gateway: $gateway, metadataValues_has: "${metadataValue}", datetimeHour_geq: $start, datetimeHour_leq: $end}) {
+ count
+ dimensions {
+ ts: datetimeHour
+ __typename
+ }
+ __typename
+ }
+ lastRequest: aiGatewayRequestsAdaptiveGroups(limit: 1, orderBy: [datetimeMinute_DESC], filter: {gateway: $gateway, metadataValues_has: "${metadataValue}", datetimeHour_geq: $start, datetimeHour_leq: $end}) {
+ dimensions {
+ ts: datetimeMinute
+ __typename
+ }
+ __typename
+ }
+ totalRequests: aiGatewayRequestsAdaptiveGroups(limit: $limit, filter: {gateway: $gateway, metadataValues_has: "${metadataValue}", datetimeHour_geq: $start, datetimeHour_leq: $end}) {
+ count
+ sum {
+ cost
+ cachedRequests
+ erroredRequests
+ uncachedTokensIn
+ uncachedTokensOut
+ cachedTokensIn
+ cachedTokensOut
+ __typename
+ }
+ __typename
+ }
+ __typename
+ }
+ __typename
+ }
+ }`
+ })
+};
+
+// Execute GraphQL query
+async function executeQuery(query: any): Promise {
+ const response = await fetch(CONFIG.GRAPHQL_ENDPOINT, {
+ method: 'POST',
+ headers: {
+ 'Authorization': `Bearer ${CONFIG.API_TOKEN}`,
+ 'Content-Type': 'application/json',
+ },
+ body: JSON.stringify(query),
+ });
+
+ if (!response.ok) {
+ const errorText = await response.text();
+ throw new Error(`HTTP ${response.status}: ${errorText}`);
+ }
+
+ return response.json();
+}
+
+// Enhanced analytics display functions
+function processProviderRequestData(data: ProviderRequestAnalytics): ProviderSummary[] {
+ const providerMap = new Map();
+
+ data.data.viewer.accounts[0]?.data.forEach(item => {
+ const provider = item.dimensions.provider;
+ if (!providerMap.has(provider)) {
+ providerMap.set(provider, { requests: 0, timestamps: [] });
+ }
+ const providerData = providerMap.get(provider)!;
+ providerData.requests += item.count;
+ providerData.timestamps.push(item.dimensions.ts);
+ });
+
+ return Array.from(providerMap.entries()).map(([name, data]) => ({
+ name,
+ totalRequests: data.requests,
+ totalTokensIn: 0,
+ totalTokensOut: 0,
+ totalCost: 0,
+ models: []
+ }));
+}
+
+function processModelTokenData(data: ModelTokenAnalytics): { providers: ProviderSummary[]; models: ModelSummary[] } {
+ const providerMap = new Map();
+ const modelMap = new Map();
+
+ data.data.viewer.accounts[0]?.data.forEach(item => {
+ const { provider, model, ts } = item.dimensions;
+ const { uncachedTokensIn, uncachedTokensOut, cost } = item.sum;
+ const modelKey = `${provider}::${model}`;
+
+ // Update provider summary
+ if (!providerMap.has(provider)) {
+ providerMap.set(provider, {
+ name: provider,
+ totalRequests: 0,
+ totalTokensIn: 0,
+ totalTokensOut: 0,
+ totalCost: 0,
+ models: []
+ });
+ }
+ const providerSummary = providerMap.get(provider)!;
+ providerSummary.totalRequests += item.count;
+ providerSummary.totalTokensIn += uncachedTokensIn;
+ providerSummary.totalTokensOut += uncachedTokensOut;
+ providerSummary.totalCost += cost;
+
+ // Update model summary
+ if (!modelMap.has(modelKey)) {
+ modelMap.set(modelKey, {
+ name: model,
+ provider,
+ requests: 0,
+ tokensIn: 0,
+ tokensOut: 0,
+ cost: 0,
+ firstSeen: ts,
+ lastSeen: ts
+ });
+ }
+ const modelSummary = modelMap.get(modelKey)!;
+ modelSummary.requests += item.count;
+ modelSummary.tokensIn += uncachedTokensIn;
+ modelSummary.tokensOut += uncachedTokensOut;
+ modelSummary.cost += cost;
+ modelSummary.lastSeen = ts > modelSummary.lastSeen ? ts : modelSummary.lastSeen;
+ modelSummary.firstSeen = ts < modelSummary.firstSeen ? ts : modelSummary.firstSeen;
+ });
+
+ // Group models by provider
+ const models = Array.from(modelMap.values());
+ providerMap.forEach(provider => {
+ provider.models = models.filter(m => m.provider === provider.name);
+ });
+
+ return { providers: Array.from(providerMap.values()), models };
+}
+
+function displayProviderSummary(providers: ProviderSummary[]): void {
+ console.log('\n๐ Provider Summary');
+ console.log('-'.repeat(80));
+
+ const providerTable = providers.map(provider => ({
+ Provider: provider.name,
+ Requests: provider.totalRequests.toLocaleString(),
+ 'Tokens In': provider.totalTokensIn.toLocaleString(),
+ 'Tokens Out': provider.totalTokensOut.toLocaleString(),
+ 'Total Cost': `$${provider.totalCost.toFixed(6)}`,
+ Models: provider.models.length
+ }));
+
+ console.table(providerTable);
+}
+
+function displayModelBreakdown(providers: ProviderSummary[], topN?: number): void {
+ console.log('\n๐ฏ Model Breakdown by Provider');
+ console.log('-'.repeat(80));
+
+ providers.forEach(provider => {
+ console.log(`\n๐ ${provider.name.toUpperCase()} Models`);
+
+ let models = provider.models.sort((a, b) => b.tokensIn + b.tokensOut - (a.tokensIn + a.tokensOut));
+ if (topN) {
+ models = models.slice(0, topN);
+ }
+
+ if (models.length === 0) {
+ console.log(' No model data available');
+ return;
+ }
+
+ const modelTable = models.map(model => ({
+ Model: model.name,
+ Requests: model.requests.toLocaleString(),
+ 'Tokens In': model.tokensIn.toLocaleString(),
+ 'Tokens Out': model.tokensOut.toLocaleString(),
+ 'Total Tokens': (model.tokensIn + model.tokensOut).toLocaleString(),
+ 'Cost': `$${model.cost.toFixed(6)}`,
+ 'First Seen': new Date(model.firstSeen).toLocaleString(),
+ 'Last Seen': new Date(model.lastSeen).toLocaleString()
+ }));
+
+ console.table(modelTable);
+ });
+}
+
+function displayTopModels(models: ModelSummary[], topN: number): void {
+ console.log(`\n๐ Top ${topN} Models by Token Usage`);
+ console.log('-'.repeat(80));
+
+ const sortedModels = models
+ .sort((a, b) => (b.tokensIn + b.tokensOut) - (a.tokensIn + a.tokensOut))
+ .slice(0, topN);
+
+ const topModelsTable = sortedModels.map((model, index) => ({
+ Rank: `#${index + 1}`,
+ Model: model.name,
+ Provider: model.provider,
+ 'Total Tokens': (model.tokensIn + model.tokensOut).toLocaleString(),
+ 'Tokens In': model.tokensIn.toLocaleString(),
+ 'Tokens Out': model.tokensOut.toLocaleString(),
+ 'Cost': `$${model.cost.toFixed(6)}`,
+ Requests: model.requests.toLocaleString()
+ }));
+
+ console.table(topModelsTable);
+}
+
+// Enhanced main display function
+function displayResults(results: QueryResult[], options: {
+ showProviders?: boolean;
+ showModels?: boolean;
+ topModels?: number;
+}): void {
+ console.log('\n๐ Cloudflare AI Gateway Analytics Results\n');
+ console.log('=' .repeat(80));
+
+ let providerData: ProviderSummary[] = [];
+ let modelData: ModelSummary[] = [];
+
+ results.forEach(result => {
+ console.log(`\n๐ ${result.name}`);
+ console.log(`โฑ๏ธ Response Time: ${result.responseTime}ms`);
+ console.log('-'.repeat(50));
+
+ if (result.error) {
+ console.log(`โ Error: ${result.error}`);
+ return;
+ }
+
+ // Handle different response types
+ if (result.name.includes('Provider Request Analytics')) {
+ const data = result.data as ProviderRequestAnalytics;
+ if (data?.data?.viewer?.accounts?.[0]?.data) {
+ providerData = processProviderRequestData(data);
+ const totalRequests = providerData.reduce((sum, p) => sum + p.totalRequests, 0);
+ console.log(`โ
Found ${providerData.length} providers with ${totalRequests.toLocaleString()} total requests`);
+ }
+ } else if (result.name.includes('Model Token Analytics')) {
+ const data = result.data as ModelTokenAnalytics;
+ if (data?.data?.viewer?.accounts?.[0]?.data) {
+ const processed = processModelTokenData(data);
+ providerData = processed.providers;
+ modelData = processed.models;
+ const totalCost = processed.providers.reduce((sum, p) => sum + p.totalCost, 0);
+ console.log(`โ
Found ${processed.providers.length} providers and ${modelData.length} models with $${totalCost.toFixed(4)} total cost`);
+ }
+ } else {
+ // Legacy analytics display
+ const data = result.data as AnalyticsResponse;
+ if (!data?.data?.viewer?.scope?.[0]) {
+ console.log('โ No data available');
+ return;
+ }
+
+ const scope = data.data.viewer.scope[0];
+ const totalRequests = scope.totalRequests?.[0];
+ const lastRequest = scope.lastRequest?.[0];
+ const latestRequests = scope.latestRequests?.[0];
+
+ if (totalRequests) {
+ const {
+ cost,
+ cachedRequests,
+ erroredRequests,
+ uncachedTokensIn,
+ uncachedTokensOut,
+ cachedTokensIn,
+ cachedTokensOut
+ } = totalRequests.sum;
+
+ const totalTokensIn = uncachedTokensIn + cachedTokensIn;
+ const totalTokensOut = uncachedTokensOut + cachedTokensOut;
+ const errorRate = totalRequests.count > 0 ? ((erroredRequests / totalRequests.count) * 100).toFixed(2) : '0.00';
+ const cacheHitRate = totalRequests.count > 0 ? ((cachedRequests / totalRequests.count) * 100).toFixed(2) : '0.00';
+
+ const analytics = [
+ {
+ Metric: 'Total Requests',
+ Value: totalRequests.count.toLocaleString(),
+ Details: `${cachedRequests} cached, ${erroredRequests} errors`
+ },
+ {
+ Metric: 'Total Cost',
+ Value: `$${cost.toFixed(6)}`,
+ Details: 'Estimated cost'
+ },
+ {
+ Metric: 'Tokens In',
+ Value: totalTokensIn.toLocaleString(),
+ Details: `${uncachedTokensIn.toLocaleString()} uncached, ${cachedTokensIn.toLocaleString()} cached`
+ },
+ {
+ Metric: 'Tokens Out',
+ Value: totalTokensOut.toLocaleString(),
+ Details: `${uncachedTokensOut.toLocaleString()} uncached, ${cachedTokensOut.toLocaleString()} cached`
+ },
+ {
+ Metric: 'Error Rate',
+ Value: `${errorRate}%`,
+ Details: `${erroredRequests} errors out of ${totalRequests.count} requests`
+ },
+ {
+ Metric: 'Cache Hit Rate',
+ Value: `${cacheHitRate}%`,
+ Details: `${cachedRequests} cached out of ${totalRequests.count} requests`
+ }
+ ];
+
+ if (lastRequest?.dimensions?.ts) {
+ analytics.push({
+ Metric: 'Last Request',
+ Value: new Date(lastRequest.dimensions.ts).toLocaleString(),
+ Details: 'Most recent request timestamp'
+ });
+ }
+
+ if (latestRequests?.count && latestRequests?.dimensions?.ts) {
+ analytics.push({
+ Metric: 'Latest Hour Activity',
+ Value: `${latestRequests.count} requests`,
+ Details: `At ${new Date(latestRequests.dimensions.ts).toLocaleString()}`
+ });
+ }
+
+ console.table(analytics);
+ } else {
+ console.log('โ No request data available');
+ }
+ }
+ });
+
+ // Display enhanced analytics if requested
+ if (options.showProviders && providerData.length > 0) {
+ displayProviderSummary(providerData);
+ }
+
+ if (options.showModels && providerData.length > 0) {
+ displayModelBreakdown(providerData, options.topModels);
+ }
+
+ if (options.topModels && modelData.length > 0) {
+ displayTopModels(modelData, options.topModels);
+ }
+
+ console.log('\n' + '='.repeat(80));
+ console.log('โ
Analysis complete!');
+}
+
+// Parse command line arguments
+function parseArgs(): {
+ userId?: string;
+ chatId?: string;
+ days?: number;
+ showModels?: boolean;
+ showProviders?: boolean;
+ granularity?: 'minute' | 'hour';
+ topModels?: number;
+} {
+ const args = process.argv.slice(2);
+ const result: {
+ userId?: string;
+ chatId?: string;
+ days?: number;
+ showModels?: boolean;
+ showProviders?: boolean;
+ granularity?: 'minute' | 'hour';
+ topModels?: number;
+ } = {};
+
+ for (let i = 0; i < args.length; i++) {
+ switch (args[i]) {
+ case '--user-id':
+ result.userId = args[i + 1];
+ i++;
+ break;
+ case '--chat-id':
+ result.chatId = args[i + 1];
+ i++;
+ break;
+ case '--days':
+ result.days = parseInt(args[i + 1]) || 1;
+ i++;
+ break;
+ case '--show-models':
+ result.showModels = true;
+ break;
+ case '--show-providers':
+ result.showProviders = true;
+ break;
+ case '--granularity':
+ const granularity = args[i + 1];
+ if (granularity === 'minute' || granularity === 'hour') {
+ result.granularity = granularity;
+ }
+ i++;
+ break;
+ case '--top-models':
+ result.topModels = parseInt(args[i + 1]) || 10;
+ i++;
+ break;
+ }
+ }
+
+ return result;
+}
+
+// Generate time range (using format from your examples)
+function getTimeRange(days: number = 1, granularity: 'minute' | 'hour' = 'hour'): { start: string; end: string } {
+ const end = new Date();
+ const start = new Date(end.getTime() - (days * 24 * 60 * 60 * 1000));
+
+ if (granularity === 'minute') {
+ // For minute-level queries, use ISO format as shown in examples
+ return {
+ start: start.toISOString(),
+ end: end.toISOString()
+ };
+ } else {
+ // Use the timezone-aware format for hour-level queries
+ const offsetMinutes = end.getTimezoneOffset();
+ const offsetHours = Math.floor(Math.abs(offsetMinutes) / 60);
+ const offsetMins = Math.abs(offsetMinutes) % 60;
+ const offsetSign = offsetMinutes <= 0 ? '+' : '-';
+ const offsetStr = `${offsetSign}${offsetHours.toString().padStart(2, '0')}:${offsetMins.toString().padStart(2, '0')}`;
+
+ // Set start time to beginning of the day
+ const startOfDay = new Date(start);
+ startOfDay.setHours(0, 0, 0, 0);
+
+ return {
+ start: startOfDay.toISOString().slice(0, 19) + offsetStr,
+ end: end.toISOString().slice(0, 19) + offsetStr
+ };
+ }
+}
+
+// Main function
+async function main(): Promise {
+ try {
+ console.log('๐ Initializing configuration from environment variables...');
+ initializeConfig();
+
+ console.log(`๐ง Configuration:`);
+ console.log(` Account: ${CONFIG.ACCOUNT_TAG}`);
+ console.log(` Gateway: ${CONFIG.GATEWAY}`);
+ console.log(` Endpoint: ${CONFIG.GRAPHQL_ENDPOINT}`);
+ console.log(` Environment: ${CONFIG.IS_STAGING ? 'Staging' : 'Production'}`);
+ console.log(` Token: ${CONFIG.API_TOKEN.substring(0, 8)}... (${CONFIG.IS_STAGING ? 'AI Gateway token' : 'API token'})`);
+
+ const args = parseArgs();
+ const granularity = args.granularity || 'hour';
+ const timeRange = getTimeRange(args.days || 1, granularity);
+
+ console.log(`๐
Analyzing data from ${timeRange.start} to ${timeRange.end}`);
+ console.log(`โ๏ธ Granularity: ${granularity}-level precision`);
+
+ const queries: Array<{ name: string; query: any }> = [];
+
+ // Enhanced query selection based on user input and options
+ if (args.userId) {
+ queries.push({
+ name: `User Analytics (${args.userId})`,
+ query: QUERIES.specificId(timeRange.start, timeRange.end, args.userId)
+ });
+
+ // Add enhanced queries if requested - using generic queries for user ID (user-specific filtering would need different implementation)
+ if (args.showProviders || args.showModels) {
+ queries.push(
+ { name: 'User Provider Request Analytics', query: QUERIES.providerRequests(timeRange.start, timeRange.end, granularity) },
+ { name: 'User Model Token Analytics', query: QUERIES.modelTokens(timeRange.start, timeRange.end, granularity) }
+ );
+ }
+ } else if (args.chatId) {
+ queries.push({
+ name: `Chat Analytics (${args.chatId})`,
+ query: QUERIES.specificId(timeRange.start, timeRange.end, args.chatId)
+ });
+
+ // Add chat-specific enhanced queries if requested
+ if (args.showProviders || args.showModels) {
+ queries.push(
+ { name: `Chat Provider Request Analytics (${args.chatId})`, query: QUERIES.chatProviderRequests(timeRange.start, timeRange.end, args.chatId, granularity) },
+ { name: `Chat Model Token Analytics (${args.chatId})`, query: QUERIES.chatModelTokens(timeRange.start, timeRange.end, args.chatId, granularity) }
+ );
+ }
+ } else {
+ // Default analytics - always include legacy queries for compatibility
+ queries.push(
+ { name: 'Total Gateway Analytics', query: QUERIES.totalGateway(timeRange.start, timeRange.end) },
+ { name: 'User-Filtered Analytics', query: QUERIES.userFiltered(timeRange.start, timeRange.end) },
+ { name: 'Chat-Filtered Analytics', query: QUERIES.chatFiltered(timeRange.start, timeRange.end) }
+ );
+
+ // Add enhanced queries if requested or if no specific filters are provided
+ if (args.showProviders || args.showModels || args.topModels || (!args.userId && !args.chatId)) {
+ queries.push(
+ { name: 'Provider Request Analytics', query: QUERIES.providerRequests(timeRange.start, timeRange.end, granularity) },
+ { name: 'Model Token Analytics', query: QUERIES.modelTokens(timeRange.start, timeRange.end, granularity) }
+ );
+ }
+ }
+
+ // Show configuration summary
+ const configSummary = [
+ `Enhanced Analytics: ${args.showProviders || args.showModels || args.topModels ? 'Enabled' : 'Disabled'}`,
+ args.showProviders ? 'Provider breakdown with costs: Yes' : '',
+ args.showModels ? 'Model breakdown with costs: Yes' : '',
+ args.topModels ? `Top models: ${args.topModels}` : '',
+ args.chatId ? `Chat-specific filtering: ${args.chatId}` : '',
+ ].filter(Boolean);
+
+ if (configSummary.length > 1) {
+ console.log(`๐ง Configuration: ${configSummary.join(', ')}`);
+ }
+
+ console.log(`\n๐ Executing ${queries.length} queries...\n`);
+
+ const results: QueryResult[] = [];
+
+ for (const { name, query } of queries) {
+ console.log(`โณ Executing: ${name}...`);
+ const startTime = Date.now();
+
+ try {
+ const data = await executeQuery(query);
+ const responseTime = Date.now() - startTime;
+
+ // Debug: Log raw response for enhanced queries in debug mode
+ if (import.meta.env.DEV && (name.includes('Provider') || name.includes('Model'))) {
+ console.log(`๐ Debug - ${name} raw response:`, JSON.stringify(data, null, 2).substring(0, 800) + '...');
+ }
+
+ results.push({
+ name,
+ responseTime,
+ data,
+ });
+
+ // Enhanced success logging with data summary
+ let dataSummary = '';
+ if (name.includes('Provider Request Analytics') && (data as ProviderRequestAnalytics)?.data?.viewer?.accounts?.[0]?.data) {
+ const providerData = (data as ProviderRequestAnalytics).data.viewer.accounts[0].data;
+ const uniqueProviders = new Set(providerData.map(d => d.dimensions.provider)).size;
+ const totalRequests = providerData.reduce((sum, d) => sum + d.count, 0);
+ dataSummary = ` (${providerData.length} records, ${uniqueProviders} providers, ${totalRequests.toLocaleString()} requests)`;
+ } else if (name.includes('Model Token Analytics') && (data as ModelTokenAnalytics)?.data?.viewer?.accounts?.[0]?.data) {
+ const modelData = (data as ModelTokenAnalytics).data.viewer.accounts[0].data;
+ const uniqueModels = new Set(modelData.map(d => d.dimensions.model)).size;
+ const uniqueProviders = new Set(modelData.map(d => d.dimensions.provider)).size;
+ const totalCost = modelData.reduce((sum, d) => sum + d.sum.cost, 0);
+ dataSummary = ` (${modelData.length} records, ${uniqueModels} models, ${uniqueProviders} providers, $${totalCost.toFixed(4)} total cost)`;
+ }
+
+ console.log(`โ
${name} completed in ${responseTime}ms${dataSummary}`);
+ } catch (error) {
+ const responseTime = Date.now() - startTime;
+ console.log(`โ ${name} failed in ${responseTime}ms`);
+ console.error(` Error details:`, error);
+
+ results.push({
+ name,
+ responseTime,
+ data: null,
+ error: error instanceof Error ? error.message : String(error),
+ });
+ }
+ }
+
+ displayResults(results, {
+ showProviders: args.showProviders,
+ showModels: args.showModels,
+ topModels: args.topModels
+ });
+
+ // Show helpful usage tips if running with basic options
+ if (!args.showProviders && !args.showModels && !args.topModels && !args.userId && !args.chatId) {
+ console.log('\n๐ก Pro Tips:');
+ console.log(' โข Use --show-providers to see provider breakdown with costs');
+ console.log(' โข Use --show-models to see model-level analytics with costs');
+ console.log(' โข Use --top-models 5 to see top 5 models by usage and cost');
+ console.log(' โข Use --granularity minute for minute-level precision');
+ console.log(' โข Use --chat-id for chat-specific provider/model analytics');
+ console.log(' โข Use --user-id for user-specific analytics (note: enhanced analytics are global)');
+ }
+
+ } catch (error) {
+ console.error('๐ฅ Fatal error:', error);
+ process.exit(1);
+ }
+}
+
+// Run the script
+main().catch(console.error);
\ No newline at end of file
diff --git a/external/drpower-vibe-production/docs/POSTMAN_COLLECTION_README.md b/external/drpower-vibe-production/docs/POSTMAN_COLLECTION_README.md
new file mode 100644
index 00000000..1a085324
--- /dev/null
+++ b/external/drpower-vibe-production/docs/POSTMAN_COLLECTION_README.md
@@ -0,0 +1,352 @@
+# V1 Dev API - Postman Collection
+
+A comprehensive Postman collection for the V1 Dev platform APIs with OAuth setup, CSRF protection, and all endpoints properly documented.
+
+## ๐ Overview
+
+This collection includes **100+ API endpoints** organized into logical groups:
+
+- ๐ **Authentication** - OAuth, email auth, session management (16 endpoints)
+- ๐ค **Agent & Code Generation** - AI-powered webapp creation (5 endpoints)
+- ๐ฑ **Apps Management** - CRUD operations, public feed, favorites (10 endpoints)
+- ๐ค **User Management** - Profile, apps with pagination (2 endpoints)
+- ๐ **Analytics & Stats** - User stats, AI Gateway analytics (4 endpoints)
+- ๐ค **Model Configuration** - AI model settings, BYOK providers (8 endpoints)
+- ๐ข **Custom Model Providers** - OpenAI-compatible API management (6 endpoints)
+- ๐ **Secrets Management** - API keys, credentials with templates (5 endpoints)
+- ๐ **GitHub Integration** - Repository export, OAuth (2 endpoints)
+
+## ๐ Quick Setup
+
+### 1. Import Collection & Environment
+
+1. **Import Collection**:
+ - Open Postman โ Import โ Upload `v1dev-api-collection.postman_collection.json`
+
+2. **Import Environment**:
+ - Import โ Upload `v1dev-environment.postman_environment.json`
+
+3. **Select Environment**:
+ - Choose "V1 Dev Environment" from the environment dropdown
+
+### 2. Configure Base URL
+
+Update the `baseUrl` environment variable:
+
+- **Production**: `https://your-production-domain.com`
+- **Local Development**: `http://localhost:8787` (Wrangler dev server)
+
+### 3. OAuth Setup in Postman
+
+โ ๏ธ **IMPORTANT**: OAuth endpoints redirect to external providers (Google/GitHub) and cannot be tested directly in Postman.
+
+#### ๐ Recommended Approach: OAuth Helper Requests
+
+1. **Use the OAuth Helper requests**:
+ - Run "๐ OAuth Helper - Get Google URL"
+ - Check the **Console tab** in Postman for the OAuth URL
+ - Copy the URL and open it in your browser
+
+2. **Complete authentication in browser**:
+ - Follow the OAuth flow in your browser
+ - After successful auth, you'll be redirected back to your app
+ - Session cookies are now set for your domain
+
+3. **Return to Postman**:
+ - Session cookies will work automatically for same-domain requests
+ - Test with "Get User Profile" to verify authentication
+
+#### Alternative: Manual URL Construction
+
+If helpers don't work, manually construct URLs:
+- **Google OAuth**: `{{baseUrl}}/api/auth/oauth/google`
+- **GitHub OAuth**: `{{baseUrl}}/api/auth/oauth/github`
+- Open these URLs directly in your browser
+
+#### Why Direct OAuth Requests Show HTML
+
+- OAuth endpoints return HTTP redirects (302) to provider websites
+- Postman shows the redirect HTML instead of following it
+- This is normal behavior - OAuth requires browser-based flows
+
+### 4. CSRF Token Automation
+
+The collection automatically handles CSRF tokens:
+
+- Pre-request scripts fetch CSRF tokens when needed
+- Tokens are stored in the `csrf_token` environment variable
+- All state-changing requests include the token automatically
+
+## ๐ Authentication Methods
+
+### 1. Email Authentication
+```json
+POST /api/auth/register
+{
+ "email": "user@example.com",
+ "password": "SecurePassword123!",
+ "name": "Test User"
+}
+
+POST /api/auth/login
+{
+ "email": "user@example.com",
+ "password": "SecurePassword123!"
+}
+```
+
+### 2. OAuth Authentication
+- **Google OAuth**: `GET /api/auth/oauth/google`
+- **GitHub OAuth**: `GET /api/auth/oauth/github`
+
+### 3. Session-Based Authentication
+- Uses secure HTTP-only cookies
+- Sessions are automatically maintained across requests
+- CSRF protection via `X-CSRF-Token` header
+
+## ๐ฑ Core API Workflows
+
+### 1. Create a New App with AI
+
+```bash
+# 1. Login or use OAuth
+POST /api/auth/login
+
+# 2. Start code generation
+POST /api/agent
+{
+ "query": "Create a React todo app with TypeScript and Tailwind CSS",
+ "agentMode": "smart",
+ "language": "typescript",
+ "frameworks": ["react", "tailwindcss"],
+ "selectedTemplate": "react-typescript"
+}
+
+# 3. Connect to WebSocket for real-time updates
+GET /api/agent/{agentId}/ws (WebSocket)
+
+# 4. Deploy preview when ready
+GET /api/agent/{agentId}/preview
+```
+
+### 2. Browse and Interact with Apps
+
+```bash
+# Get public apps (no auth required)
+GET /api/apps/public?page=1&limit=20&sort=stars&order=desc
+
+# Get app details (no auth required)
+GET /api/apps/{appId}
+
+# Star an app (requires auth)
+POST /api/apps/{appId}/star
+
+# Fork an app (requires auth)
+POST /api/apps/{appId}/fork
+```
+
+### 3. Configure AI Models
+
+```bash
+# Get available models and providers
+GET /api/model-configs/byok-providers
+
+# Update model configuration for specific agent action
+PUT /api/model-configs/planner
+{
+ "modelName": "claude-3-5-sonnet-20241022",
+ "maxTokens": 4096,
+ "temperature": 0.7,
+ "reasoningEffort": "medium"
+}
+
+# Test model configuration
+POST /api/model-configs/test
+{
+ "agentActionName": "planner",
+ "useUserKeys": true
+}
+```
+
+### 4. Manage API Keys and Secrets
+
+```bash
+# Get secret templates
+GET /api/secrets/templates
+
+# Store an API key
+POST /api/secrets
+{
+ "templateId": "openai_api_key",
+ "name": "My OpenAI API Key",
+ "envVarName": "OPENAI_API_KEY",
+ "value": "sk-your-api-key-here"
+}
+
+# Create custom model provider
+POST /api/user/providers
+{
+ "name": "My Custom OpenAI Provider",
+ "baseUrl": "https://api.openai.com/v1",
+ "apiKey": "sk-your-key",
+ "models": [...]
+}
+```
+
+## ๐ง Advanced Features
+
+### Environment Variables
+The collection uses these automatically managed variables:
+
+| Variable | Description | Auto-populated |
+|----------|-------------|----------------|
+| `csrf_token` | CSRF protection token | โ
|
+| `user_id` | Current user ID | โ
|
+| `session_id` | Current session ID | โ
|
+| `agent_id` | Current agent/app ID | โ
|
+| `app_id` | Current app ID | โ
|
+| `provider_id` | Model provider ID | Manual |
+| `secret_id` | Secret ID | Manual |
+
+### Request Automation
+
+- **CSRF Tokens**: Automatically fetched and included
+- **Session Management**: Cookies handled transparently
+- **Variable Population**: IDs extracted from responses
+- **Error Handling**: Test scripts validate responses
+
+### WebSocket Testing
+
+For WebSocket endpoints like agent communication:
+
+1. Use a WebSocket client (wscat, Postman WebSocket, etc.)
+2. Connect to: `ws://localhost:8787/api/agent/{agentId}/ws`
+3. Include authentication cookies
+4. Send/receive real-time messages during code generation
+
+## ๐ ๏ธ Development Setup
+
+### Local Development
+
+1. **Start Wrangler Dev Server**:
+ ```bash
+ cd /path/to/v1dev
+ npm run local # Starts on http://localhost:8787
+ ```
+
+2. **Update Environment**:
+ - Set `baseUrl` to `http://localhost:8787`
+ - Ensure `.dev.vars` contains required environment variables
+
+3. **Test Authentication**:
+ - OAuth may require ngrok for localhost callback URLs
+ - Email auth works directly with localhost
+
+### Production Testing
+
+1. Update `baseUrl` to your production domain
+2. Ensure OAuth apps are configured with correct callback URLs
+3. Test with real OAuth credentials
+
+## ๐ API Documentation
+
+### Authentication Levels
+
+- **Public**: No authentication required
+- **Authenticated**: Requires valid session
+- **Owner Only**: Requires ownership of the resource
+
+### Common Parameters
+
+| Parameter | Description | Example |
+|-----------|-------------|---------|
+| `page` | Page number for pagination | `1` |
+| `limit` | Items per page | `20` |
+| `sort` | Sort field | `createdAt`, `stars` |
+| `order` | Sort order | `asc`, `desc` |
+| `period` | Time period filter | `today`, `week`, `month`, `all` |
+| `search` | Search query | `"todo app"` |
+
+### Response Format
+
+All API responses follow this structure:
+
+```json
+{
+ "success": true,
+ "data": { ... },
+ "message": "Optional message",
+ "pagination": { // For paginated responses
+ "page": 1,
+ "limit": 20,
+ "total": 100,
+ "totalPages": 5
+ }
+}
+```
+
+### Error Responses
+
+```json
+{
+ "success": false,
+ "error": "Error message",
+ "code": "ERROR_CODE",
+ "details": { ... } // Optional additional details
+}
+```
+
+## ๐จ Troubleshooting
+
+### Common Issues
+
+1. **CSRF Token Errors**:
+ - Ensure pre-request scripts are enabled
+ - Manually run "Get CSRF Token" request
+ - Check that `X-CSRF-Token` header is included in POST/PUT/DELETE requests
+
+2. **Authentication Issues**:
+ - Verify cookies are enabled in Postman
+ - Check that OAuth callback URLs match your configuration
+ - Ensure session hasn't expired (check "Get User Profile")
+
+3. **WebSocket Connection Issues**:
+ - WebSockets require active session authentication
+ - Use external WebSocket client if Postman WebSocket support is limited
+ - Verify agent ownership for WebSocket connections
+
+4. **Local Development Issues**:
+ - Ensure Wrangler is running (`npm run local`)
+ - Check that `.dev.vars` file contains required environment variables
+ - Verify D1 database is set up (`npm run db:setup`)
+
+### Getting Help
+
+1. **Check API Response**: Look at response body for detailed error messages
+2. **Verify Environment**: Ensure correct `baseUrl` is set
+3. **Test Authentication**: Run "Check Auth Status" to verify session
+4. **Review Logs**: Check browser DevTools or Wrangler logs for additional context
+
+## ๐ฏ Testing Workflows
+
+### Complete User Journey
+
+1. **Register/Login** โ Authentication working
+2. **Create App** โ AI generation working
+3. **Browse Public Apps** โ Public feed working
+4. **Star/Fork App** โ Social features working
+5. **Configure Models** โ AI customization working
+6. **Manage Secrets** โ Security features working
+7. **Export to GitHub** โ Integration working
+
+### Quick Health Check
+
+Run these requests to verify the system:
+
+1. `GET /api/auth/providers` - System status
+2. `GET /api/apps/public` - Public API working
+3. `POST /api/auth/login` - Authentication working
+4. `GET /api/model-configs` - AI system working
+5. `GET /api/stats` - Analytics working
+
+This collection provides comprehensive coverage of all V1 Dev APIs with proper authentication, error handling, and real-world usage examples. Perfect for development, testing, and integration work!
\ No newline at end of file
diff --git a/external/drpower-vibe-production/docs/architecture-diagrams.md b/external/drpower-vibe-production/docs/architecture-diagrams.md
new file mode 100644
index 00000000..68eeb4e2
--- /dev/null
+++ b/external/drpower-vibe-production/docs/architecture-diagrams.md
@@ -0,0 +1,837 @@
+# Cloudflare Orange Build - Architecture Diagrams
+
+This document contains comprehensive Mermaid diagrams representing the architecture of the Cloudflare Orange Build project.
+
+## Presentation-Ready Architecture Diagram
+
+*Copy-paste this beautiful diagram directly into your slides*
+
+```mermaid
+graph TB
+ %% User Interface Layer
+ subgraph "๐ฅ๏ธ Frontend (React + Vite)"
+ UI["๐จ User Interface Dashboard & Chat"]
+ Chat["๐ฌ Real-time Chat WebSocket Connection"]
+ Preview["๐๏ธ Live Preview Sandbox Integration"]
+ end
+
+ %% API Gateway Layer
+ subgraph "๐ฆ API Gateway Layer"
+ APIRouter["๐ API Router Hono Framework"]
+ end
+
+ %% Core Backend Services
+ subgraph "๐ Core Services (Cloudflare Workers)"
+ Auth["๐ Auth Service JWT + OAuth"]
+ AgentController["๐๏ธ Agent Controller WebSocket Manager"]
+ SandboxService["๐ฆ Sandbox Service Container Orchestration"]
+ end
+
+ %% Agent System (Durable Objects)
+ subgraph "๐ค Agent System (Durable Objects)"
+ CodeGenAgent["โก Code Generator Agent Deterministic State Machine"]
+ AgentState["๐พ Agent State Persistent in Durable Object"]
+ end
+
+ %% AI Operations Pipeline
+ subgraph "๐ง AI Operations Pipeline"
+ BlueprintGen["๐ Blueprint Generation"]
+ PhaseGen["๐ Phase Planning"]
+ PhaseImpl["โ๏ธ Implementation"]
+ CodeReview["๐ Code Review"]
+ CodeFixer["๐ ๏ธ Real-time Fixing"]
+ end
+
+ %% Storage & Infrastructure
+ subgraph "โ๏ธ Cloudflare Infrastructure"
+ D1["๐๏ธ D1 Database User Data + Apps"]
+ KV["๐ KV Storage Sessions + Cache"]
+ R2["๐ R2 Storage Templates + Assets"]
+ Containers["๐ณ Containers Sandbox Runtime"]
+ AIGateway["๐ช AI Gateway Multi-Provider Router"]
+ end
+
+ %% External AI Providers
+ subgraph "๐ค AI Providers"
+ Gemini["๐ Gemini (Primary)"]
+ OpenAI["๐ง OpenAI GPT-4"]
+ Claude["๐ญ Anthropic Claude"]
+ Cerebras["๐งช Cerebras"]
+ end
+
+ %% External Services
+ subgraph "๐ External Services"
+ GitHub["๐ GitHub API"]
+ OAuth["๐ OAuth Providers"]
+ end
+
+ %% Request Flow
+ UI --> APIRouter
+ Chat --> APIRouter
+ Preview --> APIRouter
+
+ APIRouter --> Auth
+ APIRouter --> AgentController
+ APIRouter --> SandboxService
+
+ %% Agent Orchestration
+ AgentController --> CodeGenAgent
+ CodeGenAgent -.-> AgentState
+ CodeGenAgent --> SandboxService
+
+ %% AI Operations Flow
+ CodeGenAgent --> BlueprintGen
+ CodeGenAgent --> PhaseGen
+ CodeGenAgent --> PhaseImpl
+ CodeGenAgent --> CodeReview
+ CodeGenAgent --> CodeFixer
+
+ %% Data Persistence
+ Auth --> D1
+ AgentController --> KV
+ SandboxService --> R2
+ SandboxService --> Containers
+ BlueprintGen --> R2
+
+ %% AI Gateway Routing
+ BlueprintGen --> AIGateway
+ PhaseGen --> AIGateway
+ PhaseImpl --> AIGateway
+ CodeReview --> AIGateway
+ CodeFixer --> AIGateway
+
+ AIGateway --> Gemini
+ AIGateway --> OpenAI
+ AIGateway --> Claude
+ AIGateway --> Cerebras
+
+ %% External Integrations
+ Auth --> OAuth
+ SandboxService --> GitHub
+
+ %% Styling
+ classDef frontend fill:#e1f5fe,stroke:#01579b,stroke-width:2px,color:#000
+ classDef router fill:#fff8e1,stroke:#f57f17,stroke-width:2px,color:#000
+ classDef backend fill:#fff3e0,stroke:#e65100,stroke-width:2px,color:#000
+ classDef agent fill:#ff9900,stroke:#cc7a00,stroke-width:3px,color:#fff
+ classDef ai fill:#f3e5f5,stroke:#4a148c,stroke-width:2px,color:#000
+ classDef infra fill:#e8f5e8,stroke:#1b5e20,stroke-width:2px,color:#000
+ classDef aiproviders fill:#fce4ec,stroke:#880e4f,stroke-width:2px,color:#000
+ classDef external fill:#f1f8e9,stroke:#33691e,stroke-width:2px,color:#000
+
+ class UI,Chat,Preview frontend
+ class APIRouter router
+ class Auth,AgentController,SandboxService backend
+ class CodeGenAgent,AgentState agent
+ class BlueprintGen,PhaseGen,PhaseImpl,CodeReview,CodeFixer ai
+ class D1,KV,R2,Containers,AIGateway infra
+ class Gemini,OpenAI,Claude,Cerebras aiproviders
+ class GitHub,OAuth external
+```
+
+---
+
+## Detailed System Diagrams
+
+### 1. Overall System Architecture
+
+```mermaid
+graph TB
+ subgraph "Frontend (Vite + React)"
+ UI[User Interface]
+ Chat[Chat Interface]
+ Preview[Live Preview]
+ Dashboard[User Dashboard]
+ end
+
+ subgraph "Cloudflare Workers (Backend)"
+ APIRouter[API Router]
+ Auth[Auth Service]
+ AgentController[Agent Controller]
+ SandboxService[Sandbox Service]
+
+ subgraph "Agents SDK (Durable Objects)"
+ CodeGenAgent[Code Generator Agent]
+ AgentState[Agent State & Context]
+ end
+ end
+
+ subgraph "AI Operations"
+ BlueprintGen[Blueprint Generation]
+ PhaseGen[Phase Generation]
+ PhaseImpl[Phase Implementation]
+ CodeReview[Code Review]
+ RealtimeCodeFixer[Realtime Code Fixer]
+ FastCodeFixer[Fast Code Fixer]
+ FileRegen[File Regeneration]
+ UserConvProcessor[User Conversation Processor]
+ end
+
+ subgraph "Cloudflare Infrastructure"
+ D1[(D1 Database)]
+ KV[KV Storage]
+ R2[R2 Object Storage]
+ Containers[Cloudflare Containers]
+ Workers[Workers Runtime]
+ AIGateway[AI Gateway]
+ end
+
+ subgraph "External Services"
+ OpenAI[OpenAI API]
+ GitHub[GitHub API]
+ OAuth[OAuth Providers]
+ end
+
+ UI --> APIRouter
+ Chat --> APIRouter
+ Preview --> APIRouter
+ Dashboard --> APIRouter
+
+ APIRouter --> Auth
+ APIRouter --> AgentController
+ APIRouter --> SandboxService
+ AgentController --> CodeGenAgent
+ CodeGenAgent --> SandboxService
+ CodeGenAgent --> BlueprintGen
+ CodeGenAgent --> PhaseGen
+ CodeGenAgent --> PhaseImpl
+ CodeGenAgent --> CodeReview
+ CodeGenAgent --> RealtimeCodeFixer
+ CodeGenAgent --> FastCodeFixer
+ CodeGenAgent --> FileRegen
+ CodeGenAgent --> UserConvProcessor
+
+ Auth --> D1
+ AgentState --> D1
+ SandboxService --> Containers
+ SandboxService --> R2
+
+ BlueprintGen --> AIGateway
+ PhaseGen --> AIGateway
+ PhaseImpl --> AIGateway
+ CodeReview --> AIGateway
+ RealtimeCodeFixer --> AIGateway
+ FastCodeFixer --> AIGateway
+ FileRegen --> AIGateway
+ UserConvProcessor --> AIGateway
+ AIGateway --> OpenAI
+
+ Auth --> OAuth
+ SandboxService --> GitHub
+ AgentController --> KV
+
+ style CodeGenAgent fill:#ff9900
+ style AIGateway fill:#ff9900
+ style D1 fill:#ff9900
+ style Containers fill:#ff9900
+```
+
+## 2. Hybrid Agent System Architecture
+
+```mermaid
+graph TB
+ subgraph "Single Orchestrating Agent (Cloudflare Agents SDK)"
+ Agent[Code Generator Agent]
+ StateMachine[Deterministic State Machine]
+ AgentState[Shared Agent State]
+ WebSocket[WebSocket Broadcasting]
+ end
+
+ subgraph "Specialized AI Operations"
+ BlueprintOp[Blueprint Generation PRD & Template Selection]
+ PhaseGen[Phase Generation Planning & Strategy]
+ PhaseImpl[Phase Implementation File Generation]
+ CodeReview[Code Review Issue Detection]
+ RealtimeCodeFixer[Realtime Code Fixer Runtime Error Resolution]
+ FastCodeFixer[Fast Code Fixer Quick Issue Fixes]
+ FileRegen[File Regeneration Surgical Code Fixes]
+ UserConvProcessor[User Conversation Processor User Feedback Integration]
+ end
+
+ subgraph "Deterministic Orchestration Logic"
+ PhaseManager[Phase Manager]
+ UserInputHandler[User Input Handler]
+ FileManager[File Content Manager]
+ ErrorHandler[Error Recovery System]
+ end
+
+ subgraph "AI Integration Layer"
+ AIGateway[Cloudflare AI Gateway]
+ PromptSystem[Dynamic Prompt System]
+ StreamingFormat[SCOF Streaming Format]
+ end
+
+ Agent --> StateMachine
+ StateMachine --> PhaseManager
+ StateMachine --> UserInputHandler
+ StateMachine --> FileManager
+ StateMachine --> ErrorHandler
+
+ PhaseManager --> BlueprintOp
+ PhaseManager --> PhaseGen
+ PhaseManager --> PhaseImpl
+ PhaseManager --> UserConvProcessor
+
+ ErrorHandler --> CodeReview
+ ErrorHandler --> RealtimeCodeFixer
+ ErrorHandler --> FastCodeFixer
+ ErrorHandler --> FileRegen
+
+ BlueprintOp --> AIGateway
+ PhaseGen --> AIGateway
+ PhaseImpl --> AIGateway
+ CodeReview --> AIGateway
+ RealtimeCodeFixer --> AIGateway
+ FastCodeFixer --> AIGateway
+ FileRegen --> AIGateway
+ UserConvProcessor --> AIGateway
+
+ AIGateway --> PromptSystem
+ AIGateway --> StreamingFormat
+
+ Agent --> AgentState
+ Agent --> WebSocket
+
+ BlueprintOp -.-> AgentState
+ PhaseGen -.-> AgentState
+ PhaseImpl -.-> AgentState
+ CodeReview -.-> AgentState
+ RealtimeCodeFixer -.-> AgentState
+ FastCodeFixer -.-> AgentState
+ FileRegen -.-> AgentState
+ UserConvProcessor -.-> AgentState
+
+ style Agent fill:#ff9900
+ style StateMachine fill:#ffcc66
+ style AIGateway fill:#ff9900
+```
+
+## 3. Authentication & User Management Flow
+
+```mermaid
+sequenceDiagram
+ participant User
+ participant Frontend
+ participant AuthController
+ participant OAuth
+ participant D1
+ participant JWT
+
+ User->>Frontend: Login Request
+ Frontend->>AuthController: POST /api/auth/providers
+ AuthController->>Frontend: Available auth methods
+
+ alt OAuth Flow
+ Frontend->>AuthController: POST /api/auth/oauth/github
+ AuthController->>OAuth: Redirect to GitHub
+ OAuth->>User: Authorization page
+ User->>OAuth: Grant permission
+ OAuth->>AuthController: Callback with code
+ AuthController->>OAuth: Exchange code for tokens
+ OAuth->>AuthController: User profile + tokens
+ AuthController->>D1: Store/update user
+ AuthController->>JWT: Generate tokens
+ JWT->>AuthController: Access/Refresh tokens
+ AuthController->>Frontend: Set HTTP-only cookies
+ else Email/Password Flow
+ Frontend->>AuthController: POST /api/auth/login
+ AuthController->>D1: Verify credentials
+ D1->>AuthController: User data
+ AuthController->>JWT: Generate tokens
+ JWT->>AuthController: Access/Refresh tokens
+ AuthController->>Frontend: Set HTTP-only cookies
+ end
+
+ Frontend->>User: Login success
+
+ Note over AuthController,D1: Session stored in D1 JWT tokens in HTTP-only cookies Automatic token refresh
+```
+
+## 4. Sandbox System & Deployment Pipeline
+
+```mermaid
+graph TD
+ subgraph "User Request"
+ UserInput[User Prompt]
+ ChatInterface[Chat Interface]
+ end
+
+ subgraph "Code Generation Phase"
+ Agent[Smart Agent]
+ CodeGen[Code Generation]
+ FileSystem[Generated Files]
+ end
+
+ subgraph "Sandbox System"
+ SandboxSDK[Cloudflare Sandbox SDK]
+ Container[Isolated Container]
+ LivePreview[Live Preview Server]
+ ResourceProv[Resource Provisioner]
+ TemplateParser[Template Parser]
+ ConfigKV[Configuration Storage KV-based wrangler.jsonc]
+ end
+
+ subgraph "Quality Assurance"
+ RuntimeCheck[Runtime Error Detection]
+ StaticAnalysis[Static Analysis]
+ CodeFixer[Auto Code Fixing]
+ end
+
+ subgraph "Deployment Options"
+ WorkersDev[workers.dev Deployment]
+ DispatchNamespace[Dispatch Namespace]
+ GitHubExport[GitHub Export]
+ end
+
+ subgraph "Cloudflare Resources"
+ D1DB[(Auto-provisioned D1)]
+ KVStore[Auto-provisioned KV]
+ CFWorkers[Cloudflare Workers]
+ end
+
+ UserInput --> ChatInterface
+ ChatInterface --> Agent
+ Agent --> CodeGen
+ CodeGen --> FileSystem
+
+ FileSystem --> SandboxSDK
+ SandboxSDK --> Container
+ Container --> LivePreview
+
+ LivePreview --> RuntimeCheck
+ RuntimeCheck --> StaticAnalysis
+ StaticAnalysis --> CodeFixer
+ CodeFixer --> FileSystem
+
+ FileSystem --> ResourceProv
+ ResourceProv --> D1DB
+ ResourceProv --> KVStore
+
+ Container --> TemplateParser
+ TemplateParser --> WorkersDev
+ TemplateParser --> DispatchNamespace
+ TemplateParser --> GitHubExport
+
+ WorkersDev --> CFWorkers
+ DispatchNamespace --> CFWorkers
+
+ style SandboxSDK fill:#ff9900
+ style Container fill:#ff9900
+ style D1DB fill:#ff9900
+ style KVStore fill:#ff9900
+ style CFWorkers fill:#ff9900
+```
+
+## 5. Database Schema & Relationships
+
+```mermaid
+erDiagram
+ users {
+ text id PK
+ text email UK
+ text username UK
+ text password_hash
+ text full_name
+ text avatar_url
+ text verification_token
+ integer email_verified
+ timestamp created_at
+ timestamp updated_at
+ }
+
+ sessions {
+ text id PK
+ text user_id FK
+ text access_token
+ text refresh_token
+ timestamp expires_at
+ timestamp created_at
+ }
+
+ teams {
+ text id PK
+ text name
+ text slug UK
+ text description
+ text avatar_url
+ text owner_id FK
+ timestamp created_at
+ }
+
+ team_members {
+ text team_id FK
+ text user_id FK
+ text role
+ timestamp joined_at
+ }
+
+ apps {
+ text id PK
+ text title
+ text description
+ text slug
+ text icon_url
+ text original_prompt
+ text final_prompt
+ json blueprint
+ text framework
+ text user_id FK
+ text team_id FK
+ text session_token
+ text visibility
+ text board_id FK
+ text status
+ text deployment_url
+ text cloudflare_account_id
+ text deployment_status
+ json deployment_metadata
+ text github_repository_url
+ text github_repository_visibility
+ integer is_archived
+ integer is_featured
+ integer version
+ text parent_app_id
+ text screenshot_url
+ timestamp screenshot_captured_at
+ timestamp created_at
+ timestamp updated_at
+ timestamp last_deployed_at
+ }
+
+ cloudflare_accounts {
+ text id PK
+ text user_id FK
+ text team_id FK
+ text account_id
+ text encrypted_api_token
+ text account_name
+ timestamp created_at
+ }
+
+ github_integrations {
+ text id PK
+ text user_id FK
+ text team_id FK
+ text github_username
+ text encrypted_access_token
+ json repositories
+ timestamp created_at
+ }
+
+ boards {
+ text id PK
+ text name
+ text description
+ text slug UK
+ text creator_id FK
+ integer is_public
+ timestamp created_at
+ }
+
+ users ||--o{ sessions : has
+ users ||--o{ teams : owns
+ users ||--o{ team_members : belongs_to
+ teams ||--o{ team_members : has
+ users ||--o{ apps : creates
+ teams ||--o{ apps : owns
+ users ||--o{ cloudflare_accounts : has
+ teams ||--o{ cloudflare_accounts : has
+ users ||--o{ github_integrations : has
+ teams ||--o{ github_integrations : has
+ users ||--o{ boards : creates
+ boards ||--o{ apps : contains
+
+## 6. User Journey Flow - App Creation to Deployment
+
+```mermaid
+journey
+ title User Creates and Deploys an App
+ section Getting Started
+ Visit Homepage: 5: User
+ Enter App Description: 4: User
+ Start Generation: 5: User
+ section Code Generation
+ AI Creates Blueprint: 3: System
+ Phase-wise Implementation: 4: System
+ Real-time Code Review: 4: System
+ Error Detection & Fixing: 3: System
+ section Live Preview
+ Sandbox Container Starts: 5: System
+ Live Preview Available: 5: User
+ Runtime Error Detection: 4: System
+ Iterative Improvements: 4: User, System
+ section Quality Assurance
+ Static Analysis: 4: System
+ Code Review Cycle: 3: System
+ Auto-fix Critical Issues: 4: System
+ User Feedback Integration: 5: User
+ section Deployment
+ Resource Provisioning: 3: System
+ Template Parsing: 3: System
+ Cloudflare Workers Deploy: 5: System
+ Live App URL Generated: 5: User
+ section Post-Deployment
+ Save to Dashboard: 4: User
+ Share with Community: 3: User
+ GitHub Export: 4: User
+```
+
+## 7. Real-time Communication Flow
+
+```mermaid
+sequenceDiagram
+ participant User
+ participant Frontend
+ participant Agent
+ participant AIGateway
+ participant Sandbox
+ participant WebSocket
+
+ User->>Frontend: Send message
+ Frontend->>Agent: WebSocket message
+ Agent->>WebSocket: Broadcast generation_started
+ WebSocket->>Frontend: Real-time update
+
+ loop Phase Generation
+ Agent->>AIGateway: Phase planning request
+ AIGateway->>Agent: Streaming response
+ Agent->>WebSocket: Broadcast phase_update
+ WebSocket->>Frontend: Live phase progress
+ end
+
+ loop Code Implementation
+ Agent->>AIGateway: Code generation request
+ AIGateway->>Agent: SCOF streaming format
+ Agent->>WebSocket: Broadcast file_generated
+ WebSocket->>Frontend: Live file updates
+ end
+
+ Agent->>Sandbox: Deploy to container
+ Sandbox->>Agent: Preview URL ready
+ Agent->>WebSocket: Broadcast preview_ready
+ WebSocket->>Frontend: Preview available
+
+ loop Quality Assurance
+ Sandbox->>Agent: Runtime errors detected
+ Agent->>AIGateway: Fix generation request
+ AIGateway->>Agent: Fixed code
+ Agent->>Sandbox: Update files
+ Agent->>WebSocket: Broadcast fixes_applied
+ WebSocket->>Frontend: Updated preview
+ end
+
+ User->>Frontend: Deploy to Cloudflare
+ Frontend->>Agent: Deploy request
+ Agent->>Sandbox: Trigger deployment
+ Sandbox->>Agent: Deployment complete
+ Agent->>WebSocket: Broadcast deployment_complete
+ WebSocket->>Frontend: Live app URL
+```
+
+## 8. AI Operations Pipeline
+
+```mermaid
+flowchart TD
+ %% User Input
+ Input["๐ฃ๏ธ User Prompt Natural Language Request"]
+
+ %% Planning Phase
+ subgraph "๐ Planning Phase"
+ TemplateSelection["๐ท๏ธ Template Selection Cloudflare Stack Templates"]
+ BlueprintGen["๐ Blueprint Generation PRD + Architecture Design"]
+ end
+
+ %% State Machine Controller
+ subgraph "๐ค Deterministic State Machine"
+ StateMachine["โ๏ธ Agent State Controller generateAllFiles()"]
+
+ subgraph "๐ State Transitions"
+ PhaseGenerating["๐ PHASE_GENERATING"]
+ PhaseImplementing["๐ PHASE_IMPLEMENTING"]
+ Reviewing["๐ REVIEWING"]
+ Finalizing["โจ FINALIZING"]
+ Idle["โ
IDLE"]
+ end
+ end
+
+ %% Operations Layer
+ subgraph "๐ง AI Operations"
+ PhaseGenOp["๐ Phase Generation Development Phases"]
+ PhaseImplOp["๐ Phase Implementation File Generation + SCOF"]
+ CodeReviewOp["๐ Code Review Issue Detection"]
+ FastCodeFixerOp["โก Fast Code Fixer Quick Issue Fixes"]
+ FileRegenOp["๐ ๏ธ File Regeneration Surgical Code Repairs"]
+ ScreenshotAnalysisOp["๐ท Screenshot Analysis Visual Validation"]
+ UserConvOp["๐ฌ User Conversation Feedback Processing"]
+ end
+
+ %% Quality Assurance
+ subgraph "๐ Quality Assurance"
+ ReviewCycles["๐ Review Cycles Up to 5 iterations"]
+ IssueCheck{{"โ ๏ธ Issues Found?"}}
+ StaticAnalysis["๐ Static Analysis Code Validation"]
+ end
+
+ %% External Integration
+ subgraph "๐ค AI Gateway"
+ AIGateway["๐ช Cloudflare AI Gateway Multi-Provider Router"]
+
+ subgraph "๐ AI Providers"
+ Gemini["๐ Gemini (Primary)"]
+ GPT["๐ง GPT-4"]
+ Claude["๐ญ Claude"]
+ Cerebras["๐งช Cerebras"]
+ end
+ end
+
+ %% Main Flow
+ Input --> TemplateSelection
+ TemplateSelection --> BlueprintGen
+ BlueprintGen --> StateMachine
+
+ %% State Machine Flow
+ StateMachine --> PhaseGenerating
+ PhaseGenerating --> PhaseImplementing
+ PhaseImplementing --> Reviewing
+ Reviewing --> Finalizing
+ Finalizing --> Idle
+
+ %% Operations Execution
+ PhaseGenerating --> PhaseGenOp
+ PhaseImplementing --> PhaseImplOp
+ Reviewing --> CodeReviewOp
+
+ %% Quality Control Loop
+ CodeReviewOp --> ReviewCycles
+ ReviewCycles --> IssueCheck
+ IssueCheck -->|"โ
Yes"| FastCodeFixerOp
+ IssueCheck -->|"โ
Yes"| FileRegenOp
+ IssueCheck -->|"โ No"| StaticAnalysis
+ FastCodeFixerOp --> PhaseImplementing
+ FileRegenOp --> PhaseImplementing
+
+ %% User Interaction
+ UserConvOp --> PhaseGenerating
+ ScreenshotAnalysisOp --> CodeReviewOp
+
+ %% AI Integration
+ TemplateSelection --> AIGateway
+ BlueprintGen --> AIGateway
+ PhaseGenOp --> AIGateway
+ PhaseImplOp --> AIGateway
+ CodeReviewOp --> AIGateway
+ FastCodeFixerOp --> AIGateway
+ FileRegenOp --> AIGateway
+ ScreenshotAnalysisOp --> AIGateway
+ UserConvOp --> AIGateway
+
+ AIGateway --> Gemini
+ AIGateway --> GPT
+ AIGateway --> Claude
+ AIGateway --> Cerebras
+
+ %% Enhanced Styling
+ classDef planning fill:#e3f2fd,stroke:#1976d2,stroke-width:2px,color:#000
+ classDef statemachine fill:#fff3e0,stroke:#f57c00,stroke-width:3px,color:#000
+ classDef states fill:#f3e5f5,stroke:#7b1fa2,stroke-width:2px,color:#000
+ classDef operations fill:#e8f5e8,stroke:#388e3c,stroke-width:2px,color:#000
+ classDef quality fill:#ffebee,stroke:#d32f2f,stroke-width:2px,color:#000
+ classDef ai fill:#ff9900,stroke:#cc7a00,stroke-width:3px,color:#fff
+ classDef decision fill:#fff8e1,stroke:#f57f17,stroke-width:2px,color:#000
+ classDef success fill:#66ff66,stroke:#2e7d32,stroke-width:3px,color:#000
+ classDef providers fill:#fce4ec,stroke:#c2185b,stroke-width:2px,color:#000
+
+ class Input,TemplateSelection,BlueprintGen planning
+ class StateMachine statemachine
+ class PhaseGenerating,PhaseImplementing,Reviewing,Finalizing states
+ class Idle success
+ class PhaseGenOp,PhaseImplOp,CodeReviewOp,FastCodeFixerOp,FileRegenOp,ScreenshotAnalysisOp,UserConvOp operations
+ class ReviewCycles,StaticAnalysis quality
+ class IssueCheck decision
+ class AIGateway ai
+ class Gemini,GPT,Claude,Cerebras providers
+```
+
+## 9. Technology Stack Overview
+
+```mermaid
+graph TB
+ subgraph "Frontend Layer"
+ React[React 18]
+ Vite[Vite Build Tool]
+ TailwindCSS[Tailwind CSS]
+ ShadcnUI[shadcn/ui Components]
+ ReactRouter[React Router]
+ end
+
+ subgraph "Backend Layer"
+ CFWorkers[Cloudflare Workers]
+ AgentsSDK[Cloudflare Agents SDK]
+ TypeScript[TypeScript]
+ HonoRouter[Hono Router]
+ AuthMiddleware[Auth Middleware]
+ BaseController[Base Controller]
+ end
+
+ subgraph "Data Layer"
+ D1[Cloudflare D1 SQLite]
+ Drizzle[Drizzle ORM]
+ KV[Cloudflare KV]
+ R2[Cloudflare R2]
+ DatabaseService[Database Service]
+ UserService[User Service]
+ AppService[App Service]
+ AuthService[Auth Service]
+ end
+
+ subgraph "AI & External Services"
+ AIGateway[Cloudflare AI Gateway]
+ OpenAI[OpenAI GPT-4]
+ GitHubAPI[GitHub API]
+ OAuth[OAuth Providers]
+ end
+
+ subgraph "Infrastructure"
+ CFContainers[Cloudflare Containers]
+ SandboxSDK[Cloudflare Sandbox SDK]
+ WebSockets[WebSocket API]
+ WorkersAnalytics[Workers Analytics]
+ end
+
+ React --> CFWorkers
+ Vite --> React
+ TailwindCSS --> React
+ ShadcnUI --> React
+ ReactRouter --> React
+
+ CFWorkers --> AgentsSDK
+ TypeScript --> CFWorkers
+ HonoRouter --> CFWorkers
+ AuthMiddleware --> CFWorkers
+ BaseController --> CFWorkers
+
+ CFWorkers --> DatabaseService
+ DatabaseService --> Drizzle
+ Drizzle --> D1
+ DatabaseService --> UserService
+ DatabaseService --> AppService
+ DatabaseService --> AuthService
+ CFWorkers --> KV
+ CFWorkers --> R2
+
+ CFWorkers --> AIGateway
+ AIGateway --> OpenAI
+ CFWorkers --> GitHubAPI
+ CFWorkers --> OAuth
+
+ CFWorkers --> CFContainers
+ CFContainers --> SandboxSDK
+ CFWorkers --> WebSockets
+ CFWorkers --> WorkersAnalytics
+
+ style CFWorkers fill:#ff9900
+ style AgentsSDK fill:#ff9900
+ style D1 fill:#ff9900
+ style AIGateway fill:#ff9900
+ style CFContainers fill:#ff9900
+```
diff --git a/external/drpower-vibe-production/docs/setup.md b/external/drpower-vibe-production/docs/setup.md
new file mode 100644
index 00000000..c86d7590
--- /dev/null
+++ b/external/drpower-vibe-production/docs/setup.md
@@ -0,0 +1,405 @@
+# VibSDK Setup Guide
+
+Local first time setup guide for VibSDK - get your AI coding platform running locally and also ready to be deployed.
+
+## Prerequisites
+
+Before getting started, make sure you have:
+
+### Required
+- **Node.js** (v18 or later)
+- **Cloudflare account** with API access
+- **Cloudflare API Token** with appropriate permissions
+
+### Recommended
+- **Bun** (automatically installed by setup script for better performance)
+- **Custom domain** configured in Cloudflare (for production deployment)
+
+### For Production Features
+- **Workers Paid Plan** (for remote Cloudflare resources)
+- **Workers for Platforms** subscription (for app deployment features)
+- **Advanced Certificate Manager** (if using first-level subdomains)
+
+## Quick Start
+
+The fastest way to get VibSDK running is with our automated setup script:
+
+```bash
+npm run setup
+# Or if you already have Bun installed: bun run setup
+```
+
+This interactive script will guide you through the entire setup process, including:
+
+- **Package manager setup** (installs Bun automatically for better performance)
+- **Cloudflare credentials** collection (Account ID and API Token)
+- **Domain configuration** (custom domain or localhost for development)
+- **Remote setup** (optional production deployment configuration)
+- **AI Gateway configuration** (Cloudflare AI Gateway recommended)
+- **API key collection** (OpenAI, Anthropic, Google AI Studio, etc.)
+- **OAuth setup** (Google, GitHub login - optional)
+- **Resource creation** (KV namespaces, D1 databases, R2 buckets, AI Gateway)
+- **File generation** (`.dev.vars` and optionally `.prod.vars`)
+- **Configuration updates** (`wrangler.jsonc` and `vite.config.ts`)
+- **Database setup** (schema generation and migrations)
+- **Template deployment** (example app templates to R2)
+- **Readiness report** (comprehensive status and next steps)
+
+## What You'll Need During Setup
+
+The setup script will ask you for the following information:
+
+### Cloudflare Account Information
+
+1. **Account ID**: Found in your Cloudflare dashboard sidebar
+2. **API Token**: Create one with these permissions:
+ - **Account** - Account:Read
+ - **Zone** - Zone Settings:Edit, Zone:Edit, DNS:Edit (if using custom domain)
+ - **Account** - Workers KV Storage:Edit, D1:Edit, Workers Scripts:Edit, Workers AI:Edit
+ - **Account** - R2:Edit (for object storage)
+ - **Account** - Cloudflare Images:Edit (for image handling)
+ - **Account** - Account Rulesets:Edit (for rate limiting)
+
+ **Important**: Some features like D1 databases and R2 may require a paid Cloudflare plan.
+
+### Domain Configuration
+
+The script now uses a **simplified, upfront domain configuration**:
+
+**With Custom Domain:**
+```bash
+Enter your custom domain (or press Enter to skip): myapp.com
+โ
Custom domain set: myapp.com
+Use remote Cloudflare resources (KV, D1, R2, etc.)? (Y/n):
+Configure for production deployment? (Y/n):
+```
+
+**Without Custom Domain:**
+```bash
+Enter your custom domain (or press Enter to skip): [press Enter]
+โ ๏ธ No custom domain provided.
+ โข Remote Cloudflare resources: Not available
+ โข Production deployment: Not available
+ โข Only local development will be configured
+
+Continue with local-only setup? (Y/n):
+```
+
+**Benefits:**
+- **One-time decision**: Domain asked once, used for both dev and production
+- **Clear consequences**: Script explains what features are unavailable without domain
+- **Retry option**: Can go back if you change your mind
+- **Y/n defaults**: Capital letter shows default choice (press Enter)
+
+### AI Gateway Configuration
+
+**Cloudflare AI Gateway (Recommended)**
+- **Automatic token setup**: When selected, `CLOUDFLARE_AI_GATEWAY_TOKEN` is automatically set to your API token
+- **No manual configuration**: The script handles all AI Gateway authentication
+- **Better performance**: Caching, rate limiting, and monitoring included
+
+**Custom OpenAI URL (Alternative)**
+- For users with existing OpenAI-compatible endpoints
+- Requires manual model configuration in `config.ts`
+
+### AI Provider Selection
+
+The setup script offers multiple AI providers with intelligent multi-selection:
+
+**Available Providers:**
+1. **OpenAI** (for GPT models)
+2. **Anthropic** (for Claude models)
+3. **Google AI Studio** (for Gemini models) - **Default & Recommended**
+4. **Cerebras** (for open source models)
+5. **OpenRouter** (for various models)
+6. **Custom provider** (for any other provider)
+
+**Provider Selection:**
+- Select multiple providers with comma-separated numbers (e.g., `1,2,3`)
+- Each selected provider will prompt for its API key
+- Custom providers automatically generate `PROVIDER_NAME_API_KEY` variables
+- Custom providers are automatically added to `worker-configuration.d.ts`
+
+### Important Model Configuration Notes
+
+**Google AI Studio (Recommended):**
+- Default model configurations use Gemini models
+- No additional config.ts editing required
+- Best performance and compatibility
+
+**Other Providers:**
+- **Strong warning**: You MUST edit `worker/agents/inferutils/config.ts`
+- Change default model configurations from Gemini to your selected providers
+- Model format: `/` (e.g., `openai/gpt-4`, `anthropic/claude-3.5-sonnet`)
+- Review fallback model configurations
+
+**Without AI Gateway:**
+- **Manual config.ts editing required** for all model configurations
+- Model names must follow `/` format
+
+### OAuth Configuration
+
+The script will also ask for OAuth credentials:
+
+- **Google OAuth**: For user authentication and login (not AI Studio access)
+- **GitHub OAuth**: For user authentication and login
+- **GitHub Export OAuth**: For exporting generated apps to GitHub repositories (separate from login OAuth)
+
+## Manual Setup (Alternative)
+
+If you prefer to set up manually:
+
+### 1. Create `.dev.vars` file
+
+Copy `.dev.vars.example` to `.dev.vars` and fill in your values:
+
+```bash
+cp .dev.vars.example .dev.vars
+```
+
+### 2. Configure Required Variables
+
+```bash
+# Essential
+CLOUDFLARE_API_TOKEN="your-api-token"
+CLOUDFLARE_ACCOUNT_ID="your-account-id"
+
+# Security
+JWT_SECRET="generated-secret"
+WEBHOOK_SECRET="generated-secret"
+
+# Domain (optional)
+CUSTOM_DOMAIN="your-domain.com"
+```
+
+### 3. Create Cloudflare Resources
+
+Create required resources in your Cloudflare account:
+- KV Namespace for `VibecoderStore`
+- D1 Database named `vibesdk-db`
+- R2 Bucket named `vibesdk-templates`
+
+### 4. Update `wrangler.jsonc`
+
+Update resource IDs in `wrangler.jsonc` with the IDs from step 3.
+
+## Starting Development
+
+After setup is complete:
+
+```bash
+# Set up database
+npm run db:migrate:local
+
+# Start development server
+npm run dev
+```
+
+Visit your app at `http://localhost:5173`
+
+## Troubleshooting
+
+### Common Issues
+
+**D1 Database "Unauthorized" Error**: This usually means:
+- Your API token lacks "D1:Edit" permissions
+- Your account doesn't have access to D1 (may require paid plan)
+- You've exceeded your D1 database quota
+- **Solution**: Update your API token permissions or upgrade your Cloudflare plan
+
+**Permission Errors**: Ensure your API token has all required permissions listed above.
+
+**Domain Not Found**: Make sure your domain is:
+- Added to Cloudflare
+- DNS is properly configured
+- API token has zone permissions
+
+**Resource Creation Failed**: Check that your account has:
+- Available KV namespace quota (10 on free plan)
+- D1 database quota (may require paid plan)
+- R2 bucket quota (may require paid plan)
+- Appropriate plan level for requested features
+
+**R2 Bucket "Unauthorized" Error**: This usually means:
+- Your API token lacks "R2:Edit" permissions
+- Your account doesn't have access to R2 (may require paid plan)
+- You've exceeded your R2 bucket quota
+- **Solution**: Update your API token permissions or upgrade your Cloudflare plan
+
+**AI Configuration Issues**:
+- **"AI Gateway token already configured" but token not in .dev.vars**: Re-run setup, this was a bug that's now fixed
+- **Models not working with custom providers**: Edit `worker/agents/inferutils/config.ts` to change default model configurations
+- **Custom provider not recognized**: Check that the provider was added to `worker-configuration.d.ts`
+- **AI Gateway creation failed**: Ensure your API token has AI Gateway permissions
+
+**Local Development & Tunnel Issues**:
+- **Cloudflared tunnel timeout**: Wait 20-30 seconds, then refresh. Tunnel creation can be slow
+- **"Tunnel creation failed"**: This is normal occasionally. The app will still work with regular preview URLs
+- **Sandbox instances dying**: Normal behavior if they restart successfully. Only worry if persistent
+- **Preview URL not accessible**: Check if tunnel is still creating, or try refreshing the instance
+- **Multiple port exposure issues on macOS**: Use tunnels (`USE_TUNNEL_FOR_PREVIEW=true`) - this is the default
+
+**Deploy to Cloudflare Button Issues (Chat Interface)**:
+- **"Deploy button not working locally"**: Chat interface deploy button requires custom domain, initial deployment, and remote dispatch bindings
+- **"Dispatch namespace not found"**: Deploy your VibSDK project to Cloudflare at least once first
+- **"Deploy fails with authentication error"**: Ensure your custom domain is properly configured and deployed
+- **Note**: This refers to deploying generated apps from the chat interface, not GitHub repository deployments
+
+**Corporate Network Issues**:
+- **SSL/TLS certificate errors in Docker containers**: Corporate networks often use custom root CA certificates
+- **Cloudflared tunnel failures**: May be blocked by corporate proxies or require certificate trust
+- **Package installation failures**: npm/bun installs may fail due to certificate validation
+
+**Corporate Network Solutions**:
+If you're on a corporate network with custom SSL certificates, you'll need to modify the `SandboxDockerfile`:
+
+1. **Copy your corporate root CA certificate** to the project root (don't commit to git!)
+2. **Edit SandboxDockerfile** to include your certificate:
+
+```dockerfile
+# Add your company's Root CA certificate for corporate network access
+COPY your-root-ca.pem /usr/local/share/ca-certificates/your-root-ca.crt
+RUN update-ca-certificates
+
+# Set SSL environment variables for cloudflared and other tools
+ENV SSL_CERT_FILE=/etc/ssl/certs/ca-certificates.crt
+ENV NODE_EXTRA_CA_CERTS=/usr/local/share/ca-certificates/your-root-ca.crt
+ENV CURL_CA_BUNDLE=/etc/ssl/certs/ca-certificates.crt
+```
+
+**โ ๏ธ Security Warning**: Never commit corporate CA certificates to public repositories. Use `.gitignore` to exclude certificate files and only use this for local development.
+
+### Getting Help
+
+1. Check the setup report for specific issues and suggestions
+2. Review the Cloudflare Workers documentation
+3. Ensure all prerequisites are met
+
+## Production Deployment
+
+If you configured remote deployment during setup, you'll have a `.prod.vars` file ready for production. Deploy with:
+
+```bash
+npm run deploy
+```
+
+This will:
+- Build the application
+- Update Cloudflare resources
+- Deploy to Cloudflare Workers
+- Apply database migrations
+- Configure custom domain routing (if specified)
+
+### Production-Only Setup
+
+If you only set up for local development initially, you can configure production later:
+
+1. **Run setup again** and choose "yes" for remote deployment configuration
+2. **Provide production domain** when prompted
+3. **Deploy** using `npm run deploy`
+
+### Manual Production Setup
+
+Alternatively, create `.prod.vars` manually based on `.dev.vars` but with:
+- Production domain in `CUSTOM_DOMAIN`
+- Production API keys and secrets
+- `ENVIRONMENT="prod"`
+
+## Next Steps
+
+Once setup is complete:
+
+1. **Start developing** with `npm run dev`
+2. **Visit** `http://localhost:5173` to access VibSDK
+3. **Try generating** your first AI-powered application
+4. **Deploy to production** when ready with `npm run deploy`
+
+## File Structure After Setup
+
+The setup script creates and modifies these files:
+
+```
+vibesdk/
+โโโ .dev.vars # Local development environment variables
+โโโ .prod.vars # Production environment variables (if configured)
+โโโ wrangler.jsonc # Updated with resource IDs and domain
+โโโ vite.config.ts # Updated for remote/local bindings
+โโโ migrations/ # Database migration files
+โโโ templates/ # Template repository (downloaded)
+```
+
+## Summary
+
+The VibSDK setup script provides a comprehensive, intelligent configuration experience:
+
+### **Key Features:**
+- **Simplified domain setup** - One-time domain configuration with clear feature implications
+- **Intelligent AI provider selection** - Multi-provider support with automatic configuration
+- **AI Gateway automation** - Automatic token setup and configuration
+- **Custom provider support** - Dynamic API key generation and worker configuration updates
+- **Production-ready** - Both local development and production deployment configuration
+- **User-friendly defaults** - Y/n prompts with clear default indicators
+
+### **What Gets Configured:**
+- Cloudflare resources (KV, D1, R2, AI Gateway, dispatch namespaces)
+- Environment variables (.dev.vars and .prod.vars)
+- Worker configuration (wrangler.jsonc, worker-configuration.d.ts)
+- Database setup and migrations
+- Template deployment
+- ARM64 compatibility
+
+The setup script handles everything from basic Cloudflare resource creation to advanced AI provider configuration, making it easy to get started regardless of your Cloudflare plan or AI provider preferences.
+
+For any issues during setup, check the troubleshooting section above or refer to the comprehensive status report the script provides at the end.
+
+## Important Caveats & Known Issues
+
+### **Local Development with Cloudflared Tunnels**
+
+**Default Behavior**: Local development uses cloudflared tunnels by default (`USE_TUNNEL_FOR_PREVIEW=true`)
+
+**Why Tunnels?**
+- **MacBook compatibility**: Cloudflare sandbox SDK Docker images have issues with multiple exposed ports on macOS
+- **Simplified networking**: Avoids complex localhost proxying setup
+- **Quick development**: Provides immediate external access for testing
+
+**Tunnel Limitations**:
+- **Startup time**: Tunnel creation can take 10-20 seconds
+- **Timeouts**: Tunnel creation may timeout occasionally (this is normal)
+- **External dependency**: Requires internet connection and cloudflare.com access
+
+### **"Deploy to Cloudflare" Button Limitations (Chat Interface)**
+
+The "Deploy to Cloudflare" button in the chat interface (for generated apps) has specific requirements for local development:
+
+> **Note**: This refers to the deployment button within the VibSDK platform's chat interface, not the GitHub repository deploy button.
+
+**Requirements**:
+1. **Custom domain** must be properly configured during setup
+2. **Initial deployment** - Project must be deployed at least once to your Cloudflare account
+3. **Remote dispatch bindings** - `wrangler.jsonc` must have remote dispatch namespace enabled
+4. **Dispatch worker** - A dispatch worker must be running in your account
+
+**Why These Requirements?**
+- The deploy feature uses Cloudflare's dispatch namespace system
+- Dispatch requires a running worker in your account to handle deployment requests
+- Local-only development isn't yet supported for this in vibesdk
+
+**Current Status**: Making "Deploy to Cloudflare" work completely in local-only mode is not yet implemented.
+
+### **Sandbox Instance Behavior**
+
+**Normal Behavior**:
+- **Instance restarts**: Sandbox deployments may occasionally die and restart
+- **Temporary failures**: Short-term deployment failures are expected
+- **Self-healing**: The system will retry and recover automatically
+
+**When to Be Concerned**:
+- Consistent failures over 5+ minutes
+- Complete inability to create instances
+- Persistent networking issues
+
+**What's Normal**:
+- Individual instance failures that resolve quickly
+- Occasional tunnel connection issues
+- Brief periods of unavailability during restarts If issue persists, please open an issue on GitHub with the status report and any additional information you think would be helpful.
diff --git a/external/drpower-vibe-production/docs/v1dev-api-collection.postman_collection.json b/external/drpower-vibe-production/docs/v1dev-api-collection.postman_collection.json
new file mode 100644
index 00000000..cf29474c
--- /dev/null
+++ b/external/drpower-vibe-production/docs/v1dev-api-collection.postman_collection.json
@@ -0,0 +1,955 @@
+{
+ "info": {
+ "name": "V1 Dev API Collection - Complete",
+ "description": "Comprehensive API collection for V1 Dev platform - AI-powered webapp generator with Cloudflare Workers backend. Includes OAuth setup, CSRF protection, and all endpoints with proper authentication.",
+ "version": "1.0.0",
+ "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json"
+ },
+ "auth": {
+ "type": "noauth"
+ },
+ "event": [
+ {
+ "listen": "prerequest",
+ "script": {
+ "type": "text/javascript",
+ "exec": [
+ "// Auto-fetch CSRF token if not present",
+ "if (!pm.globals.get('csrf_token')) {",
+ " const getCsrfRequest = {",
+ " url: pm.environment.get('baseUrl') + '/api/auth/csrf-token',",
+ " method: 'GET',",
+ " header: {",
+ " 'Content-Type': 'application/json'",
+ " }",
+ " };",
+ " ",
+ " pm.sendRequest(getCsrfRequest, (error, response) => {",
+ " if (!error && response.json().success) {",
+ " pm.globals.set('csrf_token', response.json().data.token);",
+ " }",
+ " });",
+ "}"
+ ]
+ }
+ }
+ ],
+ "variable": [
+ {
+ "key": "baseUrl",
+ "value": "{{baseUrl}}",
+ "type": "string",
+ "description": "Base URL for the API"
+ },
+ {
+ "key": "version",
+ "value": "1.0.0",
+ "type": "string"
+ }
+ ],
+ "item": [
+ {
+ "name": "๐ฅ Health Check",
+ "description": "System health and status endpoints",
+ "item": [
+ {
+ "name": "Health Check",
+ "request": {
+ "method": "GET",
+ "header": [
+ {
+ "key": "Content-Type",
+ "value": "application/json"
+ }
+ ],
+ "url": {
+ "raw": "{{baseUrl}}/api/health",
+ "host": ["{{baseUrl}}"],
+ "path": ["api", "health"]
+ },
+ "description": "Check if the API is healthy and responding"
+ },
+ "response": []
+ }
+ ],
+ "description": "System health check endpoint"
+ },
+ {
+ "name": "๐ Authentication",
+ "description": "Authentication endpoints including OAuth, email auth, and session management",
+ "item": [
+ {
+ "name": "Get CSRF Token",
+ "event": [
+ {
+ "listen": "test",
+ "script": {
+ "exec": [
+ "if (pm.response.code === 200) {",
+ " const response = pm.response.json();",
+ " if (response.success && response.data.token) {",
+ " pm.globals.set('csrf_token', response.data.token);",
+ " pm.test('CSRF token saved', () => {",
+ " pm.expect(pm.globals.get('csrf_token')).to.not.be.undefined;",
+ " });",
+ " }",
+ "}",
+ "",
+ "pm.test('Status code is 200', () => {",
+ " pm.response.to.have.status(200);",
+ "});"
+ ],
+ "type": "text/javascript"
+ }
+ }
+ ],
+ "request": {
+ "method": "GET",
+ "header": [
+ {
+ "key": "Content-Type",
+ "value": "application/json"
+ }
+ ],
+ "url": {
+ "raw": "{{baseUrl}}/api/auth/csrf-token",
+ "host": ["{{baseUrl}}"],
+ "path": ["api", "auth", "csrf-token"]
+ },
+ "description": "Fetch CSRF token required for state-changing operations"
+ },
+ "response": []
+ },
+ {
+ "name": "Get Auth Providers",
+ "request": {
+ "method": "GET",
+ "header": [
+ {
+ "key": "Content-Type",
+ "value": "application/json"
+ }
+ ],
+ "url": {
+ "raw": "{{baseUrl}}/api/auth/providers",
+ "host": ["{{baseUrl}}"],
+ "path": ["api", "auth", "providers"]
+ },
+ "description": "Get available authentication providers (Google, GitHub, email)"
+ },
+ "response": []
+ },
+ {
+ "name": "Register User",
+ "event": [
+ {
+ "listen": "test",
+ "script": {
+ "exec": [
+ "if (pm.response.code === 200) {",
+ " const response = pm.response.json();",
+ " if (response.success && response.data.user) {",
+ " pm.globals.set('user_id', response.data.user.id);",
+ " pm.globals.set('session_id', response.data.sessionId);",
+ " }",
+ "}"
+ ],
+ "type": "text/javascript"
+ }
+ }
+ ],
+ "request": {
+ "method": "POST",
+ "header": [
+ {
+ "key": "Content-Type",
+ "value": "application/json"
+ },
+ {
+ "key": "X-CSRF-Token",
+ "value": "{{csrf_token}}"
+ }
+ ],
+ "body": {
+ "mode": "raw",
+ "raw": "{\n \"email\": \"user@example.com\",\n \"password\": \"SecurePassword123!\",\n \"name\": \"Test User\"\n}"
+ },
+ "url": {
+ "raw": "{{baseUrl}}/api/auth/register",
+ "host": ["{{baseUrl}}"],
+ "path": ["api", "auth", "register"]
+ },
+ "description": "Register a new user account"
+ },
+ "response": []
+ },
+ {
+ "name": "Login with Email",
+ "event": [
+ {
+ "listen": "test",
+ "script": {
+ "exec": [
+ "if (pm.response.code === 200) {",
+ " const response = pm.response.json();",
+ " if (response.success && response.data.user) {",
+ " pm.globals.set('user_id', response.data.user.id);",
+ " pm.globals.set('session_id', response.data.sessionId);",
+ " pm.test('User logged in successfully', () => {",
+ " pm.expect(response.data.user.email).to.not.be.undefined;",
+ " });",
+ " }",
+ "}",
+ "",
+ "pm.test('Status code is 200', () => {",
+ " pm.response.to.have.status(200);",
+ "});"
+ ],
+ "type": "text/javascript"
+ }
+ }
+ ],
+ "request": {
+ "method": "POST",
+ "header": [
+ {
+ "key": "Content-Type",
+ "value": "application/json"
+ },
+ {
+ "key": "X-CSRF-Token",
+ "value": "{{csrf_token}}"
+ }
+ ],
+ "body": {
+ "mode": "raw",
+ "raw": "{\n \"email\": \"user@example.com\",\n \"password\": \"SecurePassword123!\"\n}"
+ },
+ "url": {
+ "raw": "{{baseUrl}}/api/auth/login",
+ "host": ["{{baseUrl}}"],
+ "path": ["api", "auth", "login"]
+ },
+ "description": "Login with email and password"
+ },
+ "response": []
+ },
+ {
+ "name": "Get User Profile",
+ "request": {
+ "auth": {
+ "type": "noauth"
+ },
+ "method": "GET",
+ "header": [
+ {
+ "key": "Content-Type",
+ "value": "application/json"
+ }
+ ],
+ "url": {
+ "raw": "{{baseUrl}}/api/auth/profile",
+ "host": ["{{baseUrl}}"],
+ "path": ["api", "auth", "profile"]
+ },
+ "description": "Get current user profile information"
+ },
+ "response": []
+ },
+ {
+ "name": "OAuth - Google (Browser Only)",
+ "event": [
+ {
+ "listen": "test",
+ "script": {
+ "exec": [
+ "// OAuth endpoints redirect to providers - not suitable for direct Postman testing",
+ "// Use this URL in your browser instead:",
+ "console.log('Open this URL in browser:', pm.request.url.toString());",
+ "",
+ "// Set a simple test for the redirect response",
+ "pm.test('OAuth redirect received', () => {",
+ " pm.expect([200, 302, 301]).to.include(pm.response.code);",
+ "});"
+ ],
+ "type": "text/javascript"
+ }
+ }
+ ],
+ "request": {
+ "method": "GET",
+ "header": [],
+ "url": {
+ "raw": "{{baseUrl}}/api/auth/oauth/google",
+ "host": ["{{baseUrl}}"],
+ "path": ["api", "auth", "oauth", "google"],
+ "query": [
+ {
+ "key": "redirect_url",
+ "value": "{{baseUrl}}/dashboard",
+ "description": "Optional redirect URL after authentication",
+ "disabled": true
+ }
+ ]
+ },
+ "description": "โ ๏ธ IMPORTANT: This endpoint redirects to Google OAuth. Copy the URL and open it in your browser to complete authentication. Postman will show HTML content but won't follow redirects to Google."
+ },
+ "response": []
+ },
+ {
+ "name": "OAuth - GitHub (Browser Only)",
+ "event": [
+ {
+ "listen": "test",
+ "script": {
+ "exec": [
+ "// OAuth endpoints redirect to providers - not suitable for direct Postman testing",
+ "// Use this URL in your browser instead:",
+ "console.log('Open this URL in browser:', pm.request.url.toString());",
+ "",
+ "// Set a simple test for the redirect response",
+ "pm.test('OAuth redirect received', () => {",
+ " pm.expect([200, 302, 301]).to.include(pm.response.code);",
+ "});"
+ ],
+ "type": "text/javascript"
+ }
+ }
+ ],
+ "request": {
+ "method": "GET",
+ "header": [],
+ "url": {
+ "raw": "{{baseUrl}}/api/auth/oauth/github",
+ "host": ["{{baseUrl}}"],
+ "path": ["api", "auth", "oauth", "github"],
+ "query": [
+ {
+ "key": "redirect_url",
+ "value": "{{baseUrl}}/dashboard",
+ "description": "Optional redirect URL after authentication",
+ "disabled": true
+ }
+ ]
+ },
+ "description": "โ ๏ธ IMPORTANT: This endpoint redirects to GitHub OAuth. Copy the URL and open it in your browser to complete authentication. Postman will show HTML content but won't follow redirects to GitHub."
+ },
+ "response": []
+ },
+ {
+ "name": "๐ OAuth Helper - Get Google URL",
+ "event": [
+ {
+ "listen": "test",
+ "script": {
+ "exec": [
+ "// Extract the OAuth URL for manual browser use",
+ "const baseUrl = pm.environment.get('baseUrl');",
+ "const googleOAuthUrl = `${baseUrl}/api/auth/oauth/google`;",
+ "",
+ "console.log('========================================');",
+ "console.log('๐ COPY THIS URL TO YOUR BROWSER:');",
+ "console.log(googleOAuthUrl);",
+ "console.log('========================================');",
+ "",
+ "pm.test('OAuth URL generated', () => {",
+ " pm.expect(googleOAuthUrl).to.include('/api/auth/oauth/google');",
+ "});"
+ ],
+ "type": "text/javascript"
+ }
+ }
+ ],
+ "request": {
+ "method": "GET",
+ "header": [],
+ "url": {
+ "raw": "{{baseUrl}}/api/auth/providers",
+ "host": ["{{baseUrl}}"],
+ "path": ["api", "auth", "providers"]
+ },
+ "description": "๐ USE THIS: Generates OAuth URLs in the console. Check the 'Console' tab after running to get the browser URL for Google OAuth."
+ },
+ "response": []
+ },
+ {
+ "name": "๐ OAuth Helper - Get GitHub URL",
+ "event": [
+ {
+ "listen": "test",
+ "script": {
+ "exec": [
+ "// Extract the OAuth URL for manual browser use",
+ "const baseUrl = pm.environment.get('baseUrl');",
+ "const githubOAuthUrl = `${baseUrl}/api/auth/oauth/github`;",
+ "",
+ "console.log('========================================');",
+ "console.log('๐ COPY THIS URL TO YOUR BROWSER:');",
+ "console.log(githubOAuthUrl);",
+ "console.log('========================================');",
+ "",
+ "pm.test('OAuth URL generated', () => {",
+ " pm.expect(githubOAuthUrl).to.include('/api/auth/oauth/github');",
+ "});"
+ ],
+ "type": "text/javascript"
+ }
+ }
+ ],
+ "request": {
+ "method": "GET",
+ "header": [],
+ "url": {
+ "raw": "{{baseUrl}}/api/auth/providers",
+ "host": ["{{baseUrl}}"],
+ "path": ["api", "auth", "providers"]
+ },
+ "description": "๐ USE THIS: Generates OAuth URLs in the console. Check the 'Console' tab after running to get the browser URL for GitHub OAuth."
+ },
+ "response": []
+ },
+ {
+ "name": "Logout",
+ "request": {
+ "method": "POST",
+ "header": [
+ {
+ "key": "Content-Type",
+ "value": "application/json"
+ },
+ {
+ "key": "X-CSRF-Token",
+ "value": "{{csrf_token}}"
+ }
+ ],
+ "url": {
+ "raw": "{{baseUrl}}/api/auth/logout",
+ "host": ["{{baseUrl}}"],
+ "path": ["api", "auth", "logout"]
+ },
+ "description": "Logout current user session"
+ },
+ "response": []
+ }
+ ],
+ "description": "All authentication related endpoints",
+ "auth": {
+ "type": "noauth"
+ }
+ },
+ {
+ "name": "๐ค Agent & Code Generation",
+ "description": "AI agent endpoints for webapp generation and management",
+ "item": [
+ {
+ "name": "Start Code Generation",
+ "event": [
+ {
+ "listen": "test",
+ "script": {
+ "exec": [
+ "if (pm.response.code === 200) {",
+ " const response = pm.response.json();",
+ " if (response.success && response.data.agentId) {",
+ " pm.globals.set('agent_id', response.data.agentId);",
+ " pm.test('Agent ID saved', () => {",
+ " pm.expect(pm.globals.get('agent_id')).to.not.be.undefined;",
+ " });",
+ " }",
+ "}",
+ "",
+ "pm.test('Status code is 200', () => {",
+ " pm.response.to.have.status(200);",
+ "});"
+ ],
+ "type": "text/javascript"
+ }
+ }
+ ],
+ "request": {
+ "method": "POST",
+ "header": [
+ {
+ "key": "Content-Type",
+ "value": "application/json"
+ },
+ {
+ "key": "X-CSRF-Token",
+ "value": "{{csrf_token}}"
+ }
+ ],
+ "body": {
+ "mode": "raw",
+ "raw": "{\n \"query\": \"Create a React todo app with TypeScript and Tailwind CSS\",\n \"agentMode\": \"smart\",\n \"language\": \"typescript\",\n \"frameworks\": [\"react\", \"tailwindcss\"],\n \"selectedTemplate\": \"react-typescript\"\n}"
+ },
+ "url": {
+ "raw": "{{baseUrl}}/api/agent",
+ "host": ["{{baseUrl}}"],
+ "path": ["api", "agent"]
+ },
+ "description": "Start a new code generation session with AI agent"
+ },
+ "response": []
+ },
+ {
+ "name": "Get Agent State",
+ "request": {
+ "method": "GET",
+ "header": [
+ {
+ "key": "Content-Type",
+ "value": "application/json"
+ }
+ ],
+ "url": {
+ "raw": "{{baseUrl}}/api/agent/{{agent_id}}",
+ "host": ["{{baseUrl}}"],
+ "path": ["api", "agent", "{{agent_id}}"]
+ },
+ "description": "Get current state of an AI agent session"
+ },
+ "response": []
+ },
+ {
+ "name": "Deploy Preview",
+ "request": {
+ "method": "GET",
+ "header": [
+ {
+ "key": "Content-Type",
+ "value": "application/json"
+ }
+ ],
+ "url": {
+ "raw": "{{baseUrl}}/api/agent/{{agent_id}}/preview",
+ "host": ["{{baseUrl}}"],
+ "path": ["api", "agent", "{{agent_id}}", "preview"]
+ },
+ "description": "Deploy a preview of the generated app"
+ },
+ "response": []
+ }
+ ],
+ "description": "AI agent and code generation endpoints"
+ },
+ {
+ "name": "๐ฑ Apps Management",
+ "description": "App CRUD operations, public feed, favorites, and visibility management",
+ "item": [
+ {
+ "name": "Get Public Apps",
+ "request": {
+ "method": "GET",
+ "header": [
+ {
+ "key": "Content-Type",
+ "value": "application/json"
+ }
+ ],
+ "url": {
+ "raw": "{{baseUrl}}/api/apps/public?page=1&limit=20&sort=createdAt&order=desc",
+ "host": ["{{baseUrl}}"],
+ "path": ["api", "apps", "public"],
+ "query": [
+ {
+ "key": "page",
+ "value": "1",
+ "description": "Page number"
+ },
+ {
+ "key": "limit",
+ "value": "20",
+ "description": "Number of apps per page"
+ },
+ {
+ "key": "sort",
+ "value": "createdAt",
+ "description": "Sort field (createdAt, stars, views)"
+ },
+ {
+ "key": "order",
+ "value": "desc",
+ "description": "Sort order (asc, desc)"
+ }
+ ]
+ },
+ "description": "Get public apps feed with pagination and filtering (no auth required)"
+ },
+ "response": []
+ },
+ {
+ "name": "Get User Apps",
+ "request": {
+ "method": "GET",
+ "header": [
+ {
+ "key": "Content-Type",
+ "value": "application/json"
+ }
+ ],
+ "url": {
+ "raw": "{{baseUrl}}/api/apps",
+ "host": ["{{baseUrl}}"],
+ "path": ["api", "apps"]
+ },
+ "description": "Get current user's apps (requires authentication)"
+ },
+ "response": []
+ },
+ {
+ "name": "Get App Details",
+ "event": [
+ {
+ "listen": "test",
+ "script": {
+ "exec": [
+ "if (pm.response.code === 200) {",
+ " const response = pm.response.json();",
+ " if (response.success && response.data.app) {",
+ " pm.globals.set('app_id', response.data.app.id);",
+ " }",
+ "}"
+ ],
+ "type": "text/javascript"
+ }
+ }
+ ],
+ "request": {
+ "method": "GET",
+ "header": [
+ {
+ "key": "Content-Type",
+ "value": "application/json"
+ }
+ ],
+ "url": {
+ "raw": "{{baseUrl}}/api/apps/{{app_id}}",
+ "host": ["{{baseUrl}}"],
+ "path": ["api", "apps", "{{app_id}}"]
+ },
+ "description": "Get detailed app information (public access)"
+ },
+ "response": []
+ },
+ {
+ "name": "Toggle App Star",
+ "request": {
+ "method": "POST",
+ "header": [
+ {
+ "key": "Content-Type",
+ "value": "application/json"
+ },
+ {
+ "key": "X-CSRF-Token",
+ "value": "{{csrf_token}}"
+ }
+ ],
+ "url": {
+ "raw": "{{baseUrl}}/api/apps/{{app_id}}/star",
+ "host": ["{{baseUrl}}"],
+ "path": ["api", "apps", "{{app_id}}", "star"]
+ },
+ "description": "Toggle star/bookmark status on any public app"
+ },
+ "response": []
+ },
+ {
+ "name": "Fork App",
+ "request": {
+ "method": "POST",
+ "header": [
+ {
+ "key": "Content-Type",
+ "value": "application/json"
+ },
+ {
+ "key": "X-CSRF-Token",
+ "value": "{{csrf_token}}"
+ }
+ ],
+ "url": {
+ "raw": "{{baseUrl}}/api/apps/{{app_id}}/fork",
+ "host": ["{{baseUrl}}"],
+ "path": ["api", "apps", "{{app_id}}", "fork"]
+ },
+ "description": "Fork any public app to your account"
+ },
+ "response": []
+ }
+ ],
+ "description": "App management and interaction endpoints"
+ },
+ {
+ "name": "๐ค User Management",
+ "description": "User profile, apps, and account management",
+ "item": [
+ {
+ "name": "Get User Apps with Pagination",
+ "request": {
+ "method": "GET",
+ "header": [
+ {
+ "key": "Content-Type",
+ "value": "application/json"
+ }
+ ],
+ "url": {
+ "raw": "{{baseUrl}}/api/user/apps?page=1&limit=20&sort=createdAt&order=desc",
+ "host": ["{{baseUrl}}"],
+ "path": ["api", "user", "apps"],
+ "query": [
+ {
+ "key": "page",
+ "value": "1",
+ "description": "Page number"
+ },
+ {
+ "key": "limit",
+ "value": "20",
+ "description": "Items per page"
+ },
+ {
+ "key": "sort",
+ "value": "createdAt",
+ "description": "Sort field"
+ },
+ {
+ "key": "order",
+ "value": "desc",
+ "description": "Sort order"
+ }
+ ]
+ },
+ "description": "Get user's apps with advanced pagination and filtering"
+ },
+ "response": []
+ },
+ {
+ "name": "Update User Profile",
+ "request": {
+ "method": "PUT",
+ "header": [
+ {
+ "key": "Content-Type",
+ "value": "application/json"
+ },
+ {
+ "key": "X-CSRF-Token",
+ "value": "{{csrf_token}}"
+ }
+ ],
+ "body": {
+ "mode": "raw",
+ "raw": "{\n \"displayName\": \"John Doe\",\n \"username\": \"johndoe\",\n \"bio\": \"Full-stack developer passionate about AI and web technologies\",\n \"timezone\": \"America/New_York\",\n \"theme\": \"dark\"\n}"
+ },
+ "url": {
+ "raw": "{{baseUrl}}/api/user/profile",
+ "host": ["{{baseUrl}}"],
+ "path": ["api", "user", "profile"]
+ },
+ "description": "Update user profile information"
+ },
+ "response": []
+ }
+ ],
+ "description": "User profile and account management"
+ },
+ {
+ "name": "๐ Analytics & Stats",
+ "description": "User statistics, activity, and AI Gateway analytics",
+ "item": [
+ {
+ "name": "Get User Stats",
+ "request": {
+ "method": "GET",
+ "header": [
+ {
+ "key": "Content-Type",
+ "value": "application/json"
+ }
+ ],
+ "url": {
+ "raw": "{{baseUrl}}/api/stats",
+ "host": ["{{baseUrl}}"],
+ "path": ["api", "stats"]
+ },
+ "description": "Get user statistics (apps created, stars received, etc.)"
+ },
+ "response": []
+ },
+ {
+ "name": "Get User Analytics",
+ "request": {
+ "method": "GET",
+ "header": [
+ {
+ "key": "Content-Type",
+ "value": "application/json"
+ }
+ ],
+ "url": {
+ "raw": "{{baseUrl}}/api/user/{{user_id}}/analytics?days=30",
+ "host": ["{{baseUrl}}"],
+ "path": ["api", "user", "{{user_id}}", "analytics"],
+ "query": [
+ {
+ "key": "days",
+ "value": "30",
+ "description": "Number of days to fetch analytics for"
+ }
+ ]
+ },
+ "description": "Get AI Gateway analytics for user (costs, usage, etc.)"
+ },
+ "response": []
+ }
+ ],
+ "description": "Analytics and statistics endpoints"
+ },
+ {
+ "name": "๐ค Model Configuration",
+ "description": "AI model configuration and BYOK provider management",
+ "item": [
+ {
+ "name": "Get Model Configs",
+ "request": {
+ "method": "GET",
+ "header": [
+ {
+ "key": "Content-Type",
+ "value": "application/json"
+ }
+ ],
+ "url": {
+ "raw": "{{baseUrl}}/api/model-configs",
+ "host": ["{{baseUrl}}"],
+ "path": ["api", "model-configs"]
+ },
+ "description": "Get all user model configurations"
+ },
+ "response": []
+ },
+ {
+ "name": "Get BYOK Providers",
+ "request": {
+ "method": "GET",
+ "header": [
+ {
+ "key": "Content-Type",
+ "value": "application/json"
+ }
+ ],
+ "url": {
+ "raw": "{{baseUrl}}/api/model-configs/byok-providers",
+ "host": ["{{baseUrl}}"],
+ "path": ["api", "model-configs", "byok-providers"]
+ },
+ "description": "Get available BYOK (Bring Your Own Key) providers and models"
+ },
+ "response": []
+ },
+ {
+ "name": "Update Model Config",
+ "request": {
+ "method": "PUT",
+ "header": [
+ {
+ "key": "Content-Type",
+ "value": "application/json"
+ },
+ {
+ "key": "X-CSRF-Token",
+ "value": "{{csrf_token}}"
+ }
+ ],
+ "body": {
+ "mode": "raw",
+ "raw": "{\n \"modelName\": \"claude-3-5-sonnet-20241022\",\n \"maxTokens\": 4096,\n \"temperature\": 0.7,\n \"reasoningEffort\": \"medium\",\n \"fallbackModel\": \"gpt-4o\",\n \"isUserOverride\": true\n}"
+ },
+ "url": {
+ "raw": "{{baseUrl}}/api/model-configs/planner",
+ "host": ["{{baseUrl}}"],
+ "path": ["api", "model-configs", "planner"]
+ },
+ "description": "Update model configuration for specific agent action"
+ },
+ "response": []
+ }
+ ],
+ "description": "AI model configuration management"
+ },
+ {
+ "name": "๐ Secrets Management",
+ "description": "User secrets and API keys management with templates",
+ "item": [
+ {
+ "name": "Get All Secrets",
+ "request": {
+ "method": "GET",
+ "header": [
+ {
+ "key": "Content-Type",
+ "value": "application/json"
+ }
+ ],
+ "url": {
+ "raw": "{{baseUrl}}/api/secrets",
+ "host": ["{{baseUrl}}"],
+ "path": ["api", "secrets"]
+ },
+ "description": "Get all user secrets including inactive ones"
+ },
+ "response": []
+ },
+ {
+ "name": "Get Secret Templates",
+ "request": {
+ "method": "GET",
+ "header": [
+ {
+ "key": "Content-Type",
+ "value": "application/json"
+ }
+ ],
+ "url": {
+ "raw": "{{baseUrl}}/api/secrets/templates",
+ "host": ["{{baseUrl}}"],
+ "path": ["api", "secrets", "templates"]
+ },
+ "description": "Get available secret templates for easy setup"
+ },
+ "response": []
+ },
+ {
+ "name": "Store Secret",
+ "request": {
+ "method": "POST",
+ "header": [
+ {
+ "key": "Content-Type",
+ "value": "application/json"
+ },
+ {
+ "key": "X-CSRF-Token",
+ "value": "{{csrf_token}}"
+ }
+ ],
+ "body": {
+ "mode": "raw",
+ "raw": "{\n \"templateId\": \"openai_api_key\",\n \"name\": \"My OpenAI API Key\",\n \"envVarName\": \"OPENAI_API_KEY\",\n \"value\": \"sk-your-openai-api-key-here\",\n \"environment\": \"production\",\n \"description\": \"OpenAI API key for GPT-4 access\"\n}"
+ },
+ "url": {
+ "raw": "{{baseUrl}}/api/secrets",
+ "host": ["{{baseUrl}}"],
+ "path": ["api", "secrets"]
+ },
+ "description": "Store a new secret with optional template"
+ },
+ "response": []
+ }
+ ],
+ "description": "Secrets management for API keys and credentials"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/external/drpower-vibe-production/docs/v1dev-environment.postman_environment.json b/external/drpower-vibe-production/docs/v1dev-environment.postman_environment.json
new file mode 100644
index 00000000..19ee46e8
--- /dev/null
+++ b/external/drpower-vibe-production/docs/v1dev-environment.postman_environment.json
@@ -0,0 +1,70 @@
+{
+ "id": "v1dev-environment-uuid",
+ "name": "V1 Dev Environment",
+ "values": [
+ {
+ "key": "baseUrl",
+ "value": "https://your-production-domain.com",
+ "enabled": true,
+ "type": "default",
+ "description": "Base URL for V1 Dev API - Update this to your actual domain"
+ },
+ {
+ "key": "localUrl",
+ "value": "http://localhost:8787",
+ "enabled": false,
+ "type": "default",
+ "description": "Local development URL (Wrangler dev server)"
+ },
+ {
+ "key": "csrf_token",
+ "value": "",
+ "enabled": true,
+ "type": "secret",
+ "description": "CSRF token - automatically populated by requests"
+ },
+ {
+ "key": "user_id",
+ "value": "",
+ "enabled": true,
+ "type": "default",
+ "description": "Current user ID - automatically populated after login"
+ },
+ {
+ "key": "session_id",
+ "value": "",
+ "enabled": true,
+ "type": "secret",
+ "description": "Current session ID - automatically populated after login"
+ },
+ {
+ "key": "agent_id",
+ "value": "",
+ "enabled": true,
+ "type": "default",
+ "description": "Current agent/app ID - automatically populated when creating apps"
+ },
+ {
+ "key": "app_id",
+ "value": "",
+ "enabled": true,
+ "type": "default",
+ "description": "Current app ID - automatically populated when working with specific apps"
+ },
+ {
+ "key": "provider_id",
+ "value": "",
+ "enabled": true,
+ "type": "default",
+ "description": "Model provider ID - set this when testing provider endpoints"
+ },
+ {
+ "key": "secret_id",
+ "value": "",
+ "enabled": true,
+ "type": "default",
+ "description": "Secret ID - set this when testing secret endpoints"
+ }
+ ],
+ "_postman_variable_scope": "environment"
+}
\ No newline at end of file
diff --git a/external/drpower-vibe-production/drizzle.config.local.ts b/external/drpower-vibe-production/drizzle.config.local.ts
new file mode 100644
index 00000000..bb081874
--- /dev/null
+++ b/external/drpower-vibe-production/drizzle.config.local.ts
@@ -0,0 +1,26 @@
+import { defineConfig } from 'drizzle-kit';
+// import * as dotenv from 'dotenv';
+// import * as fs from 'fs';
+// import * as path from 'path';
+
+// // Load environment variables from .dev.vars
+// const devVarsPath = path.join(__dirname, '.dev.vars');
+// if (fs.existsSync(devVarsPath)) {
+// const devVars = fs.readFileSync(devVarsPath, 'utf-8');
+// const parsed = dotenv.parse(devVars);
+// Object.assign(process.env, parsed);
+// }
+
+export default defineConfig({
+ schema: './worker/database/schema.ts',
+ out: './migrations',
+ dialect: 'sqlite',
+ driver: 'd1-http',
+// dbCredentials: {
+// accountId: process.env.CLOUDFLARE_ACCOUNT_ID!,
+// token: process.env.CLOUDFLARE_D1_TOKEN!,
+// databaseId: process.env.CLOUDFLARE_D1_ID!,
+// },
+ verbose: true,
+ strict: true,
+});
diff --git a/external/drpower-vibe-production/drizzle.config.remote.ts b/external/drpower-vibe-production/drizzle.config.remote.ts
new file mode 100644
index 00000000..bb081874
--- /dev/null
+++ b/external/drpower-vibe-production/drizzle.config.remote.ts
@@ -0,0 +1,26 @@
+import { defineConfig } from 'drizzle-kit';
+// import * as dotenv from 'dotenv';
+// import * as fs from 'fs';
+// import * as path from 'path';
+
+// // Load environment variables from .dev.vars
+// const devVarsPath = path.join(__dirname, '.dev.vars');
+// if (fs.existsSync(devVarsPath)) {
+// const devVars = fs.readFileSync(devVarsPath, 'utf-8');
+// const parsed = dotenv.parse(devVars);
+// Object.assign(process.env, parsed);
+// }
+
+export default defineConfig({
+ schema: './worker/database/schema.ts',
+ out: './migrations',
+ dialect: 'sqlite',
+ driver: 'd1-http',
+// dbCredentials: {
+// accountId: process.env.CLOUDFLARE_ACCOUNT_ID!,
+// token: process.env.CLOUDFLARE_D1_TOKEN!,
+// databaseId: process.env.CLOUDFLARE_D1_ID!,
+// },
+ verbose: true,
+ strict: true,
+});
diff --git a/external/drpower-vibe-production/eslint.config.js b/external/drpower-vibe-production/eslint.config.js
new file mode 100644
index 00000000..6d7249a7
--- /dev/null
+++ b/external/drpower-vibe-production/eslint.config.js
@@ -0,0 +1,49 @@
+import js from '@eslint/js'
+import globals from 'globals'
+import reactHooks from 'eslint-plugin-react-hooks'
+import reactRefresh from 'eslint-plugin-react-refresh'
+import tseslint from 'typescript-eslint'
+import importPlugin from 'eslint-plugin-import'
+
+export default tseslint.config(
+ { ignores: ['dist', 'wrangler-configuration.d.ts', 'test-diff-formatters/**', '**/*.test.ts', '**/*.test.tsx', '**/*.spec.ts', '**/*.spec.tsx'] },
+ {
+ extends: [
+ js.configs.recommended,
+ ...tseslint.configs.recommended
+ ],
+ files: ['src/**/*.{ts,tsx}', 'worker/**/*.{ts,tsx}'],
+ languageOptions: {
+ ecmaVersion: 2022,
+ globals: globals.browser,
+ },
+ plugins: {
+ 'react-hooks': reactHooks,
+ 'react-refresh': reactRefresh,
+ },
+ rules: {
+ ...reactHooks.configs.recommended.rules,
+ 'react-hooks/exhaustive-deps': 'error',
+ '@typescript-eslint/no-explicit-any': 'off',
+ '@typescript-eslint/no-unused-vars': 'off',
+ 'react-refresh/only-export-components': [
+ 'warn',
+ { allowConstantExport: true },
+ ],
+ },
+ settings: {
+ 'import/resolver': {
+ typescript: true,
+ node: true,
+ },
+ },
+ },
+ // Disable react-refresh/only-export-components for UI components
+ // as shadcn/ui components commonly export both components and utilities
+ {
+ files: ['src/components/ui/**/*.{ts,tsx}'],
+ rules: {
+ 'react-refresh/only-export-components': 'off',
+ },
+ },
+)
diff --git a/external/drpower-vibe-production/index.html b/external/drpower-vibe-production/index.html
new file mode 100644
index 00000000..17c67793
--- /dev/null
+++ b/external/drpower-vibe-production/index.html
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
+
+
+ Build
+
+
+
+
+
+
+
diff --git a/external/drpower-vibe-production/knip.json b/external/drpower-vibe-production/knip.json
new file mode 100644
index 00000000..ad28ddda
--- /dev/null
+++ b/external/drpower-vibe-production/knip.json
@@ -0,0 +1,74 @@
+{
+ "$schema": "https://unpkg.com/knip@5/schema.json",
+ "workspaces": {
+ ".": {
+ "entry": [
+ "src/main.tsx",
+ "worker/index.ts",
+ "scripts/**/*.ts",
+ "container/**/*.ts",
+ "debug-tools/**/*.{ts,py}"
+ ],
+ "project": [
+ "**/*.{ts,tsx,js,jsx,mjs,cjs}"
+ ],
+ "ignore": [
+ "**/*.d.ts",
+ "dist/**",
+ "build/**",
+ ".wrangler/**",
+ "migrations/**/*.sql",
+ "public/**"
+ ],
+ "ignoreDependencies": [
+ "@rolldown/binding-linux-x64-gnu",
+ "prettier-plugin-tailwindcss",
+ "tw-animate-css",
+ "@tailwindcss/typography",
+ "@tailwindcss/vite"
+ ],
+ "ignoreBinaries": [
+ "wrangler",
+ "drizzle-kit",
+ "tsx",
+ "vitest"
+ ]
+ }
+ },
+ "rules": {
+ "files": "error",
+ "dependencies": "error",
+ "devDependencies": "error",
+ "optionalPeerDependencies": "warn",
+ "unlisted": "error",
+ "binaries": "error",
+ "unresolved": "error",
+ "exports": "error",
+ "types": "error",
+ "nsExports": "error",
+ "duplicates": "warn",
+ "enumMembers": "error",
+ "classMembers": "error"
+ },
+ "exclude": [
+ "unresolved"
+ ],
+ "ignoreExportsUsedInFile": true,
+ "typescript": {
+ "config": ["tsconfig.json", "tsconfig.*.json"]
+ },
+ "vite": {
+ "config": ["vite.config.ts"]
+ },
+ "eslint": {
+ "config": ["eslint.config.js"]
+ },
+ "vitest": {
+ "config": ["vitest.config.ts"],
+ "entry": ["**/*.{test,spec}.{js,jsx,ts,tsx}"]
+ },
+ "jest": {
+ "config": ["jest.config.{js,ts,mjs,cjs,json}"],
+ "entry": ["**/__tests__/**", "**/*.{test,spec}.{js,jsx,ts,tsx}"]
+ }
+}
diff --git a/external/drpower-vibe-production/migrations/0000_living_forge.sql b/external/drpower-vibe-production/migrations/0000_living_forge.sql
new file mode 100644
index 00000000..264cb39c
--- /dev/null
+++ b/external/drpower-vibe-production/migrations/0000_living_forge.sql
@@ -0,0 +1,358 @@
+CREATE TABLE `api_keys` (
+ `id` text PRIMARY KEY NOT NULL,
+ `user_id` text NOT NULL,
+ `name` text NOT NULL,
+ `key_hash` text NOT NULL,
+ `key_preview` text NOT NULL,
+ `scopes` text NOT NULL,
+ `is_active` integer DEFAULT true,
+ `last_used` integer,
+ `request_count` integer DEFAULT 0,
+ `expires_at` integer,
+ `created_at` integer DEFAULT CURRENT_TIMESTAMP,
+ `updated_at` integer DEFAULT CURRENT_TIMESTAMP,
+ FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE cascade
+);
+--> statement-breakpoint
+CREATE UNIQUE INDEX `api_keys_key_hash_unique` ON `api_keys` (`key_hash`);--> statement-breakpoint
+CREATE INDEX `api_keys_user_id_idx` ON `api_keys` (`user_id`);--> statement-breakpoint
+CREATE INDEX `api_keys_key_hash_idx` ON `api_keys` (`key_hash`);--> statement-breakpoint
+CREATE INDEX `api_keys_is_active_idx` ON `api_keys` (`is_active`);--> statement-breakpoint
+CREATE INDEX `api_keys_expires_at_idx` ON `api_keys` (`expires_at`);--> statement-breakpoint
+CREATE TABLE `app_comments` (
+ `id` text PRIMARY KEY NOT NULL,
+ `app_id` text NOT NULL,
+ `user_id` text NOT NULL,
+ `content` text NOT NULL,
+ `parent_comment_id` text,
+ `is_edited` integer DEFAULT false,
+ `is_deleted` integer DEFAULT false,
+ `created_at` integer DEFAULT CURRENT_TIMESTAMP,
+ `updated_at` integer DEFAULT CURRENT_TIMESTAMP,
+ FOREIGN KEY (`app_id`) REFERENCES `apps`(`id`) ON UPDATE no action ON DELETE cascade,
+ FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE cascade
+);
+--> statement-breakpoint
+CREATE INDEX `app_comments_app_idx` ON `app_comments` (`app_id`);--> statement-breakpoint
+CREATE INDEX `app_comments_user_idx` ON `app_comments` (`user_id`);--> statement-breakpoint
+CREATE INDEX `app_comments_parent_idx` ON `app_comments` (`parent_comment_id`);--> statement-breakpoint
+CREATE TABLE `app_likes` (
+ `id` text PRIMARY KEY NOT NULL,
+ `app_id` text NOT NULL,
+ `user_id` text NOT NULL,
+ `reaction_type` text DEFAULT 'like' NOT NULL,
+ `created_at` integer DEFAULT CURRENT_TIMESTAMP,
+ FOREIGN KEY (`app_id`) REFERENCES `apps`(`id`) ON UPDATE no action ON DELETE cascade,
+ FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE cascade
+);
+--> statement-breakpoint
+CREATE UNIQUE INDEX `app_likes_app_user_idx` ON `app_likes` (`app_id`,`user_id`);--> statement-breakpoint
+CREATE INDEX `app_likes_user_idx` ON `app_likes` (`user_id`);--> statement-breakpoint
+CREATE TABLE `app_views` (
+ `id` text PRIMARY KEY NOT NULL,
+ `app_id` text NOT NULL,
+ `user_id` text,
+ `session_token` text,
+ `ip_address_hash` text,
+ `referrer` text,
+ `user_agent` text,
+ `device_type` text,
+ `viewed_at` integer DEFAULT CURRENT_TIMESTAMP,
+ `duration_seconds` integer,
+ FOREIGN KEY (`app_id`) REFERENCES `apps`(`id`) ON UPDATE no action ON DELETE cascade,
+ FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE cascade
+);
+--> statement-breakpoint
+CREATE INDEX `app_views_app_idx` ON `app_views` (`app_id`);--> statement-breakpoint
+CREATE INDEX `app_views_user_idx` ON `app_views` (`user_id`);--> statement-breakpoint
+CREATE INDEX `app_views_viewed_at_idx` ON `app_views` (`viewed_at`);--> statement-breakpoint
+CREATE TABLE `apps` (
+ `id` text PRIMARY KEY NOT NULL,
+ `title` text NOT NULL,
+ `description` text,
+ `icon_url` text,
+ `original_prompt` text NOT NULL,
+ `final_prompt` text,
+ `blueprint` text,
+ `framework` text,
+ `user_id` text,
+ `session_token` text,
+ `visibility` text DEFAULT 'private' NOT NULL,
+ `status` text DEFAULT 'generating' NOT NULL,
+ `deployment_url` text,
+ `github_repository_url` text,
+ `github_repository_visibility` text,
+ `is_archived` integer DEFAULT false,
+ `is_featured` integer DEFAULT false,
+ `version` integer DEFAULT 1,
+ `parent_app_id` text,
+ `screenshot_url` text,
+ `screenshot_captured_at` integer,
+ `created_at` integer DEFAULT CURRENT_TIMESTAMP,
+ `updated_at` integer DEFAULT CURRENT_TIMESTAMP,
+ `last_deployed_at` integer,
+ FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE cascade
+);
+--> statement-breakpoint
+CREATE INDEX `apps_user_idx` ON `apps` (`user_id`);--> statement-breakpoint
+CREATE INDEX `apps_status_idx` ON `apps` (`status`);--> statement-breakpoint
+CREATE INDEX `apps_visibility_idx` ON `apps` (`visibility`);--> statement-breakpoint
+CREATE INDEX `apps_session_token_idx` ON `apps` (`session_token`);--> statement-breakpoint
+CREATE INDEX `apps_parent_app_idx` ON `apps` (`parent_app_id`);--> statement-breakpoint
+CREATE INDEX `apps_search_idx` ON `apps` (`title`,`description`);--> statement-breakpoint
+CREATE INDEX `apps_framework_status_idx` ON `apps` (`framework`,`status`);--> statement-breakpoint
+CREATE INDEX `apps_visibility_status_idx` ON `apps` (`visibility`,`status`);--> statement-breakpoint
+CREATE INDEX `apps_created_at_idx` ON `apps` (`created_at`);--> statement-breakpoint
+CREATE INDEX `apps_updated_at_idx` ON `apps` (`updated_at`);--> statement-breakpoint
+CREATE TABLE `audit_logs` (
+ `id` text PRIMARY KEY NOT NULL,
+ `user_id` text,
+ `entity_type` text NOT NULL,
+ `entity_id` text NOT NULL,
+ `action` text NOT NULL,
+ `old_values` text,
+ `new_values` text,
+ `ip_address` text,
+ `user_agent` text,
+ `created_at` integer DEFAULT CURRENT_TIMESTAMP,
+ FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE set null
+);
+--> statement-breakpoint
+CREATE INDEX `audit_logs_user_idx` ON `audit_logs` (`user_id`);--> statement-breakpoint
+CREATE INDEX `audit_logs_entity_idx` ON `audit_logs` (`entity_type`,`entity_id`);--> statement-breakpoint
+CREATE INDEX `audit_logs_created_at_idx` ON `audit_logs` (`created_at`);--> statement-breakpoint
+CREATE TABLE `auth_attempts` (
+ `id` integer PRIMARY KEY AUTOINCREMENT NOT NULL,
+ `identifier` text NOT NULL,
+ `attempt_type` text NOT NULL,
+ `success` integer NOT NULL,
+ `ip_address` text NOT NULL,
+ `user_agent` text,
+ `attempted_at` integer DEFAULT CURRENT_TIMESTAMP
+);
+--> statement-breakpoint
+CREATE INDEX `auth_attempts_lookup_idx` ON `auth_attempts` (`identifier`,`attempted_at`);--> statement-breakpoint
+CREATE INDEX `auth_attempts_ip_idx` ON `auth_attempts` (`ip_address`,`attempted_at`);--> statement-breakpoint
+CREATE INDEX `auth_attempts_success_idx` ON `auth_attempts` (`success`,`attempted_at`);--> statement-breakpoint
+CREATE INDEX `auth_attempts_type_idx` ON `auth_attempts` (`attempt_type`,`attempted_at`);--> statement-breakpoint
+CREATE TABLE `comment_likes` (
+ `id` text PRIMARY KEY NOT NULL,
+ `comment_id` text NOT NULL,
+ `user_id` text NOT NULL,
+ `reaction_type` text DEFAULT 'like' NOT NULL,
+ `created_at` integer DEFAULT CURRENT_TIMESTAMP,
+ FOREIGN KEY (`comment_id`) REFERENCES `app_comments`(`id`) ON UPDATE no action ON DELETE cascade,
+ FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE cascade
+);
+--> statement-breakpoint
+CREATE UNIQUE INDEX `comment_likes_comment_user_idx` ON `comment_likes` (`comment_id`,`user_id`);--> statement-breakpoint
+CREATE INDEX `comment_likes_user_idx` ON `comment_likes` (`user_id`);--> statement-breakpoint
+CREATE INDEX `comment_likes_comment_idx` ON `comment_likes` (`comment_id`);--> statement-breakpoint
+CREATE TABLE `email_verification_tokens` (
+ `id` text PRIMARY KEY NOT NULL,
+ `user_id` text NOT NULL,
+ `token_hash` text NOT NULL,
+ `email` text NOT NULL,
+ `expires_at` integer NOT NULL,
+ `used` integer DEFAULT false,
+ `created_at` integer DEFAULT CURRENT_TIMESTAMP,
+ FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE cascade
+);
+--> statement-breakpoint
+CREATE UNIQUE INDEX `email_verification_tokens_token_hash_unique` ON `email_verification_tokens` (`token_hash`);--> statement-breakpoint
+CREATE INDEX `email_verification_tokens_lookup_idx` ON `email_verification_tokens` (`token_hash`);--> statement-breakpoint
+CREATE INDEX `email_verification_tokens_expiry_idx` ON `email_verification_tokens` (`expires_at`);--> statement-breakpoint
+CREATE TABLE `favorites` (
+ `id` text PRIMARY KEY NOT NULL,
+ `user_id` text NOT NULL,
+ `app_id` text NOT NULL,
+ `created_at` integer DEFAULT CURRENT_TIMESTAMP,
+ FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE cascade,
+ FOREIGN KEY (`app_id`) REFERENCES `apps`(`id`) ON UPDATE no action ON DELETE cascade
+);
+--> statement-breakpoint
+CREATE UNIQUE INDEX `favorites_user_app_idx` ON `favorites` (`user_id`,`app_id`);--> statement-breakpoint
+CREATE INDEX `favorites_user_idx` ON `favorites` (`user_id`);--> statement-breakpoint
+CREATE INDEX `favorites_app_idx` ON `favorites` (`app_id`);--> statement-breakpoint
+CREATE TABLE `oauth_states` (
+ `id` text PRIMARY KEY NOT NULL,
+ `state` text NOT NULL,
+ `provider` text NOT NULL,
+ `redirect_uri` text,
+ `scopes` text DEFAULT '[]',
+ `user_id` text,
+ `code_verifier` text,
+ `nonce` text,
+ `created_at` integer DEFAULT CURRENT_TIMESTAMP,
+ `expires_at` integer NOT NULL,
+ `is_used` integer DEFAULT false,
+ FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE no action
+);
+--> statement-breakpoint
+CREATE UNIQUE INDEX `oauth_states_state_unique` ON `oauth_states` (`state`);--> statement-breakpoint
+CREATE UNIQUE INDEX `oauth_states_state_idx` ON `oauth_states` (`state`);--> statement-breakpoint
+CREATE INDEX `oauth_states_expires_at_idx` ON `oauth_states` (`expires_at`);--> statement-breakpoint
+CREATE TABLE `password_reset_tokens` (
+ `id` text PRIMARY KEY NOT NULL,
+ `user_id` text NOT NULL,
+ `token_hash` text NOT NULL,
+ `expires_at` integer NOT NULL,
+ `used` integer DEFAULT false,
+ `created_at` integer DEFAULT CURRENT_TIMESTAMP,
+ FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE cascade
+);
+--> statement-breakpoint
+CREATE UNIQUE INDEX `password_reset_tokens_token_hash_unique` ON `password_reset_tokens` (`token_hash`);--> statement-breakpoint
+CREATE INDEX `password_reset_tokens_lookup_idx` ON `password_reset_tokens` (`token_hash`);--> statement-breakpoint
+CREATE INDEX `password_reset_tokens_expiry_idx` ON `password_reset_tokens` (`expires_at`);--> statement-breakpoint
+CREATE TABLE `sessions` (
+ `id` text PRIMARY KEY NOT NULL,
+ `user_id` text NOT NULL,
+ `device_info` text,
+ `user_agent` text,
+ `ip_address` text,
+ `is_revoked` integer DEFAULT false,
+ `revoked_at` integer,
+ `revoked_reason` text,
+ `access_token_hash` text NOT NULL,
+ `refresh_token_hash` text NOT NULL,
+ `expires_at` integer NOT NULL,
+ `created_at` integer DEFAULT CURRENT_TIMESTAMP,
+ `last_activity` integer,
+ FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE cascade
+);
+--> statement-breakpoint
+CREATE INDEX `sessions_user_id_idx` ON `sessions` (`user_id`);--> statement-breakpoint
+CREATE INDEX `sessions_expires_at_idx` ON `sessions` (`expires_at`);--> statement-breakpoint
+CREATE INDEX `sessions_access_token_hash_idx` ON `sessions` (`access_token_hash`);--> statement-breakpoint
+CREATE INDEX `sessions_refresh_token_hash_idx` ON `sessions` (`refresh_token_hash`);--> statement-breakpoint
+CREATE INDEX `sessions_last_activity_idx` ON `sessions` (`last_activity`);--> statement-breakpoint
+CREATE INDEX `sessions_is_revoked_idx` ON `sessions` (`is_revoked`);--> statement-breakpoint
+CREATE TABLE `stars` (
+ `id` text PRIMARY KEY NOT NULL,
+ `user_id` text NOT NULL,
+ `app_id` text NOT NULL,
+ `starred_at` integer DEFAULT CURRENT_TIMESTAMP,
+ FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE cascade,
+ FOREIGN KEY (`app_id`) REFERENCES `apps`(`id`) ON UPDATE no action ON DELETE cascade
+);
+--> statement-breakpoint
+CREATE UNIQUE INDEX `stars_user_app_idx` ON `stars` (`user_id`,`app_id`);--> statement-breakpoint
+CREATE INDEX `stars_user_idx` ON `stars` (`user_id`);--> statement-breakpoint
+CREATE INDEX `stars_app_idx` ON `stars` (`app_id`);--> statement-breakpoint
+CREATE TABLE `system_settings` (
+ `id` text PRIMARY KEY NOT NULL,
+ `key` text NOT NULL,
+ `value` text,
+ `description` text,
+ `updated_at` integer DEFAULT CURRENT_TIMESTAMP,
+ `updated_by` text,
+ FOREIGN KEY (`updated_by`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE no action
+);
+--> statement-breakpoint
+CREATE UNIQUE INDEX `system_settings_key_unique` ON `system_settings` (`key`);--> statement-breakpoint
+CREATE UNIQUE INDEX `system_settings_key_idx` ON `system_settings` (`key`);--> statement-breakpoint
+CREATE TABLE `user_model_configs` (
+ `id` text PRIMARY KEY NOT NULL,
+ `user_id` text NOT NULL,
+ `agent_action_name` text NOT NULL,
+ `model_name` text,
+ `max_tokens` integer,
+ `temperature` real,
+ `reasoning_effort` text,
+ `provider_override` text,
+ `fallback_model` text,
+ `is_active` integer DEFAULT true,
+ `created_at` integer DEFAULT CURRENT_TIMESTAMP,
+ `updated_at` integer DEFAULT CURRENT_TIMESTAMP,
+ FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE cascade
+);
+--> statement-breakpoint
+CREATE UNIQUE INDEX `user_model_configs_user_agent_idx` ON `user_model_configs` (`user_id`,`agent_action_name`);--> statement-breakpoint
+CREATE INDEX `user_model_configs_user_idx` ON `user_model_configs` (`user_id`);--> statement-breakpoint
+CREATE INDEX `user_model_configs_is_active_idx` ON `user_model_configs` (`is_active`);--> statement-breakpoint
+CREATE TABLE `user_model_providers` (
+ `id` text PRIMARY KEY NOT NULL,
+ `user_id` text NOT NULL,
+ `name` text NOT NULL,
+ `base_url` text NOT NULL,
+ `secret_id` text,
+ `is_active` integer DEFAULT true,
+ `created_at` integer DEFAULT CURRENT_TIMESTAMP,
+ `updated_at` integer DEFAULT CURRENT_TIMESTAMP,
+ FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE cascade,
+ FOREIGN KEY (`secret_id`) REFERENCES `user_secrets`(`id`) ON UPDATE no action ON DELETE no action
+);
+--> statement-breakpoint
+CREATE UNIQUE INDEX `user_model_providers_user_name_idx` ON `user_model_providers` (`user_id`,`name`);--> statement-breakpoint
+CREATE INDEX `user_model_providers_user_idx` ON `user_model_providers` (`user_id`);--> statement-breakpoint
+CREATE INDEX `user_model_providers_is_active_idx` ON `user_model_providers` (`is_active`);--> statement-breakpoint
+CREATE TABLE `user_secrets` (
+ `id` text PRIMARY KEY NOT NULL,
+ `user_id` text NOT NULL,
+ `name` text NOT NULL,
+ `provider` text NOT NULL,
+ `secret_type` text NOT NULL,
+ `encrypted_value` text NOT NULL,
+ `key_preview` text NOT NULL,
+ `description` text,
+ `expires_at` integer,
+ `last_used` integer,
+ `usage_count` integer DEFAULT 0,
+ `is_active` integer DEFAULT true,
+ `created_at` integer DEFAULT CURRENT_TIMESTAMP,
+ `updated_at` integer DEFAULT CURRENT_TIMESTAMP,
+ FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE cascade
+);
+--> statement-breakpoint
+CREATE INDEX `user_secrets_user_idx` ON `user_secrets` (`user_id`);--> statement-breakpoint
+CREATE INDEX `user_secrets_provider_idx` ON `user_secrets` (`provider`);--> statement-breakpoint
+CREATE INDEX `user_secrets_user_provider_idx` ON `user_secrets` (`user_id`,`provider`,`secret_type`);--> statement-breakpoint
+CREATE INDEX `user_secrets_active_idx` ON `user_secrets` (`is_active`);--> statement-breakpoint
+CREATE TABLE `users` (
+ `id` text PRIMARY KEY NOT NULL,
+ `email` text NOT NULL,
+ `username` text,
+ `display_name` text NOT NULL,
+ `avatar_url` text,
+ `bio` text,
+ `provider` text NOT NULL,
+ `provider_id` text NOT NULL,
+ `email_verified` integer DEFAULT false,
+ `password_hash` text,
+ `failed_login_attempts` integer DEFAULT 0,
+ `locked_until` integer,
+ `password_changed_at` integer,
+ `preferences` text DEFAULT '{}',
+ `theme` text DEFAULT 'system',
+ `timezone` text DEFAULT 'UTC',
+ `is_active` integer DEFAULT true,
+ `is_suspended` integer DEFAULT false,
+ `created_at` integer DEFAULT CURRENT_TIMESTAMP,
+ `updated_at` integer DEFAULT CURRENT_TIMESTAMP,
+ `last_active_at` integer,
+ `deleted_at` integer
+);
+--> statement-breakpoint
+CREATE UNIQUE INDEX `users_email_unique` ON `users` (`email`);--> statement-breakpoint
+CREATE UNIQUE INDEX `users_username_unique` ON `users` (`username`);--> statement-breakpoint
+CREATE INDEX `users_email_idx` ON `users` (`email`);--> statement-breakpoint
+CREATE UNIQUE INDEX `users_provider_unique_idx` ON `users` (`provider`,`provider_id`);--> statement-breakpoint
+CREATE INDEX `users_username_idx` ON `users` (`username`);--> statement-breakpoint
+CREATE INDEX `users_failed_login_attempts_idx` ON `users` (`failed_login_attempts`);--> statement-breakpoint
+CREATE INDEX `users_locked_until_idx` ON `users` (`locked_until`);--> statement-breakpoint
+CREATE INDEX `users_is_active_idx` ON `users` (`is_active`);--> statement-breakpoint
+CREATE INDEX `users_last_active_at_idx` ON `users` (`last_active_at`);--> statement-breakpoint
+CREATE TABLE `verification_otps` (
+ `id` text PRIMARY KEY NOT NULL,
+ `email` text NOT NULL,
+ `otp` text NOT NULL,
+ `expires_at` integer NOT NULL,
+ `used` integer DEFAULT false,
+ `used_at` integer,
+ `created_at` integer DEFAULT CURRENT_TIMESTAMP
+);
+--> statement-breakpoint
+CREATE INDEX `verification_otps_email_idx` ON `verification_otps` (`email`);--> statement-breakpoint
+CREATE INDEX `verification_otps_expires_at_idx` ON `verification_otps` (`expires_at`);--> statement-breakpoint
+CREATE INDEX `verification_otps_used_idx` ON `verification_otps` (`used`);
\ No newline at end of file
diff --git a/external/drpower-vibe-production/migrations/0001_married_moondragon.sql b/external/drpower-vibe-production/migrations/0001_married_moondragon.sql
new file mode 100644
index 00000000..f02838e3
--- /dev/null
+++ b/external/drpower-vibe-production/migrations/0001_married_moondragon.sql
@@ -0,0 +1 @@
+ALTER TABLE `apps` DROP COLUMN `blueprint`;
\ No newline at end of file
diff --git a/external/drpower-vibe-production/migrations/0002_nebulous_fantastic_four.sql b/external/drpower-vibe-production/migrations/0002_nebulous_fantastic_four.sql
new file mode 100644
index 00000000..40224f56
--- /dev/null
+++ b/external/drpower-vibe-production/migrations/0002_nebulous_fantastic_four.sql
@@ -0,0 +1,22 @@
+-- Migration to replace deploymentUrl with deploymentId
+-- For URLs like https://., extract the deploymentId
+
+-- Step 1: Add the new deploymentId column
+ALTER TABLE `apps` ADD COLUMN `deployment_id` TEXT;
+
+-- Step 2: Update existing rows to populate deploymentId from deploymentUrl
+-- Extract the substring between 'https://' and the first '.'
+UPDATE `apps`
+SET `deployment_id` = CASE
+ WHEN `deployment_url` IS NOT NULL AND `deployment_url` != ''
+ THEN substr(
+ `deployment_url`,
+ 9, -- Skip 'https://' (8 chars + 1)
+ instr(substr(`deployment_url`, 9), '.') - 1 -- Find position of first '.' after 'https://'
+ )
+ ELSE NULL
+END
+WHERE `deployment_url` IS NOT NULL;
+
+-- Step 3: Drop the old deploymentUrl column
+ALTER TABLE `apps` DROP COLUMN `deployment_url`;
diff --git a/external/drpower-vibe-production/migrations/0003_bumpy_albert_cleary.sql b/external/drpower-vibe-production/migrations/0003_bumpy_albert_cleary.sql
new file mode 100644
index 00000000..b2fd07dc
--- /dev/null
+++ b/external/drpower-vibe-production/migrations/0003_bumpy_albert_cleary.sql
@@ -0,0 +1,2 @@
+CREATE INDEX `app_views_app_viewed_at_idx` ON `app_views` (`app_id`,`viewed_at`);--> statement-breakpoint
+CREATE INDEX `stars_app_starred_at_idx` ON `stars` (`app_id`,`starred_at`);
\ No newline at end of file
diff --git a/external/drpower-vibe-production/migrations/meta/0000_snapshot.json b/external/drpower-vibe-production/migrations/meta/0000_snapshot.json
new file mode 100644
index 00000000..db4923c1
--- /dev/null
+++ b/external/drpower-vibe-production/migrations/meta/0000_snapshot.json
@@ -0,0 +1,2557 @@
+{
+ "version": "6",
+ "dialect": "sqlite",
+ "id": "c41950c7-1162-4690-ae3f-072e69e53375",
+ "prevId": "00000000-0000-0000-0000-000000000000",
+ "tables": {
+ "api_keys": {
+ "name": "api_keys",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "key_hash": {
+ "name": "key_hash",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "key_preview": {
+ "name": "key_preview",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "scopes": {
+ "name": "scopes",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "is_active": {
+ "name": "is_active",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false,
+ "default": true
+ },
+ "last_used": {
+ "name": "last_used",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "request_count": {
+ "name": "request_count",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false,
+ "default": 0
+ },
+ "expires_at": {
+ "name": "expires_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false,
+ "default": "CURRENT_TIMESTAMP"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false,
+ "default": "CURRENT_TIMESTAMP"
+ }
+ },
+ "indexes": {
+ "api_keys_key_hash_unique": {
+ "name": "api_keys_key_hash_unique",
+ "columns": [
+ "key_hash"
+ ],
+ "isUnique": true
+ },
+ "api_keys_user_id_idx": {
+ "name": "api_keys_user_id_idx",
+ "columns": [
+ "user_id"
+ ],
+ "isUnique": false
+ },
+ "api_keys_key_hash_idx": {
+ "name": "api_keys_key_hash_idx",
+ "columns": [
+ "key_hash"
+ ],
+ "isUnique": false
+ },
+ "api_keys_is_active_idx": {
+ "name": "api_keys_is_active_idx",
+ "columns": [
+ "is_active"
+ ],
+ "isUnique": false
+ },
+ "api_keys_expires_at_idx": {
+ "name": "api_keys_expires_at_idx",
+ "columns": [
+ "expires_at"
+ ],
+ "isUnique": false
+ }
+ },
+ "foreignKeys": {
+ "api_keys_user_id_users_id_fk": {
+ "name": "api_keys_user_id_users_id_fk",
+ "tableFrom": "api_keys",
+ "tableTo": "users",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "checkConstraints": {}
+ },
+ "app_comments": {
+ "name": "app_comments",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "app_id": {
+ "name": "app_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "content": {
+ "name": "content",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "parent_comment_id": {
+ "name": "parent_comment_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "is_edited": {
+ "name": "is_edited",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false,
+ "default": false
+ },
+ "is_deleted": {
+ "name": "is_deleted",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false,
+ "default": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false,
+ "default": "CURRENT_TIMESTAMP"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false,
+ "default": "CURRENT_TIMESTAMP"
+ }
+ },
+ "indexes": {
+ "app_comments_app_idx": {
+ "name": "app_comments_app_idx",
+ "columns": [
+ "app_id"
+ ],
+ "isUnique": false
+ },
+ "app_comments_user_idx": {
+ "name": "app_comments_user_idx",
+ "columns": [
+ "user_id"
+ ],
+ "isUnique": false
+ },
+ "app_comments_parent_idx": {
+ "name": "app_comments_parent_idx",
+ "columns": [
+ "parent_comment_id"
+ ],
+ "isUnique": false
+ }
+ },
+ "foreignKeys": {
+ "app_comments_app_id_apps_id_fk": {
+ "name": "app_comments_app_id_apps_id_fk",
+ "tableFrom": "app_comments",
+ "tableTo": "apps",
+ "columnsFrom": [
+ "app_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "app_comments_user_id_users_id_fk": {
+ "name": "app_comments_user_id_users_id_fk",
+ "tableFrom": "app_comments",
+ "tableTo": "users",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "checkConstraints": {}
+ },
+ "app_likes": {
+ "name": "app_likes",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "app_id": {
+ "name": "app_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "reaction_type": {
+ "name": "reaction_type",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": "'like'"
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false,
+ "default": "CURRENT_TIMESTAMP"
+ }
+ },
+ "indexes": {
+ "app_likes_app_user_idx": {
+ "name": "app_likes_app_user_idx",
+ "columns": [
+ "app_id",
+ "user_id"
+ ],
+ "isUnique": true
+ },
+ "app_likes_user_idx": {
+ "name": "app_likes_user_idx",
+ "columns": [
+ "user_id"
+ ],
+ "isUnique": false
+ }
+ },
+ "foreignKeys": {
+ "app_likes_app_id_apps_id_fk": {
+ "name": "app_likes_app_id_apps_id_fk",
+ "tableFrom": "app_likes",
+ "tableTo": "apps",
+ "columnsFrom": [
+ "app_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "app_likes_user_id_users_id_fk": {
+ "name": "app_likes_user_id_users_id_fk",
+ "tableFrom": "app_likes",
+ "tableTo": "users",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "checkConstraints": {}
+ },
+ "app_views": {
+ "name": "app_views",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "app_id": {
+ "name": "app_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "session_token": {
+ "name": "session_token",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "ip_address_hash": {
+ "name": "ip_address_hash",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "referrer": {
+ "name": "referrer",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "user_agent": {
+ "name": "user_agent",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "device_type": {
+ "name": "device_type",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "viewed_at": {
+ "name": "viewed_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false,
+ "default": "CURRENT_TIMESTAMP"
+ },
+ "duration_seconds": {
+ "name": "duration_seconds",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ }
+ },
+ "indexes": {
+ "app_views_app_idx": {
+ "name": "app_views_app_idx",
+ "columns": [
+ "app_id"
+ ],
+ "isUnique": false
+ },
+ "app_views_user_idx": {
+ "name": "app_views_user_idx",
+ "columns": [
+ "user_id"
+ ],
+ "isUnique": false
+ },
+ "app_views_viewed_at_idx": {
+ "name": "app_views_viewed_at_idx",
+ "columns": [
+ "viewed_at"
+ ],
+ "isUnique": false
+ }
+ },
+ "foreignKeys": {
+ "app_views_app_id_apps_id_fk": {
+ "name": "app_views_app_id_apps_id_fk",
+ "tableFrom": "app_views",
+ "tableTo": "apps",
+ "columnsFrom": [
+ "app_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "app_views_user_id_users_id_fk": {
+ "name": "app_views_user_id_users_id_fk",
+ "tableFrom": "app_views",
+ "tableTo": "users",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "checkConstraints": {}
+ },
+ "apps": {
+ "name": "apps",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "title": {
+ "name": "title",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "description": {
+ "name": "description",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "icon_url": {
+ "name": "icon_url",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "original_prompt": {
+ "name": "original_prompt",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "final_prompt": {
+ "name": "final_prompt",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "blueprint": {
+ "name": "blueprint",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "framework": {
+ "name": "framework",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "session_token": {
+ "name": "session_token",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "visibility": {
+ "name": "visibility",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": "'private'"
+ },
+ "status": {
+ "name": "status",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": "'generating'"
+ },
+ "deployment_url": {
+ "name": "deployment_url",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "github_repository_url": {
+ "name": "github_repository_url",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "github_repository_visibility": {
+ "name": "github_repository_visibility",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "is_archived": {
+ "name": "is_archived",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false,
+ "default": false
+ },
+ "is_featured": {
+ "name": "is_featured",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false,
+ "default": false
+ },
+ "version": {
+ "name": "version",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false,
+ "default": 1
+ },
+ "parent_app_id": {
+ "name": "parent_app_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "screenshot_url": {
+ "name": "screenshot_url",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "screenshot_captured_at": {
+ "name": "screenshot_captured_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false,
+ "default": "CURRENT_TIMESTAMP"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false,
+ "default": "CURRENT_TIMESTAMP"
+ },
+ "last_deployed_at": {
+ "name": "last_deployed_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ }
+ },
+ "indexes": {
+ "apps_user_idx": {
+ "name": "apps_user_idx",
+ "columns": [
+ "user_id"
+ ],
+ "isUnique": false
+ },
+ "apps_status_idx": {
+ "name": "apps_status_idx",
+ "columns": [
+ "status"
+ ],
+ "isUnique": false
+ },
+ "apps_visibility_idx": {
+ "name": "apps_visibility_idx",
+ "columns": [
+ "visibility"
+ ],
+ "isUnique": false
+ },
+ "apps_session_token_idx": {
+ "name": "apps_session_token_idx",
+ "columns": [
+ "session_token"
+ ],
+ "isUnique": false
+ },
+ "apps_parent_app_idx": {
+ "name": "apps_parent_app_idx",
+ "columns": [
+ "parent_app_id"
+ ],
+ "isUnique": false
+ },
+ "apps_search_idx": {
+ "name": "apps_search_idx",
+ "columns": [
+ "title",
+ "description"
+ ],
+ "isUnique": false
+ },
+ "apps_framework_status_idx": {
+ "name": "apps_framework_status_idx",
+ "columns": [
+ "framework",
+ "status"
+ ],
+ "isUnique": false
+ },
+ "apps_visibility_status_idx": {
+ "name": "apps_visibility_status_idx",
+ "columns": [
+ "visibility",
+ "status"
+ ],
+ "isUnique": false
+ },
+ "apps_created_at_idx": {
+ "name": "apps_created_at_idx",
+ "columns": [
+ "created_at"
+ ],
+ "isUnique": false
+ },
+ "apps_updated_at_idx": {
+ "name": "apps_updated_at_idx",
+ "columns": [
+ "updated_at"
+ ],
+ "isUnique": false
+ }
+ },
+ "foreignKeys": {
+ "apps_user_id_users_id_fk": {
+ "name": "apps_user_id_users_id_fk",
+ "tableFrom": "apps",
+ "tableTo": "users",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "checkConstraints": {}
+ },
+ "audit_logs": {
+ "name": "audit_logs",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "entity_type": {
+ "name": "entity_type",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "entity_id": {
+ "name": "entity_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "action": {
+ "name": "action",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "old_values": {
+ "name": "old_values",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "new_values": {
+ "name": "new_values",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "ip_address": {
+ "name": "ip_address",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "user_agent": {
+ "name": "user_agent",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false,
+ "default": "CURRENT_TIMESTAMP"
+ }
+ },
+ "indexes": {
+ "audit_logs_user_idx": {
+ "name": "audit_logs_user_idx",
+ "columns": [
+ "user_id"
+ ],
+ "isUnique": false
+ },
+ "audit_logs_entity_idx": {
+ "name": "audit_logs_entity_idx",
+ "columns": [
+ "entity_type",
+ "entity_id"
+ ],
+ "isUnique": false
+ },
+ "audit_logs_created_at_idx": {
+ "name": "audit_logs_created_at_idx",
+ "columns": [
+ "created_at"
+ ],
+ "isUnique": false
+ }
+ },
+ "foreignKeys": {
+ "audit_logs_user_id_users_id_fk": {
+ "name": "audit_logs_user_id_users_id_fk",
+ "tableFrom": "audit_logs",
+ "tableTo": "users",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "checkConstraints": {}
+ },
+ "auth_attempts": {
+ "name": "auth_attempts",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "integer",
+ "primaryKey": true,
+ "notNull": true,
+ "autoincrement": true
+ },
+ "identifier": {
+ "name": "identifier",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "attempt_type": {
+ "name": "attempt_type",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "success": {
+ "name": "success",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "ip_address": {
+ "name": "ip_address",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "user_agent": {
+ "name": "user_agent",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "attempted_at": {
+ "name": "attempted_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false,
+ "default": "CURRENT_TIMESTAMP"
+ }
+ },
+ "indexes": {
+ "auth_attempts_lookup_idx": {
+ "name": "auth_attempts_lookup_idx",
+ "columns": [
+ "identifier",
+ "attempted_at"
+ ],
+ "isUnique": false
+ },
+ "auth_attempts_ip_idx": {
+ "name": "auth_attempts_ip_idx",
+ "columns": [
+ "ip_address",
+ "attempted_at"
+ ],
+ "isUnique": false
+ },
+ "auth_attempts_success_idx": {
+ "name": "auth_attempts_success_idx",
+ "columns": [
+ "success",
+ "attempted_at"
+ ],
+ "isUnique": false
+ },
+ "auth_attempts_type_idx": {
+ "name": "auth_attempts_type_idx",
+ "columns": [
+ "attempt_type",
+ "attempted_at"
+ ],
+ "isUnique": false
+ }
+ },
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "checkConstraints": {}
+ },
+ "comment_likes": {
+ "name": "comment_likes",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "comment_id": {
+ "name": "comment_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "reaction_type": {
+ "name": "reaction_type",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": "'like'"
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false,
+ "default": "CURRENT_TIMESTAMP"
+ }
+ },
+ "indexes": {
+ "comment_likes_comment_user_idx": {
+ "name": "comment_likes_comment_user_idx",
+ "columns": [
+ "comment_id",
+ "user_id"
+ ],
+ "isUnique": true
+ },
+ "comment_likes_user_idx": {
+ "name": "comment_likes_user_idx",
+ "columns": [
+ "user_id"
+ ],
+ "isUnique": false
+ },
+ "comment_likes_comment_idx": {
+ "name": "comment_likes_comment_idx",
+ "columns": [
+ "comment_id"
+ ],
+ "isUnique": false
+ }
+ },
+ "foreignKeys": {
+ "comment_likes_comment_id_app_comments_id_fk": {
+ "name": "comment_likes_comment_id_app_comments_id_fk",
+ "tableFrom": "comment_likes",
+ "tableTo": "app_comments",
+ "columnsFrom": [
+ "comment_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "comment_likes_user_id_users_id_fk": {
+ "name": "comment_likes_user_id_users_id_fk",
+ "tableFrom": "comment_likes",
+ "tableTo": "users",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "checkConstraints": {}
+ },
+ "email_verification_tokens": {
+ "name": "email_verification_tokens",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "token_hash": {
+ "name": "token_hash",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "email": {
+ "name": "email",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "expires_at": {
+ "name": "expires_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "used": {
+ "name": "used",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false,
+ "default": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false,
+ "default": "CURRENT_TIMESTAMP"
+ }
+ },
+ "indexes": {
+ "email_verification_tokens_token_hash_unique": {
+ "name": "email_verification_tokens_token_hash_unique",
+ "columns": [
+ "token_hash"
+ ],
+ "isUnique": true
+ },
+ "email_verification_tokens_lookup_idx": {
+ "name": "email_verification_tokens_lookup_idx",
+ "columns": [
+ "token_hash"
+ ],
+ "isUnique": false
+ },
+ "email_verification_tokens_expiry_idx": {
+ "name": "email_verification_tokens_expiry_idx",
+ "columns": [
+ "expires_at"
+ ],
+ "isUnique": false
+ }
+ },
+ "foreignKeys": {
+ "email_verification_tokens_user_id_users_id_fk": {
+ "name": "email_verification_tokens_user_id_users_id_fk",
+ "tableFrom": "email_verification_tokens",
+ "tableTo": "users",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "checkConstraints": {}
+ },
+ "favorites": {
+ "name": "favorites",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "app_id": {
+ "name": "app_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false,
+ "default": "CURRENT_TIMESTAMP"
+ }
+ },
+ "indexes": {
+ "favorites_user_app_idx": {
+ "name": "favorites_user_app_idx",
+ "columns": [
+ "user_id",
+ "app_id"
+ ],
+ "isUnique": true
+ },
+ "favorites_user_idx": {
+ "name": "favorites_user_idx",
+ "columns": [
+ "user_id"
+ ],
+ "isUnique": false
+ },
+ "favorites_app_idx": {
+ "name": "favorites_app_idx",
+ "columns": [
+ "app_id"
+ ],
+ "isUnique": false
+ }
+ },
+ "foreignKeys": {
+ "favorites_user_id_users_id_fk": {
+ "name": "favorites_user_id_users_id_fk",
+ "tableFrom": "favorites",
+ "tableTo": "users",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "favorites_app_id_apps_id_fk": {
+ "name": "favorites_app_id_apps_id_fk",
+ "tableFrom": "favorites",
+ "tableTo": "apps",
+ "columnsFrom": [
+ "app_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "checkConstraints": {}
+ },
+ "oauth_states": {
+ "name": "oauth_states",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "state": {
+ "name": "state",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "provider": {
+ "name": "provider",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "redirect_uri": {
+ "name": "redirect_uri",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "scopes": {
+ "name": "scopes",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false,
+ "default": "'[]'"
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "code_verifier": {
+ "name": "code_verifier",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "nonce": {
+ "name": "nonce",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false,
+ "default": "CURRENT_TIMESTAMP"
+ },
+ "expires_at": {
+ "name": "expires_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "is_used": {
+ "name": "is_used",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false,
+ "default": false
+ }
+ },
+ "indexes": {
+ "oauth_states_state_unique": {
+ "name": "oauth_states_state_unique",
+ "columns": [
+ "state"
+ ],
+ "isUnique": true
+ },
+ "oauth_states_state_idx": {
+ "name": "oauth_states_state_idx",
+ "columns": [
+ "state"
+ ],
+ "isUnique": true
+ },
+ "oauth_states_expires_at_idx": {
+ "name": "oauth_states_expires_at_idx",
+ "columns": [
+ "expires_at"
+ ],
+ "isUnique": false
+ }
+ },
+ "foreignKeys": {
+ "oauth_states_user_id_users_id_fk": {
+ "name": "oauth_states_user_id_users_id_fk",
+ "tableFrom": "oauth_states",
+ "tableTo": "users",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "checkConstraints": {}
+ },
+ "password_reset_tokens": {
+ "name": "password_reset_tokens",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "token_hash": {
+ "name": "token_hash",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "expires_at": {
+ "name": "expires_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "used": {
+ "name": "used",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false,
+ "default": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false,
+ "default": "CURRENT_TIMESTAMP"
+ }
+ },
+ "indexes": {
+ "password_reset_tokens_token_hash_unique": {
+ "name": "password_reset_tokens_token_hash_unique",
+ "columns": [
+ "token_hash"
+ ],
+ "isUnique": true
+ },
+ "password_reset_tokens_lookup_idx": {
+ "name": "password_reset_tokens_lookup_idx",
+ "columns": [
+ "token_hash"
+ ],
+ "isUnique": false
+ },
+ "password_reset_tokens_expiry_idx": {
+ "name": "password_reset_tokens_expiry_idx",
+ "columns": [
+ "expires_at"
+ ],
+ "isUnique": false
+ }
+ },
+ "foreignKeys": {
+ "password_reset_tokens_user_id_users_id_fk": {
+ "name": "password_reset_tokens_user_id_users_id_fk",
+ "tableFrom": "password_reset_tokens",
+ "tableTo": "users",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "checkConstraints": {}
+ },
+ "sessions": {
+ "name": "sessions",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "device_info": {
+ "name": "device_info",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "user_agent": {
+ "name": "user_agent",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "ip_address": {
+ "name": "ip_address",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "is_revoked": {
+ "name": "is_revoked",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false,
+ "default": false
+ },
+ "revoked_at": {
+ "name": "revoked_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "revoked_reason": {
+ "name": "revoked_reason",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "access_token_hash": {
+ "name": "access_token_hash",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "refresh_token_hash": {
+ "name": "refresh_token_hash",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "expires_at": {
+ "name": "expires_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false,
+ "default": "CURRENT_TIMESTAMP"
+ },
+ "last_activity": {
+ "name": "last_activity",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ }
+ },
+ "indexes": {
+ "sessions_user_id_idx": {
+ "name": "sessions_user_id_idx",
+ "columns": [
+ "user_id"
+ ],
+ "isUnique": false
+ },
+ "sessions_expires_at_idx": {
+ "name": "sessions_expires_at_idx",
+ "columns": [
+ "expires_at"
+ ],
+ "isUnique": false
+ },
+ "sessions_access_token_hash_idx": {
+ "name": "sessions_access_token_hash_idx",
+ "columns": [
+ "access_token_hash"
+ ],
+ "isUnique": false
+ },
+ "sessions_refresh_token_hash_idx": {
+ "name": "sessions_refresh_token_hash_idx",
+ "columns": [
+ "refresh_token_hash"
+ ],
+ "isUnique": false
+ },
+ "sessions_last_activity_idx": {
+ "name": "sessions_last_activity_idx",
+ "columns": [
+ "last_activity"
+ ],
+ "isUnique": false
+ },
+ "sessions_is_revoked_idx": {
+ "name": "sessions_is_revoked_idx",
+ "columns": [
+ "is_revoked"
+ ],
+ "isUnique": false
+ }
+ },
+ "foreignKeys": {
+ "sessions_user_id_users_id_fk": {
+ "name": "sessions_user_id_users_id_fk",
+ "tableFrom": "sessions",
+ "tableTo": "users",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "checkConstraints": {}
+ },
+ "stars": {
+ "name": "stars",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "app_id": {
+ "name": "app_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "starred_at": {
+ "name": "starred_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false,
+ "default": "CURRENT_TIMESTAMP"
+ }
+ },
+ "indexes": {
+ "stars_user_app_idx": {
+ "name": "stars_user_app_idx",
+ "columns": [
+ "user_id",
+ "app_id"
+ ],
+ "isUnique": true
+ },
+ "stars_user_idx": {
+ "name": "stars_user_idx",
+ "columns": [
+ "user_id"
+ ],
+ "isUnique": false
+ },
+ "stars_app_idx": {
+ "name": "stars_app_idx",
+ "columns": [
+ "app_id"
+ ],
+ "isUnique": false
+ }
+ },
+ "foreignKeys": {
+ "stars_user_id_users_id_fk": {
+ "name": "stars_user_id_users_id_fk",
+ "tableFrom": "stars",
+ "tableTo": "users",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "stars_app_id_apps_id_fk": {
+ "name": "stars_app_id_apps_id_fk",
+ "tableFrom": "stars",
+ "tableTo": "apps",
+ "columnsFrom": [
+ "app_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "checkConstraints": {}
+ },
+ "system_settings": {
+ "name": "system_settings",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "key": {
+ "name": "key",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "value": {
+ "name": "value",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "description": {
+ "name": "description",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false,
+ "default": "CURRENT_TIMESTAMP"
+ },
+ "updated_by": {
+ "name": "updated_by",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ }
+ },
+ "indexes": {
+ "system_settings_key_unique": {
+ "name": "system_settings_key_unique",
+ "columns": [
+ "key"
+ ],
+ "isUnique": true
+ },
+ "system_settings_key_idx": {
+ "name": "system_settings_key_idx",
+ "columns": [
+ "key"
+ ],
+ "isUnique": true
+ }
+ },
+ "foreignKeys": {
+ "system_settings_updated_by_users_id_fk": {
+ "name": "system_settings_updated_by_users_id_fk",
+ "tableFrom": "system_settings",
+ "tableTo": "users",
+ "columnsFrom": [
+ "updated_by"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "checkConstraints": {}
+ },
+ "user_model_configs": {
+ "name": "user_model_configs",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "agent_action_name": {
+ "name": "agent_action_name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "model_name": {
+ "name": "model_name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "max_tokens": {
+ "name": "max_tokens",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "temperature": {
+ "name": "temperature",
+ "type": "real",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "reasoning_effort": {
+ "name": "reasoning_effort",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "provider_override": {
+ "name": "provider_override",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "fallback_model": {
+ "name": "fallback_model",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "is_active": {
+ "name": "is_active",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false,
+ "default": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false,
+ "default": "CURRENT_TIMESTAMP"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false,
+ "default": "CURRENT_TIMESTAMP"
+ }
+ },
+ "indexes": {
+ "user_model_configs_user_agent_idx": {
+ "name": "user_model_configs_user_agent_idx",
+ "columns": [
+ "user_id",
+ "agent_action_name"
+ ],
+ "isUnique": true
+ },
+ "user_model_configs_user_idx": {
+ "name": "user_model_configs_user_idx",
+ "columns": [
+ "user_id"
+ ],
+ "isUnique": false
+ },
+ "user_model_configs_is_active_idx": {
+ "name": "user_model_configs_is_active_idx",
+ "columns": [
+ "is_active"
+ ],
+ "isUnique": false
+ }
+ },
+ "foreignKeys": {
+ "user_model_configs_user_id_users_id_fk": {
+ "name": "user_model_configs_user_id_users_id_fk",
+ "tableFrom": "user_model_configs",
+ "tableTo": "users",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "checkConstraints": {}
+ },
+ "user_model_providers": {
+ "name": "user_model_providers",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "base_url": {
+ "name": "base_url",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "secret_id": {
+ "name": "secret_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "is_active": {
+ "name": "is_active",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false,
+ "default": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false,
+ "default": "CURRENT_TIMESTAMP"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false,
+ "default": "CURRENT_TIMESTAMP"
+ }
+ },
+ "indexes": {
+ "user_model_providers_user_name_idx": {
+ "name": "user_model_providers_user_name_idx",
+ "columns": [
+ "user_id",
+ "name"
+ ],
+ "isUnique": true
+ },
+ "user_model_providers_user_idx": {
+ "name": "user_model_providers_user_idx",
+ "columns": [
+ "user_id"
+ ],
+ "isUnique": false
+ },
+ "user_model_providers_is_active_idx": {
+ "name": "user_model_providers_is_active_idx",
+ "columns": [
+ "is_active"
+ ],
+ "isUnique": false
+ }
+ },
+ "foreignKeys": {
+ "user_model_providers_user_id_users_id_fk": {
+ "name": "user_model_providers_user_id_users_id_fk",
+ "tableFrom": "user_model_providers",
+ "tableTo": "users",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "user_model_providers_secret_id_user_secrets_id_fk": {
+ "name": "user_model_providers_secret_id_user_secrets_id_fk",
+ "tableFrom": "user_model_providers",
+ "tableTo": "user_secrets",
+ "columnsFrom": [
+ "secret_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "checkConstraints": {}
+ },
+ "user_secrets": {
+ "name": "user_secrets",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "provider": {
+ "name": "provider",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "secret_type": {
+ "name": "secret_type",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "encrypted_value": {
+ "name": "encrypted_value",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "key_preview": {
+ "name": "key_preview",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "description": {
+ "name": "description",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "expires_at": {
+ "name": "expires_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "last_used": {
+ "name": "last_used",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "usage_count": {
+ "name": "usage_count",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false,
+ "default": 0
+ },
+ "is_active": {
+ "name": "is_active",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false,
+ "default": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false,
+ "default": "CURRENT_TIMESTAMP"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false,
+ "default": "CURRENT_TIMESTAMP"
+ }
+ },
+ "indexes": {
+ "user_secrets_user_idx": {
+ "name": "user_secrets_user_idx",
+ "columns": [
+ "user_id"
+ ],
+ "isUnique": false
+ },
+ "user_secrets_provider_idx": {
+ "name": "user_secrets_provider_idx",
+ "columns": [
+ "provider"
+ ],
+ "isUnique": false
+ },
+ "user_secrets_user_provider_idx": {
+ "name": "user_secrets_user_provider_idx",
+ "columns": [
+ "user_id",
+ "provider",
+ "secret_type"
+ ],
+ "isUnique": false
+ },
+ "user_secrets_active_idx": {
+ "name": "user_secrets_active_idx",
+ "columns": [
+ "is_active"
+ ],
+ "isUnique": false
+ }
+ },
+ "foreignKeys": {
+ "user_secrets_user_id_users_id_fk": {
+ "name": "user_secrets_user_id_users_id_fk",
+ "tableFrom": "user_secrets",
+ "tableTo": "users",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "checkConstraints": {}
+ },
+ "users": {
+ "name": "users",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "email": {
+ "name": "email",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "username": {
+ "name": "username",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "display_name": {
+ "name": "display_name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "avatar_url": {
+ "name": "avatar_url",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "bio": {
+ "name": "bio",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "provider": {
+ "name": "provider",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "provider_id": {
+ "name": "provider_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "email_verified": {
+ "name": "email_verified",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false,
+ "default": false
+ },
+ "password_hash": {
+ "name": "password_hash",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "failed_login_attempts": {
+ "name": "failed_login_attempts",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false,
+ "default": 0
+ },
+ "locked_until": {
+ "name": "locked_until",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "password_changed_at": {
+ "name": "password_changed_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "preferences": {
+ "name": "preferences",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false,
+ "default": "'{}'"
+ },
+ "theme": {
+ "name": "theme",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false,
+ "default": "'system'"
+ },
+ "timezone": {
+ "name": "timezone",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false,
+ "default": "'UTC'"
+ },
+ "is_active": {
+ "name": "is_active",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false,
+ "default": true
+ },
+ "is_suspended": {
+ "name": "is_suspended",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false,
+ "default": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false,
+ "default": "CURRENT_TIMESTAMP"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false,
+ "default": "CURRENT_TIMESTAMP"
+ },
+ "last_active_at": {
+ "name": "last_active_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "deleted_at": {
+ "name": "deleted_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ }
+ },
+ "indexes": {
+ "users_email_unique": {
+ "name": "users_email_unique",
+ "columns": [
+ "email"
+ ],
+ "isUnique": true
+ },
+ "users_username_unique": {
+ "name": "users_username_unique",
+ "columns": [
+ "username"
+ ],
+ "isUnique": true
+ },
+ "users_email_idx": {
+ "name": "users_email_idx",
+ "columns": [
+ "email"
+ ],
+ "isUnique": false
+ },
+ "users_provider_unique_idx": {
+ "name": "users_provider_unique_idx",
+ "columns": [
+ "provider",
+ "provider_id"
+ ],
+ "isUnique": true
+ },
+ "users_username_idx": {
+ "name": "users_username_idx",
+ "columns": [
+ "username"
+ ],
+ "isUnique": false
+ },
+ "users_failed_login_attempts_idx": {
+ "name": "users_failed_login_attempts_idx",
+ "columns": [
+ "failed_login_attempts"
+ ],
+ "isUnique": false
+ },
+ "users_locked_until_idx": {
+ "name": "users_locked_until_idx",
+ "columns": [
+ "locked_until"
+ ],
+ "isUnique": false
+ },
+ "users_is_active_idx": {
+ "name": "users_is_active_idx",
+ "columns": [
+ "is_active"
+ ],
+ "isUnique": false
+ },
+ "users_last_active_at_idx": {
+ "name": "users_last_active_at_idx",
+ "columns": [
+ "last_active_at"
+ ],
+ "isUnique": false
+ }
+ },
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "checkConstraints": {}
+ },
+ "verification_otps": {
+ "name": "verification_otps",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "email": {
+ "name": "email",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "otp": {
+ "name": "otp",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "expires_at": {
+ "name": "expires_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "used": {
+ "name": "used",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false,
+ "default": false
+ },
+ "used_at": {
+ "name": "used_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false,
+ "default": "CURRENT_TIMESTAMP"
+ }
+ },
+ "indexes": {
+ "verification_otps_email_idx": {
+ "name": "verification_otps_email_idx",
+ "columns": [
+ "email"
+ ],
+ "isUnique": false
+ },
+ "verification_otps_expires_at_idx": {
+ "name": "verification_otps_expires_at_idx",
+ "columns": [
+ "expires_at"
+ ],
+ "isUnique": false
+ },
+ "verification_otps_used_idx": {
+ "name": "verification_otps_used_idx",
+ "columns": [
+ "used"
+ ],
+ "isUnique": false
+ }
+ },
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "checkConstraints": {}
+ }
+ },
+ "views": {},
+ "enums": {},
+ "_meta": {
+ "schemas": {},
+ "tables": {},
+ "columns": {}
+ },
+ "internal": {
+ "indexes": {}
+ }
+}
\ No newline at end of file
diff --git a/external/drpower-vibe-production/migrations/meta/0001_snapshot.json b/external/drpower-vibe-production/migrations/meta/0001_snapshot.json
new file mode 100644
index 00000000..35c00ae1
--- /dev/null
+++ b/external/drpower-vibe-production/migrations/meta/0001_snapshot.json
@@ -0,0 +1,2550 @@
+{
+ "version": "6",
+ "dialect": "sqlite",
+ "id": "ac0ed0af-c60e-4939-8a7f-d0ea8763f347",
+ "prevId": "c41950c7-1162-4690-ae3f-072e69e53375",
+ "tables": {
+ "api_keys": {
+ "name": "api_keys",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "key_hash": {
+ "name": "key_hash",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "key_preview": {
+ "name": "key_preview",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "scopes": {
+ "name": "scopes",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "is_active": {
+ "name": "is_active",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false,
+ "default": true
+ },
+ "last_used": {
+ "name": "last_used",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "request_count": {
+ "name": "request_count",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false,
+ "default": 0
+ },
+ "expires_at": {
+ "name": "expires_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false,
+ "default": "CURRENT_TIMESTAMP"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false,
+ "default": "CURRENT_TIMESTAMP"
+ }
+ },
+ "indexes": {
+ "api_keys_key_hash_unique": {
+ "name": "api_keys_key_hash_unique",
+ "columns": [
+ "key_hash"
+ ],
+ "isUnique": true
+ },
+ "api_keys_user_id_idx": {
+ "name": "api_keys_user_id_idx",
+ "columns": [
+ "user_id"
+ ],
+ "isUnique": false
+ },
+ "api_keys_key_hash_idx": {
+ "name": "api_keys_key_hash_idx",
+ "columns": [
+ "key_hash"
+ ],
+ "isUnique": false
+ },
+ "api_keys_is_active_idx": {
+ "name": "api_keys_is_active_idx",
+ "columns": [
+ "is_active"
+ ],
+ "isUnique": false
+ },
+ "api_keys_expires_at_idx": {
+ "name": "api_keys_expires_at_idx",
+ "columns": [
+ "expires_at"
+ ],
+ "isUnique": false
+ }
+ },
+ "foreignKeys": {
+ "api_keys_user_id_users_id_fk": {
+ "name": "api_keys_user_id_users_id_fk",
+ "tableFrom": "api_keys",
+ "tableTo": "users",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "checkConstraints": {}
+ },
+ "app_comments": {
+ "name": "app_comments",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "app_id": {
+ "name": "app_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "content": {
+ "name": "content",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "parent_comment_id": {
+ "name": "parent_comment_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "is_edited": {
+ "name": "is_edited",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false,
+ "default": false
+ },
+ "is_deleted": {
+ "name": "is_deleted",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false,
+ "default": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false,
+ "default": "CURRENT_TIMESTAMP"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false,
+ "default": "CURRENT_TIMESTAMP"
+ }
+ },
+ "indexes": {
+ "app_comments_app_idx": {
+ "name": "app_comments_app_idx",
+ "columns": [
+ "app_id"
+ ],
+ "isUnique": false
+ },
+ "app_comments_user_idx": {
+ "name": "app_comments_user_idx",
+ "columns": [
+ "user_id"
+ ],
+ "isUnique": false
+ },
+ "app_comments_parent_idx": {
+ "name": "app_comments_parent_idx",
+ "columns": [
+ "parent_comment_id"
+ ],
+ "isUnique": false
+ }
+ },
+ "foreignKeys": {
+ "app_comments_app_id_apps_id_fk": {
+ "name": "app_comments_app_id_apps_id_fk",
+ "tableFrom": "app_comments",
+ "tableTo": "apps",
+ "columnsFrom": [
+ "app_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "app_comments_user_id_users_id_fk": {
+ "name": "app_comments_user_id_users_id_fk",
+ "tableFrom": "app_comments",
+ "tableTo": "users",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "checkConstraints": {}
+ },
+ "app_likes": {
+ "name": "app_likes",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "app_id": {
+ "name": "app_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "reaction_type": {
+ "name": "reaction_type",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": "'like'"
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false,
+ "default": "CURRENT_TIMESTAMP"
+ }
+ },
+ "indexes": {
+ "app_likes_app_user_idx": {
+ "name": "app_likes_app_user_idx",
+ "columns": [
+ "app_id",
+ "user_id"
+ ],
+ "isUnique": true
+ },
+ "app_likes_user_idx": {
+ "name": "app_likes_user_idx",
+ "columns": [
+ "user_id"
+ ],
+ "isUnique": false
+ }
+ },
+ "foreignKeys": {
+ "app_likes_app_id_apps_id_fk": {
+ "name": "app_likes_app_id_apps_id_fk",
+ "tableFrom": "app_likes",
+ "tableTo": "apps",
+ "columnsFrom": [
+ "app_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "app_likes_user_id_users_id_fk": {
+ "name": "app_likes_user_id_users_id_fk",
+ "tableFrom": "app_likes",
+ "tableTo": "users",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "checkConstraints": {}
+ },
+ "app_views": {
+ "name": "app_views",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "app_id": {
+ "name": "app_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "session_token": {
+ "name": "session_token",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "ip_address_hash": {
+ "name": "ip_address_hash",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "referrer": {
+ "name": "referrer",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "user_agent": {
+ "name": "user_agent",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "device_type": {
+ "name": "device_type",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "viewed_at": {
+ "name": "viewed_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false,
+ "default": "CURRENT_TIMESTAMP"
+ },
+ "duration_seconds": {
+ "name": "duration_seconds",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ }
+ },
+ "indexes": {
+ "app_views_app_idx": {
+ "name": "app_views_app_idx",
+ "columns": [
+ "app_id"
+ ],
+ "isUnique": false
+ },
+ "app_views_user_idx": {
+ "name": "app_views_user_idx",
+ "columns": [
+ "user_id"
+ ],
+ "isUnique": false
+ },
+ "app_views_viewed_at_idx": {
+ "name": "app_views_viewed_at_idx",
+ "columns": [
+ "viewed_at"
+ ],
+ "isUnique": false
+ }
+ },
+ "foreignKeys": {
+ "app_views_app_id_apps_id_fk": {
+ "name": "app_views_app_id_apps_id_fk",
+ "tableFrom": "app_views",
+ "tableTo": "apps",
+ "columnsFrom": [
+ "app_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "app_views_user_id_users_id_fk": {
+ "name": "app_views_user_id_users_id_fk",
+ "tableFrom": "app_views",
+ "tableTo": "users",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "checkConstraints": {}
+ },
+ "apps": {
+ "name": "apps",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "title": {
+ "name": "title",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "description": {
+ "name": "description",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "icon_url": {
+ "name": "icon_url",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "original_prompt": {
+ "name": "original_prompt",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "final_prompt": {
+ "name": "final_prompt",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "framework": {
+ "name": "framework",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "session_token": {
+ "name": "session_token",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "visibility": {
+ "name": "visibility",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": "'private'"
+ },
+ "status": {
+ "name": "status",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": "'generating'"
+ },
+ "deployment_url": {
+ "name": "deployment_url",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "github_repository_url": {
+ "name": "github_repository_url",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "github_repository_visibility": {
+ "name": "github_repository_visibility",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "is_archived": {
+ "name": "is_archived",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false,
+ "default": false
+ },
+ "is_featured": {
+ "name": "is_featured",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false,
+ "default": false
+ },
+ "version": {
+ "name": "version",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false,
+ "default": 1
+ },
+ "parent_app_id": {
+ "name": "parent_app_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "screenshot_url": {
+ "name": "screenshot_url",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "screenshot_captured_at": {
+ "name": "screenshot_captured_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false,
+ "default": "CURRENT_TIMESTAMP"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false,
+ "default": "CURRENT_TIMESTAMP"
+ },
+ "last_deployed_at": {
+ "name": "last_deployed_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ }
+ },
+ "indexes": {
+ "apps_user_idx": {
+ "name": "apps_user_idx",
+ "columns": [
+ "user_id"
+ ],
+ "isUnique": false
+ },
+ "apps_status_idx": {
+ "name": "apps_status_idx",
+ "columns": [
+ "status"
+ ],
+ "isUnique": false
+ },
+ "apps_visibility_idx": {
+ "name": "apps_visibility_idx",
+ "columns": [
+ "visibility"
+ ],
+ "isUnique": false
+ },
+ "apps_session_token_idx": {
+ "name": "apps_session_token_idx",
+ "columns": [
+ "session_token"
+ ],
+ "isUnique": false
+ },
+ "apps_parent_app_idx": {
+ "name": "apps_parent_app_idx",
+ "columns": [
+ "parent_app_id"
+ ],
+ "isUnique": false
+ },
+ "apps_search_idx": {
+ "name": "apps_search_idx",
+ "columns": [
+ "title",
+ "description"
+ ],
+ "isUnique": false
+ },
+ "apps_framework_status_idx": {
+ "name": "apps_framework_status_idx",
+ "columns": [
+ "framework",
+ "status"
+ ],
+ "isUnique": false
+ },
+ "apps_visibility_status_idx": {
+ "name": "apps_visibility_status_idx",
+ "columns": [
+ "visibility",
+ "status"
+ ],
+ "isUnique": false
+ },
+ "apps_created_at_idx": {
+ "name": "apps_created_at_idx",
+ "columns": [
+ "created_at"
+ ],
+ "isUnique": false
+ },
+ "apps_updated_at_idx": {
+ "name": "apps_updated_at_idx",
+ "columns": [
+ "updated_at"
+ ],
+ "isUnique": false
+ }
+ },
+ "foreignKeys": {
+ "apps_user_id_users_id_fk": {
+ "name": "apps_user_id_users_id_fk",
+ "tableFrom": "apps",
+ "tableTo": "users",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "checkConstraints": {}
+ },
+ "audit_logs": {
+ "name": "audit_logs",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "entity_type": {
+ "name": "entity_type",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "entity_id": {
+ "name": "entity_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "action": {
+ "name": "action",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "old_values": {
+ "name": "old_values",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "new_values": {
+ "name": "new_values",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "ip_address": {
+ "name": "ip_address",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "user_agent": {
+ "name": "user_agent",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false,
+ "default": "CURRENT_TIMESTAMP"
+ }
+ },
+ "indexes": {
+ "audit_logs_user_idx": {
+ "name": "audit_logs_user_idx",
+ "columns": [
+ "user_id"
+ ],
+ "isUnique": false
+ },
+ "audit_logs_entity_idx": {
+ "name": "audit_logs_entity_idx",
+ "columns": [
+ "entity_type",
+ "entity_id"
+ ],
+ "isUnique": false
+ },
+ "audit_logs_created_at_idx": {
+ "name": "audit_logs_created_at_idx",
+ "columns": [
+ "created_at"
+ ],
+ "isUnique": false
+ }
+ },
+ "foreignKeys": {
+ "audit_logs_user_id_users_id_fk": {
+ "name": "audit_logs_user_id_users_id_fk",
+ "tableFrom": "audit_logs",
+ "tableTo": "users",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "checkConstraints": {}
+ },
+ "auth_attempts": {
+ "name": "auth_attempts",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "integer",
+ "primaryKey": true,
+ "notNull": true,
+ "autoincrement": true
+ },
+ "identifier": {
+ "name": "identifier",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "attempt_type": {
+ "name": "attempt_type",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "success": {
+ "name": "success",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "ip_address": {
+ "name": "ip_address",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "user_agent": {
+ "name": "user_agent",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "attempted_at": {
+ "name": "attempted_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false,
+ "default": "CURRENT_TIMESTAMP"
+ }
+ },
+ "indexes": {
+ "auth_attempts_lookup_idx": {
+ "name": "auth_attempts_lookup_idx",
+ "columns": [
+ "identifier",
+ "attempted_at"
+ ],
+ "isUnique": false
+ },
+ "auth_attempts_ip_idx": {
+ "name": "auth_attempts_ip_idx",
+ "columns": [
+ "ip_address",
+ "attempted_at"
+ ],
+ "isUnique": false
+ },
+ "auth_attempts_success_idx": {
+ "name": "auth_attempts_success_idx",
+ "columns": [
+ "success",
+ "attempted_at"
+ ],
+ "isUnique": false
+ },
+ "auth_attempts_type_idx": {
+ "name": "auth_attempts_type_idx",
+ "columns": [
+ "attempt_type",
+ "attempted_at"
+ ],
+ "isUnique": false
+ }
+ },
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "checkConstraints": {}
+ },
+ "comment_likes": {
+ "name": "comment_likes",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "comment_id": {
+ "name": "comment_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "reaction_type": {
+ "name": "reaction_type",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": "'like'"
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false,
+ "default": "CURRENT_TIMESTAMP"
+ }
+ },
+ "indexes": {
+ "comment_likes_comment_user_idx": {
+ "name": "comment_likes_comment_user_idx",
+ "columns": [
+ "comment_id",
+ "user_id"
+ ],
+ "isUnique": true
+ },
+ "comment_likes_user_idx": {
+ "name": "comment_likes_user_idx",
+ "columns": [
+ "user_id"
+ ],
+ "isUnique": false
+ },
+ "comment_likes_comment_idx": {
+ "name": "comment_likes_comment_idx",
+ "columns": [
+ "comment_id"
+ ],
+ "isUnique": false
+ }
+ },
+ "foreignKeys": {
+ "comment_likes_comment_id_app_comments_id_fk": {
+ "name": "comment_likes_comment_id_app_comments_id_fk",
+ "tableFrom": "comment_likes",
+ "tableTo": "app_comments",
+ "columnsFrom": [
+ "comment_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "comment_likes_user_id_users_id_fk": {
+ "name": "comment_likes_user_id_users_id_fk",
+ "tableFrom": "comment_likes",
+ "tableTo": "users",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "checkConstraints": {}
+ },
+ "email_verification_tokens": {
+ "name": "email_verification_tokens",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "token_hash": {
+ "name": "token_hash",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "email": {
+ "name": "email",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "expires_at": {
+ "name": "expires_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "used": {
+ "name": "used",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false,
+ "default": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false,
+ "default": "CURRENT_TIMESTAMP"
+ }
+ },
+ "indexes": {
+ "email_verification_tokens_token_hash_unique": {
+ "name": "email_verification_tokens_token_hash_unique",
+ "columns": [
+ "token_hash"
+ ],
+ "isUnique": true
+ },
+ "email_verification_tokens_lookup_idx": {
+ "name": "email_verification_tokens_lookup_idx",
+ "columns": [
+ "token_hash"
+ ],
+ "isUnique": false
+ },
+ "email_verification_tokens_expiry_idx": {
+ "name": "email_verification_tokens_expiry_idx",
+ "columns": [
+ "expires_at"
+ ],
+ "isUnique": false
+ }
+ },
+ "foreignKeys": {
+ "email_verification_tokens_user_id_users_id_fk": {
+ "name": "email_verification_tokens_user_id_users_id_fk",
+ "tableFrom": "email_verification_tokens",
+ "tableTo": "users",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "checkConstraints": {}
+ },
+ "favorites": {
+ "name": "favorites",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "app_id": {
+ "name": "app_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false,
+ "default": "CURRENT_TIMESTAMP"
+ }
+ },
+ "indexes": {
+ "favorites_user_app_idx": {
+ "name": "favorites_user_app_idx",
+ "columns": [
+ "user_id",
+ "app_id"
+ ],
+ "isUnique": true
+ },
+ "favorites_user_idx": {
+ "name": "favorites_user_idx",
+ "columns": [
+ "user_id"
+ ],
+ "isUnique": false
+ },
+ "favorites_app_idx": {
+ "name": "favorites_app_idx",
+ "columns": [
+ "app_id"
+ ],
+ "isUnique": false
+ }
+ },
+ "foreignKeys": {
+ "favorites_user_id_users_id_fk": {
+ "name": "favorites_user_id_users_id_fk",
+ "tableFrom": "favorites",
+ "tableTo": "users",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "favorites_app_id_apps_id_fk": {
+ "name": "favorites_app_id_apps_id_fk",
+ "tableFrom": "favorites",
+ "tableTo": "apps",
+ "columnsFrom": [
+ "app_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "checkConstraints": {}
+ },
+ "oauth_states": {
+ "name": "oauth_states",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "state": {
+ "name": "state",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "provider": {
+ "name": "provider",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "redirect_uri": {
+ "name": "redirect_uri",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "scopes": {
+ "name": "scopes",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false,
+ "default": "'[]'"
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "code_verifier": {
+ "name": "code_verifier",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "nonce": {
+ "name": "nonce",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false,
+ "default": "CURRENT_TIMESTAMP"
+ },
+ "expires_at": {
+ "name": "expires_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "is_used": {
+ "name": "is_used",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false,
+ "default": false
+ }
+ },
+ "indexes": {
+ "oauth_states_state_unique": {
+ "name": "oauth_states_state_unique",
+ "columns": [
+ "state"
+ ],
+ "isUnique": true
+ },
+ "oauth_states_state_idx": {
+ "name": "oauth_states_state_idx",
+ "columns": [
+ "state"
+ ],
+ "isUnique": true
+ },
+ "oauth_states_expires_at_idx": {
+ "name": "oauth_states_expires_at_idx",
+ "columns": [
+ "expires_at"
+ ],
+ "isUnique": false
+ }
+ },
+ "foreignKeys": {
+ "oauth_states_user_id_users_id_fk": {
+ "name": "oauth_states_user_id_users_id_fk",
+ "tableFrom": "oauth_states",
+ "tableTo": "users",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "checkConstraints": {}
+ },
+ "password_reset_tokens": {
+ "name": "password_reset_tokens",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "token_hash": {
+ "name": "token_hash",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "expires_at": {
+ "name": "expires_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "used": {
+ "name": "used",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false,
+ "default": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false,
+ "default": "CURRENT_TIMESTAMP"
+ }
+ },
+ "indexes": {
+ "password_reset_tokens_token_hash_unique": {
+ "name": "password_reset_tokens_token_hash_unique",
+ "columns": [
+ "token_hash"
+ ],
+ "isUnique": true
+ },
+ "password_reset_tokens_lookup_idx": {
+ "name": "password_reset_tokens_lookup_idx",
+ "columns": [
+ "token_hash"
+ ],
+ "isUnique": false
+ },
+ "password_reset_tokens_expiry_idx": {
+ "name": "password_reset_tokens_expiry_idx",
+ "columns": [
+ "expires_at"
+ ],
+ "isUnique": false
+ }
+ },
+ "foreignKeys": {
+ "password_reset_tokens_user_id_users_id_fk": {
+ "name": "password_reset_tokens_user_id_users_id_fk",
+ "tableFrom": "password_reset_tokens",
+ "tableTo": "users",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "checkConstraints": {}
+ },
+ "sessions": {
+ "name": "sessions",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "device_info": {
+ "name": "device_info",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "user_agent": {
+ "name": "user_agent",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "ip_address": {
+ "name": "ip_address",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "is_revoked": {
+ "name": "is_revoked",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false,
+ "default": false
+ },
+ "revoked_at": {
+ "name": "revoked_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "revoked_reason": {
+ "name": "revoked_reason",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "access_token_hash": {
+ "name": "access_token_hash",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "refresh_token_hash": {
+ "name": "refresh_token_hash",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "expires_at": {
+ "name": "expires_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false,
+ "default": "CURRENT_TIMESTAMP"
+ },
+ "last_activity": {
+ "name": "last_activity",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ }
+ },
+ "indexes": {
+ "sessions_user_id_idx": {
+ "name": "sessions_user_id_idx",
+ "columns": [
+ "user_id"
+ ],
+ "isUnique": false
+ },
+ "sessions_expires_at_idx": {
+ "name": "sessions_expires_at_idx",
+ "columns": [
+ "expires_at"
+ ],
+ "isUnique": false
+ },
+ "sessions_access_token_hash_idx": {
+ "name": "sessions_access_token_hash_idx",
+ "columns": [
+ "access_token_hash"
+ ],
+ "isUnique": false
+ },
+ "sessions_refresh_token_hash_idx": {
+ "name": "sessions_refresh_token_hash_idx",
+ "columns": [
+ "refresh_token_hash"
+ ],
+ "isUnique": false
+ },
+ "sessions_last_activity_idx": {
+ "name": "sessions_last_activity_idx",
+ "columns": [
+ "last_activity"
+ ],
+ "isUnique": false
+ },
+ "sessions_is_revoked_idx": {
+ "name": "sessions_is_revoked_idx",
+ "columns": [
+ "is_revoked"
+ ],
+ "isUnique": false
+ }
+ },
+ "foreignKeys": {
+ "sessions_user_id_users_id_fk": {
+ "name": "sessions_user_id_users_id_fk",
+ "tableFrom": "sessions",
+ "tableTo": "users",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "checkConstraints": {}
+ },
+ "stars": {
+ "name": "stars",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "app_id": {
+ "name": "app_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "starred_at": {
+ "name": "starred_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false,
+ "default": "CURRENT_TIMESTAMP"
+ }
+ },
+ "indexes": {
+ "stars_user_app_idx": {
+ "name": "stars_user_app_idx",
+ "columns": [
+ "user_id",
+ "app_id"
+ ],
+ "isUnique": true
+ },
+ "stars_user_idx": {
+ "name": "stars_user_idx",
+ "columns": [
+ "user_id"
+ ],
+ "isUnique": false
+ },
+ "stars_app_idx": {
+ "name": "stars_app_idx",
+ "columns": [
+ "app_id"
+ ],
+ "isUnique": false
+ }
+ },
+ "foreignKeys": {
+ "stars_user_id_users_id_fk": {
+ "name": "stars_user_id_users_id_fk",
+ "tableFrom": "stars",
+ "tableTo": "users",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "stars_app_id_apps_id_fk": {
+ "name": "stars_app_id_apps_id_fk",
+ "tableFrom": "stars",
+ "tableTo": "apps",
+ "columnsFrom": [
+ "app_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "checkConstraints": {}
+ },
+ "system_settings": {
+ "name": "system_settings",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "key": {
+ "name": "key",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "value": {
+ "name": "value",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "description": {
+ "name": "description",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false,
+ "default": "CURRENT_TIMESTAMP"
+ },
+ "updated_by": {
+ "name": "updated_by",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ }
+ },
+ "indexes": {
+ "system_settings_key_unique": {
+ "name": "system_settings_key_unique",
+ "columns": [
+ "key"
+ ],
+ "isUnique": true
+ },
+ "system_settings_key_idx": {
+ "name": "system_settings_key_idx",
+ "columns": [
+ "key"
+ ],
+ "isUnique": true
+ }
+ },
+ "foreignKeys": {
+ "system_settings_updated_by_users_id_fk": {
+ "name": "system_settings_updated_by_users_id_fk",
+ "tableFrom": "system_settings",
+ "tableTo": "users",
+ "columnsFrom": [
+ "updated_by"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "checkConstraints": {}
+ },
+ "user_model_configs": {
+ "name": "user_model_configs",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "agent_action_name": {
+ "name": "agent_action_name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "model_name": {
+ "name": "model_name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "max_tokens": {
+ "name": "max_tokens",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "temperature": {
+ "name": "temperature",
+ "type": "real",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "reasoning_effort": {
+ "name": "reasoning_effort",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "provider_override": {
+ "name": "provider_override",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "fallback_model": {
+ "name": "fallback_model",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "is_active": {
+ "name": "is_active",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false,
+ "default": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false,
+ "default": "CURRENT_TIMESTAMP"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false,
+ "default": "CURRENT_TIMESTAMP"
+ }
+ },
+ "indexes": {
+ "user_model_configs_user_agent_idx": {
+ "name": "user_model_configs_user_agent_idx",
+ "columns": [
+ "user_id",
+ "agent_action_name"
+ ],
+ "isUnique": true
+ },
+ "user_model_configs_user_idx": {
+ "name": "user_model_configs_user_idx",
+ "columns": [
+ "user_id"
+ ],
+ "isUnique": false
+ },
+ "user_model_configs_is_active_idx": {
+ "name": "user_model_configs_is_active_idx",
+ "columns": [
+ "is_active"
+ ],
+ "isUnique": false
+ }
+ },
+ "foreignKeys": {
+ "user_model_configs_user_id_users_id_fk": {
+ "name": "user_model_configs_user_id_users_id_fk",
+ "tableFrom": "user_model_configs",
+ "tableTo": "users",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "checkConstraints": {}
+ },
+ "user_model_providers": {
+ "name": "user_model_providers",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "base_url": {
+ "name": "base_url",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "secret_id": {
+ "name": "secret_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "is_active": {
+ "name": "is_active",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false,
+ "default": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false,
+ "default": "CURRENT_TIMESTAMP"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false,
+ "default": "CURRENT_TIMESTAMP"
+ }
+ },
+ "indexes": {
+ "user_model_providers_user_name_idx": {
+ "name": "user_model_providers_user_name_idx",
+ "columns": [
+ "user_id",
+ "name"
+ ],
+ "isUnique": true
+ },
+ "user_model_providers_user_idx": {
+ "name": "user_model_providers_user_idx",
+ "columns": [
+ "user_id"
+ ],
+ "isUnique": false
+ },
+ "user_model_providers_is_active_idx": {
+ "name": "user_model_providers_is_active_idx",
+ "columns": [
+ "is_active"
+ ],
+ "isUnique": false
+ }
+ },
+ "foreignKeys": {
+ "user_model_providers_user_id_users_id_fk": {
+ "name": "user_model_providers_user_id_users_id_fk",
+ "tableFrom": "user_model_providers",
+ "tableTo": "users",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "user_model_providers_secret_id_user_secrets_id_fk": {
+ "name": "user_model_providers_secret_id_user_secrets_id_fk",
+ "tableFrom": "user_model_providers",
+ "tableTo": "user_secrets",
+ "columnsFrom": [
+ "secret_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "checkConstraints": {}
+ },
+ "user_secrets": {
+ "name": "user_secrets",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "provider": {
+ "name": "provider",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "secret_type": {
+ "name": "secret_type",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "encrypted_value": {
+ "name": "encrypted_value",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "key_preview": {
+ "name": "key_preview",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "description": {
+ "name": "description",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "expires_at": {
+ "name": "expires_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "last_used": {
+ "name": "last_used",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "usage_count": {
+ "name": "usage_count",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false,
+ "default": 0
+ },
+ "is_active": {
+ "name": "is_active",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false,
+ "default": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false,
+ "default": "CURRENT_TIMESTAMP"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false,
+ "default": "CURRENT_TIMESTAMP"
+ }
+ },
+ "indexes": {
+ "user_secrets_user_idx": {
+ "name": "user_secrets_user_idx",
+ "columns": [
+ "user_id"
+ ],
+ "isUnique": false
+ },
+ "user_secrets_provider_idx": {
+ "name": "user_secrets_provider_idx",
+ "columns": [
+ "provider"
+ ],
+ "isUnique": false
+ },
+ "user_secrets_user_provider_idx": {
+ "name": "user_secrets_user_provider_idx",
+ "columns": [
+ "user_id",
+ "provider",
+ "secret_type"
+ ],
+ "isUnique": false
+ },
+ "user_secrets_active_idx": {
+ "name": "user_secrets_active_idx",
+ "columns": [
+ "is_active"
+ ],
+ "isUnique": false
+ }
+ },
+ "foreignKeys": {
+ "user_secrets_user_id_users_id_fk": {
+ "name": "user_secrets_user_id_users_id_fk",
+ "tableFrom": "user_secrets",
+ "tableTo": "users",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "checkConstraints": {}
+ },
+ "users": {
+ "name": "users",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "email": {
+ "name": "email",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "username": {
+ "name": "username",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "display_name": {
+ "name": "display_name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "avatar_url": {
+ "name": "avatar_url",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "bio": {
+ "name": "bio",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "provider": {
+ "name": "provider",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "provider_id": {
+ "name": "provider_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "email_verified": {
+ "name": "email_verified",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false,
+ "default": false
+ },
+ "password_hash": {
+ "name": "password_hash",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "failed_login_attempts": {
+ "name": "failed_login_attempts",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false,
+ "default": 0
+ },
+ "locked_until": {
+ "name": "locked_until",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "password_changed_at": {
+ "name": "password_changed_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "preferences": {
+ "name": "preferences",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false,
+ "default": "'{}'"
+ },
+ "theme": {
+ "name": "theme",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false,
+ "default": "'system'"
+ },
+ "timezone": {
+ "name": "timezone",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false,
+ "default": "'UTC'"
+ },
+ "is_active": {
+ "name": "is_active",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false,
+ "default": true
+ },
+ "is_suspended": {
+ "name": "is_suspended",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false,
+ "default": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false,
+ "default": "CURRENT_TIMESTAMP"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false,
+ "default": "CURRENT_TIMESTAMP"
+ },
+ "last_active_at": {
+ "name": "last_active_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "deleted_at": {
+ "name": "deleted_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ }
+ },
+ "indexes": {
+ "users_email_unique": {
+ "name": "users_email_unique",
+ "columns": [
+ "email"
+ ],
+ "isUnique": true
+ },
+ "users_username_unique": {
+ "name": "users_username_unique",
+ "columns": [
+ "username"
+ ],
+ "isUnique": true
+ },
+ "users_email_idx": {
+ "name": "users_email_idx",
+ "columns": [
+ "email"
+ ],
+ "isUnique": false
+ },
+ "users_provider_unique_idx": {
+ "name": "users_provider_unique_idx",
+ "columns": [
+ "provider",
+ "provider_id"
+ ],
+ "isUnique": true
+ },
+ "users_username_idx": {
+ "name": "users_username_idx",
+ "columns": [
+ "username"
+ ],
+ "isUnique": false
+ },
+ "users_failed_login_attempts_idx": {
+ "name": "users_failed_login_attempts_idx",
+ "columns": [
+ "failed_login_attempts"
+ ],
+ "isUnique": false
+ },
+ "users_locked_until_idx": {
+ "name": "users_locked_until_idx",
+ "columns": [
+ "locked_until"
+ ],
+ "isUnique": false
+ },
+ "users_is_active_idx": {
+ "name": "users_is_active_idx",
+ "columns": [
+ "is_active"
+ ],
+ "isUnique": false
+ },
+ "users_last_active_at_idx": {
+ "name": "users_last_active_at_idx",
+ "columns": [
+ "last_active_at"
+ ],
+ "isUnique": false
+ }
+ },
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "checkConstraints": {}
+ },
+ "verification_otps": {
+ "name": "verification_otps",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "email": {
+ "name": "email",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "otp": {
+ "name": "otp",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "expires_at": {
+ "name": "expires_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "used": {
+ "name": "used",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false,
+ "default": false
+ },
+ "used_at": {
+ "name": "used_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false,
+ "default": "CURRENT_TIMESTAMP"
+ }
+ },
+ "indexes": {
+ "verification_otps_email_idx": {
+ "name": "verification_otps_email_idx",
+ "columns": [
+ "email"
+ ],
+ "isUnique": false
+ },
+ "verification_otps_expires_at_idx": {
+ "name": "verification_otps_expires_at_idx",
+ "columns": [
+ "expires_at"
+ ],
+ "isUnique": false
+ },
+ "verification_otps_used_idx": {
+ "name": "verification_otps_used_idx",
+ "columns": [
+ "used"
+ ],
+ "isUnique": false
+ }
+ },
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "checkConstraints": {}
+ }
+ },
+ "views": {},
+ "enums": {},
+ "_meta": {
+ "schemas": {},
+ "tables": {},
+ "columns": {}
+ },
+ "internal": {
+ "indexes": {}
+ }
+}
\ No newline at end of file
diff --git a/external/drpower-vibe-production/migrations/meta/0002_snapshot.json b/external/drpower-vibe-production/migrations/meta/0002_snapshot.json
new file mode 100644
index 00000000..55a99e92
--- /dev/null
+++ b/external/drpower-vibe-production/migrations/meta/0002_snapshot.json
@@ -0,0 +1,2552 @@
+{
+ "version": "6",
+ "dialect": "sqlite",
+ "id": "c6d41ea1-5cb3-4ead-9678-e9f51e8ff424",
+ "prevId": "ac0ed0af-c60e-4939-8a7f-d0ea8763f347",
+ "tables": {
+ "api_keys": {
+ "name": "api_keys",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "key_hash": {
+ "name": "key_hash",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "key_preview": {
+ "name": "key_preview",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "scopes": {
+ "name": "scopes",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "is_active": {
+ "name": "is_active",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false,
+ "default": true
+ },
+ "last_used": {
+ "name": "last_used",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "request_count": {
+ "name": "request_count",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false,
+ "default": 0
+ },
+ "expires_at": {
+ "name": "expires_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false,
+ "default": "CURRENT_TIMESTAMP"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false,
+ "default": "CURRENT_TIMESTAMP"
+ }
+ },
+ "indexes": {
+ "api_keys_key_hash_unique": {
+ "name": "api_keys_key_hash_unique",
+ "columns": [
+ "key_hash"
+ ],
+ "isUnique": true
+ },
+ "api_keys_user_id_idx": {
+ "name": "api_keys_user_id_idx",
+ "columns": [
+ "user_id"
+ ],
+ "isUnique": false
+ },
+ "api_keys_key_hash_idx": {
+ "name": "api_keys_key_hash_idx",
+ "columns": [
+ "key_hash"
+ ],
+ "isUnique": false
+ },
+ "api_keys_is_active_idx": {
+ "name": "api_keys_is_active_idx",
+ "columns": [
+ "is_active"
+ ],
+ "isUnique": false
+ },
+ "api_keys_expires_at_idx": {
+ "name": "api_keys_expires_at_idx",
+ "columns": [
+ "expires_at"
+ ],
+ "isUnique": false
+ }
+ },
+ "foreignKeys": {
+ "api_keys_user_id_users_id_fk": {
+ "name": "api_keys_user_id_users_id_fk",
+ "tableFrom": "api_keys",
+ "tableTo": "users",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "checkConstraints": {}
+ },
+ "app_comments": {
+ "name": "app_comments",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "app_id": {
+ "name": "app_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "content": {
+ "name": "content",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "parent_comment_id": {
+ "name": "parent_comment_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "is_edited": {
+ "name": "is_edited",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false,
+ "default": false
+ },
+ "is_deleted": {
+ "name": "is_deleted",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false,
+ "default": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false,
+ "default": "CURRENT_TIMESTAMP"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false,
+ "default": "CURRENT_TIMESTAMP"
+ }
+ },
+ "indexes": {
+ "app_comments_app_idx": {
+ "name": "app_comments_app_idx",
+ "columns": [
+ "app_id"
+ ],
+ "isUnique": false
+ },
+ "app_comments_user_idx": {
+ "name": "app_comments_user_idx",
+ "columns": [
+ "user_id"
+ ],
+ "isUnique": false
+ },
+ "app_comments_parent_idx": {
+ "name": "app_comments_parent_idx",
+ "columns": [
+ "parent_comment_id"
+ ],
+ "isUnique": false
+ }
+ },
+ "foreignKeys": {
+ "app_comments_app_id_apps_id_fk": {
+ "name": "app_comments_app_id_apps_id_fk",
+ "tableFrom": "app_comments",
+ "tableTo": "apps",
+ "columnsFrom": [
+ "app_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "app_comments_user_id_users_id_fk": {
+ "name": "app_comments_user_id_users_id_fk",
+ "tableFrom": "app_comments",
+ "tableTo": "users",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "checkConstraints": {}
+ },
+ "app_likes": {
+ "name": "app_likes",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "app_id": {
+ "name": "app_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "reaction_type": {
+ "name": "reaction_type",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": "'like'"
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false,
+ "default": "CURRENT_TIMESTAMP"
+ }
+ },
+ "indexes": {
+ "app_likes_app_user_idx": {
+ "name": "app_likes_app_user_idx",
+ "columns": [
+ "app_id",
+ "user_id"
+ ],
+ "isUnique": true
+ },
+ "app_likes_user_idx": {
+ "name": "app_likes_user_idx",
+ "columns": [
+ "user_id"
+ ],
+ "isUnique": false
+ }
+ },
+ "foreignKeys": {
+ "app_likes_app_id_apps_id_fk": {
+ "name": "app_likes_app_id_apps_id_fk",
+ "tableFrom": "app_likes",
+ "tableTo": "apps",
+ "columnsFrom": [
+ "app_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "app_likes_user_id_users_id_fk": {
+ "name": "app_likes_user_id_users_id_fk",
+ "tableFrom": "app_likes",
+ "tableTo": "users",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "checkConstraints": {}
+ },
+ "app_views": {
+ "name": "app_views",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "app_id": {
+ "name": "app_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "session_token": {
+ "name": "session_token",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "ip_address_hash": {
+ "name": "ip_address_hash",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "referrer": {
+ "name": "referrer",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "user_agent": {
+ "name": "user_agent",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "device_type": {
+ "name": "device_type",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "viewed_at": {
+ "name": "viewed_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false,
+ "default": "CURRENT_TIMESTAMP"
+ },
+ "duration_seconds": {
+ "name": "duration_seconds",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ }
+ },
+ "indexes": {
+ "app_views_app_idx": {
+ "name": "app_views_app_idx",
+ "columns": [
+ "app_id"
+ ],
+ "isUnique": false
+ },
+ "app_views_user_idx": {
+ "name": "app_views_user_idx",
+ "columns": [
+ "user_id"
+ ],
+ "isUnique": false
+ },
+ "app_views_viewed_at_idx": {
+ "name": "app_views_viewed_at_idx",
+ "columns": [
+ "viewed_at"
+ ],
+ "isUnique": false
+ }
+ },
+ "foreignKeys": {
+ "app_views_app_id_apps_id_fk": {
+ "name": "app_views_app_id_apps_id_fk",
+ "tableFrom": "app_views",
+ "tableTo": "apps",
+ "columnsFrom": [
+ "app_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "app_views_user_id_users_id_fk": {
+ "name": "app_views_user_id_users_id_fk",
+ "tableFrom": "app_views",
+ "tableTo": "users",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "checkConstraints": {}
+ },
+ "apps": {
+ "name": "apps",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "title": {
+ "name": "title",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "description": {
+ "name": "description",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "icon_url": {
+ "name": "icon_url",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "original_prompt": {
+ "name": "original_prompt",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "final_prompt": {
+ "name": "final_prompt",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "framework": {
+ "name": "framework",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "session_token": {
+ "name": "session_token",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "visibility": {
+ "name": "visibility",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": "'private'"
+ },
+ "status": {
+ "name": "status",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": "'generating'"
+ },
+ "deployment_id": {
+ "name": "deployment_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "github_repository_url": {
+ "name": "github_repository_url",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "github_repository_visibility": {
+ "name": "github_repository_visibility",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "is_archived": {
+ "name": "is_archived",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false,
+ "default": false
+ },
+ "is_featured": {
+ "name": "is_featured",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false,
+ "default": false
+ },
+ "version": {
+ "name": "version",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false,
+ "default": 1
+ },
+ "parent_app_id": {
+ "name": "parent_app_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "screenshot_url": {
+ "name": "screenshot_url",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "screenshot_captured_at": {
+ "name": "screenshot_captured_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false,
+ "default": "CURRENT_TIMESTAMP"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false,
+ "default": "CURRENT_TIMESTAMP"
+ },
+ "last_deployed_at": {
+ "name": "last_deployed_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ }
+ },
+ "indexes": {
+ "apps_user_idx": {
+ "name": "apps_user_idx",
+ "columns": [
+ "user_id"
+ ],
+ "isUnique": false
+ },
+ "apps_status_idx": {
+ "name": "apps_status_idx",
+ "columns": [
+ "status"
+ ],
+ "isUnique": false
+ },
+ "apps_visibility_idx": {
+ "name": "apps_visibility_idx",
+ "columns": [
+ "visibility"
+ ],
+ "isUnique": false
+ },
+ "apps_session_token_idx": {
+ "name": "apps_session_token_idx",
+ "columns": [
+ "session_token"
+ ],
+ "isUnique": false
+ },
+ "apps_parent_app_idx": {
+ "name": "apps_parent_app_idx",
+ "columns": [
+ "parent_app_id"
+ ],
+ "isUnique": false
+ },
+ "apps_search_idx": {
+ "name": "apps_search_idx",
+ "columns": [
+ "title",
+ "description"
+ ],
+ "isUnique": false
+ },
+ "apps_framework_status_idx": {
+ "name": "apps_framework_status_idx",
+ "columns": [
+ "framework",
+ "status"
+ ],
+ "isUnique": false
+ },
+ "apps_visibility_status_idx": {
+ "name": "apps_visibility_status_idx",
+ "columns": [
+ "visibility",
+ "status"
+ ],
+ "isUnique": false
+ },
+ "apps_created_at_idx": {
+ "name": "apps_created_at_idx",
+ "columns": [
+ "created_at"
+ ],
+ "isUnique": false
+ },
+ "apps_updated_at_idx": {
+ "name": "apps_updated_at_idx",
+ "columns": [
+ "updated_at"
+ ],
+ "isUnique": false
+ }
+ },
+ "foreignKeys": {
+ "apps_user_id_users_id_fk": {
+ "name": "apps_user_id_users_id_fk",
+ "tableFrom": "apps",
+ "tableTo": "users",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "checkConstraints": {}
+ },
+ "audit_logs": {
+ "name": "audit_logs",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "entity_type": {
+ "name": "entity_type",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "entity_id": {
+ "name": "entity_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "action": {
+ "name": "action",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "old_values": {
+ "name": "old_values",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "new_values": {
+ "name": "new_values",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "ip_address": {
+ "name": "ip_address",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "user_agent": {
+ "name": "user_agent",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false,
+ "default": "CURRENT_TIMESTAMP"
+ }
+ },
+ "indexes": {
+ "audit_logs_user_idx": {
+ "name": "audit_logs_user_idx",
+ "columns": [
+ "user_id"
+ ],
+ "isUnique": false
+ },
+ "audit_logs_entity_idx": {
+ "name": "audit_logs_entity_idx",
+ "columns": [
+ "entity_type",
+ "entity_id"
+ ],
+ "isUnique": false
+ },
+ "audit_logs_created_at_idx": {
+ "name": "audit_logs_created_at_idx",
+ "columns": [
+ "created_at"
+ ],
+ "isUnique": false
+ }
+ },
+ "foreignKeys": {
+ "audit_logs_user_id_users_id_fk": {
+ "name": "audit_logs_user_id_users_id_fk",
+ "tableFrom": "audit_logs",
+ "tableTo": "users",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "checkConstraints": {}
+ },
+ "auth_attempts": {
+ "name": "auth_attempts",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "integer",
+ "primaryKey": true,
+ "notNull": true,
+ "autoincrement": true
+ },
+ "identifier": {
+ "name": "identifier",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "attempt_type": {
+ "name": "attempt_type",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "success": {
+ "name": "success",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "ip_address": {
+ "name": "ip_address",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "user_agent": {
+ "name": "user_agent",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "attempted_at": {
+ "name": "attempted_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false,
+ "default": "CURRENT_TIMESTAMP"
+ }
+ },
+ "indexes": {
+ "auth_attempts_lookup_idx": {
+ "name": "auth_attempts_lookup_idx",
+ "columns": [
+ "identifier",
+ "attempted_at"
+ ],
+ "isUnique": false
+ },
+ "auth_attempts_ip_idx": {
+ "name": "auth_attempts_ip_idx",
+ "columns": [
+ "ip_address",
+ "attempted_at"
+ ],
+ "isUnique": false
+ },
+ "auth_attempts_success_idx": {
+ "name": "auth_attempts_success_idx",
+ "columns": [
+ "success",
+ "attempted_at"
+ ],
+ "isUnique": false
+ },
+ "auth_attempts_type_idx": {
+ "name": "auth_attempts_type_idx",
+ "columns": [
+ "attempt_type",
+ "attempted_at"
+ ],
+ "isUnique": false
+ }
+ },
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "checkConstraints": {}
+ },
+ "comment_likes": {
+ "name": "comment_likes",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "comment_id": {
+ "name": "comment_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "reaction_type": {
+ "name": "reaction_type",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": "'like'"
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false,
+ "default": "CURRENT_TIMESTAMP"
+ }
+ },
+ "indexes": {
+ "comment_likes_comment_user_idx": {
+ "name": "comment_likes_comment_user_idx",
+ "columns": [
+ "comment_id",
+ "user_id"
+ ],
+ "isUnique": true
+ },
+ "comment_likes_user_idx": {
+ "name": "comment_likes_user_idx",
+ "columns": [
+ "user_id"
+ ],
+ "isUnique": false
+ },
+ "comment_likes_comment_idx": {
+ "name": "comment_likes_comment_idx",
+ "columns": [
+ "comment_id"
+ ],
+ "isUnique": false
+ }
+ },
+ "foreignKeys": {
+ "comment_likes_comment_id_app_comments_id_fk": {
+ "name": "comment_likes_comment_id_app_comments_id_fk",
+ "tableFrom": "comment_likes",
+ "tableTo": "app_comments",
+ "columnsFrom": [
+ "comment_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "comment_likes_user_id_users_id_fk": {
+ "name": "comment_likes_user_id_users_id_fk",
+ "tableFrom": "comment_likes",
+ "tableTo": "users",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "checkConstraints": {}
+ },
+ "email_verification_tokens": {
+ "name": "email_verification_tokens",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "token_hash": {
+ "name": "token_hash",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "email": {
+ "name": "email",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "expires_at": {
+ "name": "expires_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "used": {
+ "name": "used",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false,
+ "default": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false,
+ "default": "CURRENT_TIMESTAMP"
+ }
+ },
+ "indexes": {
+ "email_verification_tokens_token_hash_unique": {
+ "name": "email_verification_tokens_token_hash_unique",
+ "columns": [
+ "token_hash"
+ ],
+ "isUnique": true
+ },
+ "email_verification_tokens_lookup_idx": {
+ "name": "email_verification_tokens_lookup_idx",
+ "columns": [
+ "token_hash"
+ ],
+ "isUnique": false
+ },
+ "email_verification_tokens_expiry_idx": {
+ "name": "email_verification_tokens_expiry_idx",
+ "columns": [
+ "expires_at"
+ ],
+ "isUnique": false
+ }
+ },
+ "foreignKeys": {
+ "email_verification_tokens_user_id_users_id_fk": {
+ "name": "email_verification_tokens_user_id_users_id_fk",
+ "tableFrom": "email_verification_tokens",
+ "tableTo": "users",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "checkConstraints": {}
+ },
+ "favorites": {
+ "name": "favorites",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "app_id": {
+ "name": "app_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false,
+ "default": "CURRENT_TIMESTAMP"
+ }
+ },
+ "indexes": {
+ "favorites_user_app_idx": {
+ "name": "favorites_user_app_idx",
+ "columns": [
+ "user_id",
+ "app_id"
+ ],
+ "isUnique": true
+ },
+ "favorites_user_idx": {
+ "name": "favorites_user_idx",
+ "columns": [
+ "user_id"
+ ],
+ "isUnique": false
+ },
+ "favorites_app_idx": {
+ "name": "favorites_app_idx",
+ "columns": [
+ "app_id"
+ ],
+ "isUnique": false
+ }
+ },
+ "foreignKeys": {
+ "favorites_user_id_users_id_fk": {
+ "name": "favorites_user_id_users_id_fk",
+ "tableFrom": "favorites",
+ "tableTo": "users",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "favorites_app_id_apps_id_fk": {
+ "name": "favorites_app_id_apps_id_fk",
+ "tableFrom": "favorites",
+ "tableTo": "apps",
+ "columnsFrom": [
+ "app_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "checkConstraints": {}
+ },
+ "oauth_states": {
+ "name": "oauth_states",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "state": {
+ "name": "state",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "provider": {
+ "name": "provider",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "redirect_uri": {
+ "name": "redirect_uri",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "scopes": {
+ "name": "scopes",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false,
+ "default": "'[]'"
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "code_verifier": {
+ "name": "code_verifier",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "nonce": {
+ "name": "nonce",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false,
+ "default": "CURRENT_TIMESTAMP"
+ },
+ "expires_at": {
+ "name": "expires_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "is_used": {
+ "name": "is_used",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false,
+ "default": false
+ }
+ },
+ "indexes": {
+ "oauth_states_state_unique": {
+ "name": "oauth_states_state_unique",
+ "columns": [
+ "state"
+ ],
+ "isUnique": true
+ },
+ "oauth_states_state_idx": {
+ "name": "oauth_states_state_idx",
+ "columns": [
+ "state"
+ ],
+ "isUnique": true
+ },
+ "oauth_states_expires_at_idx": {
+ "name": "oauth_states_expires_at_idx",
+ "columns": [
+ "expires_at"
+ ],
+ "isUnique": false
+ }
+ },
+ "foreignKeys": {
+ "oauth_states_user_id_users_id_fk": {
+ "name": "oauth_states_user_id_users_id_fk",
+ "tableFrom": "oauth_states",
+ "tableTo": "users",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "checkConstraints": {}
+ },
+ "password_reset_tokens": {
+ "name": "password_reset_tokens",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "token_hash": {
+ "name": "token_hash",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "expires_at": {
+ "name": "expires_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "used": {
+ "name": "used",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false,
+ "default": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false,
+ "default": "CURRENT_TIMESTAMP"
+ }
+ },
+ "indexes": {
+ "password_reset_tokens_token_hash_unique": {
+ "name": "password_reset_tokens_token_hash_unique",
+ "columns": [
+ "token_hash"
+ ],
+ "isUnique": true
+ },
+ "password_reset_tokens_lookup_idx": {
+ "name": "password_reset_tokens_lookup_idx",
+ "columns": [
+ "token_hash"
+ ],
+ "isUnique": false
+ },
+ "password_reset_tokens_expiry_idx": {
+ "name": "password_reset_tokens_expiry_idx",
+ "columns": [
+ "expires_at"
+ ],
+ "isUnique": false
+ }
+ },
+ "foreignKeys": {
+ "password_reset_tokens_user_id_users_id_fk": {
+ "name": "password_reset_tokens_user_id_users_id_fk",
+ "tableFrom": "password_reset_tokens",
+ "tableTo": "users",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "checkConstraints": {}
+ },
+ "sessions": {
+ "name": "sessions",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "device_info": {
+ "name": "device_info",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "user_agent": {
+ "name": "user_agent",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "ip_address": {
+ "name": "ip_address",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "is_revoked": {
+ "name": "is_revoked",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false,
+ "default": false
+ },
+ "revoked_at": {
+ "name": "revoked_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "revoked_reason": {
+ "name": "revoked_reason",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "access_token_hash": {
+ "name": "access_token_hash",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "refresh_token_hash": {
+ "name": "refresh_token_hash",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "expires_at": {
+ "name": "expires_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false,
+ "default": "CURRENT_TIMESTAMP"
+ },
+ "last_activity": {
+ "name": "last_activity",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ }
+ },
+ "indexes": {
+ "sessions_user_id_idx": {
+ "name": "sessions_user_id_idx",
+ "columns": [
+ "user_id"
+ ],
+ "isUnique": false
+ },
+ "sessions_expires_at_idx": {
+ "name": "sessions_expires_at_idx",
+ "columns": [
+ "expires_at"
+ ],
+ "isUnique": false
+ },
+ "sessions_access_token_hash_idx": {
+ "name": "sessions_access_token_hash_idx",
+ "columns": [
+ "access_token_hash"
+ ],
+ "isUnique": false
+ },
+ "sessions_refresh_token_hash_idx": {
+ "name": "sessions_refresh_token_hash_idx",
+ "columns": [
+ "refresh_token_hash"
+ ],
+ "isUnique": false
+ },
+ "sessions_last_activity_idx": {
+ "name": "sessions_last_activity_idx",
+ "columns": [
+ "last_activity"
+ ],
+ "isUnique": false
+ },
+ "sessions_is_revoked_idx": {
+ "name": "sessions_is_revoked_idx",
+ "columns": [
+ "is_revoked"
+ ],
+ "isUnique": false
+ }
+ },
+ "foreignKeys": {
+ "sessions_user_id_users_id_fk": {
+ "name": "sessions_user_id_users_id_fk",
+ "tableFrom": "sessions",
+ "tableTo": "users",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "checkConstraints": {}
+ },
+ "stars": {
+ "name": "stars",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "app_id": {
+ "name": "app_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "starred_at": {
+ "name": "starred_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false,
+ "default": "CURRENT_TIMESTAMP"
+ }
+ },
+ "indexes": {
+ "stars_user_app_idx": {
+ "name": "stars_user_app_idx",
+ "columns": [
+ "user_id",
+ "app_id"
+ ],
+ "isUnique": true
+ },
+ "stars_user_idx": {
+ "name": "stars_user_idx",
+ "columns": [
+ "user_id"
+ ],
+ "isUnique": false
+ },
+ "stars_app_idx": {
+ "name": "stars_app_idx",
+ "columns": [
+ "app_id"
+ ],
+ "isUnique": false
+ }
+ },
+ "foreignKeys": {
+ "stars_user_id_users_id_fk": {
+ "name": "stars_user_id_users_id_fk",
+ "tableFrom": "stars",
+ "tableTo": "users",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "stars_app_id_apps_id_fk": {
+ "name": "stars_app_id_apps_id_fk",
+ "tableFrom": "stars",
+ "tableTo": "apps",
+ "columnsFrom": [
+ "app_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "checkConstraints": {}
+ },
+ "system_settings": {
+ "name": "system_settings",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "key": {
+ "name": "key",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "value": {
+ "name": "value",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "description": {
+ "name": "description",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false,
+ "default": "CURRENT_TIMESTAMP"
+ },
+ "updated_by": {
+ "name": "updated_by",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ }
+ },
+ "indexes": {
+ "system_settings_key_unique": {
+ "name": "system_settings_key_unique",
+ "columns": [
+ "key"
+ ],
+ "isUnique": true
+ },
+ "system_settings_key_idx": {
+ "name": "system_settings_key_idx",
+ "columns": [
+ "key"
+ ],
+ "isUnique": true
+ }
+ },
+ "foreignKeys": {
+ "system_settings_updated_by_users_id_fk": {
+ "name": "system_settings_updated_by_users_id_fk",
+ "tableFrom": "system_settings",
+ "tableTo": "users",
+ "columnsFrom": [
+ "updated_by"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "checkConstraints": {}
+ },
+ "user_model_configs": {
+ "name": "user_model_configs",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "agent_action_name": {
+ "name": "agent_action_name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "model_name": {
+ "name": "model_name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "max_tokens": {
+ "name": "max_tokens",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "temperature": {
+ "name": "temperature",
+ "type": "real",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "reasoning_effort": {
+ "name": "reasoning_effort",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "provider_override": {
+ "name": "provider_override",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "fallback_model": {
+ "name": "fallback_model",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "is_active": {
+ "name": "is_active",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false,
+ "default": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false,
+ "default": "CURRENT_TIMESTAMP"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false,
+ "default": "CURRENT_TIMESTAMP"
+ }
+ },
+ "indexes": {
+ "user_model_configs_user_agent_idx": {
+ "name": "user_model_configs_user_agent_idx",
+ "columns": [
+ "user_id",
+ "agent_action_name"
+ ],
+ "isUnique": true
+ },
+ "user_model_configs_user_idx": {
+ "name": "user_model_configs_user_idx",
+ "columns": [
+ "user_id"
+ ],
+ "isUnique": false
+ },
+ "user_model_configs_is_active_idx": {
+ "name": "user_model_configs_is_active_idx",
+ "columns": [
+ "is_active"
+ ],
+ "isUnique": false
+ }
+ },
+ "foreignKeys": {
+ "user_model_configs_user_id_users_id_fk": {
+ "name": "user_model_configs_user_id_users_id_fk",
+ "tableFrom": "user_model_configs",
+ "tableTo": "users",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "checkConstraints": {}
+ },
+ "user_model_providers": {
+ "name": "user_model_providers",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "base_url": {
+ "name": "base_url",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "secret_id": {
+ "name": "secret_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "is_active": {
+ "name": "is_active",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false,
+ "default": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false,
+ "default": "CURRENT_TIMESTAMP"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false,
+ "default": "CURRENT_TIMESTAMP"
+ }
+ },
+ "indexes": {
+ "user_model_providers_user_name_idx": {
+ "name": "user_model_providers_user_name_idx",
+ "columns": [
+ "user_id",
+ "name"
+ ],
+ "isUnique": true
+ },
+ "user_model_providers_user_idx": {
+ "name": "user_model_providers_user_idx",
+ "columns": [
+ "user_id"
+ ],
+ "isUnique": false
+ },
+ "user_model_providers_is_active_idx": {
+ "name": "user_model_providers_is_active_idx",
+ "columns": [
+ "is_active"
+ ],
+ "isUnique": false
+ }
+ },
+ "foreignKeys": {
+ "user_model_providers_user_id_users_id_fk": {
+ "name": "user_model_providers_user_id_users_id_fk",
+ "tableFrom": "user_model_providers",
+ "tableTo": "users",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "user_model_providers_secret_id_user_secrets_id_fk": {
+ "name": "user_model_providers_secret_id_user_secrets_id_fk",
+ "tableFrom": "user_model_providers",
+ "tableTo": "user_secrets",
+ "columnsFrom": [
+ "secret_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "checkConstraints": {}
+ },
+ "user_secrets": {
+ "name": "user_secrets",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "provider": {
+ "name": "provider",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "secret_type": {
+ "name": "secret_type",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "encrypted_value": {
+ "name": "encrypted_value",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "key_preview": {
+ "name": "key_preview",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "description": {
+ "name": "description",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "expires_at": {
+ "name": "expires_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "last_used": {
+ "name": "last_used",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "usage_count": {
+ "name": "usage_count",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false,
+ "default": 0
+ },
+ "is_active": {
+ "name": "is_active",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false,
+ "default": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false,
+ "default": "CURRENT_TIMESTAMP"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false,
+ "default": "CURRENT_TIMESTAMP"
+ }
+ },
+ "indexes": {
+ "user_secrets_user_idx": {
+ "name": "user_secrets_user_idx",
+ "columns": [
+ "user_id"
+ ],
+ "isUnique": false
+ },
+ "user_secrets_provider_idx": {
+ "name": "user_secrets_provider_idx",
+ "columns": [
+ "provider"
+ ],
+ "isUnique": false
+ },
+ "user_secrets_user_provider_idx": {
+ "name": "user_secrets_user_provider_idx",
+ "columns": [
+ "user_id",
+ "provider",
+ "secret_type"
+ ],
+ "isUnique": false
+ },
+ "user_secrets_active_idx": {
+ "name": "user_secrets_active_idx",
+ "columns": [
+ "is_active"
+ ],
+ "isUnique": false
+ }
+ },
+ "foreignKeys": {
+ "user_secrets_user_id_users_id_fk": {
+ "name": "user_secrets_user_id_users_id_fk",
+ "tableFrom": "user_secrets",
+ "tableTo": "users",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "checkConstraints": {}
+ },
+ "users": {
+ "name": "users",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "email": {
+ "name": "email",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "username": {
+ "name": "username",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "display_name": {
+ "name": "display_name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "avatar_url": {
+ "name": "avatar_url",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "bio": {
+ "name": "bio",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "provider": {
+ "name": "provider",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "provider_id": {
+ "name": "provider_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "email_verified": {
+ "name": "email_verified",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false,
+ "default": false
+ },
+ "password_hash": {
+ "name": "password_hash",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "failed_login_attempts": {
+ "name": "failed_login_attempts",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false,
+ "default": 0
+ },
+ "locked_until": {
+ "name": "locked_until",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "password_changed_at": {
+ "name": "password_changed_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "preferences": {
+ "name": "preferences",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false,
+ "default": "'{}'"
+ },
+ "theme": {
+ "name": "theme",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false,
+ "default": "'system'"
+ },
+ "timezone": {
+ "name": "timezone",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false,
+ "default": "'UTC'"
+ },
+ "is_active": {
+ "name": "is_active",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false,
+ "default": true
+ },
+ "is_suspended": {
+ "name": "is_suspended",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false,
+ "default": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false,
+ "default": "CURRENT_TIMESTAMP"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false,
+ "default": "CURRENT_TIMESTAMP"
+ },
+ "last_active_at": {
+ "name": "last_active_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "deleted_at": {
+ "name": "deleted_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ }
+ },
+ "indexes": {
+ "users_email_unique": {
+ "name": "users_email_unique",
+ "columns": [
+ "email"
+ ],
+ "isUnique": true
+ },
+ "users_username_unique": {
+ "name": "users_username_unique",
+ "columns": [
+ "username"
+ ],
+ "isUnique": true
+ },
+ "users_email_idx": {
+ "name": "users_email_idx",
+ "columns": [
+ "email"
+ ],
+ "isUnique": false
+ },
+ "users_provider_unique_idx": {
+ "name": "users_provider_unique_idx",
+ "columns": [
+ "provider",
+ "provider_id"
+ ],
+ "isUnique": true
+ },
+ "users_username_idx": {
+ "name": "users_username_idx",
+ "columns": [
+ "username"
+ ],
+ "isUnique": false
+ },
+ "users_failed_login_attempts_idx": {
+ "name": "users_failed_login_attempts_idx",
+ "columns": [
+ "failed_login_attempts"
+ ],
+ "isUnique": false
+ },
+ "users_locked_until_idx": {
+ "name": "users_locked_until_idx",
+ "columns": [
+ "locked_until"
+ ],
+ "isUnique": false
+ },
+ "users_is_active_idx": {
+ "name": "users_is_active_idx",
+ "columns": [
+ "is_active"
+ ],
+ "isUnique": false
+ },
+ "users_last_active_at_idx": {
+ "name": "users_last_active_at_idx",
+ "columns": [
+ "last_active_at"
+ ],
+ "isUnique": false
+ }
+ },
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "checkConstraints": {}
+ },
+ "verification_otps": {
+ "name": "verification_otps",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "email": {
+ "name": "email",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "otp": {
+ "name": "otp",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "expires_at": {
+ "name": "expires_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "used": {
+ "name": "used",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false,
+ "default": false
+ },
+ "used_at": {
+ "name": "used_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false,
+ "default": "CURRENT_TIMESTAMP"
+ }
+ },
+ "indexes": {
+ "verification_otps_email_idx": {
+ "name": "verification_otps_email_idx",
+ "columns": [
+ "email"
+ ],
+ "isUnique": false
+ },
+ "verification_otps_expires_at_idx": {
+ "name": "verification_otps_expires_at_idx",
+ "columns": [
+ "expires_at"
+ ],
+ "isUnique": false
+ },
+ "verification_otps_used_idx": {
+ "name": "verification_otps_used_idx",
+ "columns": [
+ "used"
+ ],
+ "isUnique": false
+ }
+ },
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "checkConstraints": {}
+ }
+ },
+ "views": {},
+ "enums": {},
+ "_meta": {
+ "schemas": {},
+ "tables": {},
+ "columns": {
+ "\"apps\".\"deployment_url\"": "\"apps\".\"deployment_id\""
+ }
+ },
+ "internal": {
+ "indexes": {}
+ }
+}
\ No newline at end of file
diff --git a/external/drpower-vibe-production/migrations/meta/0003_snapshot.json b/external/drpower-vibe-production/migrations/meta/0003_snapshot.json
new file mode 100644
index 00000000..b98f1c71
--- /dev/null
+++ b/external/drpower-vibe-production/migrations/meta/0003_snapshot.json
@@ -0,0 +1,2566 @@
+{
+ "version": "6",
+ "dialect": "sqlite",
+ "id": "94bca5d1-b73e-4b73-840d-58ea6448cac1",
+ "prevId": "c6d41ea1-5cb3-4ead-9678-e9f51e8ff424",
+ "tables": {
+ "api_keys": {
+ "name": "api_keys",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "key_hash": {
+ "name": "key_hash",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "key_preview": {
+ "name": "key_preview",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "scopes": {
+ "name": "scopes",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "is_active": {
+ "name": "is_active",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false,
+ "default": true
+ },
+ "last_used": {
+ "name": "last_used",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "request_count": {
+ "name": "request_count",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false,
+ "default": 0
+ },
+ "expires_at": {
+ "name": "expires_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false,
+ "default": "CURRENT_TIMESTAMP"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false,
+ "default": "CURRENT_TIMESTAMP"
+ }
+ },
+ "indexes": {
+ "api_keys_key_hash_unique": {
+ "name": "api_keys_key_hash_unique",
+ "columns": [
+ "key_hash"
+ ],
+ "isUnique": true
+ },
+ "api_keys_user_id_idx": {
+ "name": "api_keys_user_id_idx",
+ "columns": [
+ "user_id"
+ ],
+ "isUnique": false
+ },
+ "api_keys_key_hash_idx": {
+ "name": "api_keys_key_hash_idx",
+ "columns": [
+ "key_hash"
+ ],
+ "isUnique": false
+ },
+ "api_keys_is_active_idx": {
+ "name": "api_keys_is_active_idx",
+ "columns": [
+ "is_active"
+ ],
+ "isUnique": false
+ },
+ "api_keys_expires_at_idx": {
+ "name": "api_keys_expires_at_idx",
+ "columns": [
+ "expires_at"
+ ],
+ "isUnique": false
+ }
+ },
+ "foreignKeys": {
+ "api_keys_user_id_users_id_fk": {
+ "name": "api_keys_user_id_users_id_fk",
+ "tableFrom": "api_keys",
+ "tableTo": "users",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "checkConstraints": {}
+ },
+ "app_comments": {
+ "name": "app_comments",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "app_id": {
+ "name": "app_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "content": {
+ "name": "content",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "parent_comment_id": {
+ "name": "parent_comment_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "is_edited": {
+ "name": "is_edited",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false,
+ "default": false
+ },
+ "is_deleted": {
+ "name": "is_deleted",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false,
+ "default": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false,
+ "default": "CURRENT_TIMESTAMP"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false,
+ "default": "CURRENT_TIMESTAMP"
+ }
+ },
+ "indexes": {
+ "app_comments_app_idx": {
+ "name": "app_comments_app_idx",
+ "columns": [
+ "app_id"
+ ],
+ "isUnique": false
+ },
+ "app_comments_user_idx": {
+ "name": "app_comments_user_idx",
+ "columns": [
+ "user_id"
+ ],
+ "isUnique": false
+ },
+ "app_comments_parent_idx": {
+ "name": "app_comments_parent_idx",
+ "columns": [
+ "parent_comment_id"
+ ],
+ "isUnique": false
+ }
+ },
+ "foreignKeys": {
+ "app_comments_app_id_apps_id_fk": {
+ "name": "app_comments_app_id_apps_id_fk",
+ "tableFrom": "app_comments",
+ "tableTo": "apps",
+ "columnsFrom": [
+ "app_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "app_comments_user_id_users_id_fk": {
+ "name": "app_comments_user_id_users_id_fk",
+ "tableFrom": "app_comments",
+ "tableTo": "users",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "checkConstraints": {}
+ },
+ "app_likes": {
+ "name": "app_likes",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "app_id": {
+ "name": "app_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "reaction_type": {
+ "name": "reaction_type",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": "'like'"
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false,
+ "default": "CURRENT_TIMESTAMP"
+ }
+ },
+ "indexes": {
+ "app_likes_app_user_idx": {
+ "name": "app_likes_app_user_idx",
+ "columns": [
+ "app_id",
+ "user_id"
+ ],
+ "isUnique": true
+ },
+ "app_likes_user_idx": {
+ "name": "app_likes_user_idx",
+ "columns": [
+ "user_id"
+ ],
+ "isUnique": false
+ }
+ },
+ "foreignKeys": {
+ "app_likes_app_id_apps_id_fk": {
+ "name": "app_likes_app_id_apps_id_fk",
+ "tableFrom": "app_likes",
+ "tableTo": "apps",
+ "columnsFrom": [
+ "app_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "app_likes_user_id_users_id_fk": {
+ "name": "app_likes_user_id_users_id_fk",
+ "tableFrom": "app_likes",
+ "tableTo": "users",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "checkConstraints": {}
+ },
+ "app_views": {
+ "name": "app_views",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "app_id": {
+ "name": "app_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "session_token": {
+ "name": "session_token",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "ip_address_hash": {
+ "name": "ip_address_hash",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "referrer": {
+ "name": "referrer",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "user_agent": {
+ "name": "user_agent",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "device_type": {
+ "name": "device_type",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "viewed_at": {
+ "name": "viewed_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false,
+ "default": "CURRENT_TIMESTAMP"
+ },
+ "duration_seconds": {
+ "name": "duration_seconds",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ }
+ },
+ "indexes": {
+ "app_views_app_idx": {
+ "name": "app_views_app_idx",
+ "columns": [
+ "app_id"
+ ],
+ "isUnique": false
+ },
+ "app_views_user_idx": {
+ "name": "app_views_user_idx",
+ "columns": [
+ "user_id"
+ ],
+ "isUnique": false
+ },
+ "app_views_viewed_at_idx": {
+ "name": "app_views_viewed_at_idx",
+ "columns": [
+ "viewed_at"
+ ],
+ "isUnique": false
+ },
+ "app_views_app_viewed_at_idx": {
+ "name": "app_views_app_viewed_at_idx",
+ "columns": [
+ "app_id",
+ "viewed_at"
+ ],
+ "isUnique": false
+ }
+ },
+ "foreignKeys": {
+ "app_views_app_id_apps_id_fk": {
+ "name": "app_views_app_id_apps_id_fk",
+ "tableFrom": "app_views",
+ "tableTo": "apps",
+ "columnsFrom": [
+ "app_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "app_views_user_id_users_id_fk": {
+ "name": "app_views_user_id_users_id_fk",
+ "tableFrom": "app_views",
+ "tableTo": "users",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "checkConstraints": {}
+ },
+ "apps": {
+ "name": "apps",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "title": {
+ "name": "title",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "description": {
+ "name": "description",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "icon_url": {
+ "name": "icon_url",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "original_prompt": {
+ "name": "original_prompt",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "final_prompt": {
+ "name": "final_prompt",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "framework": {
+ "name": "framework",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "session_token": {
+ "name": "session_token",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "visibility": {
+ "name": "visibility",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": "'private'"
+ },
+ "status": {
+ "name": "status",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": "'generating'"
+ },
+ "deployment_id": {
+ "name": "deployment_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "github_repository_url": {
+ "name": "github_repository_url",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "github_repository_visibility": {
+ "name": "github_repository_visibility",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "is_archived": {
+ "name": "is_archived",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false,
+ "default": false
+ },
+ "is_featured": {
+ "name": "is_featured",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false,
+ "default": false
+ },
+ "version": {
+ "name": "version",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false,
+ "default": 1
+ },
+ "parent_app_id": {
+ "name": "parent_app_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "screenshot_url": {
+ "name": "screenshot_url",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "screenshot_captured_at": {
+ "name": "screenshot_captured_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false,
+ "default": "CURRENT_TIMESTAMP"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false,
+ "default": "CURRENT_TIMESTAMP"
+ },
+ "last_deployed_at": {
+ "name": "last_deployed_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ }
+ },
+ "indexes": {
+ "apps_user_idx": {
+ "name": "apps_user_idx",
+ "columns": [
+ "user_id"
+ ],
+ "isUnique": false
+ },
+ "apps_status_idx": {
+ "name": "apps_status_idx",
+ "columns": [
+ "status"
+ ],
+ "isUnique": false
+ },
+ "apps_visibility_idx": {
+ "name": "apps_visibility_idx",
+ "columns": [
+ "visibility"
+ ],
+ "isUnique": false
+ },
+ "apps_session_token_idx": {
+ "name": "apps_session_token_idx",
+ "columns": [
+ "session_token"
+ ],
+ "isUnique": false
+ },
+ "apps_parent_app_idx": {
+ "name": "apps_parent_app_idx",
+ "columns": [
+ "parent_app_id"
+ ],
+ "isUnique": false
+ },
+ "apps_search_idx": {
+ "name": "apps_search_idx",
+ "columns": [
+ "title",
+ "description"
+ ],
+ "isUnique": false
+ },
+ "apps_framework_status_idx": {
+ "name": "apps_framework_status_idx",
+ "columns": [
+ "framework",
+ "status"
+ ],
+ "isUnique": false
+ },
+ "apps_visibility_status_idx": {
+ "name": "apps_visibility_status_idx",
+ "columns": [
+ "visibility",
+ "status"
+ ],
+ "isUnique": false
+ },
+ "apps_created_at_idx": {
+ "name": "apps_created_at_idx",
+ "columns": [
+ "created_at"
+ ],
+ "isUnique": false
+ },
+ "apps_updated_at_idx": {
+ "name": "apps_updated_at_idx",
+ "columns": [
+ "updated_at"
+ ],
+ "isUnique": false
+ }
+ },
+ "foreignKeys": {
+ "apps_user_id_users_id_fk": {
+ "name": "apps_user_id_users_id_fk",
+ "tableFrom": "apps",
+ "tableTo": "users",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "checkConstraints": {}
+ },
+ "audit_logs": {
+ "name": "audit_logs",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "entity_type": {
+ "name": "entity_type",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "entity_id": {
+ "name": "entity_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "action": {
+ "name": "action",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "old_values": {
+ "name": "old_values",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "new_values": {
+ "name": "new_values",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "ip_address": {
+ "name": "ip_address",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "user_agent": {
+ "name": "user_agent",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false,
+ "default": "CURRENT_TIMESTAMP"
+ }
+ },
+ "indexes": {
+ "audit_logs_user_idx": {
+ "name": "audit_logs_user_idx",
+ "columns": [
+ "user_id"
+ ],
+ "isUnique": false
+ },
+ "audit_logs_entity_idx": {
+ "name": "audit_logs_entity_idx",
+ "columns": [
+ "entity_type",
+ "entity_id"
+ ],
+ "isUnique": false
+ },
+ "audit_logs_created_at_idx": {
+ "name": "audit_logs_created_at_idx",
+ "columns": [
+ "created_at"
+ ],
+ "isUnique": false
+ }
+ },
+ "foreignKeys": {
+ "audit_logs_user_id_users_id_fk": {
+ "name": "audit_logs_user_id_users_id_fk",
+ "tableFrom": "audit_logs",
+ "tableTo": "users",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "checkConstraints": {}
+ },
+ "auth_attempts": {
+ "name": "auth_attempts",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "integer",
+ "primaryKey": true,
+ "notNull": true,
+ "autoincrement": true
+ },
+ "identifier": {
+ "name": "identifier",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "attempt_type": {
+ "name": "attempt_type",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "success": {
+ "name": "success",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "ip_address": {
+ "name": "ip_address",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "user_agent": {
+ "name": "user_agent",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "attempted_at": {
+ "name": "attempted_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false,
+ "default": "CURRENT_TIMESTAMP"
+ }
+ },
+ "indexes": {
+ "auth_attempts_lookup_idx": {
+ "name": "auth_attempts_lookup_idx",
+ "columns": [
+ "identifier",
+ "attempted_at"
+ ],
+ "isUnique": false
+ },
+ "auth_attempts_ip_idx": {
+ "name": "auth_attempts_ip_idx",
+ "columns": [
+ "ip_address",
+ "attempted_at"
+ ],
+ "isUnique": false
+ },
+ "auth_attempts_success_idx": {
+ "name": "auth_attempts_success_idx",
+ "columns": [
+ "success",
+ "attempted_at"
+ ],
+ "isUnique": false
+ },
+ "auth_attempts_type_idx": {
+ "name": "auth_attempts_type_idx",
+ "columns": [
+ "attempt_type",
+ "attempted_at"
+ ],
+ "isUnique": false
+ }
+ },
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "checkConstraints": {}
+ },
+ "comment_likes": {
+ "name": "comment_likes",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "comment_id": {
+ "name": "comment_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "reaction_type": {
+ "name": "reaction_type",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": "'like'"
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false,
+ "default": "CURRENT_TIMESTAMP"
+ }
+ },
+ "indexes": {
+ "comment_likes_comment_user_idx": {
+ "name": "comment_likes_comment_user_idx",
+ "columns": [
+ "comment_id",
+ "user_id"
+ ],
+ "isUnique": true
+ },
+ "comment_likes_user_idx": {
+ "name": "comment_likes_user_idx",
+ "columns": [
+ "user_id"
+ ],
+ "isUnique": false
+ },
+ "comment_likes_comment_idx": {
+ "name": "comment_likes_comment_idx",
+ "columns": [
+ "comment_id"
+ ],
+ "isUnique": false
+ }
+ },
+ "foreignKeys": {
+ "comment_likes_comment_id_app_comments_id_fk": {
+ "name": "comment_likes_comment_id_app_comments_id_fk",
+ "tableFrom": "comment_likes",
+ "tableTo": "app_comments",
+ "columnsFrom": [
+ "comment_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "comment_likes_user_id_users_id_fk": {
+ "name": "comment_likes_user_id_users_id_fk",
+ "tableFrom": "comment_likes",
+ "tableTo": "users",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "checkConstraints": {}
+ },
+ "email_verification_tokens": {
+ "name": "email_verification_tokens",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "token_hash": {
+ "name": "token_hash",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "email": {
+ "name": "email",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "expires_at": {
+ "name": "expires_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "used": {
+ "name": "used",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false,
+ "default": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false,
+ "default": "CURRENT_TIMESTAMP"
+ }
+ },
+ "indexes": {
+ "email_verification_tokens_token_hash_unique": {
+ "name": "email_verification_tokens_token_hash_unique",
+ "columns": [
+ "token_hash"
+ ],
+ "isUnique": true
+ },
+ "email_verification_tokens_lookup_idx": {
+ "name": "email_verification_tokens_lookup_idx",
+ "columns": [
+ "token_hash"
+ ],
+ "isUnique": false
+ },
+ "email_verification_tokens_expiry_idx": {
+ "name": "email_verification_tokens_expiry_idx",
+ "columns": [
+ "expires_at"
+ ],
+ "isUnique": false
+ }
+ },
+ "foreignKeys": {
+ "email_verification_tokens_user_id_users_id_fk": {
+ "name": "email_verification_tokens_user_id_users_id_fk",
+ "tableFrom": "email_verification_tokens",
+ "tableTo": "users",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "checkConstraints": {}
+ },
+ "favorites": {
+ "name": "favorites",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "app_id": {
+ "name": "app_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false,
+ "default": "CURRENT_TIMESTAMP"
+ }
+ },
+ "indexes": {
+ "favorites_user_app_idx": {
+ "name": "favorites_user_app_idx",
+ "columns": [
+ "user_id",
+ "app_id"
+ ],
+ "isUnique": true
+ },
+ "favorites_user_idx": {
+ "name": "favorites_user_idx",
+ "columns": [
+ "user_id"
+ ],
+ "isUnique": false
+ },
+ "favorites_app_idx": {
+ "name": "favorites_app_idx",
+ "columns": [
+ "app_id"
+ ],
+ "isUnique": false
+ }
+ },
+ "foreignKeys": {
+ "favorites_user_id_users_id_fk": {
+ "name": "favorites_user_id_users_id_fk",
+ "tableFrom": "favorites",
+ "tableTo": "users",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "favorites_app_id_apps_id_fk": {
+ "name": "favorites_app_id_apps_id_fk",
+ "tableFrom": "favorites",
+ "tableTo": "apps",
+ "columnsFrom": [
+ "app_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "checkConstraints": {}
+ },
+ "oauth_states": {
+ "name": "oauth_states",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "state": {
+ "name": "state",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "provider": {
+ "name": "provider",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "redirect_uri": {
+ "name": "redirect_uri",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "scopes": {
+ "name": "scopes",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false,
+ "default": "'[]'"
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "code_verifier": {
+ "name": "code_verifier",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "nonce": {
+ "name": "nonce",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false,
+ "default": "CURRENT_TIMESTAMP"
+ },
+ "expires_at": {
+ "name": "expires_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "is_used": {
+ "name": "is_used",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false,
+ "default": false
+ }
+ },
+ "indexes": {
+ "oauth_states_state_unique": {
+ "name": "oauth_states_state_unique",
+ "columns": [
+ "state"
+ ],
+ "isUnique": true
+ },
+ "oauth_states_state_idx": {
+ "name": "oauth_states_state_idx",
+ "columns": [
+ "state"
+ ],
+ "isUnique": true
+ },
+ "oauth_states_expires_at_idx": {
+ "name": "oauth_states_expires_at_idx",
+ "columns": [
+ "expires_at"
+ ],
+ "isUnique": false
+ }
+ },
+ "foreignKeys": {
+ "oauth_states_user_id_users_id_fk": {
+ "name": "oauth_states_user_id_users_id_fk",
+ "tableFrom": "oauth_states",
+ "tableTo": "users",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "checkConstraints": {}
+ },
+ "password_reset_tokens": {
+ "name": "password_reset_tokens",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "token_hash": {
+ "name": "token_hash",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "expires_at": {
+ "name": "expires_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "used": {
+ "name": "used",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false,
+ "default": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false,
+ "default": "CURRENT_TIMESTAMP"
+ }
+ },
+ "indexes": {
+ "password_reset_tokens_token_hash_unique": {
+ "name": "password_reset_tokens_token_hash_unique",
+ "columns": [
+ "token_hash"
+ ],
+ "isUnique": true
+ },
+ "password_reset_tokens_lookup_idx": {
+ "name": "password_reset_tokens_lookup_idx",
+ "columns": [
+ "token_hash"
+ ],
+ "isUnique": false
+ },
+ "password_reset_tokens_expiry_idx": {
+ "name": "password_reset_tokens_expiry_idx",
+ "columns": [
+ "expires_at"
+ ],
+ "isUnique": false
+ }
+ },
+ "foreignKeys": {
+ "password_reset_tokens_user_id_users_id_fk": {
+ "name": "password_reset_tokens_user_id_users_id_fk",
+ "tableFrom": "password_reset_tokens",
+ "tableTo": "users",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "checkConstraints": {}
+ },
+ "sessions": {
+ "name": "sessions",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "device_info": {
+ "name": "device_info",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "user_agent": {
+ "name": "user_agent",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "ip_address": {
+ "name": "ip_address",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "is_revoked": {
+ "name": "is_revoked",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false,
+ "default": false
+ },
+ "revoked_at": {
+ "name": "revoked_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "revoked_reason": {
+ "name": "revoked_reason",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "access_token_hash": {
+ "name": "access_token_hash",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "refresh_token_hash": {
+ "name": "refresh_token_hash",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "expires_at": {
+ "name": "expires_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false,
+ "default": "CURRENT_TIMESTAMP"
+ },
+ "last_activity": {
+ "name": "last_activity",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ }
+ },
+ "indexes": {
+ "sessions_user_id_idx": {
+ "name": "sessions_user_id_idx",
+ "columns": [
+ "user_id"
+ ],
+ "isUnique": false
+ },
+ "sessions_expires_at_idx": {
+ "name": "sessions_expires_at_idx",
+ "columns": [
+ "expires_at"
+ ],
+ "isUnique": false
+ },
+ "sessions_access_token_hash_idx": {
+ "name": "sessions_access_token_hash_idx",
+ "columns": [
+ "access_token_hash"
+ ],
+ "isUnique": false
+ },
+ "sessions_refresh_token_hash_idx": {
+ "name": "sessions_refresh_token_hash_idx",
+ "columns": [
+ "refresh_token_hash"
+ ],
+ "isUnique": false
+ },
+ "sessions_last_activity_idx": {
+ "name": "sessions_last_activity_idx",
+ "columns": [
+ "last_activity"
+ ],
+ "isUnique": false
+ },
+ "sessions_is_revoked_idx": {
+ "name": "sessions_is_revoked_idx",
+ "columns": [
+ "is_revoked"
+ ],
+ "isUnique": false
+ }
+ },
+ "foreignKeys": {
+ "sessions_user_id_users_id_fk": {
+ "name": "sessions_user_id_users_id_fk",
+ "tableFrom": "sessions",
+ "tableTo": "users",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "checkConstraints": {}
+ },
+ "stars": {
+ "name": "stars",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "app_id": {
+ "name": "app_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "starred_at": {
+ "name": "starred_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false,
+ "default": "CURRENT_TIMESTAMP"
+ }
+ },
+ "indexes": {
+ "stars_user_app_idx": {
+ "name": "stars_user_app_idx",
+ "columns": [
+ "user_id",
+ "app_id"
+ ],
+ "isUnique": true
+ },
+ "stars_user_idx": {
+ "name": "stars_user_idx",
+ "columns": [
+ "user_id"
+ ],
+ "isUnique": false
+ },
+ "stars_app_idx": {
+ "name": "stars_app_idx",
+ "columns": [
+ "app_id"
+ ],
+ "isUnique": false
+ },
+ "stars_app_starred_at_idx": {
+ "name": "stars_app_starred_at_idx",
+ "columns": [
+ "app_id",
+ "starred_at"
+ ],
+ "isUnique": false
+ }
+ },
+ "foreignKeys": {
+ "stars_user_id_users_id_fk": {
+ "name": "stars_user_id_users_id_fk",
+ "tableFrom": "stars",
+ "tableTo": "users",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "stars_app_id_apps_id_fk": {
+ "name": "stars_app_id_apps_id_fk",
+ "tableFrom": "stars",
+ "tableTo": "apps",
+ "columnsFrom": [
+ "app_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "checkConstraints": {}
+ },
+ "system_settings": {
+ "name": "system_settings",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "key": {
+ "name": "key",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "value": {
+ "name": "value",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "description": {
+ "name": "description",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false,
+ "default": "CURRENT_TIMESTAMP"
+ },
+ "updated_by": {
+ "name": "updated_by",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ }
+ },
+ "indexes": {
+ "system_settings_key_unique": {
+ "name": "system_settings_key_unique",
+ "columns": [
+ "key"
+ ],
+ "isUnique": true
+ },
+ "system_settings_key_idx": {
+ "name": "system_settings_key_idx",
+ "columns": [
+ "key"
+ ],
+ "isUnique": true
+ }
+ },
+ "foreignKeys": {
+ "system_settings_updated_by_users_id_fk": {
+ "name": "system_settings_updated_by_users_id_fk",
+ "tableFrom": "system_settings",
+ "tableTo": "users",
+ "columnsFrom": [
+ "updated_by"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "checkConstraints": {}
+ },
+ "user_model_configs": {
+ "name": "user_model_configs",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "agent_action_name": {
+ "name": "agent_action_name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "model_name": {
+ "name": "model_name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "max_tokens": {
+ "name": "max_tokens",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "temperature": {
+ "name": "temperature",
+ "type": "real",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "reasoning_effort": {
+ "name": "reasoning_effort",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "provider_override": {
+ "name": "provider_override",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "fallback_model": {
+ "name": "fallback_model",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "is_active": {
+ "name": "is_active",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false,
+ "default": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false,
+ "default": "CURRENT_TIMESTAMP"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false,
+ "default": "CURRENT_TIMESTAMP"
+ }
+ },
+ "indexes": {
+ "user_model_configs_user_agent_idx": {
+ "name": "user_model_configs_user_agent_idx",
+ "columns": [
+ "user_id",
+ "agent_action_name"
+ ],
+ "isUnique": true
+ },
+ "user_model_configs_user_idx": {
+ "name": "user_model_configs_user_idx",
+ "columns": [
+ "user_id"
+ ],
+ "isUnique": false
+ },
+ "user_model_configs_is_active_idx": {
+ "name": "user_model_configs_is_active_idx",
+ "columns": [
+ "is_active"
+ ],
+ "isUnique": false
+ }
+ },
+ "foreignKeys": {
+ "user_model_configs_user_id_users_id_fk": {
+ "name": "user_model_configs_user_id_users_id_fk",
+ "tableFrom": "user_model_configs",
+ "tableTo": "users",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "checkConstraints": {}
+ },
+ "user_model_providers": {
+ "name": "user_model_providers",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "base_url": {
+ "name": "base_url",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "secret_id": {
+ "name": "secret_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "is_active": {
+ "name": "is_active",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false,
+ "default": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false,
+ "default": "CURRENT_TIMESTAMP"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false,
+ "default": "CURRENT_TIMESTAMP"
+ }
+ },
+ "indexes": {
+ "user_model_providers_user_name_idx": {
+ "name": "user_model_providers_user_name_idx",
+ "columns": [
+ "user_id",
+ "name"
+ ],
+ "isUnique": true
+ },
+ "user_model_providers_user_idx": {
+ "name": "user_model_providers_user_idx",
+ "columns": [
+ "user_id"
+ ],
+ "isUnique": false
+ },
+ "user_model_providers_is_active_idx": {
+ "name": "user_model_providers_is_active_idx",
+ "columns": [
+ "is_active"
+ ],
+ "isUnique": false
+ }
+ },
+ "foreignKeys": {
+ "user_model_providers_user_id_users_id_fk": {
+ "name": "user_model_providers_user_id_users_id_fk",
+ "tableFrom": "user_model_providers",
+ "tableTo": "users",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "user_model_providers_secret_id_user_secrets_id_fk": {
+ "name": "user_model_providers_secret_id_user_secrets_id_fk",
+ "tableFrom": "user_model_providers",
+ "tableTo": "user_secrets",
+ "columnsFrom": [
+ "secret_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "checkConstraints": {}
+ },
+ "user_secrets": {
+ "name": "user_secrets",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "provider": {
+ "name": "provider",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "secret_type": {
+ "name": "secret_type",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "encrypted_value": {
+ "name": "encrypted_value",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "key_preview": {
+ "name": "key_preview",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "description": {
+ "name": "description",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "expires_at": {
+ "name": "expires_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "last_used": {
+ "name": "last_used",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "usage_count": {
+ "name": "usage_count",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false,
+ "default": 0
+ },
+ "is_active": {
+ "name": "is_active",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false,
+ "default": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false,
+ "default": "CURRENT_TIMESTAMP"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false,
+ "default": "CURRENT_TIMESTAMP"
+ }
+ },
+ "indexes": {
+ "user_secrets_user_idx": {
+ "name": "user_secrets_user_idx",
+ "columns": [
+ "user_id"
+ ],
+ "isUnique": false
+ },
+ "user_secrets_provider_idx": {
+ "name": "user_secrets_provider_idx",
+ "columns": [
+ "provider"
+ ],
+ "isUnique": false
+ },
+ "user_secrets_user_provider_idx": {
+ "name": "user_secrets_user_provider_idx",
+ "columns": [
+ "user_id",
+ "provider",
+ "secret_type"
+ ],
+ "isUnique": false
+ },
+ "user_secrets_active_idx": {
+ "name": "user_secrets_active_idx",
+ "columns": [
+ "is_active"
+ ],
+ "isUnique": false
+ }
+ },
+ "foreignKeys": {
+ "user_secrets_user_id_users_id_fk": {
+ "name": "user_secrets_user_id_users_id_fk",
+ "tableFrom": "user_secrets",
+ "tableTo": "users",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "checkConstraints": {}
+ },
+ "users": {
+ "name": "users",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "email": {
+ "name": "email",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "username": {
+ "name": "username",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "display_name": {
+ "name": "display_name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "avatar_url": {
+ "name": "avatar_url",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "bio": {
+ "name": "bio",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "provider": {
+ "name": "provider",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "provider_id": {
+ "name": "provider_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "email_verified": {
+ "name": "email_verified",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false,
+ "default": false
+ },
+ "password_hash": {
+ "name": "password_hash",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "failed_login_attempts": {
+ "name": "failed_login_attempts",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false,
+ "default": 0
+ },
+ "locked_until": {
+ "name": "locked_until",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "password_changed_at": {
+ "name": "password_changed_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "preferences": {
+ "name": "preferences",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false,
+ "default": "'{}'"
+ },
+ "theme": {
+ "name": "theme",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false,
+ "default": "'system'"
+ },
+ "timezone": {
+ "name": "timezone",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false,
+ "default": "'UTC'"
+ },
+ "is_active": {
+ "name": "is_active",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false,
+ "default": true
+ },
+ "is_suspended": {
+ "name": "is_suspended",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false,
+ "default": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false,
+ "default": "CURRENT_TIMESTAMP"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false,
+ "default": "CURRENT_TIMESTAMP"
+ },
+ "last_active_at": {
+ "name": "last_active_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "deleted_at": {
+ "name": "deleted_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ }
+ },
+ "indexes": {
+ "users_email_unique": {
+ "name": "users_email_unique",
+ "columns": [
+ "email"
+ ],
+ "isUnique": true
+ },
+ "users_username_unique": {
+ "name": "users_username_unique",
+ "columns": [
+ "username"
+ ],
+ "isUnique": true
+ },
+ "users_email_idx": {
+ "name": "users_email_idx",
+ "columns": [
+ "email"
+ ],
+ "isUnique": false
+ },
+ "users_provider_unique_idx": {
+ "name": "users_provider_unique_idx",
+ "columns": [
+ "provider",
+ "provider_id"
+ ],
+ "isUnique": true
+ },
+ "users_username_idx": {
+ "name": "users_username_idx",
+ "columns": [
+ "username"
+ ],
+ "isUnique": false
+ },
+ "users_failed_login_attempts_idx": {
+ "name": "users_failed_login_attempts_idx",
+ "columns": [
+ "failed_login_attempts"
+ ],
+ "isUnique": false
+ },
+ "users_locked_until_idx": {
+ "name": "users_locked_until_idx",
+ "columns": [
+ "locked_until"
+ ],
+ "isUnique": false
+ },
+ "users_is_active_idx": {
+ "name": "users_is_active_idx",
+ "columns": [
+ "is_active"
+ ],
+ "isUnique": false
+ },
+ "users_last_active_at_idx": {
+ "name": "users_last_active_at_idx",
+ "columns": [
+ "last_active_at"
+ ],
+ "isUnique": false
+ }
+ },
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "checkConstraints": {}
+ },
+ "verification_otps": {
+ "name": "verification_otps",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "email": {
+ "name": "email",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "otp": {
+ "name": "otp",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "expires_at": {
+ "name": "expires_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "used": {
+ "name": "used",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false,
+ "default": false
+ },
+ "used_at": {
+ "name": "used_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false,
+ "default": "CURRENT_TIMESTAMP"
+ }
+ },
+ "indexes": {
+ "verification_otps_email_idx": {
+ "name": "verification_otps_email_idx",
+ "columns": [
+ "email"
+ ],
+ "isUnique": false
+ },
+ "verification_otps_expires_at_idx": {
+ "name": "verification_otps_expires_at_idx",
+ "columns": [
+ "expires_at"
+ ],
+ "isUnique": false
+ },
+ "verification_otps_used_idx": {
+ "name": "verification_otps_used_idx",
+ "columns": [
+ "used"
+ ],
+ "isUnique": false
+ }
+ },
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "checkConstraints": {}
+ }
+ },
+ "views": {},
+ "enums": {},
+ "_meta": {
+ "schemas": {},
+ "tables": {},
+ "columns": {}
+ },
+ "internal": {
+ "indexes": {}
+ }
+}
\ No newline at end of file
diff --git a/external/drpower-vibe-production/migrations/meta/_journal.json b/external/drpower-vibe-production/migrations/meta/_journal.json
new file mode 100644
index 00000000..f962262a
--- /dev/null
+++ b/external/drpower-vibe-production/migrations/meta/_journal.json
@@ -0,0 +1,34 @@
+{
+ "version": "7",
+ "dialect": "sqlite",
+ "entries": [
+ {
+ "idx": 0,
+ "version": "6",
+ "when": 1757000935039,
+ "tag": "0000_living_forge",
+ "breakpoints": true
+ },
+ {
+ "idx": 1,
+ "version": "6",
+ "when": 1757447858711,
+ "tag": "0001_married_moondragon",
+ "breakpoints": true
+ },
+ {
+ "idx": 2,
+ "version": "6",
+ "when": 1758608665393,
+ "tag": "0002_nebulous_fantastic_four",
+ "breakpoints": true
+ },
+ {
+ "idx": 3,
+ "version": "6",
+ "when": 1759483967805,
+ "tag": "0003_bumpy_albert_cleary",
+ "breakpoints": true
+ }
+ ]
+}
\ No newline at end of file
diff --git a/external/drpower-vibe-production/package.json b/external/drpower-vibe-production/package.json
new file mode 100644
index 00000000..10413beb
--- /dev/null
+++ b/external/drpower-vibe-production/package.json
@@ -0,0 +1,161 @@
+{
+ "name": "drpower-vibe-production",
+ "private": true,
+ "version": "0.0.0",
+ "type": "module",
+ "scripts": {
+ "setup": "tsx scripts/setup.ts",
+ "dev": "DEV_MODE=true vite",
+ "build": "tsc -b --incremental && vite build",
+ "lint": "eslint .",
+ "preview": "npm run build && vite preview",
+ "deploy": "bun --env-file .prod.vars scripts/deploy.ts",
+ "cf-typegen": "wrangler types --include-runtime false",
+ "test": "vitest run",
+ "test:watch": "vitest",
+ "test:coverage": "vitest run --coverage",
+ "db:generate": "drizzle-kit generate --config=drizzle.config.local.ts",
+ "db:generate:remote": "drizzle-kit generate --config=drizzle.config.remote.ts",
+ "db:migrate:local": "wrangler d1 migrations apply vibesdk-db --local",
+ "db:migrate:remote": "CI=true wrangler d1 migrations apply vibesdk-db --remote",
+ "db:studio": "drizzle-kit studio --config=drizzle.config.local.ts",
+ "db:studio:remote": "drizzle-kit studio --config=drizzle.config.remote.ts",
+ "db:setup": "bun run scripts/setup-database.ts",
+ "db:drop": "drizzle-kit drop --config=drizzle.config.local.ts",
+ "db:drop:remote": "drizzle-kit drop --config=drizzle.config.remote.ts",
+ "db:introspect": "drizzle-kit introspect --config=drizzle.config.local.ts",
+ "db:check": "drizzle-kit check --config=drizzle.config.local.ts",
+ "db:up": "drizzle-kit up --config=drizzle.config.local.ts",
+ "knip": "knip",
+ "knip:fix": "knip --fix",
+ "knip:production": "knip --production",
+ "knip:dependencies": "knip --dependencies",
+ "knip:exports": "knip --exports"
+ },
+ "dependencies": {
+ "@cloudflare/containers": "^0.0.25",
+ "@cloudflare/sandbox": "0.1.3",
+ "@noble/ciphers": "^1.3.0",
+ "@octokit/rest": "^22.0.0",
+ "@radix-ui/react-accordion": "^1.2.12",
+ "@radix-ui/react-alert-dialog": "^1.1.15",
+ "@radix-ui/react-aspect-ratio": "^1.1.7",
+ "@radix-ui/react-avatar": "^1.1.10",
+ "@radix-ui/react-checkbox": "^1.3.3",
+ "@radix-ui/react-collapsible": "^1.1.12",
+ "@radix-ui/react-context-menu": "^2.2.16",
+ "@radix-ui/react-dialog": "^1.1.15",
+ "@radix-ui/react-dropdown-menu": "^2.1.16",
+ "@radix-ui/react-hover-card": "^1.1.15",
+ "@radix-ui/react-label": "^2.1.7",
+ "@radix-ui/react-menubar": "^1.1.16",
+ "@radix-ui/react-navigation-menu": "^1.2.14",
+ "@radix-ui/react-popover": "^1.1.15",
+ "@radix-ui/react-progress": "^1.1.7",
+ "@radix-ui/react-radio-group": "^1.3.8",
+ "@radix-ui/react-scroll-area": "^1.2.10",
+ "@radix-ui/react-select": "^2.2.6",
+ "@radix-ui/react-separator": "^1.1.7",
+ "@radix-ui/react-slider": "^1.3.6",
+ "@radix-ui/react-slot": "^1.2.3",
+ "@radix-ui/react-switch": "^1.2.6",
+ "@radix-ui/react-tabs": "^1.1.13",
+ "@radix-ui/react-toggle": "^1.1.10",
+ "@radix-ui/react-toggle-group": "^1.1.11",
+ "@radix-ui/react-tooltip": "^1.2.8",
+ "@rolldown/binding-linux-x64-gnu": "^1.0.0-beta.9-commit.d91dfb5",
+ "@sentry/cloudflare": "^10.15.0",
+ "@sentry/react": "^10.15.0",
+ "@sentry/vite-plugin": "^4.3.0",
+ "@typescript-eslint/eslint-plugin": "^8.44.1",
+ "@typescript-eslint/parser": "^8.44.1",
+ "@typescript-eslint/typescript-estree": "^8.44.1",
+ "@vitejs/plugin-react": "^5.0.4",
+ "@vitejs/plugin-react-oxc": "^0.4.2",
+ "agents": "^0.1.6",
+ "chalk": "^5.6.2",
+ "class-variance-authority": "^0.7.1",
+ "cloudflare": "^4.5.0",
+ "clsx": "^2.1.1",
+ "cmdk": "^1.1.1",
+ "date-fns": "^4.1.0",
+ "diff": "^8.0.2",
+ "dotenv": "^17.2.2",
+ "drizzle-orm": "^0.44.5",
+ "embla-carousel-react": "^8.6.0",
+ "esbuild": "^0.25.10",
+ "eslint-import-resolver-typescript": "^4.4.4",
+ "eslint-plugin-import": "^2.32.0",
+ "framer-motion": "^12.23.22",
+ "hono": "^4.9.9",
+ "html2canvas-pro": "^1.5.11",
+ "input-otp": "^1.4.2",
+ "jose": "^5.10.0",
+ "jsonc-parser": "^3.3.1",
+ "lucide-react": "^0.541.0",
+ "monaco-editor": "^0.52.2",
+ "next-themes": "^0.4.6",
+ "node-fetch": "^3.3.2",
+ "openai": "^5.23.1",
+ "partysocket": "^1.1.5",
+ "perfect-arrows": "^0.3.7",
+ "react": "^19.1.1",
+ "react-day-picker": "^9.11.0",
+ "react-dom": "^19.1.1",
+ "react-feather": "^2.0.10",
+ "react-hook-form": "^7.63.0",
+ "react-markdown": "^10.1.0",
+ "react-resizable-panels": "^3.0.6",
+ "react-router": "^7.9.3",
+ "recharts": "^3.2.1",
+ "rehype-external-links": "^3.0.0",
+ "remark-gfm": "^4.0.1",
+ "sonner": "^2.0.7",
+ "suffix-array": "^0.1.4",
+ "tailwind-merge": "^3.3.1",
+ "vaul": "^1.1.2",
+ "vite-plugin-svgr": "^4.5.0",
+ "zod": "^3.25.76"
+ },
+ "devDependencies": {
+ "@cloudflare/vite-plugin": "^1.13.9",
+ "@cloudflare/vitest-pool-workers": "^0.8.71",
+ "@eslint/js": "^9.36.0",
+ "@types/node": "^22.18.6",
+ "@tailwindcss/typography": "^0.5.19",
+ "@tailwindcss/vite": "^4.1.13",
+ "@types/jest": "^29.5.14",
+ "@types/react": "^19.1.14",
+ "@types/react-dom": "^19.1.9",
+ "@vitejs/plugin-react-swc": "^3.11.0",
+ "drizzle-kit": "^0.31.5",
+ "eslint": "^9.36.0",
+ "eslint-plugin-react-hooks": "^5.2.0",
+ "eslint-plugin-react-refresh": "^0.4.22",
+ "glob": "^11.0.3",
+ "globals": "^16.4.0",
+ "jest": "^29.7.0",
+ "knip": "^5.64.1",
+ "prettier": "^3.6.2",
+ "prettier-plugin-tailwindcss": "^0.6.14",
+ "tailwindcss": "^4.1.13",
+ "ts-jest": "^29.4.4",
+ "tsx": "^4.20.6",
+ "tw-animate-css": "^1.4.0",
+ "typescript": "^5.9.2",
+ "typescript-eslint": "^8.44.1",
+ "vite": "npm:rolldown-vite@^7.1.13",
+ "vite-plugin-monaco-editor": "^1.1.0",
+ "vite-plugin-node-polyfills": "^0.24.0",
+ "vitest": "^3.2.4",
+ "wrangler": "4.41.0"
+ },
+ "prettier": {
+ "singleQuote": true,
+ "useTabs": true
+ },
+ "overrides": {
+ "vite": "npm:rolldown-vite@latest",
+ "@cloudflare/containers": "^0.0.28"
+ }
+}
diff --git a/external/drpower-vibe-production/public/favicon.ico b/external/drpower-vibe-production/public/favicon.ico
new file mode 100644
index 00000000..6d647f9d
Binary files /dev/null and b/external/drpower-vibe-production/public/favicon.ico differ
diff --git a/external/drpower-vibe-production/public/vite.svg b/external/drpower-vibe-production/public/vite.svg
new file mode 100644
index 00000000..e7b8dfb1
--- /dev/null
+++ b/external/drpower-vibe-production/public/vite.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/external/drpower-vibe-production/samplePrompts.md b/external/drpower-vibe-production/samplePrompts.md
new file mode 100644
index 00000000..8fdf08af
--- /dev/null
+++ b/external/drpower-vibe-production/samplePrompts.md
@@ -0,0 +1,24 @@
+Please build a chatgpt clone for me with a beautiful UI, sidebar, ability to save and retrieve previous conversations, choose between various models etc. It should be able to use tools. Use Cloudflare agents to build it
+
+Build a game where I see a map with circles on cities. Each city is a cloudflare datacenter. The map shows with data centres highlighted for 20 seconds, and after that, we show grayed out circles in multiple regions. The user's goal is to remember and click as many regions as they can in 20 seconds and get points based on that
+
+Build a drag and drop flow based editor for configuring dynamic routing to ai models. i should be able to add model nodes, conditional nodes, rate limit nodes to build my routing graph
+
+Create a file manager dashboard with a sidebar for navigation, a main area for file display,
+a toolbar for actions. The sidebar should allow users to navigate between folders in a natural way.
+The main area should show files and folders in a grid or list view.
+The toolbar should have buttons for creating, deleting, and renaming files and folders.
+I should also be able to open, close, edit the files as well as upload and download files.
+The design should be clean and modern, with a focus on usability.
+
+Please build a tool/dashboard for me for evaluating various LLM models and comparing their performances side by side - in terms of latency and token generation speed (assume 1 token ~ 4 chars or 0.75 words). I should be able to add custom model names, set custom baseUrl and apiKey per model etc from UI itself.
+I should also be able to evaluate all these models together in parallel on various tasks such as code generation or text generation (I should give a single prompt and have all models work on it in parallel with streaming output, and see how fast they work). The generated code should not be attempted to be executed. Whatever the output for all the models, I should also be able to ask a 'judge' model to evaluate and give rating to each model's output w.r.t each other.
+Thus this should be a complete LLM testing/sandboxing environment
+
+Make a full banking app
+
+create web-based 3D model viewers for rendering and manipulating 3D assets in real time
+
+Build a full github clone. I should be able to view a repo page, see issues/PRs, navigate to files, also explore new repos, see my open issues/ other use profiles. There should also be a profile page with the timeline for a users changes, and a github like grid with their contributions. Add any other features to make it a complete github clone.
+
+Build a full fledged github clone with all it's functionalities. I should be able to create/edit/commit files, make repos, have organizations, pull requests, issues, proper markdown rendering, code rendering, proper code editor, kanban board, github actions, likes, stars, forks, users, etc.
\ No newline at end of file
diff --git a/external/drpower-vibe-production/scripts/deploy.ts b/external/drpower-vibe-production/scripts/deploy.ts
new file mode 100644
index 00000000..e833ba70
--- /dev/null
+++ b/external/drpower-vibe-production/scripts/deploy.ts
@@ -0,0 +1,2091 @@
+#!/usr/bin/env node
+
+/**
+ * Cloudflare Orange Build - Automated Deployment Script
+ *
+ * This script handles the complete setup and deployment process for the
+ * Cloudflare Orange Build platform, including:
+ * - Workers for Platforms dispatch namespace creation
+ * - Templates repository deployment to R2
+ * - Container configuration updates
+ * - Environment validation
+ *
+ * Used by the "Deploy to Cloudflare" button for one-click deployment.
+ */
+
+import { execSync } from 'child_process';
+import { existsSync, readFileSync, writeFileSync, mkdirSync } from 'fs';
+import { join, dirname } from 'path';
+import { fileURLToPath } from 'url';
+import { parse, modify, applyEdits } from 'jsonc-parser';
+import Cloudflare from 'cloudflare';
+
+// Get current directory for ES modules
+const __filename = fileURLToPath(import.meta.url);
+const __dirname = dirname(__filename);
+const PROJECT_ROOT = join(__dirname, '..');
+
+// Types for configuration
+interface WranglerConfig {
+ name: string;
+ dispatch_namespaces?: Array<{
+ binding: string;
+ namespace: string;
+ experimental_remote?: boolean;
+ }>;
+ r2_buckets?: Array<{
+ binding: string;
+ bucket_name: string;
+ experimental_remote?: boolean;
+ }>;
+ containers?: Array<{
+ class_name: string;
+ image: string;
+ max_instances: number;
+ instance_type?: {
+ vcpu: number;
+ memory_mib: number;
+ disk_mb?: number;
+ } | string;
+ rollout_step_percentage?: number;
+ }>;
+ d1_databases?: Array<{
+ binding: string;
+ database_name: string;
+ database_id: string;
+ migrations_dir?: string;
+ experimental_remote?: boolean;
+ }>;
+ routes?: Array<{
+ pattern: string;
+ custom_domain: boolean;
+ zone_id?: string;
+ }>;
+ vars?: {
+ TEMPLATES_REPOSITORY?: string;
+ CLOUDFLARE_AI_GATEWAY?: string;
+ MAX_SANDBOX_INSTANCES?: string;
+ CUSTOM_DOMAIN?: string;
+ CUSTOM_PREVIEW_DOMAIN?: string;
+ SANDBOX_INSTANCE_TYPE?: string;
+ DISPATCH_NAMESPACE?: string;
+ [key: string]: string | undefined;
+ };
+}
+
+interface EnvironmentConfig {
+ CLOUDFLARE_API_TOKEN: string;
+ CLOUDFLARE_ACCOUNT_ID: string;
+ TEMPLATES_REPOSITORY: string;
+ CLOUDFLARE_AI_GATEWAY?: string;
+ CLOUDFLARE_AI_GATEWAY_TOKEN?: string;
+}
+
+class DeploymentError extends Error {
+ constructor(
+ message: string,
+ public cause?: Error,
+ ) {
+ super(message);
+ this.name = 'DeploymentError';
+ }
+}
+
+class CloudflareDeploymentManager {
+ private config: WranglerConfig;
+ private env: EnvironmentConfig;
+ private cloudflare: Cloudflare;
+ private aiGatewayCloudflare?: Cloudflare; // Separate SDK instance for AI Gateway operations
+ private conflictingVarsForCleanup: Record | null = null; // For signal cleanup
+
+ constructor() {
+ this.validateEnvironment();
+ this.config = this.parseWranglerConfig();
+ this.extractConfigurationValues();
+ this.env = this.getEnvironmentVariables();
+ this.cloudflare = new Cloudflare({
+ apiToken: this.env.CLOUDFLARE_API_TOKEN,
+ });
+
+ // Set up signal handling for graceful cleanup
+ this.setupSignalHandlers();
+ }
+
+ /**
+ * Sets up signal handlers for graceful cleanup on Ctrl+C or termination
+ * Reuses existing restoreOriginalVars method following DRY principles
+ */
+ private setupSignalHandlers(): void {
+ const gracefulExit = async (signal: string) => {
+ console.log(`\n๐ Received ${signal}, performing cleanup...`);
+
+ try {
+ // Restore conflicting vars using existing restoration method
+ if (this.conflictingVarsForCleanup) {
+ console.log('๐ Restoring original wrangler.jsonc configuration...');
+ await this.restoreOriginalVars(this.conflictingVarsForCleanup);
+ } else {
+ console.log('โน๏ธ No configuration changes to restore');
+ }
+ } catch (error) {
+ console.error(`โ Error during cleanup: ${error instanceof Error ? error.message : String(error)}`);
+ }
+
+ console.log('๐ Cleanup completed. Exiting...');
+ process.exit(1);
+ };
+
+ // Handle Ctrl+C (SIGINT)
+ process.on('SIGINT', () => gracefulExit('SIGINT'));
+
+ // Handle termination (SIGTERM)
+ process.on('SIGTERM', () => gracefulExit('SIGTERM'));
+
+ console.log('โ
Signal handlers registered for graceful cleanup');
+ }
+
+ /**
+ * Validates that all required build variables are present
+ */
+ private validateEnvironment(): void {
+ const requiredBuildVars = ['CLOUDFLARE_API_TOKEN'];
+
+ const missingVars = requiredBuildVars.filter(
+ (varName) => !process.env[varName],
+ );
+
+ if (missingVars.length > 0) {
+ throw new DeploymentError(
+ `Missing required build variables: ${missingVars.join(', ')}\n` +
+ `Please ensure all required build variables are configured in your deployment.`,
+ );
+ }
+ console.log('โ
Build variables validation passed');
+ }
+
+
+ /**
+ * Extracts and validates key configuration values from wrangler.jsonc
+ */
+ private extractConfigurationValues(): void {
+ console.log(
+ '๐ Extracting configuration values from wrangler.jsonc...',
+ );
+
+ // Log key extracted values
+ const databaseName = this.config.d1_databases?.[0]?.database_name;
+ const customDomain = this.config.vars?.CUSTOM_DOMAIN;
+ const customPreviewDomain = this.config.vars?.CUSTOM_PREVIEW_DOMAIN;
+ const maxInstances = this.config.vars?.MAX_SANDBOX_INSTANCES;
+ const templatesRepo = this.config.vars?.TEMPLATES_REPOSITORY;
+ const aiGateway = this.config.vars?.CLOUDFLARE_AI_GATEWAY;
+ const dispatchNamespace = this.config.vars?.DISPATCH_NAMESPACE;
+
+ console.log('๐ Configuration Summary:');
+ console.log(` Database Name: ${databaseName || 'Not configured'}`);
+ console.log(` Custom Domain: ${customDomain || 'Not configured'}`);
+ console.log(` Custom Preview Domain: ${customPreviewDomain || 'Not configured'}`);
+ console.log(
+ ` Max Sandbox Instances: ${maxInstances || 'Not configured'}`,
+ );
+ console.log(
+ ` Templates Repository: ${templatesRepo || 'Not configured'}`,
+ );
+ console.log(` AI Gateway: ${aiGateway || 'Not configured'}`);
+ console.log(` Dispatch Namespace: ${dispatchNamespace || 'Not configured'}`);
+
+ // Validate critical configuration
+ if (!databaseName) {
+ console.warn(
+ 'โ ๏ธ No D1 database configured - database operations may fail',
+ );
+ }
+
+ if (!customDomain) {
+ console.warn(
+ 'โ ๏ธ No custom domain configured - using default routes',
+ );
+ }
+
+ console.log('โ
Configuration extraction completed');
+ }
+
+ /**
+ * Safely parses wrangler.jsonc file, handling comments and JSON-like syntax
+ */
+ private parseWranglerConfig(): WranglerConfig {
+ const wranglerPath = this.getWranglerPath();
+
+ if (!existsSync(wranglerPath)) {
+ throw new DeploymentError(
+ 'wrangler.jsonc not found',
+ new Error('Please ensure wrangler.jsonc exists in the project root'),
+ );
+ }
+
+ try {
+ const { config } = this.readWranglerConfig();
+ this.logSuccess(`Parsed wrangler.jsonc - Project: ${config.name}`);
+ return config;
+ } catch (error) {
+ throw new DeploymentError(
+ 'Failed to parse wrangler.jsonc',
+ error instanceof Error ? error : new Error(`Please check your wrangler.jsonc syntax: ${String(error)}`),
+ );
+ }
+ }
+
+ /**
+ * Gets and validates environment variables, with defaults from wrangler.jsonc
+ */
+ private getEnvironmentVariables(): EnvironmentConfig {
+ const apiToken = process.env.CLOUDFLARE_API_TOKEN!;
+ const aiGatewayToken = process.env.CLOUDFLARE_AI_GATEWAY_TOKEN || apiToken;
+
+ return {
+ CLOUDFLARE_API_TOKEN: apiToken,
+ CLOUDFLARE_ACCOUNT_ID:
+ process.env.CLOUDFLARE_ACCOUNT_ID ||
+ this.config.vars?.CLOUDFLARE_ACCOUNT_ID!,
+ TEMPLATES_REPOSITORY:
+ process.env.TEMPLATES_REPOSITORY ||
+ this.config.vars?.TEMPLATES_REPOSITORY!,
+ CLOUDFLARE_AI_GATEWAY:
+ process.env.CLOUDFLARE_AI_GATEWAY ||
+ this.config.vars?.CLOUDFLARE_AI_GATEWAY || "orange-build-gateway",
+ CLOUDFLARE_AI_GATEWAY_TOKEN: aiGatewayToken,
+ };
+ }
+
+ /**
+ * Creates or ensures Workers for Platforms dispatch namespace exists
+ */
+ private async ensureDispatchNamespace(): Promise {
+ const dispatchConfig = this.config.dispatch_namespaces?.[0];
+ if (!dispatchConfig) {
+ console.log('โน๏ธ No dispatch namespace configuration found, skipping setup');
+ return;
+ }
+
+ const namespaceName = dispatchConfig.namespace;
+ console.log(`๐ Checking dispatch namespace: ${namespaceName}`);
+
+ try {
+ // Check if namespace exists using Cloudflare SDK
+ try {
+ await this.cloudflare.workersForPlatforms.dispatch.namespaces.get(
+ namespaceName,
+ { account_id: this.env.CLOUDFLARE_ACCOUNT_ID },
+ );
+ console.log(
+ `โ
Dispatch namespace '${namespaceName}' already exists`,
+ );
+ return;
+ } catch (error: any) {
+ // Check if error indicates dispatch namespaces are not available
+ const errorMessage = error?.message || '';
+ if (errorMessage.includes('You do not have access to dispatch namespaces') ||
+ errorMessage.includes('code: 10121')) {
+ console.log('โ ๏ธ Dispatch namespaces became unavailable during execution');
+ console.log(' Workers for Platforms access may have changed');
+ return;
+ }
+
+ // If error is not 404, re-throw it
+ if (
+ error?.status !== 404 &&
+ error?.message?.indexOf('not found') === -1
+ ) {
+ throw error;
+ }
+ // Namespace doesn't exist, continue to create it
+ }
+
+ console.log(`๐ฆ Creating dispatch namespace: ${namespaceName}`);
+
+ await this.cloudflare.workersForPlatforms.dispatch.namespaces.create(
+ {
+ account_id: this.env.CLOUDFLARE_ACCOUNT_ID,
+ name: namespaceName,
+ },
+ );
+
+ console.log(
+ `โ
Successfully created dispatch namespace: ${namespaceName}`,
+ );
+ } catch (error) {
+ // Check if the error is related to dispatch namespace access
+ const errorMessage = error instanceof Error ? error.message : String(error);
+ if (errorMessage.includes('You do not have access to dispatch namespaces') ||
+ errorMessage.includes('code: 10121')) {
+ console.warn('โ ๏ธ Dispatch namespaces are not available for this account');
+ console.warn(' Skipping dispatch namespace setup and continuing deployment');
+ return;
+ }
+
+ throw new DeploymentError(
+ `Failed to ensure dispatch namespace: ${namespaceName}`,
+ error instanceof Error ? error : new Error(String(error)),
+ );
+ }
+ }
+
+ /**
+ * Creates or ensures AI Gateway exists (non-blocking)
+ */
+ private async ensureAIGateway(): Promise {
+ if (!this.env.CLOUDFLARE_AI_GATEWAY) {
+ console.log(
+ 'โน๏ธ AI Gateway setup skipped (CLOUDFLARE_AI_GATEWAY not provided)',
+ );
+ return;
+ }
+
+ const gatewayName = this.env.CLOUDFLARE_AI_GATEWAY;
+ console.log(`๐ Checking AI Gateway: ${gatewayName}`);
+
+ try {
+ // Step 1: Check main token permissions and create AI Gateway token if needed
+ console.log('๐ Checking API token permissions...');
+ const tokenCheck = await this.checkTokenPermissions();
+ const aiGatewayToken = await this.ensureAIGatewayToken();
+
+ // Step 2: Check if gateway exists first using appropriate SDK
+ const aiGatewaySDK = this.getAIGatewaySDK();
+
+ try {
+ await aiGatewaySDK.aiGateway.get(gatewayName, {
+ account_id: this.env.CLOUDFLARE_ACCOUNT_ID,
+ });
+ console.log(`โ
AI Gateway '${gatewayName}' already exists`);
+ return;
+ } catch (error: any) {
+ // If error is not 404, log but continue
+ if (
+ error?.status !== 404 &&
+ !error?.message?.includes('not found')
+ ) {
+ console.warn(
+ `โ ๏ธ Could not check AI Gateway '${gatewayName}': ${error.message}`,
+ );
+ return;
+ }
+ // Gateway doesn't exist, continue to create it
+ }
+
+ // Validate gateway name length (64 character limit)
+ if (gatewayName.length > 64) {
+ console.warn(
+ `โ ๏ธ AI Gateway name too long (${gatewayName.length} > 64 chars), skipping creation`,
+ );
+ return;
+ }
+
+ // Step 3: Create AI Gateway with authentication based on token availability
+ console.log(`๐ฆ Creating AI Gateway: ${gatewayName}`);
+
+ await aiGatewaySDK.aiGateway.create({
+ account_id: this.env.CLOUDFLARE_ACCOUNT_ID,
+ id: gatewayName,
+ cache_invalidate_on_update: true,
+ cache_ttl: 3600,
+ collect_logs: true,
+ rate_limiting_interval: 0,
+ rate_limiting_limit: 0,
+ rate_limiting_technique: 'sliding',
+ authentication: !!aiGatewayToken, // Enable authentication only if we have a token
+ });
+
+ console.log(
+ `โ
Successfully created AI Gateway: ${gatewayName} (authentication: ${aiGatewayToken ? 'enabled' : 'disabled'})`,
+ );
+ } catch (error) {
+ // Non-blocking: Log warning but continue deployment
+ console.warn(
+ `โ ๏ธ Could not create AI Gateway '${gatewayName}': ${error instanceof Error ? error.message : String(error)}`,
+ );
+ console.warn(
+ ' Continuing deployment without AI Gateway setup...',
+ );
+ }
+ }
+
+ /**
+ * Verifies if the current API token has AI Gateway permissions
+ */
+ private async checkTokenPermissions(): Promise<{
+ hasAIGatewayAccess: boolean;
+ tokenInfo?: any;
+ }> {
+ try {
+ const verifyResponse = await fetch(
+ 'https://api.cloudflare.com/client/v4/user/tokens/verify',
+ {
+ headers: {
+ Authorization: `Bearer ${this.env.CLOUDFLARE_API_TOKEN}`,
+ },
+ },
+ );
+
+ if (!verifyResponse.ok) {
+ console.warn('โ ๏ธ Could not verify API token permissions');
+ return { hasAIGatewayAccess: false };
+ }
+
+ const verifyData = await verifyResponse.json();
+ if (!verifyData.success) {
+ console.warn('โ ๏ธ API token verification failed');
+ return { hasAIGatewayAccess: false };
+ }
+
+ // For now, assume we need to create a separate token for AI Gateway operations
+ // This is a conservative approach since permission checking is complex
+ console.log(
+ 'โน๏ธ Main API token verified, but will create dedicated AI Gateway token',
+ );
+ return { hasAIGatewayAccess: false, tokenInfo: verifyData.result };
+ } catch (error) {
+ console.warn(
+ `โ ๏ธ Token verification failed: ${error instanceof Error ? error.message : String(error)}`,
+ );
+ return { hasAIGatewayAccess: false };
+ }
+ }
+
+ /**
+ * Creates AI Gateway authentication token if needed (non-blocking)
+ * Returns the token if created/available, null otherwise
+ */
+ private async ensureAIGatewayToken(): Promise {
+ const currentToken = this.env.CLOUDFLARE_AI_GATEWAY_TOKEN;
+
+ // Check if token is already set and not the default placeholder
+ if (
+ currentToken &&
+ currentToken !== 'optional-your-cf-ai-gateway-token'
+ ) {
+ console.log('โ
AI Gateway token already configured');
+ // Initialize separate AI Gateway SDK instance
+ this.aiGatewayCloudflare = new Cloudflare({
+ apiToken: currentToken,
+ });
+ return currentToken;
+ }
+
+ try {
+ console.log(`๐ Creating AI Gateway authentication token...`);
+
+ // Create API token with required permissions for AI Gateway including RUN
+ const tokenResponse = await fetch(
+ `https://api.cloudflare.com/client/v4/user/tokens`,
+ {
+ method: 'POST',
+ headers: {
+ Authorization: `Bearer ${this.env.CLOUDFLARE_API_TOKEN}`,
+ 'Content-Type': 'application/json',
+ },
+ body: JSON.stringify({
+ name: `AI Gateway Token - ${new Date().toISOString().split('T')[0]}`,
+ policies: [
+ {
+ effect: 'allow',
+ resources: {
+ [`com.cloudflare.api.account.${this.env.CLOUDFLARE_ACCOUNT_ID}`]:
+ '*',
+ },
+ permission_groups: [
+ // Note: Using descriptive names, actual IDs would need to be fetched from the API
+ { name: 'AI Gateway Read' },
+ { name: 'AI Gateway Edit' },
+ { name: 'AI Gateway Run' }, // This is the key permission for authentication
+ { name: 'Workers AI Read' },
+ { name: 'Workers AI Edit' },
+ ],
+ },
+ ],
+ condition: {
+ request_ip: { in: [], not_in: [] },
+ },
+ expires_on: new Date(
+ Date.now() + 365 * 24 * 60 * 60 * 1000,
+ ).toISOString(), // 1 year
+ }),
+ },
+ );
+
+ if (!tokenResponse.ok) {
+ const errorData = await tokenResponse
+ .json()
+ .catch(() => ({ errors: [{ message: 'Unknown error' }] }));
+ throw new Error(
+ `API token creation failed: ${errorData.errors?.[0]?.message || tokenResponse.statusText}`,
+ );
+ }
+
+ const tokenData = await tokenResponse.json();
+
+ if (tokenData.success && tokenData.result?.value) {
+ const newToken = tokenData.result.value;
+ console.log(
+ 'โ
AI Gateway authentication token created successfully',
+ );
+ console.log(` Token ID: ${tokenData.result.id}`);
+ console.warn(
+ 'โ ๏ธ Please save this token and add it to CLOUDFLARE_AI_GATEWAY_TOKEN:',
+ );
+ console.warn(` ${newToken}`);
+
+ // Initialize separate AI Gateway SDK instance
+ this.aiGatewayCloudflare = new Cloudflare({
+ apiToken: newToken,
+ });
+ return newToken;
+ } else {
+ throw new Error(
+ 'Token creation succeeded but no token value returned',
+ );
+ }
+ } catch (error) {
+ // Non-blocking: Log warning but continue
+ console.warn(
+ `โ ๏ธ Could not create AI Gateway token: ${error instanceof Error ? error.message : String(error)}`,
+ );
+ console.warn(
+ ' AI Gateway will be created without authentication...',
+ );
+ return null;
+ }
+ }
+
+ /**
+ * Gets the appropriate Cloudflare SDK instance for AI Gateway operations
+ */
+ private getAIGatewaySDK(): Cloudflare {
+ return this.aiGatewayCloudflare || this.cloudflare;
+ }
+
+ /**
+ * Clones templates repository and deploys templates to R2
+ */
+ private async deployTemplates(): Promise {
+ const templatesDir = join(PROJECT_ROOT, 'templates');
+ const templatesRepo = this.env.TEMPLATES_REPOSITORY;
+
+ console.log(`๐ฅ Setting up templates from: ${templatesRepo}`);
+
+ try {
+ // Create templates directory if it doesn't exist
+ if (!existsSync(templatesDir)) {
+ mkdirSync(templatesDir, { recursive: true });
+ }
+
+ // Clone repository if not already present
+ if (!existsSync(join(templatesDir, '.git'))) {
+ console.log(`๐ Cloning templates repository...`);
+ execSync(`git clone "${templatesRepo}" "${templatesDir}"`, {
+ stdio: 'pipe',
+ cwd: PROJECT_ROOT,
+ });
+ console.log('โ
Templates repository cloned successfully');
+ } else {
+ console.log(
+ '๐ Templates repository already exists, pulling latest changes...',
+ );
+ try {
+ execSync('git pull origin main || git pull origin master', {
+ stdio: 'pipe',
+ cwd: templatesDir,
+ });
+ console.log('โ
Templates repository updated');
+ } catch (pullError) {
+ console.warn(
+ 'โ ๏ธ Could not pull latest changes, continuing with existing templates',
+ );
+ }
+ }
+
+ // Find R2 bucket name from config
+ const templatesBucket = this.config.r2_buckets?.find(
+ (bucket) => bucket.binding === 'TEMPLATES_BUCKET',
+ );
+
+ if (!templatesBucket) {
+ throw new Error(
+ 'TEMPLATES_BUCKET not found in wrangler.jsonc r2_buckets configuration',
+ );
+ }
+
+ // Check if deploy script exists
+ const deployScript = join(templatesDir, 'deploy_templates.sh');
+ if (!existsSync(deployScript)) {
+ console.warn(
+ 'โ ๏ธ deploy_templates.sh not found in templates repository, skipping template deployment',
+ );
+ return;
+ }
+
+ // Make script executable
+ execSync(`chmod +x "${deployScript}"`, { cwd: templatesDir });
+
+ // Run deployment script with environment variables
+ console.log(
+ `๐ Deploying templates to R2 bucket: ${templatesBucket.bucket_name}`,
+ );
+
+ const deployEnv = {
+ ...process.env,
+ CLOUDFLARE_API_TOKEN: this.env.CLOUDFLARE_API_TOKEN,
+ CLOUDFLARE_ACCOUNT_ID: this.env.CLOUDFLARE_ACCOUNT_ID,
+ BUCKET_NAME: templatesBucket.bucket_name,
+ R2_BUCKET_NAME: templatesBucket.bucket_name,
+ };
+
+ execSync('./deploy_templates.sh', {
+ stdio: 'inherit',
+ cwd: templatesDir,
+ env: deployEnv,
+ });
+
+ console.log('โ
Templates deployed successfully to R2');
+ } catch (error) {
+ // Don't fail the entire deployment if templates fail
+ console.warn(
+ 'โ ๏ธ Templates deployment failed, but continuing with main deployment:',
+ );
+ console.warn(
+ ` ${error instanceof Error ? error.message : String(error)}`,
+ );
+ }
+ }
+
+ /**
+ * Cleans ARM64 platform flags from SandboxDockerfile for production deployment
+ * Returns the original content if ARM64 flags were removed (for restoration)
+ */
+ private cleanDockerfileForDeployment(): string | null {
+ const dockerfilePath = join(PROJECT_ROOT, 'SandboxDockerfile');
+
+ if (!existsSync(dockerfilePath)) {
+ console.log(' โน๏ธ SandboxDockerfile not found - skipping ARM64 cleanup');
+ return null;
+ }
+
+ try {
+ const originalContent = readFileSync(dockerfilePath, 'utf-8');
+ let modified = false;
+
+ // Split content into lines for processing
+ const lines = originalContent.split('\n');
+ const cleanedLines = lines.map(line => {
+ // Look for FROM statements with --platform=linux/arm64 and remove the flag
+ const fromMatch = line.match(/^(\s*FROM\s+)--platform=linux\/arm64\s+(.*)/);
+ if (fromMatch) {
+ modified = true;
+ const [, prefix, image] = fromMatch;
+ return `${prefix}${image}`;
+ }
+ return line;
+ });
+
+ if (modified) {
+ writeFileSync(dockerfilePath, cleanedLines.join('\n'), 'utf-8');
+ console.log(' โ
Removed ARM64 platform flags from SandboxDockerfile');
+ return originalContent; // Return original for restoration
+ } else {
+ console.log(' โ
No ARM64 platform flags found in SandboxDockerfile');
+ return null; // Nothing to restore
+ }
+
+ } catch (error) {
+ console.warn(
+ ` โ ๏ธ Could not clean SandboxDockerfile: ${error instanceof Error ? error.message : String(error)}`,
+ );
+ console.warn(' โ Continuing deployment, but may encounter platform compatibility issues');
+ return null;
+ }
+ }
+
+ /**
+ * Restores ARM64 platform flags to SandboxDockerfile if they were previously removed
+ */
+ private restoreDockerfileARM64Flags(originalContent: string): void {
+ const dockerfilePath = join(PROJECT_ROOT, 'SandboxDockerfile');
+
+ try {
+ writeFileSync(dockerfilePath, originalContent, 'utf-8');
+ console.log('๐ Restored ARM64 platform flags to SandboxDockerfile for local development');
+ } catch (error) {
+ console.warn(
+ `โ ๏ธ Could not restore ARM64 flags to SandboxDockerfile: ${error instanceof Error ? error.message : String(error)}`,
+ );
+ console.warn(' You may need to manually re-run the setup script to restore ARM64 flags');
+ }
+ }
+
+ /**
+ * Updates package.json database commands with the actual database name from wrangler.jsonc
+ */
+ private updatePackageJsonDatabaseCommands(): void {
+ const databaseName = this.config.d1_databases?.[0]?.database_name;
+
+ if (!databaseName) {
+ console.log(
+ 'โน๏ธ No D1 database found in wrangler.jsonc, skipping package.json database command update',
+ );
+ return;
+ }
+
+ console.log(
+ `๐ง Updating package.json database commands with database: ${databaseName}`,
+ );
+
+ try {
+ const packageJsonPath = join(PROJECT_ROOT, 'package.json');
+ const content = readFileSync(packageJsonPath, 'utf-8');
+
+ // Parse the package.json file
+ const packageJson = JSON.parse(content);
+
+ if (!packageJson.scripts) {
+ console.warn('โ ๏ธ No scripts section found in package.json');
+ return;
+ }
+
+ // Update database migration commands
+ const commandsToUpdate = ['db:migrate:local', 'db:migrate:remote'];
+
+ let updated = false;
+ commandsToUpdate.forEach((command) => {
+ if (packageJson.scripts[command]) {
+ const oldCommand = packageJson.scripts[command];
+
+ // Replace any existing database name in the wrangler d1 migrations apply command
+ const newCommand = oldCommand.replace(
+ /wrangler d1 migrations apply [^\s]+ /,
+ `wrangler d1 migrations apply ${databaseName} `,
+ );
+
+ if (newCommand !== oldCommand) {
+ packageJson.scripts[command] = newCommand;
+ console.log(
+ ` โ
Updated ${command}: ${oldCommand} โ ${newCommand}`,
+ );
+ updated = true;
+ }
+ }
+ });
+
+ if (updated) {
+ // Write back the updated package.json with proper formatting
+ writeFileSync(
+ packageJsonPath,
+ JSON.stringify(packageJson, null, '\t'),
+ 'utf-8',
+ );
+ console.log(
+ 'โ
Updated package.json database commands successfully',
+ );
+ } else {
+ console.log(
+ 'โน๏ธ No database commands needed updating in package.json',
+ );
+ }
+ } catch (error) {
+ console.warn(
+ `โ ๏ธ Could not update package.json database commands: ${error instanceof Error ? error.message : String(error)}`,
+ );
+ // Non-blocking - continue deployment
+ }
+ }
+
+ /**
+ * Gets the zone name and ID for a given domain by testing subdomains
+ */
+ private async detectZoneForDomain(customDomain: string, originalDomain: string): Promise<{
+ zoneName: string | null;
+ zoneId: string | null;
+ }> {
+ console.log(`๐ Detecting zone for custom domain: ${customDomain}, Original domain was: ${originalDomain}`);
+
+ // Extract possible zone names by progressively removing subdomains
+ const domainParts = customDomain.split('.');
+ const possibleZones: string[] = [];
+
+ // Generate all possible zone names from longest to shortest
+ // e.g., for 'abc.test.xyz.build.cloudflare.dev' generates:
+ // ['abc.test.xyz.build.cloudflare.dev', 'test.xyz.build.cloudflare.dev', 'xyz.build.cloudflare.dev', 'build.cloudflare.dev', 'cloudflare.dev']
+ for (let i = 0; i < domainParts.length - 1; i++) {
+ const zoneName = domainParts.slice(i).join('.');
+ possibleZones.push(zoneName);
+ }
+
+ console.log(`๐ Testing possible zones: ${possibleZones.join(', ')}`);
+
+ // Test each possible zone name
+ for (const zoneName of possibleZones) {
+ try {
+ console.log(` Testing zone: ${zoneName}`);
+ const response = await fetch(
+ `https://api.cloudflare.com/client/v4/zones?name=${encodeURIComponent(zoneName)}`,
+ {
+ headers: {
+ 'Content-Type': 'application/json',
+ 'Authorization': `Bearer ${this.env.CLOUDFLARE_API_TOKEN}`,
+ },
+ }
+ );
+
+ if (!response.ok) {
+ console.log(` โ API error for zone ${zoneName}: ${response.status} ${response.statusText}`);
+ continue;
+ }
+
+ const data = await response.json();
+
+ if (data.success && data.result && data.result.length > 0) {
+ const zone = data.result[0];
+ console.log(` โ
Found zone: ${zoneName} (ID: ${zone.id})`);
+ console.log(` Zone status: ${zone.status}`);
+ console.log(` Account: ${zone.account.name}`);
+ return {
+ zoneName: zoneName,
+ zoneId: zone.id,
+ };
+ } else {
+ console.log(` โ No zone found for: ${zoneName}`);
+ }
+ } catch (error) {
+ console.log(` โ Error checking zone ${zoneName}: ${error instanceof Error ? error.message : String(error)}`);
+ }
+ }
+
+ console.error(`โ No valid zone found for custom domain: ${customDomain}`);
+ console.error(` Tested zones: ${possibleZones.join(', ')}`);
+ console.error(` Please ensure:`);
+ console.error(` 1. The domain is managed by Cloudflare`);
+ console.error(` 2. Your API token has zone read permissions`);
+ console.error(` 3. The domain is active and properly configured`);
+
+ return { zoneName: null, zoneId: null };
+ }
+
+
+ /**
+ * Updates wrangler.jsonc routes and deployment settings based on CUSTOM_DOMAIN
+ */
+ /**
+ * Standard formatting options for JSONC modifications
+ */
+ private static readonly JSONC_FORMAT_OPTIONS = {
+ formattingOptions: {
+ insertSpaces: true,
+ keepLines: true,
+ tabSize: 4
+ }
+ };
+
+ /**
+ * Gets the path to wrangler.jsonc
+ */
+ private getWranglerPath(): string {
+ return join(PROJECT_ROOT, 'wrangler.jsonc');
+ }
+
+ /**
+ * Reads and parses wrangler.jsonc file
+ */
+ private readWranglerConfig(): { content: string; config: WranglerConfig } {
+ const wranglerPath = this.getWranglerPath();
+ const content = readFileSync(wranglerPath, 'utf-8');
+ const config = parse(content) as WranglerConfig;
+ return { content, config };
+ }
+
+ /**
+ * Writes content to wrangler.jsonc file
+ */
+ private writeWranglerConfig(content: string): void {
+ const wranglerPath = this.getWranglerPath();
+ writeFileSync(wranglerPath, content, 'utf-8');
+ }
+
+ /**
+ * Standardized success logging
+ */
+ private logSuccess(message: string, details?: string[]): void {
+ console.log(`โ
${message}`);
+ if (details) {
+ details.forEach(detail => console.log(` ${detail}`));
+ }
+ }
+
+ /**
+ * Standardized warning logging
+ */
+ private logWarning(message: string, details?: string[]): void {
+ console.warn(`โ ๏ธ ${message}`);
+ if (details) {
+ details.forEach(detail => console.warn(` ${detail}`));
+ }
+ }
+
+ /**
+ * Updates a specific field in wrangler.jsonc configuration
+ */
+ private updateWranglerField(content: string, field: string, value: T): string {
+ const edits = modify(content, [field], value, CloudflareDeploymentManager.JSONC_FORMAT_OPTIONS);
+ return applyEdits(content, edits);
+ }
+
+ /**
+ * Updates wrangler.jsonc for workers.dev deployment (no custom domain)
+ */
+ private updateWranglerForWorkersDev(content: string): string {
+ let updatedContent = content;
+
+ // Remove routes property if it exists
+ const removeRoutesEdits = modify(content, ['routes'], undefined, CloudflareDeploymentManager.JSONC_FORMAT_OPTIONS);
+ updatedContent = applyEdits(updatedContent, removeRoutesEdits);
+
+ // Set workers_dev = true and preview_urls = true
+ updatedContent = this.updateWranglerField(updatedContent, 'workers_dev', true);
+ updatedContent = this.updateWranglerField(updatedContent, 'preview_urls', true);
+
+ return updatedContent;
+ }
+
+ /**
+ * Updates wrangler.jsonc for custom domain deployment
+ */
+ private updateWranglerForCustomDomain(
+ content: string,
+ routes: Array<{ pattern: string; custom_domain: boolean; zone_id?: string; zone_name?: string }>,
+ preserveExistingFlags: boolean = false
+ ): string {
+ let updatedContent = content;
+
+ // Update routes
+ updatedContent = this.updateWranglerField(updatedContent, 'routes', routes);
+
+ // Only update workers_dev and preview_urls if not preserving existing flags
+ if (!preserveExistingFlags) {
+ updatedContent = this.updateWranglerField(updatedContent, 'workers_dev', false);
+ updatedContent = this.updateWranglerField(updatedContent, 'preview_urls', false);
+ }
+
+ return updatedContent;
+ }
+
+ /**
+ * Safely detects zone information for a domain, handling failures gracefully
+ */
+ private async safeDetectZoneForDomain(
+ customDomain: string,
+ originalCustomDomain: string | null
+ ): Promise<{ zoneName: string | null; zoneId: string | null; success: boolean }> {
+ try {
+ if (!originalCustomDomain) {
+ return { zoneName: null, zoneId: null, success: false };
+ }
+
+ const { zoneName, zoneId } = await this.detectZoneForDomain(customDomain, originalCustomDomain);
+ if (!zoneId) {
+ return { zoneName: null, zoneId: null, success: false };
+ }
+ return { zoneName, zoneId, success: true };
+ } catch (error) {
+ console.warn(
+ `โ ๏ธ Zone detection failed for custom domain ${customDomain}: ${error instanceof Error ? error.message : String(error)}`
+ );
+ console.log(' โ Continuing without zone-specific routes');
+ return { zoneName: null, zoneId: null, success: false };
+ }
+ }
+
+ private async updateCustomDomainRoutes(): Promise {
+ const customDomain = this.config.vars?.CUSTOM_DOMAIN || process.env.CUSTOM_DOMAIN;
+ // Check for CUSTOM_PREVIEW_DOMAIN (env var takes priority)
+ const customPreviewDomain = process.env.CUSTOM_PREVIEW_DOMAIN || this.config.vars?.CUSTOM_PREVIEW_DOMAIN;
+
+ try {
+ const { content, config } = this.readWranglerConfig();
+
+ // Get the original custom domain from existing routes (route with custom_domain: true)
+ const originalCustomDomain = config.routes?.find(route => route.custom_domain)?.pattern || null;
+
+ if (!customDomain) {
+ console.log(
+ 'โน๏ธ CUSTOM_DOMAIN not set - removing routes and enabling workers.dev',
+ );
+
+ const updatedContent = this.updateWranglerForWorkersDev(content);
+ this.writeWranglerConfig(updatedContent);
+
+ this.logSuccess('Updated wrangler.jsonc for workers.dev deployment:', [
+ '- Removed routes configuration',
+ '- Set workers_dev: true',
+ '- Set preview_urls: true'
+ ]);
+ return '';
+ }
+
+ console.log(
+ `๐ง Updating wrangler.jsonc routes with custom domain: ${customDomain}`,
+ );
+
+ // Check if we have a custom preview domain for wildcard routes
+ if (customPreviewDomain && customPreviewDomain !== '') {
+ console.log(
+ `๐ง Using CUSTOM_PREVIEW_DOMAIN for wildcard routes: ${customPreviewDomain}`,
+ );
+ }
+
+ // Safely detect zone information for main domain
+ const { zoneId, success: zoneDetectionSuccess } = await this.safeDetectZoneForDomain(customDomain, originalCustomDomain);
+
+ // If we have a custom preview domain, detect its zone information for wildcard routes
+ let previewZoneName: string | null = null;
+ let previewZoneId: string | null = null;
+ let previewZoneDetectionSuccess = false;
+
+ if (customPreviewDomain && customPreviewDomain !== '') {
+ const previewZoneInfo = await this.safeDetectZoneForDomain(customPreviewDomain, customPreviewDomain);
+ previewZoneName = previewZoneInfo.zoneName;
+ previewZoneId = previewZoneInfo.zoneId;
+ previewZoneDetectionSuccess = previewZoneInfo.success;
+
+ if (previewZoneDetectionSuccess) {
+ console.log(`๐ Preview domain zone detected:`);
+ console.log(` Preview Zone Name: ${previewZoneName}`);
+ console.log(` Preview Zone ID: ${previewZoneId}`);
+ }
+ }
+
+ // Define the expected routes based on zone detection success
+ let expectedRoutes: Array<{
+ pattern: string;
+ custom_domain: boolean;
+ zone_id?: string;
+ }>;
+ const existingWildcardRoute = config.routes?.find(route => !route.custom_domain);
+
+ // Determine which domain and zone to use for wildcard pattern
+ const wildcardDomain = (customPreviewDomain && customPreviewDomain !== '') ? customPreviewDomain : customDomain;
+ const wildcardZoneId = (customPreviewDomain && previewZoneDetectionSuccess && previewZoneId)
+ ? previewZoneId
+ : (zoneDetectionSuccess && zoneId ? zoneId : undefined);
+
+ const wildcardRoute: {
+ pattern: string;
+ custom_domain: boolean;
+ zone_id?: string;
+ } = {
+ pattern: `*${wildcardDomain}/*`,
+ custom_domain: false,
+ };
+
+ if (wildcardZoneId) {
+ // Custom domain with zone information for wildcard pattern
+ console.log(`๐ Creating routes with zone information:`);
+ console.log(` Main Domain: ${customDomain}`);
+ console.log(` Wildcard Domain: ${wildcardDomain}`);
+ console.log(` Wildcard Zone ID: ${wildcardZoneId}`);
+ wildcardRoute.zone_id = wildcardZoneId;
+ } else {
+ const existingZoneId = existingWildcardRoute && existingWildcardRoute.zone_id;
+ if (existingZoneId) {
+ wildcardRoute.zone_id = existingZoneId;
+ console.warn(
+ `๐ Using fallback wildcard route configuration (zone detection ${zoneDetectionSuccess ? 'returned no zone' : 'failed'})`
+ );
+ } else {
+ // Fatal error
+ console.error(`Failed to detect zone for custom domain ${customDomain}. Make sure the domain is properly configured in Cloudflare.`);
+ throw new Error(`Failed to detect zone for custom domain ${customDomain}`);
+ }
+ }
+
+ expectedRoutes = [
+ { pattern: customDomain, custom_domain: true },
+ wildcardRoute,
+ ];
+
+ // Check if routes need updating
+ let needsUpdate = false;
+
+ if (!config.routes || !Array.isArray(config.routes)) {
+ needsUpdate = true;
+ } else if (config.routes.length !== expectedRoutes.length) {
+ needsUpdate = true;
+ } else {
+ for (let i = 0; i < expectedRoutes.length; i++) {
+ const expected = expectedRoutes[i];
+ const actual = config.routes[i] as any;
+
+ if (
+ actual.pattern !== expected.pattern ||
+ actual.custom_domain !== expected.custom_domain
+ ) {
+ needsUpdate = true;
+ break;
+ }
+
+ const actualZoneId = (actual && (actual as any).zone_id) ?? null;
+ const expectedZoneId = expected.zone_id ?? null;
+ if (actualZoneId !== expectedZoneId) {
+ needsUpdate = true;
+ break;
+ }
+ }
+ }
+
+ if (!needsUpdate) {
+ console.log(
+ 'โน๏ธ Routes already match custom domain configuration',
+ );
+ return customDomain;
+ }
+
+ // Update wrangler configuration
+ // If zone detection failed, preserve existing workers_dev and preview_urls values
+ const preserveExistingFlags = !zoneDetectionSuccess;
+ const updatedContent = this.updateWranglerForCustomDomain(content, expectedRoutes, preserveExistingFlags);
+ this.writeWranglerConfig(updatedContent);
+
+ // Log the changes
+ const routeDetails = expectedRoutes.map((route, index) => {
+ const infoParts = [`custom_domain: ${route.custom_domain}`];
+ if (route.zone_id) {
+ infoParts.push(`zone_id: ${route.zone_id}`);
+ }
+ return `Route ${index + 1}: ${route.pattern} (${infoParts.join(', ')})`;
+ });
+
+ if (!preserveExistingFlags) {
+ routeDetails.push('Set workers_dev: false', 'Set preview_urls: false');
+ } else {
+ routeDetails.push('Preserved existing workers_dev and preview_urls settings');
+ }
+
+ this.logSuccess('Updated wrangler.jsonc routes:', routeDetails);
+ return customDomain;
+ } catch (error) {
+ console.error(
+ `โ ๏ธ Could not update custom domain routes: ${error instanceof Error ? error.message : String(error)}`,
+ );
+ throw error;
+ }
+ }
+
+ /**
+ * Updates container configuration based on MAX_SANDBOX_INSTANCES (env var overrides wrangler.jsonc)
+ */
+ private updateContainerConfiguration(): void {
+ // Environment variable takes priority over wrangler.jsonc vars
+ const maxInstances =
+ process.env.MAX_SANDBOX_INSTANCES ||
+ this.config.vars?.MAX_SANDBOX_INSTANCES || "10";
+
+ if (!maxInstances) {
+ console.log(
+ 'โน๏ธ MAX_SANDBOX_INSTANCES not set in environment variables or wrangler.jsonc vars, skipping container configuration update',
+ );
+ return;
+ }
+
+ const source = process.env.MAX_SANDBOX_INSTANCES
+ ? 'environment variable'
+ : 'wrangler.jsonc vars';
+ console.log(
+ `๐ง Using MAX_SANDBOX_INSTANCES from ${source}: ${maxInstances}`,
+ );
+
+ const maxInstancesNum = parseInt(maxInstances, 10);
+ if (isNaN(maxInstancesNum) || maxInstancesNum <= 0) {
+ console.warn(
+ `โ ๏ธ Invalid MAX_SANDBOX_INSTANCES value: ${maxInstances}, skipping update`,
+ );
+ return;
+ }
+
+ console.log(
+ `๐ง Updating container configuration: MAX_SANDBOX_INSTANCES=${maxInstancesNum}`,
+ );
+
+ try {
+ const { content, config } = this.readWranglerConfig();
+
+ if (!config.containers || !Array.isArray(config.containers)) {
+ console.warn(
+ 'โ ๏ธ No containers configuration found in wrangler.jsonc',
+ );
+ return;
+ }
+
+ // Find the index of UserAppSandboxService container
+ const sandboxContainerIndex = config.containers.findIndex(
+ (container) => container.class_name === 'UserAppSandboxService',
+ );
+
+ if (sandboxContainerIndex === -1) {
+ console.warn(
+ 'โ ๏ธ UserAppSandboxService container not found in wrangler.jsonc',
+ );
+ return;
+ }
+
+ const oldMaxInstances =
+ config.containers[sandboxContainerIndex].max_instances;
+
+ // Use jsonc-parser's modify function to properly edit the file
+ // Path to the max_instances field: ['containers', index, 'max_instances']
+ const edits = modify(
+ content,
+ ['containers', sandboxContainerIndex, 'max_instances'],
+ maxInstancesNum,
+ CloudflareDeploymentManager.JSONC_FORMAT_OPTIONS,
+ );
+
+ // Apply the edits to get the updated content
+ const updatedContent = applyEdits(content, edits);
+
+ // Write back the updated configuration
+ this.writeWranglerConfig(updatedContent);
+
+ this.logSuccess(
+ `Updated UserAppSandboxService max_instances: ${oldMaxInstances} โ ${maxInstancesNum}`
+ );
+ } catch (error) {
+ throw new DeploymentError(
+ 'Failed to update container configuration',
+ error instanceof Error ? error : new Error(String(error)),
+ );
+ }
+ }
+
+ /**
+ * Updates container instance types based on SANDBOX_INSTANCE_TYPE variable
+ */
+ private updateContainerInstanceTypes(): void {
+ // Environment variable takes priority over wrangler.jsonc vars
+ const sandboxInstanceType =
+ process.env.SANDBOX_INSTANCE_TYPE ||
+ this.config.vars?.SANDBOX_INSTANCE_TYPE ||
+ 'standard-3';
+
+ console.log(
+ `๐ง Configuring container instance types: ${sandboxInstanceType}`,
+ );
+
+ try {
+ const { content, config } = this.readWranglerConfig();
+
+ if (!config.containers || !Array.isArray(config.containers)) {
+ console.warn(
+ 'โ ๏ธ No containers configuration found in wrangler.jsonc',
+ );
+ return;
+ }
+
+ // Find the indices of both containers
+ const userAppContainerIndex = config.containers.findIndex(
+ (container) => container.class_name === 'UserAppSandboxService',
+ );
+
+ if (userAppContainerIndex === -1) {
+ console.warn(
+ 'โ ๏ธ UserAppSandboxService container not found in wrangler.jsonc',
+ );
+ return;
+ }
+
+ // Determine the instance type configuration
+ let userAppInstanceType: any;
+
+ if (sandboxInstanceType === 'enhanced') {
+ // Enhanced configuration as specified
+ userAppInstanceType = {
+ vcpu: 4,
+ memory_mib: 4096,
+ disk_mb: 6144
+ };
+ console.log(' Using enhanced instance type configuration');
+ } else {
+ // Use the string value directly
+ userAppInstanceType = sandboxInstanceType;
+ console.log(` Using instance type string: ${sandboxInstanceType}`);
+ }
+
+ // Update UserAppSandboxService instance_type
+ let updatedContent = content;
+ const userAppInstanceTypeEdits = modify(
+ updatedContent,
+ ['containers', userAppContainerIndex, 'instance_type'],
+ userAppInstanceType,
+ CloudflareDeploymentManager.JSONC_FORMAT_OPTIONS
+ );
+ updatedContent = applyEdits(updatedContent, userAppInstanceTypeEdits);
+
+ // Write back the updated configuration
+ this.writeWranglerConfig(updatedContent);
+
+ this.logSuccess(`Updated container instance types for SANDBOX_INSTANCE_TYPE: ${sandboxInstanceType}`, [
+ `UserAppSandboxService: ${JSON.stringify(userAppInstanceType)}`,
+ ]);
+
+ } catch (error) {
+ this.logWarning(
+ `Could not update container instance types: ${error instanceof Error ? error.message : String(error)}`,
+ ['Continuing with current configuration...']
+ );
+ // Non-blocking - continue deployment
+ }
+ }
+
+ /**
+ * Updates dispatch namespace configuration based on DISPATCH_NAMESPACE (env var overrides wrangler.jsonc)
+ * If dispatch namespaces are not available, clears the DISPATCH_NAMESPACE var
+ */
+ private updateDispatchNamespace(dispatchNamespacesAvailable: boolean): void {
+ // If dispatch namespaces are not available, clear the DISPATCH_NAMESPACE var
+ if (!dispatchNamespacesAvailable) {
+ console.log('๐ง Dispatch namespaces not available - clearing DISPATCH_NAMESPACE var');
+ try {
+ const { content } = this.readWranglerConfig();
+
+ // Clear the DISPATCH_NAMESPACE var
+ const varsEdits = modify(
+ content,
+ ['vars', 'DISPATCH_NAMESPACE'],
+ '',
+ CloudflareDeploymentManager.JSONC_FORMAT_OPTIONS,
+ );
+ const updatedContent = applyEdits(content, varsEdits);
+
+ this.writeWranglerConfig(updatedContent);
+ this.logSuccess('Cleared DISPATCH_NAMESPACE var (dispatch namespaces not available)');
+
+ // Update internal config
+ if (this.config.vars) {
+ this.config.vars.DISPATCH_NAMESPACE = '';
+ }
+ } catch (error) {
+ this.logWarning(
+ `Could not clear DISPATCH_NAMESPACE var: ${error instanceof Error ? error.message : String(error)}`,
+ ['Continuing with deployment...']
+ );
+ }
+ return;
+ }
+
+ // Environment variable takes priority over wrangler.jsonc vars
+ const dispatchNamespace =
+ process.env.DISPATCH_NAMESPACE ||
+ this.config.vars?.DISPATCH_NAMESPACE ||
+ "orange-build-default-namespace";
+
+ const source = process.env.DISPATCH_NAMESPACE
+ ? 'environment variable'
+ : this.config.vars?.DISPATCH_NAMESPACE
+ ? 'wrangler.jsonc vars'
+ : 'default value';
+ console.log(
+ `๐ง Using DISPATCH_NAMESPACE from ${source}: ${dispatchNamespace}`,
+ );
+
+ // Validate namespace name
+ if (!dispatchNamespace || dispatchNamespace.trim() === '') {
+ console.warn(
+ 'โ ๏ธ Invalid DISPATCH_NAMESPACE value: empty string, using default',
+ );
+ return;
+ }
+
+ // Basic format validation (alphanumeric, hyphens, underscores)
+ const namespacePattern = /^[a-zA-Z0-9_-]+$/;
+ if (!namespacePattern.test(dispatchNamespace)) {
+ console.warn(
+ `โ ๏ธ Invalid DISPATCH_NAMESPACE format: ${dispatchNamespace}, must contain only letters, numbers, hyphens, and underscores`,
+ );
+ return;
+ }
+
+ console.log(
+ `๐ง Updating dispatch namespace configuration: DISPATCH_NAMESPACE=${dispatchNamespace}`,
+ );
+
+ try {
+ const { content, config } = this.readWranglerConfig();
+
+ if (!config.dispatch_namespaces || !Array.isArray(config.dispatch_namespaces)) {
+ console.warn(
+ 'โ ๏ธ No dispatch_namespaces configuration found in wrangler.jsonc',
+ );
+ return;
+ }
+
+ if (config.dispatch_namespaces.length === 0) {
+ console.warn(
+ 'โ ๏ธ Empty dispatch_namespaces array in wrangler.jsonc',
+ );
+ return;
+ }
+
+ const currentNamespace = config.dispatch_namespaces[0].namespace;
+
+ // Check if update is needed
+ if (currentNamespace === dispatchNamespace) {
+ console.log(
+ `โน๏ธ Dispatch namespace already set to: ${dispatchNamespace}`,
+ );
+ return;
+ }
+
+ let updatedContent = content;
+
+ // Update dispatch_namespaces[0].namespace
+ const namespaceEdits = modify(
+ updatedContent,
+ ['dispatch_namespaces', 0, 'namespace'],
+ dispatchNamespace,
+ CloudflareDeploymentManager.JSONC_FORMAT_OPTIONS,
+ );
+ updatedContent = applyEdits(updatedContent, namespaceEdits);
+
+ // Update vars.DISPATCH_NAMESPACE for consistency
+ const varsEdits = modify(
+ updatedContent,
+ ['vars', 'DISPATCH_NAMESPACE'],
+ dispatchNamespace,
+ CloudflareDeploymentManager.JSONC_FORMAT_OPTIONS,
+ );
+ updatedContent = applyEdits(updatedContent, varsEdits);
+
+ // Write back the updated configuration
+ this.writeWranglerConfig(updatedContent);
+
+ this.logSuccess(
+ `Updated dispatch namespace: ${currentNamespace} โ ${dispatchNamespace}`,
+ [
+ 'Updated dispatch_namespaces[0].namespace',
+ 'Updated vars.DISPATCH_NAMESPACE for consistency'
+ ]
+ );
+
+ // Update internal config to reflect changes
+ if (!this.config.dispatch_namespaces) {
+ this.config.dispatch_namespaces = [];
+ }
+ this.config.dispatch_namespaces[0].namespace = dispatchNamespace;
+ if (!this.config.vars) {
+ this.config.vars = {};
+ }
+ this.config.vars.DISPATCH_NAMESPACE = dispatchNamespace;
+
+ } catch (error) {
+ this.logWarning(
+ `Could not update dispatch namespace configuration: ${error instanceof Error ? error.message : String(error)}`,
+ ['Continuing with current configuration...']
+ );
+ // Non-blocking - continue deployment
+ }
+ }
+
+ /**
+ * Cleans Wrangler cache and build artifacts
+ */
+ private cleanWranglerCache(): void {
+ console.log('๐งน Cleaning Wrangler cache and build artifacts...');
+
+ try {
+ // Remove .wrangler directory (contains wrangler cache and state)
+ execSync('rm -rf .wrangler', {
+ stdio: 'pipe',
+ cwd: PROJECT_ROOT,
+ });
+ console.log(' โ
Removed .wrangler directory');
+
+ // Remove wrangler.json files from dist/* directories
+ // Use find to locate and remove any wrangler.json files in dist subdirectories
+ try {
+ execSync('find dist -name "wrangler.json" -type f -delete 2>/dev/null || true', {
+ stdio: 'pipe',
+ cwd: PROJECT_ROOT,
+ });
+ console.log(' โ
Removed cached wrangler.json files from dist');
+ } catch (findError) {
+ // Non-critical - continue if find fails
+ console.log(' โน๏ธ No cached wrangler.json files found in dist');
+ }
+
+ console.log('โ
Cache cleanup completed');
+ } catch (error) {
+ // Non-blocking - log warning but continue
+ console.warn(
+ `โ ๏ธ Cache cleanup failed: ${error instanceof Error ? error.message : String(error)}`,
+ );
+ console.warn(' Continuing with deployment...');
+ }
+ }
+
+ /**
+ * Builds the project (clean dist and run build)
+ */
+ private async buildProject(): Promise {
+ console.log('๐จ Building project...');
+
+ try {
+ // Run build
+ execSync('bun run build', {
+ stdio: 'inherit',
+ cwd: PROJECT_ROOT,
+ });
+
+ console.log('โ
Project build completed');
+ } catch (error) {
+ throw new DeploymentError(
+ 'Failed to build project',
+ error instanceof Error ? error : new Error(String(error)),
+ );
+ }
+ }
+
+ /**
+ * Deploys the project using Wrangler
+ */
+ private async wranglerDeploy(): Promise {
+ console.log('๐ Deploying to Cloudflare Workers...');
+
+ try {
+ execSync('wrangler deploy', {
+ stdio: 'inherit',
+ cwd: PROJECT_ROOT,
+ });
+
+ console.log('โ
Wrangler deployment completed');
+ } catch (error) {
+ throw new DeploymentError(
+ 'Failed to deploy with Wrangler',
+ error instanceof Error ? error : new Error(String(error)),
+ );
+ }
+ }
+
+ /**
+ * Temporarily removes conflicting vars from wrangler.jsonc before deployment
+ * Returns the original vars for restoration later
+ */
+ private async removeConflictingVars(): Promise | null> {
+ const prodVarsPath = join(PROJECT_ROOT, '.prod.vars');
+
+ if (!existsSync(prodVarsPath)) {
+ console.log('โน๏ธ No .prod.vars file found, skipping conflict resolution');
+ return null;
+ }
+
+ try {
+ console.log('๐ Checking for var/secret conflicts...');
+
+ // Read .prod.vars to see which secrets will be uploaded
+ const prodVarsContent = readFileSync(prodVarsPath, 'utf-8');
+ const secretVarNames = new Set();
+
+ prodVarsContent.split('\n').forEach(line => {
+ line = line.trim();
+ if (line && !line.startsWith('#') && line.includes('=')) {
+ const varName = line.split('=')[0].trim();
+ secretVarNames.add(varName);
+ }
+ });
+
+ // Check which vars in wrangler.jsonc conflict with secrets
+ const conflictingVars: Record = {};
+ const originalVars = { ...(this.config.vars || {}) };
+
+ Object.keys(originalVars).forEach(varName => {
+ if (secretVarNames.has(varName)) {
+ conflictingVars[varName] = originalVars[varName] || '';
+ console.log(`๐ Found conflict: ${varName} (will be moved from var to secret)`);
+ }
+ });
+
+ if (Object.keys(conflictingVars).length === 0) {
+ console.log('โ
No var/secret conflicts found');
+ return null;
+ }
+
+ console.log(`โ ๏ธ Temporarily removing ${Object.keys(conflictingVars).length} conflicting vars from wrangler.jsonc`);
+
+ // Remove conflicting vars from wrangler.jsonc
+ const { content } = this.readWranglerConfig();
+
+ const updatedVars = { ...originalVars };
+ Object.keys(conflictingVars).forEach(varName => {
+ delete updatedVars[varName];
+ });
+
+ // Update wrangler.jsonc with vars removed
+ const edits = modify(
+ content,
+ ['vars'],
+ updatedVars,
+ CloudflareDeploymentManager.JSONC_FORMAT_OPTIONS
+ );
+
+ const updatedContent = applyEdits(content, edits);
+ this.writeWranglerConfig(updatedContent);
+
+ this.logSuccess('Temporarily removed conflicting vars from wrangler.jsonc');
+ return conflictingVars;
+
+ } catch (error) {
+ this.logWarning(`Could not remove conflicting vars: ${error instanceof Error ? error.message : String(error)}`);
+ return null;
+ }
+ }
+
+ /**
+ * Restores the original vars to wrangler.jsonc after deployment
+ */
+ private async restoreOriginalVars(originalConflictingVars: Record | null): Promise {
+ if (!originalConflictingVars || Object.keys(originalConflictingVars).length === 0) {
+ return;
+ }
+
+ try {
+ console.log('๐ Restoring original vars to wrangler.jsonc...');
+
+ const { content, config } = this.readWranglerConfig();
+
+ // Merge back the conflicting vars
+ const restoredVars = {
+ ...(config.vars || {}),
+ ...originalConflictingVars
+ };
+
+ const edits = modify(
+ content,
+ ['vars'],
+ restoredVars,
+ CloudflareDeploymentManager.JSONC_FORMAT_OPTIONS
+ );
+
+ const updatedContent = applyEdits(content, edits);
+ this.writeWranglerConfig(updatedContent);
+
+ this.logSuccess(`Restored ${Object.keys(originalConflictingVars).length} original vars to wrangler.jsonc`);
+
+ } catch (error) {
+ this.logWarning(`Could not restore original vars: ${error instanceof Error ? error.message : String(error)}`, [
+ 'You may need to manually restore wrangler.jsonc vars'
+ ]);
+ }
+ }
+
+ /**
+ * Creates .prod.vars file with current environment variables
+ */
+ private createProdVarsFile(): void {
+ const prodVarsPath = join(PROJECT_ROOT, '.prod.vars');
+
+ console.log(
+ '๐ Creating .prod.vars file from environment variables...',
+ );
+
+ // Map of environment variables to include in production secrets
+ const secretVars = [
+ 'CLOUDFLARE_API_TOKEN',
+ 'CLOUDFLARE_ACCOUNT_ID',
+ 'TEMPLATES_REPOSITORY',
+ 'CLOUDFLARE_AI_GATEWAY',
+ 'CLOUDFLARE_AI_GATEWAY_URL',
+ 'CLOUDFLARE_AI_GATEWAY_TOKEN',
+ 'ANTHROPIC_API_KEY',
+ 'OPENAI_API_KEY',
+ 'GOOGLE_AI_STUDIO_API_KEY',
+ 'OPENROUTER_API_KEY',
+ 'GROQ_API_KEY',
+ 'GOOGLE_CLIENT_SECRET',
+ 'GOOGLE_CLIENT_ID',
+ 'GITHUB_CLIENT_ID',
+ 'GITHUB_CLIENT_SECRET',
+ 'JWT_SECRET',
+ 'WEBHOOK_SECRET',
+ 'MAX_SANDBOX_INSTANCES',
+ ];
+
+ const prodVarsContent: string[] = [
+ '# Production environment variables for Cloudflare Orange Build',
+ '# Generated automatically during deployment',
+ '',
+ '# Essential Secrets:',
+ ];
+
+ // Add environment variables that are set
+ secretVars.forEach((varName) => {
+ let value = process.env[varName];
+
+ // Apply fallback logic for CLOUDFLARE_AI_GATEWAY_TOKEN
+ if (varName === 'CLOUDFLARE_AI_GATEWAY_TOKEN' && (!value || value === '')) {
+ value = this.env.CLOUDFLARE_AI_GATEWAY_TOKEN;
+ }
+
+ if (value && value !== '') {
+ // Skip placeholder values
+ if (
+ value.startsWith('optional-') ||
+ value.startsWith('your-')
+ ) {
+ prodVarsContent.push(
+ `# ${varName}="${value}" # Placeholder - update with actual value`,
+ );
+ } else {
+ prodVarsContent.push(`${varName}="${value}"`);
+ }
+ } else {
+ prodVarsContent.push(
+ `# ${varName}="" # Not set in current environment`,
+ );
+ }
+ });
+
+ // Add environment marker
+ prodVarsContent.push('');
+ // prodVarsContent.push('ENVIRONMENT="prod"');
+
+ try {
+ writeFileSync(
+ prodVarsPath,
+ prodVarsContent.join('\n') + '\n',
+ 'utf-8',
+ );
+ console.log(
+ `โ
Created .prod.vars file with ${secretVars.length} environment variables`,
+ );
+ } catch (error) {
+ console.warn(
+ `โ ๏ธ Could not create .prod.vars file: ${error instanceof Error ? error.message : String(error)}`,
+ );
+ throw new DeploymentError(
+ 'Failed to create .prod.vars file',
+ error instanceof Error ? error : new Error(String(error)),
+ );
+ }
+ }
+
+ /**
+ * Updates secrets using Wrangler (non-blocking)
+ */
+ private async updateSecrets(): Promise {
+ console.log('๐ Updating production secrets...');
+
+ try {
+ const prodVarsPath = join(PROJECT_ROOT, '.prod.vars');
+
+ // Check if .prod.vars file exists, create it if not
+ if (!existsSync(prodVarsPath)) {
+ console.log(
+ '๐ .prod.vars file not found, creating from environment variables...',
+ );
+ this.createProdVarsFile();
+ }
+
+ // Verify file exists after creation attempt
+ if (!existsSync(prodVarsPath)) {
+ console.warn(
+ 'โ ๏ธ Could not create .prod.vars file, skipping secret update',
+ );
+ return;
+ }
+
+ execSync('wrangler secret bulk .prod.vars', {
+ stdio: 'inherit',
+ cwd: PROJECT_ROOT,
+ });
+
+ console.log('โ
Production secrets updated successfully');
+ } catch (error) {
+ // Non-blocking: Log warning but don't fail deployment
+ console.warn(
+ `โ ๏ธ Could not update secrets: ${error instanceof Error ? error.message : String(error)}`,
+ );
+ console.warn(
+ ' You may need to update secrets manually if required',
+ );
+ }
+ }
+
+ /**
+ * Checks if dispatch namespaces (Workers for Platforms) are available for the user
+ */
+ private async checkDispatchNamespaceAvailability(): Promise {
+ console.log('๐ Checking dispatch namespace availability (Workers for Platforms)...');
+
+ try {
+ // Run the wrangler dispatch-namespace list command to test availability
+ const result = execSync('npx wrangler dispatch-namespace list', {
+ stdio: 'pipe',
+ cwd: PROJECT_ROOT,
+ encoding: 'utf8',
+ });
+
+ // If the command succeeds without error, dispatch namespaces are available
+ console.log('โ
Dispatch namespaces are available');
+ return true;
+
+ } catch (error: any) {
+ // Parse the error to check if it's specifically about dispatch namespace access
+ const errorOutput = error.stderr || error.stdout || error.message || '';
+
+ if (errorOutput.includes('You do not have access to dispatch namespaces') ||
+ errorOutput.includes('code: 10121')) {
+ console.log('โ ๏ธ Dispatch namespaces are NOT available');
+ console.log(' Workers for Platforms is not enabled for this account');
+ console.log(' You can purchase it at: https://dash.cloudflare.com?to=/:account/workers-for-platforms');
+ console.log(' If you are an Enterprise customer, please contact your account team');
+ return false;
+ }
+
+ // For other errors, log them but assume availability (conservative approach)
+ console.warn(`โ ๏ธ Could not verify dispatch namespace availability: ${errorOutput}`);
+ console.warn(' Proceeding with assumption that dispatch namespaces are available');
+ return true;
+ }
+ }
+
+ /**
+ * Comments out the dispatch_namespaces section in wrangler.jsonc when not available
+ */
+ private commentOutDispatchNamespaces(): void {
+ try {
+ console.log('๐ง Commenting out dispatch_namespaces in wrangler.jsonc...');
+
+ const wranglerPath = join(PROJECT_ROOT, 'wrangler.jsonc');
+ const content = readFileSync(wranglerPath, 'utf-8');
+
+ // Check if dispatch_namespaces is currently uncommented
+ if (!content.includes('"dispatch_namespaces": [')) {
+ console.log('โน๏ธ dispatch_namespaces already commented out or not present');
+ return;
+ }
+
+ // Comment out the dispatch_namespaces section
+ // Look for the pattern and replace it with commented version
+ const commentedContent = content.replace(
+ /(\s*)"dispatch_namespaces": \[[\s\S]*?\]/,
+ '$1// "dispatch_namespaces": [\n$1// {\n$1// "binding": "DISPATCHER",\n$1// "namespace": "orange-build-default-namespace",\n$1// "experimental_remote": true\n$1// }\n$1// ]'
+ );
+
+ if (commentedContent !== content) {
+ this.writeWranglerConfig(commentedContent);
+ this.logSuccess('Successfully commented out dispatch_namespaces in wrangler.jsonc');
+ } else {
+ console.log('โน๏ธ No changes needed for dispatch_namespaces');
+ }
+
+ } catch (error) {
+ this.logWarning(`Could not comment out dispatch_namespaces: ${error instanceof Error ? error.message : String(error)}`, [
+ 'Continuing with deployment...'
+ ]);
+ }
+ }
+
+ /**
+ * Runs database migrations
+ */
+ private async runDatabaseMigrations(): Promise {
+ console.log('Running database migrations...');
+ try {
+ await execSync(
+ 'bun run db:generate && bun run db:migrate:remote',
+ {
+ stdio: 'inherit',
+ cwd: PROJECT_ROOT,
+ encoding: 'utf8',
+ }
+ );
+ } catch (error) {
+ console.warn('Database migrations failed:', error instanceof Error ? error.message : String(error));
+ }
+ }
+
+ /**
+ * Main deployment orchestration method
+ */
+ public async deploy(): Promise {
+ console.log(
+ '๐งก Cloudflare Orange Build - Automated Deployment Starting...\n',
+ );
+
+ const startTime = Date.now();
+ let customDomain: string | null = null;
+ let originalDockerfileContent: string | null = null;
+
+ try {
+ // Step 1: Early Configuration Updates (must happen before any wrangler commands)
+ this.cleanWranglerCache();
+ console.log('\n๐ Step 1: Updating configuration files...');
+
+ console.log(' ๐ง Cleaning ARM64 development flags from Dockerfile');
+ originalDockerfileContent = this.cleanDockerfileForDeployment();
+
+ console.log(' ๐ง Updating package.json database commands');
+ this.updatePackageJsonDatabaseCommands();
+
+ console.log(' ๐ง Updating wrangler.jsonc custom domain routes');
+ customDomain = await this.updateCustomDomainRoutes();
+
+ console.log(' ๐ง Updating container instance types');
+ this.updateContainerInstanceTypes();
+
+ console.log('โ
Configuration files updated successfully!\n');
+
+ // Step 1.5: Check dispatch namespace availability early
+ console.log('\n๐ Step 1.5: Checking dispatch namespace availability...');
+ const dispatchNamespacesAvailable = await this.checkDispatchNamespaceAvailability();
+
+ // Comment out dispatch_namespaces in wrangler.jsonc if not available
+ if (!dispatchNamespacesAvailable) {
+ this.commentOutDispatchNamespaces();
+ }
+ console.log('โ
Dispatch namespace availability check completed!\n');
+
+ // Step 2: Update container configuration if needed
+ console.log('\n๐ Step 2: Updating container configuration...');
+ this.updateContainerConfiguration();
+ this.updateDispatchNamespace(dispatchNamespacesAvailable);
+
+ // Step 3: Resolve var/secret conflicts before deployment
+ console.log('\n๐ Step 3: Resolving var/secret conflicts...');
+ const conflictingVars = await this.removeConflictingVars();
+
+ // Store for potential cleanup on early exit
+ this.conflictingVarsForCleanup = conflictingVars;
+
+ // Steps 2-4: Run all setup operations in parallel
+ const operations: Promise[] = [
+ this.deployTemplates(),
+ this.buildProject(),
+ ];
+
+ // Only add dispatch namespace setup if available
+ if (dispatchNamespacesAvailable) {
+ operations.push(this.ensureDispatchNamespace());
+ }
+
+ // Add AI Gateway setup if gateway name is provided
+ if (this.env.CLOUDFLARE_AI_GATEWAY) {
+ operations.push(this.ensureAIGateway());
+ }
+
+ // Log the operations that will run in parallel
+ console.log(
+ '๐ Step 4: Running all setup operations in parallel...',
+ );
+ if (dispatchNamespacesAvailable) {
+ console.log(' ๐ Workers for Platforms namespace setup');
+ } else {
+ console.log(' โญ๏ธ Skipping Workers for Platforms namespace setup (not available)');
+ }
+ console.log(' ๐ Templates repository deployment');
+ console.log(' ๐ Project build (clean + compile)');
+ if (this.env.CLOUDFLARE_AI_GATEWAY) {
+ console.log(' ๐ AI Gateway setup and configuration');
+ }
+
+ await Promise.all(operations);
+
+ console.log(
+ 'โ
Parallel setup and build operations completed!',
+ );
+
+ let deploymentSucceeded = false;
+ try {
+ // Step 5: Deploy with Wrangler (now without conflicts)
+ console.log('\n๐ Step 5: Deploying to Cloudflare Workers...');
+ await this.wranglerDeploy();
+
+ // Step 6: Update secrets (now no conflicts)
+ console.log('\n๐ Step 6: Updating production secrets...');
+ await this.updateSecrets();
+
+ deploymentSucceeded = true;
+ } finally {
+ // Step 7: Always restore original vars (even if deployment failed)
+ console.log('\n๐ Step 7: Restoring original configuration...');
+ await this.restoreOriginalVars(conflictingVars);
+
+ // Clear the backup since we've restored
+ this.conflictingVarsForCleanup = null;
+ }
+
+ // Step 8: Run database migrations
+ console.log('\n๐ Step 8: Running database migrations...');
+ await this.runDatabaseMigrations();
+
+ // Deployment complete
+ if (deploymentSucceeded) {
+ const duration = Math.round((Date.now() - startTime) / 1000);
+ console.log(
+ `\n๐ Complete deployment finished successfully in ${duration}s!`,
+ );
+ console.log(
+ `โ
Your Cloudflare Orange Build platform is now live at https://${customDomain}! ๐`,
+ );
+
+ // Restore ARM64 flags for continued local development
+ if (originalDockerfileContent) {
+ console.log('\n๐ Restoring local development configuration...');
+ this.restoreDockerfileARM64Flags(originalDockerfileContent);
+ }
+ } else {
+ throw new DeploymentError('Deployment failed during wrangler deploy or secret update');
+ }
+ } catch (error) {
+ console.error('\nโ Deployment failed:');
+
+ if (error instanceof DeploymentError) {
+ console.error(` ${error.message}`);
+ if (error.cause) {
+ console.error(` Caused by: ${error.cause.message}`);
+ }
+ } else {
+ console.error(` ${error}`);
+ }
+
+ console.error('\n๐ Troubleshooting tips:');
+ console.error(
+ ' - Verify all environment variables are correctly set',
+ );
+ console.error(
+ ' - Check your Cloudflare API token has required permissions',
+ );
+ console.error(
+ ' - Ensure your account has access to Workers for Platforms',
+ );
+ console.error(' - Verify the templates repository is accessible');
+ console.error(
+ ' - Check that bun is installed and build script works',
+ );
+
+ process.exit(1);
+ } finally {
+ // Always restore ARM64 flags if they were removed, even on deployment failure
+ if (originalDockerfileContent) {
+ console.log('\n๐ Restoring local development configuration...');
+ this.restoreDockerfileARM64Flags(originalDockerfileContent);
+ }
+ }
+ }
+}
+
+// Main execution
+if (import.meta.url === `file://${process.argv[1]}`) {
+ const deployer = new CloudflareDeploymentManager();
+ deployer.deploy().catch((error) => {
+ console.error('Unexpected error:', error);
+ process.exit(1);
+ });
+}
+
+export default CloudflareDeploymentManager;
diff --git a/external/drpower-vibe-production/scripts/setup.ts b/external/drpower-vibe-production/scripts/setup.ts
new file mode 100755
index 00000000..b115ca32
--- /dev/null
+++ b/external/drpower-vibe-production/scripts/setup.ts
@@ -0,0 +1,2023 @@
+#!/usr/bin/env node
+
+import { execSync } from 'child_process';
+import { existsSync, readFileSync, writeFileSync } from 'fs';
+import { join, dirname } from 'path';
+import { fileURLToPath } from 'url';
+import { parse, modify, applyEdits } from 'jsonc-parser';
+import Cloudflare from 'cloudflare';
+import { createInterface } from 'readline';
+
+// Get current directory for ES modules
+const __filename = fileURLToPath(import.meta.url);
+const __dirname = dirname(__filename);
+const PROJECT_ROOT = join(__dirname, '..');
+
+interface SetupConfig {
+ accountId: string;
+ apiToken: string;
+ customDomain?: string;
+ useAIGateway: boolean;
+ aiGatewayUrl?: string;
+ useRemoteBindings: boolean;
+ devVars: Record;
+ setupRemote?: boolean;
+ prodDomain?: string;
+ prodVars?: Record;
+ customProviderKeys?: Array<{key: string, provider: string}>;
+}
+
+interface ResourceInfo {
+ kvNamespaces: Array<{ name: string; id: string; binding: string; accessible: boolean }>;
+ d1Databases: Array<{ name: string; id: string; binding: string; accessible: boolean }>;
+ r2Buckets: Array<{ name: string; binding: string; accessible: boolean }>;
+ dispatchNamespaces: Array<{ name: string; binding: string; accessible: boolean }>;
+ zones: Array<{ name: string; id: string }>;
+ aiGateway?: { name: string; exists: boolean; tokenCreated: boolean; tokenError?: string };
+}
+
+interface ReadinessReport {
+ localDevReady: boolean;
+ deploymentReady: boolean;
+ issues: string[];
+ suggestions: string[];
+ resourcesCreated: string[];
+ accountInfo?: {
+ plan: string;
+ features: string[];
+ };
+}
+
+class SetupManager {
+ private config!: SetupConfig;
+ private cloudflare!: Cloudflare;
+ private aiGatewayCloudflare?: Cloudflare;
+ private existingConfig: Record = {};
+ private packageManager: 'bun' | 'npm' = 'npm';
+ private readline = createInterface({
+ input: process.stdin,
+ output: process.stdout,
+ });
+
+ constructor() {
+ console.log('๐ VibSDK Development Setup');
+ console.log('============================\n');
+ }
+
+ async setup(): Promise {
+ try {
+ await this.setupPackageManager();
+ this.loadExistingConfig();
+ await this.collectUserConfig();
+ await this.initializeCloudflareClient();
+
+ const resources = await this.validateAndSetupResources();
+
+ await this.safeExecute('generate .dev.vars file', () => this.generateDevVarsFile());
+ if (this.config.setupRemote) {
+ await this.safeExecute('generate .prod.vars file', () => this.generateProdVarsFile());
+ }
+ await this.safeExecute('update wrangler.jsonc', () => this.updateWranglerConfig(resources));
+ await this.safeExecute('update vite.config.ts', () => this.updateViteConfig());
+
+ const report = await this.generateReadinessReport(resources);
+
+ // Setup AI Gateway if configured
+ if (this.config.useAIGateway) {
+ await this.safeExecute('setup AI Gateway', () => this.ensureAIGateway(resources));
+ }
+
+ // Update worker configuration for custom providers
+ if (this.config.customProviderKeys && this.config.customProviderKeys.length > 0) {
+ await this.safeExecute('update worker configuration', () => this.updateWorkerConfiguration());
+ }
+
+ await this.patchDockerfileForARM64();
+
+ this.displayFinalReport(report, resources);
+ } catch (error) {
+ console.error('\nโ Setup encountered a critical error:', error instanceof Error ? error.message : String(error));
+ console.error('\n๐ก Troubleshooting:');
+ console.error(' 1. Verify your Cloudflare API token has the required permissions');
+ console.error(' 2. Check your account has access to the required Cloudflare services');
+ console.error(' 3. Try running the script again or set up manually');
+ console.error('\n๐ See docs/setup.md for manual setup instructions');
+ process.exit(1);
+ } finally {
+ this.readline.close();
+ }
+ }
+
+ private async safeExecute(actionName: string, action: () => Promise): Promise {
+ try {
+ await action();
+ } catch (error) {
+ console.error(`โ Failed to ${actionName}:`, error instanceof Error ? error.message : String(error));
+ console.error(' You may need to complete this step manually');
+ }
+ }
+
+ private async setupPackageManager(): Promise {
+ console.log('๐ฆ Package Manager Setup');
+ console.log('------------------------\n');
+
+ const hasBun = await this.checkCommandExists('bun');
+ const hasNpm = await this.checkCommandExists('npm');
+
+ if (hasBun) {
+ console.log('โ
Bun is available - using bun for optimal performance');
+ this.packageManager = 'bun';
+ } else if (hasNpm) {
+ console.log('โ
npm is available');
+ console.log('๐ฅ Installing Bun for better performance...');
+
+ try {
+ execSync('curl -fsSL https://bun.sh/install | bash', { stdio: 'inherit' });
+
+ if (await this.checkCommandExists('bun')) {
+ console.log('โ
Bun installed successfully!');
+ this.packageManager = 'bun';
+ } else {
+ console.log('โ ๏ธ Bun installation completed, but may require shell restart. Using npm for now.');
+ this.packageManager = 'npm';
+ }
+ } catch (error) {
+ console.error('โ Failed to install Bun:', error instanceof Error ? error.message : String(error));
+ console.log(' Continuing with npm...');
+ this.packageManager = 'npm';
+ }
+ } else {
+ throw new Error('Neither npm nor bun is available. Please install Node.js with npm or Bun.');
+ }
+
+ console.log(`\n๐ฆ Using package manager: ${this.packageManager}\n`);
+ }
+
+ private async checkCommandExists(command: string): Promise {
+ try {
+ execSync(`${command} --version`, { stdio: 'pipe' });
+ return true;
+ } catch {
+ return false;
+ }
+ }
+
+ private loadExistingConfig(): void {
+ const devVarsPath = join(PROJECT_ROOT, '.dev.vars');
+ const prodVarsPath = join(PROJECT_ROOT, '.prod.vars');
+
+ // Load .dev.vars
+ if (existsSync(devVarsPath)) {
+ console.log('๐ Found existing .dev.vars file - reading current configuration...');
+ this.parseConfigFile(devVarsPath);
+ }
+
+ // Load .prod.vars for production config
+ if (existsSync(prodVarsPath)) {
+ console.log('๐ Found existing .prod.vars file - reading production configuration...');
+ this.parseConfigFile(prodVarsPath);
+ }
+
+ if (!existsSync(devVarsPath) && !existsSync(prodVarsPath)) {
+ console.log('๐ No existing configuration files found - starting fresh setup');
+ return;
+ }
+
+ const configuredKeys = Object.keys(this.existingConfig);
+ if (configuredKeys.length > 0) {
+ console.log(`โ
Found ${configuredKeys.length} existing configuration values`);
+ console.log(' Will only prompt for missing or updated values\n');
+ }
+ }
+
+ private parseConfigFile(filePath: string): void {
+ try {
+ const content = readFileSync(filePath, 'utf-8');
+ const lines = content.split('\n');
+
+ for (const line of lines) {
+ const trimmed = line.trim();
+ if (trimmed && !trimmed.startsWith('#') && trimmed.includes('=')) {
+ const [key, ...valueParts] = trimmed.split('=');
+ const value = valueParts.join('=').replace(/^["']|["']$/g, ''); // Remove quotes
+ if (key && value) {
+ this.existingConfig[key] = value;
+ }
+ }
+ }
+ } catch (error) {
+ console.warn(`โ ๏ธ Could not read config file: ${error instanceof Error ? error.message : String(error)}`);
+ }
+ }
+
+ private async prompt(question: string): Promise {
+ return new Promise((resolve) => {
+ this.readline.question(question, (answer) => {
+ resolve(answer.trim());
+ });
+ });
+ }
+
+ private async promptWithDefault(question: string, existingValue?: string): Promise {
+ if (existingValue) {
+ const maskedValue = this.maskSensitiveValue(question, existingValue);
+ const answer = await this.prompt(`${question} [current: ${maskedValue}]: `);
+ return answer || existingValue;
+ }
+ return this.prompt(question);
+ }
+
+ private maskSensitiveValue(question: string, value: string): string {
+ const sensitivePatterns = ['TOKEN', 'SECRET', 'KEY', 'PASSWORD'];
+ const isSensitive = sensitivePatterns.some(pattern =>
+ question.toUpperCase().includes(pattern)
+ );
+
+ if (isSensitive && value.length > 8) {
+ return `${value.substring(0, 4)}...${value.substring(value.length - 4)}`;
+ }
+ return value;
+ }
+
+ private async collectUserConfig(): Promise {
+ console.log('๐ Configuration Review & Setup');
+ console.log('--------------------------------\n');
+
+ // Get Cloudflare account ID
+ const accountId = await this.promptWithDefault(
+ 'Enter your Cloudflare Account ID: ',
+ this.existingConfig.CLOUDFLARE_ACCOUNT_ID
+ );
+ if (!accountId) {
+ throw new Error('Account ID is required');
+ }
+
+ // Get API token
+ const apiToken = await this.promptWithDefault(
+ 'Enter your Cloudflare API Token: ',
+ this.existingConfig.CLOUDFLARE_API_TOKEN
+ );
+ if (!apiToken) {
+ throw new Error('API Token is required');
+ }
+
+ // Domain Configuration - Ask once upfront with existing domain detection
+ console.log('\n๐ Domain Configuration');
+ console.log('A custom domain is required for production deployment and remote resource access.');
+ console.log('Without a custom domain, only local development will be available.\n');
+
+ let customDomain: string | undefined;
+ let useRemoteBindings = false;
+ let setupRemote = false;
+ let prodDomain: string | undefined;
+
+ // Check if we already have a production domain configured
+ const existingProdDomain = this.existingConfig.CUSTOM_DOMAIN &&
+ this.existingConfig.CUSTOM_DOMAIN !== 'localhost:5173' ?
+ this.existingConfig.CUSTOM_DOMAIN : undefined;
+
+ while (true) {
+ customDomain = await this.promptWithDefault(
+ 'Enter your custom domain (or press Enter to skip): ',
+ existingProdDomain || this.existingConfig.CUSTOM_DOMAIN
+ );
+
+ if (!customDomain || customDomain.trim() === '' || customDomain === 'localhost:5173') {
+ console.log('\nโ ๏ธ No custom domain provided.');
+ console.log(' โข Remote Cloudflare resources: Not available');
+ console.log(' โข Production deployment: Not available');
+ console.log(' โข Only local development will be configured\n');
+
+ const continueChoice = await this.prompt('Continue with local-only setup? (Y/n): ');
+ if (continueChoice.toLowerCase() === 'n') {
+ console.log('Please provide a custom domain:\n');
+ continue;
+ }
+
+ customDomain = 'localhost:5173';
+ useRemoteBindings = false;
+ setupRemote = false;
+ break;
+ } else {
+ console.log(`โ
Custom domain set: ${customDomain}`);
+ prodDomain = customDomain; // Use same domain for production
+
+ // Ask about remote resources
+ const remoteChoice = await this.prompt('Use remote Cloudflare resources (KV, D1, R2, etc.)? (Y/n): ');
+ useRemoteBindings = remoteChoice.toLowerCase() !== 'n';
+
+ // Ask about production setup
+ const prodChoice = await this.prompt('Configure for production deployment? (Y/n): ');
+ setupRemote = prodChoice.toLowerCase() !== 'n';
+
+ if (useRemoteBindings) {
+ console.log('โ
Remote Cloudflare resources will be used');
+ } else {
+ console.log('โ
Local-only bindings selected');
+ }
+
+ break;
+ }
+ }
+
+ const finalDomain = customDomain || 'localhost:5173';
+
+ // AI Gateway configuration
+ console.log('\n๐ค AI Gateway Configuration');
+ const useAIGatewayChoice = await this.prompt('Use Cloudflare AI Gateway? [STRONGLY RECOMMENDED for developer experience] (Y/n): ');
+ const useAIGateway = useAIGatewayChoice.toLowerCase() !== 'n';
+
+ let aiGatewayUrl: string | undefined;
+ const devVars: Record = {};
+ const providedProviders: string[] = [];
+ let customProviderKeys: Array<{key: string, provider: string}> = [];
+
+ if (useAIGateway) {
+ console.log('โ
AI Gateway enabled - will auto-configure CLOUDFLARE_AI_GATEWAY_TOKEN');
+
+ // Generate suggested URL
+ const wranglerConfig = this.parseWranglerConfig();
+ const gatewayName = wranglerConfig.vars?.CLOUDFLARE_AI_GATEWAY || 'vibesdk-gateway';
+ const suggestedUrl = `https://gateway.ai.cloudflare.com/v1/${accountId}/${gatewayName}/`;
+
+ // Use existing URL if available, otherwise use suggested URL as default
+ const existingUrl = this.existingConfig.CLOUDFLARE_AI_GATEWAY_URL;
+ const defaultUrl = existingUrl || suggestedUrl;
+
+ if (!existingUrl) {
+ console.log(`\n๐ก AI Gateway URL format: https://gateway.ai.cloudflare.com/v1///`);
+ console.log(` Suggested: ${suggestedUrl}`);
+ }
+
+ aiGatewayUrl = await this.promptWithDefault(
+ 'Enter AI Gateway URL: ',
+ defaultUrl
+ );
+
+ if (!aiGatewayUrl || aiGatewayUrl.trim() === '') {
+ throw new Error('AI Gateway URL is required when AI Gateway is enabled');
+ }
+ } else {
+ console.log('\nโ ๏ธ WARNING: Without AI Gateway, you MUST manually edit worker/agents/inferutils/config.ts');
+ console.log(' to configure your models. Model names should be in format: "/"');
+ console.log(' Example: "openai/gpt-4" or "anthropic/claude-3-5-sonnet"\n');
+
+ aiGatewayUrl = await this.prompt('Enter custom OpenAI-compatible URL (optional): ');
+ }
+
+ // AI Provider configuration
+ console.log('\n๐ง AI Provider Configuration');
+ console.log('Available providers:');
+ console.log(' 1. OpenAI (for GPT models)');
+ console.log(' 2. Anthropic (for Claude models)');
+ console.log(' 3. Google AI Studio (for Gemini models) [DEFAULT]');
+ console.log(' 4. Cerebras (for open source models)');
+ console.log(' 5. OpenRouter (for various models)');
+ console.log(' 6. Custom provider\n');
+
+ const providerChoice = await this.prompt('Select providers (comma-separated numbers, e.g., 1,2,3): ');
+ const selectedProviders = providerChoice.split(',').map(n => parseInt(n.trim())).filter(n => n >= 1 && n <= 6);
+
+ if (selectedProviders.length === 0) {
+ console.log('โ ๏ธ No providers selected - you MUST configure at least one provider!');
+ console.log(' Adding Google AI Studio as default...');
+ selectedProviders.push(3);
+ }
+
+ // Process selected providers
+ const providerMap = {
+ 1: { name: 'OpenAI', key: 'OPENAI_API_KEY', provider: 'openai' },
+ 2: { name: 'Anthropic', key: 'ANTHROPIC_API_KEY', provider: 'anthropic' },
+ 3: { name: 'Google AI Studio', key: 'GOOGLE_AI_STUDIO_API_KEY', provider: 'google-ai-studio' },
+ 4: { name: 'Cerebras', key: 'CEREBRAS_API_KEY', provider: 'cerebras' },
+ 5: { name: 'OpenRouter', key: 'OPENROUTER_API_KEY', provider: 'openrouter' }
+ };
+
+ console.log('\n๐ API Key Configuration');
+ for (const choice of selectedProviders) {
+ if (choice === 6) {
+ // Custom provider
+ const customProviderName = await this.prompt('Enter custom provider name: ');
+ if (customProviderName) {
+ const customKey = `${customProviderName.toUpperCase().replace(/[^A-Z0-9]/g, '_')}_API_KEY`;
+ const apiKey = await this.prompt(`${customKey}: `);
+ if (apiKey) {
+ devVars[customKey] = apiKey;
+ customProviderKeys.push({ key: customKey, provider: customProviderName });
+ providedProviders.push(customProviderName);
+ }
+ }
+ } else {
+ const provider = providerMap[choice as keyof typeof providerMap];
+ if (provider) {
+ const existing = this.existingConfig[provider.key];
+ const value = await this.promptWithDefault(`${provider.name} API Key (${provider.key}): `, existing);
+ if (value) {
+ devVars[provider.key] = value;
+ providedProviders.push(provider.provider);
+ }
+ }
+ }
+ }
+
+ // Warning about config.ts if not using Gemini as default
+ const hasGemini = selectedProviders.includes(3);
+ if (!hasGemini) {
+ console.log('\nโ ๏ธ IMPORTANT: You selected providers other than Google AI Studio (Gemini).');
+ console.log(' You MUST edit worker/agents/inferutils/config.ts to change the default model configurations');
+ console.log(' from Gemini models to your selected providers!\n');
+ }
+
+ // OAuth and other configuration with smart prompts
+ console.log('\n๐ OAuth & Other Configuration');
+ console.log('โน๏ธ These credentials enable user authentication and external integrations:');
+ console.log(' โข Google: For Google OAuth user login');
+ console.log(' โข GitHub: For GitHub OAuth user login');
+ console.log(' โข GitHub Export: For exporting generated apps to GitHub repositories\n');
+
+ const otherVars = [
+ 'GOOGLE_CLIENT_ID',
+ 'GOOGLE_CLIENT_SECRET',
+ 'GITHUB_CLIENT_ID',
+ 'GITHUB_CLIENT_SECRET',
+ 'GITHUB_EXPORTER_CLIENT_ID',
+ 'GITHUB_EXPORTER_CLIENT_SECRET'
+ ];
+
+ for (const varName of otherVars) {
+ const existing = this.existingConfig[varName];
+ const value = await this.promptWithDefault(`${varName}: `, existing);
+ if (value) {
+ devVars[varName] = value;
+ }
+ }
+
+ // Provide guidance on model configuration
+ if (providedProviders.length > 0) {
+ console.log(`\nโ
API keys configured for: ${providedProviders.join(', ')}`);
+ }
+
+ if (!providedProviders.includes('google-ai-studio')) {
+ console.log('\nโ ๏ธ No Google AI Studio key provided.');
+ console.log(' You may need to update model configs in worker/agents/inferutils/config.ts');
+ console.log(' to use alternative models (OpenAI, Anthropic, etc.) for Gemini fallbacks.');
+ }
+
+ // Generate or preserve required secrets
+ devVars.JWT_SECRET = this.existingConfig.JWT_SECRET || this.generateRandomSecret(64);
+ devVars.WEBHOOK_SECRET = this.existingConfig.WEBHOOK_SECRET || this.generateRandomSecret(32);
+ devVars.USE_TUNNEL_FOR_PREVIEW = 'true';
+
+ // Auto-set AI Gateway token if using AI Gateway
+ if (useAIGateway) {
+ devVars.CLOUDFLARE_AI_GATEWAY_TOKEN = apiToken;
+ }
+
+ // Prepare production vars (copy dev vars as defaults)
+ const prodVars = setupRemote && prodDomain ? { ...devVars, CUSTOM_DOMAIN: prodDomain } : undefined;
+
+ this.config = {
+ accountId,
+ apiToken,
+ customDomain: finalDomain,
+ useAIGateway,
+ aiGatewayUrl,
+ useRemoteBindings,
+ devVars,
+ setupRemote,
+ prodDomain,
+ prodVars,
+ customProviderKeys
+ };
+
+ console.log('\nโ
Configuration collected successfully\n');
+ }
+
+ private generateRandomSecret(length: number): string {
+ const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
+ let result = '';
+ for (let i = 0; i < length; i++) {
+ result += chars.charAt(Math.floor(Math.random() * chars.length));
+ }
+ return result;
+ }
+
+ private async initializeCloudflareClient(): Promise {
+ console.log('๐ Validating Cloudflare credentials...');
+
+ this.cloudflare = new Cloudflare({
+ apiToken: this.config.apiToken,
+ });
+
+ try {
+ // Verify token by getting account info
+ const account = await this.cloudflare.accounts.get({ account_id: this.config.accountId });
+ console.log(`โ
Connected to account: ${account.name}`);
+ } catch (error) {
+ throw new Error(`Failed to validate Cloudflare credentials: ${error instanceof Error ? error.message : String(error)}`);
+ }
+ }
+
+ private async validateAndSetupResources(): Promise {
+ console.log('๐ Validating and setting up Cloudflare resources...');
+
+ const wranglerConfig = this.parseWranglerConfig();
+ const resources: ResourceInfo = {
+ kvNamespaces: [],
+ d1Databases: [],
+ r2Buckets: [],
+ dispatchNamespaces: [],
+ zones: []
+ };
+
+ await this.processKVNamespaces(wranglerConfig, resources);
+ await this.processD1Databases(wranglerConfig, resources);
+
+ await this.safeExecute('setup database', () => this.setupDatabase(resources));
+
+ await this.processR2Buckets(wranglerConfig, resources);
+
+ await this.safeExecute('deploy templates', () => this.deployTemplates(resources));
+
+ await this.processDispatchNamespaces(wranglerConfig, resources);
+ await this.processCustomDomain(resources);
+
+ return resources;
+ }
+
+ private async processKVNamespaces(wranglerConfig: any, resources: ResourceInfo): Promise {
+ if (!wranglerConfig.kv_namespaces) return;
+
+ for (const kv of wranglerConfig.kv_namespaces) {
+ if (!this.config.useRemoteBindings) {
+ resources.kvNamespaces.push(this.createLocalResource(kv.binding, 'local'));
+ } else {
+ try {
+ const kvInfo = await this.ensureKVNamespace(kv.binding);
+ resources.kvNamespaces.push({
+ name: kvInfo.title,
+ id: kvInfo.id,
+ binding: kv.binding,
+ accessible: true
+ });
+ } catch (error) {
+ this.handleResourceError('KV namespace', kv.binding, error);
+ resources.kvNamespaces.push(this.createLocalResource(kv.binding, 'local'));
+ }
+ }
+ }
+ }
+
+ private async processD1Databases(wranglerConfig: any, resources: ResourceInfo): Promise {
+ if (!wranglerConfig.d1_databases) return;
+
+ for (const db of wranglerConfig.d1_databases) {
+ if (!this.config.useRemoteBindings) {
+ resources.d1Databases.push(this.createLocalResource(db.binding, 'local', db.database_name));
+ } else {
+ try {
+ const dbInfo = await this.ensureD1Database(db.database_name, db.binding);
+ resources.d1Databases.push({
+ name: dbInfo.name,
+ id: dbInfo.uuid,
+ binding: db.binding,
+ accessible: true
+ });
+ } catch (error) {
+ this.handleResourceError('D1 database', db.database_name, error, 'D1 Database not accessible (likely requires paid plan)');
+ resources.d1Databases.push(this.createLocalResource(db.binding, 'local', db.database_name));
+ }
+ }
+ }
+ }
+
+ private async processR2Buckets(wranglerConfig: any, resources: ResourceInfo): Promise {
+ if (!wranglerConfig.r2_buckets) return;
+
+ for (const bucket of wranglerConfig.r2_buckets) {
+ if (!this.config.useRemoteBindings) {
+ resources.r2Buckets.push({ name: bucket.bucket_name, binding: bucket.binding, accessible: false });
+ } else {
+ try {
+ await this.ensureR2Bucket(bucket.bucket_name, bucket.binding);
+ resources.r2Buckets.push({ name: bucket.bucket_name, binding: bucket.binding, accessible: true });
+ } catch (error) {
+ this.handleResourceError('R2 bucket', bucket.bucket_name, error, 'R2 Bucket not accessible (likely requires paid plan)');
+ resources.r2Buckets.push({ name: bucket.bucket_name, binding: bucket.binding, accessible: false });
+ }
+ }
+ }
+ }
+
+ private async processDispatchNamespaces(wranglerConfig: any, resources: ResourceInfo): Promise {
+ if (!wranglerConfig.dispatch_namespaces) return;
+
+ for (const dispatch of wranglerConfig.dispatch_namespaces) {
+ if (!this.config.useRemoteBindings) {
+ resources.dispatchNamespaces.push({ name: dispatch.namespace, binding: dispatch.binding, accessible: false });
+ } else {
+ try {
+ await this.ensureDispatchNamespace(dispatch.namespace, dispatch.binding);
+ resources.dispatchNamespaces.push({ name: dispatch.namespace, binding: dispatch.binding, accessible: true });
+ } catch (error) {
+ this.handleResourceError('dispatch namespace', dispatch.namespace, error, 'Dispatch Namespace not accessible (requires Workers for Platforms)');
+ resources.dispatchNamespaces.push({ name: dispatch.namespace, binding: dispatch.binding, accessible: false });
+ }
+ }
+ }
+ }
+
+ private async processCustomDomain(resources: ResourceInfo): Promise {
+ // Check production domain first (priority for zone detection)
+ const domainToCheck = this.config.setupRemote && this.config.prodDomain
+ ? this.config.prodDomain
+ : (this.config.customDomain !== 'localhost:5173' ? this.config.customDomain : null);
+
+ if (domainToCheck) {
+ try {
+ const zoneInfo = await this.detectZoneForDomain(domainToCheck);
+ if (zoneInfo.zoneId) {
+ resources.zones.push({ name: zoneInfo.zoneName!, id: zoneInfo.zoneId });
+ }
+ } catch (error) {
+ console.warn(`โ ๏ธ Domain validation failed for ${domainToCheck}: ${error instanceof Error ? error.message : String(error)}`);
+ }
+ }
+ }
+
+ private createLocalResource(binding: string, id: string, name?: string) {
+ const resourceName = name || `${binding}-local`;
+ console.log(`๐ ${binding} - using local-only mode (user preference)`);
+ return { name: resourceName, id, binding, accessible: false };
+ }
+
+ private handleResourceError(resourceType: string, resourceName: string, error: unknown, specificMessage?: string): void {
+ const errorMsg = `Failed to setup ${resourceType} ${resourceName}: ${error instanceof Error ? error.message : String(error)}`;
+ console.warn(`โ ๏ธ ${errorMsg} - will use local-only mode`);
+
+ if (specificMessage && error instanceof Error && error.message.includes('Unauthorized')) {
+ console.warn(`๐ก ${specificMessage}`);
+ console.warn(' Will continue with local-only for development');
+ }
+ }
+
+ private async ensureKVNamespace(binding: string): Promise<{ id: string; title: string }> {
+ const namespaceName = `vibesdk-${binding.toLowerCase()}-local`;
+
+ try {
+ // Check if namespace exists using direct API call
+ const response = await fetch(
+ `https://api.cloudflare.com/client/v4/accounts/${this.config.accountId}/storage/kv/namespaces`,
+ {
+ headers: {
+ 'Authorization': `Bearer ${this.config.apiToken}`,
+ 'Content-Type': 'application/json',
+ },
+ }
+ );
+
+ if (response.ok) {
+ const data = await response.json();
+ if (data.success && data.result) {
+ const existingNamespace = data.result.find((ns: any) => ns.title === namespaceName);
+ if (existingNamespace) {
+ console.log(`โ
KV namespace '${namespaceName}' already exists`);
+ return { id: existingNamespace.id, title: existingNamespace.title };
+ }
+ }
+ }
+
+ // Create new namespace
+ console.log(`๐ฆ Creating KV namespace: ${namespaceName}`);
+ const createResponse = await fetch(
+ `https://api.cloudflare.com/client/v4/accounts/${this.config.accountId}/storage/kv/namespaces`,
+ {
+ method: 'POST',
+ headers: {
+ 'Authorization': `Bearer ${this.config.apiToken}`,
+ 'Content-Type': 'application/json',
+ },
+ body: JSON.stringify({ title: namespaceName }),
+ }
+ );
+
+ if (createResponse.ok) {
+ const data = await createResponse.json();
+ if (data.success && data.result) {
+ console.log(`โ
Created KV namespace: ${namespaceName}`);
+ return { id: data.result.id, title: data.result.title };
+ }
+ }
+
+ throw new Error(`Failed to create KV namespace: ${createResponse.statusText}`);
+
+ } catch (error) {
+ throw new Error(`Failed to ensure KV namespace ${namespaceName}: ${error instanceof Error ? error.message : String(error)}`);
+ }
+ }
+
+ private async ensureD1Database(dbName: string, binding: string): Promise<{ uuid: string; name: string }> {
+ const baseUrl = `https://api.cloudflare.com/client/v4/accounts/${this.config.accountId}/d1/database`;
+ const headers = {
+ 'Authorization': `Bearer ${this.config.apiToken}`,
+ 'Content-Type': 'application/json',
+ };
+
+ const listResponse = await fetch(baseUrl, { headers });
+ if (listResponse.ok) {
+ const data = await listResponse.json();
+ const existingDb = data.result?.find((db: any) => db.name === dbName);
+ if (existingDb) {
+ console.log(`โ
D1 database '${dbName}' already exists`);
+ return { uuid: existingDb.uuid, name: existingDb.name };
+ }
+ }
+
+ console.log(`๐ฆ Creating D1 database: ${dbName}`);
+ const createResponse = await fetch(baseUrl, {
+ method: 'POST',
+ headers,
+ body: JSON.stringify({ name: dbName }),
+ });
+
+ const data = await createResponse.json();
+ if (!createResponse.ok || !data.success) {
+ const errorDetails = data.errors?.map((e: any) => e.message).join(', ') || createResponse.statusText;
+ throw new Error(`HTTP ${createResponse.status}: ${errorDetails}`);
+ }
+
+ console.log(`โ
Created D1 database: ${dbName}`);
+ return { uuid: data.result.uuid, name: data.result.name };
+ }
+
+ private async ensureDispatchNamespace(namespaceName: string, binding: string): Promise {
+ try {
+ console.log(`๐ Checking dispatch namespace: ${namespaceName}`);
+
+ // Use wrangler CLI to check if namespace exists
+ try {
+ execSync(`wrangler dispatch-namespace get ${namespaceName}`, {
+ stdio: 'pipe',
+ env: {
+ ...process.env,
+ CLOUDFLARE_API_TOKEN: this.config.apiToken,
+ CLOUDFLARE_ACCOUNT_ID: this.config.accountId,
+ },
+ });
+ console.log(`โ
Dispatch namespace '${namespaceName}' already exists`);
+ return;
+ } catch (error) {
+ // If namespace doesn't exist, create it
+ console.log(`๐ฆ Creating dispatch namespace: ${namespaceName}`);
+
+ execSync(`wrangler dispatch-namespace create ${namespaceName}`, {
+ stdio: 'pipe',
+ env: {
+ ...process.env,
+ CLOUDFLARE_API_TOKEN: this.config.apiToken,
+ CLOUDFLARE_ACCOUNT_ID: this.config.accountId,
+ },
+ });
+
+ console.log(`โ
Created dispatch namespace: ${namespaceName}`);
+ }
+ } catch (error) {
+ // Handle wrangler CLI errors gracefully
+ const stderr = error instanceof Error && 'stderr' in error ? (error as any).stderr?.toString() : '';
+
+ if (stderr.includes('You do not have access to dispatch namespaces') ||
+ stderr.includes('not available')) {
+ throw new Error('Dispatch namespaces not available on this account plan');
+ }
+
+ throw new Error(`Wrangler CLI error: ${error instanceof Error ? error.message : String(error)}`);
+ }
+ }
+
+ private async ensureR2Bucket(bucketName: string, binding: string): Promise {
+ const headers = {
+ 'Authorization': `Bearer ${this.config.apiToken}`,
+ 'Content-Type': 'application/json',
+ };
+
+ const checkResponse = await fetch(
+ `https://api.cloudflare.com/client/v4/accounts/${this.config.accountId}/r2/buckets/${bucketName}`,
+ { headers }
+ );
+
+ if (checkResponse.ok) {
+ console.log(`โ
R2 bucket '${bucketName}' already exists`);
+ return;
+ }
+
+ if (checkResponse.status !== 404) {
+ const errorData = await checkResponse.json().catch(() => ({}));
+ const errorDetails = errorData.errors?.map((e: any) => e.message).join(', ') || checkResponse.statusText;
+ throw new Error(`HTTP ${checkResponse.status}: ${errorDetails}`);
+ }
+
+ console.log(`๐ฆ Creating R2 bucket: ${bucketName}`);
+ const createResponse = await fetch(
+ `https://api.cloudflare.com/client/v4/accounts/${this.config.accountId}/r2/buckets`,
+ {
+ method: 'POST',
+ headers,
+ body: JSON.stringify({ name: bucketName }),
+ }
+ );
+
+ const data = await createResponse.json();
+ if (!createResponse.ok || !data.success) {
+ const errorDetails = data.errors?.map((e: any) => e.message).join(', ') || createResponse.statusText;
+ throw new Error(`HTTP ${createResponse.status}: ${errorDetails}`);
+ }
+
+ console.log(`โ
Created R2 bucket: ${bucketName}`);
+ }
+
+ private async ensureAIGateway(resources: ResourceInfo): Promise {
+ const gatewayName = this.config.useAIGateway ? 'vibesdk-gateway' : null;
+
+ if (!gatewayName) {
+ console.log('โน๏ธ AI Gateway setup skipped (not configured)');
+ return;
+ }
+
+ console.log(`๐ Checking AI Gateway: ${gatewayName}`);
+ let tokenCreated = false;
+ let tokenError: string | undefined;
+ let aiGatewayToken: string | null = null;
+
+ try {
+ // Check API token permissions first
+ console.log('๐ Checking API token permissions...');
+ await this.checkTokenPermissions();
+
+ try {
+ aiGatewayToken = await this.ensureAIGatewayToken();
+ tokenCreated = !!aiGatewayToken;
+ } catch (tokenErr) {
+ tokenError = tokenErr instanceof Error ? tokenErr.message : String(tokenErr);
+ console.warn(`โ ๏ธ Token creation issue: ${tokenError}`);
+ }
+
+ // Check if gateway exists first
+ const aiGatewaySDK = this.getAIGatewaySDK();
+
+ try {
+ await aiGatewaySDK.aiGateway.get(gatewayName, {
+ account_id: this.config.accountId,
+ });
+ console.log(`โ
AI Gateway '${gatewayName}' already exists`);
+ resources.aiGateway = { name: gatewayName, exists: true, tokenCreated, tokenError };
+ return;
+ } catch (error: any) {
+ if (error?.status !== 404 && !error?.message?.includes('not found')) {
+ console.warn(`โ ๏ธ Could not check AI Gateway '${gatewayName}': ${error.message}`);
+ resources.aiGateway = { name: gatewayName, exists: false, tokenCreated, tokenError: error.message };
+ return;
+ }
+ }
+
+ // Validate gateway name length
+ if (gatewayName.length > 64) {
+ const lengthError = `Gateway name too long (${gatewayName.length} > 64 chars)`;
+ console.warn(`โ ๏ธ ${lengthError}, skipping creation`);
+ resources.aiGateway = { name: gatewayName, exists: false, tokenCreated, tokenError: lengthError };
+ return;
+ }
+
+ // Create AI Gateway
+ console.log(`๐ฆ Creating AI Gateway: ${gatewayName}`);
+ await aiGatewaySDK.aiGateway.create({
+ account_id: this.config.accountId,
+ id: gatewayName,
+ cache_invalidate_on_update: true,
+ cache_ttl: 3600,
+ collect_logs: true,
+ rate_limiting_interval: 0,
+ rate_limiting_limit: 0,
+ rate_limiting_technique: 'sliding',
+ authentication: !!aiGatewayToken,
+ });
+
+ console.log(`โ
Successfully created AI Gateway: ${gatewayName} (authentication: ${aiGatewayToken ? 'enabled' : 'disabled'})`);
+ resources.aiGateway = { name: gatewayName, exists: true, tokenCreated, tokenError };
+ } catch (error) {
+ const errorMessage = error instanceof Error ? error.message : String(error);
+ console.warn(`โ ๏ธ Could not create AI Gateway '${gatewayName}': ${errorMessage}`);
+ console.warn(' Continuing setup without AI Gateway...');
+ resources.aiGateway = { name: gatewayName, exists: false, tokenCreated, tokenError: errorMessage };
+ }
+ }
+
+ private async checkTokenPermissions(): Promise<{ hasAIGatewayAccess: boolean; tokenInfo?: any }> {
+ try {
+ const verifyResponse = await fetch('https://api.cloudflare.com/client/v4/user/tokens/verify', {
+ headers: { Authorization: `Bearer ${this.config.apiToken}` },
+ });
+
+ if (!verifyResponse.ok) {
+ console.warn('โ ๏ธ Could not verify API token permissions');
+ return { hasAIGatewayAccess: false };
+ }
+
+ const data = await verifyResponse.json();
+ return { hasAIGatewayAccess: true, tokenInfo: data.result };
+ } catch (error) {
+ console.warn('โ ๏ธ Token verification failed');
+ return { hasAIGatewayAccess: false };
+ }
+ }
+
+ private async ensureAIGatewayToken(): Promise {
+ const currentToken = this.config.devVars.CLOUDFLARE_AI_GATEWAY_TOKEN;
+
+ if (currentToken && currentToken !== 'optional-your-cf-ai-gateway-token') {
+ console.log('โ
AI Gateway token already configured');
+ this.aiGatewayCloudflare = new Cloudflare({ apiToken: currentToken });
+ return currentToken;
+ }
+
+ try {
+ console.log('๐ Creating AI Gateway authentication token...');
+ const tokenResponse = await fetch('https://api.cloudflare.com/client/v4/user/tokens', {
+ method: 'POST',
+ headers: {
+ Authorization: `Bearer ${this.config.apiToken}`,
+ 'Content-Type': 'application/json',
+ },
+ body: JSON.stringify({
+ name: `AI Gateway Token - ${new Date().toISOString().split('T')[0]}`,
+ policies: [{
+ effect: 'allow',
+ resources: { [`com.cloudflare.api.account.${this.config.accountId}`]: '*' },
+ permission_groups: [
+ { name: 'AI Gateway Read' },
+ { name: 'AI Gateway Edit' },
+ { name: 'AI Gateway Run' },
+ { name: 'Workers AI Read' },
+ { name: 'Workers AI Edit' },
+ ],
+ }],
+ condition: { request_ip: { in: [], not_in: [] } },
+ expires_on: new Date(Date.now() + 365 * 24 * 60 * 60 * 1000).toISOString(),
+ }),
+ });
+
+ if (!tokenResponse.ok) {
+ const errorData = await tokenResponse.json().catch(() => ({ errors: [{ message: 'Unknown error' }] }));
+ throw new Error(`API token creation failed: ${errorData.errors?.[0]?.message || tokenResponse.statusText}`);
+ }
+
+ const tokenData = await tokenResponse.json();
+ if (tokenData.success && tokenData.result?.value) {
+ const newToken = tokenData.result.value;
+ console.log('โ
AI Gateway authentication token created successfully');
+ console.log(` Token ID: ${tokenData.result.id}`);
+ console.warn('โ ๏ธ Please save this token and add it to CLOUDFLARE_AI_GATEWAY_TOKEN:');
+ console.warn(` ${newToken}`);
+
+ // Initialize AI Gateway SDK with new token
+ this.aiGatewayCloudflare = new Cloudflare({ apiToken: newToken });
+ return newToken;
+ }
+
+ throw new Error('Token creation succeeded but no token returned');
+ } catch (error) {
+ console.warn(`โ ๏ธ Could not create AI Gateway token: ${error instanceof Error ? error.message : String(error)}`);
+ return null;
+ }
+ }
+
+ private getAIGatewaySDK(): Cloudflare {
+ return this.aiGatewayCloudflare || this.cloudflare;
+ }
+
+ private async detectZoneForDomain(customDomain: string): Promise<{ zoneName: string | null; zoneId: string | null }> {
+ console.log(`๐ Detecting zone for domain: ${customDomain}`);
+
+ // Extract possible zone names
+ const domainParts = customDomain.split('.');
+ const possibleZones: string[] = [];
+
+ for (let i = 0; i < domainParts.length - 1; i++) {
+ const zoneName = domainParts.slice(i).join('.');
+ possibleZones.push(zoneName);
+ }
+
+ // Test each possible zone
+ for (const zoneName of possibleZones) {
+ try {
+ const zones = await this.cloudflare.zones.list({
+ account: { id: this.config.accountId },
+ name: zoneName
+ });
+
+ if (zones.result && zones.result.length > 0) {
+ const zone = zones.result[0];
+ console.log(`โ
Found zone: ${zoneName} (ID: ${zone.id})`);
+ return { zoneName, zoneId: zone.id };
+ }
+ } catch (error) {
+ console.log(` Testing zone ${zoneName}: not found`);
+ }
+ }
+
+ console.warn(`โ ๏ธ No valid zone found for domain: ${customDomain}`);
+ return { zoneName: null, zoneId: null };
+ }
+
+ private parseWranglerConfig(): any {
+ const wranglerPath = join(PROJECT_ROOT, 'wrangler.jsonc');
+ if (!existsSync(wranglerPath)) {
+ throw new Error('wrangler.jsonc not found in project root');
+ }
+
+ const content = readFileSync(wranglerPath, 'utf-8');
+ return parse(content);
+ }
+
+ private static readonly FALLBACK_WORKER_VARS = new Set([
+ 'TEMPLATES_REPOSITORY', 'ALLOWED_EMAIL', 'DISPATCH_NAMESPACE', 'CLOUDFLARE_AI_GATEWAY', 'ENABLE_READ_REPLICAS',
+ 'ANTHROPIC_API_KEY', 'OPENAI_API_KEY', 'GOOGLE_AI_STUDIO_API_KEY', 'OPENROUTER_API_KEY', 'CEREBRAS_API_KEY', 'GROQ_API_KEY',
+ 'SANDBOX_SERVICE_API_KEY', 'SANDBOX_SERVICE_TYPE', 'SANDBOX_SERVICE_URL',
+ 'CLOUDFLARE_API_TOKEN', 'CLOUDFLARE_ACCOUNT_ID', 'CLOUDFLARE_AI_GATEWAY_URL', 'CLOUDFLARE_AI_GATEWAY_TOKEN',
+ 'SERPAPI_KEY', 'GOOGLE_CLIENT_SECRET', 'GOOGLE_CLIENT_ID', 'GITHUB_CLIENT_ID', 'GITHUB_CLIENT_SECRET',
+ 'JWT_SECRET', 'ENTROPY_KEY', 'ENVIRONMENT', 'SECRETS_ENCRYPTION_KEY',
+ 'MAX_SANDBOX_INSTANCES', 'SANDBOX_INSTANCE_TYPE', 'CUSTOM_DOMAIN', 'CUSTOM_PREVIEW_DOMAIN',
+ 'ALLOCATION_STRATEGY', 'GITHUB_EXPORTER_CLIENT_ID', 'GITHUB_EXPORTER_CLIENT_SECRET',
+ 'CF_ACCESS_ID', 'CF_ACCESS_SECRET', 'SENTRY_DSN'
+ ]);
+
+ private parseWorkerConfiguration(): Set {
+ const configPath = join(PROJECT_ROOT, 'worker-configuration.d.ts');
+
+ if (!existsSync(configPath)) {
+ console.warn('โ ๏ธ worker-configuration.d.ts not found, using fallback variable list');
+ return SetupManager.FALLBACK_WORKER_VARS;
+ }
+
+ try {
+ const content = readFileSync(configPath, 'utf-8');
+ const envInterfaceMatch = content.match(/interface Env \{([\s\S]*?)\}/);
+
+ if (envInterfaceMatch) {
+ const managedVars = new Set();
+ const lines = envInterfaceMatch[1].split('\n');
+
+ for (const line of lines) {
+ const match = line.trim().match(/^([A-Z_][A-Z0-9_]*)\s*:\s*(string|"[^"]*");?$/);
+ if (match) managedVars.add(match[1]);
+ }
+
+ return managedVars;
+ }
+
+ return SetupManager.FALLBACK_WORKER_VARS;
+ } catch (error) {
+ console.warn(`โ ๏ธ Could not parse worker-configuration.d.ts: ${error instanceof Error ? error.message : String(error)}`);
+ return SetupManager.FALLBACK_WORKER_VARS;
+ }
+ }
+
+ private async generateDevVarsFile(): Promise {
+ console.log('๐ Generating .dev.vars file...');
+
+ const devVarsPath = join(PROJECT_ROOT, '.dev.vars');
+
+ // Parse worker-configuration.d.ts to get all managed variables
+ const managedVars = this.parseWorkerConfiguration();
+
+ // Read existing .dev.vars file to preserve values
+ const existingVars = new Map();
+ if (existsSync(devVarsPath)) {
+ const existingContent = readFileSync(devVarsPath, 'utf-8');
+ existingContent.split('\n').forEach(line => {
+ const trimmed = line.trim();
+ if (trimmed && !trimmed.startsWith('#')) {
+ const [key, ...valueParts] = trimmed.split('=');
+ if (key && valueParts.length > 0) {
+ const value = valueParts.join('=').replace(/^"(.*)"$/, '$1');
+ existingVars.set(key.trim(), value);
+ }
+ }
+ });
+ }
+
+ // Variables that the setup script should manage (subset of all managed vars)
+ const setupManagedVars = new Set([
+ 'CUSTOM_DOMAIN', 'ENVIRONMENT', 'CLOUDFLARE_API_TOKEN', 'CLOUDFLARE_ACCOUNT_ID',
+ 'CLOUDFLARE_AI_GATEWAY_TOKEN', 'CLOUDFLARE_AI_GATEWAY_URL',
+ 'ANTHROPIC_API_KEY', 'OPENAI_API_KEY', 'GOOGLE_AI_STUDIO_API_KEY', 'OPENROUTER_API_KEY', 'GROQ_API_KEY',
+ 'GOOGLE_CLIENT_ID', 'GOOGLE_CLIENT_SECRET', 'GITHUB_CLIENT_ID', 'GITHUB_CLIENT_SECRET',
+ 'GITHUB_EXPORTER_CLIENT_ID', 'GITHUB_EXPORTER_CLIENT_SECRET',
+ 'JWT_SECRET', 'WEBHOOK_SECRET'
+ ]);
+
+ // Collect unmanaged variables to preserve (anything not in worker config or setup managed)
+ const preservedVars = new Map();
+ existingVars.forEach((value, key) => {
+ if (!managedVars.has(key) && !setupManagedVars.has(key)) {
+ preservedVars.set(key, value);
+ }
+ });
+
+ // For variables in worker config but not setup managed, preserve existing values
+ const workerConfigVarsToPreserve = new Map();
+ managedVars.forEach(varName => {
+ if (!setupManagedVars.has(varName) && existingVars.has(varName)) {
+ workerConfigVarsToPreserve.set(varName, existingVars.get(varName)!);
+ }
+ });
+
+ let content = '';
+
+ // Security Configuration
+ content += '# Security Configuration\n';
+ content += `CUSTOM_DOMAIN="${this.config.customDomain}"\n`;
+ content += 'ENVIRONMENT="dev"\n\n';
+
+ // Essential Secrets
+ content += '# Essential Secrets\n';
+ content += `CLOUDFLARE_API_TOKEN="${this.config.apiToken}"\n`;
+ content += `CLOUDFLARE_ACCOUNT_ID="${this.config.accountId}"\n\n`;
+
+ // AI Gateway Configuration
+ content += '# AI Gateway Configuration\n';
+ content += `CLOUDFLARE_AI_GATEWAY_TOKEN="${this.config.devVars?.CLOUDFLARE_AI_GATEWAY_TOKEN}"\n`;
+ if (this.config.aiGatewayUrl) {
+ content += `CLOUDFLARE_AI_GATEWAY_URL="${this.config.aiGatewayUrl}"\n`;
+ }
+ content += '\n';
+
+ // Provider specific secrets
+ content += '# Provider specific secrets\n';
+ const providerVars = ['ANTHROPIC_API_KEY', 'OPENAI_API_KEY', 'GOOGLE_AI_STUDIO_API_KEY', 'OPENROUTER_API_KEY', 'GROQ_API_KEY'];
+ for (const varName of providerVars) {
+ if (this.config.devVars[varName]) {
+ content += `${varName}="${this.config.devVars[varName]}"\n`;
+ } else {
+ content += `#${varName}=""\n`;
+ }
+ }
+ content += '\n';
+
+ // OAuth Configuration
+ content += '# OAuth Configuration\n';
+ const oauthVars = ['GOOGLE_CLIENT_ID', 'GOOGLE_CLIENT_SECRET', 'GITHUB_CLIENT_ID', 'GITHUB_CLIENT_SECRET'];
+ for (const varName of oauthVars) {
+ if (this.config.devVars[varName]) {
+ content += `${varName}="${this.config.devVars[varName]}"\n`;
+ } else {
+ content += `#${varName}=""\n`;
+ }
+ }
+
+ // GitHub Exporter Configuration (if configured)
+ if (this.config.devVars.GITHUB_EXPORTER_CLIENT_ID || this.config.devVars.GITHUB_EXPORTER_CLIENT_SECRET) {
+ content += '\n# GitHub Exporter OAuth Configuration\n';
+ content += `GITHUB_EXPORTER_CLIENT_ID="${this.config.devVars.GITHUB_EXPORTER_CLIENT_ID || ''}"\n`;
+ content += `GITHUB_EXPORTER_CLIENT_SECRET="${this.config.devVars.GITHUB_EXPORTER_CLIENT_SECRET || ''}"\n`;
+ }
+ content += '\n';
+
+ // Required secrets
+ content += '# Required secrets\n';
+ content += `JWT_SECRET="${this.config.devVars.JWT_SECRET}"\n`;
+ content += `WEBHOOK_SECRET="${this.config.devVars.WEBHOOK_SECRET}"\n`;
+
+ // Worker configuration variables (preserved from existing .dev.vars)
+ if (workerConfigVarsToPreserve.size > 0) {
+ content += '\n# Worker configuration variables (preserved)\n';
+ // Sort variables by name for consistent output
+ const sortedWorkerVars = Array.from(workerConfigVarsToPreserve.entries()).sort(([a], [b]) => a.localeCompare(b));
+ for (const [key, value] of sortedWorkerVars) {
+ content += `${key}="${value}"\n`;
+ }
+ }
+
+ // Additional worker config variables not yet set (as commented placeholders)
+ const unsetWorkerVars = Array.from(managedVars).filter(varName =>
+ !setupManagedVars.has(varName) && !workerConfigVarsToPreserve.has(varName)
+ ).sort();
+
+ if (unsetWorkerVars.length > 0) {
+ content += '\n# Additional worker configuration variables (uncomment and set as needed)\n';
+ for (const varName of unsetWorkerVars) {
+ content += `#${varName}=""\n`;
+ }
+ }
+
+ // Preserved variables (not in worker config at all)
+ if (preservedVars.size > 0) {
+ content += '\n# Additional variables (preserved from existing .dev.vars)\n';
+ const sortedPreserved = Array.from(preservedVars.entries()).sort(([a], [b]) => a.localeCompare(b));
+ for (const [key, value] of sortedPreserved) {
+ content += `${key}="${value}"\n`;
+ }
+ }
+
+ writeFileSync(devVarsPath, content, 'utf-8');
+
+ const totalPreserved = preservedVars.size + workerConfigVarsToPreserve.size;
+ if (totalPreserved > 0) {
+ console.log(`โ
.dev.vars file updated (preserved ${totalPreserved} existing variables)`);
+ if (workerConfigVarsToPreserve.size > 0) {
+ console.log(` โข ${workerConfigVarsToPreserve.size} worker configuration variables preserved`);
+ }
+ if (preservedVars.size > 0) {
+ console.log(` โข ${preservedVars.size} custom variables preserved`);
+ }
+ } else {
+ console.log('โ
.dev.vars file created successfully');
+ }
+ }
+
+ private async generateProdVarsFile(): Promise {
+ if (!this.config.setupRemote || !this.config.prodVars || !this.config.prodDomain) return;
+
+ console.log('๐ Generating .prod.vars file...');
+
+ const prodVarsPath = join(PROJECT_ROOT, '.prod.vars');
+ const managedVars = this.parseWorkerConfiguration();
+
+ const setupManagedVars = new Set([
+ 'CUSTOM_DOMAIN', 'ENVIRONMENT', 'CLOUDFLARE_API_TOKEN', 'CLOUDFLARE_ACCOUNT_ID',
+ 'CLOUDFLARE_AI_GATEWAY_TOKEN', 'CLOUDFLARE_AI_GATEWAY_URL',
+ 'ANTHROPIC_API_KEY', 'OPENAI_API_KEY', 'GOOGLE_AI_STUDIO_API_KEY', 'OPENROUTER_API_KEY', 'GROQ_API_KEY',
+ 'GOOGLE_CLIENT_ID', 'GOOGLE_CLIENT_SECRET', 'GITHUB_CLIENT_ID', 'GITHUB_CLIENT_SECRET',
+ 'GITHUB_EXPORTER_CLIENT_ID', 'GITHUB_EXPORTER_CLIENT_SECRET',
+ 'JWT_SECRET', 'WEBHOOK_SECRET'
+ ]);
+
+ let content = '';
+
+ // Production Configuration
+ content += '# Production Configuration\n';
+ content += `CUSTOM_DOMAIN="${this.config.prodDomain}"\n`;
+ content += 'ENVIRONMENT="prod"\n\n';
+
+ // Essential Secrets
+ content += '# Essential Secrets\n';
+ content += `CLOUDFLARE_API_TOKEN="${this.config.apiToken}"\n`;
+ content += `CLOUDFLARE_ACCOUNT_ID="${this.config.accountId}"\n\n`;
+
+ // AI Gateway Configuration
+ content += '# AI Gateway Configuration\n';
+ content += `CLOUDFLARE_AI_GATEWAY_TOKEN="${this.config.prodVars?.CLOUDFLARE_AI_GATEWAY_TOKEN}"\n`;
+ if (this.config.aiGatewayUrl) {
+ content += `CLOUDFLARE_AI_GATEWAY_URL="${this.config.aiGatewayUrl}"\n`;
+ }
+ content += '\n';
+
+ // Provider specific secrets
+ content += '# Provider specific secrets\n';
+ const providerVars = ['ANTHROPIC_API_KEY', 'OPENAI_API_KEY', 'GOOGLE_AI_STUDIO_API_KEY', 'OPENROUTER_API_KEY', 'GROQ_API_KEY'];
+ for (const varName of providerVars) {
+ if (this.config.prodVars[varName]) {
+ content += `${varName}="${this.config.prodVars[varName]}"\n`;
+ } else {
+ content += `#${varName}=""\n`;
+ }
+ }
+ content += '\n';
+
+ // OAuth Configuration
+ content += '# OAuth Configuration\n';
+ const oauthVars = ['GOOGLE_CLIENT_ID', 'GOOGLE_CLIENT_SECRET', 'GITHUB_CLIENT_ID', 'GITHUB_CLIENT_SECRET'];
+ for (const varName of oauthVars) {
+ if (this.config.prodVars[varName]) {
+ content += `${varName}="${this.config.prodVars[varName]}"\n`;
+ } else {
+ content += `#${varName}=""\n`;
+ }
+ }
+
+ // GitHub Exporter Configuration (if configured)
+ if (this.config.prodVars.GITHUB_EXPORTER_CLIENT_ID || this.config.prodVars.GITHUB_EXPORTER_CLIENT_SECRET) {
+ content += '\n# GitHub Exporter OAuth Configuration\n';
+ content += `GITHUB_EXPORTER_CLIENT_ID="${this.config.prodVars.GITHUB_EXPORTER_CLIENT_ID || ''}"\n`;
+ content += `GITHUB_EXPORTER_CLIENT_SECRET="${this.config.prodVars.GITHUB_EXPORTER_CLIENT_SECRET || ''}"\n`;
+ }
+ content += '\n';
+
+ // Required secrets
+ content += '# Required secrets\n';
+ content += `JWT_SECRET="${this.config.prodVars.JWT_SECRET}"\n`;
+ content += `WEBHOOK_SECRET="${this.config.prodVars.WEBHOOK_SECRET}"\n`;
+
+ writeFileSync(prodVarsPath, content, 'utf-8');
+ console.log('โ
.prod.vars file created successfully for production deployment');
+ }
+
+ private async updateWranglerConfig(resources: ResourceInfo): Promise {
+ console.log('๐ง Updating wrangler.jsonc configuration...');
+
+ const wranglerPath = join(PROJECT_ROOT, 'wrangler.jsonc');
+ const content = readFileSync(wranglerPath, 'utf-8');
+ let updatedContent = content;
+
+ // Update KV namespace IDs and remote flags
+ for (const kv of resources.kvNamespaces) {
+ const kvPath = ['kv_namespaces'];
+ const kvNamespaces = parse(content).kv_namespaces || [];
+ const updatedKvNamespaces = kvNamespaces.map((ns: any) => {
+ if (ns.binding === kv.binding) {
+ return {
+ ...ns,
+ id: kv.id,
+ remote: kv.accessible // Set remote based on accessibility
+ };
+ }
+ return ns;
+ });
+
+ const edits = modify(updatedContent, kvPath, updatedKvNamespaces, {
+ formattingOptions: { insertSpaces: true, tabSize: 4 }
+ });
+ updatedContent = applyEdits(updatedContent, edits);
+ }
+
+ // Update D1 database IDs and remote flags
+ for (const db of resources.d1Databases) {
+ const dbPath = ['d1_databases'];
+ const databases = parse(updatedContent).d1_databases || [];
+ const updatedDatabases = databases.map((database: any) => {
+ if (database.binding === db.binding) {
+ return {
+ ...database,
+ database_id: db.id,
+ remote: db.accessible // Set remote based on accessibility
+ };
+ }
+ return database;
+ });
+
+ const edits = modify(updatedContent, dbPath, updatedDatabases, {
+ formattingOptions: { insertSpaces: true, tabSize: 4 }
+ });
+ updatedContent = applyEdits(updatedContent, edits);
+ }
+
+ // Update R2 bucket remote flags
+ const wranglerConfig = parse(updatedContent);
+ if (wranglerConfig.r2_buckets && resources.r2Buckets.length > 0) {
+ const r2Path = ['r2_buckets'];
+ const r2Buckets = wranglerConfig.r2_buckets || [];
+ const updatedR2Buckets = r2Buckets.map((bucket: any) => {
+ const matchingResource = resources.r2Buckets.find(r => r.binding === bucket.binding);
+ if (matchingResource) {
+ return {
+ ...bucket,
+ remote: matchingResource.accessible // Set remote based on accessibility
+ };
+ }
+ return bucket;
+ });
+
+ const r2Edits = modify(updatedContent, r2Path, updatedR2Buckets, {
+ formattingOptions: { insertSpaces: true, tabSize: 4 }
+ });
+ updatedContent = applyEdits(updatedContent, r2Edits);
+ }
+
+ // Update dispatch namespace remote flags
+ if (wranglerConfig.dispatch_namespaces && resources.dispatchNamespaces.length > 0) {
+ const dispatchPath = ['dispatch_namespaces'];
+ const dispatchNamespaces = wranglerConfig.dispatch_namespaces || [];
+ const updatedDispatchNamespaces = dispatchNamespaces.map((dispatch: any) => {
+ const matchingResource = resources.dispatchNamespaces.find(r => r.binding === dispatch.binding);
+ if (matchingResource) {
+ return {
+ ...dispatch,
+ remote: matchingResource.accessible // Set remote based on accessibility
+ };
+ }
+ return dispatch;
+ });
+
+ const dispatchEdits = modify(updatedContent, dispatchPath, updatedDispatchNamespaces, {
+ formattingOptions: { insertSpaces: true, tabSize: 4 }
+ });
+ updatedContent = applyEdits(updatedContent, dispatchEdits);
+ }
+
+ // Determine which domain to use in wrangler.jsonc
+ // Priority: Production domain > Custom local domain (if not localhost) > Don't set at all
+ const wranglerDomain = this.config.setupRemote && this.config.prodDomain
+ ? this.config.prodDomain
+ : (this.config.customDomain !== 'localhost:5173' ? this.config.customDomain : null);
+
+ if (wranglerDomain) {
+ // Update CUSTOM_DOMAIN in vars with production or custom domain
+ const varsEdits = modify(updatedContent, ['vars', 'CUSTOM_DOMAIN'], wranglerDomain, {
+ formattingOptions: { insertSpaces: true, tabSize: 4 }
+ });
+ updatedContent = applyEdits(updatedContent, varsEdits);
+
+ // Add routes for the domain
+ const zone = resources.zones[0];
+ const routes = [
+ {
+ pattern: wranglerDomain,
+ custom_domain: true
+ },
+ {
+ pattern: `*${wranglerDomain}/*`,
+ custom_domain: false,
+ ...(zone ? { zone_id: zone.id } : {})
+ }
+ ];
+
+ const routesEdits = modify(updatedContent, ['routes'], routes, {
+ formattingOptions: { insertSpaces: true, tabSize: 4 }
+ });
+ updatedContent = applyEdits(updatedContent, routesEdits);
+
+ // Set workers_dev = false and preview_urls = false for custom domain
+ const workersDevEdits = modify(updatedContent, ['workers_dev'], false, {
+ formattingOptions: { insertSpaces: true, tabSize: 4 }
+ });
+ updatedContent = applyEdits(updatedContent, workersDevEdits);
+
+ const previewUrlsEdits = modify(updatedContent, ['preview_urls'], false, {
+ formattingOptions: { insertSpaces: true, tabSize: 4 }
+ });
+ updatedContent = applyEdits(updatedContent, previewUrlsEdits);
+
+ console.log(`โ
Updated routes for domain: ${wranglerDomain}`);
+ if (zone) {
+ console.log(` โข Main domain: ${wranglerDomain} (custom_domain: true)`);
+ console.log(` โข Wildcard pattern: *${wranglerDomain}/* (zone_id: ${zone.id})`);
+ } else {
+ console.log(` โข Main domain: ${wranglerDomain} (custom_domain: true)`);
+ console.log(` โข Wildcard pattern: *${wranglerDomain}/* (no zone detected)`);
+ }
+
+ if (this.config.setupRemote && this.config.prodDomain) {
+ console.log(` โข Production domain configured for deployment`);
+ }
+ } else {
+ // For localhost-only development, remove routes and enable workers.dev
+ const routesEdits = modify(updatedContent, ['routes'], undefined, {
+ formattingOptions: { insertSpaces: true, tabSize: 4 }
+ });
+ updatedContent = applyEdits(updatedContent, routesEdits);
+
+ // Don't set CUSTOM_DOMAIN for localhost (keep it unset or remove it)
+ const varsEdits = modify(updatedContent, ['vars', 'CUSTOM_DOMAIN'], 'localhost:5173', {
+ formattingOptions: { insertSpaces: true, tabSize: 4 }
+ });
+ updatedContent = applyEdits(updatedContent, varsEdits);
+
+ // Set workers_dev = true and preview_urls = true for localhost development
+ const workersDevEdits = modify(updatedContent, ['workers_dev'], true, {
+ formattingOptions: { insertSpaces: true, tabSize: 4 }
+ });
+ updatedContent = applyEdits(updatedContent, workersDevEdits);
+
+ const previewUrlsEdits = modify(updatedContent, ['preview_urls'], true, {
+ formattingOptions: { insertSpaces: true, tabSize: 4 }
+ });
+ updatedContent = applyEdits(updatedContent, previewUrlsEdits);
+
+ console.log('โ
Configured for localhost development (workers.dev deployment)');
+ }
+
+ writeFileSync(wranglerPath, updatedContent, 'utf-8');
+ console.log('โ
wrangler.jsonc updated successfully');
+ }
+
+ private async updateViteConfig(): Promise {
+ console.log('๐ง Updating vite.config.ts configuration...');
+
+ const viteConfigPath = join(PROJECT_ROOT, 'vite.config.ts');
+ if (!existsSync(viteConfigPath)) {
+ console.warn('โ ๏ธ vite.config.ts not found - skipping vite configuration update');
+ return;
+ }
+
+ let content = readFileSync(viteConfigPath, 'utf-8');
+
+ // Update the remoteBindings setting based on user preference
+ const remoteBindingsValue = this.config.useRemoteBindings ? 'true' : 'false';
+
+ // Look for the experimental.remoteBindings setting and update it
+ const remoteBindingsRegex = /experimental:\s*{\s*remoteBindings:\s*(true|false)\s*}/;
+
+ if (remoteBindingsRegex.test(content)) {
+ content = content.replace(
+ remoteBindingsRegex,
+ `experimental: { remoteBindings: ${remoteBindingsValue} }`
+ );
+ console.log(`โ
Updated vite.config.ts remoteBindings to: ${remoteBindingsValue}`);
+ } else {
+ console.warn('โ ๏ธ Could not find remoteBindings setting in vite.config.ts');
+ console.warn(` Please manually set: experimental: { remoteBindings: ${remoteBindingsValue} }`);
+ }
+
+ writeFileSync(viteConfigPath, content, 'utf-8');
+ console.log('โ
vite.config.ts updated successfully');
+ }
+
+ private async generateReadinessReport(resources: ResourceInfo): Promise {
+ console.log('๐ Generating readiness report...');
+
+ const issues: string[] = [];
+ const suggestions: string[] = [];
+ const resourcesCreated: string[] = [];
+
+ // Check for created resources
+ resources.kvNamespaces.forEach(kv => {
+ const status = kv.accessible ? 'โ
' : '๐ (local-only)';
+ resourcesCreated.push(`KV Namespace: ${kv.name} (${kv.binding}) ${status}`);
+ });
+
+ resources.d1Databases.forEach(db => {
+ const status = db.accessible ? 'โ
' : '๐ (local-only)';
+ resourcesCreated.push(`D1 Database: ${db.name} (${db.binding}) ${status}`);
+ });
+
+ resources.r2Buckets.forEach(bucket => {
+ const status = bucket.accessible ? 'โ
' : '๐ (local-only)';
+ resourcesCreated.push(`R2 Bucket: ${bucket.name} (${bucket.binding}) ${status}`);
+ });
+
+ resources.dispatchNamespaces.forEach(dispatch => {
+ const status = dispatch.accessible ? 'โ
' : '๐ (local-only)';
+ resourcesCreated.push(`Dispatch Namespace: ${dispatch.name} (${dispatch.binding}) ${status}`);
+ });
+
+ if (resources.aiGateway) {
+ const gateway = resources.aiGateway;
+ const status = gateway.exists ? 'โ
' : 'โ';
+ resourcesCreated.push(`AI Gateway: ${gateway.name} ${status}`);
+
+ if (gateway.tokenError) {
+ issues.push(`AI Gateway token issue: ${gateway.tokenError}`);
+ }
+ if (!gateway.tokenCreated && gateway.exists) {
+ suggestions.push('Consider configuring AI Gateway authentication token for enhanced security');
+ }
+ }
+
+ // Check for potential issues
+ if (!this.config.customDomain || this.config.customDomain === 'localhost:5173') {
+ suggestions.push('Consider setting up a custom domain for production deployment');
+ }
+
+ if (resources.zones.length === 0 && this.config.customDomain && this.config.customDomain !== 'localhost:5173') {
+ issues.push('Custom domain zone not found - domain routing may not work');
+ suggestions.push('Ensure your domain is managed by Cloudflare and API token has zone permissions');
+ }
+
+ // Check account features (simplified check)
+ let accountInfo;
+ try {
+ const account = await this.cloudflare.accounts.get({ account_id: this.config.accountId });
+ accountInfo = {
+ plan: 'Free', // This would need to be determined from actual account data
+ features: ['Workers', 'KV', 'D1', 'R2'] // Simplified feature list
+ };
+ } catch (error) {
+ issues.push('Could not retrieve account information');
+ }
+
+ const localDevReady = issues.length === 0;
+ const deploymentReady = localDevReady && this.config.customDomain !== 'localhost:5173';
+
+ return {
+ localDevReady,
+ deploymentReady,
+ issues,
+ suggestions,
+ resourcesCreated,
+ accountInfo
+ };
+ }
+
+ private displayFinalReport(report: ReadinessReport, resources: ResourceInfo): void {
+ console.log('\n๐ฏ Setup Complete - Readiness Report');
+ console.log('=====================================\n');
+
+ // Local Development Status
+ console.log(`๐ Local Development: ${report.localDevReady ? 'โ
READY' : 'โ ISSUES FOUND'}`);
+ console.log(`๐ Remote Deployment: ${report.deploymentReady ? 'โ
READY' : 'โ ๏ธ PARTIAL'}`);
+ console.log('');
+
+ // Resources Created
+ if (report.resourcesCreated.length > 0) {
+ console.log('๐ฆ Resources Created/Validated:');
+ report.resourcesCreated.forEach(resource => {
+ console.log(` โ
${resource}`);
+ });
+ console.log('');
+ }
+
+ // Issues
+ if (report.issues.length > 0) {
+ console.log('โ Issues Found:');
+ report.issues.forEach(issue => {
+ console.log(` โข ${issue}`);
+ });
+ console.log('');
+ }
+
+ // Suggestions
+ if (report.suggestions.length > 0) {
+ console.log('๐ก Suggestions:');
+ report.suggestions.forEach(suggestion => {
+ console.log(` โข ${suggestion}`);
+ });
+ console.log('');
+ }
+
+ // Next Steps
+ console.log('๐ฏ Next Steps:');
+ console.log(` 1. Run \`${this.packageManager} run dev\` to start local development`);
+ console.log(' 2. Visit your app at http://localhost:5173');
+ console.log(' 3. Database and templates are ready to use!');
+
+ if (this.config.setupRemote && this.config.prodDomain) {
+ console.log(` 4. For production deployment to ${this.config.prodDomain}, run \`npm run deploy\``);
+ console.log(' 5. .prod.vars file is ready for production environment variables');
+ } else if (this.config.customDomain && this.config.customDomain !== 'localhost:5173') {
+ console.log(' 4. For production deployment, run `npm run deploy`');
+ }
+
+ if (!this.config.useRemoteBindings) {
+ console.log('\n๐ Local-Only Mode:');
+ console.log(' โข All Cloudflare resources configured for local development');
+ console.log(' โข Perfect for free tier users and local testing');
+ console.log(' โข To enable remote bindings later, re-run the setup script');
+ }
+
+ // Additional setup information
+ const hasGoogleAI = Object.keys(this.config.devVars).includes('GOOGLE_AI_STUDIO_API_KEY');
+ const hasOAuth = ['GOOGLE_CLIENT_ID', 'GITHUB_CLIENT_ID', 'GITHUB_EXPORTER_CLIENT_ID'].some(key =>
+ Object.keys(this.config.devVars).includes(key)
+ );
+ const hasRemoteR2 = resources.r2Buckets.some(bucket => bucket.accessible);
+ const isARM64 = process.arch === 'arm64';
+
+ if (!hasGoogleAI || hasOAuth || !hasRemoteR2 || isARM64) {
+ console.log('\n๐ก Setup Information:');
+
+ if (!hasGoogleAI) {
+ console.log(' โข Edit worker/agents/inferutils/config.ts to configure AI models');
+ console.log(' โข Update fallback models from Gemini to your available providers');
+ }
+
+ if (hasOAuth) {
+ console.log(' โข OAuth credentials configured - users can now log in');
+ if (Object.keys(this.config.devVars).includes('GITHUB_EXPORTER_CLIENT_ID')) {
+ console.log(' โข GitHub Export OAuth configured - users can export apps to GitHub');
+ }
+ }
+
+ if (!hasRemoteR2) {
+ console.log(' โข Templates deployed to local R2 for development');
+ console.log(' โข For production, ensure remote R2 access is available');
+ } else {
+ console.log(' โข Templates deployed to both local and remote R2');
+ console.log(' โข Ready for both local development and production');
+ }
+
+ if (isARM64) {
+ console.log(' โข SandboxDockerfile patched for ARM64 local development');
+ console.log(' โข ARM64 flags will be automatically removed during deployment');
+ }
+ }
+
+ console.log('\nโจ Happy coding with VibSDK! โจ');
+ }
+
+ private async updateWorkerConfiguration(): Promise {
+ const workerConfigPath = join(PROJECT_ROOT, 'worker-configuration.d.ts');
+
+ if (!existsSync(workerConfigPath) || !this.config.customProviderKeys?.length) {
+ return;
+ }
+
+ console.log('๐ Updating worker configuration for custom providers...');
+
+ try {
+ let content = readFileSync(workerConfigPath, 'utf-8');
+
+ // Find the Env interface
+ const envInterfaceMatch = content.match(/interface Env \{([\s\S]*?)\}/);
+ if (!envInterfaceMatch) {
+ console.warn('โ ๏ธ Could not find Env interface in worker-configuration.d.ts');
+ return;
+ }
+
+ // Check which custom keys need to be added
+ const keysToAdd: string[] = [];
+ for (const customProvider of this.config.customProviderKeys) {
+ if (!content.includes(`${customProvider.key}: string;`)) {
+ keysToAdd.push(customProvider.key);
+ }
+ }
+
+ if (keysToAdd.length === 0) {
+ console.log('โ
Worker configuration already up to date');
+ return;
+ }
+
+ // Add missing keys to the Env interface
+ const envContent = envInterfaceMatch[1];
+ const lastApiKeyMatch = envContent.match(/.*_API_KEY: string;/g);
+
+ if (lastApiKeyMatch) {
+ const lastApiKeyLine = lastApiKeyMatch[lastApiKeyMatch.length - 1];
+ const insertPoint = content.indexOf(lastApiKeyLine) + lastApiKeyLine.length;
+
+ const newKeys = keysToAdd.map(key => `\n\t\t${key}: string;`).join('');
+ content = content.slice(0, insertPoint) + newKeys + content.slice(insertPoint);
+ }
+
+ // Also update the NodeJS.ProcessEnv extends part
+ const processEnvMatch = content.match(/interface ProcessEnv extends StringifyValues>/);
+ if (processEnvMatch) {
+ const existingKeys = processEnvMatch[1];
+ const missingKeys = keysToAdd.filter(key => !existingKeys.includes(key));
+
+ if (missingKeys.length > 0) {
+ const updatedKeys = existingKeys + ' | "' + missingKeys.join('" | "') + '"';
+ content = content.replace(processEnvMatch[0], processEnvMatch[0].replace(existingKeys, updatedKeys));
+ }
+ }
+
+ writeFileSync(workerConfigPath, content, 'utf-8');
+ console.log(`โ
Added ${keysToAdd.length} custom provider key(s) to worker configuration`);
+
+ } catch (error) {
+ console.warn(`โ ๏ธ Could not update worker configuration: ${error instanceof Error ? error.message : String(error)}`);
+ }
+ }
+
+ private async patchDockerfileForARM64(): Promise {
+ // Check if we're running on ARM64 architecture
+ const arch = process.arch;
+ const platform = process.platform;
+
+ if (arch !== 'arm64') {
+ console.log('โน๏ธ Non-ARM64 platform detected - no Dockerfile patching needed');
+ return;
+ }
+
+ console.log('\n๐ง ARM64 Platform Configuration');
+ console.log('-------------------------------\n');
+ console.log(`๐๏ธ ARM64 ${platform} detected - patching SandboxDockerfile for local development`);
+
+ const dockerfilePath = join(PROJECT_ROOT, 'SandboxDockerfile');
+
+ if (!existsSync(dockerfilePath)) {
+ console.warn('โ ๏ธ SandboxDockerfile not found - skipping ARM64 patching');
+ return;
+ }
+
+ try {
+ let content = readFileSync(dockerfilePath, 'utf-8');
+ let modified = false;
+
+ // Split content into lines for processing
+ const lines = content.split('\n');
+ const updatedLines = lines.map(line => {
+ // Look for FROM statements that don't already have --platform
+ const fromMatch = line.match(/^(\s*FROM\s+)(?!.*--platform=)(.*)/);
+ if (fromMatch) {
+ modified = true;
+ const [, prefix, image] = fromMatch;
+ return `${prefix}--platform=linux/arm64 ${image}`;
+ }
+ return line;
+ });
+
+ if (modified) {
+ writeFileSync(dockerfilePath, updatedLines.join('\n'), 'utf-8');
+ console.log('โ
SandboxDockerfile patched with ARM64 platform flags');
+
+ console.log('\nโ ๏ธ IMPORTANT ARM64 NOTICE:');
+ console.log(' โข SandboxDockerfile has been modified for local ARM64 development');
+ console.log(' โข The --platform=linux/arm64 flags are for local development only');
+ console.log(' โข These flags will be automatically removed during deployment');
+ console.log(' โข Do NOT commit these changes to production repositories');
+
+ } else {
+ console.log('โ
SandboxDockerfile already contains ARM64 platform flags');
+ }
+
+ } catch (error) {
+ console.error('โ Failed to patch SandboxDockerfile:', error instanceof Error ? error.message : String(error));
+ console.error(' You may need to manually add --platform=linux/arm64 to FROM statements');
+ }
+ }
+
+ private async deployTemplates(resources: ResourceInfo): Promise {
+ console.log('\n๐ฆ Templates Deployment');
+ console.log('------------------------\n');
+
+ // Check if we have R2 bucket configured
+ const wranglerConfig = this.parseWranglerConfig();
+ const templatesBucket = wranglerConfig.r2_buckets?.find(
+ (bucket: any) => bucket.binding === 'TEMPLATES_BUCKET'
+ );
+
+ if (!templatesBucket) {
+ console.log('โน๏ธ No TEMPLATES_BUCKET found in wrangler.jsonc - skipping templates deployment');
+ return;
+ }
+
+ // Check if templates repository is configured
+ const templatesRepo = wranglerConfig.vars?.TEMPLATES_REPOSITORY;
+ if (!templatesRepo) {
+ console.log('โน๏ธ No TEMPLATES_REPOSITORY configured - skipping templates deployment');
+ return;
+ }
+
+ const templatesDir = join(PROJECT_ROOT, 'templates');
+ const hasRemoteR2 = resources.r2Buckets.some(bucket => bucket.accessible);
+
+ try {
+ console.log(`๐ฅ Setting up templates from: ${templatesRepo}`);
+
+ // Create templates directory if it doesn't exist
+ if (!existsSync(templatesDir)) {
+ execSync(`mkdir -p "${templatesDir}"`, { cwd: PROJECT_ROOT });
+ }
+
+ // Clone repository if not already present
+ if (!existsSync(join(templatesDir, '.git'))) {
+ console.log(`๐ Cloning templates repository...`);
+ execSync(`git clone "${templatesRepo}" "${templatesDir}"`, {
+ stdio: 'pipe',
+ cwd: PROJECT_ROOT,
+ });
+ console.log('โ
Templates repository cloned successfully');
+ } else {
+ console.log('๐ Templates repository already exists, pulling latest changes...');
+ try {
+ execSync('git pull origin main || git pull origin master', {
+ stdio: 'pipe',
+ cwd: templatesDir,
+ });
+ console.log('โ
Templates repository updated');
+ } catch (pullError) {
+ console.warn('โ ๏ธ Could not pull latest changes, continuing with existing templates');
+ }
+ }
+
+ // Check if deploy script exists
+ const deployScript = join(templatesDir, 'deploy_templates.sh');
+ if (!existsSync(deployScript)) {
+ console.warn('โ ๏ธ deploy_templates.sh not found in templates repository');
+ console.warn(' Skipping template deployment - templates may need to be deployed manually');
+ return;
+ }
+
+ // Make script executable
+ execSync(`chmod +x "${deployScript}"`, { cwd: templatesDir });
+
+ // Deploy to local R2 first (always available)
+ console.log(`๐ Deploying templates to local R2 bucket: ${templatesBucket.bucket_name}`);
+
+ const localDeployEnv = {
+ ...process.env,
+ CLOUDFLARE_API_TOKEN: this.config.apiToken,
+ CLOUDFLARE_ACCOUNT_ID: this.config.accountId,
+ BUCKET_NAME: templatesBucket.bucket_name,
+ R2_BUCKET_NAME: templatesBucket.bucket_name,
+ LOCAL_R2: 'true',
+ };
+
+ execSync('./deploy_templates.sh', {
+ stdio: 'inherit',
+ cwd: templatesDir,
+ env: localDeployEnv,
+ });
+
+ console.log('โ
Templates deployed successfully to local R2');
+
+ // Deploy to remote R2 if available
+ if (hasRemoteR2) {
+ console.log(`๐ Deploying templates to remote R2 bucket: ${templatesBucket.bucket_name}`);
+
+ const remoteDeployEnv = {
+ ...process.env,
+ CLOUDFLARE_API_TOKEN: this.config.apiToken,
+ CLOUDFLARE_ACCOUNT_ID: this.config.accountId,
+ BUCKET_NAME: templatesBucket.bucket_name,
+ R2_BUCKET_NAME: templatesBucket.bucket_name,
+ LOCAL_R2: 'false',
+ };
+
+ try {
+ execSync('./deploy_templates.sh', {
+ stdio: 'inherit',
+ cwd: templatesDir,
+ env: remoteDeployEnv,
+ });
+
+ console.log('โ
Templates deployed successfully to remote R2');
+ console.log('๐ฏ Templates ready for both local development and production!');
+ } catch (remoteError) {
+ console.warn('โ ๏ธ Remote R2 deployment failed:', remoteError instanceof Error ? remoteError.message : String(remoteError));
+ console.warn(' Local R2 deployment successful - development can continue');
+ console.warn(' Remote deployment can be done manually later or on next setup run');
+ }
+ } else {
+ console.log('๐ Note: Templates deployed to local R2 for development');
+ console.log(' For production deployment, ensure remote R2 access is available');
+ }
+
+ } catch (error) {
+ // Don't fail the entire setup if templates fail
+ console.error('โ Templates deployment failed:', error instanceof Error ? error.message : String(error));
+ console.error('๐ก Troubleshooting:');
+ console.error(' 1. Check if git is installed and accessible');
+ console.error(' 2. Verify templates repository URL is correct');
+ console.error(' 3. Ensure deploy_templates.sh script exists in templates repo');
+ console.error(' 4. Check R2 bucket access permissions');
+ console.error('\nโ ๏ธ Continuing setup - templates can be deployed manually later');
+
+ if (hasRemoteR2) {
+ console.error(` Manual command: cd templates && ./deploy_templates.sh`);
+ } else {
+ console.error(` Manual command: cd templates && LOCAL_R2=true ./deploy_templates.sh`);
+ }
+ }
+ }
+
+ private async setupDatabase(resources: ResourceInfo): Promise {
+ console.log('\n๐๏ธ Database Setup');
+ console.log('------------------\n');
+
+ const hasRemoteD1 = resources.d1Databases.some(db => db.accessible);
+
+ try {
+ console.log('๐ Generating database schema...');
+ execSync(`${this.packageManager} run db:generate`, {
+ stdio: 'inherit',
+ cwd: PROJECT_ROOT
+ });
+ console.log('โ
Database schema generated successfully');
+
+ console.log('\n๐ Running local database migrations...');
+ execSync(`${this.packageManager} run db:migrate:local`, {
+ stdio: 'inherit',
+ cwd: PROJECT_ROOT
+ });
+ console.log('โ
Local database migrations completed successfully');
+
+ // Run remote migrations if D1 is accessible
+ if (hasRemoteD1) {
+ console.log('\n๐ Running remote database migrations...');
+ try {
+ execSync(`${this.packageManager} run db:migrate:remote`, {
+ stdio: 'inherit',
+ cwd: PROJECT_ROOT
+ });
+ console.log('โ
Remote database migrations completed successfully');
+ } catch (remoteError) {
+ console.error('โ ๏ธ Remote database migration failed:', remoteError instanceof Error ? remoteError.message : String(remoteError));
+ console.error(' Local database is ready, but remote database may need manual migration');
+ console.error(` Run manually: ${this.packageManager} run db:migrate:remote`);
+ }
+ } else {
+ console.log('\n๐ Remote D1 not accessible - skipping remote migrations');
+ console.log(' Local database setup complete for development');
+ }
+
+ console.log('\n๐ฏ Database setup complete!');
+ if (hasRemoteD1) {
+ console.log(' โ
Both local and remote databases are ready');
+ } else {
+ console.log(' โ
Local database is ready for development');
+ }
+ } catch (error) {
+ console.error('\nโ Database setup failed:', error instanceof Error ? error.message : String(error));
+ console.error('๐ก You can run these commands manually:');
+ console.error(` ${this.packageManager} run db:generate`);
+ console.error(` ${this.packageManager} run db:migrate:local`);
+ if (hasRemoteD1) {
+ console.error(` ${this.packageManager} run db:migrate:remote`);
+ }
+ console.error('\nโ ๏ธ Continuing setup - you can run database commands manually later');
+ }
+ }
+}
+
+// Main execution
+async function main() {
+ const setup = new SetupManager();
+ await setup.setup();
+}
+
+if (import.meta.url === `file://${process.argv[1]}`) {
+ main().catch((error) => {
+ console.error('Setup failed:', error);
+ process.exit(1);
+ });
+}
diff --git a/external/drpower-vibe-production/scripts/undeploy.ts b/external/drpower-vibe-production/scripts/undeploy.ts
new file mode 100644
index 00000000..d4caf7db
--- /dev/null
+++ b/external/drpower-vibe-production/scripts/undeploy.ts
@@ -0,0 +1,624 @@
+#!/usr/bin/env node
+
+/**
+ * Cloudflare Orange Build - Automated Undeployment Script
+ *
+ * This script safely removes all Cloudflare resources associated with
+ * the Orange Build platform, including:
+ * - Worker
+ * - Containers
+ * - KV namespaces
+ * - R2 buckets
+ * - Container images
+ * - D1 database (optional, with --force flag)
+ * - Dispatch namespace (optional, with --force flag)
+ *
+ * Usage:
+ * bun scripts/undeploy.ts # Standard cleanup (preserves D1 + dispatch namespace)
+ * bun scripts/undeploy.ts all --force # Complete cleanup (destroys everything)
+ */
+
+import { execSync, spawnSync } from 'child_process';
+import { existsSync, readFileSync } from 'fs';
+import { join, dirname } from 'path';
+import { fileURLToPath } from 'url';
+import { parse } from 'jsonc-parser';
+
+// Get current directory for ES modules
+const __filename = fileURLToPath(import.meta.url);
+const __dirname = dirname(__filename);
+const PROJECT_ROOT = join(__dirname, '..');
+
+// Types for configuration
+interface WranglerConfig {
+ name: string;
+ dispatch_namespaces?: Array<{
+ binding: string;
+ namespace: string;
+ experimental_remote?: boolean;
+ }>;
+ r2_buckets?: Array<{
+ binding: string;
+ bucket_name: string;
+ experimental_remote?: boolean;
+ }>;
+ containers?: Array<{
+ class_name: string;
+ image: string;
+ max_instances: number;
+ }>;
+ d1_databases?: Array<{
+ binding: string;
+ database_name: string;
+ database_id: string;
+ migrations_dir?: string;
+ experimental_remote?: boolean;
+ }>;
+ kv_namespaces?: Array<{
+ binding: string;
+ id: string;
+ experimental_remote?: boolean;
+ }>;
+}
+
+class UndeploymentError extends Error {
+ constructor(message: string, public cause?: Error) {
+ super(message);
+ this.name = 'UndeploymentError';
+ }
+}
+
+class CloudflareUndeploymentManager {
+ private config: WranglerConfig;
+ private forceMode: boolean = false;
+ private allMode: boolean = false;
+
+ constructor() {
+ this.parseArguments();
+ this.config = this.parseWranglerConfig();
+ }
+
+ /**
+ * Parse command line arguments
+ */
+ private parseArguments(): void {
+ const args = process.argv.slice(2);
+ this.allMode = args.includes('all');
+ this.forceMode = args.includes('--force');
+
+ if (this.allMode && !this.forceMode) {
+ console.warn('โ ๏ธ Warning: "all" mode requires --force flag for safety');
+ console.warn(' Usage: bun scripts/undeploy.ts all --force');
+ process.exit(1);
+ }
+
+ console.log(`๐จ Undeployment Mode: ${this.allMode ? 'COMPLETE DESTRUCTION' : 'Standard Cleanup'}`);
+ if (this.allMode) {
+ console.log('โ ๏ธ This will DELETE ALL RESOURCES including D1 database and dispatch namespace!');
+ } else {
+ console.log('โน๏ธ This will preserve D1 database and dispatch namespace');
+ }
+ }
+
+ /**
+ * Safely parses wrangler.jsonc file
+ */
+ private parseWranglerConfig(): WranglerConfig {
+ const wranglerPath = join(PROJECT_ROOT, 'wrangler.jsonc');
+
+ if (!existsSync(wranglerPath)) {
+ throw new UndeploymentError('wrangler.jsonc file not found in project root');
+ }
+
+ try {
+ const content = readFileSync(wranglerPath, 'utf-8');
+ const config = parse(content) as WranglerConfig;
+
+ console.log(`๐ Parsed wrangler.jsonc - Project: ${config.name}`);
+ return config;
+ } catch (error) {
+ throw new UndeploymentError(
+ 'Failed to parse wrangler.jsonc file',
+ error instanceof Error ? error : new Error(String(error))
+ );
+ }
+ }
+
+ /**
+ * Validate wrangler command for security
+ */
+ private validateWranglerCommand(command: string): void {
+ // Allowlist of safe wrangler commands and patterns
+ const allowedCommands = [
+ /^delete\s+[a-zA-Z0-9_-]+$/,
+ /^kv\s+namespace\s+delete\s+--namespace-id=[a-f0-9-]+$/,
+ /^r2\s+bucket\s+delete\s+[a-zA-Z0-9_-]+$/,
+ /^d1\s+delete\s+[a-zA-Z0-9_-]+\s+--skip-confirmation$/,
+ /^dispatch-namespace\s+delete\s+[a-zA-Z0-9_-]+$/,
+ /^containers\s+list$/,
+ /^containers\s+delete\s+[a-f0-9-]+$/,
+ /^containers\s+images\s+list$/,
+ /^containers\s+images\s+delete\s+[a-zA-Z0-9_:.-]+$/
+ ];
+
+ const isAllowed = allowedCommands.some(pattern => pattern.test(command.trim()));
+ if (!isAllowed) {
+ throw new UndeploymentError(`Invalid or potentially unsafe wrangler command: ${command}`);
+ }
+ }
+
+ /**
+ * Execute wrangler command with error handling (synchronous)
+ */
+ private execWranglerCommand(command: string, description: string): boolean {
+ try {
+ console.log(`๐ ${description}...`);
+
+ // Validate command for security
+ this.validateWranglerCommand(command);
+
+ // For delete commands, set environment variables for non-interactive mode
+ const env = command.includes('delete') ? {
+ ...process.env,
+ CI: 'true',
+ WRANGLER_NON_INTERACTIVE: 'true',
+ NODE_ENV: 'production'
+ } : process.env;
+
+ // Use secure array-based execution - eliminates command injection vectors
+ const args = command.trim().split(/\s+/);
+ const result = spawnSync('wrangler', args, {
+ stdio: 'pipe',
+ cwd: PROJECT_ROOT,
+ encoding: 'utf8',
+ env: env
+ });
+
+ if (result.status !== 0) {
+ throw new Error(result.stderr || result.stdout || 'Command failed');
+ }
+ console.log(`โ
${description} completed successfully`);
+ return true;
+ } catch (error) {
+ console.warn(`โ ๏ธ ${description} failed: ${error instanceof Error ? error.message : String(error)}`);
+ return false;
+ }
+ }
+
+ /**
+ * Execute wrangler command with error handling (asynchronous for parallel execution)
+ */
+ private async execWranglerCommandAsync(command: string, description: string): Promise {
+ try {
+ console.log(`๐ ${description}...`);
+
+ return new Promise((resolve) => {
+ try {
+ // Validate command for security
+ this.validateWranglerCommand(command);
+
+ // For delete commands, set environment variables for non-interactive mode
+ const env = command.includes('delete') ? {
+ ...process.env,
+ CI: 'true',
+ WRANGLER_NON_INTERACTIVE: 'true',
+ NODE_ENV: 'production'
+ } : process.env;
+
+ // Use secure array-based execution - eliminates command injection vectors
+ const args = command.trim().split(/\s+/);
+ const result = spawnSync('wrangler', args, {
+ stdio: 'pipe',
+ cwd: PROJECT_ROOT,
+ encoding: 'utf8',
+ env: env
+ });
+
+ if (result.status !== 0) {
+ throw new Error(result.stderr || result.stdout || 'Command failed');
+ }
+ console.log(`โ
${description} completed successfully`);
+ resolve(true);
+ } catch (error) {
+ console.warn(`โ ๏ธ ${description} failed: ${error instanceof Error ? error.message : String(error)}`);
+ resolve(false);
+ }
+ });
+ } catch (error) {
+ console.warn(`โ ๏ธ ${description} failed: ${error instanceof Error ? error.message : String(error)}`);
+ return false;
+ }
+ }
+
+ /**
+ * Delete the main Worker
+ */
+ private async deleteWorker(): Promise {
+ console.log('\n๐๏ธ Deleting Worker...');
+
+ const success = this.execWranglerCommand(
+ `delete ${this.config.name}`,
+ `Deleting Worker: ${this.config.name}`
+ );
+
+ if (!success) {
+ console.warn(' Worker may not exist or already deleted');
+ }
+ }
+
+ /**
+ * Delete KV namespaces (in parallel)
+ */
+ private async deleteKVNamespaces(): Promise {
+ if (!this.config.kv_namespaces || this.config.kv_namespaces.length === 0) {
+ console.log('\n๐ฆ No KV namespaces configured, skipping...');
+ return;
+ }
+
+ console.log(`\n๐ฆ Deleting ${this.config.kv_namespaces.length} KV namespaces in parallel...`);
+
+ const deletePromises = this.config.kv_namespaces.map(kvNamespace =>
+ this.execWranglerCommandAsync(
+ `kv namespace delete --namespace-id=${kvNamespace.id}`,
+ `Deleting KV namespace: ${kvNamespace.binding} (ID: ${kvNamespace.id})`
+ )
+ );
+
+ const results = await Promise.allSettled(deletePromises);
+ const successCount = results.filter(result => result.status === 'fulfilled' && result.value).length;
+
+ console.log(`โ
Deleted ${successCount}/${this.config.kv_namespaces.length} KV namespaces`);
+ }
+
+ /**
+ * Delete R2 buckets (in parallel)
+ */
+ private async deleteR2Buckets(): Promise {
+ if (!this.config.r2_buckets || this.config.r2_buckets.length === 0) {
+ console.log('\n๐ชฃ No R2 buckets configured, skipping...');
+ return;
+ }
+
+ console.log(`\n๐ชฃ Deleting ${this.config.r2_buckets.length} R2 buckets in parallel...`);
+
+ const deletePromises = this.config.r2_buckets.map(bucket =>
+ this.execWranglerCommandAsync(
+ `r2 bucket delete ${bucket.bucket_name}`,
+ `Deleting R2 bucket: ${bucket.bucket_name}`
+ )
+ );
+
+ const results = await Promise.allSettled(deletePromises);
+ const successCount = results.filter(result => result.status === 'fulfilled' && result.value).length;
+
+ console.log(`โ
Deleted ${successCount}/${this.config.r2_buckets.length} R2 buckets`);
+ }
+
+ /**
+ * Delete D1 databases (only in force mode, in parallel)
+ */
+ private async deleteD1Database(): Promise {
+ if (!this.allMode || !this.forceMode) {
+ console.log('\n๐๏ธ D1 databases preserved (use "all --force" to delete)');
+ return;
+ }
+
+ if (!this.config.d1_databases || this.config.d1_databases.length === 0) {
+ console.log('\n๐๏ธ No D1 databases configured, skipping...');
+ return;
+ }
+
+ console.log(`\n๐๏ธ Deleting ${this.config.d1_databases.length} D1 databases in parallel...`);
+
+ const deletePromises = this.config.d1_databases.map(database =>
+ this.execWranglerCommandAsync(
+ `d1 delete ${database.database_name} --skip-confirmation`,
+ `Deleting D1 database: ${database.database_name}`
+ )
+ );
+
+ const results = await Promise.allSettled(deletePromises);
+ const successCount = results.filter(result => result.status === 'fulfilled' && result.value).length;
+
+ console.log(`โ
Deleted ${successCount}/${this.config.d1_databases.length} D1 databases`);
+ }
+
+ /**
+ * Delete dispatch namespaces (only in force mode, in parallel)
+ */
+ private async deleteDispatchNamespace(): Promise {
+ if (!this.allMode || !this.forceMode) {
+ console.log('\n๐ Dispatch namespaces preserved (use "all --force" to delete)');
+ return;
+ }
+
+ if (!this.config.dispatch_namespaces || this.config.dispatch_namespaces.length === 0) {
+ console.log('\n๐ No dispatch namespaces configured, skipping...');
+ return;
+ }
+
+ console.log(`\n๐ Deleting ${this.config.dispatch_namespaces.length} dispatch namespaces in parallel...`);
+
+ const deletePromises = this.config.dispatch_namespaces.map(dispatchNs =>
+ this.execWranglerCommandAsync(
+ `dispatch-namespace delete ${dispatchNs.namespace}`,
+ `Deleting dispatch namespace: ${dispatchNs.namespace}`
+ )
+ );
+
+ const results = await Promise.allSettled(deletePromises);
+ const successCount = results.filter(result => result.status === 'fulfilled' && result.value).length;
+
+ console.log(`โ
Deleted ${successCount}/${this.config.dispatch_namespaces.length} dispatch namespaces`);
+ }
+
+ /**
+ * Delete containers (in parallel)
+ */
+ private async deleteContainers(): Promise {
+ console.log('\n๐ณ Deleting containers...');
+
+ try {
+ // Get list of all containers
+ const output = execSync('wrangler containers list', {
+ stdio: 'pipe',
+ cwd: PROJECT_ROOT,
+ encoding: 'utf-8'
+ });
+
+ // Parse JSON output from wrangler
+ let containers: any[] = [];
+ try {
+ // Extract JSON part from the output (skip warnings)
+ const lines = output.split('\n');
+ const jsonStart = lines.findIndex(line => line.trim().startsWith('['));
+ if (jsonStart !== -1) {
+ const jsonOutput = lines.slice(jsonStart).join('\n');
+ containers = JSON.parse(jsonOutput);
+ }
+ } catch (parseError) {
+ console.warn('โ ๏ธ Could not parse containers list JSON output');
+ return;
+ }
+
+ // Generate patterns to match our worker containers
+ const workerName = this.config.name;
+ const containerPatterns = [
+ `${workerName}-`,
+ `${workerName.replace('_', '-')}-`,
+ `${workerName.replace('-', '_')}-`
+ ];
+
+ // Collect all container IDs that belong to our worker
+ const containersToDelete: { id: string, name: string }[] = [];
+
+ for (const container of containers) {
+ if (!container.id || !container.name) continue;
+
+ // Check if this container belongs to our worker
+ const isOurContainer = containerPatterns.some(pattern =>
+ container.name.toLowerCase().includes(pattern.toLowerCase())
+ );
+
+ if (isOurContainer) {
+ containersToDelete.push({ id: container.id, name: container.name });
+ }
+ }
+
+ if (containersToDelete.length === 0) {
+ console.log('๐ฆ No containers found for this worker');
+ return;
+ }
+
+ console.log(`๐ Deleting ${containersToDelete.length} containers in parallel...`);
+
+ // Delete all containers in parallel
+ const deletePromises = containersToDelete.map(container =>
+ this.execWranglerCommandAsync(
+ `containers delete ${container.id}`,
+ `Deleting container: ${container.name} (${container.id})`
+ )
+ );
+
+ const results = await Promise.allSettled(deletePromises);
+ const successCount = results.filter(result => result.status === 'fulfilled' && result.value).length;
+
+ console.log(`โ
Deleted ${successCount}/${containersToDelete.length} containers`);
+
+ } catch (error) {
+ const errorMessage = error instanceof Error ? error.message : String(error);
+ if (errorMessage.includes('DELETE method not allowed for the oauth_token authentication scheme')) {
+ console.warn(`โ ๏ธ Container deletion failed due to authentication method:`);
+ console.warn(` Containers deletion requires API token authentication, not OAuth.`);
+ console.warn(` Please ensure you're using 'wrangler login' with API token or set CLOUDFLARE_API_TOKEN.`);
+ console.warn(` For more info: https://developers.cloudflare.com/workers/wrangler/authentication/`);
+ } else {
+ console.warn(`โ ๏ธ Could not list/delete containers: ${errorMessage}`);
+ }
+ }
+ }
+
+ /**
+ * Delete container images related to this worker (in parallel)
+ */
+ private async deleteContainerImages(): Promise {
+ console.log('\n๐ณ Deleting container images...');
+
+ try {
+ // Get list of all container images
+ const output = execSync('wrangler containers images list', {
+ stdio: 'pipe',
+ cwd: PROJECT_ROOT,
+ encoding: 'utf-8'
+ });
+
+ // Parse the output to find images related to our worker
+ const lines = output.split('\n');
+ const imageLines = lines.slice(1).filter(line => line.trim()); // Skip header
+
+ // Generate patterns to match our worker images
+ const workerName = this.config.name;
+ const imagePatterns = [
+ `${workerName}-`,
+ `${workerName.replace('_', '-')}-`,
+ `${workerName.replace('-', '_')}-`
+ ];
+
+ // Collect all images that belong to our worker
+ const imagesToDelete: string[] = [];
+
+ for (const line of imageLines) {
+ const parts = line.trim().split(/\s+/);
+ if (parts.length < 2) continue;
+
+ const [repository, tag] = parts;
+
+ // Check if this image belongs to our worker
+ const isOurImage = imagePatterns.some(pattern =>
+ repository.toLowerCase().includes(pattern.toLowerCase())
+ );
+
+ if (isOurImage) {
+ imagesToDelete.push(`${repository}:${tag}`);
+ }
+ }
+
+ if (imagesToDelete.length === 0) {
+ console.log('๐ฆ No container images found for this worker');
+ return;
+ }
+
+ console.log(`๐ Deleting ${imagesToDelete.length} container images in parallel...`);
+
+ // Delete all images in parallel
+ const deletePromises = imagesToDelete.map(imageRef =>
+ this.execWranglerCommandAsync(
+ `containers images delete ${imageRef}`,
+ `Deleting container image: ${imageRef}`
+ )
+ );
+
+ const results = await Promise.allSettled(deletePromises);
+ const successCount = results.filter(result => result.status === 'fulfilled' && result.value).length;
+
+ console.log(`โ
Deleted ${successCount}/${imagesToDelete.length} container images`);
+
+ } catch (error) {
+ console.warn(`โ ๏ธ Could not list/delete container images: ${error instanceof Error ? error.message : String(error)}`);
+ }
+ }
+
+ /**
+ * Show final confirmation and summary
+ */
+ private showFinalSummary(): void {
+ console.log('\n' + '='.repeat(60));
+ console.log('๐ฏ UNDEPLOYMENT SUMMARY');
+ console.log('='.repeat(60));
+
+ const cleanedResources = [
+ 'โ
Containers deleted',
+ 'โ
Container images deleted',
+ 'โ
Worker deleted',
+ 'โ
KV namespaces deleted',
+ 'โ
R2 buckets deleted'
+ ];
+
+ if (this.allMode && this.forceMode) {
+ cleanedResources.push('โ
D1 database deleted');
+ cleanedResources.push('โ
Dispatch namespace deleted');
+ } else {
+ cleanedResources.push('โช D1 database preserved');
+ cleanedResources.push('โช Dispatch namespace preserved');
+ }
+
+ cleanedResources.forEach(resource => console.log(` ${resource}`));
+
+ console.log('\n๐ก To completely remove all resources, use:');
+ console.log(' bun scripts/undeploy.ts all --force');
+
+ console.log('\n๐งก Orange Build cleanup completed!');
+ }
+
+ /**
+ * Main undeployment orchestration method
+ */
+ public async undeploy(): Promise {
+ console.log('๐งก Cloudflare Orange Build - Automated Undeployment Starting...\n');
+
+ const startTime = Date.now();
+
+ try {
+ // Step 1: Delete containers first (they may reference images)
+ await this.deleteContainers();
+
+ // Step 2: Delete container images (after containers are deleted)
+ await this.deleteContainerImages();
+
+ // Step 3: Delete Worker (must be done before other resources)
+ await this.deleteWorker();
+
+ // Step 4: Delete supporting resources in parallel
+ console.log('\n๐ Step 4: Deleting supporting resources in parallel...');
+ const supportingResourcePromises = [
+ this.deleteKVNamespaces(),
+ this.deleteR2Buckets()
+ ];
+
+ await Promise.all(supportingResourcePromises);
+ console.log('โ
Supporting resources deletion completed!');
+
+ // Step 5: Delete persistent resources in parallel (only with --force)
+ if (this.allMode && this.forceMode) {
+ console.log('\n๐ Step 5: Deleting persistent resources in parallel...');
+ const persistentResourcePromises = [
+ this.deleteD1Database(),
+ this.deleteDispatchNamespace()
+ ];
+
+ await Promise.all(persistentResourcePromises);
+ console.log('โ
Persistent resources deletion completed!');
+ } else {
+ await this.deleteD1Database(); // This will just log preservation message
+ await this.deleteDispatchNamespace(); // This will just log preservation message
+ }
+
+ // Final summary
+ const duration = Math.round((Date.now() - startTime) / 1000);
+ console.log(`\nโฑ๏ธ Undeployment completed in ${duration}s`);
+
+ this.showFinalSummary();
+
+ } catch (error) {
+ console.error('\nโ Undeployment failed:');
+
+ if (error instanceof UndeploymentError) {
+ console.error(` ${error.message}`);
+ if (error.cause) {
+ console.error(` Caused by: ${error.cause.message}`);
+ }
+ } else {
+ console.error(` ${error}`);
+ }
+
+ console.error('\n๐ Troubleshooting tips:');
+ console.error(' - Ensure you have proper Cloudflare API permissions');
+ console.error(' - Check that wrangler is authenticated');
+ console.error(' - Verify resources exist before attempting deletion');
+ console.error(' - Some resources may have already been deleted manually');
+
+ process.exit(1);
+ }
+ }
+}
+
+// Main execution
+if (import.meta.url === `file://${process.argv[1]}`) {
+ const undeployer = new CloudflareUndeploymentManager();
+ undeployer.undeploy().catch((error) => {
+ console.error('Unexpected error:', error);
+ process.exit(1);
+ });
+}
+
+export default CloudflareUndeploymentManager;
\ No newline at end of file
diff --git a/external/drpower-vibe-production/shared/types/errors.ts b/external/drpower-vibe-production/shared/types/errors.ts
new file mode 100644
index 00000000..9fdc2916
--- /dev/null
+++ b/external/drpower-vibe-production/shared/types/errors.ts
@@ -0,0 +1,60 @@
+import type { RateLimitError } from "worker/services/rate-limit/errors";
+import type { RateLimitType } from "worker/services/rate-limit/config";
+
+/**
+ * Security error types for proper error handling
+ */
+export enum SecurityErrorType {
+ UNAUTHORIZED = 'UNAUTHORIZED',
+ FORBIDDEN = 'FORBIDDEN',
+ INVALID_TOKEN = 'INVALID_TOKEN',
+ TOKEN_EXPIRED = 'TOKEN_EXPIRED',
+ RATE_LIMITED = 'RATE_LIMITED',
+ INVALID_INPUT = 'INVALID_INPUT',
+ CSRF_VIOLATION = 'CSRF_VIOLATION',
+}
+
+/**
+ * Custom security error class
+ */
+export class SecurityError extends Error {
+ constructor(
+ public type: SecurityErrorType,
+ message: string,
+ public statusCode: number = 401
+ ) {
+ super(message);
+ this.name = 'SecurityError';
+ }
+}
+
+export class RateLimitExceededError extends SecurityError {
+ public details: RateLimitError;
+ constructor(
+ message: string,
+ public limitType: RateLimitType,
+ public limit?: number,
+ public period?: number,
+ public suggestions?: string[]
+ ) {
+ super(SecurityErrorType.RATE_LIMITED, message, 429);
+ this.name = 'RateLimitExceededError';
+ this.details = {
+ message,
+ limitType,
+ limit,
+ period,
+ suggestions
+ };
+ }
+
+ static fromRateLimitError(error: RateLimitError): RateLimitExceededError {
+ return new RateLimitExceededError(
+ error.message,
+ error.limitType,
+ error.limit,
+ error.period,
+ error.suggestions
+ );
+ }
+}
\ No newline at end of file
diff --git a/external/drpower-vibe-production/src/App.tsx b/external/drpower-vibe-production/src/App.tsx
new file mode 100644
index 00000000..b47bf1e7
--- /dev/null
+++ b/external/drpower-vibe-production/src/App.tsx
@@ -0,0 +1,24 @@
+import { Outlet } from 'react-router';
+import { AuthProvider } from './contexts/auth-context';
+import { AuthModalProvider } from './components/auth/AuthModalProvider';
+import { ThemeProvider } from './contexts/theme-context';
+import { Toaster } from './components/ui/sonner';
+import { AppLayout } from './components/layout/app-layout';
+import { ErrorBoundary } from './components/ErrorBoundary';
+
+export default function App() {
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+}
\ No newline at end of file
diff --git a/external/drpower-vibe-production/src/api-types.ts b/external/drpower-vibe-production/src/api-types.ts
new file mode 100644
index 00000000..f2c4001f
--- /dev/null
+++ b/external/drpower-vibe-production/src/api-types.ts
@@ -0,0 +1,269 @@
+/**
+ * Centralized API types - imports and re-exports types from worker
+ * This file serves as the single source of truth for frontend-worker API communication
+ */
+import { SessionResponse } from 'worker/utils/authUtils';
+import { AuthUser } from './api-types';
+
+export type { SecretTemplate } from 'worker/types/secretsTemplates';
+
+// Base API Response Types
+export type { ControllerResponse, ApiResponse } from 'worker/api/controllers/types';
+
+// Database Types
+export type {
+ PaginationInfo,
+ EnhancedAppData,
+ AppWithFavoriteStatus,
+ TimePeriod,
+ AppSortOption,
+ SortOrder,
+ AppQueryOptions,
+ PublicAppQueryOptions
+} from 'worker/database/types';
+
+// App-related API Types
+export type {
+ AppsListData,
+ PublicAppsData,
+ SingleAppData,
+ FavoriteToggleData,
+ CreateAppData,
+ UpdateAppVisibilityData,
+ AppDeleteData,
+ AppWithUserAndStats
+} from 'worker/api/controllers/apps/types';
+
+export type {
+ AppDetailsData,
+ AppStarToggleData,
+ GeneratedCodeFile
+} from 'worker/api/controllers/appView/types';
+
+// User-related API Types
+export type {
+ UserAppsData,
+ ProfileUpdateData,
+} from 'worker/api/controllers/user/types';
+
+// Stats API Types
+export type {
+ UserStatsData,
+ UserActivityData
+} from 'worker/api/controllers/stats/types';
+
+// Analytics API Types
+export type {
+ UserAnalyticsResponseData,
+ AgentAnalyticsResponseData,
+} from 'worker/api/controllers/analytics/types';
+
+export type { PlatformStatusData } from 'worker/api/controllers/status/types';
+
+// Model Config API Types
+export type {
+ ModelConfigsData,
+ ModelConfigData,
+ ModelConfigUpdateData,
+ ModelConfigTestData,
+ ModelConfigResetData,
+ ModelConfigDefaultsData,
+ ModelConfigDeleteData,
+ ByokProvidersData,
+ UserProviderStatus,
+ ModelsByProvider
+} from 'worker/api/controllers/modelConfig/types';
+
+// Model Provider API Types
+export type {
+ ModelProvidersListData,
+ ModelProviderData,
+ ModelProviderCreateData,
+ ModelProviderUpdateData,
+ ModelProviderDeleteData,
+ ModelProviderTestData,
+ CreateProviderRequest,
+ UpdateProviderRequest,
+ TestProviderRequest
+} from 'worker/api/controllers/modelProviders/types';
+
+// Frontend model config update interface that matches backend schema
+export interface ModelConfigUpdate {
+ modelName?: string | null;
+ maxTokens?: number | null;
+ temperature?: number | null;
+ reasoningEffort?: string | null;
+ fallbackModel?: string | null;
+ isUserOverride?: boolean;
+}
+
+// Secrets API Types
+export type {
+ SecretsData,
+ SecretStoreData,
+ SecretDeleteData,
+ SecretTemplatesData
+} from 'worker/api/controllers/secrets/types';
+
+// Agent/CodeGen API Types
+export type {
+ AgentConnectionData,
+} from 'worker/api/controllers/agent/types';
+
+// WebSocket Types
+export type {
+ WebSocketMessage,
+ WebSocketMessageData,
+ CodeFixEdits
+} from 'worker/api/websocketTypes';
+
+// Database/Schema Types commonly used in frontend
+export type {
+ App,
+ User,
+ UserModelConfig,
+ UserModelProvider
+} from 'worker/database/schema';
+
+export type {
+ FavoriteToggleResult,
+ UserStats,
+ UserActivity,
+ EncryptedSecret,
+ UserModelConfigWithMetadata,
+ ModelTestResult
+} from 'worker/database/types';
+
+// Agent/Generator Types
+export type {
+ Blueprint as BlueprintType,
+ ClientReportedErrorType,
+ CodeReviewOutputType,
+ FileConceptType,
+ FileOutputType as GeneratedFile,
+} from 'worker/agents/schemas';
+
+export type {
+ CodeGenState
+} from 'worker/agents/core/state';
+
+export type {
+ RuntimeError,
+ StaticAnalysisResponse
+} from 'worker/services/sandbox/sandboxTypes';
+
+// Config/Inference Types
+export type {
+ AgentActionKey,
+ AgentConfig,
+ ModelConfig,
+ ReasoningEffortType as ReasoningEffort,
+ ProviderOverrideType as ProviderOverride
+} from 'worker/agents/inferutils/config.types';
+
+export type { RateLimitError } from "worker/services/rate-limit/errors";
+export type { AgentPreviewResponse, CodeGenArgs } from 'worker/api/controllers/agent/types';
+export type { RateLimitErrorResponse } from 'worker/api/responses';
+export { RateLimitExceededError, SecurityError, SecurityErrorType } from 'shared/types/errors';
+
+export type { AIModels } from 'worker/agents/inferutils/config.types';
+// Model selection types
+export type ModelSelectionMode = 'platform' | 'byok' | 'custom';
+
+// Match chat FileType interface
+export interface FileType {
+ filePath: string;
+ fileContents: string;
+ explanation?: string;
+ isGenerating?: boolean;
+ needsFixing?: boolean;
+ hasErrors?: boolean;
+ language?: string;
+}
+
+// Streaming response wrapper types for agent session creation
+export interface StreamingResponse {
+ success: boolean;
+ stream: Response;
+}
+
+export type AgentStreamingResponse = StreamingResponse;
+
+export {
+ type ImageAttachment,
+ isSupportedImageType,
+ MAX_IMAGE_SIZE_BYTES,
+ MAX_IMAGES_PER_MESSAGE,
+ SUPPORTED_IMAGE_MIME_TYPES
+} from 'worker/types/image-attachment';
+
+// Auth types imported from worker
+export type {
+ AuthSession,
+ ApiKeyInfo,
+ AuthResult,
+ AuthUser,
+ OAuthProvider
+} from 'worker/types/auth-types';
+export type {
+ SessionResponse
+} from 'worker/utils/authUtils';
+
+// Auth API Response Types (using existing worker types)
+export type LoginResponseData = SessionResponse;
+
+export type RegisterResponseData = SessionResponse & {
+ requiresVerification?: boolean;
+};
+
+export type ProfileResponseData = {
+ user: AuthUser;
+ sessionId: string;
+};
+
+export interface AuthProvidersResponseData {
+ providers: {
+ google: boolean;
+ github: boolean;
+ email: boolean;
+ };
+ hasOAuth: boolean;
+ requiresEmailAuth: boolean;
+ csrfToken?: string;
+ csrfExpiresIn?: number;
+}
+
+export interface CsrfTokenResponseData {
+ token: string;
+ headerName: string;
+ expiresIn?: number;
+}
+
+// Active Sessions Response - matches getUserSessions + isCurrent from controller
+export interface ActiveSessionsData {
+ sessions: Array<{
+ id: string;
+ userAgent: string | null;
+ ipAddress: string | null;
+ lastActivity: Date;
+ createdAt: Date;
+ isCurrent: boolean;
+ }>;
+}
+
+// API Keys Response - matches controller response format
+export interface ApiKeysData {
+ keys: Array<{
+ id: string;
+ name: string;
+ keyPreview: string;
+ createdAt: Date | null;
+ lastUsed: Date | null;
+ isActive: boolean;
+ }>;
+}
+
+export type {
+ GitHubExportOptions,
+ GitHubExportResult,
+} from 'worker/services/github/types';
diff --git a/external/drpower-vibe-production/src/assets/fonts/DepartureMono-Regular.woff b/external/drpower-vibe-production/src/assets/fonts/DepartureMono-Regular.woff
new file mode 100644
index 00000000..b7bb6723
Binary files /dev/null and b/external/drpower-vibe-production/src/assets/fonts/DepartureMono-Regular.woff differ
diff --git a/external/drpower-vibe-production/src/assets/provider-logos/anthropic.svg b/external/drpower-vibe-production/src/assets/provider-logos/anthropic.svg
new file mode 100644
index 00000000..342d40be
--- /dev/null
+++ b/external/drpower-vibe-production/src/assets/provider-logos/anthropic.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/external/drpower-vibe-production/src/assets/provider-logos/cerebras.svg b/external/drpower-vibe-production/src/assets/provider-logos/cerebras.svg
new file mode 100644
index 00000000..994bbe60
--- /dev/null
+++ b/external/drpower-vibe-production/src/assets/provider-logos/cerebras.svg
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/external/drpower-vibe-production/src/assets/provider-logos/cloudflare.svg b/external/drpower-vibe-production/src/assets/provider-logos/cloudflare.svg
new file mode 100644
index 00000000..84ce0dc0
--- /dev/null
+++ b/external/drpower-vibe-production/src/assets/provider-logos/cloudflare.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/external/drpower-vibe-production/src/assets/provider-logos/google.svg b/external/drpower-vibe-production/src/assets/provider-logos/google.svg
new file mode 100644
index 00000000..787c8371
--- /dev/null
+++ b/external/drpower-vibe-production/src/assets/provider-logos/google.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/external/drpower-vibe-production/src/assets/provider-logos/openai.svg b/external/drpower-vibe-production/src/assets/provider-logos/openai.svg
new file mode 100644
index 00000000..401b8f7c
--- /dev/null
+++ b/external/drpower-vibe-production/src/assets/provider-logos/openai.svg
@@ -0,0 +1 @@
+
diff --git a/external/drpower-vibe-production/src/components/ErrorBoundary.tsx b/external/drpower-vibe-production/src/components/ErrorBoundary.tsx
new file mode 100644
index 00000000..1b243acf
--- /dev/null
+++ b/external/drpower-vibe-production/src/components/ErrorBoundary.tsx
@@ -0,0 +1,66 @@
+import * as Sentry from '@sentry/react';
+import { ReactNode } from 'react';
+import { Button } from '@/components/ui/button';
+import { AlertCircle } from 'lucide-react';
+
+function ErrorFallback({ error, resetError }: { error: Error | unknown; resetError: () => void; }) {
+ const errorMessage = error instanceof Error ? error.message : 'An unexpected error occurred';
+ return (
+
+
+
+
+
+
Something went wrong
+
+ An unexpected error occurred. Our team has been notified.
+
+
+
+ {import.meta.env.DEV && (
+
+ )}
+
+
+
+ Try Again
+
+ window.location.href = '/'}
+ variant="outline"
+ >
+ Go Home
+
+
+
+
+ );
+}
+
+interface ErrorBoundaryProps {
+ children: ReactNode;
+ showDialog?: boolean;
+}
+
+export function ErrorBoundary({
+ children,
+ showDialog = false
+}: ErrorBoundaryProps) {
+ return (
+
+ {children}
+
+ );
+}
+
+// Export the default fallback component for reuse
+export { ErrorFallback };
diff --git a/external/drpower-vibe-production/src/components/agent-mode-display.tsx b/external/drpower-vibe-production/src/components/agent-mode-display.tsx
new file mode 100644
index 00000000..c171d438
--- /dev/null
+++ b/external/drpower-vibe-production/src/components/agent-mode-display.tsx
@@ -0,0 +1,28 @@
+import { Zap } from 'lucide-react';
+import { type AgentMode } from './agent-mode-toggle';
+
+interface AgentModeDisplayProps {
+ mode: AgentMode;
+ className?: string;
+}
+
+export function AgentModeDisplay({
+ mode,
+ className = ''
+}: AgentModeDisplayProps) {
+ return (
+
+ {mode === 'smart' ? (
+ <>
+
+
Smart
+ >
+ ) : (
+ <>
+
+
Reliable
+ >
+ )}
+
+ );
+}
\ No newline at end of file
diff --git a/external/drpower-vibe-production/src/components/agent-mode-toggle.tsx b/external/drpower-vibe-production/src/components/agent-mode-toggle.tsx
new file mode 100644
index 00000000..7608e549
--- /dev/null
+++ b/external/drpower-vibe-production/src/components/agent-mode-toggle.tsx
@@ -0,0 +1,88 @@
+import { useState } from 'react';
+import { Zap, Settings } from 'lucide-react';
+
+export type AgentMode = 'deterministic' | 'smart';
+
+interface AgentModeToggleProps {
+ value: AgentMode;
+ onChange: (mode: AgentMode) => void;
+ disabled?: boolean;
+ className?: string;
+}
+
+export function AgentModeToggle({ value, onChange, disabled = false, className = '' }: AgentModeToggleProps) {
+ const [showTooltip, setShowTooltip] = useState(false);
+
+ return (
+
+
+
+ Mode:
+
+
+
+
setShowTooltip(true)}
+ onMouseLeave={() => setShowTooltip(false)}
+ >
+
onChange('deterministic')}
+ className={`flex items-center gap-1.5 px-2.5 py-1.5 text-xs font-medium rounded-md transition-all duration-200 ${
+ value === 'deterministic'
+ ? 'bg-white dark:bg-slate-700 shadow-sm text-emerald-700 dark:text-emerald-400 border border-slate-200 dark:border-slate-600'
+ : 'text-slate-600 dark:text-slate-400 hover:text-slate-800 dark:hover:text-slate-200'
+ } ${disabled ? 'opacity-50 cursor-not-allowed' : 'cursor-pointer'}`}
+ >
+
+ Reliable
+
+
onChange('smart')}
+ className={`flex items-center gap-1.5 px-2.5 py-1.5 text-xs font-medium rounded-md transition-all duration-200 ${
+ value === 'smart'
+ ? 'bg-white dark:bg-slate-700 shadow-sm text-violet-700 dark:text-violet-400 border border-slate-200 dark:border-slate-600'
+ : 'text-slate-600 dark:text-slate-400 hover:text-slate-800 dark:hover:text-slate-200'
+ } ${disabled ? 'opacity-50 cursor-not-allowed' : 'cursor-pointer'}`}
+ >
+
+ Smart
+
+
+
+ {/* Tooltip */}
+ {showTooltip && (
+
+
+
+
+
+
Reliable:
+
Structured & consistent
+
+
+
+ Smart:
+ AI-orchestrated & adaptive
+
+
+
+
+
+ )}
+
+
+ );
+}
\ No newline at end of file
diff --git a/external/drpower-vibe-production/src/components/analytics/cost-display.tsx b/external/drpower-vibe-production/src/components/analytics/cost-display.tsx
new file mode 100644
index 00000000..9323be30
--- /dev/null
+++ b/external/drpower-vibe-production/src/components/analytics/cost-display.tsx
@@ -0,0 +1,70 @@
+/**
+ * Clean Cost Display Component
+ * Minimal, theme-integrated cost display for analytics data
+ */
+
+import { DollarSign } from 'lucide-react';
+import { cn } from '@/lib/utils';
+import { formatCost, type AnalyticsDisplayProps } from '@/utils/analytics';
+
+interface CostDisplayProps extends AnalyticsDisplayProps {
+ loading?: boolean;
+ variant?: 'inline' | 'card';
+ className?: string;
+ label?: string;
+}
+
+export function CostDisplay({
+ cost,
+ loading = false,
+ variant = 'inline',
+ className,
+ label
+}: CostDisplayProps) {
+ if (loading) {
+ return (
+
+ );
+ }
+
+ if (variant === 'inline') {
+ return (
+
+
+
+ {formatCost(cost)}
+
+
+ );
+ }
+
+ // Card variant for future use
+ if (variant === 'card') {
+ return (
+
+
+
+
{label || 'Total Cost'}
+
+
+ {formatCost(cost)}
+
+
+ );
+ }
+
+ return null;
+}
\ No newline at end of file
diff --git a/external/drpower-vibe-production/src/components/auth/AuthModalProvider.tsx b/external/drpower-vibe-production/src/components/auth/AuthModalProvider.tsx
new file mode 100644
index 00000000..1a603c86
--- /dev/null
+++ b/external/drpower-vibe-production/src/components/auth/AuthModalProvider.tsx
@@ -0,0 +1,99 @@
+/**
+ * Authentication Modal Provider
+ * Provides global authentication modal management
+ */
+
+import React, { createContext, useContext, useState, useCallback, useEffect } from 'react';
+import { LoginModal } from './login-modal';
+import { useAuth } from '../../contexts/auth-context';
+import { setGlobalAuthModalTrigger } from '../../lib/api-client';
+
+interface AuthModalContextType {
+ showAuthModal: (context?: string, onSuccess?: () => void, intendedUrl?: string) => void;
+ hideAuthModal: () => void;
+ isAuthModalOpen: boolean;
+}
+
+const AuthModalContext = createContext(undefined);
+
+export function useAuthModal() {
+ const context = useContext(AuthModalContext);
+ if (context === undefined) {
+ throw new Error('useAuthModal must be used within an AuthModalProvider');
+ }
+ return context;
+}
+
+interface AuthModalProviderProps {
+ children: React.ReactNode;
+}
+
+export function AuthModalProvider({ children }: AuthModalProviderProps) {
+ const [isAuthModalOpen, setIsAuthModalOpen] = useState(false);
+ const [modalContext, setModalContext] = useState();
+ const [pendingAction, setPendingAction] = useState<(() => void) | undefined>();
+ const [intendedUrl, setIntendedUrlState] = useState();
+ const { login, loginWithEmail, register, error, clearError, isAuthenticated } = useAuth();
+
+ const showAuthModal = useCallback((context?: string, onSuccess?: () => void, intendedUrl?: string) => {
+ setModalContext(context);
+ setPendingAction(onSuccess ? () => onSuccess : undefined);
+ setIntendedUrlState(intendedUrl);
+ setIsAuthModalOpen(true);
+ }, []);
+
+ const hideAuthModal = useCallback(() => {
+ setIsAuthModalOpen(false);
+ setModalContext(undefined);
+ setPendingAction(undefined);
+ setIntendedUrlState(undefined);
+ clearError();
+ }, [clearError]);
+
+ // Close modal and execute pending action when user becomes authenticated
+ useEffect(() => {
+ if (isAuthenticated && isAuthModalOpen) {
+ hideAuthModal();
+ // Execute the pending action after a brief delay to ensure modal is closed
+ if (pendingAction) {
+ setTimeout(() => {
+ pendingAction();
+ }, 100);
+ }
+ }
+ }, [isAuthenticated, pendingAction, isAuthModalOpen, hideAuthModal]);
+
+ const handleLogin = useCallback((provider: 'google' | 'github', redirectUrl?: string) => {
+ // Use the intended URL if available, otherwise use the provided redirect URL
+ const finalRedirectUrl = intendedUrl || redirectUrl;
+ login(provider, finalRedirectUrl);
+ }, [login, intendedUrl]);
+
+ // Set up global auth modal trigger for API client
+ useEffect(() => {
+ setGlobalAuthModalTrigger(showAuthModal);
+ }, [showAuthModal]);
+
+ const value: AuthModalContextType = {
+ showAuthModal,
+ hideAuthModal,
+ isAuthModalOpen,
+ };
+
+ return (
+
+ {children}
+
+
+ );
+}
\ No newline at end of file
diff --git a/external/drpower-vibe-production/src/components/auth/auth-button.tsx b/external/drpower-vibe-production/src/components/auth/auth-button.tsx
new file mode 100644
index 00000000..e7f0f2a8
--- /dev/null
+++ b/external/drpower-vibe-production/src/components/auth/auth-button.tsx
@@ -0,0 +1,183 @@
+/**
+ * Enhanced Auth Button
+ * Provides OAuth + Email/Password authentication with enhanced UI
+ */
+
+import { useState } from 'react';
+import { LogIn, LogOut, Settings } from 'lucide-react';
+import { useNavigate } from 'react-router';
+import { motion, AnimatePresence } from 'framer-motion';
+import clsx from 'clsx';
+import { useAuth } from '../../contexts/auth-context';
+import { LoginModal } from './login-modal';
+import { Avatar, AvatarFallback, AvatarImage } from '../ui/avatar';
+import {
+ DropdownMenu,
+ DropdownMenuContent,
+ DropdownMenuItem,
+ DropdownMenuLabel,
+ DropdownMenuTrigger,
+ DropdownMenuGroup,
+} from '../ui/dropdown-menu';
+import { Button } from '../ui/button';
+import { Skeleton } from '../ui/skeleton';
+
+interface AuthButtonProps {
+ className?: string;
+}
+
+export function AuthButton({ className }: AuthButtonProps) {
+ const {
+ user,
+ isAuthenticated,
+ isLoading,
+ error,
+ login, // OAuth method
+ loginWithEmail,
+ register,
+ logout,
+ clearError,
+ } = useAuth();
+
+ const navigate = useNavigate();
+ const [showLoginModal, setShowLoginModal] = useState(false);
+
+ if (isLoading) {
+ return ;
+ }
+
+ if (!isAuthenticated || !user) {
+ return (
+ <>
+ setShowLoginModal(true)}
+ className={clsx('gap-2', className)}
+ >
+
+ Sign In
+
+
+ setShowLoginModal(false)}
+ onLogin={(provider) => {
+ // For backward compatibility with original login interface
+ login(provider);
+ setShowLoginModal(false);
+ }}
+ onEmailLogin={async (credentials) => {
+ await loginWithEmail(credentials);
+ if (!error) {
+ setShowLoginModal(false);
+ }
+ }}
+ onOAuthLogin={(provider) => {
+ login(provider);
+ setShowLoginModal(false);
+ }}
+ onRegister={async (data) => {
+ await register(data);
+ if (!error) {
+ setShowLoginModal(false);
+ }
+ }}
+ error={error}
+ onClearError={clearError}
+ />
+ >
+ );
+ }
+
+ // Get user initials for avatar fallback
+ const getInitials = () => {
+ if (user.displayName) {
+ return user.displayName
+ .split(' ')
+ .map((n) => n[0])
+ .join('')
+ .toUpperCase()
+ .slice(0, 2);
+ }
+ return user.email.charAt(0).toUpperCase();
+ };
+
+ return (
+
+
+
+
+
+
+ {getInitials()}
+
+
+ {user.emailVerified && (
+
+ )}
+
+
+
+
+
+
+
+
+
+
+
+ {getInitials()}
+
+
+
+
+
+ {user.displayName || 'User'}
+
+
+
+ {user.email}
+
+
+
+
+
+
+ navigate('/settings')}
+ className="cursor-pointer"
+ >
+
+ Settings
+
+
+
+ logout()}
+ className="cursor-pointer text-destructive focus:text-text-primary"
+ >
+
+ Sign Out
+
+
+
+
+
+ );
+}
diff --git a/external/drpower-vibe-production/src/components/auth/login-modal.tsx b/external/drpower-vibe-production/src/components/auth/login-modal.tsx
new file mode 100644
index 00000000..b27db2f1
--- /dev/null
+++ b/external/drpower-vibe-production/src/components/auth/login-modal.tsx
@@ -0,0 +1,473 @@
+/**
+ * Enhanced Login Modal
+ * Supports both OAuth and email/password authentication with backward compatibility
+ */
+
+import { useState } from 'react';
+import { motion, AnimatePresence } from 'framer-motion';
+import { X, AlertCircle, Eye, EyeOff } from 'lucide-react';
+import { createPortal } from 'react-dom';
+import clsx from 'clsx';
+import { useAuth } from '@/contexts/auth-context';
+// import {
+// validateEmail,
+// validatePassword,
+// validateDisplayName,
+// } from '../../utils/validationUtils';
+
+interface LoginModalProps {
+ isOpen: boolean;
+ onClose: () => void;
+
+ // Original OAuth-only interface (for backward compatibility)
+ onLogin: (provider: 'google' | 'github') => void;
+
+ // New enhanced interfaces (optional)
+ onEmailLogin?: (credentials: {
+ email: string;
+ password: string;
+ }) => Promise;
+ onOAuthLogin?: (provider: 'google' | 'github', redirectUrl?: string) => void;
+ onRegister?: (data: {
+ email: string;
+ password: string;
+ name?: string;
+ }) => Promise;
+ error?: string | null;
+ onClearError?: () => void;
+
+ // Contextual messaging
+ actionContext?: string; // e.g., "to star this app", "to fork this project"
+ showCloseButton?: boolean;
+}
+
+type AuthMode = 'login' | 'register';
+
+export function LoginModal({
+ isOpen,
+ onClose,
+ onLogin, // Original OAuth interface
+ onEmailLogin,
+ onOAuthLogin,
+ onRegister,
+ error,
+ onClearError,
+ actionContext,
+ showCloseButton = true,
+}: LoginModalProps) {
+ const { authProviders, hasOAuth, requiresEmailAuth } = useAuth();
+ const [mode, setMode] = useState('login');
+ const [showPassword, setShowPassword] = useState(false);
+ const [isLoading, setIsLoading] = useState(false);
+
+ // Form state
+ const [email, setEmail] = useState('');
+ const [password, setPassword] = useState('');
+ const [name, setName] = useState('');
+ const [confirmPassword, setConfirmPassword] = useState('');
+
+ // Validation errors
+ const [validationErrors, setValidationErrors] = useState<
+ Record
+ >({});
+
+ // Determine if enhanced features are available
+ const hasEmailAuth = requiresEmailAuth && !!onEmailLogin;
+ const hasRegistration = requiresEmailAuth && !!onRegister;
+ const showGitHub = authProviders?.github && hasOAuth;
+ const showGoogle = authProviders?.google && hasOAuth;
+
+ const resetForm = () => {
+ setEmail('');
+ setPassword('');
+ setName('');
+ setConfirmPassword('');
+ setValidationErrors({});
+ setShowPassword(false);
+ if (onClearError) onClearError();
+ };
+
+ const handleClose = () => {
+ resetForm();
+ onClose();
+ };
+
+ const switchMode = (newMode: AuthMode) => {
+ setMode(newMode);
+ resetForm();
+ setValidationErrors({});
+ if (onClearError) onClearError();
+ };
+
+ const validateForm = (): boolean => {
+ const errors: Record = {};
+
+ // Basic email validation
+ const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
+ if (!email.trim()) {
+ errors.email = 'Email is required';
+ } else if (!emailRegex.test(email)) {
+ errors.email = 'Invalid email format';
+ }
+
+ // Basic password validation
+ if (!password) {
+ errors.password = 'Password is required';
+ } else if (password.length < 8) {
+ errors.password = 'Password must be at least 8 characters';
+ }
+
+ // Additional validation for registration
+ if (mode === 'register') {
+ // Name validation
+ if (!name.trim()) {
+ errors.name = 'Name is required';
+ } else if (name.trim().length < 2) {
+ errors.name = 'Name must be at least 2 characters';
+ }
+
+ // Confirm password validation
+ if (password !== confirmPassword) {
+ errors.confirmPassword = 'Passwords do not match';
+ }
+ }
+
+ setValidationErrors(errors);
+ return Object.keys(errors).length === 0;
+ };
+
+ const handleSubmit = async (e: React.FormEvent) => {
+ e.preventDefault();
+
+ if (!validateForm()) return;
+
+ setIsLoading(true);
+ try {
+ if (mode === 'login' && onEmailLogin) {
+ await onEmailLogin({ email, password });
+ } else if (mode === 'register' && onRegister) {
+ await onRegister({ email, password, name: name.trim() });
+ }
+ // Don't auto-close here - let the parent handle success/error
+ } catch (err) {
+ // Error handling is done in the auth context
+ } finally {
+ setIsLoading(false);
+ }
+ };
+
+ const handleOAuthClick = (provider: 'google' | 'github') => {
+ // Use the new interface if available, otherwise fall back to original
+ if (onOAuthLogin) {
+ // Pass the current URL as redirect URL for context preservation
+ onOAuthLogin(provider, window.location.pathname + window.location.search);
+ } else {
+ onLogin(provider);
+ }
+ };
+
+ if (!isOpen) return null;
+
+ return createPortal(
+
+ {isOpen && (
+
+ {/* Backdrop */}
+
+
+ {/* Modal */}
+
+
+ {/* Header */}
+
+ {showCloseButton && (
+
+
+
+ )}
+
+
+
+
+ {actionContext
+ ? `Sign in ${actionContext}`
+ : hasEmailAuth && mode === 'register'
+ ? 'Create an account'
+ : 'Welcome back'}
+
+
+ {actionContext
+ ? 'Authentication required for this action'
+ : hasEmailAuth && mode === 'register'
+ ? 'Join to start building amazing applications'
+ : 'Sign in to save your apps and access your workspace'}
+
+
+
+
+ {/* Error display */}
+ {error && (
+
+ )}
+
+ {/* Authentication Options */}
+
+ {/* GitHub */}
+ {showGitHub && (
+
handleOAuthClick('github')}
+ // disabled={isLoading}
+ className="w-full group relative overflow-hidden rounded-xl bg-gray-900 dark:bg-bg-1 p-4 text-white transition-all hover:bg-gray-800 dark:hover:bg-[#1a1e22] border border-gray-800 dark:border-bg-4 disabled:opacity-50 disabled:cursor-not-allowed"
+ >
+
+
+
+
+
+ Continue with GitHub
+
+
+
+
+ )}
+
+ {/* Google */}
+ {showGoogle && (
+
handleOAuthClick('google')}
+ // disabled={isLoading}
+ className="w-full group relative overflow-hidden rounded-xl bg-white dark:bg-bg-4 p-4 text-gray-800 dark:text-text-primary transition-all hover:bg-gray-50 dark:hover:bg-bg-4/80 border border-gray-200 dark:border-border-primary disabled:opacity-50 disabled:cursor-not-allowed"
+ >
+
+
+
+
+
+
+
+
+ Continue with Google
+
+
+
+
+ )}
+
+ {/* Divider (only if both OAuth and email are available) */}
+ {hasEmailAuth && hasOAuth && (
+
+
+
+ Or continue with
+
+
+ )}
+
+ {/* Email/Password Form */}
+ {hasEmailAuth && (
+
+ )}
+
+
+ {/* Footer */}
+
+ {/* Mode switching (only if registration is available) */}
+ {hasRegistration && hasEmailAuth && (
+
+
+ switchMode(
+ mode === 'login'
+ ? 'register'
+ : 'login',
+ )
+ }
+ className="text-sm text-text-tertiary hover:text-text-primary transition-colors"
+ >
+ {mode === 'login'
+ ? "Don't have an account? Sign up"
+ : "Already have an account? Sign in"
+ }
+
+
+ )}
+
+
+ By continuing, you agree to our{' '}
+
+ Terms of Service
+ {' '}
+ and{' '}
+
+ Privacy Policy
+
+
+
+
+
+
+ )}
+ ,
+ document.body,
+ );
+}
diff --git a/external/drpower-vibe-production/src/components/byok-api-keys-modal.tsx b/external/drpower-vibe-production/src/components/byok-api-keys-modal.tsx
new file mode 100644
index 00000000..00198480
--- /dev/null
+++ b/external/drpower-vibe-production/src/components/byok-api-keys-modal.tsx
@@ -0,0 +1,589 @@
+/**
+ * Enhanced BYOK API Keys Modal with Two-Tab Layout
+ * Tab 1: Add new keys
+ * Tab 2: Manage existing keys with toggle/delete functionality
+ */
+
+import { useState, useEffect } from 'react';
+import { Key, Check, AlertCircle, Loader2, Plus, Settings, Trash2, Eye } from 'lucide-react';
+import { Button } from '@/components/ui/button';
+import { Input } from '@/components/ui/input';
+import { Label } from '@/components/ui/label';
+import { Badge } from '@/components/ui/badge';
+import { Switch } from '@/components/ui/switch';
+import { Separator } from '@/components/ui/separator';
+import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs';
+import {
+ Dialog,
+ DialogContent,
+ DialogDescription,
+ DialogFooter,
+ DialogHeader,
+ DialogTitle,
+} from '@/components/ui/dialog';
+import {
+ AlertDialog,
+ AlertDialogAction,
+ AlertDialogCancel,
+ AlertDialogContent,
+ AlertDialogDescription,
+ AlertDialogFooter,
+ AlertDialogHeader,
+ AlertDialogTitle,
+} from '@/components/ui/alert-dialog';
+import { apiClient } from '@/lib/api-client';
+import { toast } from 'sonner';
+import type { SecretTemplate } from '@/api-types';
+
+// Import provider logos
+import OpenAILogo from '@/assets/provider-logos/openai.svg?react';
+import AnthropicLogo from '@/assets/provider-logos/anthropic.svg?react';
+import GoogleLogo from '@/assets/provider-logos/google.svg?react';
+import CerebrasLogo from '@/assets/provider-logos/cerebras.svg?react';
+import CloudflareLogo from '@/assets/provider-logos/cloudflare.svg?react';
+
+interface ByokApiKeysModalProps {
+ isOpen: boolean;
+ onClose: () => void;
+ onKeyAdded?: () => void;
+}
+
+interface ManagedSecret {
+ id: string;
+ name: string;
+ provider: string;
+ keyPreview: string;
+ isActive: boolean;
+ lastUsed: string | null;
+ createdAt: string;
+ logo: React.ComponentType<{ className?: string }>;
+}
+
+// Logo mapping for dynamic provider support
+const PROVIDER_LOGOS: Record> = {
+ openai: OpenAILogo,
+ anthropic: AnthropicLogo,
+ 'google-ai-studio': GoogleLogo,
+ cerebras: CerebrasLogo,
+};
+
+interface BYOKProvider {
+ id: string;
+ name: string;
+ provider: string;
+ logo: React.ComponentType<{ className?: string }>;
+ placeholder: string;
+ validation: RegExp;
+}
+
+/**
+ * Convert BYOK template to provider configuration
+ */
+function templateToBYOKProvider(template: SecretTemplate): BYOKProvider {
+ const logo = PROVIDER_LOGOS[template.provider] || (() =>
);
+
+ return {
+ id: template.id,
+ name: template.displayName.replace(' (BYOK)', ''),
+ provider: template.provider,
+ logo,
+ placeholder: template.placeholder,
+ validation: new RegExp(template.validation),
+ };
+}
+
+export function ByokApiKeysModal({ isOpen, onClose, onKeyAdded }: ByokApiKeysModalProps) {
+ // Tab management
+ const [activeTab, setActiveTab] = useState<'add' | 'manage'>('add');
+
+ // Add keys tab state
+ const [selectedProvider, setSelectedProvider] = useState(null);
+ const [apiKey, setApiKey] = useState('');
+ const [isSaving, setIsSaving] = useState(false);
+ const [byokProviders, setBYOKProviders] = useState([]);
+ const [isLoading, setIsLoading] = useState(true);
+
+ // Manage keys tab state
+ const [managedSecrets, setManagedSecrets] = useState([]);
+ const [loadingSecrets, setLoadingSecrets] = useState(false);
+ const [toggleLoadingId, setToggleLoadingId] = useState(null);
+ const [deleteDialogOpen, setDeleteDialogOpen] = useState(false);
+ const [secretToDelete, setSecretToDelete] = useState(null);
+ const [isDeleting, setIsDeleting] = useState(false);
+
+ // Get selected provider details
+ const provider = byokProviders.find(p => p.id === selectedProvider);
+
+ // Load BYOK templates and existing secrets when modal opens
+ useEffect(() => {
+ if (isOpen) {
+ // Reset add keys tab
+ setSelectedProvider(null);
+ setApiKey('');
+ setIsSaving(false);
+
+ // Reset manage keys tab
+ setToggleLoadingId(null);
+ setDeleteDialogOpen(false);
+ setSecretToDelete(null);
+ setIsDeleting(false);
+
+ // Load data
+ loadBYOKProviders();
+ loadManagedSecrets();
+ }
+ }, [isOpen]);
+
+ const loadBYOKProviders = async () => {
+ try {
+ setIsLoading(true);
+ const response = await apiClient.getBYOKTemplates();
+
+ if (response.success && response.data) {
+ const providers = response.data.templates.map(templateToBYOKProvider);
+ setBYOKProviders(providers);
+ } else {
+ toast.error('Failed to load BYOK providers');
+ }
+ } catch (error) {
+ console.error('Error loading BYOK templates:', error);
+ toast.error('Failed to load BYOK providers');
+ } finally {
+ setIsLoading(false);
+ }
+ };
+
+ const loadManagedSecrets = async () => {
+ try {
+ setLoadingSecrets(true);
+ const response = await apiClient.getAllSecrets(); // Use getAllSecrets for toggle functionality
+
+ if (response.success && response.data) {
+ // Filter BYOK secrets only (show both active and inactive for management)
+ const byokSecrets = response.data.secrets.filter(secret =>
+ secret.secretType.endsWith('_BYOK')
+ );
+
+ // Convert to ManagedSecret format with logos
+ const managedSecrets: ManagedSecret[] = byokSecrets.map(secret => {
+ const logo = PROVIDER_LOGOS[secret.provider] || (() =>
);
+
+ return {
+ id: secret.id,
+ name: secret.name,
+ provider: secret.provider,
+ keyPreview: secret.keyPreview,
+ isActive: secret.isActive ?? false,
+ lastUsed: secret.lastUsed ? secret.lastUsed.toString() : null,
+ createdAt: secret.createdAt?.toString() ?? '',
+ logo
+ };
+ });
+
+ setManagedSecrets(managedSecrets);
+ } else {
+ toast.error('Failed to load managed secrets');
+ }
+ } catch (error) {
+ console.error('Error loading managed secrets:', error);
+ toast.error('Failed to load managed secrets');
+ } finally {
+ setLoadingSecrets(false);
+ }
+ };
+
+ // Handle provider selection
+ const handleProviderSelect = (providerId: string) => {
+ setSelectedProvider(providerId);
+ setApiKey('');
+ };
+
+ // Validate key format
+ const isKeyFormatValid = provider && apiKey && provider.validation.test(apiKey);
+
+ // Save API key
+ const handleSaveKey = async () => {
+ if (!provider || !apiKey || !isKeyFormatValid) return;
+
+ setIsSaving(true);
+
+ try {
+ await apiClient.storeSecret({
+ templateId: provider.id,
+ value: apiKey.trim(),
+ environment: 'production'
+ });
+
+ toast.success(`${provider.name} API key added successfully!`);
+ onKeyAdded?.();
+
+ // Reload managed secrets and switch to manage tab
+ await loadManagedSecrets();
+ setActiveTab('manage');
+
+ // Reset add form
+ setSelectedProvider(null);
+ setApiKey('');
+ } catch (error) {
+ console.error('Failed to save API key:', error);
+ toast.error('Failed to save API key. Please try again.');
+ } finally {
+ setIsSaving(false);
+ }
+ };
+
+ // Toggle secret active status
+ const handleToggleSecret = async (secretId: string) => {
+ setToggleLoadingId(secretId);
+
+ try {
+ const response = await apiClient.toggleSecret(secretId);
+
+ if (response.success && response.data) {
+ const updatedSecret = response.data.secret;
+ toast.success(response.data.message);
+
+ // Update local state
+ setManagedSecrets(prev =>
+ prev.map(secret =>
+ secret.id === secretId
+ ? { ...secret, isActive: updatedSecret.isActive ?? false }
+ : secret
+ )
+ );
+
+ // Notify parent about key changes
+ onKeyAdded?.();
+ } else {
+ toast.error('Failed to toggle secret status');
+ }
+ } catch (error) {
+ console.error('Error toggling secret:', error);
+ toast.error('Failed to toggle secret status');
+ } finally {
+ setToggleLoadingId(null);
+ }
+ };
+
+ // Delete secret
+ const handleDeleteSecret = async () => {
+ if (!secretToDelete) return;
+
+ setIsDeleting(true);
+
+ try {
+ await apiClient.deleteSecret(secretToDelete.id);
+ toast.success(`${secretToDelete.name} API key deleted successfully`);
+
+ // Remove from local state
+ setManagedSecrets(prev =>
+ prev.filter(secret => secret.id !== secretToDelete.id)
+ );
+
+ // Notify parent about key changes
+ onKeyAdded?.();
+
+ // Close dialog
+ setDeleteDialogOpen(false);
+ setSecretToDelete(null);
+ } catch (error) {
+ console.error('Error deleting secret:', error);
+ toast.error('Failed to delete API key');
+ } finally {
+ setIsDeleting(false);
+ }
+ };
+
+ const openDeleteDialog = (secret: ManagedSecret) => {
+ setSecretToDelete(secret);
+ setDeleteDialogOpen(true);
+ };
+
+ const formatDate = (dateString: string) => {
+ const date = new Date(dateString);
+ return date.toLocaleDateString('en-US', {
+ year: 'numeric',
+ month: 'short',
+ day: 'numeric'
+ });
+ };
+
+ return (
+ <>
+
+
+
+
+
+ Bring Your Own Key
+
+ via AI Gateway
+
+
+
+ Add your API keys to use your own provider accounts for billing, or manage existing keys
+
+
+
+ setActiveTab(value as 'add' | 'manage')} className="space-y-6">
+
+
+
+ Add Keys
+
+
+
+ Manage Keys
+
+
+
+ {/* Add Keys Tab */}
+
+ {/* Provider Selection - Clean List */}
+
+
Select Provider
+ {isLoading ? (
+
+ {[1, 2, 3, 4].map((i) => (
+
+ ))}
+
+ ) : (
+
+ {byokProviders.map((providerOption) => {
+ const LogoComponent = providerOption.logo;
+ const isSelected = selectedProvider === providerOption.id;
+ return (
+
handleProviderSelect(providerOption.id)}
+ className={`w-full flex items-center gap-3 p-3 rounded-lg border-2 transition-all duration-200 text-left ${
+ isSelected
+ ? 'border-blue-500 bg-blue-50'
+ : 'border-gray-200 hover:border-gray-300 hover:bg-gray-50'
+ }`}
+ >
+
+
+
+ {providerOption.name}
+
+ );
+ })}
+
+ )}
+
+
+ {/* API Key Input - Smooth Expansion */}
+ {selectedProvider && provider && (
+
+
+ Enter your {provider.name} API key
+
+
+
setApiKey(e.target.value)}
+ placeholder={provider.placeholder}
+ className={`pr-10 ${
+ apiKey
+ ? isKeyFormatValid
+ ? 'border-green-500 focus:border-green-500'
+ : 'border-red-500 focus:border-red-500'
+ : ''
+ }`}
+ />
+ {apiKey && (
+
+ {isKeyFormatValid ? (
+
+ ) : (
+
+ )}
+
+ )}
+
+ {apiKey && !isKeyFormatValid && (
+
+ Invalid format. Expected: {provider.placeholder}
+
+ )}
+
+ )}
+
+
+ {/* Manage Keys Tab */}
+
+ {loadingSecrets ? (
+
+ {[1, 2, 3].map((i) => (
+
+ ))}
+
+ ) : managedSecrets.length === 0 ? (
+
+
+
No API keys configured
+
Add your first API key using the "Add Keys" tab
+
+ ) : (
+
+
+ Your API Keys
+
+ {managedSecrets.filter(s => s.isActive).length} active, {managedSecrets.length} total
+
+
+
+
+ {managedSecrets.map((secret) => {
+ const LogoComponent = secret.logo;
+ const isTogglingThis = toggleLoadingId === secret.id;
+
+ return (
+
+ {/* Provider Logo */}
+
+
+
+
+ {/* Key Info */}
+
+
+
+ {secret.name.replace(' (BYOK)', '')}
+
+
+ {secret.isActive ? "Active" : "Inactive"}
+
+
+
+
+
+ {secret.keyPreview}
+
+
+
Added {formatDate(secret.createdAt)}
+ {secret.lastUsed && (
+ <>
+
+
Last used {formatDate(secret.lastUsed)}
+ >
+ )}
+
+
+
+ {/* Controls */}
+
+
+ handleToggleSecret(secret.id)}
+ disabled={isTogglingThis}
+ />
+ {isTogglingThis && (
+
+ )}
+
+
+
openDeleteDialog(secret)}
+ className="text-red-600 hover:text-red-700 hover:bg-red-50"
+ >
+
+
+
+
+ );
+ })}
+
+
+ )}
+
+
+
+
+
+ Close
+
+ {activeTab === 'add' && selectedProvider && (
+
+ {isSaving ? (
+ <>
+
+ Adding...
+ >
+ ) : (
+ <>
+
+ Add Key
+ >
+ )}
+
+ )}
+
+
+
+
+ {/* Delete Confirmation Dialog */}
+
+
+
+ Delete API Key
+
+ Are you sure you want to delete the {secretToDelete?.name} API key? This action cannot be undone.
+
+
+
+ Cancel
+
+ {isDeleting ? (
+ <>
+
+ Deleting...
+ >
+ ) : (
+ 'Delete Key'
+ )}
+
+
+
+
+ >
+ );
+}
\ No newline at end of file
diff --git a/external/drpower-vibe-production/src/components/config-card.tsx b/external/drpower-vibe-production/src/components/config-card.tsx
new file mode 100644
index 00000000..d2e9bf69
--- /dev/null
+++ b/external/drpower-vibe-production/src/components/config-card.tsx
@@ -0,0 +1,247 @@
+import { Settings, Play, RotateCcw, Zap, Brain, Code2 } from 'lucide-react';
+import { Button } from '@/components/ui/button';
+import { Badge } from '@/components/ui/badge';
+import { Card, CardContent, CardHeader } from '@/components/ui/card';
+import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@/components/ui/tooltip';
+import type { ModelConfig, UserModelConfigWithMetadata } from '@/api-types';
+import type { AgentDisplayConfig } from './model-config-tabs';
+
+interface ConfigCardProps {
+ agent: AgentDisplayConfig;
+ userConfig?: UserModelConfigWithMetadata;
+ defaultConfig?: ModelConfig;
+ onConfigure: () => void;
+ onTest: () => void;
+ onReset: () => void;
+ isTesting: boolean;
+}
+
+// Helper function to get model display name
+const getModelDisplayName = (modelValue?: string) => {
+ if (!modelValue) return 'Default';
+
+ return modelValue.split('/').pop() || modelValue;
+};
+
+// Helper function to get provider badge info
+const getProviderInfo = (modelValue?: string) => {
+ if (!modelValue) return { name: 'Default', color: 'bg-bg-3 text-text-tertiary' };
+
+ // Check specific prefixes first to avoid incorrect matches
+ if (modelValue.includes('cerebras/')) {
+ return { name: 'Cerebras', color: 'bg-purple-100 text-purple-800 dark:bg-purple-900/20 dark:text-purple-400' };
+ }
+ if (modelValue.includes('[openrouter]')) {
+ return { name: 'OpenRouter', color: 'bg-pink-100 text-pink-800 dark:bg-pink-900/20 dark:text-pink-400' };
+ }
+ if (modelValue.includes('openai/') || modelValue.includes('gpt') || modelValue.includes('o3') || modelValue.includes('o4')) {
+ return { name: 'OpenAI', color: 'bg-green-100 text-green-800 dark:bg-green-900/20 dark:text-green-400' };
+ }
+ if (modelValue.includes('anthropic/') || modelValue.includes('claude')) {
+ return { name: 'Anthropic', color: 'bg-orange-100 text-orange-800 dark:bg-orange-900/20 dark:text-orange-400' };
+ }
+ if (modelValue.includes('google-ai-studio/') || modelValue.includes('gemini')) {
+ return { name: 'Google', color: 'bg-blue-100 text-blue-800 dark:bg-blue-900/20 dark:text-blue-400' };
+ }
+
+ return { name: 'Custom', color: 'bg-gray-100 text-gray-800 dark:bg-gray-900/20 dark:text-gray-400' };
+};
+
+// Helper function to get agent icon based on type
+const getAgentIcon = (agentKey: string) => {
+ if (agentKey.includes('Code') || agentKey.includes('phase') || agentKey.includes('file')) return Code2;
+ if (agentKey.includes('fast') || agentKey.includes('template')) return Zap;
+ if (agentKey.includes('blueprint') || agentKey.includes('suggestion') || agentKey.includes('review')) return Brain;
+ return Settings;
+};
+
+// Helper function to format parameter values for display
+const formatParameterValue = (value: unknown, type: string): string | null => {
+ if (value === null || value === undefined) return null;
+
+ switch (type) {
+ case 'temperature':
+ return `T: ${value}`;
+ case 'maxTokens':
+ return typeof value === 'number' ? `${Math.round(value / 1000)}K tokens` : String(value);
+ case 'reasoningEffort':
+ return typeof value === 'string' ? `${value.charAt(0).toUpperCase()}${value.slice(1)}` : String(value);
+ default:
+ return String(value);
+ }
+};
+
+export function ConfigCard({
+ agent,
+ userConfig,
+ defaultConfig,
+ onConfigure,
+ onTest,
+ onReset,
+ isTesting
+}: ConfigCardProps) {
+ const isCustomized = userConfig?.isUserOverride || false;
+ const currentModel = userConfig?.name || defaultConfig?.name;
+ const modelDisplayName = getModelDisplayName(currentModel);
+ const providerInfo = getProviderInfo(currentModel);
+ const AgentIcon = getAgentIcon(agent.key);
+
+ // Get current parameter values
+ const temperature = userConfig?.temperature ?? defaultConfig?.temperature;
+ const maxTokens = userConfig?.max_tokens ?? defaultConfig?.max_tokens;
+ const reasoningEffort = userConfig?.reasoning_effort ?? defaultConfig?.reasoning_effort;
+
+ return (
+
+
+
+
+
+
+
+ {agent.name}
+
+
+ {agent.description}
+
+
+
+
+
+
+ {isCustomized ? "Custom" : "Default"}
+
+
+
+
+
+
+
+ {/* Current Model */}
+
+
+
+ {modelDisplayName}
+
+
+ {providerInfo.name}
+
+
+
+ {/* Parameter Summary - Contained within card bounds */}
+
+ {temperature !== null && temperature !== undefined ? (
+
+
+
+
+ {formatParameterValue(temperature, 'temperature')}
+
+
+
+ Temperature: {temperature}
+
+
+
+ ) : null}
+
+ {maxTokens ? (
+
+
+
+
+ {formatParameterValue(maxTokens, 'maxTokens')}
+
+
+
+ Max Tokens: {maxTokens?.toLocaleString()}
+
+
+
+ ) : null}
+
+ {reasoningEffort ? (
+
+
+
+
+ {formatParameterValue(reasoningEffort, 'reasoningEffort')}
+
+
+
+ Reasoning Effort: {reasoningEffort}
+
+
+
+ ) : null}
+
+
+
+
+ {/* Action Buttons - Fixed at bottom with proper containment */}
+
+
+
+ Configure
+
+
+
+
+
+
+ {isTesting ? (
+
+ ) : (
+
+ )}
+
+
+
+ {isTesting ? 'Testing...' : 'Test Config'}
+
+
+
+
+ {isCustomized && (
+
+
+
+
+
+
+
+
+ Reset to Default
+
+
+
+ )}
+
+
+
+ );
+}
\ No newline at end of file
diff --git a/external/drpower-vibe-production/src/components/config-modal.tsx b/external/drpower-vibe-production/src/components/config-modal.tsx
new file mode 100644
index 00000000..d7c7d980
--- /dev/null
+++ b/external/drpower-vibe-production/src/components/config-modal.tsx
@@ -0,0 +1,552 @@
+/**
+ * Redesigned Model Configuration Modal
+ * Three-mode interface: Platform Models, BYOK (Bring Your Own Key), Custom Providers
+ */
+
+import { useState, useEffect, useMemo } from 'react';
+import { Settings, Play, RotateCcw, Info, Key } from 'lucide-react';
+import { Button } from '@/components/ui/button';
+import { Input } from '@/components/ui/input';
+import { Label } from '@/components/ui/label';
+import { Badge } from '@/components/ui/badge';
+import { Separator } from '@/components/ui/separator';
+import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select';
+import { ModelSelector } from '@/components/ui/model-selector';
+import { Check } from 'lucide-react';
+import {
+ Dialog,
+ DialogContent,
+ DialogDescription,
+ DialogFooter,
+ DialogHeader,
+ DialogTitle,
+} from '@/components/ui/dialog';
+import { Alert, AlertDescription } from '@/components/ui/alert';
+import { apiClient } from '@/lib/api-client';
+import { ByokApiKeysModal } from './byok-api-keys-modal';
+import type {
+ ModelConfig,
+ UserModelConfigWithMetadata,
+ ModelConfigUpdate,
+ AIModels,
+ ByokProvidersData
+} from '@/api-types';
+import type { AgentDisplayConfig } from './model-config-tabs';
+
+interface ConfigModalProps {
+ isOpen: boolean;
+ onClose: () => void;
+ agentConfig: AgentDisplayConfig;
+ userConfig?: UserModelConfigWithMetadata;
+ defaultConfig?: ModelConfig;
+ onSave: (config: ModelConfigUpdate) => Promise;
+ onTest: (tempConfig?: ModelConfigUpdate) => Promise;
+ onReset: () => Promise;
+ isTesting: boolean;
+}
+
+
+// Helper to extract provider from model name (e.g., "openai/gpt-4" -> "openai")
+const getProviderFromModel = (modelName: string): string => {
+ if (!modelName || modelName === 'default') return '';
+ return modelName.split('/')[0] || '';
+};
+
+// Helper to check if user has BYOK key for a model's provider
+const hasUserKeyForModel = (modelName: string, byokProviders: Array<{ provider: string; hasValidKey: boolean }>): boolean => {
+ const provider = getProviderFromModel(modelName);
+ if (!provider) return false;
+
+ return byokProviders.some(p => p.provider === provider && p.hasValidKey);
+};
+
+// Helper to get clean model display name
+const getModelDisplayName = (model: AIModels | string): string => {
+ return typeof model === 'string' ? model : model;
+};
+
+// Model recommendations by agent
+const getModelRecommendation = (agentAction: string) => {
+ const recommendations: Record = {
+ templateSelection: '๐ก Recommended: Fast models for quick template selection',
+ blueprint: '๐๏ธ Recommended: Creative models for architecture design',
+ projectSetup: 'โ๏ธ Recommended: Reliable models for precise setup',
+ phaseGeneration: '๐ Recommended: Large context models for comprehensive planning',
+ firstPhaseImplementation: '๐ Recommended: High-capability models for foundation development',
+ phaseImplementation: 'โก Recommended: Strong coding models for implementation',
+ realtimeCodeFixer: '๐ Recommended: Fast debugging models',
+ fastCodeFixer: 'โก Recommended: Ultra-fast models for quick fixes',
+ conversationalResponse: '๐ฌ Recommended: Balanced models for natural conversation',
+ codeReview: '๐ Recommended: Analytical models with large context',
+ fileRegeneration: '๐ Recommended: Pure coding models',
+ screenshotAnalysis: '๐๏ธ Recommended: Vision-capable models for image analysis'
+ };
+ return recommendations[agentAction] || '';
+};
+
+export function ConfigModal({
+ isOpen,
+ onClose,
+ agentConfig,
+ userConfig,
+ defaultConfig,
+ onSave,
+ onTest,
+ onReset,
+ isTesting
+}: ConfigModalProps) {
+ // Form state
+ const [formData, setFormData] = useState({
+ modelName: userConfig?.name || 'default',
+ maxTokens: userConfig?.max_tokens?.toString() || '',
+ temperature: userConfig?.temperature?.toString() || '',
+ reasoningEffort: userConfig?.reasoning_effort || 'default',
+ fallbackModel: userConfig?.fallbackModel || 'default'
+ });
+
+ // UI state
+ const [hasChanges, setHasChanges] = useState(false);
+ const [byokModalOpen, setByokModalOpen] = useState(false);
+
+ // Modal lifecycle tracking
+ const [isInitialOpen, setIsInitialOpen] = useState(false);
+
+ // BYOK data state
+ const [byokData, setByokData] = useState(null);
+ const [loadingByok, setLoadingByok] = useState(false);
+
+ // Load BYOK data
+ const loadByokData = async () => {
+ try {
+ setLoadingByok(true);
+ const response = await apiClient.getByokProviders();
+ if (response.success && response.data) {
+ setByokData(response.data);
+ }
+ } catch (error) {
+ console.error('Failed to load BYOK data:', error);
+ } finally {
+ setLoadingByok(false);
+ }
+ };
+
+ // Handle modal open/close lifecycle
+ useEffect(() => {
+ if (isOpen && !isInitialOpen) {
+ // First time opening - reset everything and load data
+ setFormData({
+ modelName: userConfig?.name || 'default',
+ maxTokens: userConfig?.max_tokens?.toString() || '',
+ temperature: userConfig?.temperature?.toString() || '',
+ reasoningEffort: userConfig?.reasoning_effort || 'default',
+ fallbackModel: userConfig?.fallbackModel || 'default'
+ });
+ setHasChanges(false);
+ setByokModalOpen(false);
+ setIsInitialOpen(true);
+ loadByokData();
+ } else if (!isOpen && isInitialOpen) {
+ // Modal closed - reset for next time
+ setIsInitialOpen(false);
+ }
+ }, [isOpen, isInitialOpen, userConfig]);
+
+ // Load BYOK data when modal opens
+ useEffect(() => {
+ if (byokData && isInitialOpen) {
+ // BYOK data is loaded, ready for model selection
+ }
+ }, [byokData, isInitialOpen, userConfig?.name]);
+
+ // Check for changes
+ useEffect(() => {
+ const originalFormData = {
+ modelName: userConfig?.name || 'default',
+ maxTokens: userConfig?.max_tokens?.toString() || '',
+ temperature: userConfig?.temperature?.toString() || '',
+ reasoningEffort: userConfig?.reasoning_effort || 'default',
+ fallbackModel: userConfig?.fallbackModel || 'default'
+ };
+
+ setHasChanges(JSON.stringify(formData) !== JSON.stringify(originalFormData));
+ }, [formData, userConfig]);
+
+ // Get unified model list with BYOK status info
+ const availableModels = useMemo(() => {
+ if (!byokData) return [];
+
+ const models: { value: string; label: string; provider: string; hasUserKey: boolean; byokAvailable: boolean }[] = [];
+ const processedModels = new Set();
+
+ // First, add all BYOK models (they have BYOK capability)
+ Object.values(byokData.modelsByProvider).forEach(providerModels => {
+ providerModels.forEach(model => {
+ const modelStr = model as string;
+ if (!processedModels.has(modelStr)) {
+ const provider = getProviderFromModel(modelStr);
+ const hasUserKey = hasUserKeyForModel(modelStr, byokData.providers);
+
+ models.push({
+ value: modelStr,
+ label: getModelDisplayName(modelStr),
+ provider,
+ hasUserKey,
+ byokAvailable: true
+ });
+ processedModels.add(modelStr);
+ }
+ });
+ });
+
+ // Then, add platform-only models (no BYOK capability)
+ byokData.platformModels.forEach(model => {
+ const modelStr = model as string;
+ if (!processedModels.has(modelStr)) {
+ models.push({
+ value: modelStr,
+ label: getModelDisplayName(modelStr),
+ provider: '',
+ hasUserKey: false,
+ byokAvailable: false
+ });
+ processedModels.add(modelStr);
+ }
+ });
+
+ return models.sort((a, b) => a.label.localeCompare(b.label));
+ }, [byokData]);
+
+ // Get current model's BYOK status
+ const selectedModelInfo = useMemo(() => {
+ const currentModel = formData.modelName && formData.modelName !== 'default'
+ ? formData.modelName
+ : '';
+
+ if (!currentModel || !byokData) {
+ return { hasUserKey: false, provider: '', requiresBYOK: false, isPlatformModel: true };
+ }
+
+ // Check if this is a BYOK-capable model
+ const isByokModel = Object.values(byokData.modelsByProvider).some(providerModels =>
+ providerModels.some(model => model === currentModel)
+ );
+
+ const provider = getProviderFromModel(currentModel);
+ const hasUserKey = hasUserKeyForModel(currentModel, byokData.providers);
+
+ return {
+ hasUserKey,
+ provider,
+ requiresBYOK: isByokModel && !hasUserKey, // Only BYOK-capable models can require keys
+ isPlatformModel: !isByokModel
+ };
+ }, [formData.modelName, byokData]);
+
+ // Create config object from current form state
+ const buildCurrentConfig = (): ModelConfigUpdate => {
+ return {
+ ...(formData.modelName !== 'default' && { modelName: formData.modelName }),
+ ...(formData.maxTokens && { maxTokens: parseInt(formData.maxTokens) }),
+ ...(formData.temperature && { temperature: parseFloat(formData.temperature) }),
+ ...(formData.reasoningEffort !== 'default' && { reasoningEffort: formData.reasoningEffort }),
+ ...(formData.fallbackModel !== 'default' && { fallbackModel: formData.fallbackModel }),
+ isUserOverride: true
+ };
+ };
+
+ const handleSave = async () => {
+ const config = buildCurrentConfig();
+ await onSave(config);
+ };
+
+ const handleTestWithCurrentConfig = async () => {
+ const currentConfig = buildCurrentConfig();
+ // We'll need to update the parent component to handle testing with temporary config
+ await onTest(currentConfig);
+ };
+
+ const handleReset = async () => {
+ await onReset();
+ onClose();
+ };
+
+ const openByokModal = () => {
+ setByokModalOpen(true);
+ };
+
+ const handleByokKeyAdded = () => {
+ // Refresh BYOK data after a key is added
+ loadByokData();
+ };
+
+ const isUserOverride = userConfig?.isUserOverride || false;
+
+ return (
+
+
+
+
+
+ Configure {agentConfig.name}
+
+
+ {agentConfig.description}
+ {getModelRecommendation(agentConfig.key) && (
+
+
+
+ {getModelRecommendation(agentConfig.key)}
+
+
+ )}
+
+
+
+
+ {/* Current Status */}
+
+
+
Configuration Status
+
+ {isUserOverride ? 'Using custom configuration' : 'Using system defaults'}
+
+
+
+ {isUserOverride ? "Custom" : "Default"}
+
+
+
+
+ {/* Model Selection Section */}
+
+
+