diff --git a/.flox/env/manifest.lock b/.flox/env/manifest.lock
index 2a61fe55..9c0b15a5 100644
--- a/.flox/env/manifest.lock
+++ b/.flox/env/manifest.lock
@@ -43,6 +43,10 @@
"pkg-path": "nodejs_24",
"version": "^24.11.1"
},
+ "nodejs_24": {
+ "pkg-path": "nodejs_24",
+ "version": "24.11.1"
+ },
"pre-commit": {
"pkg-path": "pre-commit",
"version": "^4.5.0"
@@ -1241,6 +1245,130 @@
"group": "toplevel",
"priority": 5
},
+ {
+ "attr_path": "nodejs_24",
+ "broken": false,
+ "derivation": "/nix/store/vf9p8gxd4dydpbghi45a369ba9gwxvg4-nodejs-24.11.1.drv",
+ "description": "Event-driven I/O framework for the V8 JavaScript engine",
+ "install_id": "nodejs_24",
+ "license": "MIT",
+ "locked_url": "https://github.com/flox/nixpkgs?rev=1306659b587dc277866c7b69eb97e5f07864d8c4",
+ "name": "nodejs-24.11.1",
+ "pname": "nodejs_24",
+ "rev": "1306659b587dc277866c7b69eb97e5f07864d8c4",
+ "rev_count": 912002,
+ "rev_date": "2025-12-15T06:20:37Z",
+ "scrape_date": "2025-12-16T03:01:53.867688Z",
+ "stabilities": [
+ "unstable"
+ ],
+ "unfree": false,
+ "version": "24.11.1",
+ "outputs_to_install": [
+ "out"
+ ],
+ "outputs": {
+ "dev": "/nix/store/akmrc34ry4wil7wq8ghxbh2dpvikschh-nodejs-24.11.1-dev",
+ "libv8": "/nix/store/h4ad12n85c04b2c5c1a4r8r5qr4i1afn-nodejs-24.11.1-libv8",
+ "out": "/nix/store/h57ln4r40qr349z1r8ri90n9i9xid3r5-nodejs-24.11.1"
+ },
+ "system": "aarch64-darwin",
+ "group": "toplevel",
+ "priority": 5
+ },
+ {
+ "attr_path": "nodejs_24",
+ "broken": false,
+ "derivation": "/nix/store/8njzr7lnj4w452wh84bilva49bwgz9lw-nodejs-24.11.1.drv",
+ "description": "Event-driven I/O framework for the V8 JavaScript engine",
+ "install_id": "nodejs_24",
+ "license": "MIT",
+ "locked_url": "https://github.com/flox/nixpkgs?rev=1306659b587dc277866c7b69eb97e5f07864d8c4",
+ "name": "nodejs-24.11.1",
+ "pname": "nodejs_24",
+ "rev": "1306659b587dc277866c7b69eb97e5f07864d8c4",
+ "rev_count": 912002,
+ "rev_date": "2025-12-15T06:20:37Z",
+ "scrape_date": "2025-12-16T03:18:11.985214Z",
+ "stabilities": [
+ "unstable"
+ ],
+ "unfree": false,
+ "version": "24.11.1",
+ "outputs_to_install": [
+ "out"
+ ],
+ "outputs": {
+ "dev": "/nix/store/82gbs5sgm1gpggsalpdlnxjkvr7bnkcc-nodejs-24.11.1-dev",
+ "libv8": "/nix/store/7ihzshxrnl1xh935ff8va57aw1avwk5j-nodejs-24.11.1-libv8",
+ "out": "/nix/store/r46dnkahacvrnavv5945aizq8b61kbwc-nodejs-24.11.1"
+ },
+ "system": "aarch64-linux",
+ "group": "toplevel",
+ "priority": 5
+ },
+ {
+ "attr_path": "nodejs_24",
+ "broken": false,
+ "derivation": "/nix/store/krwzsq16irazaldn5ygdkx7zfpbybxba-nodejs-24.11.1.drv",
+ "description": "Event-driven I/O framework for the V8 JavaScript engine",
+ "install_id": "nodejs_24",
+ "license": "MIT",
+ "locked_url": "https://github.com/flox/nixpkgs?rev=1306659b587dc277866c7b69eb97e5f07864d8c4",
+ "name": "nodejs-24.11.1",
+ "pname": "nodejs_24",
+ "rev": "1306659b587dc277866c7b69eb97e5f07864d8c4",
+ "rev_count": 912002,
+ "rev_date": "2025-12-15T06:20:37Z",
+ "scrape_date": "2025-12-16T03:34:11.125879Z",
+ "stabilities": [
+ "unstable"
+ ],
+ "unfree": false,
+ "version": "24.11.1",
+ "outputs_to_install": [
+ "out"
+ ],
+ "outputs": {
+ "dev": "/nix/store/xj8y0v2v5ssdxbpz1bz9yivn109g3i4x-nodejs-24.11.1-dev",
+ "libv8": "/nix/store/136306lidz9kx8dlka3l2sf1q9i6ilc3-nodejs-24.11.1-libv8",
+ "out": "/nix/store/68d27bbipwcv99inwbs6fb7vxbgd4q6j-nodejs-24.11.1"
+ },
+ "system": "x86_64-darwin",
+ "group": "toplevel",
+ "priority": 5
+ },
+ {
+ "attr_path": "nodejs_24",
+ "broken": false,
+ "derivation": "/nix/store/n15i8fv8a00hy7ng11km2lr8wvgz552w-nodejs-24.11.1.drv",
+ "description": "Event-driven I/O framework for the V8 JavaScript engine",
+ "install_id": "nodejs_24",
+ "license": "MIT",
+ "locked_url": "https://github.com/flox/nixpkgs?rev=1306659b587dc277866c7b69eb97e5f07864d8c4",
+ "name": "nodejs-24.11.1",
+ "pname": "nodejs_24",
+ "rev": "1306659b587dc277866c7b69eb97e5f07864d8c4",
+ "rev_count": 912002,
+ "rev_date": "2025-12-15T06:20:37Z",
+ "scrape_date": "2025-12-16T03:50:36.781636Z",
+ "stabilities": [
+ "unstable"
+ ],
+ "unfree": false,
+ "version": "24.11.1",
+ "outputs_to_install": [
+ "out"
+ ],
+ "outputs": {
+ "dev": "/nix/store/qayyvn6vq1yx49lr30kkrpdl1jy99752-nodejs-24.11.1-dev",
+ "libv8": "/nix/store/i9s2w9xib1fbqrc2qmmyz17frhpvkmsk-nodejs-24.11.1-libv8",
+ "out": "/nix/store/lgggxsrdzisnbligi7irlh4qmqczs0xk-nodejs-24.11.1"
+ },
+ "system": "x86_64-linux",
+ "group": "toplevel",
+ "priority": 5
+ },
{
"attr_path": "pre-commit",
"broken": false,
diff --git a/.flox/env/manifest.toml b/.flox/env/manifest.toml
index 26a7303c..f634057b 100644
--- a/.flox/env/manifest.toml
+++ b/.flox/env/manifest.toml
@@ -34,6 +34,8 @@ docker.pkg-path = "docker"
docker.version = "^29.1.2"
docker-compose.pkg-path = "docker-compose"
docker-compose.version = "^5.0.0"
+nodejs_24.pkg-path = "nodejs_24"
+nodejs_24.version = "24.11.1"
[hook]
on-activate = """
diff --git a/.infer/agents.yaml b/.infer/agents.yaml
index a23ff1be..e2694bff 100644
--- a/.infer/agents.yaml
+++ b/.infer/agents.yaml
@@ -1,6 +1,6 @@
agents:
- name: mock-agent
- url: http://localhost:8081
+ url: http://localhost:8082
oci: ghcr.io/inference-gateway/mock-agent:latest
run: true
model: deepseek/deepseek-chat
diff --git a/.infer/config.yaml b/.infer/config.yaml
index 787ff16b..dfaf3c32 100644
--- a/.infer/config.yaml
+++ b/.infer/config.yaml
@@ -596,6 +596,32 @@ mcp:
liveness_probe_interval: 10
max_retries: 10
servers: []
+api:
+ host: 127.0.0.1
+ port: 8081
+ read_timeout: 30
+ write_timeout: 30
+ idle_timeout: 120
+ cors:
+ enabled: true
+ allowed_origins:
+ - http://localhost:3000
+ - http://localhost:3001
+ allowed_methods:
+ - GET
+ - POST
+ - PUT
+ - PATCH
+ - DELETE
+ - OPTIONS
+ allowed_headers:
+ - Content-Type
+ - Authorization
+ ui:
+ port: 3000
+ auto_open: true
+ mode: npm
+ working_dir: ./ui
pricing:
enabled: true
currency: USD
diff --git a/Taskfile.yml b/Taskfile.yml
index 5f4efa76..9121fe8b 100644
--- a/Taskfile.yml
+++ b/Taskfile.yml
@@ -63,7 +63,7 @@ tasks:
desc: Run linter (requires golangci-lint)
cmds:
- golangci-lint run
- - markdownlint . --ignore CHANGELOG.md --fix
+ - markdownlint . --ignore CHANGELOG.md --ignore ui/node_modules --fix
fmt:
desc: Format Go code
diff --git a/cmd/agents.go b/cmd/agents.go
index ecf2e0d3..61b2ef9e 100644
--- a/cmd/agents.go
+++ b/cmd/agents.go
@@ -425,7 +425,7 @@ func listAgents(cmd *cobra.Command, args []string) error {
format, _ := cmd.Flags().GetString("format")
if format == "json" {
- combinedOutput := map[string]interface{}{
+ combinedOutput := map[string]any{
"local": localAgents,
"external": externalAgents,
"total": totalAgents,
diff --git a/cmd/chat.go b/cmd/chat.go
index 93cc7745..f0a7acc4 100644
--- a/cmd/chat.go
+++ b/cmd/chat.go
@@ -15,6 +15,8 @@ import (
clipboard "github.com/inference-gateway/cli/internal/clipboard"
container "github.com/inference-gateway/cli/internal/container"
domain "github.com/inference-gateway/cli/internal/domain"
+ handlers "github.com/inference-gateway/cli/internal/handlers"
+ logger "github.com/inference-gateway/cli/internal/logger"
sdk "github.com/inference-gateway/sdk"
cobra "github.com/spf13/cobra"
viper "github.com/spf13/viper"
@@ -25,12 +27,20 @@ var chatCmd = &cobra.Command{
Short: "Start an interactive chat session with model selection",
Long: `Start an interactive chat session where you can select a model from a dropdown
and have a conversational interface with the inference gateway.`,
- RunE: func(_ *cobra.Command, args []string) error {
+ RunE: func(cmd *cobra.Command, args []string) error {
cfg, err := getConfigFromViper()
if err != nil {
return fmt.Errorf("failed to load config: %w", err)
}
+ headless, _ := cmd.Flags().GetBool("headless")
+ sessionID, _ := cmd.Flags().GetString("session-id")
+ conversationID, _ := cmd.Flags().GetString("conversation-id")
+
+ if headless {
+ return runHeadlessChat(cfg, V, sessionID, conversationID)
+ }
+
if !isInteractiveTerminal() {
return runNonInteractiveChat(cfg, V)
}
@@ -56,6 +66,23 @@ func StartChatSession(cfg *config.Config, v *viper.Viper) error {
fmt.Printf(" Make sure the inference gateway is running at: %s\n\n", cfg.Gateway.URL)
}
+ agentManager := services.GetAgentManager()
+ if agentManager != nil {
+ agentCtx := context.Background()
+ if err := agentManager.StartAgents(agentCtx); err != nil {
+ logger.Error("Failed to start agents", "error", err)
+ }
+ }
+
+ mcpManager := services.GetMCPManager()
+ if mcpManager != nil {
+ mcpCtx, mcpCancel := context.WithTimeout(context.Background(), 120*time.Second)
+ defer mcpCancel()
+ if err := mcpManager.StartServers(mcpCtx); err != nil {
+ logger.Error("Some MCP servers failed to start", "error", err)
+ }
+ }
+
ctx, cancel := context.WithTimeout(context.Background(), time.Duration(cfg.Gateway.Timeout)*time.Second)
defer cancel()
@@ -86,10 +113,8 @@ func StartChatSession(cfg *config.Config, v *viper.Viper) error {
messageQueue := services.GetMessageQueue()
themeService := services.GetThemeService()
toolRegistry := services.GetToolRegistry()
- mcpManager := services.GetMCPManager()
taskRetentionService := services.GetTaskRetentionService()
backgroundTaskService := services.GetBackgroundTaskService()
- agentManager := services.GetAgentManager()
conversationOptimizer := services.GetConversationOptimizer()
versionInfo := GetVersionInfo()
@@ -300,6 +325,22 @@ func processStreamingOutput(events <-chan domain.ChatEvent) error {
return nil
}
+// runHeadlessChat runs the chat in headless mode (JSON I/O via stdin/stdout)
+func runHeadlessChat(cfg *config.Config, v *viper.Viper, sessionID string, conversationID string) error {
+ services := container.NewServiceContainer(cfg, v)
+
+ handler := handlers.NewHeadlessHandler(sessionID, conversationID, services, cfg)
+ defer func() {
+ _ = handler.Shutdown()
+ }()
+
+ return handler.Start()
+}
+
func init() {
rootCmd.AddCommand(chatCmd)
+
+ chatCmd.Flags().Bool("headless", false, "Run in headless mode (JSON I/O via stdin/stdout)")
+ chatCmd.Flags().String("session-id", "", "Session identifier for headless mode")
+ chatCmd.Flags().String("conversation-id", "", "Conversation ID to continue (for headless mode)")
}
diff --git a/cmd/root.go b/cmd/root.go
index 050b3130..4d16e685 100644
--- a/cmd/root.go
+++ b/cmd/root.go
@@ -120,6 +120,16 @@ func initConfig() { // nolint:funlen
v.SetDefault("tools.web_fetch.safety.timeout", defaults.Tools.WebFetch.Safety.Timeout)
v.SetDefault("tools.web_fetch.safety.allow_redirect", defaults.Tools.WebFetch.Safety.AllowRedirect)
v.SetDefault("tools.web_fetch.require_approval", defaults.Tools.WebFetch.RequireApproval)
+ v.SetDefault("api", defaults.API)
+ v.SetDefault("api.host", defaults.API.Host)
+ v.SetDefault("api.port", defaults.API.Port)
+ v.SetDefault("api.read_timeout", defaults.API.ReadTimeout)
+ v.SetDefault("api.write_timeout", defaults.API.WriteTimeout)
+ v.SetDefault("api.idle_timeout", defaults.API.IdleTimeout)
+ v.SetDefault("api.cors.enabled", defaults.API.CORS.Enabled)
+ v.SetDefault("api.cors.allowed_origins", defaults.API.CORS.AllowedOrigins)
+ v.SetDefault("api.cors.allowed_methods", defaults.API.CORS.AllowedMethods)
+ v.SetDefault("api.cors.allowed_headers", defaults.API.CORS.AllowedHeaders)
v.SetDefault("pricing", defaults.Pricing)
v.SetDefault("pricing.enabled", defaults.Pricing.Enabled)
v.SetDefault("pricing.currency", defaults.Pricing.Currency)
diff --git a/cmd/serve.go b/cmd/serve.go
new file mode 100644
index 00000000..34c98b74
--- /dev/null
+++ b/cmd/serve.go
@@ -0,0 +1,331 @@
+package cmd
+
+import (
+ "context"
+ "fmt"
+ "net/http"
+ "os"
+ "os/signal"
+ "syscall"
+ "time"
+
+ config "github.com/inference-gateway/cli/config"
+ container "github.com/inference-gateway/cli/internal/container"
+ handlers "github.com/inference-gateway/cli/internal/handlers"
+ storage "github.com/inference-gateway/cli/internal/infra/storage"
+ logger "github.com/inference-gateway/cli/internal/logger"
+ svc "github.com/inference-gateway/cli/internal/services"
+ history "github.com/inference-gateway/cli/internal/ui/history"
+ utils "github.com/inference-gateway/cli/internal/utils"
+ cobra "github.com/spf13/cobra"
+)
+
+var serveCmd = &cobra.Command{
+ Use: "serve",
+ Short: "Start the API server for conversation storage queries",
+ Long: `Start an HTTP API server that exposes REST endpoints for accessing conversation storage.
+This allows the UI and other clients to query conversations, statistics, and manage stored data
+without requiring direct database access.
+
+The API server provides:
+ - Conversation listing and querying
+ - Conversation statistics and analytics
+ - Metadata management
+ - Health checks
+
+All headless chat sessions continue to save their conversations directly to the configured
+storage backend (JSONL, SQLite, PostgreSQL, or Redis).`,
+ RunE: func(cmd *cobra.Command, args []string) error {
+ cfg, err := getConfigFromViper()
+ if err != nil {
+ return fmt.Errorf("failed to load config: %w", err)
+ }
+
+ port, _ := cmd.Flags().GetInt("port")
+ host, _ := cmd.Flags().GetString("host")
+ enableUI, _ := cmd.Flags().GetBool("ui")
+
+ if port != 0 {
+ cfg.API.Port = port
+ }
+ if host != "" {
+ cfg.API.Host = host
+ }
+
+ return startAPIServer(cfg, V, enableUI)
+ },
+}
+
+func init() {
+ rootCmd.AddCommand(serveCmd)
+
+ serveCmd.Flags().Int("port", 0, "API server port (default: 8080)")
+ serveCmd.Flags().String("host", "", "API server host (default: 127.0.0.1)")
+ serveCmd.Flags().Bool("ui", false, "Start the web UI and automatically open it in browser")
+}
+
+func startAPIServer(cfg *config.Config, v any, enableUI bool) error {
+ services, uiManager := initializeServices(cfg, enableUI)
+ defer cleanupServices(services, uiManager)
+
+ if err := checkStorageHealth(services.GetStorage()); err != nil {
+ logger.Warn("Storage health check failed", "error", err)
+ fmt.Printf("Warning: Storage backend may not be available: %v\n", err)
+ }
+
+ server, serverReady, serverErrors := setupAndStartServer(cfg, services)
+ handleUIStartup(cfg, uiManager, serverReady, enableUI)
+
+ return waitForShutdown(server, serverErrors)
+}
+
+func initializeServices(cfg *config.Config, enableUI bool) (*container.ServiceContainer, *svc.UIManager) {
+ services := container.NewServiceContainer(cfg, V)
+
+ var uiManager *svc.UIManager
+ if enableUI {
+ uiManager = svc.NewUIManager(cfg)
+ }
+
+ if agentManager := services.GetAgentManager(); agentManager != nil {
+ ctx := context.Background()
+ if err := agentManager.StartAgents(ctx); err != nil {
+ logger.Error("Failed to start agents", "error", err)
+ }
+ }
+
+ if mcpManager := services.GetMCPManager(); mcpManager != nil {
+ ctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)
+ defer cancel()
+ if err := mcpManager.StartServers(ctx); err != nil {
+ logger.Error("Some MCP servers failed to start", "error", err)
+ }
+ }
+
+ return services, uiManager
+}
+
+func cleanupServices(services *container.ServiceContainer, uiManager *svc.UIManager) {
+ if uiManager != nil && uiManager.IsRunning() {
+ _ = uiManager.Stop()
+ }
+
+ ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
+ defer cancel()
+ _ = services.Shutdown(ctx)
+}
+
+func checkStorageHealth(strg storage.ConversationStorage) error {
+ ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
+ defer cancel()
+ return strg.Health(ctx)
+}
+
+func setupAndStartServer(cfg *config.Config, services *container.ServiceContainer) (*http.Server, chan struct{}, chan error) {
+ historyManager, err := history.NewHistoryManager(1000)
+ if err != nil {
+ logger.Warn("Failed to initialize history manager", "error", err)
+ historyManager = nil
+ }
+
+ apiHandler := handlers.NewAPIHandler(
+ services.GetStorage(),
+ services.GetModelService(),
+ services.GetStateManager(),
+ services.GetMCPManager(),
+ historyManager,
+ )
+
+ sessionManager := handlers.NewSessionManager()
+ wsHandler := handlers.NewWebSocketHandler(sessionManager)
+
+ handler := setupHTTPHandlers(cfg, apiHandler, wsHandler)
+ addr := fmt.Sprintf("%s:%d", cfg.API.Host, cfg.API.Port)
+ server := &http.Server{
+ Addr: addr,
+ Handler: handler,
+ ReadTimeout: time.Duration(cfg.API.ReadTimeout) * time.Second,
+ WriteTimeout: time.Duration(cfg.API.WriteTimeout) * time.Second,
+ IdleTimeout: time.Duration(cfg.API.IdleTimeout) * time.Second,
+ }
+
+ serverErrors := make(chan error, 1)
+ serverReady := make(chan struct{})
+
+ go startServerAsync(server, addr, cfg, serverErrors, serverReady)
+
+ return server, serverReady, serverErrors
+}
+
+func setupHTTPHandlers(cfg *config.Config, apiHandler *handlers.APIHandler, wsHandler *handlers.WebSocketHandler) http.Handler {
+ mux := http.NewServeMux()
+
+ mux.HandleFunc("/health", apiHandler.HandleHealth)
+ mux.HandleFunc("/ws", wsHandler.HandleWebSocket)
+ mux.HandleFunc("/api/v1/models", apiHandler.HandleListModels)
+ mux.HandleFunc("/api/v1/conversations", apiHandler.HandleListConversations)
+ mux.HandleFunc("/api/v1/conversations/", apiHandler.HandleConversationByID)
+ mux.HandleFunc("/api/v1/conversations/needs-titles", apiHandler.HandleConversationsNeedingTitles)
+ mux.HandleFunc("/api/v1/agents/status", apiHandler.HandleAgentsStatus)
+ mux.HandleFunc("/api/v1/mcp/status", apiHandler.HandleMCPStatus)
+ mux.HandleFunc("/api/history", apiHandler.HandleHistory)
+
+ handler := http.Handler(mux)
+ if cfg.API.CORS.Enabled {
+ handler = enableCORS(handler, cfg.API.CORS)
+ }
+
+ return handler
+}
+
+func startServerAsync(server *http.Server, addr string, cfg *config.Config, serverErrors chan error, serverReady chan struct{}) {
+ logger.Info("Starting API server", "address", addr)
+
+ go func() {
+ serverErrors <- server.ListenAndServe()
+ }()
+
+ if waitForServerReady(addr) {
+ printServerInfo(addr, cfg)
+ close(serverReady)
+ }
+}
+
+func waitForServerReady(addr string) bool {
+ apiURL := fmt.Sprintf("http://%s/health", addr)
+ for i := 0; i < 20; i++ {
+ time.Sleep(100 * time.Millisecond)
+ resp, err := http.Get(apiURL)
+ if err == nil {
+ if closeErr := resp.Body.Close(); closeErr != nil {
+ logger.Warn("Failed to close response body", "error", closeErr)
+ }
+ if resp.StatusCode == http.StatusOK {
+ return true
+ }
+ }
+ }
+ return false
+}
+
+func printServerInfo(addr string, cfg *config.Config) {
+ fmt.Printf("API server listening on http://%s\n", addr)
+ fmt.Printf(" Storage: %s\n", cfg.Storage.Type)
+ fmt.Printf("\nAvailable endpoints:\n")
+ fmt.Printf(" GET /health - Health check\n")
+ fmt.Printf(" WS /ws - WebSocket for live chat\n")
+ fmt.Printf(" GET /api/v1/models - List available models\n")
+ fmt.Printf(" GET /api/v1/conversations - List conversations\n")
+ fmt.Printf(" GET /api/v1/conversations/:id - Get conversation\n")
+ fmt.Printf(" DELETE /api/v1/conversations/:id - Delete conversation\n")
+ fmt.Printf(" PATCH /api/v1/conversations/:id/metadata - Update metadata\n")
+ fmt.Printf(" GET /api/v1/conversations/needs-titles - List conversations needing titles\n")
+ fmt.Printf(" GET /api/v1/agents/status - Get A2A agents status\n")
+ fmt.Printf(" GET /api/v1/mcp/status - Get MCP servers status\n")
+ fmt.Printf(" GET /api/history - Get command history\n")
+ fmt.Printf(" POST /api/history - Save command to history\n\n")
+}
+
+func handleUIStartup(cfg *config.Config, uiManager *svc.UIManager, serverReady chan struct{}, enableUI bool) {
+ if !enableUI || uiManager == nil {
+ return
+ }
+
+ go func() {
+ <-serverReady
+
+ ctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)
+ defer cancel()
+
+ if err := uiManager.Start(ctx); err != nil {
+ logger.Error("Failed to start UI server", "error", err)
+ fmt.Printf("Warning: UI server failed to start: %v\n", err)
+ return
+ }
+
+ if cfg.API.UI.AutoOpen {
+ openBrowserForUI(uiManager)
+ }
+ }()
+}
+
+func openBrowserForUI(uiManager *svc.UIManager) {
+ uiURL := uiManager.GetURL()
+ logger.Info("Opening browser", "url", uiURL)
+ if err := utils.OpenBrowser(uiURL); err != nil {
+ logger.Warn("Failed to open browser", "error", err)
+ fmt.Printf("Could not open browser automatically. Please visit %s\n", uiURL)
+ } else {
+ fmt.Printf("Browser opened at %s\n", uiURL)
+ }
+}
+
+func waitForShutdown(server *http.Server, serverErrors chan error) error {
+ shutdown := make(chan os.Signal, 1)
+ signal.Notify(shutdown, os.Interrupt, syscall.SIGTERM)
+
+ select {
+ case err := <-serverErrors:
+ return fmt.Errorf("server error: %w", err)
+ case sig := <-shutdown:
+ logger.Info("Shutting down API server", "signal", sig)
+ fmt.Printf("\nShutting down gracefully...\n")
+
+ ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
+ defer cancel()
+
+ if err := server.Shutdown(ctx); err != nil {
+ if closeErr := server.Close(); closeErr != nil {
+ logger.Warn("Failed to force close server", "error", closeErr)
+ }
+ return fmt.Errorf("could not stop server gracefully: %w", err)
+ }
+ }
+
+ return nil
+}
+
+// enableCORS wraps the handler with CORS middleware
+func enableCORS(next http.Handler, corsConfig config.CORSConfig) http.Handler {
+ return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ origin := r.Header.Get("Origin")
+
+ allowed := false
+ for _, allowedOrigin := range corsConfig.AllowedOrigins {
+ if allowedOrigin == "*" || allowedOrigin == origin {
+ allowed = true
+ break
+ }
+ }
+
+ if allowed {
+ if origin != "" {
+ w.Header().Set("Access-Control-Allow-Origin", origin)
+ } else if len(corsConfig.AllowedOrigins) > 0 {
+ w.Header().Set("Access-Control-Allow-Origin", corsConfig.AllowedOrigins[0])
+ }
+ w.Header().Set("Access-Control-Allow-Methods", joinStrings(corsConfig.AllowedMethods, ", "))
+ w.Header().Set("Access-Control-Allow-Headers", joinStrings(corsConfig.AllowedHeaders, ", "))
+ w.Header().Set("Access-Control-Max-Age", "86400") // 24 hours
+ }
+
+ if r.Method == "OPTIONS" {
+ w.WriteHeader(http.StatusOK)
+ return
+ }
+
+ next.ServeHTTP(w, r)
+ })
+}
+
+// joinStrings joins a slice of strings with a separator
+func joinStrings(strs []string, sep string) string {
+ if len(strs) == 0 {
+ return ""
+ }
+ result := strs[0]
+ for _, s := range strs[1:] {
+ result += sep + s
+ }
+ return result
+}
diff --git a/config/api.go b/config/api.go
new file mode 100644
index 00000000..afec83a4
--- /dev/null
+++ b/config/api.go
@@ -0,0 +1,28 @@
+package config
+
+// APIConfig contains HTTP API server settings
+type APIConfig struct {
+ Host string `yaml:"host" mapstructure:"host"`
+ Port int `yaml:"port" mapstructure:"port"`
+ ReadTimeout int `yaml:"read_timeout" mapstructure:"read_timeout"`
+ WriteTimeout int `yaml:"write_timeout" mapstructure:"write_timeout"`
+ IdleTimeout int `yaml:"idle_timeout" mapstructure:"idle_timeout"`
+ CORS CORSConfig `yaml:"cors" mapstructure:"cors"`
+ UI UIConfig `yaml:"ui" mapstructure:"ui"`
+}
+
+// CORSConfig contains CORS (Cross-Origin Resource Sharing) settings
+type CORSConfig struct {
+ Enabled bool `yaml:"enabled" mapstructure:"enabled"`
+ AllowedOrigins []string `yaml:"allowed_origins" mapstructure:"allowed_origins"`
+ AllowedMethods []string `yaml:"allowed_methods" mapstructure:"allowed_methods"`
+ AllowedHeaders []string `yaml:"allowed_headers" mapstructure:"allowed_headers"`
+}
+
+// UIConfig contains web UI settings
+type UIConfig struct {
+ Port int `yaml:"port" mapstructure:"port"`
+ AutoOpen bool `yaml:"auto_open" mapstructure:"auto_open"`
+ Mode string `yaml:"mode" mapstructure:"mode"`
+ WorkingDir string `yaml:"working_dir" mapstructure:"working_dir"`
+}
diff --git a/config/config.go b/config/config.go
index cbda45b6..bbaaf5d1 100644
--- a/config/config.go
+++ b/config/config.go
@@ -37,6 +37,7 @@ type Config struct {
Chat ChatConfig `yaml:"chat" mapstructure:"chat"`
A2A A2AConfig `yaml:"a2a" mapstructure:"a2a"`
MCP MCPConfig `yaml:"mcp" mapstructure:"mcp"`
+ API APIConfig `yaml:"api" mapstructure:"api"`
Pricing PricingConfig `yaml:"pricing" mapstructure:"pricing"`
Init InitConfig `yaml:"init" mapstructure:"init"`
Compact CompactConfig `yaml:"compact" mapstructure:"compact"`
@@ -887,7 +888,26 @@ Respond with ONLY the title, no quotes or explanation.`,
},
},
},
- MCP: *DefaultMCPConfig(),
+ MCP: *DefaultMCPConfig(),
+ API: APIConfig{
+ Host: "127.0.0.1",
+ Port: 8081,
+ ReadTimeout: 30,
+ WriteTimeout: 30,
+ IdleTimeout: 120,
+ CORS: CORSConfig{
+ Enabled: true,
+ AllowedOrigins: []string{"http://localhost:3000", "http://localhost:3001"},
+ AllowedMethods: []string{"GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS"},
+ AllowedHeaders: []string{"Content-Type", "Authorization"},
+ },
+ UI: UIConfig{
+ Port: 3000,
+ AutoOpen: true,
+ Mode: "npm",
+ WorkingDir: "./ui",
+ },
+ },
Pricing: GetDefaultPricingConfig(),
Init: InitConfig{
Prompt: `Please analyze this project and generate a comprehensive AGENTS.md file. Start by using the Tree tool to understand the project structure.
diff --git a/go.mod b/go.mod
index 6fbeeed1..7173dafd 100644
--- a/go.mod
+++ b/go.mod
@@ -11,6 +11,7 @@ require (
github.com/charmbracelet/lipgloss v1.1.1-0.20250404203927-76690c660834
github.com/go-redis/redis/v8 v8.11.5
github.com/google/uuid v1.6.0
+ github.com/gorilla/websocket v1.5.3
github.com/inference-gateway/adk v0.16.2
github.com/inference-gateway/sdk v1.14.1
github.com/ledongthuc/pdf v0.0.0-20250511090121-5959a4027728
diff --git a/go.sum b/go.sum
index 81f4d104..32f0ca5a 100644
--- a/go.sum
+++ b/go.sum
@@ -92,6 +92,8 @@ github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/gorilla/css v1.0.1 h1:ntNaBIghp6JmvWnxbZKANoLyuXTPZ4cAMlo6RyhlbO8=
github.com/gorilla/css v1.0.1/go.mod h1:BvnYkspnSzMmwRK+b8/xgNPLiIuNZr6vbZBTPQ2A3b0=
+github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=
+github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM=
github.com/hexops/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSow5/V2vxeg=
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
diff --git a/internal/container/container.go b/internal/container/container.go
index 05437f38..c9081233 100644
--- a/internal/container/container.go
+++ b/internal/container/container.go
@@ -125,7 +125,8 @@ func (c *ServiceContainer) initializeGatewayManager() {
c.gatewayManager = services.NewGatewayManager(c.sessionID, c.config, c.containerRuntime)
}
-// initializeAgentManager creates and starts the agent manager if A2A is enabled
+// initializeAgentManager creates the agent manager if A2A is enabled
+// Note: This does NOT start agents. Caller must explicitly call agentManager.StartAgents(ctx).
func (c *ServiceContainer) initializeAgentManager() {
agentsPath := filepath.Join(config.ConfigDirName, config.AgentsFileName)
c.agentsConfigService = services.NewAgentsConfigService(agentsPath)
@@ -160,11 +161,6 @@ func (c *ServiceContainer) initializeAgentManager() {
c.agentManager.SetStatusCallback(func(agentName string, state domain.AgentState, message string, url string, image string) {
c.stateManager.UpdateAgentStatus(agentName, state, message, url, image)
})
-
- ctx := context.Background()
- if err := c.agentManager.StartAgents(ctx); err != nil {
- logger.Warn("Failed to start agents in background", "error", err)
- }
}
// initializeFileWriterServices creates the new file writer architecture services
@@ -176,38 +172,14 @@ func (c *ServiceContainer) initializeFileWriterServices() {
c.paramExtractor = tools.NewParameterExtractor()
}
-// initializeMCPManager creates and starts MCP manager if enabled
+// initializeMCPManager creates the MCP manager if enabled
+// Note: This does NOT start MCP servers. Caller must explicitly call mcpManager.StartServers(ctx).
func (c *ServiceContainer) initializeMCPManager() {
if !c.config.MCP.Enabled {
return
}
c.mcpManager = services.NewMCPManager(c.sessionID, &c.config.MCP, c.containerRuntime)
-
- hasServersToStart := c.hasAutoStartMCPServers()
- if !hasServersToStart {
- return
- }
-
- logger.Info("Starting MCP servers in background...")
- go func() {
- ctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)
- defer cancel()
-
- if err := c.mcpManager.StartServers(ctx); err != nil {
- logger.Warn("Some MCP servers failed to start", "error", err)
- }
- }()
-}
-
-// hasAutoStartMCPServers checks if any MCP servers are configured for auto-start
-func (c *ServiceContainer) hasAutoStartMCPServers() bool {
- for _, server := range c.config.MCP.Servers {
- if server.Run && server.Enabled {
- return true
- }
- }
- return false
}
// initializeDomainServices creates and wires domain service implementations
diff --git a/internal/domain/chat_events.go b/internal/domain/chat_events.go
index cca9effe..5d5e18bc 100644
--- a/internal/domain/chat_events.go
+++ b/internal/domain/chat_events.go
@@ -4,9 +4,10 @@ import "time"
// ToolInfo represents basic tool information for UI display
type ToolInfo struct {
- CallID string
- Name string
- Status string
+ CallID string
+ Name string
+ Status string
+ Arguments string
}
// BaseChatEvent provides common implementation for ChatEvent interface
@@ -31,6 +32,7 @@ type ToolExecutionProgressEvent struct {
ToolName string
Status string
Message string
+ Result string
}
// BashOutputChunkEvent indicates a new chunk of bash output is available
diff --git a/internal/domain/interfaces.go b/internal/domain/interfaces.go
index 86c162fe..80cd5087 100644
--- a/internal/domain/interfaces.go
+++ b/internal/domain/interfaces.go
@@ -102,6 +102,7 @@ type ConversationRepository interface {
FormatToolResultExpanded(result *ToolExecutionResult, terminalWidth int) string
RemovePendingToolCallByID(toolCallID string)
StartNewConversation(title string) error
+ GetCurrentConversationID() string
DeleteMessagesAfterIndex(index int) error
}
diff --git a/internal/handlers/api_handlers.go b/internal/handlers/api_handlers.go
new file mode 100644
index 00000000..98fb7e4a
--- /dev/null
+++ b/internal/handlers/api_handlers.go
@@ -0,0 +1,464 @@
+package handlers
+
+import (
+ "context"
+ "encoding/json"
+ "fmt"
+ "net/http"
+ "strconv"
+ "strings"
+ "time"
+
+ domain "github.com/inference-gateway/cli/internal/domain"
+ storage "github.com/inference-gateway/cli/internal/infra/storage"
+ logger "github.com/inference-gateway/cli/internal/logger"
+ history "github.com/inference-gateway/cli/internal/ui/history"
+)
+
+// APIHandler handles HTTP API requests for conversation storage
+type APIHandler struct {
+ storage storage.ConversationStorage
+ modelService domain.ModelService
+ stateManager domain.StateManager
+ mcpManager domain.MCPManager
+ historyManager *history.HistoryManager
+}
+
+// NewAPIHandler creates a new API handler
+func NewAPIHandler(
+ storage storage.ConversationStorage,
+ modelService domain.ModelService,
+ stateManager domain.StateManager,
+ mcpManager domain.MCPManager,
+ historyManager *history.HistoryManager,
+) *APIHandler {
+ return &APIHandler{
+ storage: storage,
+ modelService: modelService,
+ stateManager: stateManager,
+ mcpManager: mcpManager,
+ historyManager: historyManager,
+ }
+}
+
+// writeJSON writes a JSON response and logs errors
+func (h *APIHandler) writeJSON(w http.ResponseWriter, statusCode int, data any) {
+ w.Header().Set("Content-Type", "application/json")
+ w.WriteHeader(statusCode)
+ if err := json.NewEncoder(w).Encode(data); err != nil {
+ logger.Error("Failed to encode JSON response", "error", err)
+ }
+}
+
+// HandleHealth handles health check requests
+func (h *APIHandler) HandleHealth(w http.ResponseWriter, r *http.Request) {
+ if r.Method != http.MethodGet {
+ http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
+ return
+ }
+
+ ctx, cancel := context.WithTimeout(r.Context(), 5*time.Second)
+ defer cancel()
+
+ err := h.storage.Health(ctx)
+ if err != nil {
+ logger.Error("Storage health check failed", "error", err)
+ h.writeJSON(w, http.StatusServiceUnavailable, map[string]any{
+ "status": "unhealthy",
+ "error": err.Error(),
+ "time": time.Now().UTC().Format(time.RFC3339),
+ })
+ return
+ }
+
+ h.writeJSON(w, http.StatusOK, map[string]any{
+ "status": "healthy",
+ "time": time.Now().UTC().Format(time.RFC3339),
+ })
+}
+
+// HandleListConversations handles GET /api/v1/conversations
+func (h *APIHandler) HandleListConversations(w http.ResponseWriter, r *http.Request) {
+ if r.Method != http.MethodGet {
+ http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
+ return
+ }
+
+ limit := 50
+ offset := 0
+
+ if limitStr := r.URL.Query().Get("limit"); limitStr != "" {
+ if l, err := strconv.Atoi(limitStr); err == nil && l > 0 {
+ limit = l
+ }
+ }
+
+ if offsetStr := r.URL.Query().Get("offset"); offsetStr != "" {
+ if o, err := strconv.Atoi(offsetStr); err == nil && o >= 0 {
+ offset = o
+ }
+ }
+
+ ctx, cancel := context.WithTimeout(r.Context(), 30*time.Second)
+ defer cancel()
+
+ conversations, err := h.storage.ListConversations(ctx, limit, offset)
+ if err != nil {
+ logger.Error("Failed to list conversations", "error", err)
+ h.writeJSON(w, http.StatusInternalServerError, map[string]any{
+ "error": fmt.Sprintf("Failed to list conversations: %v", err),
+ })
+ return
+ }
+
+ h.writeJSON(w, http.StatusOK, map[string]any{
+ "conversations": conversations,
+ "count": len(conversations),
+ "limit": limit,
+ "offset": offset,
+ })
+}
+
+// HandleConversationByID handles conversation-specific operations
+// GET /api/v1/conversations/:id - Get conversation
+// DELETE /api/v1/conversations/:id - Delete conversation
+// PATCH /api/v1/conversations/:id/metadata - Update metadata
+func (h *APIHandler) HandleConversationByID(w http.ResponseWriter, r *http.Request) {
+ path := strings.TrimPrefix(r.URL.Path, "/api/v1/conversations/")
+ if path == "" {
+ http.Error(w, "Conversation ID required", http.StatusBadRequest)
+ return
+ }
+
+ parts := strings.Split(path, "/")
+ conversationID := parts[0]
+
+ if len(parts) > 1 && parts[1] == "metadata" {
+ if r.Method == http.MethodPatch {
+ h.handleUpdateMetadata(w, r, conversationID)
+ return
+ }
+ http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
+ return
+ }
+
+ switch r.Method {
+ case http.MethodGet:
+ h.handleGetConversation(w, r, conversationID)
+ case http.MethodDelete:
+ h.handleDeleteConversation(w, r, conversationID)
+ default:
+ http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
+ }
+}
+
+// handleGetConversation retrieves a specific conversation
+func (h *APIHandler) handleGetConversation(w http.ResponseWriter, r *http.Request, conversationID string) {
+ ctx, cancel := context.WithTimeout(r.Context(), 30*time.Second)
+ defer cancel()
+
+ entries, metadata, err := h.storage.LoadConversation(ctx, conversationID)
+ if err != nil {
+ logger.Error("Failed to load conversation", "id", conversationID, "error", err)
+ h.writeJSON(w, http.StatusNotFound, map[string]any{
+ "error": fmt.Sprintf("Conversation not found: %v", err),
+ })
+ return
+ }
+
+ h.writeJSON(w, http.StatusOK, map[string]any{
+ "id": conversationID,
+ "entries": entries,
+ "metadata": metadata,
+ })
+}
+
+// handleDeleteConversation deletes a conversation
+func (h *APIHandler) handleDeleteConversation(w http.ResponseWriter, r *http.Request, conversationID string) {
+ ctx, cancel := context.WithTimeout(r.Context(), 30*time.Second)
+ defer cancel()
+
+ err := h.storage.DeleteConversation(ctx, conversationID)
+ if err != nil {
+ logger.Error("Failed to delete conversation", "id", conversationID, "error", err)
+ h.writeJSON(w, http.StatusInternalServerError, map[string]any{
+ "error": fmt.Sprintf("Failed to delete conversation: %v", err),
+ })
+ return
+ }
+
+ h.writeJSON(w, http.StatusOK, map[string]any{
+ "success": true,
+ "message": "Conversation deleted successfully",
+ })
+}
+
+// handleUpdateMetadata updates conversation metadata
+func (h *APIHandler) handleUpdateMetadata(w http.ResponseWriter, r *http.Request, conversationID string) {
+ var updates storage.ConversationMetadata
+ if err := json.NewDecoder(r.Body).Decode(&updates); err != nil {
+ http.Error(w, fmt.Sprintf("Invalid request body: %v", err), http.StatusBadRequest)
+ return
+ }
+
+ updates.ID = conversationID
+
+ ctx, cancel := context.WithTimeout(r.Context(), 30*time.Second)
+ defer cancel()
+
+ err := h.storage.UpdateConversationMetadata(ctx, conversationID, updates)
+ if err != nil {
+ logger.Error("Failed to update conversation metadata", "id", conversationID, "error", err)
+ h.writeJSON(w, http.StatusInternalServerError, map[string]any{
+ "error": fmt.Sprintf("Failed to update metadata: %v", err),
+ })
+ return
+ }
+
+ h.writeJSON(w, http.StatusOK, map[string]any{
+ "success": true,
+ "message": "Metadata updated successfully",
+ })
+}
+
+// HandleConversationsNeedingTitles handles GET /api/v1/conversations/needs-titles
+func (h *APIHandler) HandleConversationsNeedingTitles(w http.ResponseWriter, r *http.Request) {
+ if r.Method != http.MethodGet {
+ http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
+ return
+ }
+
+ limit := 10
+ if limitStr := r.URL.Query().Get("limit"); limitStr != "" {
+ if l, err := strconv.Atoi(limitStr); err == nil && l > 0 {
+ limit = l
+ }
+ }
+
+ ctx, cancel := context.WithTimeout(r.Context(), 30*time.Second)
+ defer cancel()
+
+ conversations, err := h.storage.ListConversationsNeedingTitles(ctx, limit)
+ if err != nil {
+ logger.Error("Failed to list conversations needing titles", "error", err)
+ h.writeJSON(w, http.StatusInternalServerError, map[string]any{
+ "error": fmt.Sprintf("Failed to list conversations: %v", err),
+ })
+ return
+ }
+
+ h.writeJSON(w, http.StatusOK, map[string]any{
+ "conversations": conversations,
+ "count": len(conversations),
+ })
+}
+
+// HandleListModels handles GET /api/v1/models
+func (h *APIHandler) HandleListModels(w http.ResponseWriter, r *http.Request) {
+ if r.Method != http.MethodGet {
+ http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
+ return
+ }
+
+ ctx, cancel := context.WithTimeout(r.Context(), 30*time.Second)
+ defer cancel()
+
+ models, err := h.modelService.ListModels(ctx)
+ if err != nil {
+ logger.Error("Failed to list models", "error", err)
+ h.writeJSON(w, http.StatusInternalServerError, map[string]any{
+ "error": fmt.Sprintf("Failed to list models: %v", err),
+ })
+ return
+ }
+
+ h.writeJSON(w, http.StatusOK, map[string]any{
+ "models": models,
+ "count": len(models),
+ })
+}
+
+// HandleAgentsStatus handles GET /api/v1/agents/status
+func (h *APIHandler) HandleAgentsStatus(w http.ResponseWriter, r *http.Request) {
+ if r.Method != http.MethodGet {
+ http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
+ return
+ }
+
+ agentReadiness := h.stateManager.GetAgentReadiness()
+
+ var agents []map[string]any
+ totalAgents := 0
+ readyAgents := 0
+
+ if agentReadiness != nil {
+ totalAgents = agentReadiness.TotalAgents
+ readyAgents = agentReadiness.ReadyAgents
+
+ agents = make([]map[string]any, 0, len(agentReadiness.Agents))
+ for _, agent := range agentReadiness.Agents {
+ errorMsg := agent.Message
+ if agent.Error != "" {
+ errorMsg = agent.Error
+ }
+ agents = append(agents, map[string]any{
+ "name": agent.Name,
+ "state": agent.State.String(),
+ "url": agent.URL,
+ "image": agent.Image,
+ "error": errorMsg,
+ })
+ }
+ } else {
+ agents = []map[string]any{}
+ }
+
+ h.writeJSON(w, http.StatusOK, map[string]any{
+ "total_agents": totalAgents,
+ "ready_agents": readyAgents,
+ "agents": agents,
+ })
+}
+
+// HandleMCPStatus handles GET /api/v1/mcp/status
+func (h *APIHandler) HandleMCPStatus(w http.ResponseWriter, r *http.Request) {
+ if r.Method != http.MethodGet {
+ http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
+ return
+ }
+
+ if h.mcpManager == nil {
+ h.writeJSON(w, http.StatusOK, map[string]any{
+ "total_servers": 0,
+ "connected_servers": 0,
+ "total_tools": 0,
+ "servers": []map[string]any{},
+ })
+ return
+ }
+
+ clients := h.mcpManager.GetClients()
+ totalServers := h.mcpManager.GetTotalServers()
+
+ servers := make([]map[string]any, 0, len(clients))
+ connectedServers := 0
+ totalTools := 0
+
+ for _, client := range clients {
+ ctx, cancel := context.WithTimeout(r.Context(), 2*time.Second)
+ toolsMap, err := client.DiscoverTools(ctx)
+ cancel()
+
+ toolCount := 0
+ connected := err == nil
+
+ if connected {
+ connectedServers++
+ for _, tools := range toolsMap {
+ toolCount += len(tools)
+ }
+ totalTools += toolCount
+ }
+
+ servers = append(servers, map[string]any{
+ "name": fmt.Sprintf("server-%d", len(servers)+1),
+ "connected": connected,
+ "tools": toolCount,
+ })
+ }
+
+ h.writeJSON(w, http.StatusOK, map[string]any{
+ "total_servers": totalServers,
+ "connected_servers": connectedServers,
+ "total_tools": totalTools,
+ "servers": servers,
+ })
+}
+
+// HandleHistory handles history requests
+// GET /api/history - Get all history commands
+// POST /api/history - Save a new command to history
+func (h *APIHandler) HandleHistory(w http.ResponseWriter, r *http.Request) {
+ switch r.Method {
+ case http.MethodGet:
+ h.handleGetHistory(w, r)
+ case http.MethodPost:
+ h.handleSaveHistory(w, r)
+ default:
+ http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
+ }
+}
+
+// handleGetHistory retrieves all history commands
+func (h *APIHandler) handleGetHistory(w http.ResponseWriter, _ *http.Request) {
+ if h.historyManager == nil {
+ h.writeJSON(w, http.StatusOK, map[string]any{
+ "history": []string{},
+ "count": 0,
+ })
+ return
+ }
+
+ shellHistory := h.historyManager.GetShellHistoryFile()
+ shellProvider, err := history.NewShellHistoryWithDir(".infer")
+ if err != nil {
+ logger.Error("Failed to initialize shell history", "error", err)
+ h.writeJSON(w, http.StatusInternalServerError, map[string]any{
+ "error": fmt.Sprintf("Failed to load history: %v", err),
+ })
+ return
+ }
+
+ commands, err := shellProvider.LoadHistory()
+ if err != nil {
+ logger.Error("Failed to load history", "file", shellHistory, "error", err)
+ h.writeJSON(w, http.StatusInternalServerError, map[string]any{
+ "error": fmt.Sprintf("Failed to load history: %v", err),
+ })
+ return
+ }
+
+ h.writeJSON(w, http.StatusOK, map[string]any{
+ "history": commands,
+ "count": len(commands),
+ })
+}
+
+// handleSaveHistory saves a new command to history
+func (h *APIHandler) handleSaveHistory(w http.ResponseWriter, r *http.Request) {
+ var request struct {
+ Command string `json:"command"`
+ }
+
+ if err := json.NewDecoder(r.Body).Decode(&request); err != nil {
+ http.Error(w, fmt.Sprintf("Invalid request body: %v", err), http.StatusBadRequest)
+ return
+ }
+
+ if strings.TrimSpace(request.Command) == "" {
+ http.Error(w, "Command cannot be empty", http.StatusBadRequest)
+ return
+ }
+
+ if h.historyManager == nil {
+ logger.Warn("History manager not available")
+ h.writeJSON(w, http.StatusOK, map[string]any{
+ "success": false,
+ "message": "History not available",
+ })
+ return
+ }
+
+ if err := h.historyManager.AddToHistory(request.Command); err != nil {
+ logger.Error("Failed to save to history", "error", err)
+ h.writeJSON(w, http.StatusInternalServerError, map[string]any{
+ "error": fmt.Sprintf("Failed to save history: %v", err),
+ })
+ return
+ }
+
+ h.writeJSON(w, http.StatusOK, map[string]any{
+ "success": true,
+ "message": "Command saved to history",
+ })
+}
diff --git a/internal/handlers/chat_command_handler.go b/internal/handlers/chat_command_handler.go
index df26b669..52e09032 100644
--- a/internal/handlers/chat_command_handler.go
+++ b/internal/handlers/chat_command_handler.go
@@ -110,6 +110,8 @@ func (c *ChatCommandHandler) executeBashCommand(commandText, command string) tea
}
_ = c.handler.conversationRepo.AddMessage(userEntry)
+ argsJSON := fmt.Sprintf(`{"command":%q}`, command)
+
return tea.Batch(
func() tea.Msg {
return domain.UpdateHistoryEvent{
@@ -133,9 +135,10 @@ func (c *ChatCommandHandler) executeBashCommand(commandText, command string) tea
},
Tools: []domain.ToolInfo{
{
- CallID: toolCallID,
- Name: "Bash",
- Status: "starting",
+ CallID: toolCallID,
+ Name: "Bash",
+ Status: "starting",
+ Arguments: argsJSON,
},
},
}
@@ -492,9 +495,10 @@ func (c *ChatCommandHandler) executeToolCommand(commandText, toolName, argsJSON
},
Tools: []domain.ToolInfo{
{
- CallID: toolCallID,
- Name: toolName,
- Status: "starting",
+ CallID: toolCallID,
+ Name: toolName,
+ Status: "starting",
+ Arguments: argsJSON,
},
},
}
diff --git a/internal/handlers/headless_handler.go b/internal/handlers/headless_handler.go
new file mode 100644
index 00000000..498b407d
--- /dev/null
+++ b/internal/handlers/headless_handler.go
@@ -0,0 +1,824 @@
+package handlers
+
+import (
+ "bufio"
+ "context"
+ "encoding/json"
+ "fmt"
+ "io"
+ "os"
+ "sync"
+ "time"
+
+ "github.com/google/uuid"
+ "github.com/inference-gateway/cli/config"
+ "github.com/inference-gateway/cli/internal/container"
+ "github.com/inference-gateway/cli/internal/domain"
+ "github.com/inference-gateway/cli/internal/logger"
+ "github.com/inference-gateway/sdk"
+)
+
+// AG-UI Protocol Event Types
+// Ref: https://docs.ag-ui.com/concepts/events
+
+// AGUIEvent is the base interface for all AG-UI events
+type AGUIEvent struct {
+ Type string `json:"type"`
+ Timestamp int64 `json:"timestamp,omitempty"`
+}
+
+// RunStarted signals the start of an agent run
+type RunStarted struct {
+ Type string `json:"type"` // "RunStarted"
+ ThreadID string `json:"threadId"`
+ RunID string `json:"runId"`
+ ParentRunID string `json:"parentRunId,omitempty"`
+ Input any `json:"input,omitempty"`
+ Timestamp int64 `json:"timestamp,omitempty"`
+}
+
+// RunFinished signals the successful completion of an agent run
+type RunFinished struct {
+ Type string `json:"type"` // "RunFinished"
+ ThreadID string `json:"threadId"`
+ RunID string `json:"runId"`
+ Result any `json:"result,omitempty"`
+ Outcome string `json:"outcome,omitempty"`
+ Timestamp int64 `json:"timestamp,omitempty"`
+}
+
+// RunError signals an error during an agent run
+type RunError struct {
+ Type string `json:"type"` // "RunError"
+ Message string `json:"message"`
+ Code string `json:"code,omitempty"`
+ Timestamp int64 `json:"timestamp,omitempty"`
+}
+
+// TextMessageStart initializes a new text message in the conversation
+type TextMessageStart struct {
+ Type string `json:"type"` // "TextMessageStart"
+ MessageID string `json:"messageId"`
+ Role string `json:"role"`
+ Timestamp int64 `json:"timestamp,omitempty"`
+}
+
+// TextMessageContent delivers incremental parts of message text as available
+type TextMessageContent struct {
+ Type string `json:"type"` // "TextMessageContent"
+ MessageID string `json:"messageId"`
+ Delta string `json:"delta"`
+ Timestamp int64 `json:"timestamp,omitempty"`
+}
+
+// TextMessageEnd marks the completion of a streaming text message
+type TextMessageEnd struct {
+ Type string `json:"type"` // "TextMessageEnd"
+ MessageID string `json:"messageId"`
+ Timestamp int64 `json:"timestamp,omitempty"`
+}
+
+// ToolCallStart indicates the agent is invoking a tool
+type ToolCallStart struct {
+ Type string `json:"type"`
+ ToolCallID string `json:"toolCallId"`
+ ToolCallName string `json:"toolCallName"`
+ Arguments string `json:"arguments,omitempty"`
+ ParentMessageID string `json:"parentMessageId,omitempty"`
+ Timestamp int64 `json:"timestamp,omitempty"`
+ Status string `json:"status,omitempty"`
+ Metadata map[string]any `json:"metadata,omitempty"`
+}
+
+// ToolCallArgs delivers incremental parts of tool argument data
+type ToolCallArgs struct {
+ Type string `json:"type"`
+ ToolCallID string `json:"toolCallId"`
+ Delta string `json:"delta"`
+ Timestamp int64 `json:"timestamp,omitempty"`
+}
+
+// ToolCallEnd marks the completion of a tool call specification
+type ToolCallEnd struct {
+ Type string `json:"type"` // "ToolCallEnd"
+ ToolCallID string `json:"toolCallId"`
+ Timestamp int64 `json:"timestamp,omitempty"`
+}
+
+// ToolCallProgress delivers intermediate progress updates during tool execution
+type ToolCallProgress struct {
+ Type string `json:"type"` // "ToolCallProgress"
+ ToolCallID string `json:"toolCallId"`
+ Status string `json:"status"` // Current execution status
+ Message string `json:"message"` // Human-readable status message
+ Output string `json:"output,omitempty"` // Streaming output (for Bash)
+ Metadata map[string]any `json:"metadata,omitempty"`
+ Timestamp int64 `json:"timestamp,omitempty"`
+}
+
+// ToolCallResult provides the output/result from executed tool
+type ToolCallResult struct {
+ Type string `json:"type"` // "ToolCallResult"
+ MessageID string `json:"messageId"`
+ ToolCallID string `json:"toolCallId"`
+ Content any `json:"content"`
+ Role string `json:"role,omitempty"`
+ Timestamp int64 `json:"timestamp,omitempty"`
+ Status string `json:"status,omitempty"` // Final status: "complete" or "failed"
+ Duration float64 `json:"duration,omitempty"` // Execution time in seconds
+ Metadata map[string]any `json:"metadata,omitempty"` // Additional metadata (e.g., exit code, error details)
+}
+
+// ParallelToolsMetadata provides summary information for parallel tool execution
+type ParallelToolsMetadata struct {
+ Type string `json:"type"` // "ParallelToolsMetadata"
+ TotalCount int `json:"totalCount"`
+ SuccessCount int `json:"successCount,omitempty"`
+ FailureCount int `json:"failureCount,omitempty"`
+ TotalDuration float64 `json:"totalDuration,omitempty"` // Total execution time in seconds
+ Timestamp int64 `json:"timestamp,omitempty"`
+}
+
+// HeadlessInput represents JSON input from UI
+type HeadlessInput struct {
+ Type string `json:"type"` // "message", "interrupt", "shutdown"
+ Content string `json:"content"` // User message content
+ Images []domain.ImageAttachment `json:"images"` // Image attachments
+ Model string `json:"model,omitempty"` // Optional model to use for this message
+}
+
+// toolExecutionState tracks the execution state of a single tool call
+type toolExecutionState struct {
+ CallID string
+ ToolName string
+ StartTime time.Time
+ Status string
+ OutputBuffer []string
+}
+
+// HeadlessHandler handles headless mode communication via stdin/stdout
+// Implements AG-UI protocol: https://docs.ag-ui.com
+type HeadlessHandler struct {
+ sessionID string
+ conversationID string
+ services *container.ServiceContainer
+ config *config.Config
+ stdin io.Reader
+ stdout io.Writer
+ ctx context.Context
+ cancel context.CancelFunc
+ currentRunID string
+ toolStates map[string]*toolExecutionState
+ toolStatesMux sync.RWMutex
+}
+
+// NewHeadlessHandler creates a new headless handler
+func NewHeadlessHandler(sessionID string, conversationID string, services *container.ServiceContainer, cfg *config.Config) *HeadlessHandler {
+ if sessionID == "" {
+ sessionID = uuid.New().String()
+ }
+
+ ctx, cancel := context.WithCancel(context.Background())
+
+ return &HeadlessHandler{
+ sessionID: sessionID,
+ conversationID: conversationID,
+ services: services,
+ config: cfg,
+ stdin: os.Stdin,
+ stdout: os.Stdout,
+ ctx: ctx,
+ cancel: cancel,
+ }
+}
+
+// Start begins the headless session
+func (h *HeadlessHandler) Start() error {
+ if err := h.services.GetGatewayManager().EnsureStarted(); err != nil {
+ logger.Error("Failed to start gateway", "error", err)
+ h.emitRunError(fmt.Sprintf("failed to start gateway: %v", err), "GATEWAY_START_FAILED")
+ return err
+ }
+
+ ctx, cancel := context.WithTimeout(h.ctx, time.Duration(h.config.Gateway.Timeout)*time.Second)
+ defer cancel()
+
+ models, err := h.services.GetModelService().ListModels(ctx)
+ if err != nil {
+ h.emitRunError(fmt.Sprintf("inference gateway is not available: %v", err), "GATEWAY_UNAVAILABLE")
+ return fmt.Errorf("inference gateway is not available: %w", err)
+ }
+
+ if len(models) == 0 {
+ h.emitRunError("no models available from inference gateway", "NO_MODELS")
+ return fmt.Errorf("no models available from inference gateway")
+ }
+
+ defaultModel := h.config.Agent.Model
+ if defaultModel == "" || !contains(models, defaultModel) {
+ defaultModel = models[0]
+ }
+
+ if err := h.services.GetModelService().SelectModel(defaultModel); err != nil {
+ h.emitRunError(fmt.Sprintf("failed to set model: %v", err), "MODEL_SELECT_FAILED")
+ return fmt.Errorf("failed to set model: %w", err)
+ }
+
+ conversationRepo := h.services.GetConversationRepository()
+ h.loadExistingConversation(conversationRepo)
+
+ h.emitEvent(RunStarted{
+ Type: "RunStarted",
+ ThreadID: h.conversationID,
+ RunID: h.sessionID,
+ Timestamp: time.Now().UnixMilli(),
+ })
+
+ return h.readLoop()
+}
+
+// readLoop continuously reads JSON input from stdin
+func (h *HeadlessHandler) readLoop() error {
+ scanner := bufio.NewScanner(h.stdin)
+ scanner.Buffer(make([]byte, 1024*1024), 10*1024*1024) // 1MB initial, 10MB max for large images
+
+ for scanner.Scan() {
+ line := scanner.Bytes()
+ if len(line) == 0 {
+ continue
+ }
+
+ var input HeadlessInput
+ if err := json.Unmarshal(line, &input); err != nil {
+ logger.Error("Failed to parse JSON input", "error", err, "line", string(line))
+ h.emitRunError(fmt.Sprintf("invalid JSON input: %v", err), "INVALID_INPUT")
+ continue
+ }
+
+ if err := h.processInput(input); err != nil {
+ if err == io.EOF {
+ return nil
+ }
+ logger.Error("Failed to process input", "error", err, "input", input)
+ h.emitRunError(fmt.Sprintf("failed to process input: %v", err), "PROCESSING_FAILED")
+ }
+ }
+
+ if err := scanner.Err(); err != nil {
+ logger.Error("Error reading from stdin", "error", err)
+ return fmt.Errorf("error reading from stdin: %w", err)
+ }
+
+ return nil
+}
+
+// processInput handles a single input message
+func (h *HeadlessHandler) processInput(input HeadlessInput) error {
+ switch input.Type {
+ case "message":
+ return h.handleMessage(input.Content, input.Images, input.Model)
+ case "interrupt":
+ return h.handleInterrupt()
+ case "shutdown":
+ h.cancel()
+ return io.EOF // Signal clean shutdown
+ default:
+ return fmt.Errorf("unknown input type: %s", input.Type)
+ }
+}
+
+// handleMessage processes a user message
+func (h *HeadlessHandler) handleMessage(content string, images []domain.ImageAttachment, model string) error {
+ if content == "" {
+ return fmt.Errorf("empty message content")
+ }
+
+ if model != "" {
+ modelService := h.services.GetModelService()
+ if err := modelService.SelectModel(model); err != nil {
+ h.emitRunError(fmt.Sprintf("Failed to select model '%s': %v", model, err), "MODEL_SELECT_FAILED")
+ return fmt.Errorf("failed to select model: %w", err)
+ }
+ logger.Info("Model selected for headless session", "model", model, "session", h.sessionID)
+ }
+
+ h.currentRunID = uuid.New().String()
+
+ userMessageID := uuid.New().String()
+ h.emitEvent(TextMessageStart{
+ Type: "TextMessageStart",
+ MessageID: userMessageID,
+ Role: "user",
+ Timestamp: time.Now().UnixMilli(),
+ })
+ h.emitEvent(TextMessageContent{
+ Type: "TextMessageContent",
+ MessageID: userMessageID,
+ Delta: content,
+ Timestamp: time.Now().UnixMilli(),
+ })
+ h.emitEvent(TextMessageEnd{
+ Type: "TextMessageEnd",
+ MessageID: userMessageID,
+ Timestamp: time.Now().UnixMilli(),
+ })
+
+ h.emitEvent(RunStarted{
+ Type: "RunStarted",
+ ThreadID: h.conversationID,
+ RunID: h.currentRunID,
+ Input: map[string]any{
+ "content": content,
+ "images": len(images),
+ },
+ Timestamp: time.Now().UnixMilli(),
+ })
+
+ var userMessage sdk.Message
+
+ if len(images) > 0 {
+ contentParts := []sdk.ContentPart{}
+
+ var textPart sdk.ContentPart
+ if err := textPart.FromTextContentPart(sdk.TextContentPart{
+ Type: sdk.Text,
+ Text: content,
+ }); err != nil {
+ h.emitRunError(fmt.Sprintf("failed to create text content part: %v", err), "CONTENT_PART_FAILED")
+ return fmt.Errorf("failed to create text content part: %w", err)
+ }
+ contentParts = append(contentParts, textPart)
+
+ for _, img := range images {
+ dataURL := fmt.Sprintf("data:%s;base64,%s", img.MimeType, img.Data)
+ imagePart, err := sdk.NewImageContentPart(dataURL, nil)
+ if err != nil {
+ h.emitRunError(fmt.Sprintf("failed to create image content part: %v", err), "IMAGE_PART_FAILED")
+ return fmt.Errorf("failed to create image content part: %w", err)
+ }
+ contentParts = append(contentParts, imagePart)
+ }
+
+ userMessage = sdk.Message{
+ Role: sdk.User,
+ Content: sdk.NewMessageContent(contentParts),
+ }
+ } else {
+ userMessage = sdk.Message{
+ Role: sdk.User,
+ Content: sdk.NewMessageContent(content),
+ }
+ }
+
+ conversationRepo := h.services.GetConversationRepository()
+
+ wasNewConversation := h.conversationID == ""
+
+ if err := conversationRepo.AddMessage(domain.ConversationEntry{
+ Message: userMessage,
+ Time: time.Now(),
+ }); err != nil {
+ logger.Warn("Failed to add user message to conversation", "error", err)
+ }
+
+ if wasNewConversation {
+ if persistentRepo, ok := conversationRepo.(interface {
+ GetCurrentConversationID() string
+ }); ok {
+ h.conversationID = persistentRepo.GetCurrentConversationID()
+ if h.conversationID != "" {
+ h.emitEvent(map[string]any{
+ "type": "ConversationCreated",
+ "conversation_id": h.conversationID,
+ "timestamp": time.Now().UnixMilli(),
+ })
+ }
+ }
+ }
+
+ entries := conversationRepo.GetMessages()
+ messages := make([]sdk.Message, len(entries))
+ for i, entry := range entries {
+ messages[i] = entry.Message
+ }
+
+ req := &domain.AgentRequest{
+ RequestID: fmt.Sprintf("req_%d", time.Now().UnixNano()),
+ Model: h.services.GetModelService().GetCurrentModel(),
+ Messages: messages,
+ }
+
+ ctx, cancel := context.WithCancel(h.ctx)
+ defer cancel()
+
+ agentService := h.services.GetAgentService()
+ events, err := agentService.RunWithStream(ctx, req)
+ if err != nil {
+ h.emitRunError(fmt.Sprintf("failed to start chat: %v", err), "CHAT_START_FAILED")
+ return fmt.Errorf("failed to start chat: %w", err)
+ }
+
+ return h.processStreamingEvents(events)
+}
+
+// processStreamingEvents handles streaming chat events
+func (h *HeadlessHandler) processStreamingEvents(events <-chan domain.ChatEvent) error {
+ var messageID string
+ var tokenStats map[string]int
+
+ for {
+ select {
+ case <-h.ctx.Done():
+ return h.ctx.Err()
+ case event, ok := <-events:
+ if !ok {
+ return h.handleStreamComplete(messageID, tokenStats)
+ }
+
+ var err error
+ messageID, tokenStats, err = h.handleEvent(event, messageID, tokenStats)
+ if err != nil {
+ return err
+ }
+ }
+ }
+}
+
+func (h *HeadlessHandler) handleStreamComplete(messageID string, tokenStats map[string]int) error {
+ h.emitEvent(RunFinished{
+ Type: "RunFinished",
+ ThreadID: h.conversationID,
+ RunID: h.currentRunID,
+ Result: map[string]any{
+ "message_id": messageID,
+ "tokens": tokenStats,
+ },
+ Outcome: "success",
+ Timestamp: time.Now().UnixMilli(),
+ })
+ return nil
+}
+
+func (h *HeadlessHandler) handleEvent(event domain.ChatEvent, messageID string, tokenStats map[string]int) (string, map[string]int, error) {
+ switch e := event.(type) {
+ case domain.ChatChunkEvent:
+ return h.handleChatChunk(e, messageID), tokenStats, nil
+ case domain.ChatCompleteEvent:
+ return h.handleChatComplete(e, messageID)
+ case domain.ChatErrorEvent:
+ return messageID, tokenStats, h.handleChatError(e)
+ case domain.ToolCallReadyEvent:
+ return h.handleToolCallReady(e, messageID), tokenStats, nil
+ case domain.ParallelToolsStartEvent:
+ return h.handleParallelToolsStart(e, messageID), tokenStats, nil
+ case domain.ToolExecutionProgressEvent:
+ h.handleToolExecutionProgress(e)
+ return messageID, tokenStats, nil
+ case domain.BashOutputChunkEvent:
+ h.handleBashOutputChunk(e)
+ return messageID, tokenStats, nil
+ case domain.ParallelToolsCompleteEvent:
+ h.handleParallelToolsComplete(e)
+ return messageID, tokenStats, nil
+ case domain.ToolApprovalRequestedEvent:
+ h.handleToolApprovalRequested(e, messageID)
+ return messageID, tokenStats, nil
+ default:
+ return messageID, tokenStats, nil
+ }
+}
+
+func (h *HeadlessHandler) handleChatChunk(e domain.ChatChunkEvent, messageID string) string {
+ if e.Content == "" {
+ return messageID
+ }
+
+ if messageID == "" {
+ messageID = uuid.New().String()
+ h.emitEvent(TextMessageStart{
+ Type: "TextMessageStart",
+ MessageID: messageID,
+ Role: "assistant",
+ Timestamp: time.Now().UnixMilli(),
+ })
+ }
+
+ h.emitEvent(TextMessageContent{
+ Type: "TextMessageContent",
+ MessageID: messageID,
+ Delta: e.Content,
+ Timestamp: time.Now().UnixMilli(),
+ })
+
+ return messageID
+}
+
+func (h *HeadlessHandler) handleChatComplete(e domain.ChatCompleteEvent, messageID string) (string, map[string]int, error) {
+ if messageID != "" {
+ h.emitEvent(TextMessageEnd{
+ Type: "TextMessageEnd",
+ MessageID: messageID,
+ Timestamp: time.Now().UnixMilli(),
+ })
+ messageID = ""
+ }
+
+ var tokenStats map[string]int
+ if e.Metrics != nil && e.Metrics.Usage != nil {
+ tokenStats = map[string]int{
+ "input_tokens": int(e.Metrics.Usage.PromptTokens),
+ "output_tokens": int(e.Metrics.Usage.CompletionTokens),
+ "total_tokens": int(e.Metrics.Usage.PromptTokens + e.Metrics.Usage.CompletionTokens),
+ }
+ }
+
+ return messageID, tokenStats, nil
+}
+
+func (h *HeadlessHandler) handleChatError(e domain.ChatErrorEvent) error {
+ h.emitRunError(fmt.Sprintf("chat error: %v", e.Error), "CHAT_ERROR")
+ return fmt.Errorf("chat error: %v", e.Error)
+}
+
+func (h *HeadlessHandler) handleToolCallReady(e domain.ToolCallReadyEvent, messageID string) string {
+ if messageID != "" {
+ h.emitEvent(TextMessageEnd{
+ Type: "TextMessageEnd",
+ MessageID: messageID,
+ Timestamp: time.Now().UnixMilli(),
+ })
+ messageID = ""
+ }
+
+ for _, toolCall := range e.ToolCalls {
+ h.emitToolCallEvents(toolCall.Id, toolCall.Function.Name, toolCall.Function.Arguments, messageID)
+ }
+
+ return messageID
+}
+
+func (h *HeadlessHandler) handleParallelToolsStart(e domain.ParallelToolsStartEvent, messageID string) string {
+ if messageID != "" {
+ h.emitEvent(TextMessageEnd{
+ Type: "TextMessageEnd",
+ MessageID: messageID,
+ Timestamp: time.Now().UnixMilli(),
+ })
+ messageID = ""
+ }
+
+ for _, tool := range e.Tools {
+ h.trackToolExecution(tool.CallID, tool.Name)
+ h.emitEvent(ToolCallStart{
+ Type: "ToolCallStart",
+ ToolCallID: tool.CallID,
+ ToolCallName: tool.Name,
+ Arguments: tool.Arguments,
+ ParentMessageID: messageID,
+ Status: "queued",
+ Timestamp: time.Now().UnixMilli(),
+ })
+ }
+
+ return messageID
+}
+
+func (h *HeadlessHandler) handleToolExecutionProgress(e domain.ToolExecutionProgressEvent) {
+ state := h.updateToolStatus(e.ToolCallID, e.Status)
+ aguiStatus := mapStatusToAGUI(e.Status)
+
+ h.emitEvent(ToolCallProgress{
+ Type: "ToolCallProgress",
+ ToolCallID: e.ToolCallID,
+ Status: aguiStatus,
+ Message: e.Message,
+ Timestamp: time.Now().UnixMilli(),
+ })
+
+ if e.Status == "complete" || e.Status == "failed" {
+ h.emitToolResult(e, state, aguiStatus)
+ }
+}
+
+func (h *HeadlessHandler) emitToolResult(e domain.ToolExecutionProgressEvent, state *toolExecutionState, aguiStatus string) {
+ if state == nil {
+ return
+ }
+
+ duration := time.Since(state.StartTime).Seconds()
+ content := e.Result
+ if content == "" {
+ content = e.Message
+ }
+
+ h.emitEvent(ToolCallResult{
+ Type: "ToolCallResult",
+ MessageID: uuid.New().String(),
+ ToolCallID: e.ToolCallID,
+ Content: content,
+ Role: "tool",
+ Status: aguiStatus,
+ Duration: duration,
+ Timestamp: time.Now().UnixMilli(),
+ })
+
+ h.removeToolState(e.ToolCallID)
+}
+
+func (h *HeadlessHandler) handleBashOutputChunk(e domain.BashOutputChunkEvent) {
+ h.emitEvent(ToolCallProgress{
+ Type: "ToolCallProgress",
+ ToolCallID: e.ToolCallID,
+ Status: "running",
+ Message: "Streaming output...",
+ Output: e.Output,
+ Metadata: map[string]any{
+ "isComplete": e.IsComplete,
+ },
+ Timestamp: time.Now().UnixMilli(),
+ })
+}
+
+func (h *HeadlessHandler) handleParallelToolsComplete(e domain.ParallelToolsCompleteEvent) {
+ h.emitEvent(ParallelToolsMetadata{
+ Type: "ParallelToolsMetadata",
+ TotalCount: e.TotalExecuted,
+ SuccessCount: e.SuccessCount,
+ FailureCount: e.FailureCount,
+ TotalDuration: e.Duration.Seconds(),
+ Timestamp: time.Now().UnixMilli(),
+ })
+}
+
+func (h *HeadlessHandler) handleToolApprovalRequested(e domain.ToolApprovalRequestedEvent, messageID string) {
+ h.emitEvent(ToolCallStart{
+ Type: "ToolCallStart",
+ ToolCallID: e.ToolCall.Id,
+ ToolCallName: e.ToolCall.Function.Name,
+ Arguments: e.ToolCall.Function.Arguments,
+ ParentMessageID: messageID,
+ Timestamp: time.Now().UnixMilli(),
+ })
+}
+
+func (h *HeadlessHandler) emitToolCallEvents(toolCallID, toolName, arguments, messageID string) {
+ h.trackToolExecution(toolCallID, toolName)
+
+ h.emitEvent(ToolCallStart{
+ Type: "ToolCallStart",
+ ToolCallID: toolCallID,
+ ToolCallName: toolName,
+ Arguments: arguments,
+ ParentMessageID: messageID,
+ Status: "queued",
+ Timestamp: time.Now().UnixMilli(),
+ })
+
+ h.emitEvent(ToolCallArgs{
+ Type: "ToolCallArgs",
+ ToolCallID: toolCallID,
+ Delta: arguments,
+ Timestamp: time.Now().UnixMilli(),
+ })
+
+ h.emitEvent(ToolCallEnd{
+ Type: "ToolCallEnd",
+ ToolCallID: toolCallID,
+ Timestamp: time.Now().UnixMilli(),
+ })
+}
+
+// handleInterrupt handles interruption signal
+func (h *HeadlessHandler) handleInterrupt() error {
+ h.cancel()
+ h.emitRunError("Chat interrupted by user", "INTERRUPTED")
+ return nil
+}
+
+// emitEvent sends an AG-UI protocol event to stdout
+func (h *HeadlessHandler) emitEvent(event any) {
+ jsonData, err := json.Marshal(event)
+ if err != nil {
+ logger.Error("Failed to marshal event", "error", err, "event", event)
+ return
+ }
+
+ if _, err := fmt.Fprintf(h.stdout, "%s\n", jsonData); err != nil {
+ logger.Error("Failed to write to stdout", "error", err)
+ }
+}
+
+// emitRunError sends a RunError event
+func (h *HeadlessHandler) emitRunError(message string, code string) {
+ h.emitEvent(RunError{
+ Type: "RunError",
+ Message: message,
+ Code: code,
+ Timestamp: time.Now().UnixMilli(),
+ })
+}
+
+// Shutdown cleanly shuts down the headless handler
+func (h *HeadlessHandler) Shutdown() error {
+ h.cancel()
+
+ ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
+ defer cancel()
+
+ return h.services.Shutdown(ctx)
+}
+
+// contains checks if a slice contains a string
+func contains(slice []string, item string) bool {
+ for _, s := range slice {
+ if s == item {
+ return true
+ }
+ }
+ return false
+}
+
+// loadExistingConversation attempts to load a conversation from storage if conversationID is set
+func (h *HeadlessHandler) loadExistingConversation(conversationRepo domain.ConversationRepository) {
+ if h.conversationID == "" {
+ return
+ }
+
+ ctx, cancel := context.WithTimeout(h.ctx, 30*time.Second)
+ defer cancel()
+
+ persistentRepo, ok := conversationRepo.(interface {
+ LoadConversation(ctx context.Context, conversationID string) error
+ })
+ if !ok {
+ logger.Warn("ConversationRepository does not support LoadConversation, will create new conversation on first message")
+ h.conversationID = ""
+ return
+ }
+
+ logger.Info("💾 Attempting to load conversation from storage...")
+ if err := persistentRepo.LoadConversation(ctx, h.conversationID); err != nil {
+ logger.Warn("Failed to load conversation, will create new one on first message", "conversation_id", h.conversationID, "error", err)
+ h.conversationID = ""
+ }
+}
+
+// trackToolExecution initializes state tracking for a tool call
+func (h *HeadlessHandler) trackToolExecution(callID, toolName string) {
+ h.toolStatesMux.Lock()
+ defer h.toolStatesMux.Unlock()
+
+ if h.toolStates == nil {
+ h.toolStates = make(map[string]*toolExecutionState)
+ }
+
+ h.toolStates[callID] = &toolExecutionState{
+ CallID: callID,
+ ToolName: toolName,
+ StartTime: time.Now(),
+ Status: "queued",
+ OutputBuffer: []string{},
+ }
+}
+
+// getToolState retrieves tool execution state (thread-safe read)
+func (h *HeadlessHandler) getToolState(callID string) *toolExecutionState {
+ h.toolStatesMux.RLock()
+ defer h.toolStatesMux.RUnlock()
+ return h.toolStates[callID]
+}
+
+// updateToolStatus updates tool status and returns updated state
+func (h *HeadlessHandler) updateToolStatus(callID, status string) *toolExecutionState {
+ h.toolStatesMux.Lock()
+ defer h.toolStatesMux.Unlock()
+
+ if state, exists := h.toolStates[callID]; exists {
+ state.Status = status
+ return state
+ }
+ return nil
+}
+
+// removeToolState cleans up tool state after completion
+func (h *HeadlessHandler) removeToolState(callID string) {
+ h.toolStatesMux.Lock()
+ defer h.toolStatesMux.Unlock()
+ delete(h.toolStates, callID)
+}
+
+// mapStatusToAGUI maps TUI status values to AG-UI protocol status
+func mapStatusToAGUI(status string) string {
+ switch status {
+ case "queued", "ready":
+ return "queued"
+ case "running", "starting", "saving", "executing", "streaming":
+ return "running"
+ case "complete", "completed", "executed":
+ return "complete"
+ case "error", "failed":
+ return "failed"
+ default:
+ return status
+ }
+}
diff --git a/internal/handlers/headless_handler_progress_test.go b/internal/handlers/headless_handler_progress_test.go
new file mode 100644
index 00000000..ca21e766
--- /dev/null
+++ b/internal/handlers/headless_handler_progress_test.go
@@ -0,0 +1,404 @@
+package handlers
+
+import (
+ "bufio"
+ "bytes"
+ "context"
+ "encoding/json"
+ "testing"
+ "time"
+
+ domain "github.com/inference-gateway/cli/internal/domain"
+)
+
+// TestMapStatusToAGUI tests the status mapping function
+func TestMapStatusToAGUI(t *testing.T) {
+ tests := []struct {
+ name string
+ input string
+ expected string
+ }{
+ {"queued maps to queued", "queued", "queued"},
+ {"ready maps to queued", "ready", "queued"},
+ {"running maps to running", "running", "running"},
+ {"starting maps to running", "starting", "running"},
+ {"saving maps to running", "saving", "running"},
+ {"executing maps to running", "executing", "running"},
+ {"streaming maps to running", "streaming", "running"},
+ {"complete maps to complete", "complete", "complete"},
+ {"completed maps to complete", "completed", "complete"},
+ {"executed maps to complete", "executed", "complete"},
+ {"error maps to failed", "error", "failed"},
+ {"failed maps to failed", "failed", "failed"},
+ {"unknown passes through", "unknown", "unknown"},
+ }
+
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ result := mapStatusToAGUI(tt.input)
+ if result != tt.expected {
+ t.Errorf("mapStatusToAGUI(%q) = %q, want %q", tt.input, result, tt.expected)
+ }
+ })
+ }
+}
+
+// TestTrackToolExecution tests tool execution state tracking
+func TestTrackToolExecution(t *testing.T) {
+ h := &HeadlessHandler{}
+
+ callID := "call_123"
+ toolName := "Bash"
+
+ h.trackToolExecution(callID, toolName)
+
+ state := h.getToolState(callID)
+ if state == nil {
+ t.Fatal("Expected state to be created, got nil")
+ }
+
+ if state.CallID != callID {
+ t.Errorf("Expected CallID %q, got %q", callID, state.CallID)
+ }
+
+ if state.ToolName != toolName {
+ t.Errorf("Expected ToolName %q, got %q", toolName, state.ToolName)
+ }
+
+ if state.Status != "queued" {
+ t.Errorf("Expected initial status 'queued', got %q", state.Status)
+ }
+
+ if state.OutputBuffer == nil {
+ t.Error("Expected OutputBuffer to be initialized, got nil")
+ }
+
+ if state.StartTime.IsZero() {
+ t.Error("Expected StartTime to be set, got zero time")
+ }
+}
+
+// TestUpdateToolStatus tests updating tool status
+func TestUpdateToolStatus(t *testing.T) {
+ h := &HeadlessHandler{}
+
+ callID := "call_123"
+ toolName := "Bash"
+
+ h.trackToolExecution(callID, toolName)
+
+ state := h.updateToolStatus(callID, "running")
+ if state == nil {
+ t.Fatal("Expected state to be returned, got nil")
+ }
+
+ if state.Status != "running" {
+ t.Errorf("Expected status 'running', got %q", state.Status)
+ }
+
+ nilState := h.updateToolStatus("non_existent", "complete")
+ if nilState != nil {
+ t.Error("Expected nil for non-existent tool, got state")
+ }
+}
+
+// TestRemoveToolState tests removing tool state
+func TestRemoveToolState(t *testing.T) {
+ h := &HeadlessHandler{}
+
+ callID := "call_123"
+ toolName := "Bash"
+
+ h.trackToolExecution(callID, toolName)
+
+ if h.getToolState(callID) == nil {
+ t.Fatal("Expected state to exist before removal")
+ }
+
+ h.removeToolState(callID)
+
+ if h.getToolState(callID) != nil {
+ t.Error("Expected state to be removed, but it still exists")
+ }
+}
+
+// TestConcurrentStateAccess tests thread-safe state access
+func TestConcurrentStateAccess(t *testing.T) {
+ h := &HeadlessHandler{}
+
+ done := make(chan bool)
+ numGoroutines := 10
+
+ for i := 0; i < numGoroutines; i++ {
+ go func(id int) {
+ callID := "call_" + string(rune('0'+id))
+ toolName := "Tool" + string(rune('0'+id))
+
+ h.trackToolExecution(callID, toolName)
+ h.updateToolStatus(callID, "running")
+ _ = h.getToolState(callID)
+ h.updateToolStatus(callID, "complete")
+ h.removeToolState(callID)
+
+ done <- true
+ }(i)
+ }
+
+ for i := 0; i < numGoroutines; i++ {
+ <-done
+ }
+
+ h.toolStatesMux.RLock()
+ remaining := len(h.toolStates)
+ h.toolStatesMux.RUnlock()
+
+ if remaining != 0 {
+ t.Errorf("Expected 0 remaining states, got %d", remaining)
+ }
+}
+
+// TestToolCallProgressEventEmission tests ToolCallProgress event emission
+func TestToolCallProgressEventEmission(t *testing.T) {
+ var stdout bytes.Buffer
+ ctx, cancel := context.WithCancel(context.Background())
+ defer cancel()
+
+ h := &HeadlessHandler{
+ stdout: &stdout,
+ ctx: ctx,
+ }
+
+ callID := "call_123"
+ h.trackToolExecution(callID, "Bash")
+
+ progressEvent := domain.ToolExecutionProgressEvent{
+ ToolCallID: callID,
+ ToolName: "Bash",
+ Status: "running",
+ Message: "Executing command...",
+ }
+
+ events := make(chan domain.ChatEvent, 1)
+ events <- progressEvent
+ close(events)
+
+ err := h.processStreamingEvents(events)
+ if err != nil {
+ t.Fatalf("Unexpected error: %v", err)
+ }
+
+ scanner := bufio.NewScanner(&stdout)
+ scanner.Split(bufio.ScanLines)
+
+ var emittedProgress bool
+ for scanner.Scan() {
+ line := scanner.Bytes()
+ var event map[string]any
+ if err := json.Unmarshal(line, &event); err != nil {
+ continue
+ }
+
+ if event["type"] == "ToolCallProgress" {
+ emittedProgress = true
+ if event["toolCallId"] != callID {
+ t.Errorf("Expected toolCallId %q, got %v", callID, event["toolCallId"])
+ }
+ if event["status"] != "running" {
+ t.Errorf("Expected status 'running', got %v", event["status"])
+ }
+ if event["message"] != "Executing command..." {
+ t.Errorf("Expected message 'Executing command...', got %v", event["message"])
+ }
+ }
+ }
+
+ if !emittedProgress {
+ t.Error("Expected ToolCallProgress event to be emitted")
+ }
+}
+
+// TestBashOutputChunkEventEmission tests BashOutputChunkEvent emission
+func TestBashOutputChunkEventEmission(t *testing.T) {
+ var stdout bytes.Buffer
+ ctx, cancel := context.WithCancel(context.Background())
+ defer cancel()
+
+ h := &HeadlessHandler{
+ stdout: &stdout,
+ ctx: ctx,
+ }
+
+ callID := "call_123"
+ h.trackToolExecution(callID, "Bash")
+
+ bashEvent := domain.BashOutputChunkEvent{
+ ToolCallID: callID,
+ Output: "total 48\n",
+ IsComplete: false,
+ }
+
+ events := make(chan domain.ChatEvent, 1)
+ events <- bashEvent
+ close(events)
+
+ err := h.processStreamingEvents(events)
+ if err != nil {
+ t.Fatalf("Unexpected error: %v", err)
+ }
+
+ scanner := bufio.NewScanner(&stdout)
+ scanner.Split(bufio.ScanLines)
+
+ var foundOutput bool
+ for scanner.Scan() {
+ line := scanner.Bytes()
+ var event map[string]any
+ if err := json.Unmarshal(line, &event); err != nil {
+ continue
+ }
+
+ if event["type"] != "ToolCallProgress" {
+ continue
+ }
+
+ foundOutput = true
+ if event["output"] != "total 48\n" {
+ t.Errorf("Expected output 'total 48\\n', got %v", event["output"])
+ }
+ if metadata, ok := event["metadata"].(map[string]any); ok {
+ if metadata["isComplete"] != false {
+ t.Errorf("Expected isComplete false, got %v", metadata["isComplete"])
+ }
+ }
+ }
+
+ if !foundOutput {
+ t.Error("Expected ToolCallProgress event with output to be emitted")
+ }
+}
+
+// TestParallelToolsMetadataEmission tests ParallelToolsMetadata event emission
+func TestParallelToolsMetadataEmission(t *testing.T) {
+ var stdout bytes.Buffer
+ ctx, cancel := context.WithCancel(context.Background())
+ defer cancel()
+
+ h := &HeadlessHandler{
+ stdout: &stdout,
+ ctx: ctx,
+ }
+
+ completeEvent := domain.ParallelToolsCompleteEvent{
+ TotalExecuted: 3,
+ SuccessCount: 2,
+ FailureCount: 1,
+ Duration: 5 * time.Second,
+ }
+
+ events := make(chan domain.ChatEvent, 1)
+ events <- completeEvent
+ close(events)
+
+ err := h.processStreamingEvents(events)
+ if err != nil {
+ t.Fatalf("Unexpected error: %v", err)
+ }
+
+ scanner := bufio.NewScanner(&stdout)
+ scanner.Split(bufio.ScanLines)
+
+ var foundMetadata bool
+ for scanner.Scan() {
+ line := scanner.Bytes()
+ var event map[string]any
+ if err := json.Unmarshal(line, &event); err != nil {
+ continue
+ }
+
+ if event["type"] != "ParallelToolsMetadata" {
+ continue
+ }
+
+ foundMetadata = true
+ if int(event["totalCount"].(float64)) != 3 {
+ t.Errorf("Expected totalCount 3, got %v", event["totalCount"])
+ }
+ if int(event["successCount"].(float64)) != 2 {
+ t.Errorf("Expected successCount 2, got %v", event["successCount"])
+ }
+ if int(event["failureCount"].(float64)) != 1 {
+ t.Errorf("Expected failureCount 1, got %v", event["failureCount"])
+ }
+ if event["totalDuration"].(float64) != 5.0 {
+ t.Errorf("Expected totalDuration 5.0, got %v", event["totalDuration"])
+ }
+ }
+
+ if !foundMetadata {
+ t.Error("Expected ParallelToolsMetadata event to be emitted")
+ }
+}
+
+// TestToolCallResultWithDuration tests ToolCallResult emission with duration
+func TestToolCallResultWithDuration(t *testing.T) {
+ var stdout bytes.Buffer
+ ctx, cancel := context.WithCancel(context.Background())
+ defer cancel()
+
+ h := &HeadlessHandler{
+ stdout: &stdout,
+ ctx: ctx,
+ }
+
+ callID := "call_123"
+ h.trackToolExecution(callID, "Bash")
+
+ completeEvent := domain.ToolExecutionProgressEvent{
+ ToolCallID: callID,
+ ToolName: "Bash",
+ Status: "complete",
+ Message: "Command executed successfully",
+ }
+
+ events := make(chan domain.ChatEvent, 1)
+ events <- completeEvent
+ close(events)
+
+ err := h.processStreamingEvents(events)
+ if err != nil {
+ t.Fatalf("Unexpected error: %v", err)
+ }
+
+ scanner := bufio.NewScanner(&stdout)
+ scanner.Split(bufio.ScanLines)
+
+ var foundResult bool
+ for scanner.Scan() {
+ line := scanner.Bytes()
+ var event map[string]any
+ if err := json.Unmarshal(line, &event); err != nil {
+ continue
+ }
+
+ if event["type"] == "ToolCallResult" {
+ foundResult = true
+ if event["status"] != "complete" {
+ t.Errorf("Expected status 'complete', got %v", event["status"])
+ }
+ if _, ok := event["duration"]; !ok {
+ t.Error("Expected duration field to be present")
+ }
+ if event["duration"].(float64) < 0 {
+ t.Errorf("Expected non-negative duration, got %v", event["duration"])
+ }
+ }
+ }
+
+ if !foundResult {
+ t.Error("Expected ToolCallResult event to be emitted")
+ }
+
+ if h.getToolState(callID) != nil {
+ t.Error("Expected tool state to be removed after completion")
+ }
+}
diff --git a/internal/handlers/session_manager.go b/internal/handlers/session_manager.go
new file mode 100644
index 00000000..69a5effb
--- /dev/null
+++ b/internal/handlers/session_manager.go
@@ -0,0 +1,314 @@
+package handlers
+
+import (
+ "bufio"
+ "context"
+ "encoding/json"
+ "fmt"
+ "io"
+ "os"
+ "os/exec"
+ "sync"
+ "time"
+
+ "github.com/inference-gateway/cli/internal/logger"
+)
+
+// SessionManager manages headless CLI sessions
+type SessionManager struct {
+ sessions map[string]*Session
+ mu sync.RWMutex
+}
+
+// Session represents a headless CLI session
+type Session struct {
+ ID string
+ ConversationID string
+ cmd *exec.Cmd
+ stdin io.WriteCloser
+ stdout io.ReadCloser
+ stderr io.ReadCloser
+ cancel context.CancelFunc
+ clients map[string]chan []byte
+ mu sync.RWMutex
+ wg sync.WaitGroup
+}
+
+// NewSessionManager creates a new session manager
+func NewSessionManager() *SessionManager {
+ return &SessionManager{
+ sessions: make(map[string]*Session),
+ }
+}
+
+// CreateSession spawns a new headless CLI process
+func (sm *SessionManager) CreateSession(ctx context.Context, sessionID string, conversationID string) (*Session, error) {
+ sm.mu.Lock()
+ defer sm.mu.Unlock()
+
+ if _, exists := sm.sessions[sessionID]; exists {
+ return nil, fmt.Errorf("session %s already exists", sessionID)
+ }
+
+ sessionCtx, cancel := context.WithCancel(ctx)
+
+ execPath, err := os.Executable()
+ if err != nil {
+ cancel()
+ return nil, fmt.Errorf("failed to get executable path: %w", err)
+ }
+
+ args := []string{"chat", "--headless", "--session-id", sessionID}
+ if conversationID != "" {
+ args = append(args, "--conversation-id", conversationID)
+ }
+
+ cmd := exec.CommandContext(sessionCtx, execPath, args...)
+
+ stdin, err := cmd.StdinPipe()
+ if err != nil {
+ cancel()
+ return nil, fmt.Errorf("failed to create stdin pipe: %w", err)
+ }
+
+ stdout, err := cmd.StdoutPipe()
+ if err != nil {
+ cancel()
+ return nil, fmt.Errorf("failed to create stdout pipe: %w", err)
+ }
+
+ stderr, err := cmd.StderrPipe()
+ if err != nil {
+ cancel()
+ return nil, fmt.Errorf("failed to create stderr pipe: %w", err)
+ }
+
+ if err := cmd.Start(); err != nil {
+ cancel()
+ return nil, fmt.Errorf("failed to start headless process: %w", err)
+ }
+
+ session := &Session{
+ ID: sessionID,
+ ConversationID: conversationID,
+ cmd: cmd,
+ stdin: stdin,
+ stdout: stdout,
+ stderr: stderr,
+ cancel: cancel,
+ clients: make(map[string]chan []byte),
+ }
+
+ sm.sessions[sessionID] = session
+
+ session.wg.Add(2)
+ go session.readOutput()
+ go session.readStderr()
+
+ return session, nil
+}
+
+// GetSession retrieves an existing session
+func (sm *SessionManager) GetSession(sessionID string) (*Session, bool) {
+ sm.mu.RLock()
+ defer sm.mu.RUnlock()
+ session, exists := sm.sessions[sessionID]
+ return session, exists
+}
+
+// CloseSession terminates a session
+func (sm *SessionManager) CloseSession(sessionID string) error {
+ sm.mu.Lock()
+ session, exists := sm.sessions[sessionID]
+ if !exists {
+ sm.mu.Unlock()
+ return fmt.Errorf("session %s not found", sessionID)
+ }
+
+ delete(sm.sessions, sessionID)
+ sm.mu.Unlock()
+
+ logger.Info("🛑 SESSION CLOSING", "session_id", sessionID, "conversation_id", session.ConversationID, "pid", session.cmd.Process.Pid)
+
+ shutdownInput := map[string]any{
+ "type": "shutdown",
+ }
+ if data, err := json.Marshal(shutdownInput); err == nil {
+ data = append(data, '\n')
+
+ session.mu.Lock()
+ _, writeErr := session.stdin.Write(data)
+ session.mu.Unlock()
+
+ if writeErr != nil {
+ logger.Warn("Failed to send shutdown signal", "session_id", sessionID, "error", writeErr)
+ } else {
+ logger.Info("Sent shutdown signal to session", "session_id", sessionID)
+ }
+ }
+
+ done := make(chan error, 1)
+ go func() {
+ done <- session.cmd.Wait()
+ }()
+
+ select {
+ case err := <-done:
+ if err != nil {
+ logger.Info("Headless process exited with error", "session_id", sessionID, "error", err)
+ } else {
+ logger.Info("Headless process exited cleanly", "session_id", sessionID)
+ }
+ case <-time.After(5 * time.Second):
+ logger.Warn("Headless process did not exit gracefully, forcing termination", "session_id", sessionID)
+ session.cancel()
+ <-done
+ }
+
+ if err := session.stdout.Close(); err != nil {
+ logger.Warn("Failed to close stdout", "error", err)
+ }
+ if err := session.stderr.Close(); err != nil {
+ logger.Warn("Failed to close stderr", "error", err)
+ }
+
+ session.wg.Wait()
+
+ if err := session.stdin.Close(); err != nil {
+ logger.Warn("Failed to close stdin", "error", err)
+ }
+ session.mu.Lock()
+ for _, ch := range session.clients {
+ close(ch)
+ }
+ session.mu.Unlock()
+
+ logger.Info("Session cleanup completed", "session_id", sessionID)
+ return nil
+}
+
+// readOutput continuously reads from the CLI stdout and broadcasts to clients
+func (s *Session) readOutput() {
+ defer s.wg.Done()
+ scanner := bufio.NewScanner(s.stdout)
+ scanner.Buffer(make([]byte, 1024*1024), 10*1024*1024) // 1MB initial, 10MB max
+
+ for scanner.Scan() {
+ line := scanner.Bytes()
+ if len(line) == 0 {
+ continue
+ }
+
+ s.mu.RLock()
+ for clientID, ch := range s.clients {
+ select {
+ case ch <- line:
+ default:
+ logger.Warn("Client channel full, skipping", "session_id", s.ID, "client_id", clientID)
+ }
+ }
+ s.mu.RUnlock()
+ }
+
+ if err := scanner.Err(); err != nil {
+ logger.Error("Error reading session output", "session_id", s.ID, "error", err)
+ }
+}
+
+// readStderr continuously reads from the CLI stderr (for debugging)
+func (s *Session) readStderr() {
+ defer s.wg.Done()
+ scanner := bufio.NewScanner(s.stderr)
+ scanner.Buffer(make([]byte, 1024*1024), 10*1024*1024) // 1MB initial, 10MB max
+
+ for scanner.Scan() {
+ }
+
+ if err := scanner.Err(); err != nil {
+ logger.Error("Error reading session stderr", "session_id", s.ID, "error", err)
+ }
+}
+
+// Subscribe adds a client to receive session output
+func (s *Session) Subscribe(clientID string) chan []byte {
+ s.mu.Lock()
+ defer s.mu.Unlock()
+
+ ch := make(chan []byte, 100)
+ s.clients[clientID] = ch
+ return ch
+}
+
+// Unsubscribe removes a client
+func (s *Session) Unsubscribe(clientID string) {
+ s.mu.Lock()
+ defer s.mu.Unlock()
+
+ ch, exists := s.clients[clientID]
+ if !exists {
+ return
+ }
+
+ delete(s.clients, clientID)
+
+ func() {
+ defer func() {
+ if r := recover(); r != nil {
+ logger.Debug("Channel already closed during unsubscribe", "session_id", s.ID, "client_id", clientID)
+ }
+ }()
+ close(ch)
+ }()
+}
+
+// SendMessage sends a message to the headless CLI
+func (s *Session) SendMessage(content string, images []any, model string) error {
+ input := map[string]any{
+ "type": "message",
+ "content": content,
+ "images": images,
+ }
+
+ if model != "" {
+ input["model"] = model
+ }
+
+ data, err := json.Marshal(input)
+ if err != nil {
+ return fmt.Errorf("failed to marshal input: %w", err)
+ }
+
+ data = append(data, '\n')
+
+ s.mu.Lock()
+ defer s.mu.Unlock()
+
+ if _, err := s.stdin.Write(data); err != nil {
+ return fmt.Errorf("failed to write to stdin: %w", err)
+ }
+
+ return nil
+}
+
+// SendInterrupt sends an interrupt signal to the headless CLI
+func (s *Session) SendInterrupt() error {
+ input := map[string]any{
+ "type": "interrupt",
+ }
+
+ data, err := json.Marshal(input)
+ if err != nil {
+ return fmt.Errorf("failed to marshal input: %w", err)
+ }
+
+ data = append(data, '\n')
+
+ s.mu.Lock()
+ defer s.mu.Unlock()
+
+ if _, err := s.stdin.Write(data); err != nil {
+ return fmt.Errorf("failed to write to stdin: %w", err)
+ }
+
+ return nil
+}
diff --git a/internal/handlers/websocket_handler.go b/internal/handlers/websocket_handler.go
new file mode 100644
index 00000000..c320aa4f
--- /dev/null
+++ b/internal/handlers/websocket_handler.go
@@ -0,0 +1,284 @@
+package handlers
+
+import (
+ "encoding/json"
+ "fmt"
+ "net/http"
+ "time"
+
+ "github.com/google/uuid"
+ "github.com/gorilla/websocket"
+ "github.com/inference-gateway/cli/internal/logger"
+)
+
+var upgrader = websocket.Upgrader{
+ ReadBufferSize: 1024,
+ WriteBufferSize: 1024,
+ CheckOrigin: func(r *http.Request) bool {
+ return true
+ },
+}
+
+// WebSocketHandler handles WebSocket connections for live chat
+type WebSocketHandler struct {
+ sessionManager *SessionManager
+}
+
+// NewWebSocketHandler creates a new WebSocket handler
+func NewWebSocketHandler(sessionManager *SessionManager) *WebSocketHandler {
+ return &WebSocketHandler{
+ sessionManager: sessionManager,
+ }
+}
+
+// HandleWebSocket handles WebSocket upgrade and communication
+func (h *WebSocketHandler) HandleWebSocket(w http.ResponseWriter, r *http.Request) {
+ logger.Info("WebSocket handler called", "method", r.Method, "path", r.URL.Path, "headers", r.Header)
+
+ conn, err := upgrader.Upgrade(w, r, nil)
+ if err != nil {
+ logger.Error("Failed to upgrade to WebSocket", "error", err)
+ return
+ }
+ defer h.closeConnection(conn)
+
+ clientID := uuid.New().String()
+ logger.Info("WebSocket client connected", "client_id", clientID)
+
+ var sessionID string
+ var session *Session
+
+ defer h.cleanupSession(session, sessionID, clientID)
+
+ h.messageLoop(conn, r, &session, &sessionID, clientID)
+}
+
+func (h *WebSocketHandler) closeConnection(conn *websocket.Conn) {
+ if err := conn.Close(); err != nil {
+ logger.Warn("Failed to close WebSocket connection", "error", err)
+ }
+}
+
+func (h *WebSocketHandler) cleanupSession(session *Session, sessionID, clientID string) {
+ if session == nil {
+ return
+ }
+
+ session.Unsubscribe(clientID)
+ logger.Info("WebSocket client disconnected, unsubscribed from session", "client_id", clientID, "session_id", sessionID)
+
+ session.mu.RLock()
+ clientCount := len(session.clients)
+ session.mu.RUnlock()
+
+ if clientCount == 0 {
+ logger.Info("Last client disconnected, closing session", "session_id", sessionID)
+ if err := h.sessionManager.CloseSession(sessionID); err != nil {
+ logger.Warn("Failed to close session", "session_id", sessionID, "error", err)
+ }
+ }
+}
+
+func (h *WebSocketHandler) messageLoop(conn *websocket.Conn, r *http.Request, session **Session, sessionID *string, clientID string) {
+ for {
+ var msg WSMessage
+ if err := conn.ReadJSON(&msg); err != nil {
+ logger.Error("Failed to read WebSocket message", "error", err)
+ return
+ }
+
+ shouldReturn := h.handleMessage(conn, r, msg, session, sessionID, clientID)
+ if shouldReturn {
+ return
+ }
+ }
+}
+
+func (h *WebSocketHandler) handleMessage(conn *websocket.Conn, r *http.Request, msg WSMessage, session **Session, sessionID *string, clientID string) bool {
+ switch msg.Type {
+ case "create_session":
+ return h.handleCreateSession(conn, r, msg, session, sessionID, clientID)
+ case "join_session":
+ return h.handleJoinSession(conn, msg, session, sessionID, clientID)
+ case "message":
+ return h.handleChatMessage(conn, msg, *session)
+ case "interrupt":
+ return h.handleInterruptMessage(conn, *session)
+ case "close_session":
+ return h.handleCloseSession(*sessionID)
+ default:
+ h.sendError(conn, fmt.Sprintf("Unknown message type: %s", msg.Type))
+ return false
+ }
+}
+
+func (h *WebSocketHandler) handleCreateSession(conn *websocket.Conn, r *http.Request, msg WSMessage, session **Session, sessionID *string, clientID string) bool {
+ requestedSessionID := msg.SessionID
+ if requestedSessionID == "" {
+ requestedSessionID = uuid.New().String()
+ }
+
+ conversationID := msg.ConversationID
+
+ logger.Info("=== CREATE_SESSION REQUEST ===",
+ "requested_session_id", requestedSessionID,
+ "conversation_id", conversationID,
+ "client_id", clientID)
+
+ existingSession, exists := h.sessionManager.GetSession(requestedSessionID)
+ canReuse := exists && (existingSession.ConversationID == conversationID ||
+ (existingSession.ConversationID == "" && conversationID == ""))
+
+ if canReuse {
+ *session = h.reuseExistingSession(existingSession, requestedSessionID, conversationID)
+ *sessionID = requestedSessionID
+ } else {
+ newSession, err := h.createNewSession(r, requestedSessionID, conversationID, exists)
+ if err != nil {
+ h.sendError(conn, fmt.Sprintf("Failed to create session: %v", err))
+ return true
+ }
+ *session = newSession
+ *sessionID = requestedSessionID
+ }
+
+ outputChan := (*session).Subscribe(clientID)
+
+ h.sendMessage(conn, WSMessage{
+ Type: "session_created",
+ SessionID: *sessionID,
+ ConversationID: (*session).ConversationID,
+ })
+
+ go h.forwardOutput(conn, outputChan)
+ return false
+}
+
+func (h *WebSocketHandler) reuseExistingSession(existingSession *Session, requestedSessionID, conversationID string) *Session {
+ logger.Info("✅ Reusing existing session with same conversation",
+ "session_id", requestedSessionID,
+ "conversation_id", conversationID)
+ return existingSession
+}
+
+func (h *WebSocketHandler) createNewSession(r *http.Request, requestedSessionID, conversationID string, exists bool) (*Session, error) {
+ if exists {
+ if err := h.sessionManager.CloseSession(requestedSessionID); err != nil {
+ logger.Warn("Failed to close existing session", "session_id", requestedSessionID, "error", err)
+ }
+ }
+
+ return h.sessionManager.CreateSession(r.Context(), requestedSessionID, conversationID)
+}
+
+func (h *WebSocketHandler) handleJoinSession(conn *websocket.Conn, msg WSMessage, session **Session, sessionID *string, clientID string) bool {
+ if msg.SessionID == "" {
+ h.sendError(conn, "Session ID required")
+ return false
+ }
+
+ var exists bool
+ *session, exists = h.sessionManager.GetSession(msg.SessionID)
+ if !exists {
+ h.sendError(conn, fmt.Sprintf("Session %s not found", msg.SessionID))
+ return false
+ }
+
+ *sessionID = msg.SessionID
+
+ outputChan := (*session).Subscribe(clientID)
+
+ h.sendMessage(conn, WSMessage{
+ Type: "session_joined",
+ SessionID: *sessionID,
+ })
+
+ go h.forwardOutput(conn, outputChan)
+ return false
+}
+
+func (h *WebSocketHandler) handleChatMessage(conn *websocket.Conn, msg WSMessage, session *Session) bool {
+ if session == nil {
+ h.sendError(conn, "No active session")
+ return false
+ }
+
+ if err := session.SendMessage(msg.Content, msg.Images, msg.Model); err != nil {
+ h.sendError(conn, fmt.Sprintf("Failed to send message: %v", err))
+ }
+ return false
+}
+
+func (h *WebSocketHandler) handleInterruptMessage(conn *websocket.Conn, session *Session) bool {
+ if session == nil {
+ h.sendError(conn, "No active session")
+ return false
+ }
+
+ if err := session.SendInterrupt(); err != nil {
+ h.sendError(conn, fmt.Sprintf("Failed to send interrupt: %v", err))
+ }
+ return false
+}
+
+func (h *WebSocketHandler) handleCloseSession(sessionID string) bool {
+ if sessionID != "" {
+ if err := h.sessionManager.CloseSession(sessionID); err != nil {
+ logger.Warn("Failed to close session", "session_id", sessionID, "error", err)
+ }
+ }
+ return true
+}
+
+// forwardOutput forwards session output to WebSocket client
+func (h *WebSocketHandler) forwardOutput(conn *websocket.Conn, outputChan chan []byte) {
+ for data := range outputChan {
+ var jsonTest map[string]any
+ if err := json.Unmarshal(data, &jsonTest); err != nil {
+ wrapped := WSMessage{
+ Type: "output",
+ Content: string(data),
+ Time: time.Now().UTC().Format(time.RFC3339),
+ }
+ if err := conn.WriteJSON(wrapped); err != nil {
+ logger.Error("Failed to write wrapped message to WebSocket", "error", err)
+ return
+ }
+ } else {
+ if err := conn.WriteMessage(websocket.TextMessage, data); err != nil {
+ logger.Error("Failed to write to WebSocket", "error", err)
+ return
+ }
+ }
+ }
+}
+
+// sendMessage sends a message to the WebSocket client
+func (h *WebSocketHandler) sendMessage(conn *websocket.Conn, msg WSMessage) {
+ if err := conn.WriteJSON(msg); err != nil {
+ logger.Error("Failed to send WebSocket message", "error", err)
+ }
+}
+
+// sendError sends an error message to the client
+func (h *WebSocketHandler) sendError(conn *websocket.Conn, errMsg string) {
+ msg := WSMessage{
+ Type: "error",
+ Error: errMsg,
+ Time: time.Now().UTC().Format(time.RFC3339),
+ }
+ h.sendMessage(conn, msg)
+}
+
+// WSMessage represents a WebSocket message
+type WSMessage struct {
+ Type string `json:"type"`
+ SessionID string `json:"session_id,omitempty"`
+ ConversationID string `json:"conversation_id,omitempty"`
+ Content string `json:"content,omitempty"`
+ Images []any `json:"images,omitempty"`
+ Model string `json:"model,omitempty"`
+ Error string `json:"error,omitempty"`
+ Time string `json:"time,omitempty"`
+ Data any `json:"data,omitempty"`
+}
diff --git a/internal/infra/storage/jsonl.go b/internal/infra/storage/jsonl.go
index 9e94c4c2..c8b95b37 100644
--- a/internal/infra/storage/jsonl.go
+++ b/internal/infra/storage/jsonl.go
@@ -53,12 +53,12 @@ func (s *JsonlStorage) conversationFilePath(conversationID string) string {
// saveConversationUnlocked saves a conversation without acquiring the lock
// Caller must hold the lock before calling this method
func (s *JsonlStorage) saveConversationUnlocked(ctx context.Context, conversationID string, entries []domain.ConversationEntry, metadata ConversationMetadata) error {
- metadataJSON, err := json.Marshal(map[string]interface{}{"metadata": metadata})
+ metadataJSON, err := json.Marshal(map[string]any{"metadata": metadata})
if err != nil {
return fmt.Errorf("failed to marshal metadata: %w", err)
}
- entriesJSON, err := json.Marshal(map[string]interface{}{"entries": entries})
+ entriesJSON, err := json.Marshal(map[string]any{"entries": entries})
if err != nil {
return fmt.Errorf("failed to marshal entries: %w", err)
}
@@ -309,12 +309,12 @@ func (s *JsonlStorage) UpdateConversationMetadata(ctx context.Context, conversat
return fmt.Errorf("failed to unmarshal entries: %w", err)
}
- metadataJSON, err := json.Marshal(map[string]interface{}{"metadata": metadata})
+ metadataJSON, err := json.Marshal(map[string]any{"metadata": metadata})
if err != nil {
return fmt.Errorf("failed to marshal metadata: %w", err)
}
- entriesJSON, err := json.Marshal(map[string]interface{}{"entries": entriesWrapper.Entries})
+ entriesJSON, err := json.Marshal(map[string]any{"entries": entriesWrapper.Entries})
if err != nil {
return fmt.Errorf("failed to marshal entries: %w", err)
}
diff --git a/internal/services/agent.go b/internal/services/agent.go
index 8d3104cb..8fc78972 100644
--- a/internal/services/agent.go
+++ b/internal/services/agent.go
@@ -106,9 +106,10 @@ func (p *eventPublisher) publishParallelToolsStart(toolCalls []sdk.ChatCompletio
tools := make([]domain.ToolInfo, len(toolCalls))
for i, tc := range toolCalls {
tools[i] = domain.ToolInfo{
- CallID: tc.Id,
- Name: tc.Function.Name,
- Status: "queued",
+ CallID: tc.Id,
+ Name: tc.Function.Name,
+ Status: "queued",
+ Arguments: tc.Function.Arguments,
}
}
@@ -139,6 +140,23 @@ func (p *eventPublisher) publishToolStatusChange(callID string, toolName string,
p.chatEvents <- event
}
+// publishToolStatusChangeWithResult publishes a ToolExecutionProgressEvent with formatted result
+func (p *eventPublisher) publishToolStatusChangeWithResult(callID string, toolName string, status string, message string, result string) {
+ event := domain.ToolExecutionProgressEvent{
+ BaseChatEvent: domain.BaseChatEvent{
+ RequestID: p.requestID,
+ Timestamp: time.Now(),
+ },
+ ToolCallID: callID,
+ ToolName: toolName,
+ Status: status,
+ Message: message,
+ Result: result,
+ }
+
+ p.chatEvents <- event
+}
+
// publishBashOutputChunk publishes a BashOutputChunkEvent for streaming bash output
func (p *eventPublisher) publishBashOutputChunk(callID string, output string, isComplete bool) {
event := domain.BashOutputChunkEvent{
@@ -847,19 +865,12 @@ func (s *AgentServiceImpl) executeToolCallsParallel(
}
for _, at := range approvalTools {
-
time.Sleep(constants.AgentToolExecutionDelay)
result := s.executeTool(ctx, *at.tool, eventPublisher, isChatMode)
+ status, message, formattedResult := extractToolResultStatus(result)
- status := "complete"
- message := "Completed successfully"
- if result.ToolExecution != nil && !result.ToolExecution.Success {
- status = "failed"
- message = "Execution failed"
- }
-
- eventPublisher.publishToolStatusChange(at.tool.Id, at.tool.Function.Name, status, message)
+ eventPublisher.publishToolStatusChangeWithResult(at.tool.Id, at.tool.Function.Name, status, message, formattedResult)
results[at.index] = result
}
@@ -889,15 +900,9 @@ func (s *AgentServiceImpl) executeToolCallsParallel(
time.Sleep(constants.AgentToolExecutionDelay)
result := s.executeTool(ctx, *toolCall, eventPublisher, isChatMode)
+ status, message, formattedResult := extractToolResultStatus(result)
- status := "complete"
- message := "Completed successfully"
- if result.ToolExecution != nil && !result.ToolExecution.Success {
- status = "failed"
- message = "Execution failed"
- }
-
- eventPublisher.publishToolStatusChange(toolCall.Id, toolCall.Function.Name, status, message)
+ eventPublisher.publishToolStatusChangeWithResult(toolCall.Id, toolCall.Function.Name, status, message, formattedResult)
resultsChan <- IndexedToolResult{
Index: index,
@@ -937,6 +942,29 @@ func (s *AgentServiceImpl) executeToolCallsParallel(
return results
}
+func extractToolResultStatus(result domain.ConversationEntry) (status, message, formattedResult string) {
+ status = "complete"
+ message = "Completed successfully"
+ formattedResult = ""
+
+ if result.ToolExecution == nil {
+ return status, message, formattedResult
+ }
+
+ if !result.ToolExecution.Success {
+ status = "failed"
+ message = "Execution failed"
+ }
+
+ if result.ToolExecution.Data != nil {
+ if jsonData, err := json.Marshal(result.ToolExecution.Data); err == nil {
+ formattedResult = string(jsonData)
+ }
+ }
+
+ return status, message, formattedResult
+}
+
//nolint:funlen,gocyclo,cyclop // Tool execution requires comprehensive error handling and status updates
func (s *AgentServiceImpl) executeTool(
ctx context.Context,
diff --git a/internal/services/agent_manager.go b/internal/services/agent_manager.go
index 81838055..e4ca6de7 100644
--- a/internal/services/agent_manager.go
+++ b/internal/services/agent_manager.go
@@ -72,6 +72,12 @@ func (am *AgentManager) StartAgents(ctx context.Context) error {
}
}
+ if len(agentsToStart) > 0 && am.containerRuntime != nil {
+ if err := am.containerRuntime.EnsureNetwork(ctx); err != nil {
+ logger.Warn("Failed to create container network", "session", am.sessionID, "error", err)
+ }
+ }
+
for _, agent := range agentsToStart {
go am.startAgentAsync(ctx, agent)
}
diff --git a/internal/services/conversation.go b/internal/services/conversation.go
index 0ab3bafd..5ba3902b 100644
--- a/internal/services/conversation.go
+++ b/internal/services/conversation.go
@@ -183,6 +183,11 @@ func (r *InMemoryConversationRepository) StartNewConversation(title string) erro
return r.Clear()
}
+// GetCurrentConversationID returns empty string for in-memory repository
+func (r *InMemoryConversationRepository) GetCurrentConversationID() string {
+ return ""
+}
+
func (r *InMemoryConversationRepository) ClearExceptFirstUserMessage() error {
r.mutex.Lock()
defer r.mutex.Unlock()
diff --git a/internal/services/ui_manager.go b/internal/services/ui_manager.go
new file mode 100644
index 00000000..515f43fd
--- /dev/null
+++ b/internal/services/ui_manager.go
@@ -0,0 +1,186 @@
+package services
+
+import (
+ "context"
+ "fmt"
+ "net/http"
+ "os"
+ "os/exec"
+ "path/filepath"
+ "time"
+
+ config "github.com/inference-gateway/cli/config"
+ logger "github.com/inference-gateway/cli/internal/logger"
+)
+
+// UIManager manages the lifecycle of the web UI server
+type UIManager struct {
+ config *config.Config
+ cmd *exec.Cmd
+ isRunning bool
+}
+
+// NewUIManager creates a new UI manager
+func NewUIManager(cfg *config.Config) *UIManager {
+ return &UIManager{
+ config: cfg,
+ }
+}
+
+// Start starts the UI server process
+func (um *UIManager) Start(ctx context.Context) error {
+ if um.isRunning {
+ return nil
+ }
+
+ switch um.config.API.UI.Mode {
+ case "npm":
+ return um.startNPM(ctx)
+ case "docker":
+ return fmt.Errorf("docker mode not yet implemented")
+ default:
+ return fmt.Errorf("unsupported UI mode: %s", um.config.API.UI.Mode)
+ }
+}
+
+// startNPM starts the UI using npm dev server
+func (um *UIManager) startNPM(ctx context.Context) error {
+ logger.Info("Starting UI development server")
+
+ workingDir := um.config.API.UI.WorkingDir
+ if !filepath.IsAbs(workingDir) {
+ cwd, err := os.Getwd()
+ if err != nil {
+ return fmt.Errorf("failed to get current directory: %w", err)
+ }
+ workingDir = filepath.Join(cwd, workingDir)
+ }
+
+ if _, err := os.Stat(workingDir); os.IsNotExist(err) {
+ return fmt.Errorf("UI directory not found: %s", workingDir)
+ }
+
+ packageJSON := filepath.Join(workingDir, "package.json")
+ if _, err := os.Stat(packageJSON); os.IsNotExist(err) {
+ return fmt.Errorf("package.json not found in UI directory: %s", workingDir)
+ }
+
+ nodeModules := filepath.Join(workingDir, "node_modules")
+ if _, err := os.Stat(nodeModules); os.IsNotExist(err) {
+ logger.Info("Installing UI dependencies")
+ fmt.Println("Installing UI dependencies (this may take a moment)...")
+ installCmd := exec.Command("npm", "install")
+ installCmd.Dir = workingDir
+ installCmd.Stdout = os.Stdout
+ installCmd.Stderr = os.Stderr
+ if err := installCmd.Run(); err != nil {
+ return fmt.Errorf("failed to install UI dependencies: %w", err)
+ }
+ }
+
+ fmt.Println("Starting UI development server...")
+
+ um.cmd = exec.Command("npm", "run", "dev")
+ um.cmd.Dir = workingDir
+
+ apiURL := fmt.Sprintf("http://localhost:%d", um.config.API.Port)
+ um.cmd.Env = append(os.Environ(),
+ fmt.Sprintf("PORT=%d", um.config.API.UI.Port),
+ fmt.Sprintf("NEXT_PUBLIC_API_URL=%s", apiURL),
+ )
+
+ logger.Info("UI environment configured",
+ "ui_port", um.config.API.UI.Port,
+ "api_url", apiURL,
+ )
+
+ if um.config.Gateway.Debug {
+ um.cmd.Stdout = os.Stdout
+ um.cmd.Stderr = os.Stderr
+ }
+
+ if err := um.cmd.Start(); err != nil {
+ return fmt.Errorf("failed to start UI server: %w", err)
+ }
+
+ fmt.Println("Waiting for UI server to become ready...")
+
+ if err := um.waitForReady(ctx); err != nil {
+ if stopErr := um.Stop(); stopErr != nil {
+ logger.Warn("Failed to stop UI server during error cleanup", "error", stopErr)
+ }
+ return fmt.Errorf("UI server failed to become ready: %w", err)
+ }
+
+ um.isRunning = true
+ fmt.Printf("UI server is ready at %s\n\n", um.GetURL())
+ logger.Info("UI server started successfully", "port", um.config.API.UI.Port)
+ return nil
+}
+
+// waitForReady waits for the UI server to become ready by polling the root URL
+func (um *UIManager) waitForReady(ctx context.Context) error {
+ url := fmt.Sprintf("http://%s:%d", um.config.API.Host, um.config.API.UI.Port)
+
+ timeout := 60 * time.Second
+ deadline := time.Now().Add(timeout)
+ ticker := time.NewTicker(500 * time.Millisecond)
+ defer ticker.Stop()
+
+ client := &http.Client{
+ Timeout: 2 * time.Second,
+ }
+
+ for {
+ select {
+ case <-ctx.Done():
+ return ctx.Err()
+ case <-ticker.C:
+ if time.Now().After(deadline) {
+ return fmt.Errorf("timeout waiting for UI server to become ready")
+ }
+
+ resp, err := client.Get(url)
+ if err == nil {
+ if closeErr := resp.Body.Close(); closeErr != nil {
+ logger.Warn("Failed to close response body", "error", closeErr)
+ }
+ if resp.StatusCode < 500 {
+ return nil
+ }
+ }
+ }
+ }
+}
+
+// Stop stops the UI server process
+func (um *UIManager) Stop() error {
+ if !um.isRunning {
+ return nil
+ }
+
+ if um.cmd == nil || um.cmd.Process == nil {
+ return nil
+ }
+
+ logger.Info("Stopping UI server", "pid", um.cmd.Process.Pid)
+
+ if err := um.cmd.Process.Kill(); err != nil {
+ logger.Warn("Failed to kill UI server process", "error", err)
+ return err
+ }
+
+ um.isRunning = false
+ logger.Info("UI server stopped successfully")
+ return nil
+}
+
+// IsRunning returns whether the UI server is running
+func (um *UIManager) IsRunning() bool {
+ return um.isRunning
+}
+
+// GetURL returns the URL where the UI is accessible
+func (um *UIManager) GetURL() string {
+ return fmt.Sprintf("http://localhost:%d", um.config.API.UI.Port)
+}
diff --git a/internal/utils/browser.go b/internal/utils/browser.go
new file mode 100644
index 00000000..5c95ec27
--- /dev/null
+++ b/internal/utils/browser.go
@@ -0,0 +1,23 @@
+package utils
+
+import (
+ "fmt"
+ "os/exec"
+ "runtime"
+)
+
+// OpenBrowser opens a URL in the default browser (cross-platform)
+func OpenBrowser(url string) error {
+ var cmd *exec.Cmd
+
+ switch runtime.GOOS {
+ case "darwin":
+ cmd = exec.Command("open", url)
+ case "linux":
+ cmd = exec.Command("xdg-open", url)
+ default:
+ return fmt.Errorf("unsupported platform: %s", runtime.GOOS)
+ }
+
+ return cmd.Start()
+}
diff --git a/tests/mocks/domain/fake_conversation_repository.go b/tests/mocks/domain/fake_conversation_repository.go
index d2f89814..5d2de2a2 100644
--- a/tests/mocks/domain/fake_conversation_repository.go
+++ b/tests/mocks/domain/fake_conversation_repository.go
@@ -113,6 +113,16 @@ type FakeConversationRepository struct {
formatToolResultForUIReturnsOnCall map[int]struct {
result1 string
}
+ GetCurrentConversationIDStub func() string
+ getCurrentConversationIDMutex sync.RWMutex
+ getCurrentConversationIDArgsForCall []struct {
+ }
+ getCurrentConversationIDReturns struct {
+ result1 string
+ }
+ getCurrentConversationIDReturnsOnCall map[int]struct {
+ result1 string
+ }
GetMessageCountStub func() int
getMessageCountMutex sync.RWMutex
getMessageCountArgsForCall []struct {
@@ -736,6 +746,59 @@ func (fake *FakeConversationRepository) FormatToolResultForUIReturnsOnCall(i int
}{result1}
}
+func (fake *FakeConversationRepository) GetCurrentConversationID() string {
+ fake.getCurrentConversationIDMutex.Lock()
+ ret, specificReturn := fake.getCurrentConversationIDReturnsOnCall[len(fake.getCurrentConversationIDArgsForCall)]
+ fake.getCurrentConversationIDArgsForCall = append(fake.getCurrentConversationIDArgsForCall, struct {
+ }{})
+ stub := fake.GetCurrentConversationIDStub
+ fakeReturns := fake.getCurrentConversationIDReturns
+ fake.recordInvocation("GetCurrentConversationID", []interface{}{})
+ fake.getCurrentConversationIDMutex.Unlock()
+ if stub != nil {
+ return stub()
+ }
+ if specificReturn {
+ return ret.result1
+ }
+ return fakeReturns.result1
+}
+
+func (fake *FakeConversationRepository) GetCurrentConversationIDCallCount() int {
+ fake.getCurrentConversationIDMutex.RLock()
+ defer fake.getCurrentConversationIDMutex.RUnlock()
+ return len(fake.getCurrentConversationIDArgsForCall)
+}
+
+func (fake *FakeConversationRepository) GetCurrentConversationIDCalls(stub func() string) {
+ fake.getCurrentConversationIDMutex.Lock()
+ defer fake.getCurrentConversationIDMutex.Unlock()
+ fake.GetCurrentConversationIDStub = stub
+}
+
+func (fake *FakeConversationRepository) GetCurrentConversationIDReturns(result1 string) {
+ fake.getCurrentConversationIDMutex.Lock()
+ defer fake.getCurrentConversationIDMutex.Unlock()
+ fake.GetCurrentConversationIDStub = nil
+ fake.getCurrentConversationIDReturns = struct {
+ result1 string
+ }{result1}
+}
+
+func (fake *FakeConversationRepository) GetCurrentConversationIDReturnsOnCall(i int, result1 string) {
+ fake.getCurrentConversationIDMutex.Lock()
+ defer fake.getCurrentConversationIDMutex.Unlock()
+ fake.GetCurrentConversationIDStub = nil
+ if fake.getCurrentConversationIDReturnsOnCall == nil {
+ fake.getCurrentConversationIDReturnsOnCall = make(map[int]struct {
+ result1 string
+ })
+ }
+ fake.getCurrentConversationIDReturnsOnCall[i] = struct {
+ result1 string
+ }{result1}
+}
+
func (fake *FakeConversationRepository) GetMessageCount() int {
fake.getMessageCountMutex.Lock()
ret, specificReturn := fake.getMessageCountReturnsOnCall[len(fake.getMessageCountArgsForCall)]
diff --git a/ui/.gitignore b/ui/.gitignore
new file mode 100644
index 00000000..b90a368f
--- /dev/null
+++ b/ui/.gitignore
@@ -0,0 +1,2 @@
+node_modules
+.next
diff --git a/ui/README.md b/ui/README.md
new file mode 100644
index 00000000..adc197b9
--- /dev/null
+++ b/ui/README.md
@@ -0,0 +1,289 @@
+# Inference Gateway UI
+
+Web interface for the Inference Gateway CLI, featuring direct database access and headless CLI session management using the AG-UI protocol.
+
+## Features
+
+- **AG-UI Protocol Compliant**: Headless CLI sessions via stdin/stdout using the [AG-UI Protocol](https://docs.ag-ui.com)
+- **Direct Database Access**: TypeScript storage client mirrors CLI's Go storage layer
+- **Multiple Storage Backends**: PostgreSQL, SQLite, Redis, JSONL, In-Memory
+- **Concurrent Chat Sessions**: Spawn and manage multiple headless CLI processes
+- **Real-time Streaming**: Live streaming responses from LLMs
+- **Multimodal Support**: Text and image inputs
+- **Conversation Management**: Browse, search, and export conversations
+- **Analytics Dashboard**: Token usage and cost statistics
+
+## Architecture
+
+```text
+┌─────────────────────────────────────────────────────────┐
+│ UI (Next.js) │
+│ ┌────────────────────┐ ┌─────────────────────┐ │
+│ │ TypeScript Storage │ │ CLI Process Mgr │ │
+│ │ Client (PG/SQLite/ │ │ (spawn headless │ │
+│ │ Redis/JSONL) │ │ CLI sessions) │ │
+│ └──────────┬─────────┘ └──────────┬──────────┘ │
+└─────────────┼────────────────────────────┼──────────────┘
+ │ │
+ │ Direct DB Access │ AG-UI Protocol
+ │ │ (stdin/stdout JSON)
+┌─────────────▼────────────────────────────▼──────────────┐
+│ Shared Database │
+│ (PostgreSQL / SQLite / Redis / JSONL) │
+└──────────────────────────┬───────────────────────────────┘
+ │
+ ┌────────────┴─────────────┐
+ │ │
+┌─────────────▼──────────┐ ┌───────────▼──────────┐
+│ CLI Session 1 │ │ CLI Session 2 │
+│ (headless, UI-spawned) │ │ (headless, UI-spawned)│
+└────────────────────────┘ └───────────────────────┘
+```
+
+## Prerequisites
+
+- Node.js 20+ or 22+
+- Inference Gateway CLI installed (`infer` binary in PATH or specified location)
+- Database setup (PostgreSQL, SQLite, Redis, or JSONL directory)
+
+## Installation
+
+```bash
+cd ui
+npm install
+```
+
+## Configuration
+
+Create a `.env.local` file:
+
+```env
+# Storage Configuration (choose one)
+NEXT_PUBLIC_STORAGE_TYPE=postgres
+
+# PostgreSQL
+NEXT_PUBLIC_STORAGE_POSTGRES_HOST=localhost
+NEXT_PUBLIC_STORAGE_POSTGRES_PORT=5432
+NEXT_PUBLIC_STORAGE_POSTGRES_DB=infer
+NEXT_PUBLIC_STORAGE_POSTGRES_USER=postgres
+NEXT_PUBLIC_STORAGE_POSTGRES_PASSWORD=password
+
+# SQLite
+NEXT_PUBLIC_STORAGE_SQLITE_PATH=/Users/username/.infer/conversations.db
+
+# Redis
+NEXT_PUBLIC_STORAGE_REDIS_HOST=localhost
+NEXT_PUBLIC_STORAGE_REDIS_PORT=6379
+NEXT_PUBLIC_STORAGE_REDIS_DB=0
+
+# JSONL
+NEXT_PUBLIC_STORAGE_JSONL_DIR=/Users/username/.infer/conversations
+
+# CLI Path
+NEXT_PUBLIC_CLI_PATH=/usr/local/bin/infer
+```
+
+## Development
+
+```bash
+npm run dev
+```
+
+Open [http://localhost:3000](http://localhost:3000)
+
+## Production Build
+
+```bash
+npm run build
+npm run start
+```
+
+## Project Structure
+
+```text
+ui/
+├── app/ # Next.js 16 app directory
+│ ├── layout.tsx # Root layout
+│ ├── page.tsx # Home page
+│ ├── providers.tsx # React Query provider
+│ ├── chat/ # Live chat page
+│ ├── conversations/ # Conversation browser page
+│ └── dashboard/ # Analytics dashboard page
+├── components/ # React components
+│ ├── chat/ # Chat UI components
+│ ├── conversations/ # Conversation list/detail components
+│ └── stats/ # Dashboard components
+├── lib/ # Core libraries
+│ ├── storage/ # TypeScript storage client
+│ │ ├── interfaces.ts # Type definitions
+│ │ ├── factory.ts # Storage factory
+│ │ ├── hooks.ts # React Query hooks
+│ │ ├── postgres/ # PostgreSQL implementation
+│ │ ├── sqlite/ # SQLite implementation
+│ │ ├── redis/ # Redis implementation
+│ │ ├── jsonl/ # JSONL implementation
+│ │ └── memory/ # In-memory implementation
+│ └── cli/ # CLI process manager
+│ ├── process-manager.ts # Session management
+│ ├── hooks.ts # React hooks
+│ └── index.ts # Exports
+└── package.json
+```
+
+## AG-UI Protocol
+
+The UI communicates with headless CLI sessions using the [AG-UI Protocol](https://docs.ag-ui.com):
+
+### Event Types (CLI → UI)
+
+**Run Lifecycle:**
+
+- `RunStarted`: Agent run begins
+- `RunFinished`: Agent run completes
+- `RunError`: Error during run
+
+**Text Messages:**
+
+- `TextMessageStart`: Start of streaming message
+- `TextMessageContent`: Delta content chunk
+- `TextMessageEnd`: End of streaming message
+
+**Tool Execution:**
+
+- `ToolCallStart`: Tool invocation begins (includes `status` and `metadata` fields)
+- `ToolCallArgs`: Tool arguments
+- `ToolCallEnd`: Tool call ready
+- `ToolCallProgress`: Intermediate progress updates during execution (NEW)
+- `ToolCallResult`: Tool execution result (includes `status`, `duration`, and `metadata` fields)
+- `ParallelToolsMetadata`: Summary metadata for parallel tool execution (NEW)
+
+### Input Types (UI → CLI)
+
+- `message`: User message with optional images
+- `interrupt`: Stop current processing
+- `shutdown`: Graceful shutdown
+
+### Tool Execution Progress Events (NEW)
+
+The AG-UI protocol now includes real-time progress tracking for tool execution:
+
+**ToolCallStart** (Enhanced):
+
+```json
+{
+ "type": "ToolCallStart",
+ "toolCallId": "call_abc123",
+ "toolCallName": "Bash",
+ "parentMessageId": "msg_xyz789",
+ "status": "queued",
+ "timestamp": 1703001234567
+}
+```
+
+**ToolCallProgress** (New):
+
+```json
+{
+ "type": "ToolCallProgress",
+ "toolCallId": "call_abc123",
+ "status": "running",
+ "message": "Executing command...",
+ "output": "total 48\ndrwxr-xr-x 12 user staff 384 Dec 21 10:30 .\n",
+ "metadata": {
+ "isComplete": false
+ },
+ "timestamp": 1703001235123
+}
+```
+
+**ToolCallResult** (Enhanced):
+
+```json
+{
+ "type": "ToolCallResult",
+ "messageId": "msg_result_456",
+ "toolCallId": "call_abc123",
+ "content": "Command executed successfully",
+ "role": "tool",
+ "status": "complete",
+ "duration": 2.34,
+ "timestamp": 1703001236567
+}
+```
+
+**ParallelToolsMetadata** (New):
+
+```json
+{
+ "type": "ParallelToolsMetadata",
+ "totalCount": 3,
+ "successCount": 2,
+ "failureCount": 1,
+ "totalDuration": 5.67,
+ "timestamp": 1703001240000
+}
+```
+
+**Status Values:**
+
+- `queued`: Tool is queued for execution
+- `running`: Tool is actively executing
+- `complete`: Tool execution completed successfully
+- `failed`: Tool execution failed with error
+
+## Usage Examples
+
+### Starting a Chat Session
+
+```typescript
+import { useCLISession } from "@/lib/cli/hooks";
+
+function ChatPage() {
+ const { start, sendMessage, messages } = useCLISession();
+
+ useEffect(() => {
+ start();
+ }, []);
+
+ const handleSend = () => {
+ sendMessage({ content: "Hello!" });
+ };
+
+ // Render chat UI
+}
+```
+
+### Accessing Storage
+
+```typescript
+import { useConversations } from "@/lib/storage/hooks";
+
+function ConversationsList() {
+ const { data: conversations } = useConversations(50, 0);
+
+ // Render conversations
+}
+```
+
+## Extracting to Separate Repository
+
+This UI is currently embedded in the CLI repository for convenience. To extract:
+
+```bash
+# From CLI repo root
+cp -r ui /path/to/new/ui-repo
+cd /path/to/new/ui-repo
+npm install
+```
+
+Update paths if needed and ensure `.env.local` points to correct CLI binary and database.
+
+## License
+
+Same as Inference Gateway CLI
+
+## Links
+
+- [AG-UI Protocol Documentation](https://docs.ag-ui.com)
+- [Inference Gateway CLI](https://github.com/inference-gateway/cli)
+- [Next.js 16 Documentation](https://nextjs.org/docs)
diff --git a/ui/app/globals.css b/ui/app/globals.css
new file mode 100644
index 00000000..9c74d1fc
--- /dev/null
+++ b/ui/app/globals.css
@@ -0,0 +1,124 @@
+@import "tailwindcss";
+@plugin "tailwindcss-animate";
+
+@custom-variant dark (&:is(.dark *));
+
+@theme inline {
+ --radius-sm: calc(var(--radius) - 4px);
+ --radius-md: calc(var(--radius) - 2px);
+ --radius-lg: var(--radius);
+ --radius-xl: calc(var(--radius) + 4px);
+ --radius-2xl: calc(var(--radius) + 8px);
+ --radius-3xl: calc(var(--radius) + 12px);
+ --radius-4xl: calc(var(--radius) + 16px);
+ --color-background: var(--background);
+ --color-foreground: var(--foreground);
+ --color-card: var(--card);
+ --color-card-foreground: var(--card-foreground);
+ --color-popover: var(--popover);
+ --color-popover-foreground: var(--popover-foreground);
+ --color-primary: var(--primary);
+ --color-primary-foreground: var(--primary-foreground);
+ --color-secondary: var(--secondary);
+ --color-secondary-foreground: var(--secondary-foreground);
+ --color-muted: var(--muted);
+ --color-muted-foreground: var(--muted-foreground);
+ --color-accent: var(--accent);
+ --color-accent-foreground: var(--accent-foreground);
+ --color-destructive: var(--destructive);
+ --color-border: var(--border);
+ --color-input: var(--input);
+ --color-ring: var(--ring);
+ --color-chart-1: var(--chart-1);
+ --color-chart-2: var(--chart-2);
+ --color-chart-3: var(--chart-3);
+ --color-chart-4: var(--chart-4);
+ --color-chart-5: var(--chart-5);
+ --color-sidebar: var(--sidebar);
+ --color-sidebar-foreground: var(--sidebar-foreground);
+ --color-sidebar-primary: var(--sidebar-primary);
+ --color-sidebar-primary-foreground: var(--sidebar-primary-foreground);
+ --color-sidebar-accent: var(--sidebar-accent);
+ --color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
+ --color-sidebar-border: var(--sidebar-border);
+ --color-sidebar-ring: var(--sidebar-ring);
+}
+
+:root {
+ --radius: 0.625rem;
+ --background: oklch(1 0 0);
+ --foreground: oklch(0.145 0 0);
+ --card: oklch(1 0 0);
+ --card-foreground: oklch(0.145 0 0);
+ --popover: oklch(1 0 0);
+ --popover-foreground: oklch(0.145 0 0);
+ --primary: oklch(0.205 0 0);
+ --primary-foreground: oklch(0.985 0 0);
+ --secondary: oklch(0.97 0 0);
+ --secondary-foreground: oklch(0.205 0 0);
+ --muted: oklch(0.97 0 0);
+ --muted-foreground: oklch(0.556 0 0);
+ --accent: oklch(0.97 0 0);
+ --accent-foreground: oklch(0.205 0 0);
+ --destructive: oklch(0.577 0.245 27.325);
+ --border: oklch(0.922 0 0);
+ --input: oklch(0.922 0 0);
+ --ring: oklch(0.708 0 0);
+ --chart-1: oklch(0.646 0.222 41.116);
+ --chart-2: oklch(0.6 0.118 184.704);
+ --chart-3: oklch(0.398 0.07 227.392);
+ --chart-4: oklch(0.828 0.189 84.429);
+ --chart-5: oklch(0.769 0.188 70.08);
+ --sidebar: oklch(0.985 0 0);
+ --sidebar-foreground: oklch(0.145 0 0);
+ --sidebar-primary: oklch(0.205 0 0);
+ --sidebar-primary-foreground: oklch(0.985 0 0);
+ --sidebar-accent: oklch(0.97 0 0);
+ --sidebar-accent-foreground: oklch(0.205 0 0);
+ --sidebar-border: oklch(0.922 0 0);
+ --sidebar-ring: oklch(0.708 0 0);
+}
+
+.dark {
+ --background: oklch(0.145 0 0);
+ --foreground: oklch(0.985 0 0);
+ --card: oklch(0.205 0 0);
+ --card-foreground: oklch(0.985 0 0);
+ --popover: oklch(0.205 0 0);
+ --popover-foreground: oklch(0.985 0 0);
+ --primary: oklch(0.922 0 0);
+ --primary-foreground: oklch(0.205 0 0);
+ --secondary: oklch(0.269 0 0);
+ --secondary-foreground: oklch(0.985 0 0);
+ --muted: oklch(0.269 0 0);
+ --muted-foreground: oklch(0.708 0 0);
+ --accent: oklch(0.269 0 0);
+ --accent-foreground: oklch(0.985 0 0);
+ --destructive: oklch(0.704 0.191 22.216);
+ --border: oklch(1 0 0 / 10%);
+ --input: oklch(1 0 0 / 15%);
+ --ring: oklch(0.556 0 0);
+ --chart-1: oklch(0.488 0.243 264.376);
+ --chart-2: oklch(0.696 0.17 162.48);
+ --chart-3: oklch(0.769 0.188 70.08);
+ --chart-4: oklch(0.627 0.265 303.9);
+ --chart-5: oklch(0.645 0.246 16.439);
+ --sidebar: oklch(0.205 0 0);
+ --sidebar-foreground: oklch(0.985 0 0);
+ --sidebar-primary: oklch(0.488 0.243 264.376);
+ --sidebar-primary-foreground: oklch(0.985 0 0);
+ --sidebar-accent: oklch(0.269 0 0);
+ --sidebar-accent-foreground: oklch(0.985 0 0);
+ --sidebar-border: oklch(1 0 0 / 10%);
+ --sidebar-ring: oklch(0.556 0 0);
+}
+
+* {
+ border-color: var(--color-border);
+ outline-color: color-mix(in oklch, var(--color-ring) 50%, transparent);
+}
+
+body {
+ background-color: var(--color-background);
+ color: var(--color-foreground);
+}
diff --git a/ui/app/layout.tsx b/ui/app/layout.tsx
new file mode 100644
index 00000000..1f702ffe
--- /dev/null
+++ b/ui/app/layout.tsx
@@ -0,0 +1,22 @@
+import type { Metadata } from "next";
+import "./globals.css";
+import { Providers } from "./providers";
+
+export const metadata: Metadata = {
+ title: "Inference Gateway UI",
+ description: "Web UI for Inference Gateway CLI",
+};
+
+export default function RootLayout({
+ children,
+}: Readonly<{
+ children: React.ReactNode;
+}>) {
+ return (
+
+
+ {children}
+
+
+ );
+}
diff --git a/ui/app/page.tsx b/ui/app/page.tsx
new file mode 100644
index 00000000..ea403ddf
--- /dev/null
+++ b/ui/app/page.tsx
@@ -0,0 +1,968 @@
+"use client";
+
+import { useState, useEffect, useRef, useCallback } from "react";
+import { apiClient } from "../lib/api/client";
+import type { ConversationSummary } from "../lib/storage/interfaces";
+import { WebSocketChatClient } from "../lib/chat/websocket-client";
+import { Button } from "@/components/ui/button";
+import {
+ Command,
+ CommandEmpty,
+ CommandGroup,
+ CommandInput,
+ CommandItem,
+ CommandList,
+} from "@/components/ui/command";
+import {
+ Popover,
+ PopoverContent,
+ PopoverTrigger,
+} from "@/components/ui/popover";
+import { Check, ChevronsUpDown, Send as SendIcon, Square } from "lucide-react";
+import { cn } from "@/lib/utils";
+import { ThemeToggle } from "@/components/theme-toggle";
+import StatusBar from "@/components/status-bar";
+import { ToolCallDisplay } from "@/components/tool-call-display";
+import type { ToolCallState } from "@/lib/agui-types";
+
+// Utility to strip ANSI color codes from terminal output
+function stripAnsiCodes(text: string): string {
+ return text.replace(/\x1B\[[0-9;]*[a-zA-Z]/g, '').replace(/\[[\d;]+m/g, '');
+}
+
+export default function Home() {
+ const [conversations, setConversations] = useState([]);
+ const [selectedConversation, setSelectedConversation] = useState(null);
+ const [loading, setLoading] = useState(true);
+ const [view, setView] = useState<"chat" | "dashboard">("chat");
+ const [liveSessionId, setLiveSessionId] = useState(null);
+ const [sidebarOpen, setSidebarOpen] = useState(false);
+ const [hasActiveNewChat, setHasActiveNewChat] = useState(false);
+ const [sessionRestored, setSessionRestored] = useState(false);
+ const [chatKey, setChatKey] = useState(0);
+
+ useEffect(() => {
+ if (loading || sessionRestored) return;
+
+ const savedSessionId = localStorage.getItem("currentSessionId");
+ const savedSessionType = localStorage.getItem("currentSessionType");
+
+ if (savedSessionId && savedSessionType === "new") {
+ setLiveSessionId("new");
+ setHasActiveNewChat(true);
+ setSessionRestored(true);
+ } else if (savedSessionId && savedSessionType === "conversation") {
+ if (conversations.some(c => c.id === savedSessionId)) {
+ setSelectedConversation(savedSessionId);
+ setLiveSessionId(savedSessionId);
+ setHasActiveNewChat(false);
+ setSessionRestored(true);
+ } else if (conversations.length > 0 || !loading) {
+ console.warn("Saved conversation not found, clearing localStorage");
+ localStorage.removeItem("currentSessionId");
+ localStorage.removeItem("currentSessionType");
+ setSessionRestored(true);
+ }
+ } else {
+ setSessionRestored(true);
+ }
+ }, [conversations, loading, sessionRestored]);
+
+ useEffect(() => {
+ loadConversations();
+ }, []);
+
+ const loadConversations = async () => {
+ try {
+ setLoading(true);
+ const response = await apiClient.listConversations(50, 0);
+ setConversations(response.conversations);
+ } catch (error) {
+ console.error("Failed to load conversations:", error);
+ } finally {
+ setLoading(false);
+ }
+ };
+
+ const handleNewChat = () => {
+ loadConversations();
+
+ setLiveSessionId("new");
+ setSelectedConversation(null);
+ setHasActiveNewChat(true);
+ setSidebarOpen(false);
+ setChatKey(prev => prev + 1);
+ localStorage.setItem("currentSessionId", "new");
+ localStorage.setItem("currentSessionType", "new");
+
+ localStorage.removeItem("wsSession_new-chat");
+ localStorage.removeItem("conversationId_new-chat");
+ };
+
+ const handleContinueConversation = (conversationId: string) => {
+ setSelectedConversation(conversationId);
+ setLiveSessionId(conversationId);
+ setHasActiveNewChat(false);
+ setSidebarOpen(false);
+ localStorage.setItem("currentSessionId", conversationId);
+ localStorage.setItem("currentSessionType", "conversation");
+ };
+
+ const handleDeleteConversation = async (conversationId: string, event: React.MouseEvent) => {
+ event.stopPropagation();
+
+ if (!confirm("Are you sure you want to delete this conversation? This cannot be undone.")) {
+ return;
+ }
+
+ try {
+ await apiClient.deleteConversation(conversationId);
+
+ setConversations(prev => prev.filter(c => c.id !== conversationId));
+
+ if (selectedConversation === conversationId) {
+ setSelectedConversation(null);
+ setLiveSessionId(null);
+ }
+ } catch (error) {
+ console.error("Failed to delete conversation:", error);
+ alert("Failed to delete conversation. Please try again.");
+ }
+ };
+
+ return (
+
+ {!sidebarOpen && (
+
+ )}
+
+ {sidebarOpen && (
+
setSidebarOpen(false)}
+ />
+ )}
+
+
+
+
+
+
Inference Gateway
+
Chat UI
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {/* Conversations List */}
+
+ {view === "chat" && (
+ <>
+ {loading ? (
+
+
+
Loading conversations...
+
+ ) : conversations.length === 0 && !hasActiveNewChat ? (
+
+
No conversations yet
+
Start a headless CLI session to create one
+
+ ) : (
+ <>
+ {hasActiveNewChat && (
+
+
+
+ ● New Chat
+
+
+ Active session
+
+
+ {new Date().toLocaleDateString()}
+
+
+
+ )}
+ {conversations.map((conv) => (
+
+
+
+
+ ))}
+ >
+ )}
+ >
+ )}
+
+ {view === "dashboard" && (
+
+
+
Total Conversations
+
{conversations.length}
+
+
+
+
Total Messages
+
+ {conversations.reduce((sum, c) => sum + c.message_count, 0)}
+
+
+
+
+
Total Tokens
+
+ {conversations.reduce((sum, c) => sum + c.token_stats.total_input_tokens + c.token_stats.total_output_tokens, 0).toLocaleString()}
+
+
+
+
+
Total Cost
+
+ ${conversations.reduce((sum, c) => sum + (c.cost_stats?.total_cost || 0), 0).toFixed(4)}
+
+
+
+ )}
+
+
+ {/* Footer */}
+
+
+ API Server
+ :8081
+
+
+
+
+ {/* Main Chat Area */}
+
+ {liveSessionId ? (
+ {
+ setLiveSessionId(null);
+ setSelectedConversation(null);
+ setHasActiveNewChat(false);
+ localStorage.removeItem("currentSessionId");
+ localStorage.removeItem("currentSessionType");
+ loadConversations();
+ }}
+ />
+ ) : (
+
+ )}
+
+
+ );
+}
+
+// ChatView is now integrated into LiveChatView - removed to avoid duplication
+
+type TimelineItem =
+ | { type: 'message'; id: string; role: string; content: string }
+ | { type: 'tool'; id: string; name: string; status: ToolCallState['status']; startTime?: number; duration?: number; message?: string; output?: string; arguments?: string; result?: any };
+
+function LiveChatView({ conversationId, onClose }: { conversationId?: string; onClose: () => void }) {
+ const [timeline, setTimeline] = useState
([]);
+ const [inputMessage, setInputMessage] = useState("");
+ const [isConnecting, setIsConnecting] = useState(true);
+ const [isSending, setIsSending] = useState(false);
+ const [error, setError] = useState(null);
+ const [models, setModels] = useState([]);
+ const [selectedModel, setSelectedModel] = useState("");
+ const [loadingModels, setLoadingModels] = useState(true);
+ const [modelSelectorOpen, setModelSelectorOpen] = useState(false);
+ const wsClientRef = useRef(null);
+ const messagesEndRef = useRef(null);
+ const inputRef = useRef(null);
+ const sendingTimeoutRef = useRef(null);
+ const cleanupTimeoutRef = useRef(null);
+ const isMountedRef = useRef(true);
+ const actualConversationIdRef = useRef(conversationId);
+ const [history, setHistory] = useState([]);
+ const [historyIndex, setHistoryIndex] = useState(-1);
+ const [currentDraft, setCurrentDraft] = useState("");
+
+ useEffect(() => {
+ apiClient
+ .listModels()
+ .then((data) => {
+ setModels(data.models);
+
+ const savedModel = localStorage.getItem("selectedModel");
+
+ if (savedModel && data.models.includes(savedModel)) {
+ setSelectedModel(savedModel);
+ } else if (data.models.length > 0) {
+ setSelectedModel(data.models[0]);
+ }
+
+ setLoadingModels(false);
+ })
+ .catch((err) => {
+ console.error("Failed to load models:", err);
+ setLoadingModels(false);
+ });
+
+ apiClient
+ .getHistory()
+ .then((data) => {
+ setHistory(data.history);
+ })
+ .catch((err) => {
+ console.error("Failed to load history:", err);
+ });
+ }, []);
+
+ useEffect(() => {
+ isMountedRef.current = true;
+
+ if (sendingTimeoutRef.current) {
+ clearTimeout(sendingTimeoutRef.current);
+ sendingTimeoutRef.current = null;
+ }
+
+ const loadExistingConversation = async () => {
+ if (conversationId) {
+ try {
+ const data = await apiClient.getConversation(conversationId);
+ const loadedTimeline: TimelineItem[] = data.entries
+ .filter((entry: any) => {
+ return entry.message.hidden !== true;
+ })
+ .map((entry: any, index: number) => ({
+ type: 'message' as const,
+ id: `msg-${index}`,
+ role: entry.message.role,
+ content: typeof entry.message.content === "string"
+ ? stripAnsiCodes(entry.message.content)
+ : JSON.stringify(entry.message.content)
+ }));
+ setTimeline(loadedTimeline);
+ } catch (error: any) {
+ console.error("[LiveChatView] Failed to load conversation:", error);
+ setError(error.message || "Failed to load conversation");
+ setIsConnecting(false);
+ }
+ } else {
+ setTimeline([]);
+ }
+ };
+
+ loadExistingConversation();
+
+ // Create session ID based on conversation to ensure proper reuse
+ // Same conversation = same session (reuse containers)
+ // Different conversation = different session (fresh start)
+ const sessionKey = conversationId || "new-chat";
+ let wsSessionId = localStorage.getItem(`wsSession_${sessionKey}`);
+
+ if (!wsSessionId) {
+ wsSessionId = `ws-${Date.now()}-${Math.random().toString(36).substring(2, 11)}`;
+ localStorage.setItem(`wsSession_${sessionKey}`, wsSessionId);
+ }
+
+ const storedConversationId = localStorage.getItem(`conversationId_${sessionKey}`);
+ const effectiveConversationId = storedConversationId || conversationId;
+
+ if (storedConversationId) {
+ actualConversationIdRef.current = storedConversationId;
+ }
+
+ const client = new WebSocketChatClient();
+ wsClientRef.current = client;
+
+ const unsubscribe = client.onMessage((event) => {
+ if (event.type === "error") {
+ setError(event.data.error);
+ if (sendingTimeoutRef.current) {
+ clearTimeout(sendingTimeoutRef.current);
+ sendingTimeoutRef.current = null;
+ }
+ setIsSending(false);
+ return;
+ }
+
+ try {
+ const data = typeof event.data === "string" ? JSON.parse(event.data) : event.data;
+
+ console.log('[WS Event]', data.type);
+
+ switch (data.type) {
+ case "TextMessageStart":
+ setTimeline((prev) => [
+ ...prev,
+ {
+ type: 'message',
+ id: data.messageId || `msg-${Date.now()}`,
+ role: data.role || 'assistant',
+ content: ''
+ }
+ ]);
+ break;
+
+ case "TextMessageContent":
+ setTimeline((prev) => {
+ const last = prev[prev.length - 1];
+ if (last && last.type === 'message') {
+ return [
+ ...prev.slice(0, -1),
+ { ...last, content: last.content + (data.delta || '') }
+ ];
+ }
+ return prev;
+ });
+ break;
+
+ case "TextMessageEnd":
+ // Message complete (no action needed)
+ break;
+
+ case "RunStarted":
+ if (data.input) {
+ setIsSending(true);
+ if (sendingTimeoutRef.current) {
+ clearTimeout(sendingTimeoutRef.current);
+ }
+ sendingTimeoutRef.current = setTimeout(() => {
+ console.warn("Run timeout - no RunFinished/RunError received within 5 minutes");
+ setIsSending(false);
+ sendingTimeoutRef.current = null;
+ }, 5 * 60 * 1000);
+ }
+ break;
+
+ case "RunFinished":
+ if (sendingTimeoutRef.current) {
+ clearTimeout(sendingTimeoutRef.current);
+ sendingTimeoutRef.current = null;
+ }
+ setIsSending(false);
+ break;
+
+ case "RunError":
+ console.error("Run error:", data);
+ if (sendingTimeoutRef.current) {
+ clearTimeout(sendingTimeoutRef.current);
+ sendingTimeoutRef.current = null;
+ }
+ setIsSending(false);
+ if (data.message) {
+ setTimeline((prev) => [
+ ...prev,
+ { type: 'message', id: `err-${Date.now()}`, role: "assistant", content: `Error: ${data.message}` }
+ ]);
+ }
+ break;
+
+ case "ToolCallStart":
+ setTimeline((prev) => [
+ ...prev,
+ {
+ type: 'tool',
+ id: data.toolCallId,
+ name: data.toolCallName,
+ status: data.status || 'queued',
+ startTime: data.timestamp || Date.now(),
+ arguments: data.arguments || '',
+ }
+ ]);
+ break;
+
+ case "ToolCallArgs":
+ console.log('[ToolCallArgs]', data.toolCallId, 'delta:', data.delta);
+ setTimeline((prev) =>
+ prev.map(item =>
+ item.type === 'tool' && item.id === data.toolCallId
+ ? {
+ ...item,
+ arguments: (item.arguments || '') + data.delta,
+ }
+ : item
+ )
+ );
+ break;
+
+ case "ToolCallEnd":
+ console.log('[ToolCallEnd]', data.toolCallId);
+ // Tool call specification complete (no UI action needed)
+ break;
+
+ case "ToolCallProgress":
+ setTimeline((prev) =>
+ prev.map(item =>
+ item.type === 'tool' && item.id === data.toolCallId
+ ? {
+ ...item,
+ status: data.status,
+ message: data.message,
+ output: data.output ? (item.output || '') + data.output : item.output,
+ }
+ : item
+ )
+ );
+ break;
+
+ case "ToolCallResult":
+ setTimeline((prev) =>
+ prev.map(item =>
+ item.type === 'tool' && item.id === data.toolCallId
+ ? {
+ ...item,
+ status: data.status || 'complete',
+ duration: data.duration,
+ output: typeof data.content === 'string' ? data.content : JSON.stringify(data.content, null, 2),
+ }
+ : item
+ )
+ );
+ break;
+
+ case "ParallelToolsMetadata":
+ console.log("Parallel tools completed:", {
+ total: data.totalCount,
+ success: data.successCount,
+ failed: data.failureCount,
+ duration: data.totalDuration,
+ });
+ break;
+
+ case "session_created":
+ if (data.conversation_id) {
+ actualConversationIdRef.current = data.conversation_id;
+
+ const sessionKey = conversationId || "new-chat";
+ localStorage.setItem(`conversationId_${sessionKey}`, data.conversation_id);
+ }
+ break;
+
+ case "ConversationCreated":
+ if (data.conversation_id) {
+ actualConversationIdRef.current = data.conversation_id;
+
+ const sessionKey = conversationId || "new-chat";
+ localStorage.setItem(`conversationId_${sessionKey}`, data.conversation_id);
+ }
+ break;
+
+ default:
+ console.log("Unknown event type:", data.type || event.type);
+ console.log("Event data:", JSON.stringify(data, null, 2));
+ break;
+ }
+ } catch (error) {
+ console.error("Failed to parse message:", error);
+ }
+ });
+
+ client.createSession(effectiveConversationId, wsSessionId)
+ .then((returnedSessionId) => {
+ setIsConnecting(false);
+ })
+ .catch((err) => {
+ console.error("[LiveChatView] Failed to create session:", err);
+ setError(`Failed to create session: ${err.message}`);
+ setIsConnecting(false);
+ });
+
+ return () => {
+ isMountedRef.current = false;
+
+ unsubscribe();
+
+ if (sendingTimeoutRef.current) {
+ clearTimeout(sendingTimeoutRef.current);
+ sendingTimeoutRef.current = null;
+ }
+
+ if (cleanupTimeoutRef.current) {
+ clearTimeout(cleanupTimeoutRef.current);
+ }
+
+ cleanupTimeoutRef.current = setTimeout(() => {
+ if (!isMountedRef.current && wsClientRef.current) {
+ wsClientRef.current.close();
+ wsClientRef.current = null;
+ }
+ }, 150);
+ };
+ }, [conversationId]);
+
+ useEffect(() => {
+ messagesEndRef.current?.scrollIntoView({ behavior: "smooth" });
+ }, [timeline]);
+
+ const handleStop = useCallback(() => {
+ if (!wsClientRef.current) return;
+
+ try {
+ wsClientRef.current.interrupt();
+ if (sendingTimeoutRef.current) {
+ clearTimeout(sendingTimeoutRef.current);
+ sendingTimeoutRef.current = null;
+ }
+ setIsSending(false);
+ } catch (err: any) {
+ console.error("[LiveChatView] Failed to stop response:", err);
+ }
+ }, []);
+
+ const handleSend = () => {
+ if (!inputMessage.trim() || !wsClientRef.current) return;
+
+ const messageContent = inputMessage;
+
+ try {
+ wsClientRef.current.sendMessage(messageContent, [], selectedModel);
+
+ apiClient.saveToHistory(messageContent).catch((err) => {
+ console.error("Failed to save to history:", err);
+ });
+ setHistory((prev) => [...prev, messageContent]);
+
+ setInputMessage("");
+ setHistoryIndex(-1);
+ setCurrentDraft("");
+ } catch (err: any) {
+ setError(`Failed to send message: ${err.message}`);
+ }
+ };
+
+ useEffect(() => {
+ const handleKeyDown = (e: KeyboardEvent) => {
+ if (e.key === "Escape" && isSending && wsClientRef.current) {
+ handleStop();
+ }
+ };
+
+ window.addEventListener("keydown", handleKeyDown);
+ return () => window.removeEventListener("keydown", handleKeyDown);
+ }, [isSending, handleStop]);
+
+ useEffect(() => {
+ if (!isSending && !isConnecting && inputRef.current) {
+ inputRef.current.focus();
+ }
+ }, [isSending, isConnecting]);
+
+ if (isConnecting) {
+ return (
+
+
+
+
Starting new chat session...
+
+
+ );
+ }
+
+ if (error) {
+ return (
+
+
+
⚠️
+
Connection Error
+
{error}
+
+
+
+ );
+ }
+
+ return (
+ <>
+
+
+
+
{conversationId ? "Continue Conversation" : "New Chat Session"}
+
Live chat via WebSocket
+
+
+
+
+ {loadingModels ? (
+
Loading...
+ ) : (
+
+
+
+
+
+
+
+
+
+ No model found.
+
+ {models.map((model) => {
+ const searchTerms = model.toLowerCase().split(/[\/\-\s]+/);
+ return (
+ {
+ setSelectedModel(model);
+ localStorage.setItem("selectedModel", model);
+ setModelSelectorOpen(false);
+ }}
+ >
+ {model}
+
+
+ );
+ })}
+
+
+
+
+
+
+ )}
+
+
+
+
+
+
+
+ {timeline.length === 0 ? (
+
+
How can I help you today?
+
+ ) : (
+
+ {timeline.map((item, index) =>
+ item.type === 'message' ? (
+
+
+
+ {stripAnsiCodes(item.content)}
+
+
+
+ ) : (
+
+ )
+ )}
+
+
+ )}
+
+
+
+
+
+
+
+ Connected to live session
+ {isSending && Generating response... (ESC to stop)}
+
+
+
+
+ >
+ );
+}
+
+function EmptyState() {
+ return (
+
+
+
💬
+
+ Welcome to Inference Gateway UI
+
+
+ Click "New Chat" to start a live session, or select a conversation from the sidebar.
+
+
+
+ );
+}
diff --git a/ui/app/providers.tsx b/ui/app/providers.tsx
new file mode 100644
index 00000000..097bb8df
--- /dev/null
+++ b/ui/app/providers.tsx
@@ -0,0 +1,27 @@
+"use client";
+
+import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
+import { useState } from "react";
+import { ThemeProvider } from "@/lib/theme-provider";
+
+export function Providers({ children }: { children: React.ReactNode }) {
+ const [queryClient] = useState(
+ () =>
+ new QueryClient({
+ defaultOptions: {
+ queries: {
+ staleTime: 60 * 1000, // 1 minute
+ refetchOnWindowFocus: false,
+ },
+ },
+ })
+ );
+
+ return (
+
+
+ {children}
+
+
+ );
+}
diff --git a/ui/components.json b/ui/components.json
new file mode 100644
index 00000000..6bb595e0
--- /dev/null
+++ b/ui/components.json
@@ -0,0 +1,22 @@
+{
+ "$schema": "https://ui.shadcn.com/schema.json",
+ "style": "new-york",
+ "rsc": true,
+ "tsx": true,
+ "tailwind": {
+ "config": "tailwind.config.ts",
+ "css": "app/globals.css",
+ "baseColor": "neutral",
+ "cssVariables": true,
+ "prefix": ""
+ },
+ "iconLibrary": "lucide",
+ "aliases": {
+ "components": "@/components",
+ "utils": "@/lib/utils",
+ "ui": "@/components/ui",
+ "lib": "@/lib",
+ "hooks": "@/hooks"
+ },
+ "registries": {}
+}
diff --git a/ui/components/status-bar.tsx b/ui/components/status-bar.tsx
new file mode 100644
index 00000000..b5d1027c
--- /dev/null
+++ b/ui/components/status-bar.tsx
@@ -0,0 +1,81 @@
+"use client";
+
+import { useAgentsStatus, useMCPStatus } from "@/lib/api/client";
+
+export default function StatusBar() {
+ const { data: agentsData, isLoading: agentsLoading, error: agentsError } = useAgentsStatus();
+ const { data: mcpData, isLoading: mcpLoading, error: mcpError } = useMCPStatus();
+
+ if (agentsLoading && mcpLoading) {
+ return null;
+ }
+
+ const hasAgents = agentsData && agentsData.total_agents > 0;
+ const hasMCP = mcpData && mcpData.total_servers > 0;
+
+ if (!hasAgents && !hasMCP) {
+ return null;
+ }
+
+ return (
+
+ {hasAgents && !agentsError && (
+
+ Agents:
+
+ {agentsData.ready_agents}/{agentsData.total_agents}
+
+
+ )}
+
+ {hasMCP && !mcpError && (
+
+ 🔌
+
+ {mcpData.connected_servers}/{mcpData.total_servers}
+
+ {mcpData.total_tools > 0 && (
+
+ ({mcpData.total_tools} {mcpData.total_tools === 1 ? "tool" : "tools"})
+
+ )}
+
+ )}
+
+ {agentsError && (
+
+ Agents:
+ error
+
+ )}
+ {mcpError && (
+
+ MCP:
+ error
+
+ )}
+
+ );
+}
+
+// Helper function to determine agent status color
+function getAgentStatusColor(data: { total_agents: number; ready_agents: number }) {
+ if (data.ready_agents === data.total_agents) {
+ return "text-green-600 dark:text-green-400 font-medium";
+ } else if (data.ready_agents > 0) {
+ return "text-yellow-600 dark:text-yellow-400 font-medium";
+ } else {
+ return "text-muted-foreground font-medium";
+ }
+}
+
+// Helper function to determine MCP status color
+function getMCPStatusColor(data: { total_servers: number; connected_servers: number }) {
+ if (data.connected_servers === data.total_servers) {
+ return "text-green-600 dark:text-green-400 font-medium";
+ } else if (data.connected_servers > 0) {
+ return "text-yellow-600 dark:text-yellow-400 font-medium";
+ } else {
+ return "text-muted-foreground font-medium";
+ }
+}
diff --git a/ui/components/theme-toggle.tsx b/ui/components/theme-toggle.tsx
new file mode 100644
index 00000000..3a4dc4ab
--- /dev/null
+++ b/ui/components/theme-toggle.tsx
@@ -0,0 +1,24 @@
+"use client";
+
+import { Moon, Sun } from "lucide-react";
+import { useTheme } from "@/lib/theme-provider";
+import { Button } from "@/components/ui/button";
+
+export function ThemeToggle() {
+ const { theme, toggleTheme } = useTheme();
+
+ return (
+
+ );
+}
diff --git a/ui/components/tool-call-display.tsx b/ui/components/tool-call-display.tsx
new file mode 100644
index 00000000..568dd0c0
--- /dev/null
+++ b/ui/components/tool-call-display.tsx
@@ -0,0 +1,198 @@
+"use client";
+
+import { useEffect, useState } from "react";
+import type { ToolCallState } from "@/lib/agui-types";
+import { Loader2, CheckCircle2, XCircle, Circle, ChevronDown, ChevronRight } from "lucide-react";
+
+interface ToolCallDisplayProps {
+ toolCalls: ToolCallState[];
+}
+
+function ToolStatusIcon({ status }: { status: ToolCallState['status'] }) {
+ switch (status) {
+ case 'queued':
+ return ;
+ case 'running':
+ return ;
+ case 'complete':
+ return ;
+ case 'failed':
+ return ;
+ }
+}
+
+function formatDuration(seconds: number): string {
+ if (seconds < 1) {
+ return `${Math.round(seconds * 1000)}ms`;
+ }
+ return `${seconds.toFixed(2)}s`;
+}
+
+function formatArguments(args: string | undefined): string {
+ if (!args) return '';
+ try {
+ const parsed = JSON.parse(args);
+ return JSON.stringify(parsed, null, 2);
+ } catch {
+ return args;
+ }
+}
+
+function getArgumentsPreview(args: string | undefined): string {
+ if (!args || args === '{}') return '';
+
+ try {
+ const parsed = JSON.parse(args);
+ const entries = Object.entries(parsed);
+ if (entries.length === 0) return '';
+
+ const formatted = entries
+ .map(([key, value]) => {
+ let valueStr: string;
+ if (typeof value === 'string') {
+ if (value.length <= 30) {
+ valueStr = value;
+ } else {
+ valueStr = `"${value.substring(0, 30)}..."`;
+ }
+ } else {
+ valueStr = JSON.stringify(value);
+ }
+ return `${key}=${valueStr}`;
+ })
+ .join(', ');
+
+ if (formatted.length > 100) {
+ return formatted.substring(0, 97) + '...';
+ }
+ return formatted;
+ } catch {
+ const trimmed = args.trim();
+ if (trimmed.length > 100) {
+ return trimmed.substring(0, 97) + '...';
+ }
+ return trimmed;
+ }
+}
+
+function getToolSignature(toolName: string, args: string | undefined): string {
+ if (!args || args === '{}') {
+ return toolName;
+ }
+ const argsPreview = getArgumentsPreview(args);
+ if (!argsPreview) {
+ return toolName;
+ }
+ return `${toolName}(${argsPreview})`;
+}
+
+function ToolCallItem({ toolCall }: { toolCall: ToolCallState }) {
+ const [elapsed, setElapsed] = useState(0);
+ const [isExpanded, setIsExpanded] = useState(false);
+
+ useEffect(() => {
+ if (toolCall.status === 'running' && toolCall.startTime) {
+ const interval = setInterval(() => {
+ setElapsed((Date.now() - toolCall.startTime!) / 1000);
+ }, 100);
+ return () => clearInterval(interval);
+ }
+ }, [toolCall.status, toolCall.startTime]);
+
+ const getStatusText = () => {
+ switch (toolCall.status) {
+ case 'queued':
+ return 'queued';
+ case 'running':
+ return `running ${formatDuration(elapsed)}`;
+ case 'complete':
+ return toolCall.duration ? `completed in ${formatDuration(toolCall.duration)}` : 'completed';
+ case 'failed':
+ return toolCall.duration ? `failed after ${formatDuration(toolCall.duration)}` : 'failed';
+ }
+ };
+
+ const getStatusColor = () => {
+ switch (toolCall.status) {
+ case 'queued':
+ return 'text-gray-600 dark:text-gray-400';
+ case 'running':
+ return 'text-blue-600 dark:text-blue-400';
+ case 'complete':
+ return 'text-green-600 dark:text-green-400';
+ case 'failed':
+ return 'text-red-600 dark:text-red-400';
+ }
+ };
+
+ const toolSignature = getToolSignature(toolCall.name, toolCall.arguments);
+
+ return (
+
+
+
+ {isExpanded && (
+ <>
+ {toolCall.arguments && (
+
+
Arguments:
+
+
{formatArguments(toolCall.arguments)}
+
+
+ )}
+
+ {toolCall.message && (
+
+ {toolCall.message}
+
+ )}
+
+ {toolCall.output && (
+
+ )}
+ >
+ )}
+
+ );
+}
+
+export function ToolCallDisplay({ toolCalls }: ToolCallDisplayProps) {
+ if (toolCalls.length === 0) {
+ return null;
+ }
+
+ return (
+
+
+ Tool Execution
+
+ {toolCalls.map((toolCall) => (
+
+ ))}
+
+ );
+}
diff --git a/ui/components/ui/button.tsx b/ui/components/ui/button.tsx
new file mode 100644
index 00000000..65d4fcd9
--- /dev/null
+++ b/ui/components/ui/button.tsx
@@ -0,0 +1,57 @@
+import * as React from "react"
+import { Slot } from "@radix-ui/react-slot"
+import { cva, type VariantProps } from "class-variance-authority"
+
+import { cn } from "@/lib/utils"
+
+const buttonVariants = cva(
+ "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0",
+ {
+ variants: {
+ variant: {
+ default:
+ "bg-primary text-primary-foreground shadow hover:bg-primary/90",
+ destructive:
+ "bg-destructive text-destructive-foreground shadow-sm hover:bg-destructive/90",
+ outline:
+ "border border-input bg-background shadow-sm hover:bg-accent hover:text-accent-foreground",
+ secondary:
+ "bg-secondary text-secondary-foreground shadow-sm hover:bg-secondary/80",
+ ghost: "hover:bg-accent hover:text-accent-foreground",
+ link: "text-primary underline-offset-4 hover:underline",
+ },
+ size: {
+ default: "h-9 px-4 py-2",
+ sm: "h-8 rounded-md px-3 text-xs",
+ lg: "h-10 rounded-md px-8",
+ icon: "h-9 w-9",
+ },
+ },
+ defaultVariants: {
+ variant: "default",
+ size: "default",
+ },
+ }
+)
+
+export interface ButtonProps
+ extends React.ButtonHTMLAttributes,
+ VariantProps {
+ asChild?: boolean
+}
+
+const Button = React.forwardRef(
+ ({ className, variant, size, asChild = false, ...props }, ref) => {
+ const Comp = asChild ? Slot : "button"
+ return (
+
+ )
+ }
+)
+Button.displayName = "Button"
+
+export { Button, buttonVariants }
diff --git a/ui/components/ui/command.tsx b/ui/components/ui/command.tsx
new file mode 100644
index 00000000..2cecd910
--- /dev/null
+++ b/ui/components/ui/command.tsx
@@ -0,0 +1,153 @@
+"use client"
+
+import * as React from "react"
+import { type DialogProps } from "@radix-ui/react-dialog"
+import { Command as CommandPrimitive } from "cmdk"
+import { Search } from "lucide-react"
+
+import { cn } from "@/lib/utils"
+import { Dialog, DialogContent } from "@/components/ui/dialog"
+
+const Command = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+))
+Command.displayName = CommandPrimitive.displayName
+
+const CommandDialog = ({ children, ...props }: DialogProps) => {
+ return (
+
+ )
+}
+
+const CommandInput = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+
+
+
+))
+
+CommandInput.displayName = CommandPrimitive.Input.displayName
+
+const CommandList = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+))
+
+CommandList.displayName = CommandPrimitive.List.displayName
+
+const CommandEmpty = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>((props, ref) => (
+
+))
+
+CommandEmpty.displayName = CommandPrimitive.Empty.displayName
+
+const CommandGroup = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+))
+
+CommandGroup.displayName = CommandPrimitive.Group.displayName
+
+const CommandSeparator = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+))
+CommandSeparator.displayName = CommandPrimitive.Separator.displayName
+
+const CommandItem = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+))
+
+CommandItem.displayName = CommandPrimitive.Item.displayName
+
+const CommandShortcut = ({
+ className,
+ ...props
+}: React.HTMLAttributes) => {
+ return (
+
+ )
+}
+CommandShortcut.displayName = "CommandShortcut"
+
+export {
+ Command,
+ CommandDialog,
+ CommandInput,
+ CommandList,
+ CommandEmpty,
+ CommandGroup,
+ CommandItem,
+ CommandShortcut,
+ CommandSeparator,
+}
diff --git a/ui/components/ui/dialog.tsx b/ui/components/ui/dialog.tsx
new file mode 100644
index 00000000..1647513e
--- /dev/null
+++ b/ui/components/ui/dialog.tsx
@@ -0,0 +1,122 @@
+"use client"
+
+import * as React from "react"
+import * as DialogPrimitive from "@radix-ui/react-dialog"
+import { X } from "lucide-react"
+
+import { cn } from "@/lib/utils"
+
+const Dialog = DialogPrimitive.Root
+
+const DialogTrigger = DialogPrimitive.Trigger
+
+const DialogPortal = DialogPrimitive.Portal
+
+const DialogClose = DialogPrimitive.Close
+
+const DialogOverlay = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+))
+DialogOverlay.displayName = DialogPrimitive.Overlay.displayName
+
+const DialogContent = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, children, ...props }, ref) => (
+
+
+
+ {children}
+
+
+ Close
+
+
+
+))
+DialogContent.displayName = DialogPrimitive.Content.displayName
+
+const DialogHeader = ({
+ className,
+ ...props
+}: React.HTMLAttributes) => (
+
+)
+DialogHeader.displayName = "DialogHeader"
+
+const DialogFooter = ({
+ className,
+ ...props
+}: React.HTMLAttributes) => (
+
+)
+DialogFooter.displayName = "DialogFooter"
+
+const DialogTitle = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+))
+DialogTitle.displayName = DialogPrimitive.Title.displayName
+
+const DialogDescription = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+))
+DialogDescription.displayName = DialogPrimitive.Description.displayName
+
+export {
+ Dialog,
+ DialogPortal,
+ DialogOverlay,
+ DialogTrigger,
+ DialogClose,
+ DialogContent,
+ DialogHeader,
+ DialogFooter,
+ DialogTitle,
+ DialogDescription,
+}
diff --git a/ui/components/ui/popover.tsx b/ui/components/ui/popover.tsx
new file mode 100644
index 00000000..70a28f66
--- /dev/null
+++ b/ui/components/ui/popover.tsx
@@ -0,0 +1,33 @@
+"use client"
+
+import * as React from "react"
+import * as PopoverPrimitive from "@radix-ui/react-popover"
+
+import { cn } from "@/lib/utils"
+
+const Popover = PopoverPrimitive.Root
+
+const PopoverTrigger = PopoverPrimitive.Trigger
+
+const PopoverAnchor = PopoverPrimitive.Anchor
+
+const PopoverContent = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, align = "center", sideOffset = 4, ...props }, ref) => (
+
+
+
+))
+PopoverContent.displayName = PopoverPrimitive.Content.displayName
+
+export { Popover, PopoverTrigger, PopoverContent, PopoverAnchor }
diff --git a/ui/components/ui/select.tsx b/ui/components/ui/select.tsx
new file mode 100644
index 00000000..6e637f7d
--- /dev/null
+++ b/ui/components/ui/select.tsx
@@ -0,0 +1,159 @@
+"use client"
+
+import * as React from "react"
+import * as SelectPrimitive from "@radix-ui/react-select"
+import { Check, ChevronDown, ChevronUp } from "lucide-react"
+
+import { cn } from "@/lib/utils"
+
+const Select = SelectPrimitive.Root
+
+const SelectGroup = SelectPrimitive.Group
+
+const SelectValue = SelectPrimitive.Value
+
+const SelectTrigger = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, children, ...props }, ref) => (
+ span]:line-clamp-1",
+ className
+ )}
+ {...props}
+ >
+ {children}
+
+
+
+
+))
+SelectTrigger.displayName = SelectPrimitive.Trigger.displayName
+
+const SelectScrollUpButton = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+
+
+))
+SelectScrollUpButton.displayName = SelectPrimitive.ScrollUpButton.displayName
+
+const SelectScrollDownButton = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+
+
+))
+SelectScrollDownButton.displayName =
+ SelectPrimitive.ScrollDownButton.displayName
+
+const SelectContent = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, children, position = "popper", ...props }, ref) => (
+
+
+
+
+ {children}
+
+
+
+
+))
+SelectContent.displayName = SelectPrimitive.Content.displayName
+
+const SelectLabel = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+))
+SelectLabel.displayName = SelectPrimitive.Label.displayName
+
+const SelectItem = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, children, ...props }, ref) => (
+
+
+
+
+
+
+ {children}
+
+))
+SelectItem.displayName = SelectPrimitive.Item.displayName
+
+const SelectSeparator = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+))
+SelectSeparator.displayName = SelectPrimitive.Separator.displayName
+
+export {
+ Select,
+ SelectGroup,
+ SelectValue,
+ SelectTrigger,
+ SelectContent,
+ SelectLabel,
+ SelectItem,
+ SelectSeparator,
+ SelectScrollUpButton,
+ SelectScrollDownButton,
+}
diff --git a/ui/eslint.config.mjs b/ui/eslint.config.mjs
new file mode 100644
index 00000000..9272d374
--- /dev/null
+++ b/ui/eslint.config.mjs
@@ -0,0 +1,14 @@
+import { defineConfig, globalIgnores } from 'eslint/config'
+import nextVitals from 'eslint-config-next/core-web-vitals'
+
+const eslintConfig = defineConfig([
+ ...nextVitals,
+ globalIgnores([
+ '.next/**',
+ 'out/**',
+ 'build/**',
+ 'next-env.d.ts',
+ ]),
+])
+
+export default eslintConfig
diff --git a/ui/lib/agui-types.ts b/ui/lib/agui-types.ts
new file mode 100644
index 00000000..e479c520
--- /dev/null
+++ b/ui/lib/agui-types.ts
@@ -0,0 +1,73 @@
+/**
+ * AG-UI Protocol Type Definitions
+ * Reference: https://docs.ag-ui.com
+ */
+
+export type ToolStatus = 'queued' | 'running' | 'complete' | 'failed';
+
+export interface ToolCallStartEvent {
+ type: 'ToolCallStart';
+ toolCallId: string;
+ toolCallName: string;
+ parentMessageId?: string;
+ status?: ToolStatus;
+ metadata?: Record;
+ timestamp?: number;
+}
+
+export interface ToolCallArgsEvent {
+ type: 'ToolCallArgs';
+ toolCallId: string;
+ delta: string;
+ timestamp?: number;
+}
+
+export interface ToolCallProgressEvent {
+ type: 'ToolCallProgress';
+ toolCallId: string;
+ status: ToolStatus;
+ message: string;
+ output?: string;
+ metadata?: Record;
+ timestamp?: number;
+}
+
+export interface ToolCallResultEvent {
+ type: 'ToolCallResult';
+ messageId: string;
+ toolCallId: string;
+ content: any;
+ role?: string;
+ status?: ToolStatus;
+ duration?: number;
+ metadata?: Record;
+ timestamp?: number;
+}
+
+export interface ParallelToolsMetadataEvent {
+ type: 'ParallelToolsMetadata';
+ totalCount: number;
+ successCount?: number;
+ failureCount?: number;
+ totalDuration?: number;
+ timestamp?: number;
+}
+
+export interface ToolCallState {
+ id: string;
+ name: string;
+ status: ToolStatus;
+ message?: string;
+ output?: string;
+ arguments?: string;
+ startTime?: number;
+ duration?: number;
+ isComplete: boolean;
+}
+
+export type AGUIEvent =
+ | ToolCallStartEvent
+ | ToolCallArgsEvent
+ | ToolCallProgressEvent
+ | ToolCallResultEvent
+ | ParallelToolsMetadataEvent;
diff --git a/ui/lib/api/client.ts b/ui/lib/api/client.ts
new file mode 100644
index 00000000..759769db
--- /dev/null
+++ b/ui/lib/api/client.ts
@@ -0,0 +1,227 @@
+/**
+ * HTTP Client for Inference Gateway API
+ *
+ * This client communicates with the `infer serve` REST API to access
+ * conversation storage without requiring direct database access.
+ */
+
+import type {
+ ConversationSummary,
+ ConversationMetadata,
+ ConversationEntry,
+ AgentsStatusResponse,
+ MCPStatusResponse
+} from "../storage/interfaces";
+
+export interface APIClientConfig {
+ baseURL?: string;
+ timeout?: number;
+}
+
+export interface ListConversationsResponse {
+ conversations: ConversationSummary[];
+ count: number;
+ limit: number;
+ offset: number;
+}
+
+export interface GetConversationResponse {
+ id: string;
+ entries: ConversationEntry[];
+ metadata: ConversationMetadata;
+}
+
+export interface HealthResponse {
+ status: "healthy" | "unhealthy";
+ time: string;
+ error?: string;
+}
+
+export class APIClient {
+ private baseURL: string;
+ private timeout: number;
+
+ constructor(config: APIClientConfig = {}) {
+ this.baseURL = config.baseURL || process.env.NEXT_PUBLIC_API_URL || "http://localhost:8081";
+ this.timeout = config.timeout || 30000;
+ }
+
+ private async fetch(
+ endpoint: string,
+ options: RequestInit = {}
+ ): Promise {
+ const url = `${this.baseURL}${endpoint}`;
+ const controller = new AbortController();
+ const timeoutId = setTimeout(() => controller.abort(), this.timeout);
+
+ try {
+ const response = await fetch(url, {
+ ...options,
+ signal: controller.signal,
+ headers: {
+ "Content-Type": "application/json",
+ ...options.headers,
+ },
+ });
+
+ clearTimeout(timeoutId);
+
+ if (!response.ok) {
+ const error = await response.json().catch(() => ({ error: response.statusText }));
+ throw new Error(error.error || `HTTP ${response.status}: ${response.statusText}`);
+ }
+
+ return await response.json();
+ } catch (error) {
+ clearTimeout(timeoutId);
+ if (error instanceof Error) {
+ if (error.name === "AbortError") {
+ throw new Error("Request timeout");
+ }
+ throw error;
+ }
+ throw new Error("Unknown error");
+ }
+ }
+
+ /**
+ * Check API health
+ */
+ async health(): Promise {
+ return this.fetch("/health");
+ }
+
+ /**
+ * List conversations with pagination
+ */
+ async listConversations(
+ limit: number = 50,
+ offset: number = 0
+ ): Promise {
+ return this.fetch(
+ `/api/v1/conversations?limit=${limit}&offset=${offset}`
+ );
+ }
+
+ /**
+ * Get a specific conversation by ID
+ */
+ async getConversation(id: string): Promise {
+ return this.fetch(`/api/v1/conversations/${id}`);
+ }
+
+ /**
+ * Delete a conversation by ID
+ */
+ async deleteConversation(id: string): Promise<{ success: boolean; message: string }> {
+ return this.fetch<{ success: boolean; message: string }>(
+ `/api/v1/conversations/${id}`,
+ { method: "DELETE" }
+ );
+ }
+
+ /**
+ * Update conversation metadata
+ */
+ async updateMetadata(
+ id: string,
+ metadata: Partial
+ ): Promise<{ success: boolean; message: string }> {
+ return this.fetch<{ success: boolean; message: string }>(
+ `/api/v1/conversations/${id}/metadata`,
+ {
+ method: "PATCH",
+ body: JSON.stringify(metadata),
+ }
+ );
+ }
+
+ /**
+ * List conversations that need title generation
+ */
+ async listConversationsNeedingTitles(
+ limit: number = 10
+ ): Promise<{ conversations: ConversationSummary[]; count: number }> {
+ return this.fetch<{ conversations: ConversationSummary[]; count: number }>(
+ `/api/v1/conversations/needs-titles?limit=${limit}`
+ );
+ }
+
+ /**
+ * List available models
+ */
+ async listModels(): Promise<{ models: string[]; count: number }> {
+ return this.fetch<{ models: string[]; count: number }>(
+ `/api/v1/models`
+ );
+ }
+
+ /**
+ * Get A2A agents status
+ */
+ async getAgentsStatus(): Promise {
+ return this.fetch(`/api/v1/agents/status`);
+ }
+
+ /**
+ * Get MCP servers status
+ */
+ async getMCPStatus(): Promise {
+ return this.fetch(`/api/v1/mcp/status`);
+ }
+
+ /**
+ * Get command history
+ */
+ async getHistory(): Promise<{ history: string[]; count: number }> {
+ return this.fetch<{ history: string[]; count: number }>(`/api/history`);
+ }
+
+ /**
+ * Save command to history
+ */
+ async saveToHistory(command: string): Promise<{ success: boolean; message: string }> {
+ return this.fetch<{ success: boolean; message: string }>(
+ `/api/history`,
+ {
+ method: "POST",
+ body: JSON.stringify({ command }),
+ }
+ );
+ }
+}
+
+// Export singleton instance
+export const apiClient = new APIClient();
+
+/**
+ * React Query Hooks
+ */
+
+import { useQuery } from "@tanstack/react-query";
+
+/**
+ * Hook to fetch agents status with auto-refresh
+ */
+export function useAgentsStatus(refreshInterval: number = 5000) {
+ return useQuery({
+ queryKey: ["agents-status"],
+ queryFn: () => apiClient.getAgentsStatus(),
+ refetchInterval: refreshInterval,
+ refetchOnWindowFocus: true,
+ staleTime: 2000, // Consider data stale after 2 seconds
+ });
+}
+
+/**
+ * Hook to fetch MCP status with auto-refresh
+ */
+export function useMCPStatus(refreshInterval: number = 5000) {
+ return useQuery({
+ queryKey: ["mcp-status"],
+ queryFn: () => apiClient.getMCPStatus(),
+ refetchInterval: refreshInterval,
+ refetchOnWindowFocus: true,
+ staleTime: 2000, // Consider data stale after 2 seconds
+ });
+}
diff --git a/ui/lib/chat/websocket-client.ts b/ui/lib/chat/websocket-client.ts
new file mode 100644
index 00000000..5e0c3d40
--- /dev/null
+++ b/ui/lib/chat/websocket-client.ts
@@ -0,0 +1,263 @@
+/**
+ * WebSocket client for live chat with headless CLI sessions
+ */
+
+export interface ChatMessage {
+ role: "user" | "assistant" | "system";
+ content: string;
+ timestamp: Date;
+}
+
+export type ChatEventHandler = (event: ChatEvent) => void;
+
+export interface ChatEvent {
+ type: string;
+ data?: any;
+}
+
+export class WebSocketChatClient {
+ private ws: WebSocket | null = null;
+ private url: string;
+ private sessionId: string | null = null;
+ private messageHandlers: Set = new Set();
+ private reconnectAttempts = 0;
+ private maxReconnectAttempts = 5;
+
+ constructor(baseURL?: string) {
+ const apiURL = baseURL || process.env.NEXT_PUBLIC_API_URL || "http://localhost:8081";
+ // Convert http://localhost:8081 to ws://localhost:8081/ws
+ this.url = apiURL.replace(/^http/, "ws") + "/ws";
+ }
+
+ /**
+ * Create a new chat session
+ * @param conversationId Optional conversation ID to continue an existing conversation
+ * @param sessionId Optional session ID to reuse existing session
+ */
+ async createSession(conversationId?: string, sessionId?: string): Promise {
+ return new Promise((resolve, reject) => {
+ let connectionTimeout: NodeJS.Timeout | null = null;
+ let isResolved = false;
+
+ const cleanup = () => {
+ if (connectionTimeout) {
+ clearTimeout(connectionTimeout);
+ connectionTimeout = null;
+ }
+ };
+
+ connectionTimeout = setTimeout(() => {
+ if (!isResolved && this.ws && this.ws.readyState !== WebSocket.OPEN) {
+ cleanup();
+ reject(new Error("Connection timeout"));
+ }
+ }, 5000);
+
+ this.ws = new WebSocket(this.url);
+
+ this.ws.onopen = () => {
+ cleanup();
+ this.reconnectAttempts = 0;
+
+ this.send({
+ type: "create_session",
+ conversation_id: conversationId,
+ session_id: sessionId,
+ });
+ };
+
+ this.ws.onmessage = (event) => {
+ try {
+ const message = JSON.parse(event.data);
+
+ if (message.type === "session_created") {
+ this.sessionId = message.session_id;
+ if (this.sessionId) {
+ isResolved = true;
+ cleanup();
+ resolve(this.sessionId);
+ } else {
+ cleanup();
+ reject(new Error("Session ID not provided"));
+ }
+ this.notifyHandlers({ type: message.type, data: message });
+ } else if (message.type === "error") {
+ console.error("WebSocket error:", message.error);
+ cleanup();
+ reject(new Error(message.error));
+ } else {
+ this.notifyHandlers({ type: message.type, data: message });
+ }
+ } catch (error) {
+ console.error("Failed to parse WebSocket message:", error);
+ }
+ };
+
+ this.ws.onerror = (error) => {
+ console.error("WebSocket error:", error);
+ };
+
+ this.ws.onclose = () => {
+ if (!isResolved) {
+ if (this.reconnectAttempts === 0) {
+ this.reconnectAttempts++;
+
+ setTimeout(() => {
+ this.createSession().then(resolve).catch(reject);
+ }, 500);
+ } else {
+ cleanup();
+ reject(new Error("WebSocket connection failed"));
+ }
+ }
+ };
+ });
+ }
+
+ /**
+ * Join an existing session
+ */
+ async joinSession(sessionId: string): Promise {
+ return new Promise((resolve, reject) => {
+ this.ws = new WebSocket(this.url);
+
+ this.ws.onopen = () => {
+ this.reconnectAttempts = 0;
+
+ this.send({
+ type: "join_session",
+ session_id: sessionId,
+ });
+ };
+
+ this.ws.onmessage = (event) => {
+ try {
+ const message = JSON.parse(event.data);
+
+ if (message.type === "session_joined") {
+ this.sessionId = message.session_id;
+ resolve();
+ } else if (message.type === "error") {
+ console.error("WebSocket error:", message.error);
+ reject(new Error(message.error));
+ } else {
+ this.notifyHandlers({ type: message.type, data: message });
+ }
+ } catch (error) {
+ console.error("Failed to parse WebSocket message:", error);
+ }
+ };
+
+ this.ws.onerror = (error) => {
+ console.error("WebSocket error:", error);
+ const errorMessage = error instanceof Error ? error.message : "WebSocket connection failed";
+ reject(new Error(errorMessage));
+ };
+
+ this.ws.onclose = () => {
+ this.handleReconnect();
+ };
+ });
+ }
+
+ /**
+ * Send a message to the chat session
+ */
+ sendMessage(content: string, images: any[] = [], model?: string): void {
+ if (!this.ws || this.ws.readyState !== WebSocket.OPEN) {
+ throw new Error("WebSocket not connected");
+ }
+
+ const message: any = {
+ type: "message",
+ content,
+ images,
+ };
+
+ // Include model if specified
+ if (model) {
+ message.model = model;
+ }
+
+ this.send(message);
+ }
+
+ /**
+ * Send an interrupt signal to stop the current response
+ */
+ interrupt(): void {
+ if (!this.ws || this.ws.readyState !== WebSocket.OPEN) {
+ throw new Error("WebSocket not connected");
+ }
+
+ this.send({
+ type: "interrupt",
+ });
+ }
+
+ /**
+ * Subscribe to chat events
+ */
+ onMessage(handler: ChatEventHandler): () => void {
+ this.messageHandlers.add(handler);
+
+ // Return unsubscribe function
+ return () => {
+ this.messageHandlers.delete(handler);
+ };
+ }
+
+ /**
+ * Close the WebSocket connection
+ */
+ close(): void {
+ if (this.ws) {
+ this.send({ type: "close_session" });
+ this.ws.close();
+ this.ws = null;
+ }
+ this.sessionId = null;
+ }
+
+ /**
+ * Get current session ID
+ */
+ getSessionId(): string | null {
+ return this.sessionId;
+ }
+
+ /**
+ * Check if connected
+ */
+ isConnected(): boolean {
+ return this.ws !== null && this.ws.readyState === WebSocket.OPEN;
+ }
+
+ private send(data: any): void {
+ if (this.ws && this.ws.readyState === WebSocket.OPEN) {
+ this.ws.send(JSON.stringify(data));
+ }
+ }
+
+ private notifyHandlers(event: ChatEvent): void {
+ this.messageHandlers.forEach((handler) => {
+ try {
+ handler(event);
+ } catch (error) {
+ console.error("Error in message handler:", error);
+ }
+ });
+ }
+
+ private handleReconnect(): void {
+ if (this.reconnectAttempts < this.maxReconnectAttempts && this.sessionId) {
+ this.reconnectAttempts++;
+
+ setTimeout(() => {
+ this.joinSession(this.sessionId!).catch((error) => {
+ console.error("Failed to reconnect:", error);
+ });
+ }, 1000 * Math.pow(2, this.reconnectAttempts));
+ }
+ }
+}
diff --git a/ui/lib/storage/factory.ts b/ui/lib/storage/factory.ts
new file mode 100644
index 00000000..490a0cff
--- /dev/null
+++ b/ui/lib/storage/factory.ts
@@ -0,0 +1,20 @@
+import type { ConversationStorage } from "./interfaces";
+import { HTTPStorage } from "./http/http-storage";
+
+/**
+ * Create storage client that communicates with the infer serve API
+ *
+ * This replaces direct database access with HTTP API calls to the
+ * `infer serve` API server, eliminating the need for native database modules.
+ */
+export function createStorage(apiURL?: string): ConversationStorage {
+ return new HTTPStorage(apiURL);
+}
+
+/**
+ * Create storage from environment variables
+ */
+export function createStorageFromEnv(): ConversationStorage {
+ const apiURL = process.env.NEXT_PUBLIC_API_URL || "http://localhost:8080";
+ return createStorage(apiURL);
+}
diff --git a/ui/lib/storage/hooks.ts b/ui/lib/storage/hooks.ts
new file mode 100644
index 00000000..c009e49e
--- /dev/null
+++ b/ui/lib/storage/hooks.ts
@@ -0,0 +1,206 @@
+import { useQuery, useMutation, useQueryClient, UseQueryOptions } from "@tanstack/react-query";
+import * as React from "react";
+import type {
+ ConversationStorage,
+ ConversationSummary,
+ ConversationEntry,
+ ConversationMetadata,
+} from "./interfaces";
+
+/**
+ * React hooks for accessing conversation storage
+ * Uses React Query for caching and state management
+ */
+
+// Storage instance should be initialized once and reused
+let storageInstance: ConversationStorage | null = null;
+
+export function setStorageInstance(storage: ConversationStorage) {
+ storageInstance = storage;
+}
+
+export function getStorageInstance(): ConversationStorage {
+ if (!storageInstance) {
+ throw new Error("Storage instance not initialized. Call setStorageInstance() first.");
+ }
+ return storageInstance;
+}
+
+// ============================================================================
+// Query Hooks
+// ============================================================================
+
+/**
+ * Hook to list conversations with pagination
+ */
+export function useConversations(
+ limit: number = 50,
+ offset: number = 0,
+ options?: Omit, "queryKey" | "queryFn">
+) {
+ const storage = getStorageInstance();
+
+ return useQuery({
+ queryKey: ["conversations", limit, offset],
+ queryFn: () => storage.listConversations(limit, offset),
+ ...options,
+ });
+}
+
+/**
+ * Hook to get a specific conversation with full details
+ */
+export function useConversation(
+ conversationId: string,
+ options?: Omit<
+ UseQueryOptions<{ entries: ConversationEntry[]; metadata: ConversationMetadata }>,
+ "queryKey" | "queryFn"
+ >
+) {
+ const storage = getStorageInstance();
+
+ return useQuery({
+ queryKey: ["conversation", conversationId],
+ queryFn: () => storage.loadConversation(conversationId),
+ enabled: !!conversationId,
+ ...options,
+ });
+}
+
+/**
+ * Hook to check storage health
+ */
+export function useStorageHealth(
+ options?: Omit, "queryKey" | "queryFn">
+) {
+ const storage = getStorageInstance();
+
+ return useQuery({
+ queryKey: ["storage", "health"],
+ queryFn: () => storage.health(),
+ ...options,
+ });
+}
+
+// ============================================================================
+// Mutation Hooks
+// ============================================================================
+
+/**
+ * Hook to delete a conversation
+ */
+export function useDeleteConversation() {
+ const storage = getStorageInstance();
+ const queryClient = useQueryClient();
+
+ return useMutation({
+ mutationFn: (conversationId: string) => storage.deleteConversation(conversationId),
+ onSuccess: () => {
+ // Invalidate conversations list to refetch
+ queryClient.invalidateQueries({ queryKey: ["conversations"] });
+ },
+ });
+}
+
+/**
+ * Hook to update conversation metadata (title, tags, etc.)
+ */
+export function useUpdateConversationMetadata() {
+ const storage = getStorageInstance();
+ const queryClient = useQueryClient();
+
+ return useMutation({
+ mutationFn: ({
+ conversationId,
+ metadata,
+ }: {
+ conversationId: string;
+ metadata: Partial;
+ }) => storage.updateConversationMetadata(conversationId, metadata),
+ onSuccess: (_, variables) => {
+ // Invalidate specific conversation and list
+ queryClient.invalidateQueries({ queryKey: ["conversation", variables.conversationId] });
+ queryClient.invalidateQueries({ queryKey: ["conversations"] });
+ },
+ });
+}
+
+/**
+ * Hook to save a conversation
+ */
+export function useSaveConversation() {
+ const storage = getStorageInstance();
+ const queryClient = useQueryClient();
+
+ return useMutation({
+ mutationFn: ({
+ conversationId,
+ entries,
+ metadata,
+ }: {
+ conversationId: string;
+ entries: ConversationEntry[];
+ metadata: ConversationMetadata;
+ }) => storage.saveConversation(conversationId, entries, metadata),
+ onSuccess: (_, variables) => {
+ // Invalidate specific conversation and list
+ queryClient.invalidateQueries({ queryKey: ["conversation", variables.conversationId] });
+ queryClient.invalidateQueries({ queryKey: ["conversations"] });
+ },
+ });
+}
+
+// ============================================================================
+// Helper Hooks
+// ============================================================================
+
+/**
+ * Hook to get conversation statistics
+ * Aggregates data from all conversations
+ */
+export function useConversationStats() {
+ const { data: conversations, ...query } = useConversations(1000, 0);
+
+ const stats = React.useMemo(() => {
+ if (!conversations) {
+ return null;
+ }
+
+ const totalConversations = conversations.length;
+ const totalMessages = conversations.reduce(
+ (sum, conv) => sum + conv.message_count,
+ 0
+ );
+ const totalInputTokens = conversations.reduce(
+ (sum, conv) => sum + conv.token_stats.total_input_tokens,
+ 0
+ );
+ const totalOutputTokens = conversations.reduce(
+ (sum, conv) => sum + conv.token_stats.total_output_tokens,
+ 0
+ );
+ const totalCost = conversations.reduce(
+ (sum, conv) => sum + conv.cost_stats.total_cost,
+ 0
+ );
+
+ const models = new Set(
+ conversations.filter((c) => c.model).map((c) => c.model!)
+ );
+
+ return {
+ totalConversations,
+ totalMessages,
+ totalInputTokens,
+ totalOutputTokens,
+ totalTokens: totalInputTokens + totalOutputTokens,
+ totalCost,
+ models: Array.from(models),
+ };
+ }, [conversations]);
+
+ return {
+ ...query,
+ data: stats,
+ };
+}
diff --git a/ui/lib/storage/http/http-storage.ts b/ui/lib/storage/http/http-storage.ts
new file mode 100644
index 00000000..90294d39
--- /dev/null
+++ b/ui/lib/storage/http/http-storage.ts
@@ -0,0 +1,78 @@
+import type {
+ ConversationStorage,
+ ConversationEntry,
+ ConversationMetadata,
+ ConversationSummary
+} from "../interfaces";
+import { APIClient } from "../../api/client";
+
+/**
+ * HTTPStorage implements ConversationStorage using the infer serve REST API
+ *
+ * This eliminates the need for direct database access and native modules.
+ * All storage operations are forwarded to the API server.
+ */
+export class HTTPStorage implements ConversationStorage {
+ private client: APIClient;
+
+ constructor(apiURL?: string) {
+ this.client = new APIClient({ baseURL: apiURL });
+ }
+
+ async saveConversation(
+ conversationID: string,
+ entries: ConversationEntry[],
+ metadata: ConversationMetadata
+ ): Promise {
+ // For now, saving is handled by the headless CLI sessions directly
+ // The UI is read-only for conversations
+ throw new Error("Saving conversations is not supported from the UI. Use the headless CLI sessions instead.");
+ }
+
+ async loadConversation(
+ conversationID: string
+ ): Promise<{ entries: ConversationEntry[]; metadata: ConversationMetadata }> {
+ const response = await this.client.getConversation(conversationID);
+ return {
+ entries: response.entries,
+ metadata: response.metadata
+ };
+ }
+
+ async listConversations(
+ limit: number,
+ offset: number
+ ): Promise {
+ const response = await this.client.listConversations(limit, offset);
+ return response.conversations;
+ }
+
+ async deleteConversation(conversationID: string): Promise {
+ await this.client.deleteConversation(conversationID);
+ }
+
+ async updateConversationMetadata(
+ conversationID: string,
+ metadata: Partial
+ ): Promise {
+ await this.client.updateMetadata(conversationID, metadata);
+ }
+
+ async listConversationsNeedingTitles(limit: number): Promise {
+ const response = await this.client.listConversationsNeedingTitles(limit);
+ return response.conversations;
+ }
+
+ async close(): Promise {
+ // No connection to close for HTTP client
+ }
+
+ async health(): Promise {
+ try {
+ const response = await this.client.health();
+ return response.status === "healthy";
+ } catch {
+ return false;
+ }
+ }
+}
diff --git a/ui/lib/storage/interfaces.ts b/ui/lib/storage/interfaces.ts
new file mode 100644
index 00000000..91d67c4f
--- /dev/null
+++ b/ui/lib/storage/interfaces.ts
@@ -0,0 +1,192 @@
+/**
+ * TypeScript Storage Interfaces
+ * Mirrors the CLI's Go domain models for conversations
+ */
+
+// Storage interface matching CLI's ConversationStorage
+export interface ConversationStorage {
+ saveConversation(
+ conversationID: string,
+ entries: ConversationEntry[],
+ metadata: ConversationMetadata
+ ): Promise;
+ loadConversation(
+ conversationID: string
+ ): Promise<{ entries: ConversationEntry[]; metadata: ConversationMetadata }>;
+ listConversations(
+ limit: number,
+ offset: number
+ ): Promise;
+ deleteConversation(conversationID: string): Promise;
+ updateConversationMetadata(
+ conversationID: string,
+ metadata: Partial
+ ): Promise;
+ listConversationsNeedingTitles(limit: number): Promise;
+ close(): Promise;
+ health(): Promise;
+}
+
+// Domain types
+export interface ConversationEntry {
+ message: Message;
+ model?: string;
+ time: Date;
+ hidden?: boolean;
+ images?: ImageAttachment[];
+ tool_execution?: ToolExecutionResult;
+ pending_tool_call?: ToolCall;
+ tool_approval_status?: ToolApprovalStatus;
+ rejected?: boolean;
+ is_plan?: boolean;
+ plan_approval_status?: PlanApprovalStatus;
+}
+
+export interface Message {
+ role: "user" | "assistant" | "system";
+ content: string | ContentPart[];
+}
+
+export interface ContentPart {
+ type: "text" | "image";
+ text?: string;
+ source?: {
+ type: "base64" | "url";
+ media_type?: string;
+ data?: string;
+ url?: string;
+ };
+}
+
+export interface ImageAttachment {
+ data: string; // Base64 encoded
+ mime_type: string;
+ filename?: string;
+ display_name?: string;
+ source_path?: string;
+}
+
+export interface ToolExecutionResult {
+ tool_name: string;
+ tool_id: string;
+ status: "success" | "error";
+ result?: any;
+ error?: string;
+ timestamp?: Date;
+}
+
+export interface ToolCall {
+ id: string;
+ type: string;
+ function: {
+ name: string;
+ arguments: string;
+ };
+}
+
+export type ToolApprovalStatus = "pending" | "approved" | "rejected";
+export type PlanApprovalStatus = "pending" | "approved" | "rejected";
+
+export interface ConversationMetadata {
+ id: string;
+ title?: string;
+ created_at: Date;
+ updated_at: Date;
+ message_count: number;
+ token_stats: SessionTokenStats;
+ cost_stats: SessionCostStats;
+ model?: string;
+ tags?: string[];
+ summary?: string;
+ title_generated?: boolean;
+ title_invalidated?: boolean;
+ title_generation_time?: Date;
+}
+
+export interface SessionTokenStats {
+ total_input_tokens: number;
+ total_output_tokens: number;
+ request_count: number;
+}
+
+export interface SessionCostStats {
+ total_cost: number;
+}
+
+export interface ConversationSummary {
+ id: string;
+ title?: string;
+ created_at: Date;
+ updated_at: Date;
+ message_count: number;
+ token_stats: SessionTokenStats;
+ cost_stats: SessionCostStats;
+ model?: string;
+ tags?: string[];
+ summary?: string;
+ title_generated?: boolean;
+ title_invalidated?: boolean;
+ title_generation_time?: Date;
+}
+
+// Storage configuration types
+export interface PostgresConfig {
+ host: string;
+ port: number;
+ database: string;
+ user: string;
+ password: string;
+}
+
+export interface SQLiteConfig {
+ path: string;
+}
+
+export interface RedisConfig {
+ host: string;
+ port: number;
+ db?: number;
+ password?: string;
+}
+
+export interface JSONLConfig {
+ directory: string;
+}
+
+export type StorageType = "postgres" | "sqlite" | "redis" | "jsonl" | "memory";
+
+export interface StorageConfig {
+ type: StorageType;
+ postgres?: PostgresConfig;
+ sqlite?: SQLiteConfig;
+ redis?: RedisConfig;
+ jsonl?: JSONLConfig;
+}
+
+// Agent and MCP status types
+export interface AgentStatus {
+ name: string;
+ state: string;
+ url?: string;
+ image?: string;
+ error?: string;
+}
+
+export interface AgentsStatusResponse {
+ total_agents: number;
+ ready_agents: number;
+ agents: AgentStatus[];
+}
+
+export interface MCPServerStatus {
+ name: string;
+ connected: boolean;
+ tools: number;
+}
+
+export interface MCPStatusResponse {
+ total_servers: number;
+ connected_servers: number;
+ total_tools: number;
+ servers: MCPServerStatus[];
+}
diff --git a/ui/lib/theme-provider.tsx b/ui/lib/theme-provider.tsx
new file mode 100644
index 00000000..8b77776b
--- /dev/null
+++ b/ui/lib/theme-provider.tsx
@@ -0,0 +1,51 @@
+"use client";
+
+import { createContext, useContext, useEffect, useState } from "react";
+
+type Theme = "light" | "dark";
+
+type ThemeContextType = {
+ theme: Theme;
+ toggleTheme: () => void;
+};
+
+const ThemeContext = createContext(undefined);
+
+export function ThemeProvider({ children }: { children: React.ReactNode }) {
+ const [theme, setTheme] = useState(() => {
+ if (typeof window === "undefined") return "dark";
+
+ const saved = localStorage.getItem("theme") as Theme | null;
+ return saved || "dark";
+ });
+
+ useEffect(() => {
+ const root = document.documentElement;
+
+ if (theme === "dark") {
+ root.classList.add("dark");
+ } else {
+ root.classList.remove("dark");
+ }
+
+ localStorage.setItem("theme", theme);
+ }, [theme]);
+
+ const toggleTheme = () => {
+ setTheme((prev) => (prev === "light" ? "dark" : "light"));
+ };
+
+ return (
+
+ {children}
+
+ );
+}
+
+export function useTheme() {
+ const context = useContext(ThemeContext);
+ if (context === undefined) {
+ throw new Error("useTheme must be used within a ThemeProvider");
+ }
+ return context;
+}
diff --git a/ui/lib/utils.ts b/ui/lib/utils.ts
new file mode 100644
index 00000000..bd0c391d
--- /dev/null
+++ b/ui/lib/utils.ts
@@ -0,0 +1,6 @@
+import { clsx, type ClassValue } from "clsx"
+import { twMerge } from "tailwind-merge"
+
+export function cn(...inputs: ClassValue[]) {
+ return twMerge(clsx(inputs))
+}
diff --git a/ui/next-env.d.ts b/ui/next-env.d.ts
new file mode 100644
index 00000000..c4b7818f
--- /dev/null
+++ b/ui/next-env.d.ts
@@ -0,0 +1,6 @@
+///
+///
+import "./.next/dev/types/routes.d.ts";
+
+// NOTE: This file should not be edited
+// see https://nextjs.org/docs/app/api-reference/config/typescript for more information.
diff --git a/ui/next.config.ts b/ui/next.config.ts
new file mode 100644
index 00000000..45550932
--- /dev/null
+++ b/ui/next.config.ts
@@ -0,0 +1,12 @@
+import type { NextConfig } from "next";
+
+const nextConfig: NextConfig = {
+ reactStrictMode: true,
+ experimental: {
+ serverActions: {
+ bodySizeLimit: "10mb", // For image uploads
+ },
+ },
+};
+
+export default nextConfig;
diff --git a/ui/package-lock.json b/ui/package-lock.json
new file mode 100644
index 00000000..2323f3cd
--- /dev/null
+++ b/ui/package-lock.json
@@ -0,0 +1,7578 @@
+{
+ "name": "@inference-gateway/ui",
+ "version": "1.0.0",
+ "lockfileVersion": 3,
+ "requires": true,
+ "packages": {
+ "": {
+ "name": "@inference-gateway/ui",
+ "version": "1.0.0",
+ "dependencies": {
+ "@radix-ui/react-dialog": "^1.1.15",
+ "@radix-ui/react-popover": "^1.1.15",
+ "@radix-ui/react-select": "^2.2.6",
+ "@radix-ui/react-slot": "^1.2.4",
+ "@tanstack/react-query": "^5.90.12",
+ "class-variance-authority": "^0.7.1",
+ "clsx": "^2.1.1",
+ "cmdk": "^1.1.1",
+ "lucide-react": "^0.562.0",
+ "next": "^16.1.0",
+ "react": "^19.2.0",
+ "react-dom": "^19.2.0",
+ "tailwind-merge": "^3.4.0",
+ "tailwindcss-animate": "^1.0.7"
+ },
+ "devDependencies": {
+ "@tailwindcss/postcss": "^4.1.18",
+ "@types/node": "^25.0.3",
+ "@types/react": "^19.2.7",
+ "@types/react-dom": "^19.2.3",
+ "autoprefixer": "^10.4.23",
+ "eslint": "^9",
+ "eslint-config-next": "^16.1.0",
+ "postcss": "^8.5.6",
+ "tailwindcss": "^4.1.18",
+ "typescript": "^5.9.3"
+ }
+ },
+ "node_modules/@alloc/quick-lru": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.2.0.tgz",
+ "integrity": "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/@babel/code-frame": {
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz",
+ "integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-validator-identifier": "^7.27.1",
+ "js-tokens": "^4.0.0",
+ "picocolors": "^1.1.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/compat-data": {
+ "version": "7.28.5",
+ "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.28.5.tgz",
+ "integrity": "sha512-6uFXyCayocRbqhZOB+6XcuZbkMNimwfVGFji8CTZnCzOHVGvDqzvitu1re2AU5LROliz7eQPhB8CpAMvnx9EjA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/core": {
+ "version": "7.28.5",
+ "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.28.5.tgz",
+ "integrity": "sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw==",
+ "dev": true,
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "@babel/code-frame": "^7.27.1",
+ "@babel/generator": "^7.28.5",
+ "@babel/helper-compilation-targets": "^7.27.2",
+ "@babel/helper-module-transforms": "^7.28.3",
+ "@babel/helpers": "^7.28.4",
+ "@babel/parser": "^7.28.5",
+ "@babel/template": "^7.27.2",
+ "@babel/traverse": "^7.28.5",
+ "@babel/types": "^7.28.5",
+ "@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"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/babel"
+ }
+ },
+ "node_modules/@babel/core/node_modules/semver": {
+ "version": "6.3.1",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
+ "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
+ "dev": true,
+ "license": "ISC",
+ "bin": {
+ "semver": "bin/semver.js"
+ }
+ },
+ "node_modules/@babel/generator": {
+ "version": "7.28.5",
+ "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.28.5.tgz",
+ "integrity": "sha512-3EwLFhZ38J4VyIP6WNtt2kUdW9dokXA9Cr4IVIFHuCpZ3H8/YFOl5JjZHisrn1fATPBmKKqXzDFvh9fUwHz6CQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/parser": "^7.28.5",
+ "@babel/types": "^7.28.5",
+ "@jridgewell/gen-mapping": "^0.3.12",
+ "@jridgewell/trace-mapping": "^0.3.28",
+ "jsesc": "^3.0.2"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-compilation-targets": {
+ "version": "7.27.2",
+ "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.27.2.tgz",
+ "integrity": "sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==",
+ "dev": true,
+ "license": "MIT",
+ "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"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-compilation-targets/node_modules/semver": {
+ "version": "6.3.1",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
+ "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
+ "dev": true,
+ "license": "ISC",
+ "bin": {
+ "semver": "bin/semver.js"
+ }
+ },
+ "node_modules/@babel/helper-globals": {
+ "version": "7.28.0",
+ "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz",
+ "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-module-imports": {
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.27.1.tgz",
+ "integrity": "sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/traverse": "^7.27.1",
+ "@babel/types": "^7.27.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-module-transforms": {
+ "version": "7.28.3",
+ "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.3.tgz",
+ "integrity": "sha512-gytXUbs8k2sXS9PnQptz5o0QnpLL51SwASIORY6XaBKF88nsOT0Zw9szLqlSGQDP/4TljBAD5y98p2U1fqkdsw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-module-imports": "^7.27.1",
+ "@babel/helper-validator-identifier": "^7.27.1",
+ "@babel/traverse": "^7.28.3"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0"
+ }
+ },
+ "node_modules/@babel/helper-string-parser": {
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz",
+ "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-validator-identifier": {
+ "version": "7.28.5",
+ "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz",
+ "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-validator-option": {
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz",
+ "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helpers": {
+ "version": "7.28.4",
+ "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.28.4.tgz",
+ "integrity": "sha512-HFN59MmQXGHVyYadKLVumYsA9dBFun/ldYxipEjzA4196jpLZd8UjEEBLkbEkvfYreDqJhZxYAWFPtrfhNpj4w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/template": "^7.27.2",
+ "@babel/types": "^7.28.4"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/parser": {
+ "version": "7.28.5",
+ "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.5.tgz",
+ "integrity": "sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/types": "^7.28.5"
+ },
+ "bin": {
+ "parser": "bin/babel-parser.js"
+ },
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/@babel/template": {
+ "version": "7.27.2",
+ "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.2.tgz",
+ "integrity": "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/code-frame": "^7.27.1",
+ "@babel/parser": "^7.27.2",
+ "@babel/types": "^7.27.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/traverse": {
+ "version": "7.28.5",
+ "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.28.5.tgz",
+ "integrity": "sha512-TCCj4t55U90khlYkVV/0TfkJkAkUg3jZFA3Neb7unZT8CPok7iiRfaX0F+WnqWqt7OxhOn0uBKXCw4lbL8W0aQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/code-frame": "^7.27.1",
+ "@babel/generator": "^7.28.5",
+ "@babel/helper-globals": "^7.28.0",
+ "@babel/parser": "^7.28.5",
+ "@babel/template": "^7.27.2",
+ "@babel/types": "^7.28.5",
+ "debug": "^4.3.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/types": {
+ "version": "7.28.5",
+ "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.5.tgz",
+ "integrity": "sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-string-parser": "^7.27.1",
+ "@babel/helper-validator-identifier": "^7.28.5"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@emnapi/core": {
+ "version": "1.7.1",
+ "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.7.1.tgz",
+ "integrity": "sha512-o1uhUASyo921r2XtHYOHy7gdkGLge8ghBEQHMWmyJFoXlpU58kIrhhN3w26lpQb6dspetweapMn2CSNwQ8I4wg==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "@emnapi/wasi-threads": "1.1.0",
+ "tslib": "^2.4.0"
+ }
+ },
+ "node_modules/@emnapi/runtime": {
+ "version": "1.7.1",
+ "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.7.1.tgz",
+ "integrity": "sha512-PVtJr5CmLwYAU9PZDMITZoR5iAOShYREoR45EyyLrbntV50mdePTgUn4AmOw90Ifcj+x2kRjdzr1HP3RrNiHGA==",
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "tslib": "^2.4.0"
+ }
+ },
+ "node_modules/@emnapi/wasi-threads": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.1.0.tgz",
+ "integrity": "sha512-WI0DdZ8xFSbgMjR1sFsKABJ/C5OnRrjT06JXbZKexJGrDuPTzZdDYfFlsgcCXCyf+suG5QU2e/y1Wo2V/OapLQ==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "tslib": "^2.4.0"
+ }
+ },
+ "node_modules/@eslint-community/eslint-utils": {
+ "version": "4.9.0",
+ "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.0.tgz",
+ "integrity": "sha512-ayVFHdtZ+hsq1t2Dy24wCmGXGe4q9Gu3smhLYALJrr473ZH27MsnSL+LKUlimp4BWJqMDMLmPpx/Q9R3OAlL4g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "eslint-visitor-keys": "^3.4.3"
+ },
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ },
+ "peerDependencies": {
+ "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0"
+ }
+ },
+ "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": {
+ "version": "3.4.3",
+ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz",
+ "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/@eslint-community/regexpp": {
+ "version": "4.12.2",
+ "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.2.tgz",
+ "integrity": "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "^12.0.0 || ^14.0.0 || >=16.0.0"
+ }
+ },
+ "node_modules/@eslint/config-array": {
+ "version": "0.21.1",
+ "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.1.tgz",
+ "integrity": "sha512-aw1gNayWpdI/jSYVgzN5pL0cfzU02GT3NBpeT/DXbx1/1x7ZKxFPd9bwrzygx/qiwIQiJ1sw/zD8qY/kRvlGHA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@eslint/object-schema": "^2.1.7",
+ "debug": "^4.3.1",
+ "minimatch": "^3.1.2"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ }
+ },
+ "node_modules/@eslint/config-helpers": {
+ "version": "0.4.2",
+ "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.4.2.tgz",
+ "integrity": "sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@eslint/core": "^0.17.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ }
+ },
+ "node_modules/@eslint/core": {
+ "version": "0.17.0",
+ "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.17.0.tgz",
+ "integrity": "sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@types/json-schema": "^7.0.15"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ }
+ },
+ "node_modules/@eslint/eslintrc": {
+ "version": "3.3.3",
+ "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.3.tgz",
+ "integrity": "sha512-Kr+LPIUVKz2qkx1HAMH8q1q6azbqBAsXJUxBl/ODDuVPX45Z9DfwB8tPjTi6nNZ8BuM3nbJxC5zCAg5elnBUTQ==",
+ "dev": true,
+ "license": "MIT",
+ "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.1",
+ "minimatch": "^3.1.2",
+ "strip-json-comments": "^3.1.1"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/@eslint/js": {
+ "version": "9.39.2",
+ "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.39.2.tgz",
+ "integrity": "sha512-q1mjIoW1VX4IvSocvM/vbTiveKC4k9eLrajNEuSsmjymSDEbpGddtpfOoN7YGAqBK3NG+uqo8ia4PDTt8buCYA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "url": "https://eslint.org/donate"
+ }
+ },
+ "node_modules/@eslint/object-schema": {
+ "version": "2.1.7",
+ "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.7.tgz",
+ "integrity": "sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ }
+ },
+ "node_modules/@eslint/plugin-kit": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.4.1.tgz",
+ "integrity": "sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@eslint/core": "^0.17.0",
+ "levn": "^0.4.1"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ }
+ },
+ "node_modules/@floating-ui/core": {
+ "version": "1.7.3",
+ "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.7.3.tgz",
+ "integrity": "sha512-sGnvb5dmrJaKEZ+LDIpguvdX3bDlEllmv4/ClQ9awcmCZrlx5jQyyMWFM5kBI+EyNOCDDiKk8il0zeuX3Zlg/w==",
+ "license": "MIT",
+ "dependencies": {
+ "@floating-ui/utils": "^0.2.10"
+ }
+ },
+ "node_modules/@floating-ui/dom": {
+ "version": "1.7.4",
+ "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.7.4.tgz",
+ "integrity": "sha512-OOchDgh4F2CchOX94cRVqhvy7b3AFb+/rQXyswmzmGakRfkMgoWVjfnLWkRirfLEfuD4ysVW16eXzwt3jHIzKA==",
+ "license": "MIT",
+ "dependencies": {
+ "@floating-ui/core": "^1.7.3",
+ "@floating-ui/utils": "^0.2.10"
+ }
+ },
+ "node_modules/@floating-ui/react-dom": {
+ "version": "2.1.6",
+ "resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-2.1.6.tgz",
+ "integrity": "sha512-4JX6rEatQEvlmgU80wZyq9RT96HZJa88q8hp0pBd+LrczeDI4o6uA2M+uvxngVHo4Ihr8uibXxH6+70zhAFrVw==",
+ "license": "MIT",
+ "dependencies": {
+ "@floating-ui/dom": "^1.7.4"
+ },
+ "peerDependencies": {
+ "react": ">=16.8.0",
+ "react-dom": ">=16.8.0"
+ }
+ },
+ "node_modules/@floating-ui/utils": {
+ "version": "0.2.10",
+ "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.10.tgz",
+ "integrity": "sha512-aGTxbpbg8/b5JfU1HXSrbH3wXZuLPJcNEcZQFMxLs3oSzgtVu6nFPkbbGGUvBcUjKV2YyB9Wxxabo+HEH9tcRQ==",
+ "license": "MIT"
+ },
+ "node_modules/@humanfs/core": {
+ "version": "0.19.1",
+ "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz",
+ "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=18.18.0"
+ }
+ },
+ "node_modules/@humanfs/node": {
+ "version": "0.16.7",
+ "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.7.tgz",
+ "integrity": "sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@humanfs/core": "^0.19.1",
+ "@humanwhocodes/retry": "^0.4.0"
+ },
+ "engines": {
+ "node": ">=18.18.0"
+ }
+ },
+ "node_modules/@humanwhocodes/module-importer": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz",
+ "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=12.22"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/nzakas"
+ }
+ },
+ "node_modules/@humanwhocodes/retry": {
+ "version": "0.4.3",
+ "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz",
+ "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=18.18"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/nzakas"
+ }
+ },
+ "node_modules/@img/colour": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/@img/colour/-/colour-1.0.0.tgz",
+ "integrity": "sha512-A5P/LfWGFSl6nsckYtjw9da+19jB8hkJ6ACTGcDfEJ0aE+l2n2El7dsVM7UVHZQ9s2lmYMWlrS21YLy2IR1LUw==",
+ "license": "MIT",
+ "optional": true,
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@img/sharp-darwin-arm64": {
+ "version": "0.34.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.34.5.tgz",
+ "integrity": "sha512-imtQ3WMJXbMY4fxb/Ndp6HBTNVtWCUI0WdobyheGf5+ad6xX8VIDO8u2xE4qc/fr08CKG/7dDseFtn6M6g/r3w==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-libvips-darwin-arm64": "1.2.4"
+ }
+ },
+ "node_modules/@img/sharp-darwin-x64": {
+ "version": "0.34.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.34.5.tgz",
+ "integrity": "sha512-YNEFAF/4KQ/PeW0N+r+aVVsoIY0/qxxikF2SWdp+NRkmMB7y9LBZAVqQ4yhGCm/H3H270OSykqmQMKLBhBJDEw==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-libvips-darwin-x64": "1.2.4"
+ }
+ },
+ "node_modules/@img/sharp-libvips-darwin-arm64": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.2.4.tgz",
+ "integrity": "sha512-zqjjo7RatFfFoP0MkQ51jfuFZBnVE2pRiaydKJ1G/rHZvnsrHAOcQALIi9sA5co5xenQdTugCvtb1cuf78Vf4g==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-libvips-darwin-x64": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.2.4.tgz",
+ "integrity": "sha512-1IOd5xfVhlGwX+zXv2N93k0yMONvUlANylbJw1eTah8K/Jtpi15KC+WSiaX/nBmbm2HxRM1gZ0nSdjSsrZbGKg==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-libvips-linux-arm": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.2.4.tgz",
+ "integrity": "sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A==",
+ "cpu": [
+ "arm"
+ ],
+ "license": "LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-libvips-linux-arm64": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.2.4.tgz",
+ "integrity": "sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-libvips-linux-ppc64": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.2.4.tgz",
+ "integrity": "sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA==",
+ "cpu": [
+ "ppc64"
+ ],
+ "license": "LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-libvips-linux-riscv64": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-riscv64/-/sharp-libvips-linux-riscv64-1.2.4.tgz",
+ "integrity": "sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA==",
+ "cpu": [
+ "riscv64"
+ ],
+ "license": "LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-libvips-linux-s390x": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.2.4.tgz",
+ "integrity": "sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ==",
+ "cpu": [
+ "s390x"
+ ],
+ "license": "LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-libvips-linux-x64": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.2.4.tgz",
+ "integrity": "sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-libvips-linuxmusl-arm64": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.2.4.tgz",
+ "integrity": "sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-libvips-linuxmusl-x64": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.2.4.tgz",
+ "integrity": "sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-linux-arm": {
+ "version": "0.34.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.34.5.tgz",
+ "integrity": "sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw==",
+ "cpu": [
+ "arm"
+ ],
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-libvips-linux-arm": "1.2.4"
+ }
+ },
+ "node_modules/@img/sharp-linux-arm64": {
+ "version": "0.34.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.34.5.tgz",
+ "integrity": "sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-libvips-linux-arm64": "1.2.4"
+ }
+ },
+ "node_modules/@img/sharp-linux-ppc64": {
+ "version": "0.34.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-linux-ppc64/-/sharp-linux-ppc64-0.34.5.tgz",
+ "integrity": "sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA==",
+ "cpu": [
+ "ppc64"
+ ],
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-libvips-linux-ppc64": "1.2.4"
+ }
+ },
+ "node_modules/@img/sharp-linux-riscv64": {
+ "version": "0.34.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-linux-riscv64/-/sharp-linux-riscv64-0.34.5.tgz",
+ "integrity": "sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw==",
+ "cpu": [
+ "riscv64"
+ ],
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-libvips-linux-riscv64": "1.2.4"
+ }
+ },
+ "node_modules/@img/sharp-linux-s390x": {
+ "version": "0.34.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.34.5.tgz",
+ "integrity": "sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg==",
+ "cpu": [
+ "s390x"
+ ],
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-libvips-linux-s390x": "1.2.4"
+ }
+ },
+ "node_modules/@img/sharp-linux-x64": {
+ "version": "0.34.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.34.5.tgz",
+ "integrity": "sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-libvips-linux-x64": "1.2.4"
+ }
+ },
+ "node_modules/@img/sharp-linuxmusl-arm64": {
+ "version": "0.34.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.34.5.tgz",
+ "integrity": "sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-libvips-linuxmusl-arm64": "1.2.4"
+ }
+ },
+ "node_modules/@img/sharp-linuxmusl-x64": {
+ "version": "0.34.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.34.5.tgz",
+ "integrity": "sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-libvips-linuxmusl-x64": "1.2.4"
+ }
+ },
+ "node_modules/@img/sharp-wasm32": {
+ "version": "0.34.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.34.5.tgz",
+ "integrity": "sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw==",
+ "cpu": [
+ "wasm32"
+ ],
+ "license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT",
+ "optional": true,
+ "dependencies": {
+ "@emnapi/runtime": "^1.7.0"
+ },
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-win32-arm64": {
+ "version": "0.34.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.34.5.tgz",
+ "integrity": "sha512-WQ3AgWCWYSb2yt+IG8mnC6Jdk9Whs7O0gxphblsLvdhSpSTtmu69ZG1Gkb6NuvxsNACwiPV6cNSZNzt0KPsw7g==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "Apache-2.0 AND LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-win32-ia32": {
+ "version": "0.34.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.34.5.tgz",
+ "integrity": "sha512-FV9m/7NmeCmSHDD5j4+4pNI8Cp3aW+JvLoXcTUo0IqyjSfAZJ8dIUmijx1qaJsIiU+Hosw6xM5KijAWRJCSgNg==",
+ "cpu": [
+ "ia32"
+ ],
+ "license": "Apache-2.0 AND LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-win32-x64": {
+ "version": "0.34.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.34.5.tgz",
+ "integrity": "sha512-+29YMsqY2/9eFEiW93eqWnuLcWcufowXewwSNIT6UwZdUUCrM3oFjMWH/Z6/TMmb4hlFenmfAVbpWeup2jryCw==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "Apache-2.0 AND LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@jridgewell/gen-mapping": {
+ "version": "0.3.13",
+ "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz",
+ "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@jridgewell/sourcemap-codec": "^1.5.0",
+ "@jridgewell/trace-mapping": "^0.3.24"
+ }
+ },
+ "node_modules/@jridgewell/remapping": {
+ "version": "2.3.5",
+ "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz",
+ "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@jridgewell/gen-mapping": "^0.3.5",
+ "@jridgewell/trace-mapping": "^0.3.24"
+ }
+ },
+ "node_modules/@jridgewell/resolve-uri": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz",
+ "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/@jridgewell/sourcemap-codec": {
+ "version": "1.5.5",
+ "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz",
+ "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@jridgewell/trace-mapping": {
+ "version": "0.3.31",
+ "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz",
+ "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@jridgewell/resolve-uri": "^3.1.0",
+ "@jridgewell/sourcemap-codec": "^1.4.14"
+ }
+ },
+ "node_modules/@napi-rs/wasm-runtime": {
+ "version": "0.2.12",
+ "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-0.2.12.tgz",
+ "integrity": "sha512-ZVWUcfwY4E/yPitQJl481FjFo3K22D6qF0DuFH6Y/nbnE11GY5uguDxZMGXPQ8WQ0128MXQD7TnfHyK4oWoIJQ==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "@emnapi/core": "^1.4.3",
+ "@emnapi/runtime": "^1.4.3",
+ "@tybys/wasm-util": "^0.10.0"
+ }
+ },
+ "node_modules/@next/env": {
+ "version": "16.1.0",
+ "resolved": "https://registry.npmjs.org/@next/env/-/env-16.1.0.tgz",
+ "integrity": "sha512-Dd23XQeFHmhf3KBW76leYVkejHlCdB7erakC2At2apL1N08Bm+dLYNP+nNHh0tzUXfPQcNcXiQyacw0PG4Fcpw==",
+ "license": "MIT"
+ },
+ "node_modules/@next/eslint-plugin-next": {
+ "version": "16.1.0",
+ "resolved": "https://registry.npmjs.org/@next/eslint-plugin-next/-/eslint-plugin-next-16.1.0.tgz",
+ "integrity": "sha512-sooC/k0LCF4/jLXYHpgfzJot04lZQqsttn8XJpTguP8N3GhqXN3wSkh68no2OcZzS/qeGwKDFTqhZ8WofdXmmQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "fast-glob": "3.3.1"
+ }
+ },
+ "node_modules/@next/swc-darwin-arm64": {
+ "version": "16.1.0",
+ "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-16.1.0.tgz",
+ "integrity": "sha512-onHq8dl8KjDb8taANQdzs3XmIqQWV3fYdslkGENuvVInFQzZnuBYYOG2HGHqqtvgmEU7xWzhgndXXxnhk4Z3fQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@next/swc-darwin-x64": {
+ "version": "16.1.0",
+ "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-16.1.0.tgz",
+ "integrity": "sha512-Am6VJTp8KhLuAH13tPrAoVIXzuComlZlMwGr++o2KDjWiKPe3VwpxYhgV6I4gKls2EnsIMggL4y7GdXyDdJcFA==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@next/swc-linux-arm64-gnu": {
+ "version": "16.1.0",
+ "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-16.1.0.tgz",
+ "integrity": "sha512-fVicfaJT6QfghNyg8JErZ+EMNQ812IS0lmKfbmC01LF1nFBcKfcs4Q75Yy8IqnsCqH/hZwGhqzj3IGVfWV6vpA==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@next/swc-linux-arm64-musl": {
+ "version": "16.1.0",
+ "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-16.1.0.tgz",
+ "integrity": "sha512-TojQnDRoX7wJWXEEwdfuJtakMDW64Q7NrxQPviUnfYJvAx5/5wcGE+1vZzQ9F17m+SdpFeeXuOr6v3jbyusYMQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@next/swc-linux-x64-gnu": {
+ "version": "16.1.0",
+ "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-16.1.0.tgz",
+ "integrity": "sha512-quhNFVySW4QwXiZkZ34SbfzNBm27vLrxZ2HwTfFFO1BBP0OY1+pI0nbyewKeq1FriqU+LZrob/cm26lwsiAi8Q==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@next/swc-linux-x64-musl": {
+ "version": "16.1.0",
+ "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-16.1.0.tgz",
+ "integrity": "sha512-6JW0z2FZUK5iOVhUIWqE4RblAhUj1EwhZ/MwteGb//SpFTOHydnhbp3868gxalwea+mbOLWO6xgxj9wA9wNvNw==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@next/swc-win32-arm64-msvc": {
+ "version": "16.1.0",
+ "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-16.1.0.tgz",
+ "integrity": "sha512-+DK/akkAvvXn5RdYN84IOmLkSy87SCmpofJPdB8vbLmf01BzntPBSYXnMvnEEv/Vcf3HYJwt24QZ/s6sWAwOMQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@next/swc-win32-x64-msvc": {
+ "version": "16.1.0",
+ "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-16.1.0.tgz",
+ "integrity": "sha512-Tr0j94MphimCCks+1rtYPzQFK+faJuhHWCegU9S9gDlgyOk8Y3kPmO64UcjyzZAlligeBtYZ/2bEyrKq0d2wqQ==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@nodelib/fs.scandir": {
+ "version": "2.1.5",
+ "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
+ "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@nodelib/fs.stat": "2.0.5",
+ "run-parallel": "^1.1.9"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/@nodelib/fs.stat": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz",
+ "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/@nodelib/fs.walk": {
+ "version": "1.2.8",
+ "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz",
+ "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@nodelib/fs.scandir": "2.1.5",
+ "fastq": "^1.6.0"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/@nolyfill/is-core-module": {
+ "version": "1.0.39",
+ "resolved": "https://registry.npmjs.org/@nolyfill/is-core-module/-/is-core-module-1.0.39.tgz",
+ "integrity": "sha512-nn5ozdjYQpUCZlWGuxcJY/KpxkWQs4DcbMCmKojjyrYDEAGy4Ce19NN4v5MduafTwJlbKc99UA8YhSVqq9yPZA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12.4.0"
+ }
+ },
+ "node_modules/@radix-ui/number": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/@radix-ui/number/-/number-1.1.1.tgz",
+ "integrity": "sha512-MkKCwxlXTgz6CFoJx3pCwn07GKp36+aZyu/u2Ln2VrA5DcdyCZkASEDBTd8x5whTQQL5CiYf4prXKLcgQdv29g==",
+ "license": "MIT"
+ },
+ "node_modules/@radix-ui/primitive": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.1.3.tgz",
+ "integrity": "sha512-JTF99U/6XIjCBo0wqkU5sK10glYe27MRRsfwoiq5zzOEZLHU3A3KCMa5X/azekYRCJ0HlwI0crAXS/5dEHTzDg==",
+ "license": "MIT"
+ },
+ "node_modules/@radix-ui/react-arrow": {
+ "version": "1.1.7",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-arrow/-/react-arrow-1.1.7.tgz",
+ "integrity": "sha512-F+M1tLhO+mlQaOWspE8Wstg+z6PwxwRd8oQ8IXceWz92kfAmalTRf0EjrouQeo7QssEPfCn05B4Ihs1K9WQ/7w==",
+ "license": "MIT",
+ "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"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ },
+ "@types/react-dom": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-collection": {
+ "version": "1.1.7",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-collection/-/react-collection-1.1.7.tgz",
+ "integrity": "sha512-Fh9rGN0MoI4ZFUNyfFVNU4y9LUz93u9/0K+yLgA2bwRojxM8JU1DyvvMBabnZPBgMWREAJvU2jjVzq+LrFUglw==",
+ "license": "MIT",
+ "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"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ },
+ "@types/react-dom": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-collection/node_modules/@radix-ui/react-slot": {
+ "version": "1.2.3",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz",
+ "integrity": "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==",
+ "license": "MIT",
+ "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"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-compose-refs": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.2.tgz",
+ "integrity": "sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg==",
+ "license": "MIT",
+ "peerDependencies": {
+ "@types/react": "*",
+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-context": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.1.2.tgz",
+ "integrity": "sha512-jCi/QKUM2r1Ju5a3J64TH2A5SpKAgh0LpknyqdQ4m6DCV0xJ2HG1xARRwNGPQfi1SLdLWZ1OJz6F4OMBBNiGJA==",
+ "license": "MIT",
+ "peerDependencies": {
+ "@types/react": "*",
+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-dialog": {
+ "version": "1.1.15",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-dialog/-/react-dialog-1.1.15.tgz",
+ "integrity": "sha512-TCglVRtzlffRNxRMEyR36DGBLJpeusFcgMVD9PZEzAKnUs1lKCgX5u9BmC2Yg+LL9MgZDugFFs1Vl+Jp4t/PGw==",
+ "license": "MIT",
+ "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"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ },
+ "@types/react-dom": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-dialog/node_modules/@radix-ui/react-slot": {
+ "version": "1.2.3",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz",
+ "integrity": "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==",
+ "license": "MIT",
+ "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"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-direction": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-direction/-/react-direction-1.1.1.tgz",
+ "integrity": "sha512-1UEWRX6jnOA2y4H5WczZ44gOOjTEmlqv1uNW4GAJEO5+bauCBhv8snY65Iw5/VOS/ghKN9gr2KjnLKxrsvoMVw==",
+ "license": "MIT",
+ "peerDependencies": {
+ "@types/react": "*",
+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-dismissable-layer": {
+ "version": "1.1.11",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.1.11.tgz",
+ "integrity": "sha512-Nqcp+t5cTB8BinFkZgXiMJniQH0PsUt2k51FUhbdfeKvc4ACcG2uQniY/8+h1Yv6Kza4Q7lD7PQV0z0oicE0Mg==",
+ "license": "MIT",
+ "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"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ },
+ "@types/react-dom": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-focus-guards": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-guards/-/react-focus-guards-1.1.3.tgz",
+ "integrity": "sha512-0rFg/Rj2Q62NCm62jZw0QX7a3sz6QCQU0LpZdNrJX8byRGaGVTqbrW9jAoIAHyMQqsNpeZ81YgSizOt5WXq0Pw==",
+ "license": "MIT",
+ "peerDependencies": {
+ "@types/react": "*",
+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-focus-scope": {
+ "version": "1.1.7",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-scope/-/react-focus-scope-1.1.7.tgz",
+ "integrity": "sha512-t2ODlkXBQyn7jkl6TNaw/MtVEVvIGelJDCG41Okq/KwUsJBwQ4XVZsHAVUkK4mBv3ewiAS3PGuUWuY2BoK4ZUw==",
+ "license": "MIT",
+ "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"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ },
+ "@types/react-dom": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-id": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-id/-/react-id-1.1.1.tgz",
+ "integrity": "sha512-kGkGegYIdQsOb4XjsfM97rXsiHaBwco+hFI66oO4s9LU+PLAC5oJ7khdOVFxkhsmlbpUqDAvXw11CluXP+jkHg==",
+ "license": "MIT",
+ "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"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-popover": {
+ "version": "1.1.15",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-popover/-/react-popover-1.1.15.tgz",
+ "integrity": "sha512-kr0X2+6Yy/vJzLYJUPCZEc8SfQcf+1COFoAqauJm74umQhta9M7lNJHP7QQS3vkvcGLQUbWpMzwrXYwrYztHKA==",
+ "license": "MIT",
+ "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"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ },
+ "@types/react-dom": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-popover/node_modules/@radix-ui/react-slot": {
+ "version": "1.2.3",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz",
+ "integrity": "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==",
+ "license": "MIT",
+ "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"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-popper": {
+ "version": "1.2.8",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-popper/-/react-popper-1.2.8.tgz",
+ "integrity": "sha512-0NJQ4LFFUuWkE7Oxf0htBKS6zLkkjBH+hM1uk7Ng705ReR8m/uelduy1DBo0PyBXPKVnBA6YBlU94MBGXrSBCw==",
+ "license": "MIT",
+ "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"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ },
+ "@types/react-dom": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-portal": {
+ "version": "1.1.9",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-portal/-/react-portal-1.1.9.tgz",
+ "integrity": "sha512-bpIxvq03if6UNwXZ+HTK71JLh4APvnXntDc6XOX8UVq4XQOVl7lwok0AvIl+b8zgCw3fSaVTZMpAPPagXbKmHQ==",
+ "license": "MIT",
+ "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"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ },
+ "@types/react-dom": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-presence": {
+ "version": "1.1.5",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-presence/-/react-presence-1.1.5.tgz",
+ "integrity": "sha512-/jfEwNDdQVBCNvjkGit4h6pMOzq8bHkopq458dPt2lMjx+eBQUohZNG9A7DtO/O5ukSbxuaNGXMjHicgwy6rQQ==",
+ "license": "MIT",
+ "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"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ },
+ "@types/react-dom": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-primitive": {
+ "version": "2.1.3",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.3.tgz",
+ "integrity": "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ==",
+ "license": "MIT",
+ "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"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ },
+ "@types/react-dom": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-primitive/node_modules/@radix-ui/react-slot": {
+ "version": "1.2.3",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz",
+ "integrity": "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==",
+ "license": "MIT",
+ "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"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-select": {
+ "version": "2.2.6",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-select/-/react-select-2.2.6.tgz",
+ "integrity": "sha512-I30RydO+bnn2PQztvo25tswPH+wFBjehVGtmagkU78yMdwTwVf12wnAOF+AeP8S2N8xD+5UPbGhkUfPyvT+mwQ==",
+ "license": "MIT",
+ "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"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ },
+ "@types/react-dom": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-select/node_modules/@radix-ui/react-slot": {
+ "version": "1.2.3",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz",
+ "integrity": "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==",
+ "license": "MIT",
+ "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"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-slot": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.4.tgz",
+ "integrity": "sha512-Jl+bCv8HxKnlTLVrcDE8zTMJ09R9/ukw4qBs/oZClOfoQk/cOTbDn+NceXfV7j09YPVQUryJPHurafcSg6EVKA==",
+ "license": "MIT",
+ "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"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-use-callback-ref": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.1.1.tgz",
+ "integrity": "sha512-FkBMwD+qbGQeMu1cOHnuGB6x4yzPjho8ap5WtbEJ26umhgqVXbhekKUQO+hZEL1vU92a3wHwdp0HAcqAUF5iDg==",
+ "license": "MIT",
+ "peerDependencies": {
+ "@types/react": "*",
+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-use-controllable-state": {
+ "version": "1.2.2",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-use-controllable-state/-/react-use-controllable-state-1.2.2.tgz",
+ "integrity": "sha512-BjasUjixPFdS+NKkypcyyN5Pmg83Olst0+c6vGov0diwTEo6mgdqVR6hxcEgFuh4QrAs7Rc+9KuGJ9TVCj0Zzg==",
+ "license": "MIT",
+ "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"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-use-effect-event": {
+ "version": "0.0.2",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-use-effect-event/-/react-use-effect-event-0.0.2.tgz",
+ "integrity": "sha512-Qp8WbZOBe+blgpuUT+lw2xheLP8q0oatc9UpmiemEICxGvFLYmHm9QowVZGHtJlGbS6A6yJ3iViad/2cVjnOiA==",
+ "license": "MIT",
+ "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"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-use-escape-keydown": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-use-escape-keydown/-/react-use-escape-keydown-1.1.1.tgz",
+ "integrity": "sha512-Il0+boE7w/XebUHyBjroE+DbByORGR9KKmITzbR7MyQ4akpORYP/ZmbhAr0DG7RmmBqoOnZdy2QlvajJ2QA59g==",
+ "license": "MIT",
+ "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"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-use-layout-effect": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-1.1.1.tgz",
+ "integrity": "sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ==",
+ "license": "MIT",
+ "peerDependencies": {
+ "@types/react": "*",
+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-use-previous": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-use-previous/-/react-use-previous-1.1.1.tgz",
+ "integrity": "sha512-2dHfToCj/pzca2Ck724OZ5L0EVrr3eHRNsG/b3xQJLA2hZpVCS99bLAX+hm1IHXDEnzU6by5z/5MIY794/a8NQ==",
+ "license": "MIT",
+ "peerDependencies": {
+ "@types/react": "*",
+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-use-rect": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-use-rect/-/react-use-rect-1.1.1.tgz",
+ "integrity": "sha512-QTYuDesS0VtuHNNvMh+CjlKJ4LJickCMUAqjlE3+j8w+RlRpwyX3apEQKGFzbZGdo7XNG1tXa+bQqIE7HIXT2w==",
+ "license": "MIT",
+ "dependencies": {
+ "@radix-ui/rect": "1.1.1"
+ },
+ "peerDependencies": {
+ "@types/react": "*",
+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-use-size": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-use-size/-/react-use-size-1.1.1.tgz",
+ "integrity": "sha512-ewrXRDTAqAXlkl6t/fkXWNAhFX9I+CkKlw6zjEwk86RSPKwZr3xpBRso655aqYafwtnbpHLj6toFzmd6xdVptQ==",
+ "license": "MIT",
+ "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"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-visually-hidden": {
+ "version": "1.2.3",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-visually-hidden/-/react-visually-hidden-1.2.3.tgz",
+ "integrity": "sha512-pzJq12tEaaIhqjbzpCuv/OypJY/BPavOofm+dbab+MHLajy277+1lLm6JFcGgF5eskJ6mquGirhXY2GD/8u8Ug==",
+ "license": "MIT",
+ "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"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ },
+ "@types/react-dom": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/rect": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/@radix-ui/rect/-/rect-1.1.1.tgz",
+ "integrity": "sha512-HPwpGIzkl28mWyZqG52jiqDJ12waP11Pa1lGoiyUkIEuMLBP0oeK/C89esbXrxsky5we7dfd8U58nm0SgAWpVw==",
+ "license": "MIT"
+ },
+ "node_modules/@rtsao/scc": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/@rtsao/scc/-/scc-1.1.0.tgz",
+ "integrity": "sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@swc/helpers": {
+ "version": "0.5.15",
+ "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.15.tgz",
+ "integrity": "sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "tslib": "^2.8.0"
+ }
+ },
+ "node_modules/@tailwindcss/node": {
+ "version": "4.1.18",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.1.18.tgz",
+ "integrity": "sha512-DoR7U1P7iYhw16qJ49fgXUlry1t4CpXeErJHnQ44JgTSKMaZUdf17cfn5mHchfJ4KRBZRFA/Coo+MUF5+gOaCQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@jridgewell/remapping": "^2.3.4",
+ "enhanced-resolve": "^5.18.3",
+ "jiti": "^2.6.1",
+ "lightningcss": "1.30.2",
+ "magic-string": "^0.30.21",
+ "source-map-js": "^1.2.1",
+ "tailwindcss": "4.1.18"
+ }
+ },
+ "node_modules/@tailwindcss/oxide": {
+ "version": "4.1.18",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.1.18.tgz",
+ "integrity": "sha512-EgCR5tTS5bUSKQgzeMClT6iCY3ToqE1y+ZB0AKldj809QXk1Y+3jB0upOYZrn9aGIzPtUsP7sX4QQ4XtjBB95A==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 10"
+ },
+ "optionalDependencies": {
+ "@tailwindcss/oxide-android-arm64": "4.1.18",
+ "@tailwindcss/oxide-darwin-arm64": "4.1.18",
+ "@tailwindcss/oxide-darwin-x64": "4.1.18",
+ "@tailwindcss/oxide-freebsd-x64": "4.1.18",
+ "@tailwindcss/oxide-linux-arm-gnueabihf": "4.1.18",
+ "@tailwindcss/oxide-linux-arm64-gnu": "4.1.18",
+ "@tailwindcss/oxide-linux-arm64-musl": "4.1.18",
+ "@tailwindcss/oxide-linux-x64-gnu": "4.1.18",
+ "@tailwindcss/oxide-linux-x64-musl": "4.1.18",
+ "@tailwindcss/oxide-wasm32-wasi": "4.1.18",
+ "@tailwindcss/oxide-win32-arm64-msvc": "4.1.18",
+ "@tailwindcss/oxide-win32-x64-msvc": "4.1.18"
+ }
+ },
+ "node_modules/@tailwindcss/oxide-android-arm64": {
+ "version": "4.1.18",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.1.18.tgz",
+ "integrity": "sha512-dJHz7+Ugr9U/diKJA0W6N/6/cjI+ZTAoxPf9Iz9BFRF2GzEX8IvXxFIi/dZBloVJX/MZGvRuFA9rqwdiIEZQ0Q==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@tailwindcss/oxide-darwin-arm64": {
+ "version": "4.1.18",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.1.18.tgz",
+ "integrity": "sha512-Gc2q4Qhs660bhjyBSKgq6BYvwDz4G+BuyJ5H1xfhmDR3D8HnHCmT/BSkvSL0vQLy/nkMLY20PQ2OoYMO15Jd0A==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@tailwindcss/oxide-darwin-x64": {
+ "version": "4.1.18",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.1.18.tgz",
+ "integrity": "sha512-FL5oxr2xQsFrc3X9o1fjHKBYBMD1QZNyc1Xzw/h5Qu4XnEBi3dZn96HcHm41c/euGV+GRiXFfh2hUCyKi/e+yw==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@tailwindcss/oxide-freebsd-x64": {
+ "version": "4.1.18",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.1.18.tgz",
+ "integrity": "sha512-Fj+RHgu5bDodmV1dM9yAxlfJwkkWvLiRjbhuO2LEtwtlYlBgiAT4x/j5wQr1tC3SANAgD+0YcmWVrj8R9trVMA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@tailwindcss/oxide-linux-arm-gnueabihf": {
+ "version": "4.1.18",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.1.18.tgz",
+ "integrity": "sha512-Fp+Wzk/Ws4dZn+LV2Nqx3IilnhH51YZoRaYHQsVq3RQvEl+71VGKFpkfHrLM/Li+kt5c0DJe/bHXK1eHgDmdiA==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@tailwindcss/oxide-linux-arm64-gnu": {
+ "version": "4.1.18",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.1.18.tgz",
+ "integrity": "sha512-S0n3jboLysNbh55Vrt7pk9wgpyTTPD0fdQeh7wQfMqLPM/Hrxi+dVsLsPrycQjGKEQk85Kgbx+6+QnYNiHalnw==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@tailwindcss/oxide-linux-arm64-musl": {
+ "version": "4.1.18",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.1.18.tgz",
+ "integrity": "sha512-1px92582HkPQlaaCkdRcio71p8bc8i/ap5807tPRDK/uw953cauQBT8c5tVGkOwrHMfc2Yh6UuxaH4vtTjGvHg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@tailwindcss/oxide-linux-x64-gnu": {
+ "version": "4.1.18",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.1.18.tgz",
+ "integrity": "sha512-v3gyT0ivkfBLoZGF9LyHmts0Isc8jHZyVcbzio6Wpzifg/+5ZJpDiRiUhDLkcr7f/r38SWNe7ucxmGW3j3Kb/g==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@tailwindcss/oxide-linux-x64-musl": {
+ "version": "4.1.18",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.1.18.tgz",
+ "integrity": "sha512-bhJ2y2OQNlcRwwgOAGMY0xTFStt4/wyU6pvI6LSuZpRgKQwxTec0/3Scu91O8ir7qCR3AuepQKLU/kX99FouqQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@tailwindcss/oxide-wasm32-wasi": {
+ "version": "4.1.18",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-wasm32-wasi/-/oxide-wasm32-wasi-4.1.18.tgz",
+ "integrity": "sha512-LffYTvPjODiP6PT16oNeUQJzNVyJl1cjIebq/rWWBF+3eDst5JGEFSc5cWxyRCJ0Mxl+KyIkqRxk1XPEs9x8TA==",
+ "bundleDependencies": [
+ "@napi-rs/wasm-runtime",
+ "@emnapi/core",
+ "@emnapi/runtime",
+ "@tybys/wasm-util",
+ "@emnapi/wasi-threads",
+ "tslib"
+ ],
+ "cpu": [
+ "wasm32"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "@emnapi/core": "^1.7.1",
+ "@emnapi/runtime": "^1.7.1",
+ "@emnapi/wasi-threads": "^1.1.0",
+ "@napi-rs/wasm-runtime": "^1.1.0",
+ "@tybys/wasm-util": "^0.10.1",
+ "tslib": "^2.4.0"
+ },
+ "engines": {
+ "node": ">=14.0.0"
+ }
+ },
+ "node_modules/@tailwindcss/oxide-win32-arm64-msvc": {
+ "version": "4.1.18",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.1.18.tgz",
+ "integrity": "sha512-HjSA7mr9HmC8fu6bdsZvZ+dhjyGCLdotjVOgLA2vEqxEBZaQo9YTX4kwgEvPCpRh8o4uWc4J/wEoFzhEmjvPbA==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@tailwindcss/oxide-win32-x64-msvc": {
+ "version": "4.1.18",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.1.18.tgz",
+ "integrity": "sha512-bJWbyYpUlqamC8dpR7pfjA0I7vdF6t5VpUGMWRkXVE3AXgIZjYUYAK7II1GNaxR8J1SSrSrppRar8G++JekE3Q==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@tailwindcss/postcss": {
+ "version": "4.1.18",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/postcss/-/postcss-4.1.18.tgz",
+ "integrity": "sha512-Ce0GFnzAOuPyfV5SxjXGn0CubwGcuDB0zcdaPuCSzAa/2vII24JTkH+I6jcbXLb1ctjZMZZI6OjDaLPJQL1S0g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@alloc/quick-lru": "^5.2.0",
+ "@tailwindcss/node": "4.1.18",
+ "@tailwindcss/oxide": "4.1.18",
+ "postcss": "^8.4.41",
+ "tailwindcss": "4.1.18"
+ }
+ },
+ "node_modules/@tanstack/query-core": {
+ "version": "5.90.12",
+ "resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-5.90.12.tgz",
+ "integrity": "sha512-T1/8t5DhV/SisWjDnaiU2drl6ySvsHj1bHBCWNXd+/T+Hh1cf6JodyEYMd5sgwm+b/mETT4EV3H+zCVczCU5hg==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/tannerlinsley"
+ }
+ },
+ "node_modules/@tanstack/react-query": {
+ "version": "5.90.12",
+ "resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-5.90.12.tgz",
+ "integrity": "sha512-graRZspg7EoEaw0a8faiUASCyJrqjKPdqJ9EwuDRUF9mEYJ1YPczI9H+/agJ0mOJkPCJDk0lsz5QTrLZ/jQ2rg==",
+ "license": "MIT",
+ "dependencies": {
+ "@tanstack/query-core": "5.90.12"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/tannerlinsley"
+ },
+ "peerDependencies": {
+ "react": "^18 || ^19"
+ }
+ },
+ "node_modules/@tybys/wasm-util": {
+ "version": "0.10.1",
+ "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.1.tgz",
+ "integrity": "sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "tslib": "^2.4.0"
+ }
+ },
+ "node_modules/@types/estree": {
+ "version": "1.0.8",
+ "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz",
+ "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@types/json-schema": {
+ "version": "7.0.15",
+ "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz",
+ "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@types/json5": {
+ "version": "0.0.29",
+ "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz",
+ "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@types/node": {
+ "version": "25.0.3",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-25.0.3.tgz",
+ "integrity": "sha512-W609buLVRVmeW693xKfzHeIV6nJGGz98uCPfeXI1ELMLXVeKYZ9m15fAMSaUPBHYLGFsVRcMmSCksQOrZV9BYA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "undici-types": "~7.16.0"
+ }
+ },
+ "node_modules/@types/react": {
+ "version": "19.2.7",
+ "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.7.tgz",
+ "integrity": "sha512-MWtvHrGZLFttgeEj28VXHxpmwYbor/ATPYbBfSFZEIRK0ecCFLl2Qo55z52Hss+UV9CRN7trSeq1zbgx7YDWWg==",
+ "devOptional": true,
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "csstype": "^3.2.2"
+ }
+ },
+ "node_modules/@types/react-dom": {
+ "version": "19.2.3",
+ "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.2.3.tgz",
+ "integrity": "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==",
+ "devOptional": true,
+ "license": "MIT",
+ "peer": true,
+ "peerDependencies": {
+ "@types/react": "^19.2.0"
+ }
+ },
+ "node_modules/@typescript-eslint/eslint-plugin": {
+ "version": "8.50.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.50.0.tgz",
+ "integrity": "sha512-O7QnmOXYKVtPrfYzMolrCTfkezCJS9+ljLdKW/+DCvRsc3UAz+sbH6Xcsv7p30+0OwUbeWfUDAQE0vpabZ3QLg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@eslint-community/regexpp": "^4.10.0",
+ "@typescript-eslint/scope-manager": "8.50.0",
+ "@typescript-eslint/type-utils": "8.50.0",
+ "@typescript-eslint/utils": "8.50.0",
+ "@typescript-eslint/visitor-keys": "8.50.0",
+ "ignore": "^7.0.0",
+ "natural-compare": "^1.4.0",
+ "ts-api-utils": "^2.1.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "@typescript-eslint/parser": "^8.50.0",
+ "eslint": "^8.57.0 || ^9.0.0",
+ "typescript": ">=4.8.4 <6.0.0"
+ }
+ },
+ "node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": {
+ "version": "7.0.5",
+ "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz",
+ "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 4"
+ }
+ },
+ "node_modules/@typescript-eslint/parser": {
+ "version": "8.50.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.50.0.tgz",
+ "integrity": "sha512-6/cmF2piao+f6wSxUsJLZjck7OQsYyRtcOZS02k7XINSNlz93v6emM8WutDQSXnroG2xwYlEVHJI+cPA7CPM3Q==",
+ "dev": true,
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "@typescript-eslint/scope-manager": "8.50.0",
+ "@typescript-eslint/types": "8.50.0",
+ "@typescript-eslint/typescript-estree": "8.50.0",
+ "@typescript-eslint/visitor-keys": "8.50.0",
+ "debug": "^4.3.4"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "eslint": "^8.57.0 || ^9.0.0",
+ "typescript": ">=4.8.4 <6.0.0"
+ }
+ },
+ "node_modules/@typescript-eslint/project-service": {
+ "version": "8.50.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.50.0.tgz",
+ "integrity": "sha512-Cg/nQcL1BcoTijEWyx4mkVC56r8dj44bFDvBdygifuS20f3OZCHmFbjF34DPSi07kwlFvqfv/xOLnJ5DquxSGQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@typescript-eslint/tsconfig-utils": "^8.50.0",
+ "@typescript-eslint/types": "^8.50.0",
+ "debug": "^4.3.4"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "typescript": ">=4.8.4 <6.0.0"
+ }
+ },
+ "node_modules/@typescript-eslint/scope-manager": {
+ "version": "8.50.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.50.0.tgz",
+ "integrity": "sha512-xCwfuCZjhIqy7+HKxBLrDVT5q/iq7XBVBXLn57RTIIpelLtEIZHXAF/Upa3+gaCpeV1NNS5Z9A+ID6jn50VD4A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@typescript-eslint/types": "8.50.0",
+ "@typescript-eslint/visitor-keys": "8.50.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ }
+ },
+ "node_modules/@typescript-eslint/tsconfig-utils": {
+ "version": "8.50.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.50.0.tgz",
+ "integrity": "sha512-vxd3G/ybKTSlm31MOA96gqvrRGv9RJ7LGtZCn2Vrc5htA0zCDvcMqUkifcjrWNNKXHUU3WCkYOzzVSFBd0wa2w==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "typescript": ">=4.8.4 <6.0.0"
+ }
+ },
+ "node_modules/@typescript-eslint/type-utils": {
+ "version": "8.50.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.50.0.tgz",
+ "integrity": "sha512-7OciHT2lKCewR0mFoBrvZJ4AXTMe/sYOe87289WAViOocEmDjjv8MvIOT2XESuKj9jp8u3SZYUSh89QA4S1kQw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@typescript-eslint/types": "8.50.0",
+ "@typescript-eslint/typescript-estree": "8.50.0",
+ "@typescript-eslint/utils": "8.50.0",
+ "debug": "^4.3.4",
+ "ts-api-utils": "^2.1.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "eslint": "^8.57.0 || ^9.0.0",
+ "typescript": ">=4.8.4 <6.0.0"
+ }
+ },
+ "node_modules/@typescript-eslint/types": {
+ "version": "8.50.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.50.0.tgz",
+ "integrity": "sha512-iX1mgmGrXdANhhITbpp2QQM2fGehBse9LbTf0sidWK6yg/NE+uhV5dfU1g6EYPlcReYmkE9QLPq/2irKAmtS9w==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ }
+ },
+ "node_modules/@typescript-eslint/typescript-estree": {
+ "version": "8.50.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.50.0.tgz",
+ "integrity": "sha512-W7SVAGBR/IX7zm1t70Yujpbk+zdPq/u4soeFSknWFdXIFuWsBGBOUu/Tn/I6KHSKvSh91OiMuaSnYp3mtPt5IQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@typescript-eslint/project-service": "8.50.0",
+ "@typescript-eslint/tsconfig-utils": "8.50.0",
+ "@typescript-eslint/types": "8.50.0",
+ "@typescript-eslint/visitor-keys": "8.50.0",
+ "debug": "^4.3.4",
+ "minimatch": "^9.0.4",
+ "semver": "^7.6.0",
+ "tinyglobby": "^0.2.15",
+ "ts-api-utils": "^2.1.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "typescript": ">=4.8.4 <6.0.0"
+ }
+ },
+ "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz",
+ "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "balanced-match": "^1.0.0"
+ }
+ },
+ "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": {
+ "version": "9.0.5",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz",
+ "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "brace-expansion": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=16 || 14 >=14.17"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/@typescript-eslint/utils": {
+ "version": "8.50.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.50.0.tgz",
+ "integrity": "sha512-87KgUXET09CRjGCi2Ejxy3PULXna63/bMYv72tCAlDJC3Yqwln0HiFJ3VJMst2+mEtNtZu5oFvX4qJGjKsnAgg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@eslint-community/eslint-utils": "^4.7.0",
+ "@typescript-eslint/scope-manager": "8.50.0",
+ "@typescript-eslint/types": "8.50.0",
+ "@typescript-eslint/typescript-estree": "8.50.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "eslint": "^8.57.0 || ^9.0.0",
+ "typescript": ">=4.8.4 <6.0.0"
+ }
+ },
+ "node_modules/@typescript-eslint/visitor-keys": {
+ "version": "8.50.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.50.0.tgz",
+ "integrity": "sha512-Xzmnb58+Db78gT/CCj/PVCvK+zxbnsw6F+O1oheYszJbBSdEjVhQi3C/Xttzxgi/GLmpvOggRs1RFpiJ8+c34Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@typescript-eslint/types": "8.50.0",
+ "eslint-visitor-keys": "^4.2.1"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ }
+ },
+ "node_modules/@unrs/resolver-binding-android-arm-eabi": {
+ "version": "1.11.1",
+ "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-android-arm-eabi/-/resolver-binding-android-arm-eabi-1.11.1.tgz",
+ "integrity": "sha512-ppLRUgHVaGRWUx0R0Ut06Mjo9gBaBkg3v/8AxusGLhsIotbBLuRk51rAzqLC8gq6NyyAojEXglNjzf6R948DNw==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ]
+ },
+ "node_modules/@unrs/resolver-binding-android-arm64": {
+ "version": "1.11.1",
+ "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-android-arm64/-/resolver-binding-android-arm64-1.11.1.tgz",
+ "integrity": "sha512-lCxkVtb4wp1v+EoN+HjIG9cIIzPkX5OtM03pQYkG+U5O/wL53LC4QbIeazgiKqluGeVEeBlZahHalCaBvU1a2g==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ]
+ },
+ "node_modules/@unrs/resolver-binding-darwin-arm64": {
+ "version": "1.11.1",
+ "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-darwin-arm64/-/resolver-binding-darwin-arm64-1.11.1.tgz",
+ "integrity": "sha512-gPVA1UjRu1Y/IsB/dQEsp2V1pm44Of6+LWvbLc9SDk1c2KhhDRDBUkQCYVWe6f26uJb3fOK8saWMgtX8IrMk3g==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ]
+ },
+ "node_modules/@unrs/resolver-binding-darwin-x64": {
+ "version": "1.11.1",
+ "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-darwin-x64/-/resolver-binding-darwin-x64-1.11.1.tgz",
+ "integrity": "sha512-cFzP7rWKd3lZaCsDze07QX1SC24lO8mPty9vdP+YVa3MGdVgPmFc59317b2ioXtgCMKGiCLxJ4HQs62oz6GfRQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ]
+ },
+ "node_modules/@unrs/resolver-binding-freebsd-x64": {
+ "version": "1.11.1",
+ "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-freebsd-x64/-/resolver-binding-freebsd-x64-1.11.1.tgz",
+ "integrity": "sha512-fqtGgak3zX4DCB6PFpsH5+Kmt/8CIi4Bry4rb1ho6Av2QHTREM+47y282Uqiu3ZRF5IQioJQ5qWRV6jduA+iGw==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ]
+ },
+ "node_modules/@unrs/resolver-binding-linux-arm-gnueabihf": {
+ "version": "1.11.1",
+ "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm-gnueabihf/-/resolver-binding-linux-arm-gnueabihf-1.11.1.tgz",
+ "integrity": "sha512-u92mvlcYtp9MRKmP+ZvMmtPN34+/3lMHlyMj7wXJDeXxuM0Vgzz0+PPJNsro1m3IZPYChIkn944wW8TYgGKFHw==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@unrs/resolver-binding-linux-arm-musleabihf": {
+ "version": "1.11.1",
+ "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm-musleabihf/-/resolver-binding-linux-arm-musleabihf-1.11.1.tgz",
+ "integrity": "sha512-cINaoY2z7LVCrfHkIcmvj7osTOtm6VVT16b5oQdS4beibX2SYBwgYLmqhBjA1t51CarSaBuX5YNsWLjsqfW5Cw==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@unrs/resolver-binding-linux-arm64-gnu": {
+ "version": "1.11.1",
+ "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm64-gnu/-/resolver-binding-linux-arm64-gnu-1.11.1.tgz",
+ "integrity": "sha512-34gw7PjDGB9JgePJEmhEqBhWvCiiWCuXsL9hYphDF7crW7UgI05gyBAi6MF58uGcMOiOqSJ2ybEeCvHcq0BCmQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@unrs/resolver-binding-linux-arm64-musl": {
+ "version": "1.11.1",
+ "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm64-musl/-/resolver-binding-linux-arm64-musl-1.11.1.tgz",
+ "integrity": "sha512-RyMIx6Uf53hhOtJDIamSbTskA99sPHS96wxVE/bJtePJJtpdKGXO1wY90oRdXuYOGOTuqjT8ACccMc4K6QmT3w==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@unrs/resolver-binding-linux-ppc64-gnu": {
+ "version": "1.11.1",
+ "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-ppc64-gnu/-/resolver-binding-linux-ppc64-gnu-1.11.1.tgz",
+ "integrity": "sha512-D8Vae74A4/a+mZH0FbOkFJL9DSK2R6TFPC9M+jCWYia/q2einCubX10pecpDiTmkJVUH+y8K3BZClycD8nCShA==",
+ "cpu": [
+ "ppc64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@unrs/resolver-binding-linux-riscv64-gnu": {
+ "version": "1.11.1",
+ "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-riscv64-gnu/-/resolver-binding-linux-riscv64-gnu-1.11.1.tgz",
+ "integrity": "sha512-frxL4OrzOWVVsOc96+V3aqTIQl1O2TjgExV4EKgRY09AJ9leZpEg8Ak9phadbuX0BA4k8U5qtvMSQQGGmaJqcQ==",
+ "cpu": [
+ "riscv64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@unrs/resolver-binding-linux-riscv64-musl": {
+ "version": "1.11.1",
+ "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-riscv64-musl/-/resolver-binding-linux-riscv64-musl-1.11.1.tgz",
+ "integrity": "sha512-mJ5vuDaIZ+l/acv01sHoXfpnyrNKOk/3aDoEdLO/Xtn9HuZlDD6jKxHlkN8ZhWyLJsRBxfv9GYM2utQ1SChKew==",
+ "cpu": [
+ "riscv64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@unrs/resolver-binding-linux-s390x-gnu": {
+ "version": "1.11.1",
+ "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-s390x-gnu/-/resolver-binding-linux-s390x-gnu-1.11.1.tgz",
+ "integrity": "sha512-kELo8ebBVtb9sA7rMe1Cph4QHreByhaZ2QEADd9NzIQsYNQpt9UkM9iqr2lhGr5afh885d/cB5QeTXSbZHTYPg==",
+ "cpu": [
+ "s390x"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@unrs/resolver-binding-linux-x64-gnu": {
+ "version": "1.11.1",
+ "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-x64-gnu/-/resolver-binding-linux-x64-gnu-1.11.1.tgz",
+ "integrity": "sha512-C3ZAHugKgovV5YvAMsxhq0gtXuwESUKc5MhEtjBpLoHPLYM+iuwSj3lflFwK3DPm68660rZ7G8BMcwSro7hD5w==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@unrs/resolver-binding-linux-x64-musl": {
+ "version": "1.11.1",
+ "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-x64-musl/-/resolver-binding-linux-x64-musl-1.11.1.tgz",
+ "integrity": "sha512-rV0YSoyhK2nZ4vEswT/QwqzqQXw5I6CjoaYMOX0TqBlWhojUf8P94mvI7nuJTeaCkkds3QE4+zS8Ko+GdXuZtA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@unrs/resolver-binding-wasm32-wasi": {
+ "version": "1.11.1",
+ "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-wasm32-wasi/-/resolver-binding-wasm32-wasi-1.11.1.tgz",
+ "integrity": "sha512-5u4RkfxJm+Ng7IWgkzi3qrFOvLvQYnPBmjmZQ8+szTK/b31fQCnleNl1GgEt7nIsZRIf5PLhPwT0WM+q45x/UQ==",
+ "cpu": [
+ "wasm32"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "@napi-rs/wasm-runtime": "^0.2.11"
+ },
+ "engines": {
+ "node": ">=14.0.0"
+ }
+ },
+ "node_modules/@unrs/resolver-binding-win32-arm64-msvc": {
+ "version": "1.11.1",
+ "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-arm64-msvc/-/resolver-binding-win32-arm64-msvc-1.11.1.tgz",
+ "integrity": "sha512-nRcz5Il4ln0kMhfL8S3hLkxI85BXs3o8EYoattsJNdsX4YUU89iOkVn7g0VHSRxFuVMdM4Q1jEpIId1Ihim/Uw==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ]
+ },
+ "node_modules/@unrs/resolver-binding-win32-ia32-msvc": {
+ "version": "1.11.1",
+ "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-ia32-msvc/-/resolver-binding-win32-ia32-msvc-1.11.1.tgz",
+ "integrity": "sha512-DCEI6t5i1NmAZp6pFonpD5m7i6aFrpofcp4LA2i8IIq60Jyo28hamKBxNrZcyOwVOZkgsRp9O2sXWBWP8MnvIQ==",
+ "cpu": [
+ "ia32"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ]
+ },
+ "node_modules/@unrs/resolver-binding-win32-x64-msvc": {
+ "version": "1.11.1",
+ "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-x64-msvc/-/resolver-binding-win32-x64-msvc-1.11.1.tgz",
+ "integrity": "sha512-lrW200hZdbfRtztbygyaq/6jP6AKE8qQN2KvPcJ+x7wiD038YtnYtZ82IMNJ69GJibV7bwL3y9FgK+5w/pYt6g==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ]
+ },
+ "node_modules/acorn": {
+ "version": "8.15.0",
+ "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz",
+ "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==",
+ "dev": true,
+ "license": "MIT",
+ "peer": true,
+ "bin": {
+ "acorn": "bin/acorn"
+ },
+ "engines": {
+ "node": ">=0.4.0"
+ }
+ },
+ "node_modules/acorn-jsx": {
+ "version": "5.3.2",
+ "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz",
+ "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==",
+ "dev": true,
+ "license": "MIT",
+ "peerDependencies": {
+ "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0"
+ }
+ },
+ "node_modules/ajv": {
+ "version": "6.12.6",
+ "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
+ "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
+ "dev": true,
+ "license": "MIT",
+ "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"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/epoberezkin"
+ }
+ },
+ "node_modules/ansi-styles": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+ "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "color-convert": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ }
+ },
+ "node_modules/argparse": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
+ "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
+ "dev": true,
+ "license": "Python-2.0"
+ },
+ "node_modules/aria-hidden": {
+ "version": "1.2.6",
+ "resolved": "https://registry.npmjs.org/aria-hidden/-/aria-hidden-1.2.6.tgz",
+ "integrity": "sha512-ik3ZgC9dY/lYVVM++OISsaYDeg1tb0VtP5uL3ouh1koGOaUMDPpbFIei4JkFimWUFPn90sbMNMXQAIVOlnYKJA==",
+ "license": "MIT",
+ "dependencies": {
+ "tslib": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/aria-query": {
+ "version": "5.3.2",
+ "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.2.tgz",
+ "integrity": "sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/array-buffer-byte-length": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.2.tgz",
+ "integrity": "sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.3",
+ "is-array-buffer": "^3.0.5"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/array-includes": {
+ "version": "3.1.9",
+ "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.9.tgz",
+ "integrity": "sha512-FmeCCAenzH0KH381SPT5FZmiA/TmpndpcaShhfgEN9eCVjnFBqq3l1xrI42y8+PPLI6hypzou4GXw00WHmPBLQ==",
+ "dev": true,
+ "license": "MIT",
+ "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"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/array.prototype.findlast": {
+ "version": "1.2.5",
+ "resolved": "https://registry.npmjs.org/array.prototype.findlast/-/array.prototype.findlast-1.2.5.tgz",
+ "integrity": "sha512-CVvd6FHg1Z3POpBLxO6E6zr+rSKEQ9L6rZHAaY7lLfhKsWYUBBOuMs0e9o24oopj6H+geRCX0YJ+TJLBK2eHyQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.7",
+ "define-properties": "^1.2.1",
+ "es-abstract": "^1.23.2",
+ "es-errors": "^1.3.0",
+ "es-object-atoms": "^1.0.0",
+ "es-shim-unscopables": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/array.prototype.findlastindex": {
+ "version": "1.2.6",
+ "resolved": "https://registry.npmjs.org/array.prototype.findlastindex/-/array.prototype.findlastindex-1.2.6.tgz",
+ "integrity": "sha512-F/TKATkzseUExPlfvmwQKGITM3DGTK+vkAsCZoDc5daVygbJBnjEUCbgkAvVFsgfXfX4YIqZ/27G3k3tdXrTxQ==",
+ "dev": true,
+ "license": "MIT",
+ "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"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/array.prototype.flat": {
+ "version": "1.3.3",
+ "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.3.tgz",
+ "integrity": "sha512-rwG/ja1neyLqCuGZ5YYrznA62D4mZXg0i1cIskIUKSiqF3Cje9/wXAls9B9s1Wa2fomMsIv8czB8jZcPmxCXFg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.8",
+ "define-properties": "^1.2.1",
+ "es-abstract": "^1.23.5",
+ "es-shim-unscopables": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/array.prototype.flatmap": {
+ "version": "1.3.3",
+ "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.3.tgz",
+ "integrity": "sha512-Y7Wt51eKJSyi80hFrJCePGGNo5ktJCslFuboqJsbf57CCPcm5zztluPlc4/aD8sWsKvlwatezpV4U1efk8kpjg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.8",
+ "define-properties": "^1.2.1",
+ "es-abstract": "^1.23.5",
+ "es-shim-unscopables": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/array.prototype.tosorted": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/array.prototype.tosorted/-/array.prototype.tosorted-1.1.4.tgz",
+ "integrity": "sha512-p6Fx8B7b7ZhL/gmUsAy0D15WhvDccw3mnGNbZpi3pmeJdxtWsj2jEaI4Y6oo3XiHfzuSgPwKc04MYt6KgvC/wA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.7",
+ "define-properties": "^1.2.1",
+ "es-abstract": "^1.23.3",
+ "es-errors": "^1.3.0",
+ "es-shim-unscopables": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/arraybuffer.prototype.slice": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.4.tgz",
+ "integrity": "sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ==",
+ "dev": true,
+ "license": "MIT",
+ "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"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/ast-types-flow": {
+ "version": "0.0.8",
+ "resolved": "https://registry.npmjs.org/ast-types-flow/-/ast-types-flow-0.0.8.tgz",
+ "integrity": "sha512-OH/2E5Fg20h2aPrbe+QL8JZQFko0YZaF+j4mnQ7BGhfavO7OpSLa8a0y9sBwomHdSbkhTS8TQNayBfnW5DwbvQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/async-function": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/async-function/-/async-function-1.0.0.tgz",
+ "integrity": "sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/autoprefixer": {
+ "version": "10.4.23",
+ "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.23.tgz",
+ "integrity": "sha512-YYTXSFulfwytnjAPlw8QHncHJmlvFKtczb8InXaAx9Q0LbfDnfEYDE55omerIJKihhmU61Ft+cAOSzQVaBUmeA==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/postcss/"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/autoprefixer"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "browserslist": "^4.28.1",
+ "caniuse-lite": "^1.0.30001760",
+ "fraction.js": "^5.3.4",
+ "picocolors": "^1.1.1",
+ "postcss-value-parser": "^4.2.0"
+ },
+ "bin": {
+ "autoprefixer": "bin/autoprefixer"
+ },
+ "engines": {
+ "node": "^10 || ^12 || >=14"
+ },
+ "peerDependencies": {
+ "postcss": "^8.1.0"
+ }
+ },
+ "node_modules/available-typed-arrays": {
+ "version": "1.0.7",
+ "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz",
+ "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "possible-typed-array-names": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/axe-core": {
+ "version": "4.11.0",
+ "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.11.0.tgz",
+ "integrity": "sha512-ilYanEU8vxxBexpJd8cWM4ElSQq4QctCLKih0TSfjIfCQTeyH/6zVrmIJfLPrKTKJRbiG+cfnZbQIjAlJmF1jQ==",
+ "dev": true,
+ "license": "MPL-2.0",
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/axobject-query": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-4.1.0.tgz",
+ "integrity": "sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/balanced-match": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
+ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/baseline-browser-mapping": {
+ "version": "2.9.11",
+ "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.9.11.tgz",
+ "integrity": "sha512-Sg0xJUNDU1sJNGdfGWhVHX0kkZ+HWcvmVymJbj6NSgZZmW/8S9Y2HQ5euytnIgakgxN6papOAWiwDo1ctFDcoQ==",
+ "license": "Apache-2.0",
+ "bin": {
+ "baseline-browser-mapping": "dist/cli.js"
+ }
+ },
+ "node_modules/brace-expansion": {
+ "version": "1.1.12",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz",
+ "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "balanced-match": "^1.0.0",
+ "concat-map": "0.0.1"
+ }
+ },
+ "node_modules/braces": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz",
+ "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "fill-range": "^7.1.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/browserslist": {
+ "version": "4.28.1",
+ "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.1.tgz",
+ "integrity": "sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/browserslist"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/browserslist"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "baseline-browser-mapping": "^2.9.0",
+ "caniuse-lite": "^1.0.30001759",
+ "electron-to-chromium": "^1.5.263",
+ "node-releases": "^2.0.27",
+ "update-browserslist-db": "^1.2.0"
+ },
+ "bin": {
+ "browserslist": "cli.js"
+ },
+ "engines": {
+ "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7"
+ }
+ },
+ "node_modules/call-bind": {
+ "version": "1.0.8",
+ "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz",
+ "integrity": "sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==",
+ "dev": true,
+ "license": "MIT",
+ "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"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/call-bind-apply-helpers": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz",
+ "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0",
+ "function-bind": "^1.1.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/call-bound": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz",
+ "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind-apply-helpers": "^1.0.2",
+ "get-intrinsic": "^1.3.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/callsites": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
+ "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/caniuse-lite": {
+ "version": "1.0.30001761",
+ "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001761.tgz",
+ "integrity": "sha512-JF9ptu1vP2coz98+5051jZ4PwQgd2ni8A+gYSN7EA7dPKIMf0pDlSUxhdmVOaV3/fYK5uWBkgSXJaRLr4+3A6g==",
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/browserslist"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/caniuse-lite"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "CC-BY-4.0"
+ },
+ "node_modules/chalk": {
+ "version": "4.1.2",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+ "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-styles": "^4.1.0",
+ "supports-color": "^7.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/chalk?sponsor=1"
+ }
+ },
+ "node_modules/class-variance-authority": {
+ "version": "0.7.1",
+ "resolved": "https://registry.npmjs.org/class-variance-authority/-/class-variance-authority-0.7.1.tgz",
+ "integrity": "sha512-Ka+9Trutv7G8M6WT6SeiRWz792K5qEqIGEGzXKhAE6xOWAY6pPH8U+9IY3oCMv6kqTmLsv7Xh/2w2RigkePMsg==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "clsx": "^2.1.1"
+ },
+ "funding": {
+ "url": "https://polar.sh/cva"
+ }
+ },
+ "node_modules/client-only": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz",
+ "integrity": "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==",
+ "license": "MIT"
+ },
+ "node_modules/clsx": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz",
+ "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/cmdk": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/cmdk/-/cmdk-1.1.1.tgz",
+ "integrity": "sha512-Vsv7kFaXm+ptHDMZ7izaRsP70GgrW9NBNGswt9OZaVBLlE0SNpDq8eu/VGXyF9r7M0azK3Wy7OlYXsuyYLFzHg==",
+ "license": "MIT",
+ "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"
+ }
+ },
+ "node_modules/color-convert": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+ "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "color-name": "~1.1.4"
+ },
+ "engines": {
+ "node": ">=7.0.0"
+ }
+ },
+ "node_modules/color-name": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/concat-map": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
+ "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/convert-source-map": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz",
+ "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/cross-spawn": {
+ "version": "7.0.6",
+ "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
+ "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "path-key": "^3.1.0",
+ "shebang-command": "^2.0.0",
+ "which": "^2.0.1"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/csstype": {
+ "version": "3.2.3",
+ "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz",
+ "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==",
+ "devOptional": true,
+ "license": "MIT"
+ },
+ "node_modules/damerau-levenshtein": {
+ "version": "1.0.8",
+ "resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz",
+ "integrity": "sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==",
+ "dev": true,
+ "license": "BSD-2-Clause"
+ },
+ "node_modules/data-view-buffer": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.2.tgz",
+ "integrity": "sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.3",
+ "es-errors": "^1.3.0",
+ "is-data-view": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/data-view-byte-length": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/data-view-byte-length/-/data-view-byte-length-1.0.2.tgz",
+ "integrity": "sha512-tuhGbE6CfTM9+5ANGf+oQb72Ky/0+s3xKUpHvShfiz2RxMFgFPjsXuRLBVMtvMs15awe45SRb83D6wH4ew6wlQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.3",
+ "es-errors": "^1.3.0",
+ "is-data-view": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/inspect-js"
+ }
+ },
+ "node_modules/data-view-byte-offset": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/data-view-byte-offset/-/data-view-byte-offset-1.0.1.tgz",
+ "integrity": "sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.2",
+ "es-errors": "^1.3.0",
+ "is-data-view": "^1.0.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/debug": {
+ "version": "4.4.3",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz",
+ "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ms": "^2.1.3"
+ },
+ "engines": {
+ "node": ">=6.0"
+ },
+ "peerDependenciesMeta": {
+ "supports-color": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/deep-is": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz",
+ "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/define-data-property": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz",
+ "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "es-define-property": "^1.0.0",
+ "es-errors": "^1.3.0",
+ "gopd": "^1.0.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/define-properties": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz",
+ "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "define-data-property": "^1.0.1",
+ "has-property-descriptors": "^1.0.0",
+ "object-keys": "^1.1.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/detect-libc": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz",
+ "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==",
+ "devOptional": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/detect-node-es": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/detect-node-es/-/detect-node-es-1.1.0.tgz",
+ "integrity": "sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==",
+ "license": "MIT"
+ },
+ "node_modules/doctrine": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz",
+ "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "esutils": "^2.0.2"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/dunder-proto": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz",
+ "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind-apply-helpers": "^1.0.1",
+ "es-errors": "^1.3.0",
+ "gopd": "^1.2.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/electron-to-chromium": {
+ "version": "1.5.267",
+ "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.267.tgz",
+ "integrity": "sha512-0Drusm6MVRXSOJpGbaSVgcQsuB4hEkMpHXaVstcPmhu5LIedxs1xNK/nIxmQIU/RPC0+1/o0AVZfBTkTNJOdUw==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/emoji-regex": {
+ "version": "9.2.2",
+ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz",
+ "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/enhanced-resolve": {
+ "version": "5.18.4",
+ "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.18.4.tgz",
+ "integrity": "sha512-LgQMM4WXU3QI+SYgEc2liRgznaD5ojbmY3sb8LxyguVkIg5FxdpTkvk72te2R38/TGKxH634oLxXRGY6d7AP+Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "graceful-fs": "^4.2.4",
+ "tapable": "^2.2.0"
+ },
+ "engines": {
+ "node": ">=10.13.0"
+ }
+ },
+ "node_modules/es-abstract": {
+ "version": "1.24.1",
+ "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.24.1.tgz",
+ "integrity": "sha512-zHXBLhP+QehSSbsS9Pt23Gg964240DPd6QCf8WpkqEXxQ7fhdZzYsocOr5u7apWonsS5EjZDmTF+/slGMyasvw==",
+ "dev": true,
+ "license": "MIT",
+ "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"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/es-define-property": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz",
+ "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/es-errors": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz",
+ "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/es-iterator-helpers": {
+ "version": "1.2.2",
+ "resolved": "https://registry.npmjs.org/es-iterator-helpers/-/es-iterator-helpers-1.2.2.tgz",
+ "integrity": "sha512-BrUQ0cPTB/IwXj23HtwHjS9n7O4h9FX94b4xc5zlTHxeLgTAdzYUDyy6KdExAl9lbN5rtfe44xpjpmj9grxs5w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.8",
+ "call-bound": "^1.0.4",
+ "define-properties": "^1.2.1",
+ "es-abstract": "^1.24.1",
+ "es-errors": "^1.3.0",
+ "es-set-tostringtag": "^2.1.0",
+ "function-bind": "^1.1.2",
+ "get-intrinsic": "^1.3.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",
+ "internal-slot": "^1.1.0",
+ "iterator.prototype": "^1.1.5",
+ "safe-array-concat": "^1.1.3"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/es-object-atoms": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz",
+ "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/es-set-tostringtag": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz",
+ "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0",
+ "get-intrinsic": "^1.2.6",
+ "has-tostringtag": "^1.0.2",
+ "hasown": "^2.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/es-shim-unscopables": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.1.0.tgz",
+ "integrity": "sha512-d9T8ucsEhh8Bi1woXCf+TIKDIROLG5WCkxg8geBCbvk22kzwC5G2OnXVMO6FUsvQlgUUXQ2itephWDLqDzbeCw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "hasown": "^2.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/es-to-primitive": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.3.0.tgz",
+ "integrity": "sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "is-callable": "^1.2.7",
+ "is-date-object": "^1.0.5",
+ "is-symbol": "^1.0.4"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/escalade": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz",
+ "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/escape-string-regexp": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
+ "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/eslint": {
+ "version": "9.39.2",
+ "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.39.2.tgz",
+ "integrity": "sha512-LEyamqS7W5HB3ujJyvi0HQK/dtVINZvd5mAAp9eT5S/ujByGjiZLCzPcHVzuXbpJDJF/cxwHlfceVUDZ2lnSTw==",
+ "dev": true,
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "@eslint-community/eslint-utils": "^4.8.0",
+ "@eslint-community/regexpp": "^4.12.1",
+ "@eslint/config-array": "^0.21.1",
+ "@eslint/config-helpers": "^0.4.2",
+ "@eslint/core": "^0.17.0",
+ "@eslint/eslintrc": "^3.3.1",
+ "@eslint/js": "9.39.2",
+ "@eslint/plugin-kit": "^0.4.1",
+ "@humanfs/node": "^0.16.6",
+ "@humanwhocodes/module-importer": "^1.0.1",
+ "@humanwhocodes/retry": "^0.4.2",
+ "@types/estree": "^1.0.6",
+ "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"
+ },
+ "bin": {
+ "eslint": "bin/eslint.js"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "url": "https://eslint.org/donate"
+ },
+ "peerDependencies": {
+ "jiti": "*"
+ },
+ "peerDependenciesMeta": {
+ "jiti": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/eslint-config-next": {
+ "version": "16.1.0",
+ "resolved": "https://registry.npmjs.org/eslint-config-next/-/eslint-config-next-16.1.0.tgz",
+ "integrity": "sha512-RlPb8E2uO/Ix/w3kizxz6+6ogw99WqtNzTG0ArRZ5NEkIYcsfRb8U0j7aTG7NjRvcrsak5QtUSuxGNN2UcA58g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@next/eslint-plugin-next": "16.1.0",
+ "eslint-import-resolver-node": "^0.3.6",
+ "eslint-import-resolver-typescript": "^3.5.2",
+ "eslint-plugin-import": "^2.32.0",
+ "eslint-plugin-jsx-a11y": "^6.10.0",
+ "eslint-plugin-react": "^7.37.0",
+ "eslint-plugin-react-hooks": "^7.0.0",
+ "globals": "16.4.0",
+ "typescript-eslint": "^8.46.0"
+ },
+ "peerDependencies": {
+ "eslint": ">=9.0.0",
+ "typescript": ">=3.3.1"
+ },
+ "peerDependenciesMeta": {
+ "typescript": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/eslint-config-next/node_modules/globals": {
+ "version": "16.4.0",
+ "resolved": "https://registry.npmjs.org/globals/-/globals-16.4.0.tgz",
+ "integrity": "sha512-ob/2LcVVaVGCYN+r14cnwnoDPUufjiYgSqRhiFD0Q1iI4Odora5RE8Iv1D24hAz5oMophRGkGz+yuvQmmUMnMw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/eslint-import-resolver-node": {
+ "version": "0.3.9",
+ "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.9.tgz",
+ "integrity": "sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "debug": "^3.2.7",
+ "is-core-module": "^2.13.0",
+ "resolve": "^1.22.4"
+ }
+ },
+ "node_modules/eslint-import-resolver-node/node_modules/debug": {
+ "version": "3.2.7",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz",
+ "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ms": "^2.1.1"
+ }
+ },
+ "node_modules/eslint-import-resolver-typescript": {
+ "version": "3.10.1",
+ "resolved": "https://registry.npmjs.org/eslint-import-resolver-typescript/-/eslint-import-resolver-typescript-3.10.1.tgz",
+ "integrity": "sha512-A1rHYb06zjMGAxdLSkN2fXPBwuSaQ0iO5M/hdyS0Ajj1VBaRp0sPD3dn1FhME3c/JluGFbwSxyCfqdSbtQLAHQ==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "@nolyfill/is-core-module": "1.0.39",
+ "debug": "^4.4.0",
+ "get-tsconfig": "^4.10.0",
+ "is-bun-module": "^2.0.0",
+ "stable-hash": "^0.0.5",
+ "tinyglobby": "^0.2.13",
+ "unrs-resolver": "^1.6.2"
+ },
+ "engines": {
+ "node": "^14.18.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint-import-resolver-typescript"
+ },
+ "peerDependencies": {
+ "eslint": "*",
+ "eslint-plugin-import": "*",
+ "eslint-plugin-import-x": "*"
+ },
+ "peerDependenciesMeta": {
+ "eslint-plugin-import": {
+ "optional": true
+ },
+ "eslint-plugin-import-x": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/eslint-module-utils": {
+ "version": "2.12.1",
+ "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.12.1.tgz",
+ "integrity": "sha512-L8jSWTze7K2mTg0vos/RuLRS5soomksDPoJLXIslC7c8Wmut3bx7CPpJijDcBZtxQ5lrbUdM+s0OlNbz0DCDNw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "debug": "^3.2.7"
+ },
+ "engines": {
+ "node": ">=4"
+ },
+ "peerDependenciesMeta": {
+ "eslint": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/eslint-module-utils/node_modules/debug": {
+ "version": "3.2.7",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz",
+ "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ms": "^2.1.1"
+ }
+ },
+ "node_modules/eslint-plugin-import": {
+ "version": "2.32.0",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.32.0.tgz",
+ "integrity": "sha512-whOE1HFo/qJDyX4SnXzP4N6zOWn79WhnCUY/iDR0mPfQZO8wcYE4JClzI2oZrhBnnMUCBCHZhO6VQyoBU95mZA==",
+ "dev": true,
+ "license": "MIT",
+ "peer": true,
+ "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"
+ },
+ "engines": {
+ "node": ">=4"
+ },
+ "peerDependencies": {
+ "eslint": "^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8 || ^9"
+ }
+ },
+ "node_modules/eslint-plugin-import/node_modules/debug": {
+ "version": "3.2.7",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz",
+ "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ms": "^2.1.1"
+ }
+ },
+ "node_modules/eslint-plugin-import/node_modules/semver": {
+ "version": "6.3.1",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
+ "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
+ "dev": true,
+ "license": "ISC",
+ "bin": {
+ "semver": "bin/semver.js"
+ }
+ },
+ "node_modules/eslint-plugin-jsx-a11y": {
+ "version": "6.10.2",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.10.2.tgz",
+ "integrity": "sha512-scB3nz4WmG75pV8+3eRUQOHZlNSUhFNq37xnpgRkCCELU3XMvXAxLk1eqWWyE22Ki4Q01Fnsw9BA3cJHDPgn2Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "aria-query": "^5.3.2",
+ "array-includes": "^3.1.8",
+ "array.prototype.flatmap": "^1.3.2",
+ "ast-types-flow": "^0.0.8",
+ "axe-core": "^4.10.0",
+ "axobject-query": "^4.1.0",
+ "damerau-levenshtein": "^1.0.8",
+ "emoji-regex": "^9.2.2",
+ "hasown": "^2.0.2",
+ "jsx-ast-utils": "^3.3.5",
+ "language-tags": "^1.0.9",
+ "minimatch": "^3.1.2",
+ "object.fromentries": "^2.0.8",
+ "safe-regex-test": "^1.0.3",
+ "string.prototype.includes": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=4.0"
+ },
+ "peerDependencies": {
+ "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9"
+ }
+ },
+ "node_modules/eslint-plugin-react": {
+ "version": "7.37.5",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.37.5.tgz",
+ "integrity": "sha512-Qteup0SqU15kdocexFNAJMvCJEfa2xUKNV4CC1xsVMrIIqEy3SQ/rqyxCWNzfrd3/ldy6HMlD2e0JDVpDg2qIA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "array-includes": "^3.1.8",
+ "array.prototype.findlast": "^1.2.5",
+ "array.prototype.flatmap": "^1.3.3",
+ "array.prototype.tosorted": "^1.1.4",
+ "doctrine": "^2.1.0",
+ "es-iterator-helpers": "^1.2.1",
+ "estraverse": "^5.3.0",
+ "hasown": "^2.0.2",
+ "jsx-ast-utils": "^2.4.1 || ^3.0.0",
+ "minimatch": "^3.1.2",
+ "object.entries": "^1.1.9",
+ "object.fromentries": "^2.0.8",
+ "object.values": "^1.2.1",
+ "prop-types": "^15.8.1",
+ "resolve": "^2.0.0-next.5",
+ "semver": "^6.3.1",
+ "string.prototype.matchall": "^4.0.12",
+ "string.prototype.repeat": "^1.0.0"
+ },
+ "engines": {
+ "node": ">=4"
+ },
+ "peerDependencies": {
+ "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9.7"
+ }
+ },
+ "node_modules/eslint-plugin-react-hooks": {
+ "version": "7.0.1",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-7.0.1.tgz",
+ "integrity": "sha512-O0d0m04evaNzEPoSW+59Mezf8Qt0InfgGIBJnpC0h3NH/WjUAR7BIKUfysC6todmtiZ/A0oUVS8Gce0WhBrHsA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/core": "^7.24.4",
+ "@babel/parser": "^7.24.4",
+ "hermes-parser": "^0.25.1",
+ "zod": "^3.25.0 || ^4.0.0",
+ "zod-validation-error": "^3.5.0 || ^4.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0"
+ }
+ },
+ "node_modules/eslint-plugin-react/node_modules/resolve": {
+ "version": "2.0.0-next.5",
+ "resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.5.tgz",
+ "integrity": "sha512-U7WjGVG9sH8tvjW5SmGbQuui75FiyjAX72HX15DwBBwF9dNiQZRQAg9nnPhYy+TUnE0+VcrttuvNI8oSxZcocA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "is-core-module": "^2.13.0",
+ "path-parse": "^1.0.7",
+ "supports-preserve-symlinks-flag": "^1.0.0"
+ },
+ "bin": {
+ "resolve": "bin/resolve"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/eslint-plugin-react/node_modules/semver": {
+ "version": "6.3.1",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
+ "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
+ "dev": true,
+ "license": "ISC",
+ "bin": {
+ "semver": "bin/semver.js"
+ }
+ },
+ "node_modules/eslint-scope": {
+ "version": "8.4.0",
+ "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.4.0.tgz",
+ "integrity": "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "esrecurse": "^4.3.0",
+ "estraverse": "^5.2.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/eslint-visitor-keys": {
+ "version": "4.2.1",
+ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz",
+ "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/espree": {
+ "version": "10.4.0",
+ "resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz",
+ "integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "acorn": "^8.15.0",
+ "acorn-jsx": "^5.3.2",
+ "eslint-visitor-keys": "^4.2.1"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/esquery": {
+ "version": "1.6.0",
+ "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz",
+ "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==",
+ "dev": true,
+ "license": "BSD-3-Clause",
+ "dependencies": {
+ "estraverse": "^5.1.0"
+ },
+ "engines": {
+ "node": ">=0.10"
+ }
+ },
+ "node_modules/esrecurse": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz",
+ "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "estraverse": "^5.2.0"
+ },
+ "engines": {
+ "node": ">=4.0"
+ }
+ },
+ "node_modules/estraverse": {
+ "version": "5.3.0",
+ "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz",
+ "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "engines": {
+ "node": ">=4.0"
+ }
+ },
+ "node_modules/esutils": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz",
+ "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/fast-deep-equal": {
+ "version": "3.1.3",
+ "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
+ "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/fast-glob": {
+ "version": "3.3.1",
+ "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.1.tgz",
+ "integrity": "sha512-kNFPyjhh5cKjrUltxs+wFx+ZkbRaxxmZ+X0ZU31SOsxCEtP9VPgtq2teZw1DebupL5GmDaNQ6yKMMVcM41iqDg==",
+ "dev": true,
+ "license": "MIT",
+ "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.4"
+ },
+ "engines": {
+ "node": ">=8.6.0"
+ }
+ },
+ "node_modules/fast-glob/node_modules/glob-parent": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
+ "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "is-glob": "^4.0.1"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/fast-json-stable-stringify": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz",
+ "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/fast-levenshtein": {
+ "version": "2.0.6",
+ "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz",
+ "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/fastq": {
+ "version": "1.19.1",
+ "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.1.tgz",
+ "integrity": "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "reusify": "^1.0.4"
+ }
+ },
+ "node_modules/file-entry-cache": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz",
+ "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "flat-cache": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=16.0.0"
+ }
+ },
+ "node_modules/fill-range": {
+ "version": "7.1.1",
+ "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz",
+ "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "to-regex-range": "^5.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/find-up": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz",
+ "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "locate-path": "^6.0.0",
+ "path-exists": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/flat-cache": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz",
+ "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "flatted": "^3.2.9",
+ "keyv": "^4.5.4"
+ },
+ "engines": {
+ "node": ">=16"
+ }
+ },
+ "node_modules/flatted": {
+ "version": "3.3.3",
+ "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz",
+ "integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/for-each": {
+ "version": "0.3.5",
+ "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.5.tgz",
+ "integrity": "sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "is-callable": "^1.2.7"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/fraction.js": {
+ "version": "5.3.4",
+ "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-5.3.4.tgz",
+ "integrity": "sha512-1X1NTtiJphryn/uLQz3whtY6jK3fTqoE3ohKs0tT+Ujr1W59oopxmoEh7Lu5p6vBaPbgoM0bzveAW4Qi5RyWDQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "*"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/rawify"
+ }
+ },
+ "node_modules/function-bind": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
+ "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
+ "dev": true,
+ "license": "MIT",
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/function.prototype.name": {
+ "version": "1.1.8",
+ "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.8.tgz",
+ "integrity": "sha512-e5iwyodOHhbMr/yNrc7fDYG4qlbIvI5gajyzPnb5TCwyhjApznQh1BMFou9b30SevY43gCJKXycoCBjMbsuW0Q==",
+ "dev": true,
+ "license": "MIT",
+ "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"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/functions-have-names": {
+ "version": "1.2.3",
+ "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz",
+ "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==",
+ "dev": true,
+ "license": "MIT",
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/generator-function": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/generator-function/-/generator-function-2.0.1.tgz",
+ "integrity": "sha512-SFdFmIJi+ybC0vjlHN0ZGVGHc3lgE0DxPAT0djjVg+kjOnSqclqmj0KQ7ykTOLP6YxoqOvuAODGdcHJn+43q3g==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/gensync": {
+ "version": "1.0.0-beta.2",
+ "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz",
+ "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/get-intrinsic": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz",
+ "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==",
+ "dev": true,
+ "license": "MIT",
+ "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"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/get-nonce": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/get-nonce/-/get-nonce-1.0.1.tgz",
+ "integrity": "sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/get-proto": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz",
+ "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "dunder-proto": "^1.0.1",
+ "es-object-atoms": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/get-symbol-description": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.1.0.tgz",
+ "integrity": "sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.3",
+ "es-errors": "^1.3.0",
+ "get-intrinsic": "^1.2.6"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/get-tsconfig": {
+ "version": "4.13.0",
+ "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.13.0.tgz",
+ "integrity": "sha512-1VKTZJCwBrvbd+Wn3AOgQP/2Av+TfTCOlE4AcRJE72W1ksZXbAx8PPBR9RzgTeSPzlPMHrbANMH3LbltH73wxQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "resolve-pkg-maps": "^1.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1"
+ }
+ },
+ "node_modules/glob-parent": {
+ "version": "6.0.2",
+ "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz",
+ "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "is-glob": "^4.0.3"
+ },
+ "engines": {
+ "node": ">=10.13.0"
+ }
+ },
+ "node_modules/globals": {
+ "version": "14.0.0",
+ "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz",
+ "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/globalthis": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.4.tgz",
+ "integrity": "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "define-properties": "^1.2.1",
+ "gopd": "^1.0.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/gopd": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz",
+ "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/graceful-fs": {
+ "version": "4.2.11",
+ "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz",
+ "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/has-bigints": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.1.0.tgz",
+ "integrity": "sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/has-flag": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+ "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/has-property-descriptors": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz",
+ "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "es-define-property": "^1.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/has-proto": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.2.0.tgz",
+ "integrity": "sha512-KIL7eQPfHQRC8+XluaIw7BHUwwqL19bQn4hzNgdr+1wXoU0KKj6rufu47lhY7KbJR2C6T6+PfyN0Ea7wkSS+qQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "dunder-proto": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/has-symbols": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz",
+ "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/has-tostringtag": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz",
+ "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "has-symbols": "^1.0.3"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/hasown": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz",
+ "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "function-bind": "^1.1.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/hermes-estree": {
+ "version": "0.25.1",
+ "resolved": "https://registry.npmjs.org/hermes-estree/-/hermes-estree-0.25.1.tgz",
+ "integrity": "sha512-0wUoCcLp+5Ev5pDW2OriHC2MJCbwLwuRx+gAqMTOkGKJJiBCLjtrvy4PWUGn6MIVefecRpzoOZ/UV6iGdOr+Cw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/hermes-parser": {
+ "version": "0.25.1",
+ "resolved": "https://registry.npmjs.org/hermes-parser/-/hermes-parser-0.25.1.tgz",
+ "integrity": "sha512-6pEjquH3rqaI6cYAXYPcz9MS4rY6R4ngRgrgfDshRptUZIc3lw0MCIJIGDj9++mfySOuPTHB4nrSW99BCvOPIA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "hermes-estree": "0.25.1"
+ }
+ },
+ "node_modules/ignore": {
+ "version": "5.3.2",
+ "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz",
+ "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 4"
+ }
+ },
+ "node_modules/import-fresh": {
+ "version": "3.3.1",
+ "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz",
+ "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "parent-module": "^1.0.0",
+ "resolve-from": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/imurmurhash": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz",
+ "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.8.19"
+ }
+ },
+ "node_modules/internal-slot": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.1.0.tgz",
+ "integrity": "sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0",
+ "hasown": "^2.0.2",
+ "side-channel": "^1.1.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/is-array-buffer": {
+ "version": "3.0.5",
+ "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.5.tgz",
+ "integrity": "sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.8",
+ "call-bound": "^1.0.3",
+ "get-intrinsic": "^1.2.6"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-async-function": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/is-async-function/-/is-async-function-2.1.1.tgz",
+ "integrity": "sha512-9dgM/cZBnNvjzaMYHVoxxfPj2QXt22Ev7SuuPrs+xav0ukGB0S6d4ydZdEiM48kLx5kDV+QBPrpVnFyefL8kkQ==",
+ "dev": true,
+ "license": "MIT",
+ "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"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-bigint": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.1.0.tgz",
+ "integrity": "sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "has-bigints": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-boolean-object": {
+ "version": "1.2.2",
+ "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.2.2.tgz",
+ "integrity": "sha512-wa56o2/ElJMYqjCjGkXri7it5FbebW5usLw/nPmCMs5DeZ7eziSYZhSmPRn0txqeW4LnAmQQU7FgqLpsEFKM4A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.3",
+ "has-tostringtag": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-bun-module": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/is-bun-module/-/is-bun-module-2.0.0.tgz",
+ "integrity": "sha512-gNCGbnnnnFAUGKeZ9PdbyeGYJqewpmc2aKHUEMO5nQPWU9lOmv7jcmQIv+qHD8fXW6W7qfuCwX4rY9LNRjXrkQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "semver": "^7.7.1"
+ }
+ },
+ "node_modules/is-callable": {
+ "version": "1.2.7",
+ "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz",
+ "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-core-module": {
+ "version": "2.16.1",
+ "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz",
+ "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "hasown": "^2.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-data-view": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/is-data-view/-/is-data-view-1.0.2.tgz",
+ "integrity": "sha512-RKtWF8pGmS87i2D6gqQu/l7EYRlVdfzemCJN/P3UOs//x1QE7mfhvzHIApBTRf7axvT6DMGwSwBXYCT0nfB9xw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.2",
+ "get-intrinsic": "^1.2.6",
+ "is-typed-array": "^1.1.13"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-date-object": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.1.0.tgz",
+ "integrity": "sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.2",
+ "has-tostringtag": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-extglob": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
+ "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/is-finalizationregistry": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/is-finalizationregistry/-/is-finalizationregistry-1.1.1.tgz",
+ "integrity": "sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.3"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-generator-function": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.1.2.tgz",
+ "integrity": "sha512-upqt1SkGkODW9tsGNG5mtXTXtECizwtS2kA161M+gJPc1xdb/Ax629af6YrTwcOeQHbewrPNlE5Dx7kzvXTizA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.4",
+ "generator-function": "^2.0.0",
+ "get-proto": "^1.0.1",
+ "has-tostringtag": "^1.0.2",
+ "safe-regex-test": "^1.1.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-glob": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
+ "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "is-extglob": "^2.1.1"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/is-map": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.3.tgz",
+ "integrity": "sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-negative-zero": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.3.tgz",
+ "integrity": "sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-number": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
+ "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.12.0"
+ }
+ },
+ "node_modules/is-number-object": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.1.1.tgz",
+ "integrity": "sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.3",
+ "has-tostringtag": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-regex": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.2.1.tgz",
+ "integrity": "sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.2",
+ "gopd": "^1.2.0",
+ "has-tostringtag": "^1.0.2",
+ "hasown": "^2.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-set": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.3.tgz",
+ "integrity": "sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-shared-array-buffer": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.4.tgz",
+ "integrity": "sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.3"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-string": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.1.1.tgz",
+ "integrity": "sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.3",
+ "has-tostringtag": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-symbol": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.1.1.tgz",
+ "integrity": "sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.2",
+ "has-symbols": "^1.1.0",
+ "safe-regex-test": "^1.1.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-typed-array": {
+ "version": "1.1.15",
+ "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.15.tgz",
+ "integrity": "sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "which-typed-array": "^1.1.16"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-weakmap": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.2.tgz",
+ "integrity": "sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-weakref": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.1.1.tgz",
+ "integrity": "sha512-6i9mGWSlqzNMEqpCp93KwRS1uUOodk2OJ6b+sq7ZPDSy2WuI5NFIxp/254TytR8ftefexkWn5xNiHUNpPOfSew==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.3"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-weakset": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.4.tgz",
+ "integrity": "sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.3",
+ "get-intrinsic": "^1.2.6"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/isarray": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz",
+ "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/isexe": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
+ "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/iterator.prototype": {
+ "version": "1.1.5",
+ "resolved": "https://registry.npmjs.org/iterator.prototype/-/iterator.prototype-1.1.5.tgz",
+ "integrity": "sha512-H0dkQoCa3b2VEeKQBOxFph+JAbcrQdE7KC0UkqwpLmv2EC4P41QXP+rqo9wYodACiG5/WM5s9oDApTU8utwj9g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "define-data-property": "^1.1.4",
+ "es-object-atoms": "^1.0.0",
+ "get-intrinsic": "^1.2.6",
+ "get-proto": "^1.0.0",
+ "has-symbols": "^1.1.0",
+ "set-function-name": "^2.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/jiti": {
+ "version": "2.6.1",
+ "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.6.1.tgz",
+ "integrity": "sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
+ "jiti": "lib/jiti-cli.mjs"
+ }
+ },
+ "node_modules/js-tokens": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
+ "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/js-yaml": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz",
+ "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "argparse": "^2.0.1"
+ },
+ "bin": {
+ "js-yaml": "bin/js-yaml.js"
+ }
+ },
+ "node_modules/jsesc": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz",
+ "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
+ "jsesc": "bin/jsesc"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/json-buffer": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz",
+ "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/json-schema-traverse": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
+ "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/json-stable-stringify-without-jsonify": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz",
+ "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/json5": {
+ "version": "2.2.3",
+ "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz",
+ "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
+ "json5": "lib/cli.js"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/jsx-ast-utils": {
+ "version": "3.3.5",
+ "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.5.tgz",
+ "integrity": "sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "array-includes": "^3.1.6",
+ "array.prototype.flat": "^1.3.1",
+ "object.assign": "^4.1.4",
+ "object.values": "^1.1.6"
+ },
+ "engines": {
+ "node": ">=4.0"
+ }
+ },
+ "node_modules/keyv": {
+ "version": "4.5.4",
+ "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz",
+ "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "json-buffer": "3.0.1"
+ }
+ },
+ "node_modules/language-subtag-registry": {
+ "version": "0.3.23",
+ "resolved": "https://registry.npmjs.org/language-subtag-registry/-/language-subtag-registry-0.3.23.tgz",
+ "integrity": "sha512-0K65Lea881pHotoGEa5gDlMxt3pctLi2RplBb7Ezh4rRdLEOtgi7n4EwK9lamnUCkKBqaeKRVebTq6BAxSkpXQ==",
+ "dev": true,
+ "license": "CC0-1.0"
+ },
+ "node_modules/language-tags": {
+ "version": "1.0.9",
+ "resolved": "https://registry.npmjs.org/language-tags/-/language-tags-1.0.9.tgz",
+ "integrity": "sha512-MbjN408fEndfiQXbFQ1vnd+1NoLDsnQW41410oQBXiyXDMYH5z505juWa4KUE1LqxRC7DgOgZDbKLxHIwm27hA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "language-subtag-registry": "^0.3.20"
+ },
+ "engines": {
+ "node": ">=0.10"
+ }
+ },
+ "node_modules/levn": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz",
+ "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "prelude-ls": "^1.2.1",
+ "type-check": "~0.4.0"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/lightningcss": {
+ "version": "1.30.2",
+ "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.30.2.tgz",
+ "integrity": "sha512-utfs7Pr5uJyyvDETitgsaqSyjCb2qNRAtuqUeWIAKztsOYdcACf2KtARYXg2pSvhkt+9NfoaNY7fxjl6nuMjIQ==",
+ "dev": true,
+ "license": "MPL-2.0",
+ "dependencies": {
+ "detect-libc": "^2.0.3"
+ },
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ },
+ "optionalDependencies": {
+ "lightningcss-android-arm64": "1.30.2",
+ "lightningcss-darwin-arm64": "1.30.2",
+ "lightningcss-darwin-x64": "1.30.2",
+ "lightningcss-freebsd-x64": "1.30.2",
+ "lightningcss-linux-arm-gnueabihf": "1.30.2",
+ "lightningcss-linux-arm64-gnu": "1.30.2",
+ "lightningcss-linux-arm64-musl": "1.30.2",
+ "lightningcss-linux-x64-gnu": "1.30.2",
+ "lightningcss-linux-x64-musl": "1.30.2",
+ "lightningcss-win32-arm64-msvc": "1.30.2",
+ "lightningcss-win32-x64-msvc": "1.30.2"
+ }
+ },
+ "node_modules/lightningcss-android-arm64": {
+ "version": "1.30.2",
+ "resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.30.2.tgz",
+ "integrity": "sha512-BH9sEdOCahSgmkVhBLeU7Hc9DWeZ1Eb6wNS6Da8igvUwAe0sqROHddIlvU06q3WyXVEOYDZ6ykBZQnjTbmo4+A==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lightningcss-darwin-arm64": {
+ "version": "1.30.2",
+ "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.30.2.tgz",
+ "integrity": "sha512-ylTcDJBN3Hp21TdhRT5zBOIi73P6/W0qwvlFEk22fkdXchtNTOU4Qc37SkzV+EKYxLouZ6M4LG9NfZ1qkhhBWA==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lightningcss-darwin-x64": {
+ "version": "1.30.2",
+ "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.30.2.tgz",
+ "integrity": "sha512-oBZgKchomuDYxr7ilwLcyms6BCyLn0z8J0+ZZmfpjwg9fRVZIR5/GMXd7r9RH94iDhld3UmSjBM6nXWM2TfZTQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lightningcss-freebsd-x64": {
+ "version": "1.30.2",
+ "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.30.2.tgz",
+ "integrity": "sha512-c2bH6xTrf4BDpK8MoGG4Bd6zAMZDAXS569UxCAGcA7IKbHNMlhGQ89eRmvpIUGfKWNVdbhSbkQaWhEoMGmGslA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lightningcss-linux-arm-gnueabihf": {
+ "version": "1.30.2",
+ "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.30.2.tgz",
+ "integrity": "sha512-eVdpxh4wYcm0PofJIZVuYuLiqBIakQ9uFZmipf6LF/HRj5Bgm0eb3qL/mr1smyXIS1twwOxNWndd8z0E374hiA==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lightningcss-linux-arm64-gnu": {
+ "version": "1.30.2",
+ "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.30.2.tgz",
+ "integrity": "sha512-UK65WJAbwIJbiBFXpxrbTNArtfuznvxAJw4Q2ZGlU8kPeDIWEX1dg3rn2veBVUylA2Ezg89ktszWbaQnxD/e3A==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lightningcss-linux-arm64-musl": {
+ "version": "1.30.2",
+ "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.30.2.tgz",
+ "integrity": "sha512-5Vh9dGeblpTxWHpOx8iauV02popZDsCYMPIgiuw97OJ5uaDsL86cnqSFs5LZkG3ghHoX5isLgWzMs+eD1YzrnA==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lightningcss-linux-x64-gnu": {
+ "version": "1.30.2",
+ "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.30.2.tgz",
+ "integrity": "sha512-Cfd46gdmj1vQ+lR6VRTTadNHu6ALuw2pKR9lYq4FnhvgBc4zWY1EtZcAc6EffShbb1MFrIPfLDXD6Xprbnni4w==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lightningcss-linux-x64-musl": {
+ "version": "1.30.2",
+ "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.30.2.tgz",
+ "integrity": "sha512-XJaLUUFXb6/QG2lGIW6aIk6jKdtjtcffUT0NKvIqhSBY3hh9Ch+1LCeH80dR9q9LBjG3ewbDjnumefsLsP6aiA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lightningcss-win32-arm64-msvc": {
+ "version": "1.30.2",
+ "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.30.2.tgz",
+ "integrity": "sha512-FZn+vaj7zLv//D/192WFFVA0RgHawIcHqLX9xuWiQt7P0PtdFEVaxgF9rjM/IRYHQXNnk61/H/gb2Ei+kUQ4xQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lightningcss-win32-x64-msvc": {
+ "version": "1.30.2",
+ "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.30.2.tgz",
+ "integrity": "sha512-5g1yc73p+iAkid5phb4oVFMB45417DkRevRbt/El/gKXJk4jid+vPFF/AXbxn05Aky8PapwzZrdJShv5C0avjw==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/locate-path": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz",
+ "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "p-locate": "^5.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/lodash.merge": {
+ "version": "4.6.2",
+ "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
+ "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/loose-envify": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
+ "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "js-tokens": "^3.0.0 || ^4.0.0"
+ },
+ "bin": {
+ "loose-envify": "cli.js"
+ }
+ },
+ "node_modules/lru-cache": {
+ "version": "5.1.1",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz",
+ "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "yallist": "^3.0.2"
+ }
+ },
+ "node_modules/lucide-react": {
+ "version": "0.562.0",
+ "resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.562.0.tgz",
+ "integrity": "sha512-82hOAu7y0dbVuFfmO4bYF1XEwYk/mEbM5E+b1jgci/udUBEE/R7LF5Ip0CCEmXe8AybRM8L+04eP+LGZeDvkiw==",
+ "license": "ISC",
+ "peerDependencies": {
+ "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0"
+ }
+ },
+ "node_modules/magic-string": {
+ "version": "0.30.21",
+ "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz",
+ "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@jridgewell/sourcemap-codec": "^1.5.5"
+ }
+ },
+ "node_modules/math-intrinsics": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
+ "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/merge2": {
+ "version": "1.4.1",
+ "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz",
+ "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/micromatch": {
+ "version": "4.0.8",
+ "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz",
+ "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "braces": "^3.0.3",
+ "picomatch": "^2.3.1"
+ },
+ "engines": {
+ "node": ">=8.6"
+ }
+ },
+ "node_modules/minimatch": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
+ "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "brace-expansion": "^1.1.7"
+ },
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/minimist": {
+ "version": "1.2.8",
+ "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz",
+ "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==",
+ "dev": true,
+ "license": "MIT",
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/ms": {
+ "version": "2.1.3",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
+ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/nanoid": {
+ "version": "3.3.11",
+ "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz",
+ "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "MIT",
+ "bin": {
+ "nanoid": "bin/nanoid.cjs"
+ },
+ "engines": {
+ "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
+ }
+ },
+ "node_modules/napi-postinstall": {
+ "version": "0.3.4",
+ "resolved": "https://registry.npmjs.org/napi-postinstall/-/napi-postinstall-0.3.4.tgz",
+ "integrity": "sha512-PHI5f1O0EP5xJ9gQmFGMS6IZcrVvTjpXjz7Na41gTE7eE2hK11lg04CECCYEEjdc17EV4DO+fkGEtt7TpTaTiQ==",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
+ "napi-postinstall": "lib/cli.js"
+ },
+ "engines": {
+ "node": "^12.20.0 || ^14.18.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/napi-postinstall"
+ }
+ },
+ "node_modules/natural-compare": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz",
+ "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/next": {
+ "version": "16.1.0",
+ "resolved": "https://registry.npmjs.org/next/-/next-16.1.0.tgz",
+ "integrity": "sha512-Y+KbmDbefYtHDDQKLNrmzE/YYzG2msqo2VXhzh5yrJ54tx/6TmGdkR5+kP9ma7i7LwZpZMfoY3m/AoPPPKxtVw==",
+ "license": "MIT",
+ "dependencies": {
+ "@next/env": "16.1.0",
+ "@swc/helpers": "0.5.15",
+ "baseline-browser-mapping": "^2.8.3",
+ "caniuse-lite": "^1.0.30001579",
+ "postcss": "8.4.31",
+ "styled-jsx": "5.1.6"
+ },
+ "bin": {
+ "next": "dist/bin/next"
+ },
+ "engines": {
+ "node": ">=20.9.0"
+ },
+ "optionalDependencies": {
+ "@next/swc-darwin-arm64": "16.1.0",
+ "@next/swc-darwin-x64": "16.1.0",
+ "@next/swc-linux-arm64-gnu": "16.1.0",
+ "@next/swc-linux-arm64-musl": "16.1.0",
+ "@next/swc-linux-x64-gnu": "16.1.0",
+ "@next/swc-linux-x64-musl": "16.1.0",
+ "@next/swc-win32-arm64-msvc": "16.1.0",
+ "@next/swc-win32-x64-msvc": "16.1.0",
+ "sharp": "^0.34.4"
+ },
+ "peerDependencies": {
+ "@opentelemetry/api": "^1.1.0",
+ "@playwright/test": "^1.51.1",
+ "babel-plugin-react-compiler": "*",
+ "react": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0",
+ "react-dom": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0",
+ "sass": "^1.3.0"
+ },
+ "peerDependenciesMeta": {
+ "@opentelemetry/api": {
+ "optional": true
+ },
+ "@playwright/test": {
+ "optional": true
+ },
+ "babel-plugin-react-compiler": {
+ "optional": true
+ },
+ "sass": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/next/node_modules/postcss": {
+ "version": "8.4.31",
+ "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz",
+ "integrity": "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==",
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/postcss/"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/postcss"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "nanoid": "^3.3.6",
+ "picocolors": "^1.0.0",
+ "source-map-js": "^1.0.2"
+ },
+ "engines": {
+ "node": "^10 || ^12 || >=14"
+ }
+ },
+ "node_modules/node-releases": {
+ "version": "2.0.27",
+ "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.27.tgz",
+ "integrity": "sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/object-assign": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
+ "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/object-inspect": {
+ "version": "1.13.4",
+ "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz",
+ "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/object-keys": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz",
+ "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/object.assign": {
+ "version": "4.1.7",
+ "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.7.tgz",
+ "integrity": "sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw==",
+ "dev": true,
+ "license": "MIT",
+ "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"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/object.entries": {
+ "version": "1.1.9",
+ "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.9.tgz",
+ "integrity": "sha512-8u/hfXFRBD1O0hPUjioLhoWFHRmt6tKA4/vZPyckBr18l1KE9uHrFaFaUi8MDRTpi4uak2goyPTSNJLXX2k2Hw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.8",
+ "call-bound": "^1.0.4",
+ "define-properties": "^1.2.1",
+ "es-object-atoms": "^1.1.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/object.fromentries": {
+ "version": "2.0.8",
+ "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.8.tgz",
+ "integrity": "sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.7",
+ "define-properties": "^1.2.1",
+ "es-abstract": "^1.23.2",
+ "es-object-atoms": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/object.groupby": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/object.groupby/-/object.groupby-1.0.3.tgz",
+ "integrity": "sha512-+Lhy3TQTuzXI5hevh8sBGqbmurHbbIjAi0Z4S63nthVLmLxfbj4T54a4CfZrXIrt9iP4mVAPYMo/v99taj3wjQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.7",
+ "define-properties": "^1.2.1",
+ "es-abstract": "^1.23.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/object.values": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.2.1.tgz",
+ "integrity": "sha512-gXah6aZrcUxjWg2zR2MwouP2eHlCBzdV4pygudehaKXSGW4v2AsRQUK+lwwXhii6KFZcunEnmSUoYp5CXibxtA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.8",
+ "call-bound": "^1.0.3",
+ "define-properties": "^1.2.1",
+ "es-object-atoms": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/optionator": {
+ "version": "0.9.4",
+ "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz",
+ "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==",
+ "dev": true,
+ "license": "MIT",
+ "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"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/own-keys": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/own-keys/-/own-keys-1.0.1.tgz",
+ "integrity": "sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "get-intrinsic": "^1.2.6",
+ "object-keys": "^1.1.1",
+ "safe-push-apply": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/p-limit": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz",
+ "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "yocto-queue": "^0.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/p-locate": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz",
+ "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "p-limit": "^3.0.2"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/parent-module": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
+ "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "callsites": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/path-exists": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
+ "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/path-key": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
+ "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/path-parse": {
+ "version": "1.0.7",
+ "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
+ "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/picocolors": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
+ "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==",
+ "license": "ISC"
+ },
+ "node_modules/picomatch": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
+ "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8.6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/jonschlinkert"
+ }
+ },
+ "node_modules/possible-typed-array-names": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz",
+ "integrity": "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/postcss": {
+ "version": "8.5.6",
+ "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz",
+ "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/postcss/"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/postcss"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "nanoid": "^3.3.11",
+ "picocolors": "^1.1.1",
+ "source-map-js": "^1.2.1"
+ },
+ "engines": {
+ "node": "^10 || ^12 || >=14"
+ }
+ },
+ "node_modules/postcss-value-parser": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz",
+ "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/prelude-ls": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz",
+ "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/prop-types": {
+ "version": "15.8.1",
+ "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz",
+ "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "loose-envify": "^1.4.0",
+ "object-assign": "^4.1.1",
+ "react-is": "^16.13.1"
+ }
+ },
+ "node_modules/punycode": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz",
+ "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/queue-microtask": {
+ "version": "1.2.3",
+ "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz",
+ "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ],
+ "license": "MIT"
+ },
+ "node_modules/react": {
+ "version": "19.2.3",
+ "resolved": "https://registry.npmjs.org/react/-/react-19.2.3.tgz",
+ "integrity": "sha512-Ku/hhYbVjOQnXDZFv2+RibmLFGwFdeeKHFcOTlrt7xplBnya5OGn/hIRDsqDiSUcfORsDC7MPxwork8jBwsIWA==",
+ "license": "MIT",
+ "peer": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/react-dom": {
+ "version": "19.2.3",
+ "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.3.tgz",
+ "integrity": "sha512-yELu4WmLPw5Mr/lmeEpox5rw3RETacE++JgHqQzd2dg+YbJuat3jH4ingc+WPZhxaoFzdv9y33G+F7Nl5O0GBg==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "scheduler": "^0.27.0"
+ },
+ "peerDependencies": {
+ "react": "^19.2.3"
+ }
+ },
+ "node_modules/react-is": {
+ "version": "16.13.1",
+ "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
+ "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/react-remove-scroll": {
+ "version": "2.7.2",
+ "resolved": "https://registry.npmjs.org/react-remove-scroll/-/react-remove-scroll-2.7.2.tgz",
+ "integrity": "sha512-Iqb9NjCCTt6Hf+vOdNIZGdTiH1QSqr27H/Ek9sv/a97gfueI/5h1s3yRi1nngzMUaOOToin5dI1dXKdXiF+u0Q==",
+ "license": "MIT",
+ "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"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "peerDependencies": {
+ "@types/react": "*",
+ "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/react-remove-scroll-bar": {
+ "version": "2.3.8",
+ "resolved": "https://registry.npmjs.org/react-remove-scroll-bar/-/react-remove-scroll-bar-2.3.8.tgz",
+ "integrity": "sha512-9r+yi9+mgU33AKcj6IbT9oRCO78WriSj6t/cF8DWBZJ9aOGPOTEDvdUDz1FwKim7QXWwmHqtdHnRJfhAxEG46Q==",
+ "license": "MIT",
+ "dependencies": {
+ "react-style-singleton": "^2.2.2",
+ "tslib": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "peerDependencies": {
+ "@types/react": "*",
+ "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/react-style-singleton": {
+ "version": "2.2.3",
+ "resolved": "https://registry.npmjs.org/react-style-singleton/-/react-style-singleton-2.2.3.tgz",
+ "integrity": "sha512-b6jSvxvVnyptAiLjbkWLE/lOnR4lfTtDAl+eUC7RZy+QQWc6wRzIV2CE6xBuMmDxc2qIihtDCZD5NPOFl7fRBQ==",
+ "license": "MIT",
+ "dependencies": {
+ "get-nonce": "^1.0.0",
+ "tslib": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "peerDependencies": {
+ "@types/react": "*",
+ "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/reflect.getprototypeof": {
+ "version": "1.0.10",
+ "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.10.tgz",
+ "integrity": "sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw==",
+ "dev": true,
+ "license": "MIT",
+ "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"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/regexp.prototype.flags": {
+ "version": "1.5.4",
+ "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.4.tgz",
+ "integrity": "sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==",
+ "dev": true,
+ "license": "MIT",
+ "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"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/resolve": {
+ "version": "1.22.11",
+ "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.11.tgz",
+ "integrity": "sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "is-core-module": "^2.16.1",
+ "path-parse": "^1.0.7",
+ "supports-preserve-symlinks-flag": "^1.0.0"
+ },
+ "bin": {
+ "resolve": "bin/resolve"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/resolve-from": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz",
+ "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/resolve-pkg-maps": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz",
+ "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==",
+ "dev": true,
+ "license": "MIT",
+ "funding": {
+ "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1"
+ }
+ },
+ "node_modules/reusify": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz",
+ "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "iojs": ">=1.0.0",
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/run-parallel": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz",
+ "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "queue-microtask": "^1.2.2"
+ }
+ },
+ "node_modules/safe-array-concat": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.3.tgz",
+ "integrity": "sha512-AURm5f0jYEOydBj7VQlVvDrjeFgthDdEF5H1dP+6mNpoXOMo1quQqJ4wvJDyRZ9+pO3kGWoOdmV08cSv2aJV6Q==",
+ "dev": true,
+ "license": "MIT",
+ "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"
+ },
+ "engines": {
+ "node": ">=0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/safe-push-apply": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/safe-push-apply/-/safe-push-apply-1.0.0.tgz",
+ "integrity": "sha512-iKE9w/Z7xCzUMIZqdBsp6pEQvwuEebH4vdpjcDWnyzaI6yl6O9FHvVpmGelvEHNsoY6wGblkxR6Zty/h00WiSA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0",
+ "isarray": "^2.0.5"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/safe-regex-test": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.1.0.tgz",
+ "integrity": "sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.2",
+ "es-errors": "^1.3.0",
+ "is-regex": "^1.2.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/scheduler": {
+ "version": "0.27.0",
+ "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.27.0.tgz",
+ "integrity": "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==",
+ "license": "MIT"
+ },
+ "node_modules/semver": {
+ "version": "7.7.3",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz",
+ "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==",
+ "devOptional": true,
+ "license": "ISC",
+ "bin": {
+ "semver": "bin/semver.js"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/set-function-length": {
+ "version": "1.2.2",
+ "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz",
+ "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==",
+ "dev": true,
+ "license": "MIT",
+ "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"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/set-function-name": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.2.tgz",
+ "integrity": "sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "define-data-property": "^1.1.4",
+ "es-errors": "^1.3.0",
+ "functions-have-names": "^1.2.3",
+ "has-property-descriptors": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/set-proto": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/set-proto/-/set-proto-1.0.0.tgz",
+ "integrity": "sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "dunder-proto": "^1.0.1",
+ "es-errors": "^1.3.0",
+ "es-object-atoms": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/sharp": {
+ "version": "0.34.5",
+ "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.34.5.tgz",
+ "integrity": "sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg==",
+ "hasInstallScript": true,
+ "license": "Apache-2.0",
+ "optional": true,
+ "dependencies": {
+ "@img/colour": "^1.0.0",
+ "detect-libc": "^2.1.2",
+ "semver": "^7.7.3"
+ },
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-darwin-arm64": "0.34.5",
+ "@img/sharp-darwin-x64": "0.34.5",
+ "@img/sharp-libvips-darwin-arm64": "1.2.4",
+ "@img/sharp-libvips-darwin-x64": "1.2.4",
+ "@img/sharp-libvips-linux-arm": "1.2.4",
+ "@img/sharp-libvips-linux-arm64": "1.2.4",
+ "@img/sharp-libvips-linux-ppc64": "1.2.4",
+ "@img/sharp-libvips-linux-riscv64": "1.2.4",
+ "@img/sharp-libvips-linux-s390x": "1.2.4",
+ "@img/sharp-libvips-linux-x64": "1.2.4",
+ "@img/sharp-libvips-linuxmusl-arm64": "1.2.4",
+ "@img/sharp-libvips-linuxmusl-x64": "1.2.4",
+ "@img/sharp-linux-arm": "0.34.5",
+ "@img/sharp-linux-arm64": "0.34.5",
+ "@img/sharp-linux-ppc64": "0.34.5",
+ "@img/sharp-linux-riscv64": "0.34.5",
+ "@img/sharp-linux-s390x": "0.34.5",
+ "@img/sharp-linux-x64": "0.34.5",
+ "@img/sharp-linuxmusl-arm64": "0.34.5",
+ "@img/sharp-linuxmusl-x64": "0.34.5",
+ "@img/sharp-wasm32": "0.34.5",
+ "@img/sharp-win32-arm64": "0.34.5",
+ "@img/sharp-win32-ia32": "0.34.5",
+ "@img/sharp-win32-x64": "0.34.5"
+ }
+ },
+ "node_modules/shebang-command": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
+ "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "shebang-regex": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/shebang-regex": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz",
+ "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/side-channel": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz",
+ "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==",
+ "dev": true,
+ "license": "MIT",
+ "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"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/side-channel-list": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz",
+ "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0",
+ "object-inspect": "^1.13.3"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/side-channel-map": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz",
+ "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.2",
+ "es-errors": "^1.3.0",
+ "get-intrinsic": "^1.2.5",
+ "object-inspect": "^1.13.3"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/side-channel-weakmap": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz",
+ "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==",
+ "dev": true,
+ "license": "MIT",
+ "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"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/source-map-js": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
+ "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==",
+ "license": "BSD-3-Clause",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/stable-hash": {
+ "version": "0.0.5",
+ "resolved": "https://registry.npmjs.org/stable-hash/-/stable-hash-0.0.5.tgz",
+ "integrity": "sha512-+L3ccpzibovGXFK+Ap/f8LOS0ahMrHTf3xu7mMLSpEGU0EO9ucaysSylKo9eRDFNhWve/y275iPmIZ4z39a9iA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/stop-iteration-iterator": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/stop-iteration-iterator/-/stop-iteration-iterator-1.1.0.tgz",
+ "integrity": "sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0",
+ "internal-slot": "^1.1.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/string.prototype.includes": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/string.prototype.includes/-/string.prototype.includes-2.0.1.tgz",
+ "integrity": "sha512-o7+c9bW6zpAdJHTtujeePODAhkuicdAryFsfVKwA+wGw89wJ4GTY484WTucM9hLtDEOpOvI+aHnzqnC5lHp4Rg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.7",
+ "define-properties": "^1.2.1",
+ "es-abstract": "^1.23.3"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/string.prototype.matchall": {
+ "version": "4.0.12",
+ "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.12.tgz",
+ "integrity": "sha512-6CC9uyBL+/48dYizRf7H7VAYCMCNTBeM78x/VTUe9bFEaxBepPJDa1Ow99LqI/1yF7kuy7Q3cQsYMrcjGUcskA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.8",
+ "call-bound": "^1.0.3",
+ "define-properties": "^1.2.1",
+ "es-abstract": "^1.23.6",
+ "es-errors": "^1.3.0",
+ "es-object-atoms": "^1.0.0",
+ "get-intrinsic": "^1.2.6",
+ "gopd": "^1.2.0",
+ "has-symbols": "^1.1.0",
+ "internal-slot": "^1.1.0",
+ "regexp.prototype.flags": "^1.5.3",
+ "set-function-name": "^2.0.2",
+ "side-channel": "^1.1.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/string.prototype.repeat": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/string.prototype.repeat/-/string.prototype.repeat-1.0.0.tgz",
+ "integrity": "sha512-0u/TldDbKD8bFCQ/4f5+mNRrXwZ8hg2w7ZR8wa16e8z9XpePWl3eGEcUD0OXpEH/VJH/2G3gjUtR3ZOiBe2S/w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "define-properties": "^1.1.3",
+ "es-abstract": "^1.17.5"
+ }
+ },
+ "node_modules/string.prototype.trim": {
+ "version": "1.2.10",
+ "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.10.tgz",
+ "integrity": "sha512-Rs66F0P/1kedk5lyYyH9uBzuiI/kNRmwJAR9quK6VOtIpZ2G+hMZd+HQbbv25MgCA6gEffoMZYxlTod4WcdrKA==",
+ "dev": true,
+ "license": "MIT",
+ "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"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/string.prototype.trimend": {
+ "version": "1.0.9",
+ "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.9.tgz",
+ "integrity": "sha512-G7Ok5C6E/j4SGfyLCloXTrngQIQU3PWtXGst3yM7Bea9FRURf1S42ZHlZZtsNque2FN2PoUhfZXYLNWwEr4dLQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.8",
+ "call-bound": "^1.0.2",
+ "define-properties": "^1.2.1",
+ "es-object-atoms": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/string.prototype.trimstart": {
+ "version": "1.0.8",
+ "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.8.tgz",
+ "integrity": "sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.7",
+ "define-properties": "^1.2.1",
+ "es-object-atoms": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/strip-bom": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz",
+ "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/strip-json-comments": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz",
+ "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/styled-jsx": {
+ "version": "5.1.6",
+ "resolved": "https://registry.npmjs.org/styled-jsx/-/styled-jsx-5.1.6.tgz",
+ "integrity": "sha512-qSVyDTeMotdvQYoHWLNGwRFJHC+i+ZvdBRYosOFgC+Wg1vx4frN2/RG/NA7SYqqvKNLf39P2LSRA2pu6n0XYZA==",
+ "license": "MIT",
+ "dependencies": {
+ "client-only": "0.0.1"
+ },
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "peerDependencies": {
+ "react": ">= 16.8.0 || 17.x.x || ^18.0.0-0 || ^19.0.0-0"
+ },
+ "peerDependenciesMeta": {
+ "@babel/core": {
+ "optional": true
+ },
+ "babel-plugin-macros": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/supports-color": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+ "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "has-flag": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/supports-preserve-symlinks-flag": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz",
+ "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/tailwind-merge": {
+ "version": "3.4.0",
+ "resolved": "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-3.4.0.tgz",
+ "integrity": "sha512-uSaO4gnW+b3Y2aWoWfFpX62vn2sR3skfhbjsEnaBI81WD1wBLlHZe5sWf0AqjksNdYTbGBEd0UasQMT3SNV15g==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/dcastil"
+ }
+ },
+ "node_modules/tailwindcss": {
+ "version": "4.1.18",
+ "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.18.tgz",
+ "integrity": "sha512-4+Z+0yiYyEtUVCScyfHCxOYP06L5Ne+JiHhY2IjR2KWMIWhJOYZKLSGZaP5HkZ8+bY0cxfzwDE5uOmzFXyIwxw==",
+ "license": "MIT",
+ "peer": true
+ },
+ "node_modules/tailwindcss-animate": {
+ "version": "1.0.7",
+ "resolved": "https://registry.npmjs.org/tailwindcss-animate/-/tailwindcss-animate-1.0.7.tgz",
+ "integrity": "sha512-bl6mpH3T7I3UFxuvDEXLxy/VuFxBk5bbzplh7tXI68mwMokNYd1t9qPBHlnyTwfa4JGC4zP516I1hYYtQ/vspA==",
+ "license": "MIT",
+ "peerDependencies": {
+ "tailwindcss": ">=3.0.0 || insiders"
+ }
+ },
+ "node_modules/tapable": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.3.0.tgz",
+ "integrity": "sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/webpack"
+ }
+ },
+ "node_modules/tinyglobby": {
+ "version": "0.2.15",
+ "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz",
+ "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "fdir": "^6.5.0",
+ "picomatch": "^4.0.3"
+ },
+ "engines": {
+ "node": ">=12.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/SuperchupuDev"
+ }
+ },
+ "node_modules/tinyglobby/node_modules/fdir": {
+ "version": "6.5.0",
+ "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz",
+ "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12.0.0"
+ },
+ "peerDependencies": {
+ "picomatch": "^3 || ^4"
+ },
+ "peerDependenciesMeta": {
+ "picomatch": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/tinyglobby/node_modules/picomatch": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz",
+ "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
+ "dev": true,
+ "license": "MIT",
+ "peer": true,
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/jonschlinkert"
+ }
+ },
+ "node_modules/to-regex-range": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
+ "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "is-number": "^7.0.0"
+ },
+ "engines": {
+ "node": ">=8.0"
+ }
+ },
+ "node_modules/ts-api-utils": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.1.0.tgz",
+ "integrity": "sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=18.12"
+ },
+ "peerDependencies": {
+ "typescript": ">=4.8.4"
+ }
+ },
+ "node_modules/tsconfig-paths": {
+ "version": "3.15.0",
+ "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz",
+ "integrity": "sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/json5": "^0.0.29",
+ "json5": "^1.0.2",
+ "minimist": "^1.2.6",
+ "strip-bom": "^3.0.0"
+ }
+ },
+ "node_modules/tsconfig-paths/node_modules/json5": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz",
+ "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "minimist": "^1.2.0"
+ },
+ "bin": {
+ "json5": "lib/cli.js"
+ }
+ },
+ "node_modules/tslib": {
+ "version": "2.8.1",
+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
+ "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
+ "license": "0BSD"
+ },
+ "node_modules/type-check": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz",
+ "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "prelude-ls": "^1.2.1"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/typed-array-buffer": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.3.tgz",
+ "integrity": "sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.3",
+ "es-errors": "^1.3.0",
+ "is-typed-array": "^1.1.14"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/typed-array-byte-length": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.3.tgz",
+ "integrity": "sha512-BaXgOuIxz8n8pIq3e7Atg/7s+DpiYrxn4vdot3w9KbnBhcRQq6o3xemQdIfynqSeXeDrF32x+WvfzmOjPiY9lg==",
+ "dev": true,
+ "license": "MIT",
+ "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"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/typed-array-byte-offset": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.4.tgz",
+ "integrity": "sha512-bTlAFB/FBYMcuX81gbL4OcpH5PmlFHqlCCpAl8AlEzMz5k53oNDvN8p1PNOWLEmI2x4orp3raOFB51tv9X+MFQ==",
+ "dev": true,
+ "license": "MIT",
+ "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"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/typed-array-length": {
+ "version": "1.0.7",
+ "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.7.tgz",
+ "integrity": "sha512-3KS2b+kL7fsuk/eJZ7EQdnEmQoaho/r6KUef7hxvltNA5DR8NAUM+8wJMbJyZ4G9/7i3v5zPBIMN5aybAh2/Jg==",
+ "dev": true,
+ "license": "MIT",
+ "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"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/typescript": {
+ "version": "5.9.3",
+ "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz",
+ "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "peer": true,
+ "bin": {
+ "tsc": "bin/tsc",
+ "tsserver": "bin/tsserver"
+ },
+ "engines": {
+ "node": ">=14.17"
+ }
+ },
+ "node_modules/typescript-eslint": {
+ "version": "8.50.0",
+ "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.50.0.tgz",
+ "integrity": "sha512-Q1/6yNUmCpH94fbgMUMg2/BSAr/6U7GBk61kZTv1/asghQOWOjTlp9K8mixS5NcJmm2creY+UFfGeW/+OcA64A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@typescript-eslint/eslint-plugin": "8.50.0",
+ "@typescript-eslint/parser": "8.50.0",
+ "@typescript-eslint/typescript-estree": "8.50.0",
+ "@typescript-eslint/utils": "8.50.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "eslint": "^8.57.0 || ^9.0.0",
+ "typescript": ">=4.8.4 <6.0.0"
+ }
+ },
+ "node_modules/unbox-primitive": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.1.0.tgz",
+ "integrity": "sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.3",
+ "has-bigints": "^1.0.2",
+ "has-symbols": "^1.1.0",
+ "which-boxed-primitive": "^1.1.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/undici-types": {
+ "version": "7.16.0",
+ "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz",
+ "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/unrs-resolver": {
+ "version": "1.11.1",
+ "resolved": "https://registry.npmjs.org/unrs-resolver/-/unrs-resolver-1.11.1.tgz",
+ "integrity": "sha512-bSjt9pjaEBnNiGgc9rUiHGKv5l4/TGzDmYw3RhnkJGtLhbnnA/5qJj7x3dNDCRx/PJxu774LlH8lCOlB4hEfKg==",
+ "dev": true,
+ "hasInstallScript": true,
+ "license": "MIT",
+ "dependencies": {
+ "napi-postinstall": "^0.3.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/unrs-resolver"
+ },
+ "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"
+ }
+ },
+ "node_modules/update-browserslist-db": {
+ "version": "1.2.3",
+ "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz",
+ "integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/browserslist"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/browserslist"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "escalade": "^3.2.0",
+ "picocolors": "^1.1.1"
+ },
+ "bin": {
+ "update-browserslist-db": "cli.js"
+ },
+ "peerDependencies": {
+ "browserslist": ">= 4.21.0"
+ }
+ },
+ "node_modules/uri-js": {
+ "version": "4.4.1",
+ "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz",
+ "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "punycode": "^2.1.0"
+ }
+ },
+ "node_modules/use-callback-ref": {
+ "version": "1.3.3",
+ "resolved": "https://registry.npmjs.org/use-callback-ref/-/use-callback-ref-1.3.3.tgz",
+ "integrity": "sha512-jQL3lRnocaFtu3V00JToYz/4QkNWswxijDaCVNZRiRTO3HQDLsdu1ZtmIUvV4yPp+rvWm5j0y0TG/S61cuijTg==",
+ "license": "MIT",
+ "dependencies": {
+ "tslib": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "peerDependencies": {
+ "@types/react": "*",
+ "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/use-sidecar": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/use-sidecar/-/use-sidecar-1.1.3.tgz",
+ "integrity": "sha512-Fedw0aZvkhynoPYlA5WXrMCAMm+nSWdZt6lzJQ7Ok8S6Q+VsHmHpRWndVRJ8Be0ZbkfPc5LRYH+5XrzXcEeLRQ==",
+ "license": "MIT",
+ "dependencies": {
+ "detect-node-es": "^1.1.0",
+ "tslib": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "peerDependencies": {
+ "@types/react": "*",
+ "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/which": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
+ "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "isexe": "^2.0.0"
+ },
+ "bin": {
+ "node-which": "bin/node-which"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/which-boxed-primitive": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.1.1.tgz",
+ "integrity": "sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==",
+ "dev": true,
+ "license": "MIT",
+ "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"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/which-builtin-type": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/which-builtin-type/-/which-builtin-type-1.2.1.tgz",
+ "integrity": "sha512-6iBczoX+kDQ7a3+YJBnh3T+KZRxM/iYNPXicqk66/Qfm1b93iu+yOImkg0zHbj5LNOcNv1TEADiZ0xa34B4q6Q==",
+ "dev": true,
+ "license": "MIT",
+ "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"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/which-collection": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/which-collection/-/which-collection-1.0.2.tgz",
+ "integrity": "sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "is-map": "^2.0.3",
+ "is-set": "^2.0.3",
+ "is-weakmap": "^2.0.2",
+ "is-weakset": "^2.0.3"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/which-typed-array": {
+ "version": "1.1.19",
+ "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.19.tgz",
+ "integrity": "sha512-rEvr90Bck4WZt9HHFC4DJMsjvu7x+r6bImz0/BrbWb7A2djJ8hnZMrWnHo9F8ssv0OMErasDhftrfROTyqSDrw==",
+ "dev": true,
+ "license": "MIT",
+ "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"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/word-wrap": {
+ "version": "1.2.5",
+ "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz",
+ "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/yallist": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz",
+ "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/yocto-queue": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz",
+ "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/zod": {
+ "version": "4.2.1",
+ "resolved": "https://registry.npmjs.org/zod/-/zod-4.2.1.tgz",
+ "integrity": "sha512-0wZ1IRqGGhMP76gLqz8EyfBXKk0J2qo2+H3fi4mcUP/KtTocoX08nmIAHl1Z2kJIZbZee8KOpBCSNPRgauucjw==",
+ "dev": true,
+ "license": "MIT",
+ "peer": true,
+ "funding": {
+ "url": "https://github.com/sponsors/colinhacks"
+ }
+ },
+ "node_modules/zod-validation-error": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/zod-validation-error/-/zod-validation-error-4.0.2.tgz",
+ "integrity": "sha512-Q6/nZLe6jxuU80qb/4uJ4t5v2VEZ44lzQjPDhYJNztRQ4wyWc6VF3D3Kb/fAuPetZQnhS3hnajCf9CsWesghLQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=18.0.0"
+ },
+ "peerDependencies": {
+ "zod": "^3.25.0 || ^4.0.0"
+ }
+ }
+ }
+}
diff --git a/ui/package.json b/ui/package.json
new file mode 100644
index 00000000..51982efe
--- /dev/null
+++ b/ui/package.json
@@ -0,0 +1,41 @@
+{
+ "name": "@inference-gateway/ui",
+ "version": "1.0.0",
+ "description": "Web UI for Inference Gateway CLI",
+ "private": true,
+ "scripts": {
+ "dev": "next dev",
+ "build": "next build",
+ "start": "next start",
+ "lint": "eslint .",
+ "lint:fix": "eslint . --fix"
+ },
+ "dependencies": {
+ "@radix-ui/react-dialog": "^1.1.15",
+ "@radix-ui/react-popover": "^1.1.15",
+ "@radix-ui/react-select": "^2.2.6",
+ "@radix-ui/react-slot": "^1.2.4",
+ "@tanstack/react-query": "^5.90.12",
+ "class-variance-authority": "^0.7.1",
+ "clsx": "^2.1.1",
+ "cmdk": "^1.1.1",
+ "lucide-react": "^0.562.0",
+ "next": "^16.1.0",
+ "react": "^19.2.0",
+ "react-dom": "^19.2.0",
+ "tailwind-merge": "^3.4.0",
+ "tailwindcss-animate": "^1.0.7"
+ },
+ "devDependencies": {
+ "@tailwindcss/postcss": "^4.1.18",
+ "@types/node": "^25.0.3",
+ "@types/react": "^19.2.7",
+ "@types/react-dom": "^19.2.3",
+ "autoprefixer": "^10.4.23",
+ "eslint": "^9",
+ "eslint-config-next": "^16.1.0",
+ "postcss": "^8.5.6",
+ "tailwindcss": "^4.1.18",
+ "typescript": "^5.9.3"
+ }
+}
diff --git a/ui/postcss.config.mjs b/ui/postcss.config.mjs
new file mode 100644
index 00000000..5d6d8457
--- /dev/null
+++ b/ui/postcss.config.mjs
@@ -0,0 +1,8 @@
+/** @type {import('postcss-load-config').Config} */
+const config = {
+ plugins: {
+ '@tailwindcss/postcss': {},
+ },
+};
+
+export default config;
diff --git a/ui/tailwind.config.ts b/ui/tailwind.config.ts
new file mode 100644
index 00000000..111cd3d3
--- /dev/null
+++ b/ui/tailwind.config.ts
@@ -0,0 +1,9 @@
+const config = {
+ content: [
+ "./pages/**/*.{js,ts,jsx,tsx,mdx}",
+ "./components/**/*.{js,ts,jsx,tsx,mdx}",
+ "./app/**/*.{js,ts,jsx,tsx,mdx}",
+ ],
+};
+
+export default config;
diff --git a/ui/tsconfig.json b/ui/tsconfig.json
new file mode 100644
index 00000000..1f518977
--- /dev/null
+++ b/ui/tsconfig.json
@@ -0,0 +1,41 @@
+{
+ "compilerOptions": {
+ "target": "ES2020",
+ "lib": [
+ "dom",
+ "dom.iterable",
+ "esnext"
+ ],
+ "allowJs": true,
+ "skipLibCheck": true,
+ "strict": true,
+ "noEmit": true,
+ "esModuleInterop": true,
+ "module": "esnext",
+ "moduleResolution": "bundler",
+ "resolveJsonModule": true,
+ "isolatedModules": true,
+ "jsx": "react-jsx",
+ "incremental": true,
+ "plugins": [
+ {
+ "name": "next"
+ }
+ ],
+ "paths": {
+ "@/*": [
+ "./*"
+ ]
+ }
+ },
+ "include": [
+ "next-env.d.ts",
+ "**/*.ts",
+ "**/*.tsx",
+ ".next/types/**/*.ts",
+ ".next/dev/types/**/*.ts"
+ ],
+ "exclude": [
+ "node_modules"
+ ]
+}