From 00f8801d51a7552d8d8ab8848ebd07241c26a59e Mon Sep 17 00:00:00 2001 From: hailin Date: Sun, 8 Feb 2026 22:54:37 -0800 Subject: [PATCH] Initial commit: IT0 AI-powered server cluster operations platform Full-stack monorepo with DDD + Clean Architecture: - Backend: 7 NestJS microservices + 5 shared libraries (TypeScript) - Mobile: Flutter app with Riverpod (Dart) - Web Admin: Next.js dashboard with Zustand + React Query - Voice: Python voice service (STT/TTS/VAD) - Infra: Docker Compose, K8s manifests, Turborepo build Co-Authored-By: Claude Opus 4.6 --- .env.example | 38 + .gitignore | 90 + README.md | 45 + deploy/docker/.env.example | 9 + deploy/docker/docker-compose.voice.yml | 22 + deploy/docker/docker-compose.yml | 228 + deploy/k8s/base/namespace.yaml | 6 + deploy/k8s/base/postgres.yaml | 57 + deploy/k8s/base/redis.yaml | 32 + docs/backend-guide.md | 3095 ++++++ docs/flutter-guide.md | 2105 ++++ docs/web-admin-guide.md | 1984 ++++ it0-web-admin/next.config.js | 16 + it0-web-admin/package-lock.json | 9512 +++++++++++++++++ it0-web-admin/package.json | 62 + it0-web-admin/postcss.config.js | 6 + .../app/(admin)/agent-config/hooks/page.tsx | 601 ++ .../src/app/(admin)/agent-config/page.tsx | 362 + .../agent-config/skills/[name]/page.tsx | 669 ++ .../app/(admin)/agent-config/skills/page.tsx | 553 + .../src/app/(admin)/audit/logs/page.tsx | 353 + .../src/app/(admin)/audit/replay/page.tsx | 714 ++ .../src/app/(admin)/communication/page.tsx | 796 ++ .../src/app/(admin)/dashboard/page.tsx | 262 + it0-web-admin/src/app/(admin)/layout.tsx | 18 + .../monitoring/alert-rules/[id]/page.tsx | 1009 ++ .../(admin)/monitoring/alert-rules/page.tsx | 734 ++ .../(admin)/monitoring/health-checks/page.tsx | 295 + .../app/(admin)/monitoring/metrics/page.tsx | 574 + .../src/app/(admin)/runbooks/[id]/page.tsx | 818 ++ .../src/app/(admin)/runbooks/page.tsx | 445 + .../app/(admin)/security/credentials/page.tsx | 846 ++ .../app/(admin)/security/permissions/page.tsx | 397 + .../app/(admin)/security/risk-rules/page.tsx | 681 ++ .../src/app/(admin)/security/roles/page.tsx | 658 ++ .../src/app/(admin)/servers/[id]/page.tsx | 974 ++ .../src/app/(admin)/servers/clusters/page.tsx | 626 ++ .../src/app/(admin)/servers/page.tsx | 571 + .../src/app/(admin)/sessions/[id]/page.tsx | 644 ++ .../src/app/(admin)/settings/page.tsx | 764 ++ .../app/(admin)/standing-orders/[id]/page.tsx | 1144 ++ .../src/app/(admin)/standing-orders/page.tsx | 728 ++ .../src/app/(admin)/tenants/[id]/page.tsx | 764 ++ .../src/app/(admin)/tenants/page.tsx | 414 + .../src/app/(admin)/terminal/page.tsx | 450 + .../src/app/(admin)/users/[id]/page.tsx | 777 ++ it0-web-admin/src/app/(admin)/users/page.tsx | 833 ++ it0-web-admin/src/app/(auth)/layout.tsx | 7 + it0-web-admin/src/app/(auth)/login/page.tsx | 84 + .../src/app/api/proxy/[...path]/route.ts | 48 + it0-web-admin/src/app/layout.tsx | 25 + it0-web-admin/src/app/page.tsx | 5 + it0-web-admin/src/app/providers.tsx | 27 + .../src/application/dto/agent-config.dto.ts | 17 + it0-web-admin/src/application/dto/index.ts | 3 + .../src/application/dto/runbook.dto.ts | 31 + .../src/application/dto/server.dto.ts | 24 + .../use-cases/agent-config/index.ts | 3 + .../use-cases/agent-config/manage-hooks.ts | 26 + .../use-cases/agent-config/switch-engine.ts | 8 + .../agent-config/update-system-prompt.ts | 8 + .../monitoring/configure-health-check.ts | 28 + .../use-cases/monitoring/create-alert-rule.ts | 31 + .../application/use-cases/monitoring/index.ts | 3 + .../use-cases/runbooks/create-runbook.ts | 10 + .../use-cases/runbooks/delete-runbook.ts | 7 + .../application/use-cases/runbooks/index.ts | 4 + .../use-cases/runbooks/test-runbook.ts | 9 + .../use-cases/runbooks/update-runbook.ts | 10 + .../application/use-cases/security/index.ts | 4 + .../use-cases/security/manage-credentials.ts | 29 + .../use-cases/security/manage-permissions.ts | 22 + .../use-cases/security/manage-risk-rules.ts | 29 + .../use-cases/servers/add-server.ts | 10 + .../application/use-cases/servers/index.ts | 3 + .../use-cases/servers/remove-server.ts | 7 + .../use-cases/servers/update-server.ts | 10 + .../src/domain/entities/agent-engine.ts | 8 + .../src/domain/entities/agent-session.ts | 32 + .../src/domain/entities/alert-rule.ts | 28 + .../src/domain/entities/audit-log.ts | 11 + it0-web-admin/src/domain/entities/cluster.ts | 12 + it0-web-admin/src/domain/entities/contact.ts | 9 + .../src/domain/entities/credential.ts | 10 + .../src/domain/entities/escalation-policy.ts | 16 + .../src/domain/entities/health-check.ts | 12 + .../src/domain/entities/hook-script.ts | 12 + it0-web-admin/src/domain/entities/index.ts | 19 + .../src/domain/entities/risk-rule.ts | 11 + it0-web-admin/src/domain/entities/role.ts | 7 + it0-web-admin/src/domain/entities/runbook.ts | 23 + it0-web-admin/src/domain/entities/server.ts | 15 + it0-web-admin/src/domain/entities/session.ts | 12 + it0-web-admin/src/domain/entities/skill.ts | 11 + .../src/domain/entities/standing-order.ts | 19 + it0-web-admin/src/domain/entities/tenant.ts | 26 + it0-web-admin/src/domain/entities/user.ts | 11 + .../repositories/agent-config.repository.ts | 11 + .../repositories/alert-rule.repository.ts | 12 + .../domain/repositories/audit.repository.ts | 7 + .../repositories/communication.repository.ts | 18 + .../src/domain/repositories/index.ts | 7 + .../domain/repositories/runbook.repository.ts | 11 + .../repositories/security.repository.ts | 21 + .../domain/repositories/server.repository.ts | 9 + .../domain/value-objects/alert-severity.ts | 8 + .../src/domain/value-objects/channel-type.ts | 11 + .../src/domain/value-objects/engine-type.ts | 7 + .../src/domain/value-objects/index.ts | 14 + .../src/domain/value-objects/risk-level.ts | 15 + .../value-objects/server-environment.ts | 13 + .../src/infrastructure/api/api-client.ts | 51 + .../src/infrastructure/api/query-keys.ts | 129 + .../api-agent-config.repository.ts | 46 + .../repositories/api-alert-rule.repository.ts | 51 + .../repositories/api-audit.repository.ts | 29 + .../api-communication.repository.ts | 71 + .../repositories/api-runbook.repository.ts | 43 + .../repositories/api-security.repository.ts | 80 + .../repositories/api-server.repository.ts | 34 + .../repositories/api-tenant.repository.ts | 41 + .../src/infrastructure/repositories/index.ts | 8 + .../src/infrastructure/ws/websocket-client.ts | 65 + it0-web-admin/src/lib/utils.ts | 6 + .../domain/alert-severity-badge.tsx | 37 + .../domain/approval-status-badge.tsx | 38 + .../domain/engine-type-selector.tsx | 57 + .../presentation/components/domain/index.ts | 11 + .../components/domain/risk-level-badge.tsx | 30 + .../domain/server-status-indicator.tsx | 52 + .../components/domain/stream-event-viewer.tsx | 124 + .../components/domain/task-status-badge.tsx | 40 + .../components/layout/sidebar.tsx | 159 + .../components/layout/top-bar.tsx | 27 + .../src/presentation/components/ui/button.tsx | 85 + .../components/ui/command-palette.tsx | 238 + .../presentation/components/ui/data-table.tsx | 278 + .../src/presentation/components/ui/dialog.tsx | 198 + .../src/presentation/components/ui/input.tsx | 78 + .../src/presentation/components/ui/toast.tsx | 224 + .../presentation/hooks/use-confirmation.ts | 174 + .../src/presentation/hooks/use-debounce.ts | 36 + .../src/presentation/hooks/use-normalise.ts | 94 + .../src/presentation/hooks/use-websocket.ts | 151 + .../redux/middleware/audit-middleware.ts | 32 + .../stores/redux/slices/agent-config-slice.ts | 51 + .../redux/slices/permission-matrix-slice.ts | 46 + .../stores/redux/slices/risk-rules.slice.ts | 109 + .../redux/slices/runbook-editor-slice.ts | 59 + .../redux/slices/session-replay.slice.ts | 87 + it0-web-admin/src/stores/redux/store.ts | 25 + .../src/stores/zustand/notification-store.ts | 44 + .../src/stores/zustand/tenant-store.ts | 31 + .../src/stores/zustand/terminal-store.ts | 47 + .../src/stores/zustand/theme-store.ts | 19 + it0-web-admin/src/stores/zustand/ui-store.ts | 15 + it0-web-admin/src/styles/globals.css | 35 + it0-web-admin/src/types/index.ts | 19 + it0-web-admin/tailwind.config.ts | 50 + it0-web-admin/tsconfig.json | 23 + it0_app/analysis_options.yaml | 8 + .../plugins/GeneratedPluginRegistrant.java | 64 + it0_app/lib/app.dart | 20 + it0_app/lib/core/config/api_endpoints.dart | 43 + it0_app/lib/core/config/app_config.dart | 29 + it0_app/lib/core/errors/error_handler.dart | 38 + it0_app/lib/core/errors/failures.dart | 21 + it0_app/lib/core/network/api_result.dart | 18 + it0_app/lib/core/network/dio_client.dart | 65 + .../lib/core/network/websocket_client.dart | 86 + it0_app/lib/core/router/app_router.dart | 91 + .../lib/core/tenant/tenant_interceptor.dart | 17 + it0_app/lib/core/tenant/tenant_provider.dart | 23 + it0_app/lib/core/theme/app_colors.dart | 27 + it0_app/lib/core/theme/app_theme.dart | 33 + it0_app/lib/core/theme/app_typography.dart | 43 + it0_app/lib/core/utils/date_formatter.dart | 23 + it0_app/lib/core/utils/logger.dart | 11 + it0_app/lib/core/widgets/empty_state.dart | 39 + it0_app/lib/core/widgets/loading_overlay.dart | 26 + it0_app/lib/core/widgets/risk_level_chip.dart | 39 + it0_app/lib/core/widgets/status_badge.dart | 48 + .../data/datasources/voice_datasource.dart | 35 + .../repositories/voice_repository_impl.dart | 41 + .../domain/entities/voice_session.dart | 44 + .../domain/repositories/voice_repository.dart | 14 + .../domain/usecases/end_voice_session.dart | 11 + .../domain/usecases/start_voice_session.dart | 12 + .../presentation/pages/agent_call_page.dart | 457 + .../providers/voice_call_providers.dart | 173 + .../data/datasources/alerts_datasource.dart | 45 + .../repositories/alerts_repository_impl.dart | 21 + .../alerts/domain/entities/alert_event.dart | 40 + .../repositories/alerts_repository.dart | 10 + .../presentation/pages/alerts_page.dart | 426 + .../providers/alerts_providers.dart | 38 + .../datasources/approvals_datasource.dart | 50 + .../approvals_repository_impl.dart | 27 + .../approvals/domain/entities/approval.dart | 48 + .../repositories/approvals_repository.dart | 13 + .../domain/usecases/approve_command.dart | 12 + .../usecases/get_pending_approvals.dart | 12 + .../domain/usecases/reject_command.dart | 12 + .../presentation/pages/approvals_page.dart | 556 + .../providers/approvals_providers.dart | 58 + .../datasources/auth_remote_datasource.dart | 49 + .../auth/data/providers/auth_provider.dart | 138 + .../auth/data/providers/tenant_provider.dart | 5 + .../repositories/auth_repository_impl.dart | 94 + .../auth/domain/entities/auth_user.dart | 21 + .../domain/repositories/auth_repository.dart | 24 + .../features/auth/domain/usecases/login.dart | 15 + .../features/auth/domain/usecases/logout.dart | 11 + .../auth/domain/usecases/refresh_token.dart | 12 + .../auth/presentation/pages/login_page.dart | 126 + .../datasources/chat_local_datasource.dart | 53 + .../datasources/chat_remote_datasource.dart | 87 + .../repositories/chat_repository_impl.dart | 156 + .../chat/domain/entities/chat_message.dart | 97 + .../chat/domain/entities/chat_session.dart | 35 + .../chat/domain/entities/stream_event.dart | 52 + .../domain/repositories/chat_repository.dart | 35 + .../chat/domain/usecases/cancel_task.dart | 11 + .../domain/usecases/get_session_history.dart | 12 + .../chat/domain/usecases/send_message.dart | 20 + .../domain/usecases/start_voice_input.dart | 18 + .../chat/presentation/pages/chat_page.dart | 636 ++ .../providers/chat_providers.dart | 410 + .../providers/voice_providers.dart | 149 + .../widgets/agent_thinking_indicator.dart | 80 + .../widgets/approval_action_card.dart | 259 + .../presentation/widgets/message_bubble.dart | 83 + .../widgets/stream_text_widget.dart | 78 + .../widgets/tool_execution_card.dart | 176 + .../widgets/voice_input_button.dart | 95 + .../datasources/dashboard_datasource.dart | 45 + .../dashboard_repository_impl.dart | 127 + .../domain/entities/dashboard_summary.dart | 44 + .../domain/entities/recent_operation.dart | 18 + .../domain/entities/server_status.dart | 34 + .../repositories/dashboard_repository.dart | 15 + .../presentation/pages/dashboard_page.dart | 441 + .../providers/dashboard_providers.dart | 46 + .../data/datasources/servers_datasource.dart | 46 + .../repositories/servers_repository_impl.dart | 24 + .../servers/domain/entities/server.dart | 44 + .../repositories/servers_repository.dart | 10 + .../presentation/pages/servers_page.dart | 480 + .../providers/servers_providers.dart | 44 + .../data/datasources/settings_datasource.dart | 71 + .../settings_repository_impl.dart | 24 + .../domain/entities/app_settings.dart | 46 + .../repositories/settings_repository.dart | 13 + .../presentation/pages/settings_page.dart | 69 + .../providers/settings_providers.dart | 108 + .../standing_orders_datasource.dart | 72 + .../standing_orders_repository_impl.dart | 44 + .../domain/entities/standing_order.dart | 48 + .../standing_orders_repository.dart | 22 + .../domain/usecases/get_standing_orders.dart | 12 + .../usecases/toggle_standing_order.dart | 12 + .../pages/standing_orders_page.dart | 418 + .../providers/standing_orders_providers.dart | 55 + .../data/datasources/tasks_datasource.dart | 74 + .../repositories/tasks_repository_impl.dart | 48 + .../features/tasks/domain/entities/task.dart | 56 + .../domain/repositories/tasks_repository.dart | 24 + .../tasks/domain/usecases/create_task.dart | 22 + .../tasks/domain/usecases/get_tasks.dart | 12 + .../tasks/presentation/pages/tasks_page.dart | 432 + .../providers/tasks_providers.dart | 64 + .../data/datasources/terminal_datasource.dart | 41 + .../terminal_repository_impl.dart | 52 + .../domain/entities/terminal_session.dart | 32 + .../repositories/terminal_repository.dart | 13 + .../presentation/pages/terminal_page.dart | 388 + .../providers/terminal_providers.dart | 31 + it0_app/lib/main.dart | 15 + it0_app/pubspec.lock | 1178 ++ it0_app/pubspec.yaml | 79 + package.json | 19 + packages/gateway/Dockerfile | 9 + packages/gateway/config/kong.yml | 149 + packages/services/agent-service/package.json | 45 + .../agent-service/src/agent.module.ts | 43 + .../src/application/dto/execute-task.dto.ts | 8 + .../src/application/dto/stream-event.dto.ts | 59 + .../src/application/dto/task-result.dto.ts | 11 + .../use-cases/approve-command.use-case.ts | 124 + .../use-cases/cancel-task.use-case.ts | 43 + .../use-cases/execute-task.use-case.ts | 144 + .../use-cases/get-session-history.use-case.ts | 33 + .../use-cases/switch-engine.use-case.ts | 25 + .../domain/entities/agent-session.entity.ts | 28 + .../src/domain/entities/agent-task.entity.ts | 34 + .../domain/entities/command-record.entity.ts | 37 + .../domain/entities/standing-order.entity.ts | 19 + .../domain/events/approval-required.event.ts | 11 + .../domain/events/command-blocked.event.ts | 9 + .../src/domain/events/task-completed.event.ts | 10 + .../ports/inbound/approve-command.port.ts | 4 + .../domain/ports/inbound/execute-task.port.ts | 5 + .../inbound/manage-standing-order.port.ts | 4 + .../ports/inbound/switch-engine.port.ts | 5 + .../ports/outbound/agent-engine.port.ts | 29 + .../ports/outbound/command-guard.port.ts | 7 + .../ports/outbound/event-publisher.port.ts | 3 + .../ports/outbound/session-repository.port.ts | 7 + .../ports/outbound/stream-emitter.port.ts | 4 + .../services/command-risk-classifier.ts | 48 + .../src/domain/services/session-manager.ts | 38 + .../domain/services/skill-manager.service.ts | 100 + .../services/standing-order-extractor.ts | 166 + .../value-objects/agent-engine-type.vo.ts | 5 + .../value-objects/command-risk-level.vo.ts | 13 + .../src/domain/value-objects/session-id.vo.ts | 15 + .../domain/value-objects/task-status.vo.ts | 8 + .../engines/claude-api/agent-loop.ts | 187 + .../engines/claude-api/claude-api-engine.ts | 264 + .../engines/claude-api/tool-executor.ts | 149 + .../claude-code-cli/claude-code-engine.ts | 284 + .../claude-code-cli/cli-process-manager.ts | 113 + .../claude-code-cli/hook-config-manager.ts | 152 + .../claude-code-cli/stream-json-parser.ts | 100 + .../claude-code-cli/system-prompt-builder.ts | 114 + .../engines/custom/custom-engine.ts | 66 + .../infrastructure/engines/engine-registry.ts | 40 + .../guards/command-guard.service.ts | 24 + .../infrastructure/guards/risk-patterns.ts | 175 + .../messaging/event-publisher.service.ts | 74 + .../messaging/stream-emitter.service.ts | 48 + .../repositories/session.repository.ts | 21 + .../repositories/task.repository.ts | 16 + .../rest/controllers/agent.controller.ts | 246 + .../rest/controllers/risk-rules.controller.ts | 129 + .../rest/controllers/session.controller.ts | 38 + .../src/interfaces/ws/agent-stream.gateway.ts | 32 + packages/services/agent-service/src/main.ts | 12 + packages/services/agent-service/tsconfig.json | 21 + packages/services/audit-service/package.json | 36 + .../use-cases/query-audit-trail.use-case.ts | 19 + .../use-cases/record-audit.use-case.ts | 23 + .../audit-service/src/audit.module.ts | 20 + .../src/domain/entities/audit-log.entity.ts | 34 + .../domain/value-objects/action-type.vo.ts | 12 + .../domain/value-objects/audit-level.vo.ts | 5 + .../repositories/audit-log.repository.ts | 78 + .../rest/controllers/audit.controller.ts | 44 + packages/services/audit-service/src/main.ts | 12 + packages/services/audit-service/tsconfig.json | 21 + packages/services/auth-service/package.json | 44 + .../src/application/services/auth.service.ts | 165 + .../services/auth-service/src/auth.module.ts | 43 + .../src/domain/entities/api-key.entity.ts | 34 + .../src/domain/entities/role.entity.ts | 22 + .../src/domain/entities/tenant.entity.ts | 40 + .../src/domain/entities/user.entity.ts | 34 + .../src/domain/value-objects/permission.vo.ts | 32 + .../src/domain/value-objects/role-type.vo.ts | 24 + .../src/infrastructure/guards/rbac.guard.ts | 18 + .../repositories/api-key.repository.ts | 21 + .../repositories/user.repository.ts | 16 + .../infrastructure/strategies/jwt.strategy.ts | 33 + .../rest/controllers/auth.controller.ts | 41 + .../rest/controllers/tenant.controller.ts | 133 + packages/services/auth-service/src/main.ts | 12 + packages/services/auth-service/tsconfig.json | 21 + packages/services/comm-service/package.json | 43 + .../services/comm-service/src/comm.module.ts | 60 + .../domain/entities/channel-config.entity.ts | 22 + .../src/domain/entities/contact.entity.ts | 37 + .../entities/escalation-policy.entity.ts | 26 + .../src/domain/entities/message.entity.ts | 40 + .../ports/outbound/channel-adapter.port.ts | 34 + .../src/domain/services/escalation-engine.ts | 143 + .../domain/value-objects/channel-type.vo.ts | 11 + .../value-objects/delivery-status.vo.ts | 7 + .../value-objects/message-priority.vo.ts | 6 + .../adapters/adapter-registry.ts | 45 + .../infrastructure/adapters/email.adapter.ts | 52 + .../infrastructure/adapters/sms.adapter.ts | 66 + .../adapters/telegram.adapter.ts | 57 + .../adapters/voice-call.adapter.ts | 77 + .../adapters/voice-service-client.adapter.ts | 54 + .../adapters/websocket.adapter.ts | 40 + .../adapters/wechat-work.adapter.ts | 58 + .../repositories/contact.repository.ts | 16 + .../escalation-policy.repository.ts | 26 + .../repositories/message.repository.ts | 21 + .../rest/controllers/channel.controller.ts | 81 + .../rest/controllers/contact.controller.ts | 55 + .../escalation-policy.controller.ts | 113 + .../rest/controllers/message.controller.ts | 36 + .../src/interfaces/ws/realtime.gateway.ts | 25 + packages/services/comm-service/src/main.ts | 12 + packages/services/comm-service/tsconfig.json | 21 + .../services/inventory-service/package.json | 35 + .../src/domain/entities/cluster.entity.ts | 25 + .../src/domain/entities/credential.entity.ts | 40 + .../src/domain/entities/server.entity.ts | 58 + .../src/domain/entities/ssh-config.entity.ts | 31 + .../ports/outbound/credential-vault.port.ts | 12 + .../ports/outbound/server-repository.port.ts | 9 + .../value-objects/connection-info.vo.ts | 23 + .../domain/value-objects/environment.vo.ts | 5 + .../domain/value-objects/server-role.vo.ts | 8 + .../crypto/credential-vault.service.ts | 20 + .../repositories/cluster.repository.ts | 16 + .../repositories/credential.repository.ts | 16 + .../repositories/server.repository.ts | 21 + .../rest/controllers/cluster.controller.ts | 40 + .../rest/controllers/credential.controller.ts | 91 + .../rest/controllers/server.controller.ts | 88 + .../inventory-service/src/inventory.module.ts | 26 + .../services/inventory-service/src/main.ts | 12 + .../services/inventory-service/tsconfig.json | 21 + .../services/monitor-service/package.json | 40 + .../src/domain/entities/alert-event.entity.ts | 37 + .../src/domain/entities/alert-rule.entity.ts | 38 + .../entities/health-check-result.entity.ts | 34 + .../domain/entities/metric-snapshot.entity.ts | 22 + .../ports/outbound/alert-dispatcher.port.ts | 9 + .../ports/outbound/metric-collector.port.ts | 6 + .../services/alert-runbook-bridge.service.ts | 92 + .../domain/services/health-check.service.ts | 168 + .../domain/value-objects/alert-severity.vo.ts | 6 + .../src/domain/value-objects/check-type.vo.ts | 7 + .../domain/value-objects/metric-type.vo.ts | 7 + .../collectors/agent-collector.ts | 62 + .../collectors/prometheus-collector.ts | 73 + .../collectors/ssh-collector.ts | 111 + .../repositories/alert-event.repository.ts | 21 + .../repositories/alert-rule.repository.ts | 16 + .../health-check-result.repository.ts | 21 + .../metric-snapshot.repository.ts | 26 + .../rest/controllers/alert.controller.ts | 58 + .../controllers/health-check.controller.ts | 41 + packages/services/monitor-service/src/main.ts | 12 + .../monitor-service/src/monitor.module.ts | 53 + .../services/monitor-service/tsconfig.json | 21 + packages/services/ops-service/package.json | 43 + .../use-cases/approve-task.use-case.ts | 116 + .../create-deployment-task.use-case.ts | 73 + .../create-inspection-task.use-case.ts | 53 + .../create-standing-order.use-case.ts | 82 + .../use-cases/execute-runbook.use-case.ts | 101 + .../execute-standing-order.use-case.ts | 37 + .../schedule-recurring-task.use-case.ts | 74 + .../update-standing-order.use-case.ts | 64 + .../entities/approval-request.entity.ts | 37 + .../src/domain/entities/ops-task.entity.ts | 46 + .../src/domain/entities/runbook.entity.ts | 40 + .../standing-order-execution.entity.ts | 58 + .../domain/entities/standing-order.entity.ts | 97 + .../domain/ports/inbound/approve-task.port.ts | 7 + .../domain/ports/inbound/create-task.port.ts | 38 + .../ports/inbound/execute-runbook.port.ts | 13 + .../ports/outbound/agent-client.port.ts | 22 + .../ports/outbound/audit-logger.port.ts | 12 + .../ports/outbound/notification.port.ts | 11 + .../ports/outbound/task-repository.port.ts | 15 + .../services/approval-workflow.service.ts | 90 + .../services/smart-escalation.service.ts | 118 + .../standing-order-executor.service.ts | 320 + .../value-objects/approval-status.vo.ts | 7 + .../value-objects/execution-result.vo.ts | 7 + .../src/domain/value-objects/task-type.vo.ts | 9 + .../clients/agent-client.service.ts | 77 + .../clients/comm-client.service.ts | 46 + .../repositories/approval.repository.ts | 21 + .../repositories/runbook.repository.ts | 16 + .../standing-order-execution.repository.ts | 16 + .../repositories/standing-order.repository.ts | 16 + .../repositories/task.repository.ts | 21 + .../rest/controllers/approval.controller.ts | 48 + .../rest/controllers/runbook.controller.ts | 56 + .../controllers/standing-order.controller.ts | 156 + .../rest/controllers/task.controller.ts | 45 + packages/services/ops-service/src/main.ts | 12 + .../services/ops-service/src/ops.module.ts | 79 + packages/services/ops-service/tsconfig.json | 21 + packages/services/voice-service/Dockerfile | 19 + .../services/voice-service/requirements.txt | 14 + .../voice-service/src/api/__init__.py | 1 + .../services/voice-service/src/api/health.py | 13 + .../services/voice-service/src/api/main.py | 65 + .../voice-service/src/api/session_router.py | 153 + .../voice-service/src/api/twilio_webhook.py | 124 + .../voice-service/src/config/settings.py | 41 + .../voice-service/src/context/__init__.py | 0 .../src/context/session_context.py | 40 + .../voice-service/src/context/tool_handler.py | 29 + .../voice-service/src/core/__init__.py | 1 + .../voice-service/src/pipeline/__init__.py | 1 + .../src/pipeline/app_transport.py | 31 + .../src/pipeline/base_pipeline.py | 69 + .../src/pipeline/twilio_transport.py | 134 + .../src/services/outbound_call_service.py | 38 + .../voice-service/src/stt/__init__.py | 1 + .../voice-service/src/stt/whisper_service.py | 45 + .../voice-service/src/tts/__init__.py | 1 + .../voice-service/src/tts/kokoro_service.py | 35 + .../voice-service/src/vad/__init__.py | 1 + .../voice-service/src/vad/silero_service.py | 34 + packages/shared/common/package.json | 24 + .../common/src/constants/event-patterns.ts | 35 + .../common/src/constants/grpc-services.ts | 37 + .../common/src/decorators/tenant.decorator.ts | 14 + .../src/filters/all-exceptions.filter.ts | 42 + .../shared/common/src/guards/roles.guard.ts | 22 + packages/shared/common/src/index.ts | 29 + .../src/interceptors/logging.interceptor.ts | 25 + .../src/interceptors/transform.interceptor.ts | 26 + .../src/interfaces/base-entity.interface.ts | 6 + .../interfaces/paginated-result.interface.ts | 14 + .../src/interfaces/tenant-info.interface.ts | 6 + .../common/src/pipes/validation.pipe.ts | 29 + .../shared/common/src/utils/crypto.util.ts | 28 + packages/shared/common/src/utils/date.util.ts | 17 + .../src/utils/tenant-context.service.ts | 24 + packages/shared/common/tsconfig.json | 11 + packages/shared/database/package.json | 24 + packages/shared/database/src/data-source.ts | 21 + .../shared/database/src/database.module.ts | 29 + packages/shared/database/src/index.ts | 3 + .../migrations/001-create-shared-schema.sql | 23 + .../002-create-tenant-schema-template.sql | 413 + .../src/migrations/003-add-missing-tables.sql | 149 + .../shared/database/src/run-migrations.ts | 181 + packages/shared/database/src/seeds/seed.sql | 170 + .../database/src/tenant-aware.repository.ts | 36 + .../src/tenant-provisioning.service.ts | 81 + packages/shared/database/tsconfig.json | 14 + packages/shared/events/package.json | 18 + packages/shared/events/src/event-bus.ts | 69 + packages/shared/events/src/event-types.ts | 64 + packages/shared/events/src/index.ts | 2 + packages/shared/events/tsconfig.json | 11 + packages/shared/proto/package.json | 15 + packages/shared/proto/src/agent.proto | 62 + packages/shared/proto/src/comm.proto | 35 + packages/shared/proto/src/index.ts | 5 + packages/shared/proto/src/inventory.proto | 56 + packages/shared/proto/tsconfig.json | 11 + packages/shared/testing/package.json | 19 + packages/shared/testing/src/index.ts | 1 + packages/shared/testing/src/test-utils.ts | 19 + packages/shared/testing/tsconfig.json | 11 + pnpm-lock.yaml | 7085 ++++++++++++ pnpm-workspace.yaml | 4 + tsconfig.base.json | 23 + turbo.json | 16 + 552 files changed, 71949 insertions(+) create mode 100644 .env.example create mode 100644 .gitignore create mode 100644 README.md create mode 100644 deploy/docker/.env.example create mode 100644 deploy/docker/docker-compose.voice.yml create mode 100644 deploy/docker/docker-compose.yml create mode 100644 deploy/k8s/base/namespace.yaml create mode 100644 deploy/k8s/base/postgres.yaml create mode 100644 deploy/k8s/base/redis.yaml create mode 100644 docs/backend-guide.md create mode 100644 docs/flutter-guide.md create mode 100644 docs/web-admin-guide.md create mode 100644 it0-web-admin/next.config.js create mode 100644 it0-web-admin/package-lock.json create mode 100644 it0-web-admin/package.json create mode 100644 it0-web-admin/postcss.config.js create mode 100644 it0-web-admin/src/app/(admin)/agent-config/hooks/page.tsx create mode 100644 it0-web-admin/src/app/(admin)/agent-config/page.tsx create mode 100644 it0-web-admin/src/app/(admin)/agent-config/skills/[name]/page.tsx create mode 100644 it0-web-admin/src/app/(admin)/agent-config/skills/page.tsx create mode 100644 it0-web-admin/src/app/(admin)/audit/logs/page.tsx create mode 100644 it0-web-admin/src/app/(admin)/audit/replay/page.tsx create mode 100644 it0-web-admin/src/app/(admin)/communication/page.tsx create mode 100644 it0-web-admin/src/app/(admin)/dashboard/page.tsx create mode 100644 it0-web-admin/src/app/(admin)/layout.tsx create mode 100644 it0-web-admin/src/app/(admin)/monitoring/alert-rules/[id]/page.tsx create mode 100644 it0-web-admin/src/app/(admin)/monitoring/alert-rules/page.tsx create mode 100644 it0-web-admin/src/app/(admin)/monitoring/health-checks/page.tsx create mode 100644 it0-web-admin/src/app/(admin)/monitoring/metrics/page.tsx create mode 100644 it0-web-admin/src/app/(admin)/runbooks/[id]/page.tsx create mode 100644 it0-web-admin/src/app/(admin)/runbooks/page.tsx create mode 100644 it0-web-admin/src/app/(admin)/security/credentials/page.tsx create mode 100644 it0-web-admin/src/app/(admin)/security/permissions/page.tsx create mode 100644 it0-web-admin/src/app/(admin)/security/risk-rules/page.tsx create mode 100644 it0-web-admin/src/app/(admin)/security/roles/page.tsx create mode 100644 it0-web-admin/src/app/(admin)/servers/[id]/page.tsx create mode 100644 it0-web-admin/src/app/(admin)/servers/clusters/page.tsx create mode 100644 it0-web-admin/src/app/(admin)/servers/page.tsx create mode 100644 it0-web-admin/src/app/(admin)/sessions/[id]/page.tsx create mode 100644 it0-web-admin/src/app/(admin)/settings/page.tsx create mode 100644 it0-web-admin/src/app/(admin)/standing-orders/[id]/page.tsx create mode 100644 it0-web-admin/src/app/(admin)/standing-orders/page.tsx create mode 100644 it0-web-admin/src/app/(admin)/tenants/[id]/page.tsx create mode 100644 it0-web-admin/src/app/(admin)/tenants/page.tsx create mode 100644 it0-web-admin/src/app/(admin)/terminal/page.tsx create mode 100644 it0-web-admin/src/app/(admin)/users/[id]/page.tsx create mode 100644 it0-web-admin/src/app/(admin)/users/page.tsx create mode 100644 it0-web-admin/src/app/(auth)/layout.tsx create mode 100644 it0-web-admin/src/app/(auth)/login/page.tsx create mode 100644 it0-web-admin/src/app/api/proxy/[...path]/route.ts create mode 100644 it0-web-admin/src/app/layout.tsx create mode 100644 it0-web-admin/src/app/page.tsx create mode 100644 it0-web-admin/src/app/providers.tsx create mode 100644 it0-web-admin/src/application/dto/agent-config.dto.ts create mode 100644 it0-web-admin/src/application/dto/index.ts create mode 100644 it0-web-admin/src/application/dto/runbook.dto.ts create mode 100644 it0-web-admin/src/application/dto/server.dto.ts create mode 100644 it0-web-admin/src/application/use-cases/agent-config/index.ts create mode 100644 it0-web-admin/src/application/use-cases/agent-config/manage-hooks.ts create mode 100644 it0-web-admin/src/application/use-cases/agent-config/switch-engine.ts create mode 100644 it0-web-admin/src/application/use-cases/agent-config/update-system-prompt.ts create mode 100644 it0-web-admin/src/application/use-cases/monitoring/configure-health-check.ts create mode 100644 it0-web-admin/src/application/use-cases/monitoring/create-alert-rule.ts create mode 100644 it0-web-admin/src/application/use-cases/monitoring/index.ts create mode 100644 it0-web-admin/src/application/use-cases/runbooks/create-runbook.ts create mode 100644 it0-web-admin/src/application/use-cases/runbooks/delete-runbook.ts create mode 100644 it0-web-admin/src/application/use-cases/runbooks/index.ts create mode 100644 it0-web-admin/src/application/use-cases/runbooks/test-runbook.ts create mode 100644 it0-web-admin/src/application/use-cases/runbooks/update-runbook.ts create mode 100644 it0-web-admin/src/application/use-cases/security/index.ts create mode 100644 it0-web-admin/src/application/use-cases/security/manage-credentials.ts create mode 100644 it0-web-admin/src/application/use-cases/security/manage-permissions.ts create mode 100644 it0-web-admin/src/application/use-cases/security/manage-risk-rules.ts create mode 100644 it0-web-admin/src/application/use-cases/servers/add-server.ts create mode 100644 it0-web-admin/src/application/use-cases/servers/index.ts create mode 100644 it0-web-admin/src/application/use-cases/servers/remove-server.ts create mode 100644 it0-web-admin/src/application/use-cases/servers/update-server.ts create mode 100644 it0-web-admin/src/domain/entities/agent-engine.ts create mode 100644 it0-web-admin/src/domain/entities/agent-session.ts create mode 100644 it0-web-admin/src/domain/entities/alert-rule.ts create mode 100644 it0-web-admin/src/domain/entities/audit-log.ts create mode 100644 it0-web-admin/src/domain/entities/cluster.ts create mode 100644 it0-web-admin/src/domain/entities/contact.ts create mode 100644 it0-web-admin/src/domain/entities/credential.ts create mode 100644 it0-web-admin/src/domain/entities/escalation-policy.ts create mode 100644 it0-web-admin/src/domain/entities/health-check.ts create mode 100644 it0-web-admin/src/domain/entities/hook-script.ts create mode 100644 it0-web-admin/src/domain/entities/index.ts create mode 100644 it0-web-admin/src/domain/entities/risk-rule.ts create mode 100644 it0-web-admin/src/domain/entities/role.ts create mode 100644 it0-web-admin/src/domain/entities/runbook.ts create mode 100644 it0-web-admin/src/domain/entities/server.ts create mode 100644 it0-web-admin/src/domain/entities/session.ts create mode 100644 it0-web-admin/src/domain/entities/skill.ts create mode 100644 it0-web-admin/src/domain/entities/standing-order.ts create mode 100644 it0-web-admin/src/domain/entities/tenant.ts create mode 100644 it0-web-admin/src/domain/entities/user.ts create mode 100644 it0-web-admin/src/domain/repositories/agent-config.repository.ts create mode 100644 it0-web-admin/src/domain/repositories/alert-rule.repository.ts create mode 100644 it0-web-admin/src/domain/repositories/audit.repository.ts create mode 100644 it0-web-admin/src/domain/repositories/communication.repository.ts create mode 100644 it0-web-admin/src/domain/repositories/index.ts create mode 100644 it0-web-admin/src/domain/repositories/runbook.repository.ts create mode 100644 it0-web-admin/src/domain/repositories/security.repository.ts create mode 100644 it0-web-admin/src/domain/repositories/server.repository.ts create mode 100644 it0-web-admin/src/domain/value-objects/alert-severity.ts create mode 100644 it0-web-admin/src/domain/value-objects/channel-type.ts create mode 100644 it0-web-admin/src/domain/value-objects/engine-type.ts create mode 100644 it0-web-admin/src/domain/value-objects/index.ts create mode 100644 it0-web-admin/src/domain/value-objects/risk-level.ts create mode 100644 it0-web-admin/src/domain/value-objects/server-environment.ts create mode 100644 it0-web-admin/src/infrastructure/api/api-client.ts create mode 100644 it0-web-admin/src/infrastructure/api/query-keys.ts create mode 100644 it0-web-admin/src/infrastructure/repositories/api-agent-config.repository.ts create mode 100644 it0-web-admin/src/infrastructure/repositories/api-alert-rule.repository.ts create mode 100644 it0-web-admin/src/infrastructure/repositories/api-audit.repository.ts create mode 100644 it0-web-admin/src/infrastructure/repositories/api-communication.repository.ts create mode 100644 it0-web-admin/src/infrastructure/repositories/api-runbook.repository.ts create mode 100644 it0-web-admin/src/infrastructure/repositories/api-security.repository.ts create mode 100644 it0-web-admin/src/infrastructure/repositories/api-server.repository.ts create mode 100644 it0-web-admin/src/infrastructure/repositories/api-tenant.repository.ts create mode 100644 it0-web-admin/src/infrastructure/repositories/index.ts create mode 100644 it0-web-admin/src/infrastructure/ws/websocket-client.ts create mode 100644 it0-web-admin/src/lib/utils.ts create mode 100644 it0-web-admin/src/presentation/components/domain/alert-severity-badge.tsx create mode 100644 it0-web-admin/src/presentation/components/domain/approval-status-badge.tsx create mode 100644 it0-web-admin/src/presentation/components/domain/engine-type-selector.tsx create mode 100644 it0-web-admin/src/presentation/components/domain/index.ts create mode 100644 it0-web-admin/src/presentation/components/domain/risk-level-badge.tsx create mode 100644 it0-web-admin/src/presentation/components/domain/server-status-indicator.tsx create mode 100644 it0-web-admin/src/presentation/components/domain/stream-event-viewer.tsx create mode 100644 it0-web-admin/src/presentation/components/domain/task-status-badge.tsx create mode 100644 it0-web-admin/src/presentation/components/layout/sidebar.tsx create mode 100644 it0-web-admin/src/presentation/components/layout/top-bar.tsx create mode 100644 it0-web-admin/src/presentation/components/ui/button.tsx create mode 100644 it0-web-admin/src/presentation/components/ui/command-palette.tsx create mode 100644 it0-web-admin/src/presentation/components/ui/data-table.tsx create mode 100644 it0-web-admin/src/presentation/components/ui/dialog.tsx create mode 100644 it0-web-admin/src/presentation/components/ui/input.tsx create mode 100644 it0-web-admin/src/presentation/components/ui/toast.tsx create mode 100644 it0-web-admin/src/presentation/hooks/use-confirmation.ts create mode 100644 it0-web-admin/src/presentation/hooks/use-debounce.ts create mode 100644 it0-web-admin/src/presentation/hooks/use-normalise.ts create mode 100644 it0-web-admin/src/presentation/hooks/use-websocket.ts create mode 100644 it0-web-admin/src/stores/redux/middleware/audit-middleware.ts create mode 100644 it0-web-admin/src/stores/redux/slices/agent-config-slice.ts create mode 100644 it0-web-admin/src/stores/redux/slices/permission-matrix-slice.ts create mode 100644 it0-web-admin/src/stores/redux/slices/risk-rules.slice.ts create mode 100644 it0-web-admin/src/stores/redux/slices/runbook-editor-slice.ts create mode 100644 it0-web-admin/src/stores/redux/slices/session-replay.slice.ts create mode 100644 it0-web-admin/src/stores/redux/store.ts create mode 100644 it0-web-admin/src/stores/zustand/notification-store.ts create mode 100644 it0-web-admin/src/stores/zustand/tenant-store.ts create mode 100644 it0-web-admin/src/stores/zustand/terminal-store.ts create mode 100644 it0-web-admin/src/stores/zustand/theme-store.ts create mode 100644 it0-web-admin/src/stores/zustand/ui-store.ts create mode 100644 it0-web-admin/src/styles/globals.css create mode 100644 it0-web-admin/src/types/index.ts create mode 100644 it0-web-admin/tailwind.config.ts create mode 100644 it0-web-admin/tsconfig.json create mode 100644 it0_app/analysis_options.yaml create mode 100644 it0_app/android/app/src/main/java/io/flutter/plugins/GeneratedPluginRegistrant.java create mode 100644 it0_app/lib/app.dart create mode 100644 it0_app/lib/core/config/api_endpoints.dart create mode 100644 it0_app/lib/core/config/app_config.dart create mode 100644 it0_app/lib/core/errors/error_handler.dart create mode 100644 it0_app/lib/core/errors/failures.dart create mode 100644 it0_app/lib/core/network/api_result.dart create mode 100644 it0_app/lib/core/network/dio_client.dart create mode 100644 it0_app/lib/core/network/websocket_client.dart create mode 100644 it0_app/lib/core/router/app_router.dart create mode 100644 it0_app/lib/core/tenant/tenant_interceptor.dart create mode 100644 it0_app/lib/core/tenant/tenant_provider.dart create mode 100644 it0_app/lib/core/theme/app_colors.dart create mode 100644 it0_app/lib/core/theme/app_theme.dart create mode 100644 it0_app/lib/core/theme/app_typography.dart create mode 100644 it0_app/lib/core/utils/date_formatter.dart create mode 100644 it0_app/lib/core/utils/logger.dart create mode 100644 it0_app/lib/core/widgets/empty_state.dart create mode 100644 it0_app/lib/core/widgets/loading_overlay.dart create mode 100644 it0_app/lib/core/widgets/risk_level_chip.dart create mode 100644 it0_app/lib/core/widgets/status_badge.dart create mode 100644 it0_app/lib/features/agent_call/data/datasources/voice_datasource.dart create mode 100644 it0_app/lib/features/agent_call/data/repositories/voice_repository_impl.dart create mode 100644 it0_app/lib/features/agent_call/domain/entities/voice_session.dart create mode 100644 it0_app/lib/features/agent_call/domain/repositories/voice_repository.dart create mode 100644 it0_app/lib/features/agent_call/domain/usecases/end_voice_session.dart create mode 100644 it0_app/lib/features/agent_call/domain/usecases/start_voice_session.dart create mode 100644 it0_app/lib/features/agent_call/presentation/pages/agent_call_page.dart create mode 100644 it0_app/lib/features/agent_call/presentation/providers/voice_call_providers.dart create mode 100644 it0_app/lib/features/alerts/data/datasources/alerts_datasource.dart create mode 100644 it0_app/lib/features/alerts/data/repositories/alerts_repository_impl.dart create mode 100644 it0_app/lib/features/alerts/domain/entities/alert_event.dart create mode 100644 it0_app/lib/features/alerts/domain/repositories/alerts_repository.dart create mode 100644 it0_app/lib/features/alerts/presentation/pages/alerts_page.dart create mode 100644 it0_app/lib/features/alerts/presentation/providers/alerts_providers.dart create mode 100644 it0_app/lib/features/approvals/data/datasources/approvals_datasource.dart create mode 100644 it0_app/lib/features/approvals/data/repositories/approvals_repository_impl.dart create mode 100644 it0_app/lib/features/approvals/domain/entities/approval.dart create mode 100644 it0_app/lib/features/approvals/domain/repositories/approvals_repository.dart create mode 100644 it0_app/lib/features/approvals/domain/usecases/approve_command.dart create mode 100644 it0_app/lib/features/approvals/domain/usecases/get_pending_approvals.dart create mode 100644 it0_app/lib/features/approvals/domain/usecases/reject_command.dart create mode 100644 it0_app/lib/features/approvals/presentation/pages/approvals_page.dart create mode 100644 it0_app/lib/features/approvals/presentation/providers/approvals_providers.dart create mode 100644 it0_app/lib/features/auth/data/datasources/auth_remote_datasource.dart create mode 100644 it0_app/lib/features/auth/data/providers/auth_provider.dart create mode 100644 it0_app/lib/features/auth/data/providers/tenant_provider.dart create mode 100644 it0_app/lib/features/auth/data/repositories/auth_repository_impl.dart create mode 100644 it0_app/lib/features/auth/domain/entities/auth_user.dart create mode 100644 it0_app/lib/features/auth/domain/repositories/auth_repository.dart create mode 100644 it0_app/lib/features/auth/domain/usecases/login.dart create mode 100644 it0_app/lib/features/auth/domain/usecases/logout.dart create mode 100644 it0_app/lib/features/auth/domain/usecases/refresh_token.dart create mode 100644 it0_app/lib/features/auth/presentation/pages/login_page.dart create mode 100644 it0_app/lib/features/chat/data/datasources/chat_local_datasource.dart create mode 100644 it0_app/lib/features/chat/data/datasources/chat_remote_datasource.dart create mode 100644 it0_app/lib/features/chat/data/repositories/chat_repository_impl.dart create mode 100644 it0_app/lib/features/chat/domain/entities/chat_message.dart create mode 100644 it0_app/lib/features/chat/domain/entities/chat_session.dart create mode 100644 it0_app/lib/features/chat/domain/entities/stream_event.dart create mode 100644 it0_app/lib/features/chat/domain/repositories/chat_repository.dart create mode 100644 it0_app/lib/features/chat/domain/usecases/cancel_task.dart create mode 100644 it0_app/lib/features/chat/domain/usecases/get_session_history.dart create mode 100644 it0_app/lib/features/chat/domain/usecases/send_message.dart create mode 100644 it0_app/lib/features/chat/domain/usecases/start_voice_input.dart create mode 100644 it0_app/lib/features/chat/presentation/pages/chat_page.dart create mode 100644 it0_app/lib/features/chat/presentation/providers/chat_providers.dart create mode 100644 it0_app/lib/features/chat/presentation/providers/voice_providers.dart create mode 100644 it0_app/lib/features/chat/presentation/widgets/agent_thinking_indicator.dart create mode 100644 it0_app/lib/features/chat/presentation/widgets/approval_action_card.dart create mode 100644 it0_app/lib/features/chat/presentation/widgets/message_bubble.dart create mode 100644 it0_app/lib/features/chat/presentation/widgets/stream_text_widget.dart create mode 100644 it0_app/lib/features/chat/presentation/widgets/tool_execution_card.dart create mode 100644 it0_app/lib/features/chat/presentation/widgets/voice_input_button.dart create mode 100644 it0_app/lib/features/dashboard/data/datasources/dashboard_datasource.dart create mode 100644 it0_app/lib/features/dashboard/data/repositories/dashboard_repository_impl.dart create mode 100644 it0_app/lib/features/dashboard/domain/entities/dashboard_summary.dart create mode 100644 it0_app/lib/features/dashboard/domain/entities/recent_operation.dart create mode 100644 it0_app/lib/features/dashboard/domain/entities/server_status.dart create mode 100644 it0_app/lib/features/dashboard/domain/repositories/dashboard_repository.dart create mode 100644 it0_app/lib/features/dashboard/presentation/pages/dashboard_page.dart create mode 100644 it0_app/lib/features/dashboard/presentation/providers/dashboard_providers.dart create mode 100644 it0_app/lib/features/servers/data/datasources/servers_datasource.dart create mode 100644 it0_app/lib/features/servers/data/repositories/servers_repository_impl.dart create mode 100644 it0_app/lib/features/servers/domain/entities/server.dart create mode 100644 it0_app/lib/features/servers/domain/repositories/servers_repository.dart create mode 100644 it0_app/lib/features/servers/presentation/pages/servers_page.dart create mode 100644 it0_app/lib/features/servers/presentation/providers/servers_providers.dart create mode 100644 it0_app/lib/features/settings/data/datasources/settings_datasource.dart create mode 100644 it0_app/lib/features/settings/data/repositories/settings_repository_impl.dart create mode 100644 it0_app/lib/features/settings/domain/entities/app_settings.dart create mode 100644 it0_app/lib/features/settings/domain/repositories/settings_repository.dart create mode 100644 it0_app/lib/features/settings/presentation/pages/settings_page.dart create mode 100644 it0_app/lib/features/settings/presentation/providers/settings_providers.dart create mode 100644 it0_app/lib/features/standing_orders/data/datasources/standing_orders_datasource.dart create mode 100644 it0_app/lib/features/standing_orders/data/repositories/standing_orders_repository_impl.dart create mode 100644 it0_app/lib/features/standing_orders/domain/entities/standing_order.dart create mode 100644 it0_app/lib/features/standing_orders/domain/repositories/standing_orders_repository.dart create mode 100644 it0_app/lib/features/standing_orders/domain/usecases/get_standing_orders.dart create mode 100644 it0_app/lib/features/standing_orders/domain/usecases/toggle_standing_order.dart create mode 100644 it0_app/lib/features/standing_orders/presentation/pages/standing_orders_page.dart create mode 100644 it0_app/lib/features/standing_orders/presentation/providers/standing_orders_providers.dart create mode 100644 it0_app/lib/features/tasks/data/datasources/tasks_datasource.dart create mode 100644 it0_app/lib/features/tasks/data/repositories/tasks_repository_impl.dart create mode 100644 it0_app/lib/features/tasks/domain/entities/task.dart create mode 100644 it0_app/lib/features/tasks/domain/repositories/tasks_repository.dart create mode 100644 it0_app/lib/features/tasks/domain/usecases/create_task.dart create mode 100644 it0_app/lib/features/tasks/domain/usecases/get_tasks.dart create mode 100644 it0_app/lib/features/tasks/presentation/pages/tasks_page.dart create mode 100644 it0_app/lib/features/tasks/presentation/providers/tasks_providers.dart create mode 100644 it0_app/lib/features/terminal/data/datasources/terminal_datasource.dart create mode 100644 it0_app/lib/features/terminal/data/repositories/terminal_repository_impl.dart create mode 100644 it0_app/lib/features/terminal/domain/entities/terminal_session.dart create mode 100644 it0_app/lib/features/terminal/domain/repositories/terminal_repository.dart create mode 100644 it0_app/lib/features/terminal/presentation/pages/terminal_page.dart create mode 100644 it0_app/lib/features/terminal/presentation/providers/terminal_providers.dart create mode 100644 it0_app/lib/main.dart create mode 100644 it0_app/pubspec.lock create mode 100644 it0_app/pubspec.yaml create mode 100644 package.json create mode 100644 packages/gateway/Dockerfile create mode 100644 packages/gateway/config/kong.yml create mode 100644 packages/services/agent-service/package.json create mode 100644 packages/services/agent-service/src/agent.module.ts create mode 100644 packages/services/agent-service/src/application/dto/execute-task.dto.ts create mode 100644 packages/services/agent-service/src/application/dto/stream-event.dto.ts create mode 100644 packages/services/agent-service/src/application/dto/task-result.dto.ts create mode 100644 packages/services/agent-service/src/application/use-cases/approve-command.use-case.ts create mode 100644 packages/services/agent-service/src/application/use-cases/cancel-task.use-case.ts create mode 100644 packages/services/agent-service/src/application/use-cases/execute-task.use-case.ts create mode 100644 packages/services/agent-service/src/application/use-cases/get-session-history.use-case.ts create mode 100644 packages/services/agent-service/src/application/use-cases/switch-engine.use-case.ts create mode 100644 packages/services/agent-service/src/domain/entities/agent-session.entity.ts create mode 100644 packages/services/agent-service/src/domain/entities/agent-task.entity.ts create mode 100644 packages/services/agent-service/src/domain/entities/command-record.entity.ts create mode 100644 packages/services/agent-service/src/domain/entities/standing-order.entity.ts create mode 100644 packages/services/agent-service/src/domain/events/approval-required.event.ts create mode 100644 packages/services/agent-service/src/domain/events/command-blocked.event.ts create mode 100644 packages/services/agent-service/src/domain/events/task-completed.event.ts create mode 100644 packages/services/agent-service/src/domain/ports/inbound/approve-command.port.ts create mode 100644 packages/services/agent-service/src/domain/ports/inbound/execute-task.port.ts create mode 100644 packages/services/agent-service/src/domain/ports/inbound/manage-standing-order.port.ts create mode 100644 packages/services/agent-service/src/domain/ports/inbound/switch-engine.port.ts create mode 100644 packages/services/agent-service/src/domain/ports/outbound/agent-engine.port.ts create mode 100644 packages/services/agent-service/src/domain/ports/outbound/command-guard.port.ts create mode 100644 packages/services/agent-service/src/domain/ports/outbound/event-publisher.port.ts create mode 100644 packages/services/agent-service/src/domain/ports/outbound/session-repository.port.ts create mode 100644 packages/services/agent-service/src/domain/ports/outbound/stream-emitter.port.ts create mode 100644 packages/services/agent-service/src/domain/services/command-risk-classifier.ts create mode 100644 packages/services/agent-service/src/domain/services/session-manager.ts create mode 100644 packages/services/agent-service/src/domain/services/skill-manager.service.ts create mode 100644 packages/services/agent-service/src/domain/services/standing-order-extractor.ts create mode 100644 packages/services/agent-service/src/domain/value-objects/agent-engine-type.vo.ts create mode 100644 packages/services/agent-service/src/domain/value-objects/command-risk-level.vo.ts create mode 100644 packages/services/agent-service/src/domain/value-objects/session-id.vo.ts create mode 100644 packages/services/agent-service/src/domain/value-objects/task-status.vo.ts create mode 100644 packages/services/agent-service/src/infrastructure/engines/claude-api/agent-loop.ts create mode 100644 packages/services/agent-service/src/infrastructure/engines/claude-api/claude-api-engine.ts create mode 100644 packages/services/agent-service/src/infrastructure/engines/claude-api/tool-executor.ts create mode 100644 packages/services/agent-service/src/infrastructure/engines/claude-code-cli/claude-code-engine.ts create mode 100644 packages/services/agent-service/src/infrastructure/engines/claude-code-cli/cli-process-manager.ts create mode 100644 packages/services/agent-service/src/infrastructure/engines/claude-code-cli/hook-config-manager.ts create mode 100644 packages/services/agent-service/src/infrastructure/engines/claude-code-cli/stream-json-parser.ts create mode 100644 packages/services/agent-service/src/infrastructure/engines/claude-code-cli/system-prompt-builder.ts create mode 100644 packages/services/agent-service/src/infrastructure/engines/custom/custom-engine.ts create mode 100644 packages/services/agent-service/src/infrastructure/engines/engine-registry.ts create mode 100644 packages/services/agent-service/src/infrastructure/guards/command-guard.service.ts create mode 100644 packages/services/agent-service/src/infrastructure/guards/risk-patterns.ts create mode 100644 packages/services/agent-service/src/infrastructure/messaging/event-publisher.service.ts create mode 100644 packages/services/agent-service/src/infrastructure/messaging/stream-emitter.service.ts create mode 100644 packages/services/agent-service/src/infrastructure/repositories/session.repository.ts create mode 100644 packages/services/agent-service/src/infrastructure/repositories/task.repository.ts create mode 100644 packages/services/agent-service/src/interfaces/rest/controllers/agent.controller.ts create mode 100644 packages/services/agent-service/src/interfaces/rest/controllers/risk-rules.controller.ts create mode 100644 packages/services/agent-service/src/interfaces/rest/controllers/session.controller.ts create mode 100644 packages/services/agent-service/src/interfaces/ws/agent-stream.gateway.ts create mode 100644 packages/services/agent-service/src/main.ts create mode 100644 packages/services/agent-service/tsconfig.json create mode 100644 packages/services/audit-service/package.json create mode 100644 packages/services/audit-service/src/application/use-cases/query-audit-trail.use-case.ts create mode 100644 packages/services/audit-service/src/application/use-cases/record-audit.use-case.ts create mode 100644 packages/services/audit-service/src/audit.module.ts create mode 100644 packages/services/audit-service/src/domain/entities/audit-log.entity.ts create mode 100644 packages/services/audit-service/src/domain/value-objects/action-type.vo.ts create mode 100644 packages/services/audit-service/src/domain/value-objects/audit-level.vo.ts create mode 100644 packages/services/audit-service/src/infrastructure/repositories/audit-log.repository.ts create mode 100644 packages/services/audit-service/src/interfaces/rest/controllers/audit.controller.ts create mode 100644 packages/services/audit-service/src/main.ts create mode 100644 packages/services/audit-service/tsconfig.json create mode 100644 packages/services/auth-service/package.json create mode 100644 packages/services/auth-service/src/application/services/auth.service.ts create mode 100644 packages/services/auth-service/src/auth.module.ts create mode 100644 packages/services/auth-service/src/domain/entities/api-key.entity.ts create mode 100644 packages/services/auth-service/src/domain/entities/role.entity.ts create mode 100644 packages/services/auth-service/src/domain/entities/tenant.entity.ts create mode 100644 packages/services/auth-service/src/domain/entities/user.entity.ts create mode 100644 packages/services/auth-service/src/domain/value-objects/permission.vo.ts create mode 100644 packages/services/auth-service/src/domain/value-objects/role-type.vo.ts create mode 100644 packages/services/auth-service/src/infrastructure/guards/rbac.guard.ts create mode 100644 packages/services/auth-service/src/infrastructure/repositories/api-key.repository.ts create mode 100644 packages/services/auth-service/src/infrastructure/repositories/user.repository.ts create mode 100644 packages/services/auth-service/src/infrastructure/strategies/jwt.strategy.ts create mode 100644 packages/services/auth-service/src/interfaces/rest/controllers/auth.controller.ts create mode 100644 packages/services/auth-service/src/interfaces/rest/controllers/tenant.controller.ts create mode 100644 packages/services/auth-service/src/main.ts create mode 100644 packages/services/auth-service/tsconfig.json create mode 100644 packages/services/comm-service/package.json create mode 100644 packages/services/comm-service/src/comm.module.ts create mode 100644 packages/services/comm-service/src/domain/entities/channel-config.entity.ts create mode 100644 packages/services/comm-service/src/domain/entities/contact.entity.ts create mode 100644 packages/services/comm-service/src/domain/entities/escalation-policy.entity.ts create mode 100644 packages/services/comm-service/src/domain/entities/message.entity.ts create mode 100644 packages/services/comm-service/src/domain/ports/outbound/channel-adapter.port.ts create mode 100644 packages/services/comm-service/src/domain/services/escalation-engine.ts create mode 100644 packages/services/comm-service/src/domain/value-objects/channel-type.vo.ts create mode 100644 packages/services/comm-service/src/domain/value-objects/delivery-status.vo.ts create mode 100644 packages/services/comm-service/src/domain/value-objects/message-priority.vo.ts create mode 100644 packages/services/comm-service/src/infrastructure/adapters/adapter-registry.ts create mode 100644 packages/services/comm-service/src/infrastructure/adapters/email.adapter.ts create mode 100644 packages/services/comm-service/src/infrastructure/adapters/sms.adapter.ts create mode 100644 packages/services/comm-service/src/infrastructure/adapters/telegram.adapter.ts create mode 100644 packages/services/comm-service/src/infrastructure/adapters/voice-call.adapter.ts create mode 100644 packages/services/comm-service/src/infrastructure/adapters/voice-service-client.adapter.ts create mode 100644 packages/services/comm-service/src/infrastructure/adapters/websocket.adapter.ts create mode 100644 packages/services/comm-service/src/infrastructure/adapters/wechat-work.adapter.ts create mode 100644 packages/services/comm-service/src/infrastructure/repositories/contact.repository.ts create mode 100644 packages/services/comm-service/src/infrastructure/repositories/escalation-policy.repository.ts create mode 100644 packages/services/comm-service/src/infrastructure/repositories/message.repository.ts create mode 100644 packages/services/comm-service/src/interfaces/rest/controllers/channel.controller.ts create mode 100644 packages/services/comm-service/src/interfaces/rest/controllers/contact.controller.ts create mode 100644 packages/services/comm-service/src/interfaces/rest/controllers/escalation-policy.controller.ts create mode 100644 packages/services/comm-service/src/interfaces/rest/controllers/message.controller.ts create mode 100644 packages/services/comm-service/src/interfaces/ws/realtime.gateway.ts create mode 100644 packages/services/comm-service/src/main.ts create mode 100644 packages/services/comm-service/tsconfig.json create mode 100644 packages/services/inventory-service/package.json create mode 100644 packages/services/inventory-service/src/domain/entities/cluster.entity.ts create mode 100644 packages/services/inventory-service/src/domain/entities/credential.entity.ts create mode 100644 packages/services/inventory-service/src/domain/entities/server.entity.ts create mode 100644 packages/services/inventory-service/src/domain/entities/ssh-config.entity.ts create mode 100644 packages/services/inventory-service/src/domain/ports/outbound/credential-vault.port.ts create mode 100644 packages/services/inventory-service/src/domain/ports/outbound/server-repository.port.ts create mode 100644 packages/services/inventory-service/src/domain/value-objects/connection-info.vo.ts create mode 100644 packages/services/inventory-service/src/domain/value-objects/environment.vo.ts create mode 100644 packages/services/inventory-service/src/domain/value-objects/server-role.vo.ts create mode 100644 packages/services/inventory-service/src/infrastructure/crypto/credential-vault.service.ts create mode 100644 packages/services/inventory-service/src/infrastructure/repositories/cluster.repository.ts create mode 100644 packages/services/inventory-service/src/infrastructure/repositories/credential.repository.ts create mode 100644 packages/services/inventory-service/src/infrastructure/repositories/server.repository.ts create mode 100644 packages/services/inventory-service/src/interfaces/rest/controllers/cluster.controller.ts create mode 100644 packages/services/inventory-service/src/interfaces/rest/controllers/credential.controller.ts create mode 100644 packages/services/inventory-service/src/interfaces/rest/controllers/server.controller.ts create mode 100644 packages/services/inventory-service/src/inventory.module.ts create mode 100644 packages/services/inventory-service/src/main.ts create mode 100644 packages/services/inventory-service/tsconfig.json create mode 100644 packages/services/monitor-service/package.json create mode 100644 packages/services/monitor-service/src/domain/entities/alert-event.entity.ts create mode 100644 packages/services/monitor-service/src/domain/entities/alert-rule.entity.ts create mode 100644 packages/services/monitor-service/src/domain/entities/health-check-result.entity.ts create mode 100644 packages/services/monitor-service/src/domain/entities/metric-snapshot.entity.ts create mode 100644 packages/services/monitor-service/src/domain/ports/outbound/alert-dispatcher.port.ts create mode 100644 packages/services/monitor-service/src/domain/ports/outbound/metric-collector.port.ts create mode 100644 packages/services/monitor-service/src/domain/services/alert-runbook-bridge.service.ts create mode 100644 packages/services/monitor-service/src/domain/services/health-check.service.ts create mode 100644 packages/services/monitor-service/src/domain/value-objects/alert-severity.vo.ts create mode 100644 packages/services/monitor-service/src/domain/value-objects/check-type.vo.ts create mode 100644 packages/services/monitor-service/src/domain/value-objects/metric-type.vo.ts create mode 100644 packages/services/monitor-service/src/infrastructure/collectors/agent-collector.ts create mode 100644 packages/services/monitor-service/src/infrastructure/collectors/prometheus-collector.ts create mode 100644 packages/services/monitor-service/src/infrastructure/collectors/ssh-collector.ts create mode 100644 packages/services/monitor-service/src/infrastructure/repositories/alert-event.repository.ts create mode 100644 packages/services/monitor-service/src/infrastructure/repositories/alert-rule.repository.ts create mode 100644 packages/services/monitor-service/src/infrastructure/repositories/health-check-result.repository.ts create mode 100644 packages/services/monitor-service/src/infrastructure/repositories/metric-snapshot.repository.ts create mode 100644 packages/services/monitor-service/src/interfaces/rest/controllers/alert.controller.ts create mode 100644 packages/services/monitor-service/src/interfaces/rest/controllers/health-check.controller.ts create mode 100644 packages/services/monitor-service/src/main.ts create mode 100644 packages/services/monitor-service/src/monitor.module.ts create mode 100644 packages/services/monitor-service/tsconfig.json create mode 100644 packages/services/ops-service/package.json create mode 100644 packages/services/ops-service/src/application/use-cases/approve-task.use-case.ts create mode 100644 packages/services/ops-service/src/application/use-cases/create-deployment-task.use-case.ts create mode 100644 packages/services/ops-service/src/application/use-cases/create-inspection-task.use-case.ts create mode 100644 packages/services/ops-service/src/application/use-cases/create-standing-order.use-case.ts create mode 100644 packages/services/ops-service/src/application/use-cases/execute-runbook.use-case.ts create mode 100644 packages/services/ops-service/src/application/use-cases/execute-standing-order.use-case.ts create mode 100644 packages/services/ops-service/src/application/use-cases/schedule-recurring-task.use-case.ts create mode 100644 packages/services/ops-service/src/application/use-cases/update-standing-order.use-case.ts create mode 100644 packages/services/ops-service/src/domain/entities/approval-request.entity.ts create mode 100644 packages/services/ops-service/src/domain/entities/ops-task.entity.ts create mode 100644 packages/services/ops-service/src/domain/entities/runbook.entity.ts create mode 100644 packages/services/ops-service/src/domain/entities/standing-order-execution.entity.ts create mode 100644 packages/services/ops-service/src/domain/entities/standing-order.entity.ts create mode 100644 packages/services/ops-service/src/domain/ports/inbound/approve-task.port.ts create mode 100644 packages/services/ops-service/src/domain/ports/inbound/create-task.port.ts create mode 100644 packages/services/ops-service/src/domain/ports/inbound/execute-runbook.port.ts create mode 100644 packages/services/ops-service/src/domain/ports/outbound/agent-client.port.ts create mode 100644 packages/services/ops-service/src/domain/ports/outbound/audit-logger.port.ts create mode 100644 packages/services/ops-service/src/domain/ports/outbound/notification.port.ts create mode 100644 packages/services/ops-service/src/domain/ports/outbound/task-repository.port.ts create mode 100644 packages/services/ops-service/src/domain/services/approval-workflow.service.ts create mode 100644 packages/services/ops-service/src/domain/services/smart-escalation.service.ts create mode 100644 packages/services/ops-service/src/domain/services/standing-order-executor.service.ts create mode 100644 packages/services/ops-service/src/domain/value-objects/approval-status.vo.ts create mode 100644 packages/services/ops-service/src/domain/value-objects/execution-result.vo.ts create mode 100644 packages/services/ops-service/src/domain/value-objects/task-type.vo.ts create mode 100644 packages/services/ops-service/src/infrastructure/clients/agent-client.service.ts create mode 100644 packages/services/ops-service/src/infrastructure/clients/comm-client.service.ts create mode 100644 packages/services/ops-service/src/infrastructure/repositories/approval.repository.ts create mode 100644 packages/services/ops-service/src/infrastructure/repositories/runbook.repository.ts create mode 100644 packages/services/ops-service/src/infrastructure/repositories/standing-order-execution.repository.ts create mode 100644 packages/services/ops-service/src/infrastructure/repositories/standing-order.repository.ts create mode 100644 packages/services/ops-service/src/infrastructure/repositories/task.repository.ts create mode 100644 packages/services/ops-service/src/interfaces/rest/controllers/approval.controller.ts create mode 100644 packages/services/ops-service/src/interfaces/rest/controllers/runbook.controller.ts create mode 100644 packages/services/ops-service/src/interfaces/rest/controllers/standing-order.controller.ts create mode 100644 packages/services/ops-service/src/interfaces/rest/controllers/task.controller.ts create mode 100644 packages/services/ops-service/src/main.ts create mode 100644 packages/services/ops-service/src/ops.module.ts create mode 100644 packages/services/ops-service/tsconfig.json create mode 100644 packages/services/voice-service/Dockerfile create mode 100644 packages/services/voice-service/requirements.txt create mode 100644 packages/services/voice-service/src/api/__init__.py create mode 100644 packages/services/voice-service/src/api/health.py create mode 100644 packages/services/voice-service/src/api/main.py create mode 100644 packages/services/voice-service/src/api/session_router.py create mode 100644 packages/services/voice-service/src/api/twilio_webhook.py create mode 100644 packages/services/voice-service/src/config/settings.py create mode 100644 packages/services/voice-service/src/context/__init__.py create mode 100644 packages/services/voice-service/src/context/session_context.py create mode 100644 packages/services/voice-service/src/context/tool_handler.py create mode 100644 packages/services/voice-service/src/core/__init__.py create mode 100644 packages/services/voice-service/src/pipeline/__init__.py create mode 100644 packages/services/voice-service/src/pipeline/app_transport.py create mode 100644 packages/services/voice-service/src/pipeline/base_pipeline.py create mode 100644 packages/services/voice-service/src/pipeline/twilio_transport.py create mode 100644 packages/services/voice-service/src/services/outbound_call_service.py create mode 100644 packages/services/voice-service/src/stt/__init__.py create mode 100644 packages/services/voice-service/src/stt/whisper_service.py create mode 100644 packages/services/voice-service/src/tts/__init__.py create mode 100644 packages/services/voice-service/src/tts/kokoro_service.py create mode 100644 packages/services/voice-service/src/vad/__init__.py create mode 100644 packages/services/voice-service/src/vad/silero_service.py create mode 100644 packages/shared/common/package.json create mode 100644 packages/shared/common/src/constants/event-patterns.ts create mode 100644 packages/shared/common/src/constants/grpc-services.ts create mode 100644 packages/shared/common/src/decorators/tenant.decorator.ts create mode 100644 packages/shared/common/src/filters/all-exceptions.filter.ts create mode 100644 packages/shared/common/src/guards/roles.guard.ts create mode 100644 packages/shared/common/src/index.ts create mode 100644 packages/shared/common/src/interceptors/logging.interceptor.ts create mode 100644 packages/shared/common/src/interceptors/transform.interceptor.ts create mode 100644 packages/shared/common/src/interfaces/base-entity.interface.ts create mode 100644 packages/shared/common/src/interfaces/paginated-result.interface.ts create mode 100644 packages/shared/common/src/interfaces/tenant-info.interface.ts create mode 100644 packages/shared/common/src/pipes/validation.pipe.ts create mode 100644 packages/shared/common/src/utils/crypto.util.ts create mode 100644 packages/shared/common/src/utils/date.util.ts create mode 100644 packages/shared/common/src/utils/tenant-context.service.ts create mode 100644 packages/shared/common/tsconfig.json create mode 100644 packages/shared/database/package.json create mode 100644 packages/shared/database/src/data-source.ts create mode 100644 packages/shared/database/src/database.module.ts create mode 100644 packages/shared/database/src/index.ts create mode 100644 packages/shared/database/src/migrations/001-create-shared-schema.sql create mode 100644 packages/shared/database/src/migrations/002-create-tenant-schema-template.sql create mode 100644 packages/shared/database/src/migrations/003-add-missing-tables.sql create mode 100644 packages/shared/database/src/run-migrations.ts create mode 100644 packages/shared/database/src/seeds/seed.sql create mode 100644 packages/shared/database/src/tenant-aware.repository.ts create mode 100644 packages/shared/database/src/tenant-provisioning.service.ts create mode 100644 packages/shared/database/tsconfig.json create mode 100644 packages/shared/events/package.json create mode 100644 packages/shared/events/src/event-bus.ts create mode 100644 packages/shared/events/src/event-types.ts create mode 100644 packages/shared/events/src/index.ts create mode 100644 packages/shared/events/tsconfig.json create mode 100644 packages/shared/proto/package.json create mode 100644 packages/shared/proto/src/agent.proto create mode 100644 packages/shared/proto/src/comm.proto create mode 100644 packages/shared/proto/src/index.ts create mode 100644 packages/shared/proto/src/inventory.proto create mode 100644 packages/shared/proto/tsconfig.json create mode 100644 packages/shared/testing/package.json create mode 100644 packages/shared/testing/src/index.ts create mode 100644 packages/shared/testing/src/test-utils.ts create mode 100644 packages/shared/testing/tsconfig.json create mode 100644 pnpm-lock.yaml create mode 100644 pnpm-workspace.yaml create mode 100644 tsconfig.base.json create mode 100644 turbo.json diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..2a41a02 --- /dev/null +++ b/.env.example @@ -0,0 +1,38 @@ +# Database +DB_HOST=localhost +DB_PORT=5432 +DB_USERNAME=it0 +DB_PASSWORD=it0_dev +DB_DATABASE=it0 + +# Redis +REDIS_URL=redis://localhost:6379 + +# JWT +JWT_SECRET=your-jwt-secret-change-in-production +JWT_EXPIRES_IN=24h + +# Agent Engine +AGENT_ENGINE_TYPE=claude_code_cli +ANTHROPIC_API_KEY=your-api-key + +# Vault +VAULT_MASTER_KEY=your-vault-master-key-change-in-production + +# Voice Service +VOICE_SERVICE_URL=http://localhost:3008 + +# Twilio (for phone calls) +TWILIO_ACCOUNT_SID= +TWILIO_AUTH_TOKEN= +TWILIO_PHONE_NUMBER= + +# Services +AUTH_SERVICE_PORT=3001 +AGENT_SERVICE_PORT=3002 +OPS_SERVICE_PORT=3003 +INVENTORY_SERVICE_PORT=3004 +MONITOR_SERVICE_PORT=3005 +COMM_SERVICE_PORT=3006 +AUDIT_SERVICE_PORT=3007 +VOICE_SERVICE_PORT=3008 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..eb1d9ed --- /dev/null +++ b/.gitignore @@ -0,0 +1,90 @@ +# Dependencies +node_modules/ +.pnp +.pnp.js +.pnpm-store/ + +# Build outputs +dist/ +.next/ +out/ +build/ +*.tsbuildinfo + +# Environment & Secrets +.env +.env.local +.env.*.local +.env.production +.env.development +!.env.example + +# IDE +.idea/ +.vscode/ +*.swp +*.swo +*~ + +# OS +.DS_Store +Thumbs.db +Desktop.ini + +# Logs +*.log +npm-debug.log* +pnpm-debug.log* +yarn-debug.log* +yarn-error.log* + +# Testing & Coverage +coverage/ +.nyc_output/ +*.lcov + +# Flutter +it0_app/.dart_tool/ +it0_app/.flutter-plugins +it0_app/.flutter-plugins-dependencies +it0_app/.packages +it0_app/.pub/ +it0_app/build/ +it0_app/android/.gradle/ +it0_app/android/app/build/ +it0_app/android/build/ +it0_app/android/local.properties +it0_app/ios/Pods/ +it0_app/ios/.symlinks/ +it0_app/ios/Flutter/Generated.xcconfig +it0_app/ios/Flutter/flutter_export_environment.sh +*.freezed.dart +*.g.dart +*.gr.dart + +# Python +__pycache__/ +*.py[cod] +*.egg-info/ +*.egg +.eggs/ +venv/ +.venv/ + +# Models (large files) +models/ + +# Turbo +.turbo/ + +# Docker +docker-compose.override.yml + +# Claude Code +.claude/ + +# Misc +*.tmp +*.temp +.cache/ +nul diff --git a/README.md b/README.md new file mode 100644 index 0000000..c4415ec --- /dev/null +++ b/README.md @@ -0,0 +1,45 @@ +# IT0 — AI-Powered Server Cluster Operations Platform + +Intelligent operations platform that combines AI agents with human oversight for managing server clusters. + +## Architecture + +- **Backend**: NestJS microservices (TypeScript) with DDD + Clean Architecture +- **Mobile**: Flutter app with Riverpod state management +- **Web Admin**: Next.js dashboard with Zustand + React Query +- **Voice**: Python service for voice-based interaction (STT/TTS/VAD) + +## Services + +| Service | Description | +|---------|-------------| +| auth-service | Authentication, RBAC, API key management | +| agent-service | AI agent orchestration (Claude CLI + API) | +| inventory-service | Server, cluster, credential management | +| monitor-service | Metrics collection, alerting, health checks | +| ops-service | Task execution, approvals, standing orders | +| comm-service | Multi-channel notifications, escalation | +| audit-service | Audit logging, compliance trail | +| voice-service | Voice pipeline (Python) | + +## Quick Start + +```bash +# Backend +pnpm install +pnpm dev + +# Flutter +cd it0_app && flutter pub get && flutter run + +# Web Admin +cd it0-web-admin && pnpm install && pnpm dev +``` + +## Tech Stack + +- **Runtime**: Node.js 20+, Dart 3.x, Python 3.11+ +- **Database**: PostgreSQL (schema-per-tenant) +- **Cache/Events**: Redis Streams +- **AI**: Anthropic Claude (CLI + API) +- **Build**: pnpm workspaces + Turborepo diff --git a/deploy/docker/.env.example b/deploy/docker/.env.example new file mode 100644 index 0000000..a21fc1b --- /dev/null +++ b/deploy/docker/.env.example @@ -0,0 +1,9 @@ +# Required +ANTHROPIC_API_KEY=your-api-key-here +JWT_SECRET=change-this-to-a-random-string +VAULT_MASTER_KEY=change-this-to-a-random-string + +# Optional - Twilio (for phone calls) +TWILIO_ACCOUNT_SID= +TWILIO_AUTH_TOKEN= +TWILIO_PHONE_NUMBER= diff --git a/deploy/docker/docker-compose.voice.yml b/deploy/docker/docker-compose.voice.yml new file mode 100644 index 0000000..46c6ddf --- /dev/null +++ b/deploy/docker/docker-compose.voice.yml @@ -0,0 +1,22 @@ +version: '3.8' + +# GPU-enabled voice service overlay +# Usage: docker compose -f docker-compose.yml -f docker-compose.voice.yml up voice-service + +services: + voice-service: + environment: + - DEVICE=cuda + volumes: + - ../../models:/app/models + deploy: + resources: + reservations: + devices: + - driver: nvidia + count: 1 + capabilities: [gpu] + healthcheck: + test: ["CMD", "curl", "-f", "http://localhost:3008/health"] + interval: 30s + start_period: 60s diff --git a/deploy/docker/docker-compose.yml b/deploy/docker/docker-compose.yml new file mode 100644 index 0000000..fad42d0 --- /dev/null +++ b/deploy/docker/docker-compose.yml @@ -0,0 +1,228 @@ +version: '3.8' + +services: + # ===== Infrastructure ===== + postgres: + image: postgres:16-alpine + environment: + POSTGRES_USER: it0 + POSTGRES_PASSWORD: it0_dev + POSTGRES_DB: it0 + ports: + - "5432:5432" + volumes: + - postgres_data:/var/lib/postgresql/data + healthcheck: + test: ["CMD-SHELL", "pg_isready -U it0"] + interval: 10s + timeout: 5s + retries: 5 + + redis: + image: redis:7-alpine + ports: + - "6379:6379" + healthcheck: + test: ["CMD", "redis-cli", "ping"] + interval: 10s + timeout: 5s + retries: 5 + + # ===== API Gateway ===== + api-gateway: + build: ../../packages/gateway + ports: + - "8000:8000" + - "8001:8001" + depends_on: + - auth-service + - agent-service + - ops-service + - inventory-service + - monitor-service + - comm-service + - audit-service + - redis + healthcheck: + test: ["CMD", "kong", "health"] + interval: 10s + timeout: 5s + retries: 3 + networks: + - it0-network + + # ===== Backend Services ===== + auth-service: + build: ../../packages/services/auth-service + ports: + - "3001:3001" + environment: + - DB_HOST=postgres + - DB_PORT=5432 + - DB_USERNAME=it0 + - DB_PASSWORD=it0_dev + - DB_DATABASE=it0 + - REDIS_URL=redis://redis:6379 + - JWT_SECRET=${JWT_SECRET:-dev-jwt-secret} + - AUTH_SERVICE_PORT=3001 + depends_on: + postgres: + condition: service_healthy + redis: + condition: service_healthy + networks: + - it0-network + + agent-service: + build: ../../packages/services/agent-service + ports: + - "3002:3002" + environment: + - DB_HOST=postgres + - DB_PORT=5432 + - DB_USERNAME=it0 + - DB_PASSWORD=it0_dev + - DB_DATABASE=it0 + - REDIS_URL=redis://redis:6379 + - ANTHROPIC_API_KEY=${ANTHROPIC_API_KEY} + - AGENT_ENGINE_TYPE=claude_code_cli + - AGENT_SERVICE_PORT=3002 + depends_on: + postgres: + condition: service_healthy + redis: + condition: service_healthy + networks: + - it0-network + + ops-service: + build: ../../packages/services/ops-service + ports: + - "3003:3003" + environment: + - DB_HOST=postgres + - DB_PORT=5432 + - DB_USERNAME=it0 + - DB_PASSWORD=it0_dev + - DB_DATABASE=it0 + - REDIS_URL=redis://redis:6379 + - OPS_SERVICE_PORT=3003 + depends_on: + postgres: + condition: service_healthy + redis: + condition: service_healthy + networks: + - it0-network + + inventory-service: + build: ../../packages/services/inventory-service + ports: + - "3004:3004" + environment: + - DB_HOST=postgres + - DB_PORT=5432 + - DB_USERNAME=it0 + - DB_PASSWORD=it0_dev + - DB_DATABASE=it0 + - VAULT_MASTER_KEY=${VAULT_MASTER_KEY:-dev-vault-key} + - INVENTORY_SERVICE_PORT=3004 + depends_on: + postgres: + condition: service_healthy + networks: + - it0-network + + monitor-service: + build: ../../packages/services/monitor-service + ports: + - "3005:3005" + environment: + - DB_HOST=postgres + - DB_PORT=5432 + - DB_USERNAME=it0 + - DB_PASSWORD=it0_dev + - DB_DATABASE=it0 + - REDIS_URL=redis://redis:6379 + - MONITOR_SERVICE_PORT=3005 + depends_on: + postgres: + condition: service_healthy + networks: + - it0-network + + comm-service: + build: ../../packages/services/comm-service + ports: + - "3006:3006" + environment: + - DB_HOST=postgres + - DB_PORT=5432 + - DB_USERNAME=it0 + - DB_PASSWORD=it0_dev + - DB_DATABASE=it0 + - REDIS_URL=redis://redis:6379 + - TWILIO_ACCOUNT_SID=${TWILIO_ACCOUNT_SID} + - TWILIO_AUTH_TOKEN=${TWILIO_AUTH_TOKEN} + - TWILIO_PHONE_NUMBER=${TWILIO_PHONE_NUMBER} + - COMM_SERVICE_PORT=3006 + depends_on: + postgres: + condition: service_healthy + redis: + condition: service_healthy + networks: + - it0-network + + audit-service: + build: ../../packages/services/audit-service + ports: + - "3007:3007" + environment: + - DB_HOST=postgres + - DB_PORT=5432 + - DB_USERNAME=it0 + - DB_PASSWORD=it0_dev + - DB_DATABASE=it0 + - AUDIT_SERVICE_PORT=3007 + depends_on: + postgres: + condition: service_healthy + networks: + - it0-network + + voice-service: + build: ../../packages/services/voice-service + ports: + - "3008:3008" + environment: + - ANTHROPIC_API_KEY=${ANTHROPIC_API_KEY} + - AGENT_SERVICE_URL=http://agent-service:3002 + - WHISPER_MODEL=large-v3 + - KOKORO_MODEL=kokoro-82m + - DEVICE=cpu + depends_on: + - agent-service + networks: + - it0-network + + # ===== Frontend ===== + web-admin: + build: ../../it0-web-admin + ports: + - "3000:3000" + environment: + - API_BASE_URL=http://api-gateway:8000 + - NEXT_PUBLIC_API_BASE_URL=/api/proxy + - NEXT_PUBLIC_WS_URL=ws://localhost:8000 + depends_on: + - api-gateway + networks: + - it0-network + +volumes: + postgres_data: + +networks: + it0-network: + driver: bridge diff --git a/deploy/k8s/base/namespace.yaml b/deploy/k8s/base/namespace.yaml new file mode 100644 index 0000000..c1fdaa2 --- /dev/null +++ b/deploy/k8s/base/namespace.yaml @@ -0,0 +1,6 @@ +apiVersion: v1 +kind: Namespace +metadata: + name: it0 + labels: + app: it0 diff --git a/deploy/k8s/base/postgres.yaml b/deploy/k8s/base/postgres.yaml new file mode 100644 index 0000000..f54a467 --- /dev/null +++ b/deploy/k8s/base/postgres.yaml @@ -0,0 +1,57 @@ +apiVersion: apps/v1 +kind: StatefulSet +metadata: + name: postgres + namespace: it0 +spec: + serviceName: postgres + replicas: 1 + selector: + matchLabels: + app: postgres + template: + metadata: + labels: + app: postgres + spec: + containers: + - name: postgres + image: postgres:16-alpine + ports: + - containerPort: 5432 + env: + - name: POSTGRES_USER + valueFrom: + secretKeyRef: + name: db-credentials + key: username + - name: POSTGRES_PASSWORD + valueFrom: + secretKeyRef: + name: db-credentials + key: password + - name: POSTGRES_DB + value: it0 + volumeMounts: + - name: postgres-data + mountPath: /var/lib/postgresql/data + volumeClaimTemplates: + - metadata: + name: postgres-data + spec: + accessModes: ["ReadWriteOnce"] + resources: + requests: + storage: 10Gi +--- +apiVersion: v1 +kind: Service +metadata: + name: postgres + namespace: it0 +spec: + selector: + app: postgres + ports: + - port: 5432 + targetPort: 5432 diff --git a/deploy/k8s/base/redis.yaml b/deploy/k8s/base/redis.yaml new file mode 100644 index 0000000..9022c10 --- /dev/null +++ b/deploy/k8s/base/redis.yaml @@ -0,0 +1,32 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: redis + namespace: it0 +spec: + replicas: 1 + selector: + matchLabels: + app: redis + template: + metadata: + labels: + app: redis + spec: + containers: + - name: redis + image: redis:7-alpine + ports: + - containerPort: 6379 +--- +apiVersion: v1 +kind: Service +metadata: + name: redis + namespace: it0 +spec: + selector: + app: redis + ports: + - port: 6379 + targetPort: 6379 diff --git a/docs/backend-guide.md b/docs/backend-guide.md new file mode 100644 index 0000000..409122a --- /dev/null +++ b/docs/backend-guide.md @@ -0,0 +1,3095 @@ +# IT0 后端开发指导文档 + +> IT Operations Intelligent Agent — 基于 Claude Code 本体的服务器集群运维智能体 + +## 1. 项目概述 + +### 1.1 定位 + +IT0 是一个**服务器集群运维智能体平台**,核心理念是将 Claude Code 作为运维大脑,通过自建的调度层和安全层,实现 AI 驱动的自动化运维。 + +### 1.2 核心设计原则 + +- **引擎可替换**:Claude Code CLI / Claude API / 自研 Agent 可无缝切换 +- **安全第一**:三层防御模型(分级权限 + Hook 拦截 + 架构隔离) +- **人在回路**:高风险操作必须人工审批,AI 只负责"看"和"想" +- **通信多元**:支持语音、文字、短信、自动电话、社交通信、邮件等多渠道通知与交互 +- **多租户 SaaS**:从第一天起支持多租户隔离,未来可对外提供服务 + +### 1.3 技术栈 + +| 层面 | 技术选型 | +|------|---------| +| 主体语言 | TypeScript (NestJS) | +| 架构模式 | DDD + Clean Architecture + 微服务 | +| 数据库 | PostgreSQL (每个服务独立 schema) | +| 消息队列 | Redis Streams / Bull Queue | +| 服务通信 | gRPC (服务间) + REST (对外 API) + WebSocket (实时推送) | +| 容器化 | Docker + Docker Compose (开发) / Kubernetes (生产) | +| API 网关 | Kong (DB-less 模式,声明式配置) | +| 监控 | Prometheus + Grafana | +| 日志 | ELK Stack / Loki | +| AI Skills | Anthropic Claude Skills(`.claude/skills/` 目录规范) | +| 语音对话引擎 | Pipecat (Python) + faster-whisper (STT) + Kokoro-82M (TTS) + Silero VAD | +| 电话线路 | Twilio Voice(仅作为 Pipecat 的电话线路载体,拨号/接听) | + +--- + +## 2. 整体架构 + +### 2.1 系统架构图 + +``` +┌─────────────────────────────────────────────────────────────┐ +│ Flutter Android App │ +│ (语音/文字交互 + 仪表盘 + 任务审批 + 日志流) │ +└──────────────────────────┬──────────────────────────────────┘ + │ HTTPS / WebSocket + ▼ +┌─────────────────────────────────────────────────────────────┐ +│ API Gateway (Kong) │ +│ 路由 / 认证 / 限流 / 日志 │ +└──────┬────────┬────────┬────────┬────────┬────────┬─────────┘ + │ │ │ │ │ │ + ▼ ▼ ▼ ▼ ▼ ▼ + ┌─────────┐┌────────┐┌────────┐┌────────┐┌────────┐┌────────┐ + │ auth ││ agent ││ ops ││ inv ││monitor ││ comm │ + │ service ││service ││service ││service ││service ││service │ + └─────────┘└───┬────┘└────────┘└────────┘└────────┘└────────┘ + │ │ + ▼ ▼ + ┌──────────────────────────┐ ┌──────────────────────┐ + │ Agent Engine Layer │ │ 通信渠道适配器 │ + │ ┌──────────────────────┐ │ │ ┌──────┐ ┌────────┐ │ + │ │ Claude Code CLI │ │ │ │ SMS │ │ IM │ │ + │ │ Claude API │ │ │ │Email │ │ Push │ │ + │ │ Custom Agent │ │ │ └──────┘ └────────┘ │ + │ └──────────────────────┘ │ └──────────┬───────────┘ + └──────────┬───────────────┘ │ + │ ┌─────────▼────────────┐ + │ │ voice-service │ + │ │ (Pipecat + Whisper) │ + │ │ 实时语音对话引擎 │ + │ └──────────────────────┘ + │ SSH / kubectl / docker + ▼ + ┌──────────────────────────┐ + │ 目标服务器集群 │ + │ prod-1 / prod-2 / k8s │ + └──────────────────────────┘ +``` + +### 2.2 微服务拆分 + +| 服务 | 端口 | 职责 | 数据库 Schema | +|------|------|------|--------------| +| `api-gateway` | 8000 | 路由、认证、限流 | — | +| `auth-service` | 3001 | 用户认证、RBAC、API Key | `it0_auth` | +| `agent-service` | 3002 | 智能体引擎、会话管理、命令执行 | `it0_agent` | +| `ops-service` | 3003 | 运维任务、审批工作流、Runbook | `it0_ops` | +| `inventory-service` | 3004 | 资产管理、凭证管理、SSH 配置 | `it0_inventory` | +| `monitor-service` | 3005 | 健康检查、指标、告警 | `it0_monitor` | +| `comm-service` | 3006 | 多渠道通信(推送/短信/邮件/IM) | `it0_comm` | +| `audit-service` | 3007 | 操作审计、合规日志 | `it0_audit` | +| `voice-service` | 3008 | 实时语音对话引擎(Pipecat + Whisper + Kokoro) | — (无状态) | + +--- + +## 3. DDD 限界上下文详细设计 + +### 3.1 Agent Core(智能体核心)— `agent-service` + +这是整个系统的**大脑**,也是架构灵活性的关键所在。 + +#### 3.1.1 Clean Architecture 分层 + +``` +agent-service/ +├── src/ +│ ├── domain/ # 领域层(零依赖) +│ │ ├── entities/ +│ │ │ ├── agent-session.entity.ts # 会话聚合根 +│ │ │ ├── agent-task.entity.ts # 任务实体 +│ │ │ ├── command-record.entity.ts # 命令记录 +│ │ │ └── standing-order.entity.ts # ★ 驻留指令(自治任务定义) +│ │ ├── value-objects/ +│ │ │ ├── agent-engine-type.vo.ts # 引擎类型(ClaudeCode/API/Custom) +│ │ │ ├── command-risk-level.vo.ts # 风险等级(L0-L3) +│ │ │ ├── task-status.vo.ts # 任务状态 +│ │ │ └── session-id.vo.ts +│ │ ├── ports/ # 端口(接口定义) +│ │ │ ├── inbound/ +│ │ │ │ ├── execute-task.port.ts # 执行任务用例 +│ │ │ │ ├── approve-command.port.ts # 审批命令用例 +│ │ │ │ ├── switch-engine.port.ts # 切换引擎用例 +│ │ │ │ └── manage-standing-order.port.ts # ★ 驻留指令管理 +│ │ │ └── outbound/ +│ │ │ ├── agent-engine.port.ts # ★ 核心抽象 — 智能体引擎接口 +│ │ │ ├── session-repository.port.ts +│ │ │ ├── command-guard.port.ts # 命令安全守卫 +│ │ │ ├── event-publisher.port.ts +│ │ │ └── stream-emitter.port.ts # 流式输出发射器 +│ │ ├── events/ +│ │ │ ├── command-blocked.event.ts +│ │ │ ├── task-completed.event.ts +│ │ │ └── approval-required.event.ts +│ │ └── services/ +│ │ ├── command-risk-classifier.ts # 命令风险分级(领域服务) +│ │ ├── session-manager.ts +│ │ └── standing-order-extractor.ts # ★ 对话 → 驻留指令提取 +│ │ +│ ├── application/ # 应用层(用例编排) +│ │ ├── use-cases/ +│ │ │ ├── execute-task.use-case.ts +│ │ │ ├── approve-command.use-case.ts +│ │ │ ├── cancel-task.use-case.ts +│ │ │ ├── get-session-history.use-case.ts +│ │ │ └── switch-engine.use-case.ts +│ │ └── dto/ +│ │ ├── execute-task.dto.ts +│ │ ├── task-result.dto.ts +│ │ └── stream-event.dto.ts +│ │ +│ ├── infrastructure/ # 基础设施层(适配器实现) +│ │ ├── engines/ # ★ 引擎适配器(Strategy 模式) +│ │ │ ├── agent-engine.interface.ts # 统一接口 +│ │ │ ├── claude-code-cli/ +│ │ │ │ ├── claude-code-engine.ts # Claude Code CLI 适配器 +│ │ │ │ ├── cli-process-manager.ts # 进程管理(spawn/kill/timeout) +│ │ │ │ ├── stream-json-parser.ts # stream-json 输出解析 +│ │ │ │ ├── system-prompt-builder.ts # 运维专用 prompt 构建 +│ │ │ │ └── hook-config-manager.ts # .claude/settings.json 管理 +│ │ │ ├── claude-api/ +│ │ │ │ ├── claude-api-engine.ts # Claude API 直连适配器 +│ │ │ │ ├── tool-executor.ts # 工具执行器(Bash/Read/Write) +│ │ │ │ └── agent-loop.ts # 自建 Agent Loop +│ │ │ └── custom/ +│ │ │ └── custom-engine.ts # 未来自研引擎预留 +│ │ ├── guards/ # 命令安全守卫 +│ │ │ ├── command-guard.service.ts # 命令拦截实现 +│ │ │ ├── risk-patterns.ts # 风险命令正则库 +│ │ │ └── pre-tool-use-hook.py # Claude Code Hook 脚本 +│ │ ├── persistence/ +│ │ │ ├── typeorm/ +│ │ │ │ ├── session.orm-entity.ts +│ │ │ │ ├── task.orm-entity.ts +│ │ │ │ └── command-record.orm-entity.ts +│ │ │ └── repositories/ +│ │ │ └── session.repository.ts +│ │ └── messaging/ +│ │ ├── event-publisher.service.ts +│ │ └── stream-emitter.service.ts # WebSocket 流推送 +│ │ +│ └── interfaces/ # 接口层(控制器) +│ ├── rest/ +│ │ ├── agent.controller.ts +│ │ └── session.controller.ts +│ └── ws/ +│ └── agent-stream.gateway.ts # WebSocket 网关 +``` + +#### 3.1.2 ★ 核心接口:AgentEngine(引擎抽象) + +这是整个架构灵活性的**核心**,使用 **Strategy + Adapter 模式**: + +```typescript +// domain/ports/outbound/agent-engine.port.ts + +/** + * 智能体引擎端口 — 所有引擎实现必须遵循此接口 + * Claude Code CLI / Claude API / 自研 Agent 均通过此接口接入 + */ +export interface AgentEnginePort { + /** 引擎类型标识 */ + readonly engineType: AgentEngineType; + + /** + * 执行任务(流式输出) + * @returns AsyncGenerator,逐步 yield 流式事件 + */ + executeTask(params: EngineTaskParams): AsyncGenerator; + + /** 取消正在执行的任务 */ + cancelTask(sessionId: string): Promise; + + /** 继续会话(追加指令) */ + continueSession(sessionId: string, message: string): AsyncGenerator; + + /** 健康检查 */ + healthCheck(): Promise; +} + +/** 引擎任务参数 */ +export interface EngineTaskParams { + sessionId: string; + prompt: string; + systemPrompt: string; + allowedTools: string[]; + maxTurns: number; + maxBudgetUsd?: number; + /** 上下文附加信息(服务器清单、历史操作等) */ + context?: Record; + /** Skill 调用:如果指定,prompt 格式为 /{skillName} {args} */ + skill?: { + name: string; + arguments: string; + }; +} + +/** 引擎流式事件(统一格式,各适配器负责转换) */ +export type EngineStreamEvent = + | { type: 'thinking'; content: string } + | { type: 'text'; content: string } + | { type: 'tool_use'; toolName: string; input: Record } + | { type: 'tool_result'; toolName: string; output: string; isError: boolean } + | { type: 'approval_required'; command: string; riskLevel: RiskLevel; taskId: string } + | { type: 'completed'; summary: string; tokensUsed?: number } + | { type: 'error'; message: string; code: string }; +``` + +#### 3.1.3 Claude Code CLI 引擎实现 + +```typescript +// infrastructure/engines/claude-code-cli/claude-code-engine.ts + +@Injectable() +export class ClaudeCodeCliEngine implements AgentEnginePort { + readonly engineType = AgentEngineType.CLAUDE_CODE_CLI; + + constructor( + private readonly processManager: CliProcessManager, + private readonly streamParser: StreamJsonParser, + private readonly promptBuilder: SystemPromptBuilder, + private readonly configService: ConfigService, + ) {} + + async *executeTask(params: EngineTaskParams): AsyncGenerator { + const systemPromptPath = await this.promptBuilder.buildAndSave(params); + + const args = [ + '-p', params.prompt, + '--system-prompt-file', systemPromptPath, + '--output-format', 'stream-json', + '--allowedTools', params.allowedTools.join(','), + '--max-turns', String(params.maxTurns), + '--session-id', params.sessionId, + '--verbose', + '--include-partial-messages', + ]; + + if (params.maxBudgetUsd) { + args.push('--max-budget-usd', String(params.maxBudgetUsd)); + } + + const process = this.processManager.spawn('claude', args); + + for await (const rawEvent of this.streamParser.parse(process.stdout)) { + // 将 Claude Code 原始 stream-json 事件转换为统一 EngineStreamEvent + const event = this.mapToEngineEvent(rawEvent); + if (event) yield event; + } + + const exitCode = await this.processManager.waitForExit(process); + if (exitCode !== 0) { + yield { type: 'error', message: `Claude Code exited with code ${exitCode}`, code: 'CLI_EXIT_ERROR' }; + } + } + + // ... 其他方法 +} +``` + +#### 3.1.4 Claude API 引擎实现(备选) + +```typescript +// infrastructure/engines/claude-api/claude-api-engine.ts + +@Injectable() +export class ClaudeApiEngine implements AgentEnginePort { + readonly engineType = AgentEngineType.CLAUDE_API; + + constructor( + private readonly toolExecutor: ToolExecutor, // 自建的 Bash/Read/Write 执行器 + private readonly configService: ConfigService, + ) {} + + async *executeTask(params: EngineTaskParams): AsyncGenerator { + const client = new Anthropic({ apiKey: this.configService.get('ANTHROPIC_API_KEY') }); + + const tools = this.buildToolDefinitions(params.allowedTools); + let messages: MessageParam[] = [{ role: 'user', content: params.prompt }]; + + // 自建 Agent Loop + for (let turn = 0; turn < params.maxTurns; turn++) { + const stream = client.messages.stream({ + model: this.configService.get('CLAUDE_MODEL', 'claude-sonnet-4-5-20250929'), + max_tokens: 8192, + system: params.systemPrompt, + messages, + tools, + }); + + for await (const event of stream) { + const mapped = this.mapStreamEvent(event); + if (mapped) yield mapped; + } + + const response = await stream.finalMessage(); + + // 检查是否需要执行工具 + const toolUseBlocks = response.content.filter(b => b.type === 'tool_use'); + if (toolUseBlocks.length === 0) break; // 无工具调用,结束 + + // 执行工具并收集结果 + const toolResults = []; + for (const block of toolUseBlocks) { + const result = await this.toolExecutor.execute(block.name, block.input); + yield { type: 'tool_result', toolName: block.name, output: result.output, isError: result.isError }; + toolResults.push({ type: 'tool_result' as const, tool_use_id: block.id, content: result.output }); + } + + messages = [...messages, + { role: 'assistant', content: response.content }, + { role: 'user', content: toolResults }, + ]; + } + } +} +``` + +#### 3.1.5 引擎切换机制 + +```typescript +// infrastructure/engines/engine-registry.ts + +@Injectable() +export class EngineRegistry { + private engines = new Map(); + + constructor( + @Optional() private readonly cliEngine: ClaudeCodeCliEngine, + @Optional() private readonly apiEngine: ClaudeApiEngine, + @Optional() private readonly customEngine: CustomEngine, + private readonly configService: ConfigService, + ) { + if (cliEngine) this.engines.set(AgentEngineType.CLAUDE_CODE_CLI, cliEngine); + if (apiEngine) this.engines.set(AgentEngineType.CLAUDE_API, apiEngine); + if (customEngine) this.engines.set(AgentEngineType.CUSTOM, customEngine); + } + + /** 获取当前活跃引擎 */ + getActiveEngine(): AgentEnginePort { + const type = this.configService.get( + 'AGENT_ENGINE_TYPE', + AgentEngineType.CLAUDE_CODE_CLI, + ); + const engine = this.engines.get(type); + if (!engine) throw new Error(`Engine ${type} not registered`); + return engine; + } + + /** 运行时切换引擎 */ + switchEngine(type: AgentEngineType): AgentEnginePort { + const engine = this.engines.get(type); + if (!engine) throw new Error(`Engine ${type} not available`); + // 更新配置 + this.configService.set('AGENT_ENGINE_TYPE', type); + return engine; + } + + /** 列出可用引擎 */ + listAvailable(): AgentEngineType[] { + return [...this.engines.keys()]; + } +} +``` + +--- + +### 3.2 Operations(运维操作)— `ops-service` + +#### 3.2.1 目录结构 + +``` +ops-service/ +├── src/ +│ ├── domain/ +│ │ ├── entities/ +│ │ │ ├── ops-task.entity.ts # 运维任务聚合根 +│ │ │ ├── approval-request.entity.ts # 审批请求 +│ │ │ ├── runbook.entity.ts # 运维手册 +│ │ │ ├── standing-order.entity.ts # ★ 驻留指令聚合根 +│ │ │ └── standing-order-execution.entity.ts # ★ 执行记录 +│ │ ├── value-objects/ +│ │ │ ├── task-type.vo.ts # 巡检/部署/回滚/扩缩容/故障恢复 +│ │ │ ├── approval-status.vo.ts # pending/approved/rejected +│ │ │ └── execution-result.vo.ts +│ │ ├── ports/ +│ │ │ ├── inbound/ +│ │ │ │ ├── create-task.port.ts +│ │ │ │ ├── approve-task.port.ts +│ │ │ │ └── execute-runbook.port.ts +│ │ │ └── outbound/ +│ │ │ ├── task-repository.port.ts +│ │ │ ├── agent-client.port.ts # 调用 agent-service +│ │ │ ├── notification.port.ts # 调用 comm-service +│ │ │ └── audit-logger.port.ts # 调用 audit-service +│ │ └── services/ +│ │ └── approval-workflow.service.ts # 审批流程领域服务 +│ │ +│ ├── application/ +│ │ └── use-cases/ +│ │ ├── create-inspection-task.use-case.ts # 创建巡检任务 +│ │ ├── create-deployment-task.use-case.ts # 创建部署任务 +│ │ ├── approve-task.use-case.ts +│ │ ├── schedule-recurring-task.use-case.ts # 定时任务 +│ │ ├── execute-runbook.use-case.ts +│ │ ├── create-standing-order.use-case.ts # ★ 创建驻留指令 +│ │ ├── update-standing-order.use-case.ts # ★ 修改驻留指令 +│ │ └── execute-standing-order.use-case.ts # ★ 执行驻留指令 +│ │ +│ ├── infrastructure/ +│ │ ├── persistence/typeorm/ +│ │ ├── clients/ +│ │ │ ├── agent-client.service.ts # gRPC 调用 agent-service +│ │ │ └── comm-client.service.ts # gRPC 调用 comm-service +│ │ └── schedulers/ +│ │ ├── cron-scheduler.service.ts # 定时巡检调度 +│ │ └── standing-order-executor.ts # ★ 驻留指令执行引擎 +│ │ +│ └── interfaces/ +│ └── rest/ +│ ├── task.controller.ts +│ ├── approval.controller.ts +│ └── runbook.controller.ts +``` + +#### 3.2.2 审批工作流 + +```typescript +// domain/services/approval-workflow.service.ts + +export class ApprovalWorkflowService { + /** + * 审批流程: + * 1. agent-service 执行任务时遇到 Level 2 命令 + * 2. 发布 approval_required 事件 + * 3. ops-service 创建审批请求 + * 4. comm-service 通知人类(推送/短信/电话) + * 5. 人类通过 Flutter App 或回复短信/邮件 审批 + * 6. ops-service 发布 approval_granted 事件 + * 7. agent-service 继续执行 + * + * 超时策略: + * - Level 1: 5 分钟超时,自动通过 + * - Level 2: 30 分钟超时,自动拒绝 + * - 紧急模式: 管理员可设置特定 Runbook 自动审批 + */ + async processApproval(request: ApprovalRequest): Promise { + // ... + } +} +``` + +--- + +### 3.3 Inventory(资产管理)— `inventory-service` + +``` +inventory-service/ +├── src/ +│ ├── domain/ +│ │ ├── entities/ +│ │ │ ├── server.entity.ts # 服务器实体 +│ │ │ ├── cluster.entity.ts # 集群聚合根 +│ │ │ ├── credential.entity.ts # 凭证(加密存储) +│ │ │ └── ssh-config.entity.ts # SSH 配置 +│ │ ├── value-objects/ +│ │ │ ├── server-role.vo.ts # web/db/cache/worker/gateway +│ │ │ ├── environment.vo.ts # dev/staging/prod +│ │ │ └── connection-info.vo.ts # IP/端口/用户 +│ │ └── ports/ +│ │ └── outbound/ +│ │ ├── server-repository.port.ts +│ │ └── credential-vault.port.ts # 凭证保险库 +│ │ +│ ├── infrastructure/ +│ │ ├── vault/ +│ │ │ └── credential-vault.service.ts # AES-256 加密存储 +│ │ └── persistence/typeorm/ +│ │ +│ └── interfaces/ +│ └── rest/ +│ ├── server.controller.ts +│ └── cluster.controller.ts +``` + +**关键:凭证安全** + +```typescript +// infrastructure/vault/credential-vault.service.ts + +@Injectable() +export class CredentialVaultService implements CredentialVaultPort { + /** + * 凭证存储规范: + * - SSH 私钥、密码等敏感信息 AES-256-GCM 加密后存入 PostgreSQL + * - 加密密钥从环境变量 VAULT_MASTER_KEY 读取 + * - 读取时解密到内存,绝不写入日志或磁盘临时文件 + * - Agent 需要 SSH 时,通过 inventory-service API 获取临时凭证 + * (凭证有 TTL,用后即弃) + */ +} +``` + +--- + +### 3.4 Monitoring(监控告警)— `monitor-service` + +``` +monitor-service/ +├── src/ +│ ├── domain/ +│ │ ├── entities/ +│ │ │ ├── health-check.entity.ts +│ │ │ ├── alert-rule.entity.ts # 告警规则 +│ │ │ ├── alert-event.entity.ts # 告警事件 +│ │ │ └── metric-snapshot.entity.ts +│ │ ├── value-objects/ +│ │ │ ├── alert-severity.vo.ts # info/warning/critical/fatal +│ │ │ ├── check-type.vo.ts # ping/http/tcp/ssh/custom +│ │ │ └── metric-type.vo.ts # cpu/mem/disk/network/custom +│ │ └── ports/outbound/ +│ │ ├── metric-collector.port.ts # 指标采集 +│ │ └── alert-dispatcher.port.ts # 告警分发 → comm-service +│ │ +│ ├── infrastructure/ +│ │ ├── collectors/ +│ │ │ ├── ssh-collector.service.ts # SSH 远程采集 +│ │ │ ├── prometheus-collector.ts # Prometheus 拉取 +│ │ │ └── agent-collector.ts # 通过 agent-service 执行采集命令 +│ │ └── schedulers/ +│ │ └── health-check-scheduler.ts # 定时健康检查 +│ │ +│ └── interfaces/ +│ └── rest/ +│ ├── health-check.controller.ts +│ └── alert.controller.ts +``` + +**告警 → 自动修复流程:** + +``` +monitor-service 检测到异常 + ↓ 发布 AlertEvent +ops-service 接收告警 + ↓ 匹配 Runbook(自动修复手册) + ↓ 如果有匹配的 Runbook 且风险等级允许 +agent-service 执行修复 + ↓ 如果需要审批 +comm-service 通知人类(推送+短信+电话升级) + ↓ 人类审批 +agent-service 继续执行 + ↓ +monitor-service 验证恢复 +``` + +--- + +### 3.5 Communication(多渠道通信)— `comm-service` + +这是联系人类的统一出口。 + +``` +comm-service/ +├── src/ +│ ├── domain/ +│ │ ├── entities/ +│ │ │ ├── message.entity.ts # 消息聚合根 +│ │ │ ├── contact.entity.ts # 联系人 +│ │ │ ├── channel-config.entity.ts # 渠道配置 +│ │ │ └── escalation-policy.entity.ts # 升级策略 +│ │ ├── value-objects/ +│ │ │ ├── channel-type.vo.ts # push/sms/voice_call/email/telegram/wechat/voice_dialog +│ │ │ ├── message-priority.vo.ts # low/normal/high/urgent +│ │ │ └── delivery-status.vo.ts # pending/sent/delivered/failed +│ │ └── ports/outbound/ +│ │ └── channel-adapter.port.ts # ★ 通信渠道抽象接口 +│ │ +│ ├── infrastructure/ +│ │ ├── channels/ # ★ 渠道适配器(Strategy 模式) +│ │ │ ├── channel-adapter.interface.ts +│ │ │ ├── websocket/ +│ │ │ │ └── ws-channel.adapter.ts # WebSocket 实时推送 +│ │ │ ├── sms/ +│ │ │ │ └── twilio-sms.adapter.ts # Twilio SMS +│ │ │ ├── voice/ +│ │ │ │ ├── pipecat-voice.adapter.ts # ★ 主要:Pipecat 实时语音对话 +│ │ │ │ └── twilio-voice.adapter.ts # Twilio 电话线路(Pipecat 拨号载体) +│ │ │ ├── email/ +│ │ │ │ └── smtp-email.adapter.ts # SMTP 邮件 +│ │ │ ├── telegram/ +│ │ │ │ └── telegram-bot.adapter.ts # Telegram Bot +│ │ │ ├── wechat/ +│ │ │ │ └── wechat-work.adapter.ts # 企业微信 +│ │ │ └── voice-client/ +│ │ │ └── voice-service-client.ts # HTTP/gRPC 调用 voice-service +│ │ ├── escalation/ +│ │ │ └── escalation-engine.service.ts # 升级引擎 +│ │ └── persistence/typeorm/ +│ │ +│ └── interfaces/ +│ ├── rest/ +│ │ ├── message.controller.ts +│ │ └── contact.controller.ts +│ └── ws/ +│ └── realtime.gateway.ts +``` + +#### 3.5.1 通信渠道抽象 + +```typescript +// domain/ports/outbound/channel-adapter.port.ts + +export interface ChannelAdapterPort { + readonly channelType: ChannelType; + + /** 发送消息(文字/语音/富文本) */ + send(params: SendMessageParams): Promise; + + /** 接收消息(双向渠道如 Telegram/微信) */ + onMessage?(callback: (msg: IncomingMessage) => void): void; + + /** 渠道是否可用 */ + isAvailable(): Promise; +} + +export interface SendMessageParams { + to: Contact; + content: string; + priority: MessagePriority; + /** 语音电话场景:TTS 文本 */ + ttsText?: string; + /** 审批场景:附带审批链接或回复指令 */ + approvalAction?: { + taskId: string; + approveKeyword: string; // 如 "Y" 或 "approve" + rejectKeyword: string; // 如 "N" 或 "reject" + }; +} +``` + +#### 3.5.2 升级策略(Escalation) + +```typescript +// infrastructure/escalation/escalation-engine.service.ts + +/** + * 通知升级策略(分层递进): + * + * ┌─── 第一层:推送 + 电话 ─────────────────────────────────────┐ + * │ 1. WebSocket + FCM 推送到 Flutter App(通知卡片) │ + * │ 用户打开 App → Pipecat 语音对话(App 内 WebSocket 音频) │ + * │ 2. 如果 2 分钟内无响应 → Pipecat 通过 Twilio 拨出电话 │ + * │ 管理员接听 → Agent 直接开口说话(同一 Pipecat 引擎) │ + * │ "您好,prod-2 磁盘 97%,我建议清理日志,您同意吗?" │ + * │ 管理员语音回复 → Agent 理解并执行 │ + * └────────────────────────────────────────────────────────────┘ + * + * ┌─── 第二层:扩大通知 ──────────────────────────────────────┐ + * │ 如果 5 分钟内仍无响应 → 短信 + IM (Telegram/企业微信) │ + * │ 如果 10 分钟内无响应 → 通知备用联系人(重复第一层) │ + * │ 如果 20 分钟内无响应 → 邮件通知管理组(完整报告) │ + * └────────────────────────────────────────────────────────────┘ + * + * 升级策略可按告警级别自定义: + * - info: 只推送 App + * - warning: App + 短信/IM + * - critical: App + Pipecat 电话语音对话 + * - fatal: 全渠道 + 备用联系人 + 管理组 + */ +``` + +#### 3.5.3 语音交互流程(Pipecat 引擎) + +IT0 使用 **Pipecat**(GitHub 10.1k stars)作为实时语音对话引擎,自部署 STT/TTS 模型,不依赖外部语音 API。 + +**主要场景:App 内语音对话** +``` +Flutter App(麦克风录音) + ↓ WebSocket 音频流 (PCM 16kHz) +voice-service (Pipecat Pipeline) + ├── Silero VAD → 检测语音活动 + 打断 + ├── faster-whisper → STT(语音转文字) + ├── Claude API → LLM 理解 + 决策 + └── Kokoro-82M → TTS(文字转语音) + ↓ WebSocket 音频流 +Flutter App 实时播放语音回复 + ↓ 同步显示实时转写文字(辅助理解 + 留痕) +``` + +**电话场景:Pipecat 主动拨号(2分钟 App 无响应后)** +``` +voice-service (Pipecat) 发起拨号请求 + ↓ Twilio REST API: 拨出电话到管理员手机 +管理员手机响铃 → 接听 + ↓ Twilio Media Streams (WebSocket 双向音频) +voice-service (Pipecat Pipeline) ← 同一个引擎 + ├── 与 App 内语音对话使用完全相同的 Pipeline + ├── Agent 接通后立即开口:"您好,我是 IT0 Agent..." + └── 唯一区别:音频通过 Twilio 电话线而非 Flutter WebSocket + ↓ Twilio Media Streams +管理员通过电话与 Agent 语音对话 +``` + +**关键设计决策**: +- **Pipecat 是唯一的语音对话引擎**,无论音频来自 App 还是电话线 +- **Twilio 只是电话线路载体**(拨号/接听),不负责任何 STT/TTS/IVR 逻辑 +- **无 IVR 菜单**:不需要"按 1 按 2",Agent 接通就说话,管理员直接语音回复 +- **所有 STT/TTS 在本地 GPU 运行**(faster-whisper + Kokoro),延迟 < 500ms +- **支持打断(barge-in)**:用户开始说话时 Pipecat 自动暂停 TTS 播放 + +#### 3.5.4 Voice Dialogue Service(voice-service) + +voice-service 是一个 **Python 微服务**,基于 Pipecat 框架运行实时语音对话 Pipeline。 + +``` +voice-service/ # Python (FastAPI + Pipecat) +├── app/ +│ ├── main.py # FastAPI 入口 +│ ├── config.py # 配置(模型路径、端口、Claude API Key) +│ ├── pipelines/ +│ │ ├── base_pipeline.py # ★ Pipecat Pipeline 定义 +│ │ │ # VAD → STT → LLM → TTS → Audio Output +│ │ ├── app_transport.py # Flutter WebSocket 音频传输 +│ │ └── twilio_transport.py # Twilio Media Streams 音频传输 +│ ├── services/ +│ │ ├── stt_service.py # faster-whisper 配置(模型: large-v3, GPU) +│ │ ├── tts_service.py # Kokoro-82M 配置(中/英双语) +│ │ ├── vad_service.py # Silero VAD 配置 +│ │ └── llm_service.py # Pipecat AnthropicLLMService (Claude API) +│ ├── context/ +│ │ ├── session_context.py # 对话上下文管理(从 agent-service 获取) +│ │ └── tool_handler.py # Agent tool 调用转发给 agent-service +│ └── api/ +│ ├── health.py # 健康检查 +│ ├── session_router.py # 创建/结束语音对话会话 +│ └── twilio_webhook.py # Twilio Media Streams WebSocket 端点 +├── models/ # 本地模型文件 +│ ├── whisper-large-v3/ # faster-whisper 模型 +│ ├── kokoro-82m/ # Kokoro TTS 模型 +│ └── silero-vad/ # Silero VAD 模型 +├── Dockerfile +├── requirements.txt +└── docker-compose.voice.yml +``` + +```python +# app/pipelines/base_pipeline.py — Pipecat Pipeline 核心 + +from pipecat.pipeline.pipeline import Pipeline +from pipecat.pipeline.task import PipelineTask +from pipecat.services.anthropic import AnthropicLLMService +from pipecat.services.faster_whisper import FasterWhisperSTTService +from pipecat.services.kokoro import KokoroTTSService +from pipecat.vad.silero import SileroVADAnalyzer + +async def create_voice_pipeline( + transport, # AppTransport 或 TwilioTransport + session_context, # 对话上下文(驻留指令、服务器信息等) +): + """ + 创建 Pipecat 语音对话 Pipeline + - 音频输入 → VAD → STT → LLM → TTS → 音频输出 + - 支持打断:用户说话时自动停止 TTS + - 支持 tool_use:Agent 可调用 agent-service 的工具 + """ + stt = FasterWhisperSTTService( + model="large-v3", + language="zh", # 中文为主,自动检测英文 + device="cuda", + ) + + llm = AnthropicLLMService( + model="claude-sonnet-4-5-20250929", + system_prompt=session_context.system_prompt, + tools=session_context.available_tools, + ) + + tts = KokoroTTSService( + model="kokoro-82m", + voice="zh_female_1", # 中文女声 + ) + + pipeline = Pipeline([ + transport.input(), # 音频输入 + SileroVADAnalyzer(), # 语音活动检测 + stt, # 语音 → 文字 + llm, # 文字 → Agent 回复 + tts, # 回复 → 语音 + transport.output(), # 音频输出 + ]) + + return PipelineTask(pipeline, allow_interruptions=True) +``` + +```python +# app/pipelines/app_transport.py — Flutter App WebSocket 传输 + +from pipecat.transports.network.websocket import WebSocketServerTransport + +class AppTransport(WebSocketServerTransport): + """ + Flutter App 通过 WebSocket 发送/接收音频流 + - 输入:PCM 16kHz 16bit 单声道(Flutter 录音格式) + - 输出:PCM 16kHz 16bit 单声道(Flutter 播放格式) + """ + def __init__(self, websocket): + super().__init__( + websocket=websocket, + params=WebSocketServerParams( + audio_in_sample_rate=16000, + audio_out_sample_rate=16000, + vad_enabled=True, + vad_analyzer=SileroVADAnalyzer(), + ), + ) +``` + +```python +# app/pipelines/twilio_transport.py — Twilio 电话线兜底传输 + +from pipecat.transports.services.twilio import TwilioTransport + +class TwilioPhoneTransport(TwilioTransport): + """ + Twilio 电话线传输:Pipecat 主动拨号 → 管理员接听 → Agent 直接开口说话 + - Pipecat 调用 Twilio REST API 拨出电话 + - 接通后通过 Media Streams WebSocket 双向传输音频 + - Agent 接通即开始语音汇报,无 IVR 菜单 + - 音频编解码:mu-law 8kHz(电话标准)↔ PCM 16kHz(Whisper 输入) + """ + async def initiate_call(self, phone_number: str, voice_session_id: str): + """主动拨出电话(由 SmartEscalationService 触发)""" + # Twilio REST API 创建呼叫,连接到 Media Streams + pass +``` + +**Docker 配置**: +```yaml +# docker-compose.voice.yml +voice-service: + build: ./packages/services/voice-service + ports: + - "3008:3008" + environment: + - ANTHROPIC_API_KEY=${ANTHROPIC_API_KEY} + - AGENT_SERVICE_URL=http://agent-service:3002 + - WHISPER_MODEL=large-v3 + - KOKORO_MODEL=kokoro-82m + - DEVICE=cuda # GPU 加速(生产环境) + volumes: + - ./models:/app/models # 模型文件挂载 + deploy: + resources: + reservations: + devices: + - driver: nvidia + count: 1 + capabilities: [gpu] + healthcheck: + test: ["CMD", "curl", "-f", "http://localhost:3008/health"] + interval: 30s + start_period: 60s # 模型加载时间 +``` + +--- + +### 3.6 Audit(审计日志)— `audit-service` + +``` +audit-service/ +├── src/ +│ ├── domain/ +│ │ ├── entities/ +│ │ │ └── audit-log.entity.ts +│ │ └── value-objects/ +│ │ ├── action-type.vo.ts # command_executed/approval/login/config_change +│ │ └── audit-level.vo.ts +│ ├── application/ +│ │ └── use-cases/ +│ │ ├── record-audit.use-case.ts +│ │ └── query-audit-trail.use-case.ts +│ └── infrastructure/ +│ └── persistence/ +│ └── append-only-repository.ts # 只追加,不可修改/删除 +``` + +**审计日志规范:** +- 所有 agent 执行的命令、结果、时间戳 +- 所有审批操作的决策人、时间、理由 +- 所有引擎切换操作 +- **只追加(Append-Only)**,不可篡改 + +--- + +### 3.7 Auth(认证授权)— `auth-service` + +``` +auth-service/ +├── src/ +│ ├── domain/ +│ │ ├── entities/ +│ │ │ ├── user.entity.ts +│ │ │ ├── role.entity.ts +│ │ │ └── api-key.entity.ts +│ │ └── value-objects/ +│ │ ├── permission.vo.ts # 细粒度权限 +│ │ └── role-type.vo.ts # admin/operator/viewer +│ └── infrastructure/ +│ ├── jwt/ +│ │ └── jwt.strategy.ts +│ └── guards/ +│ └── rbac.guard.ts +``` + +**RBAC 权限模型:** + +| 角色 | 查看仪表盘 | 发起巡检 | 审批操作 | 管理服务器 | 切换引擎 | +|------|-----------|---------|---------|-----------|---------| +| viewer | ✅ | ❌ | ❌ | ❌ | ❌ | +| operator | ✅ | ✅ | ❌ | ❌ | ❌ | +| admin | ✅ | ✅ | ✅ | ✅ | ✅ | + +--- + +### 3.8 ★ 自治运维工作流 — 驻留指令(Standing Orders) + +这是 IT0 的**核心运维范式**:管理员通过对话定义任务 → Agent 自主执行 → 遇到无法决策的事件智能升级给人类。 + +#### 3.8.1 核心概念:驻留指令 + +**驻留指令(Standing Order)** 是管理员通过对话与 Agent 协商后生成的**持久化自治任务定义**。一旦创建,Agent 将按照定义自主执行,无需管理员持续在线。 + +``` +┌────────────────────────────────────────────────────────────────────────┐ +│ IT0 自治运维工作流 │ +│ │ +│ ┌──────────────┐ ┌──────────────┐ ┌──────────────────────┐ │ +│ │ Phase 1 │ │ Phase 2 │ │ Phase 3 │ │ +│ │ 对话定义 │───→│ 自治执行 │───→│ 智能升级 │ │ +│ │ │ │ │ │ │ │ +│ │ 管理员通过 │ │ Agent 按计划 │ │ 遇到无法决策的情况: │ │ +│ │ 文字/语音对话 │ │ 或事件触发 │ │ │ │ +│ │ 与 Agent 沟通 │ │ 自动执行任务 │ │ 致命 → 语音电话 │ │ +│ │ 任务需求 │ │ │ │ 严重 → IM 实时消息 │ │ +│ │ │ │ 在决策边界内 │ │ 警告 → 短信/推送 │ │ +│ │ Agent 提取为 │ │ 自主处理问题 │ │ 信息 → 邮件/日报 │ │ +│ │ 结构化指令 │ │ │ │ │ │ +│ │ │ │ 记录所有操作 │ │ 人类响应后继续执行 │ │ +│ │ 管理员确认 │ │ 到审计日志 │ │ 或按人类指示调整 │ │ +│ └──────────────┘ └──────────────┘ └──────────────────────┘ │ +│ │ +│ ── 管理员随时可以修改、暂停、删除驻留指令 ── │ +└────────────────────────────────────────────────────────────────────────┘ +``` + +#### 3.8.2 对话 → 指令提取(Task Definition Session) + +管理员与 Agent 的对话中,Agent 使用专门的**指令提取模式**,从自然语言中结构化提取任务定义: + +``` +管理员: "以后每天早上8点帮我巡检所有生产服务器,检查CPU、内存、磁盘用量。 + 如果磁盘超过85%,自动清理超过7天的日志。 + 如果超过95%,马上电话通知我。 + 其他异常发短信就行。" + +Agent: "收到。让我确认一下您的要求: + + 📋 驻留指令:每日生产环境巡检 + ⏰ 触发方式:每天 08:00(cron: 0 8 * * *) + 🎯 目标范围:所有 environment=prod 的服务器 + + 📌 自治操作(Agent 可自主执行): + ├─ 检查 CPU / 内存 / 磁盘使用率 + └─ 磁盘 > 85%:自动清理 7 天前的日志 + + 🚨 升级策略: + ├─ 磁盘 > 95%:立即语音电话通知 + └─ 其他异常:短信通知 + + ⚠️ 决策边界(Agent 不会自主执行的操作): + ├─ ❌ 不会重启任何服务 + ├─ ❌ 不会修改任何配置文件 + └─ ❌ 不会删除非日志文件 + + 是否确认创建?" + +管理员: "确认。另外如果CPU持续超过90%超过10分钟,也给我打电话。" + +Agent: "已更新。追加条件: + ├─ CPU > 90% 持续 10 分钟:语音电话通知 + + ✅ 驻留指令已创建,将从明天 08:00 开始执行。 + 指令ID: SO-20260208-001" +``` + +**指令提取流程(agent-service 内部)**: + +```typescript +// domain/services/standing-order-extractor.ts + +/** + * 从对话中提取驻留指令的领域服务 + * + * 流程: + * 1. 对话进行中,Agent 检测到用户意图是「定义持续性任务」 + * 2. Agent 切换到指令提取模式(System Prompt 追加指令提取 schema) + * 3. Agent 生成结构化的指令草案(JSON) + * 4. 草案经人类确认后持久化为 StandingOrder + * 5. 后续对话修改可增量更新现有指令 + * + * 关键:Agent 的 System Prompt 中包含 StandingOrder JSON Schema, + * 使 Agent 输出符合格式的指令定义,而非自由文本。 + */ +export class StandingOrderExtractorService { + /** + * 检测对话意图是否为「定义任务」 + * 关键词:以后、每天、定时、自动、帮我...检查/清理/巡检... + */ + detectTaskDefinitionIntent(message: string): boolean; + + /** + * 将 Agent 的结构化输出解析为 StandingOrder 草案 + */ + parseOrderDraft(agentOutput: string): StandingOrderDraft; + + /** + * 人类确认后,将草案转为正式驻留指令 + */ + confirmOrder(draft: StandingOrderDraft, userId: string): StandingOrder; +} +``` + +#### 3.8.3 驻留指令实体(跨 agent-service + ops-service) + +```typescript +// ops-service/domain/entities/standing-order.entity.ts + +export class StandingOrder { + id: string; // SO-{date}-{seq} + tenantId: string; + name: string; // "每日生产巡检" + description: string; // 自然语言描述 + + /** 定义此指令的对话会话ID(溯源) */ + definedInSessionId: string; + + /** ── 触发方式 ── */ + trigger: { + type: 'cron' | 'event' | 'threshold' | 'manual'; + cronExpression?: string; // "0 8 * * *" + eventType?: string; // 'alert_fired' | 'deployment_completed' + thresholdCondition?: { // 指标阈值触发 + metricType: string; + operator: '>' | '<' | '==' | '>='; + value: number; + durationSeconds: number; // 持续时间 + }; + }; + + /** ── 目标范围 ── */ + targets: { + serverIds?: string[]; // 指定服务器 + clusterIds?: string[]; // 指定集群 + allServers?: boolean; // 所有服务器 + environmentFilter?: string[]; // ['prod'] 只在生产环境 + tagFilter?: Record; // 按标签筛选 + }; + + /** ── Agent 执行指令 ── */ + agentInstructions: { + prompt: string; // 结构化的执行指令(从对话提取) + skills?: string[]; // 使用的 Skills + runbookId?: string; // 关联的 Runbook(可选) + maxRiskLevel: 0 | 1; // 自治执行允许的最高风险等级(0=只读,1=低风险写入) + maxTurns: number; // Agent 最大对话轮次 + maxBudgetUsd?: number; // 单次执行预算上限 + }; + + /** ── 决策边界(Agent 自治范围)── */ + decisionBoundary: { + /** 可自动执行的操作白名单 */ + allowedActions: string[]; // ['cleanup_logs', 'report_metrics'] + /** 必须升级给人类的情况 */ + escalateConditions: string[]; // ['disk_full', 'service_down', 'data_loss_risk'] + /** 各阈值的通知方式 */ + escalationRules: Array<{ + condition: string; // "disk_usage > 95%" 或 "cpu > 90% for 10min" + channel: 'voice_call' | 'im' | 'sms' | 'push' | 'email'; + priority: 'urgent' | 'high' | 'normal' | 'low'; + }>; + }; + + /** ── 升级策略 ── */ + escalationPolicyId: string; // 关联的 comm-service 升级策略 + + /** ── 生命周期 ── */ + status: 'active' | 'paused' | 'archived'; + validFrom: Date; + validUntil?: Date; // 可选过期时间 + createdBy: string; + lastModifiedInSessionId?: string; // 最后修改此指令的对话ID + createdAt: Date; + updatedAt: Date; +} +``` + +#### 3.8.4 自治执行引擎(ops-service + agent-service 协作) + +```typescript +// ops-service/infrastructure/schedulers/standing-order-executor.service.ts + +/** + * 驻留指令执行引擎 + * + * 职责: + * 1. 根据触发条件(cron/event/threshold)启动执行 + * 2. 创建「无人值守会话」(headless session) + * 3. 在 Agent 的决策边界内自主运行 + * 4. 记录所有操作到审计日志 + * 5. 遇到边界外情况 → 触发智能升级 + */ +@Injectable() +export class StandingOrderExecutorService { + constructor( + private readonly orderRepo: StandingOrderRepository, + private readonly agentClient: AgentClientPort, + private readonly commClient: CommClientPort, + private readonly auditLogger: AuditLoggerPort, + ) {} + + /** + * Cron 触发:Bull Queue 定时任务调度 + * 每分钟扫描所有 active 的 cron 型驻留指令,匹配到期的加入执行队列 + */ + @Cron('* * * * *') + async scanCronOrders(): Promise { + const dueOrders = await this.orderRepo.findDueCronOrders(); + for (const order of dueOrders) { + await this.executionQueue.add('execute-standing-order', { + orderId: order.id, + triggerType: 'cron', + triggeredAt: new Date(), + }); + } + } + + /** + * 事件触发:监听 Redis Streams 事件 + * alert_fired / deployment_completed 等事件匹配 event 型驻留指令 + */ + @OnEvent('alert.fired') + async onAlertFired(event: AlertEvent): Promise { + const matchingOrders = await this.orderRepo.findByEventTrigger('alert_fired'); + for (const order of matchingOrders) { + if (this.matchesTargets(order, event.serverId)) { + await this.executeOrder(order, { triggerEvent: event }); + } + } + } + + /** + * 核心执行逻辑 + */ + async executeOrder(order: StandingOrder, context: ExecutionContext): Promise { + // 1. 创建无人值守执行记录 + const execution = await this.createExecution(order); + + // 2. 构建 Agent 任务参数(注入决策边界到 System Prompt) + const taskParams = this.buildTaskParams(order, context); + + // 3. 调用 agent-service 执行(gRPC 流式调用) + try { + for await (const event of this.agentClient.executeTask(taskParams)) { + // 记录每一步操作 + await this.auditLogger.log({ + actionType: 'standing_order_step', + orderId: order.id, + executionId: execution.id, + detail: event, + }); + + // 4. 检查是否触发升级条件 + if (event.type === 'approval_required') { + await this.handleEscalation(order, execution, event); + } + + // 5. 检查自定义升级规则 + if (this.matchesEscalationRule(order, event)) { + await this.triggerCustomEscalation(order, execution, event); + } + } + + execution.status = 'completed'; + } catch (error) { + execution.status = 'failed'; + execution.error = error.message; + // 执行失败也要通知 + await this.notifyExecutionFailure(order, execution, error); + } + + await this.orderRepo.saveExecution(execution); + } + + /** + * 构建 Agent 任务参数 + * 关键:将决策边界注入到 System Prompt 中,约束 Agent 行为 + */ + private buildTaskParams(order: StandingOrder, ctx: ExecutionContext): EngineTaskParams { + const boundaryPrompt = ` +## 驻留指令执行模式 + +你正在执行驻留指令「${order.name}」(ID: ${order.id})。 +这是一次**无人值守的自治执行**,请严格遵守以下边界: + +### 允许的操作(可自主执行) +${order.decisionBoundary.allowedActions.map(a => `- ${a}`).join('\n')} + +### 禁止的操作(必须停止并请求人工干预) +${order.decisionBoundary.escalateConditions.map(c => `- 遇到「${c}」→ 立即停止,输出 ESCALATION_REQUIRED`).join('\n')} + +### 最高风险等级限制 +仅允许执行风险等级 ≤ ${order.agentInstructions.maxRiskLevel} 的命令。 +遇到更高风险的命令,输出 ESCALATION_REQUIRED 并说明原因。 + +### 执行目标 +${JSON.stringify(order.targets, null, 2)} +`; + + return { + sessionId: `so-${order.id}-${Date.now()}`, + prompt: order.agentInstructions.prompt, + systemPrompt: boundaryPrompt, + allowedTools: this.resolveAllowedTools(order), + maxTurns: order.agentInstructions.maxTurns, + maxBudgetUsd: order.agentInstructions.maxBudgetUsd, + context: { + standingOrderId: order.id, + triggerContext: ctx, + isHeadless: true, // 标记为无人值守模式 + }, + }; + } +} +``` + +#### 3.8.5 智能升级(Smart Escalation) + +当 Agent 在自治执行中遇到超出决策边界的情况时,按以下**三层递进**流程升级: + +``` +Agent 执行中检测到异常 + │ + ├── 匹配驻留指令中的 escalationRules + │ ├── condition: "disk_usage > 95%" → priority: urgent + │ ├── condition: "cpu > 90% for 10min" → priority: urgent + │ └── condition: "other_anomaly" → priority: normal + │ + ├── 如果无具体规则匹配 → 使用驻留指令关联的 escalationPolicy + │ + └── 三层递进升级 + │ + ├── ⏱ 第一层:唤醒管理员 + │ ├── ① App 推送(WebSocket + FCM)→ 全屏通知卡片 + │ │ └── 管理员打开 App →「语音对话」→ Pipecat WebSocket 语音对话 + │ │ Agent 语音汇报 + 听取指示(支持打断) + │ │ + │ ├── ② 2分钟无响应 → Pipecat 通过 Twilio 拨出电话 + │ │ 管理员接听 → Agent 直接开口说话(无 IVR 菜单): + │ │ "您好,我是 IT0 Agent。prod-2 磁盘使用率 97%, + │ │ 我建议清理 /var/log 下超过7天的日志。您同意吗?" + │ │ 管理员语音回复 → Agent 理解并执行 + │ │ (与 App 内语音对话使用同一 Pipecat 引擎) + │ │ + │ └── 管理员通过任意渠道回复后 → SmartEscalationService.onHumanResponse() + │ + ├── ⏱ 第二层:扩大通知范围(5 分钟内仍无响应) + │ ├── IM 消息(Telegram/企业微信): + │ │ 结构化消息 + 操作按钮(批准/拒绝/查看详情) + │ ├── 短信(简要描述 + 回复 Y/N 审批) + │ └── 通知备用联系人(重复第一层流程) + │ + └── ⏱ 第三层:全渠道通知(10+ 分钟无响应) + ├── 邮件:完整报告(日志摘录、指标图表、操作链接) + └── 通知管理组所有成员 +``` + +```typescript +// ops-service/domain/services/smart-escalation.service.ts + +export class SmartEscalationService { + constructor( + private readonly commClient: CommClientPort, + private readonly voiceClient: VoiceServiceClientPort, // ★ voice-service 客户端 + private readonly agentClient: AgentClientPort, + private readonly orderRepo: StandingOrderRepository, + private readonly execRepo: StandingOrderExecutionRepository, + ) {} + + /** + * 根据驻留指令的升级规则 + 当前事件,决定通知渠道和内容 + */ + async escalate( + order: StandingOrder, + execution: StandingOrderExecution, + event: EngineStreamEvent, + ): Promise { + const rule = this.matchRule(order.decisionBoundary.escalationRules, event); + const notification = this.buildNotification(order, execution, event, rule); + + // 1. 第一层:App 推送 + 语音对话预备 + await this.commClient.send({ + tenantId: order.tenantId, + channel: 'push', // 推送到 App + priority: rule?.priority ?? 'normal', + content: notification, + callbackContext: { + executionId: execution.id, + orderId: order.id, + awaitingAction: 'approve_or_reject', + }, + }); + + // 2. 预创建语音对话会话(用户打开 App 时可立即接入) + const voiceSession = await this.voiceClient.prepareSession({ + executionId: execution.id, + agentContext: { + orderId: order.id, + orderName: order.name, + eventSummary: event.summary, + sessionId: execution.sessionId, + }, + }); + + // 3. 启动超时升级调度 + await this.scheduleEscalationChain(execution, rule, voiceSession); + + // 4. 暂停执行,等待人类响应 + execution.status = 'awaiting_human'; + execution.escalatedAt = new Date(); + execution.escalationChannel = 'push'; // 初始渠道 + } + + /** + * 超时升级链:App推送 → 电话唤醒 → IM/短信 → 备用联系人 → 邮件/管理组 + */ + private async scheduleEscalationChain( + execution: StandingOrderExecution, + rule: EscalationRule | null, + voiceSession: VoiceSessionInfo, + ) { + const chain = [ + { delay: 2 * 60_000, action: 'pipecat_phone_call', params: { voiceSessionId: voiceSession.id } }, + { delay: 5 * 60_000, action: 'im_and_sms' }, + { delay: 10 * 60_000, action: 'backup_contacts' }, + { delay: 20 * 60_000, action: 'email_management_group' }, + ]; + + // 使用 Bull Queue 延迟任务实现,每一步检查是否已有响应 + for (const step of chain) { + await this.escalationQueue.add('escalation-step', { + executionId: execution.id, + ...step, + }, { delay: step.delay }); + } + } + + /** + * 人类响应回调(通过任何渠道回复后触发) + * - App 内语音对话 → voice-service 回调 + * - Pipecat 电话语音对话 → voice-service 回调(同一引擎) + * - IM 按钮/文字 → comm-service 回调 + * - 短信回复 → comm-service 回调 + */ + async onHumanResponse( + executionId: string, + response: HumanResponse, + ): Promise { + const execution = await this.execRepo.findById(executionId); + + // 取消后续升级链(人已响应) + await this.escalationQueue.removeByExecutionId(executionId); + + if (response.action === 'approve') { + await this.agentClient.continueSession(execution.sessionId, + `人工审批已通过。操作者: ${response.respondedBy},渠道: ${response.channel}。继续执行。` + ); + execution.status = 'running'; + } else if (response.action === 'reject') { + await this.agentClient.cancelTask(execution.sessionId); + execution.status = 'aborted_by_human'; + } else if (response.action === 'instruct') { + // 人类通过语音或文字给出详细指示 + await this.agentClient.continueSession(execution.sessionId, + `管理员指示(${response.channel}):${response.instruction}` + ); + execution.status = 'running'; + } + + execution.humanResponse = response; + execution.respondedBy = response.respondedBy; + execution.respondedAt = new Date(); + } +} +``` + +**Pipecat 主动拨号**(voice-service 通过 Twilio REST API 发起电话): +```python +# voice-service — 主动拨出电话 +# app/services/outbound_call_service.py + +from twilio.rest import Client as TwilioClient + +class OutboundCallService: + """ + 当 App 推送 2 分钟无响应时,SmartEscalationService 调用此服务 + → Pipecat 通过 Twilio 拨出电话 + → 管理员接听后 Pipecat 直接开始语音对话(无 IVR 菜单) + """ + + async def initiate_call( + self, + phone_number: str, + voice_session_id: str, # 预创建的语音会话 ID + agent_context: dict, # 驻留指令上下文 + ): + # 1. Twilio 拨出电话,接通后连接到 Pipecat Media Streams + call = self.twilio_client.calls.create( + to=phone_number, + from_=self.twilio_phone_number, + twiml=f''' + + + + + + ''', + ) + + # 2. Pipecat Pipeline 在 Media Streams 连接时自动启动 + # Agent 接通即开口说话:"您好,我是 IT0 Agent..." + # 无需任何 IVR 菜单或按键操作 + return call.sid +``` + +```typescript +// comm-service — 触发 Pipecat 拨号 +// SmartEscalationService 在 2 分钟超时后调用 + +async function triggerPipecatCall( + voiceSessionId: string, + contact: Contact, +) { + // 调用 voice-service API,让 Pipecat 拨出电话 + await this.voiceClient.initiateCall({ + phoneNumber: contact.phone, + voiceSessionId, + // Pipecat 接通后 Agent 直接开口说话 + // 使用与 App 内完全相同的 Pipeline + }); +} +``` + +#### 3.8.6 对话修改驻留指令 + +管理员可以随时通过对话修改已有的驻留指令: + +``` +管理员: "把每日巡检的时间改成早上7点,另外磁盘清理阈值从85%降到80%。" + +Agent: "已更新驻留指令 SO-20260208-001: + ├─ ⏰ 触发时间:08:00 → 07:00(cron: 0 7 * * *) + └─ 📌 磁盘清理阈值:85% → 80% + + 其他配置保持不变。变更已生效。" + +管理员: "暂停这个指令,我这周在做服务器迁移。" + +Agent: "已暂停驻留指令 SO-20260208-001「每日生产巡检」。 + ⏸ 状态:active → paused + 需要恢复时告诉我即可。" +``` + +**实现要点**: +- Agent System Prompt 中包含当前租户所有活跃的驻留指令列表 +- Agent 检测到修改意图时,调用 `update_standing_order` tool +- 所有修改记录关联对话 session ID(完整溯源) +- 修改操作写入审计日志 + +#### 3.8.7 执行结果汇报 + +``` +┌─────────────────────────────────────────────────────────────────┐ +│ 驻留指令执行汇报方式 │ +│ │ +│ ┌───────────────────┐ │ +│ │ 正常完成 │ → 静默记录审计日志 │ +│ │(一切正常) │ 可在 Web Admin / Flutter 查看 │ +│ └───────────────────┘ │ +│ │ +│ ┌───────────────────┐ │ +│ │ 有发现但已自治处理 │ → 推送通知 + 执行摘要 │ +│ │(如自动清理了日志) │ "已清理 prod-2 的 3.2GB 旧日志" │ +│ └───────────────────┘ │ +│ │ +│ ┌───────────────────┐ │ +│ │ 需人工决策 │ → 按 escalationRules 升级 │ +│ │(超出决策边界) │ 电话/IM/短信/邮件 │ +│ └───────────────────┘ │ +│ │ +│ ┌───────────────────┐ │ +│ │ 执行失败 │ → 立即推送 + 短信 │ +│ │(Agent 异常/超时) │ 含错误详情和建议 │ +│ └───────────────────┘ │ +│ │ +│ ── 日报/周报 ── │ +│ 每日 20:00 汇总当天所有驻留指令执行情况 │ +│ 推送到 Flutter App / 邮件 │ +└─────────────────────────────────────────────────────────────────┘ +``` + +#### 3.8.8 相关 Tools(agent-service 注册的工具) + +```typescript +// agent-service 的 tool 定义(供 Agent 调用) + +const standingOrderTools = [ + { + name: 'create_standing_order', + description: '创建新的驻留指令(当用户要求定义持续性/定时任务时使用)', + input_schema: { /* StandingOrderDraft JSON Schema */ }, + }, + { + name: 'update_standing_order', + description: '修改已有的驻留指令(当用户要求调整任务参数时使用)', + input_schema: { + orderId: { type: 'string' }, + changes: { /* 部分更新的字段 */ }, + }, + }, + { + name: 'pause_standing_order', + description: '暂停驻留指令(用户要求暂停某个定时任务时使用)', + input_schema: { orderId: { type: 'string' } }, + }, + { + name: 'resume_standing_order', + description: '恢复已暂停的驻留指令', + input_schema: { orderId: { type: 'string' } }, + }, + { + name: 'list_standing_orders', + description: '列出当前所有驻留指令及其状态', + input_schema: { statusFilter: { type: 'string', enum: ['active', 'paused', 'all'] } }, + }, + { + name: 'report_escalation', + description: '无人值守执行中遇到超出决策边界的情况,请求人工干预', + input_schema: { + severity: { type: 'string', enum: ['fatal', 'critical', 'warning', 'info'] }, + situation: { type: 'string', description: '当前状况描述' }, + suggestedAction: { type: 'string', description: 'Agent 建议的操作' }, + requiresImmediate: { type: 'boolean' }, + }, + }, +]; +``` + +--- + +## 4. 数据库设计(PostgreSQL) + +> **多租户说明**:根据 §7 的 Schema-per-Tenant 策略,以下业务表存在于每个租户的独立 Schema(`it0_t_{tenantId}`)中。 +> 所有表额外包含 `tenant_id` 列作为**防御性纵深**——即使 Schema 隔离失败,行级 `tenant_id` 仍可防止数据泄露。 +> 公共管理表(`tenants`, `plans` 等)位于 `it0_shared` Schema,参见 §7.5。 + +### 4.1 agent-service — `it0_agent` schema + +```sql +-- 智能体会话 +CREATE TABLE agent_sessions ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + tenant_id VARCHAR(20) NOT NULL, -- ★ 防御性纵深:冗余租户标识 + engine_type VARCHAR(20) NOT NULL, -- 'claude_code_cli' | 'claude_api' | 'custom' + status VARCHAR(20) NOT NULL DEFAULT 'active', + system_prompt TEXT, + created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), + updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), + metadata JSONB DEFAULT '{}' +); + +-- 命令执行记录 +CREATE TABLE command_records ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + tenant_id VARCHAR(20) NOT NULL, + session_id UUID NOT NULL REFERENCES agent_sessions(id), + command TEXT NOT NULL, + risk_level SMALLINT NOT NULL DEFAULT 0, -- 0-3 + status VARCHAR(20) NOT NULL, -- 'executed' | 'blocked' | 'approved' | 'rejected' + output TEXT, + error TEXT, + approved_by UUID, -- 审批人 + executed_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), + duration_ms INTEGER +); + +CREATE INDEX idx_command_records_session ON command_records(session_id); +CREATE INDEX idx_command_records_risk ON command_records(risk_level) WHERE risk_level >= 2; +``` + +### 4.2 ops-service — `it0_ops` schema + +```sql +-- 运维任务 +CREATE TABLE ops_tasks ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + tenant_id VARCHAR(20) NOT NULL, + type VARCHAR(30) NOT NULL, -- 'inspection' | 'deployment' | 'rollback' | 'scaling' | 'recovery' + title VARCHAR(200) NOT NULL, + description TEXT, + status VARCHAR(20) NOT NULL DEFAULT 'pending', + priority SMALLINT NOT NULL DEFAULT 1, + target_servers UUID[] NOT NULL, -- 目标服务器 ID 列表 + runbook_id UUID, + agent_session_id UUID, + created_by UUID NOT NULL, + created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), + completed_at TIMESTAMPTZ, + result_summary TEXT +); + +-- 审批请求 +CREATE TABLE approval_requests ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + tenant_id VARCHAR(20) NOT NULL, + task_id UUID NOT NULL REFERENCES ops_tasks(id), + command TEXT NOT NULL, + risk_level SMALLINT NOT NULL, + status VARCHAR(20) NOT NULL DEFAULT 'pending', + decided_by UUID, + decided_at TIMESTAMPTZ, + reason TEXT, + expires_at TIMESTAMPTZ NOT NULL, + notification_channels VARCHAR(20)[] -- 已通知的渠道 +); + +-- 运维手册(Runbook) +CREATE TABLE runbooks ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + tenant_id VARCHAR(20) NOT NULL, + name VARCHAR(200) NOT NULL, + description TEXT, + trigger_type VARCHAR(30), -- 'manual' | 'alert' | 'schedule' + prompt_template TEXT NOT NULL, -- Agent 执行的 prompt 模板 + allowed_tools VARCHAR(50)[] NOT NULL, + max_risk_level SMALLINT NOT NULL DEFAULT 1, -- 此 Runbook 允许的最高风险等级 + auto_approve BOOLEAN NOT NULL DEFAULT FALSE, + is_active BOOLEAN NOT NULL DEFAULT TRUE, + created_at TIMESTAMPTZ NOT NULL DEFAULT NOW() +); + +-- ★ 驻留指令(Standing Orders)— 自治运维任务定义 +CREATE TABLE standing_orders ( + id VARCHAR(30) PRIMARY KEY, -- SO-{date}-{seq} + tenant_id VARCHAR(20) NOT NULL, + name VARCHAR(200) NOT NULL, + description TEXT, + -- 对话溯源 + defined_in_session_id UUID NOT NULL, -- 定义此指令的对话ID + last_modified_in_session_id UUID, -- 最后修改的对话ID + -- 触发方式 + trigger_type VARCHAR(20) NOT NULL, -- 'cron' | 'event' | 'threshold' | 'manual' + trigger_config JSONB NOT NULL, -- { cronExpression, eventType, thresholdCondition } + -- 目标范围 + targets JSONB NOT NULL, -- { serverIds, clusterIds, allServers, environmentFilter, tagFilter } + -- Agent 执行参数 + agent_prompt TEXT NOT NULL, -- 从对话提取的结构化执行指令 + agent_skills VARCHAR(100)[], -- 使用的 Skills 名称列表 + runbook_id UUID REFERENCES runbooks(id), -- 关联的 Runbook(可选) + max_risk_level SMALLINT NOT NULL DEFAULT 0, -- 自治执行最高风险等级(0=只读,1=低风险写入) + max_turns INTEGER NOT NULL DEFAULT 20, + max_budget_usd NUMERIC(6,2), + -- 决策边界 + decision_boundary JSONB NOT NULL, -- { allowedActions, escalateConditions, escalationRules } + -- 升级策略 + escalation_policy_id UUID, + -- 生命周期 + status VARCHAR(20) NOT NULL DEFAULT 'active', -- 'active' | 'paused' | 'archived' + valid_from TIMESTAMPTZ NOT NULL DEFAULT NOW(), + valid_until TIMESTAMPTZ, + -- 元信息 + created_by UUID NOT NULL, + created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), + updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW() +); + +CREATE INDEX idx_standing_orders_status ON standing_orders(status) WHERE status = 'active'; +CREATE INDEX idx_standing_orders_trigger ON standing_orders(trigger_type); + +-- 驻留指令执行记录 +CREATE TABLE standing_order_executions ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + tenant_id VARCHAR(20) NOT NULL, + order_id VARCHAR(30) NOT NULL REFERENCES standing_orders(id), + -- 触发信息 + trigger_type VARCHAR(20) NOT NULL, + trigger_context JSONB, -- 触发上下文(告警事件、cron 时间等) + -- 执行状态 + session_id VARCHAR(100), -- Agent 会话ID + status VARCHAR(20) NOT NULL DEFAULT 'running', + -- 'running' | 'completed' | 'failed' | 'awaiting_human' | 'aborted_by_human' + -- 结果 + result_summary TEXT, -- Agent 执行摘要 + actions_taken JSONB DEFAULT '[]', -- 执行的操作列表 + error TEXT, + -- 升级信息 + escalated_at TIMESTAMPTZ, + escalation_channel VARCHAR(20), -- 升级使用的渠道 + human_response JSONB, -- 人类响应内容 + responded_by UUID, + responded_at TIMESTAMPTZ, + -- 时间 + started_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), + completed_at TIMESTAMPTZ, + duration_ms INTEGER +); + +CREATE INDEX idx_so_executions_order ON standing_order_executions(order_id, started_at DESC); +CREATE INDEX idx_so_executions_status ON standing_order_executions(status) WHERE status IN ('running', 'awaiting_human'); +``` + +### 4.3 inventory-service — `it0_inventory` schema + +```sql +-- 服务器 +CREATE TABLE servers ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + tenant_id VARCHAR(20) NOT NULL, + name VARCHAR(100) NOT NULL UNIQUE, + host VARCHAR(255) NOT NULL, -- IP 或主机名(内网 IP 也可以) + port INTEGER NOT NULL DEFAULT 22, + environment VARCHAR(20) NOT NULL, -- 'dev' | 'staging' | 'prod' + role VARCHAR(30) NOT NULL, -- 'web' | 'db' | 'cache' | 'worker' | 'gateway' + cluster_id UUID REFERENCES clusters(id), + ssh_user VARCHAR(50) NOT NULL, -- SSH 登录用户名 + credential_id UUID NOT NULL REFERENCES credentials(id), -- SSH 密钥或密码 + -- ★ SSH ProxyJump 跳板机配置 + network_type VARCHAR(20) NOT NULL DEFAULT 'public', -- 'public' | 'private'(内网需跳板机) + jump_server_id UUID REFERENCES servers(id), -- 跳板机(自引用,NULL=直连) + -- 额外 SSH 选项 + ssh_options JSONB DEFAULT '{}', -- 自定义 SSH 参数,如 StrictHostKeyChecking 等 + tags JSONB DEFAULT '{}', + status VARCHAR(20) NOT NULL DEFAULT 'active', + description TEXT, + created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), + updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW() +); + +CREATE INDEX idx_servers_environment ON servers(environment); +CREATE INDEX idx_servers_cluster ON servers(cluster_id); + +-- 集群 +CREATE TABLE clusters ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + tenant_id VARCHAR(20) NOT NULL, + name VARCHAR(100) NOT NULL UNIQUE, + type VARCHAR(20) NOT NULL, -- 'bare_metal' | 'k8s' | 'docker_swarm' + environment VARCHAR(20) NOT NULL, + description TEXT, + kubeconfig TEXT, -- 加密存储 + created_at TIMESTAMPTZ NOT NULL DEFAULT NOW() +); + +-- 凭证(加密存储) +CREATE TABLE credentials ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + tenant_id VARCHAR(20) NOT NULL, + name VARCHAR(100) NOT NULL, + type VARCHAR(20) NOT NULL, -- 'ssh_key' | 'ssh_password' | 'token' | 'kubeconfig' + encrypted_value BYTEA NOT NULL, -- AES-256-GCM 加密 + iv BYTEA NOT NULL, + -- 元信息(非敏感) + fingerprint VARCHAR(100), -- SSH key 指纹(用于展示,不含私钥) + key_type VARCHAR(20), -- 'rsa' | 'ed25519' | 'ecdsa'(仅 ssh_key 类型) + created_by UUID, + created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), + expires_at TIMESTAMPTZ, + last_used_at TIMESTAMPTZ +); + +/** + * SSH 连接构建逻辑(agent-service 中实现): + * + * 1. 查询目标服务器:server = getServer(serverId) + * 2. 查询凭证:credential = getCredential(server.credential_id) + * 3. 判断连接方式: + * + * A) 直连(network_type = 'public' 或 jump_server_id = NULL): + * ssh -i /tmp/key_{uuid} -p {port} {ssh_user}@{host} + * + * B) 跳板机(network_type = 'private' 且 jump_server_id != NULL): + * jumpServer = getServer(server.jump_server_id) + * jumpCred = getCredential(jumpServer.credential_id) + * ssh -J {jump_user}@{jump_host}:{jump_port} -i /tmp/key_{uuid} -p {port} {ssh_user}@{host} + * + * C) 多级跳板(jump_server 自身也有 jump_server_id): + * 递归构建 ProxyJump 链:ssh -J jump1,jump2 target + * + * 4. 凭证通过临时文件注入(TTL=5min,用后删除) + * 5. 额外 SSH 选项从 server.ssh_options 读取 + */ +``` + +### 4.4 monitor-service — `it0_monitor` schema + +```sql +-- 告警规则 +CREATE TABLE alert_rules ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + tenant_id VARCHAR(20) NOT NULL, + name VARCHAR(200) NOT NULL, + metric_type VARCHAR(30) NOT NULL, -- 'cpu' | 'memory' | 'disk' | 'network' | 'custom' + condition JSONB NOT NULL, -- { "operator": ">", "threshold": 90, "duration_seconds": 300 } + severity VARCHAR(20) NOT NULL, -- 'info' | 'warning' | 'critical' | 'fatal' + target_servers UUID[], -- NULL = 所有服务器 + runbook_id UUID, -- 自动修复关联的 Runbook + is_active BOOLEAN NOT NULL DEFAULT TRUE, + created_at TIMESTAMPTZ NOT NULL DEFAULT NOW() +); + +-- 告警事件 +CREATE TABLE alert_events ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + tenant_id VARCHAR(20) NOT NULL, + rule_id UUID NOT NULL REFERENCES alert_rules(id), + server_id UUID NOT NULL, + severity VARCHAR(20) NOT NULL, + message TEXT NOT NULL, + metric_value DOUBLE PRECISION, + status VARCHAR(20) NOT NULL DEFAULT 'firing', -- 'firing' | 'resolved' | 'acknowledged' + fired_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), + resolved_at TIMESTAMPTZ, + acknowledged_by UUID +); + +-- 指标快照(时序数据,定期归档) +CREATE TABLE metric_snapshots ( + id BIGSERIAL PRIMARY KEY, + tenant_id VARCHAR(20) NOT NULL, + server_id UUID NOT NULL, + metric_type VARCHAR(30) NOT NULL, + value DOUBLE PRECISION NOT NULL, + recorded_at TIMESTAMPTZ NOT NULL DEFAULT NOW() +); + +-- 分区表优化(按月) +-- CREATE TABLE metric_snapshots_2026_02 PARTITION OF metric_snapshots +-- FOR VALUES FROM ('2026-02-01') TO ('2026-03-01'); +``` + +### 4.5 comm-service — `it0_comm` schema + +```sql +-- 联系人 +CREATE TABLE contacts ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + tenant_id VARCHAR(20) NOT NULL, + user_id UUID NOT NULL, + name VARCHAR(100) NOT NULL, + phone VARCHAR(20), -- 短信/电话 + email VARCHAR(200), + telegram_id VARCHAR(100), + wechat_id VARCHAR(100), + preferred_channel VARCHAR(20) NOT NULL DEFAULT 'websocket', + is_active BOOLEAN NOT NULL DEFAULT TRUE +); + +-- 消息记录 +CREATE TABLE messages ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + tenant_id VARCHAR(20) NOT NULL, + contact_id UUID NOT NULL REFERENCES contacts(id), + channel_type VARCHAR(20) NOT NULL, + direction VARCHAR(10) NOT NULL, -- 'outbound' | 'inbound' + content TEXT NOT NULL, + priority VARCHAR(10) NOT NULL DEFAULT 'normal', + delivery_status VARCHAR(20) NOT NULL DEFAULT 'pending', + related_task_id UUID, -- 关联的运维任务 + sent_at TIMESTAMPTZ, + delivered_at TIMESTAMPTZ, + created_at TIMESTAMPTZ NOT NULL DEFAULT NOW() +); + +-- 升级策略 +CREATE TABLE escalation_policies ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + tenant_id VARCHAR(20) NOT NULL, + name VARCHAR(100) NOT NULL, + severity VARCHAR(20) NOT NULL, -- 匹配告警级别 + steps JSONB NOT NULL, -- [{"delay_seconds": 0, "channel": "websocket"}, {"delay_seconds": 120, "channel": "sms"}, ...] + is_default BOOLEAN NOT NULL DEFAULT FALSE +); +``` + +### 4.6 audit-service — `it0_audit` schema + +```sql +-- 审计日志(只追加,不可修改/删除) +CREATE TABLE audit_logs ( + id BIGSERIAL PRIMARY KEY, + tenant_id VARCHAR(20) NOT NULL, + action_type VARCHAR(50) NOT NULL, -- 'command_executed' | 'approval_decided' | 'engine_switched' | etc. + actor_type VARCHAR(20) NOT NULL, -- 'agent' | 'user' | 'system' + actor_id VARCHAR(100), + resource_type VARCHAR(50), -- 'server' | 'task' | 'session' + resource_id VARCHAR(100), + detail JSONB NOT NULL, + ip_address INET, + created_at TIMESTAMPTZ NOT NULL DEFAULT NOW() +); + +-- 性能索引 +CREATE INDEX idx_audit_logs_action ON audit_logs(action_type, created_at); +CREATE INDEX idx_audit_logs_actor ON audit_logs(actor_id, created_at); +CREATE INDEX idx_audit_logs_resource ON audit_logs(resource_type, resource_id); + +-- 防篡改:REVOKE DELETE, UPDATE ON audit_logs FROM app_user; +``` + +--- + +## 5. 服务间通信 + +### 5.1 通信矩阵 + +``` + ┌─────────────────────────────────────────────┐ + │ 事件总线 (Redis Streams) │ + └──┬────┬────┬────┬────┬────┬────┬────────────┘ + │ │ │ │ │ │ │ + ┌────┴─┐ ┌┴────┐ ┌─┴──┐ ┌┴───┐ ┌─┴──┐ ┌──┴──┐ + │auth │ │agent│ │ops │ │inv │ │mon │ │comm │ + └──────┘ └──┬──┘ └─┬──┘ └────┘ └──┬─┘ └─────┘ + │ │ │ + gRPC 同步调用: │ + ops → agent (执行任务) │ + agent → inv (获取凭证) │ + mon → ops (触发自动修复) ───────────┘ + ops → comm (通知人类) +``` + +### 5.2 关键事件流 + +```typescript +// 事件定义(所有服务共享的 proto/event 包) + +// agent-service 发布 +interface CommandExecutedEvent { + sessionId: string; + command: string; + riskLevel: number; + output: string; + timestamp: Date; +} + +interface ApprovalRequiredEvent { + taskId: string; + command: string; + riskLevel: number; + targetServer: string; + expiresAt: Date; +} + +// monitor-service 发布 +interface AlertFiredEvent { + ruleId: string; + serverId: string; + severity: 'info' | 'warning' | 'critical' | 'fatal'; + message: string; + metricValue: number; +} + +// ops-service 发布 +interface TaskCompletedEvent { + taskId: string; + result: 'success' | 'failure' | 'partial'; + summary: string; +} +``` + +--- + +## 6. 安全架构 + +### 6.1 三层防御模型 + +``` +Layer 1: 命令分级(agent-service 领域层) + ├── Level 0: 只读命令 → 自动执行 + ├── Level 1: 低风险写入 → 前端确认 + ├── Level 2: 高风险操作 → 多渠道审批 + └── Level 3: 禁止命令 → 直接拦截 + +Layer 2: Hook 拦截(Claude Code PreToolUse) + └── pre-tool-use-hook.py + ├── 正则匹配危险模式 + ├── 写入审批队列 + └── 返回 block/allow + +Layer 3: 架构隔离(目标服务器侧) + ├── 受限 SSH 用户 (rbash) + ├── K8s RBAC (ops-agent ClusterRole) + ├── 数据库只读账号 + └── 网络隔离 (Security Group) +``` + +### 6.2 凭证安全流程 + +``` +agent-service 需要 SSH 到 prod-1 + ↓ gRPC 请求 +inventory-service 接收请求 + ↓ 验证调用者权限 + ↓ 从 PostgreSQL 读取加密凭证 + ↓ 内存中解密 + ↓ 生成临时凭证(TTL=5min) + ↓ 返回 +agent-service 使用临时凭证执行 SSH + ↓ 完成后 +临时凭证自动过期 +``` + +--- + +## 7. 多租户 SaaS 架构 + +### 7.1 隔离策略 + +采用 **Schema-per-Tenant**(同一 PostgreSQL 实例,每个租户独立 Schema)+ **行级隔离**混合模式: + +``` +┌─────────────────────────────────────────────────────────────┐ +│ PostgreSQL 实例 │ +│ │ +│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ +│ │ it0_shared │ │ it0_t_001 │ │ it0_t_002 │ ... │ +│ │ (公共表) │ │ (租户A数据) │ │ (租户B数据) │ │ +│ │ │ │ │ │ │ │ +│ │ tenants │ │ servers │ │ servers │ │ +│ │ plans │ │ credentials │ │ credentials │ │ +│ │ billing │ │ tasks │ │ tasks │ │ +│ │ global_cfg │ │ sessions │ │ sessions │ │ +│ └─────────────┘ └─────────────┘ └─────────────┘ │ +└─────────────────────────────────────────────────────────────┘ +``` + +| 表类别 | 隔离方式 | 说明 | +|--------|---------|------| +| tenants, plans, billing | 公共 Schema (`it0_shared`) | 跨租户的 SaaS 管理数据 | +| servers, credentials, tasks, sessions, skills, runbooks, alerts, contacts, audit_logs | 租户独立 Schema (`it0_t_{tenantId}`) | 完全隔离,互不可见 | + +### 7.2 租户上下文传播(AsyncLocalStorage) + +所有微服务共享同一个 **TenantContext** 模式,基于 Node.js `AsyncLocalStorage`: + +```typescript +// packages/shared/tenant/tenant-context.service.ts + +import { AsyncLocalStorage } from 'async_hooks'; + +export interface TenantInfo { + tenantId: string; + tenantName: string; + plan: 'free' | 'pro' | 'enterprise'; + schemaName: string; // 'it0_t_{tenantId}' +} + +export class TenantContextService { + private static storage = new AsyncLocalStorage(); + + /** 设置当前请求的租户上下文 */ + static run(tenant: TenantInfo, fn: () => T): T { + return this.storage.run(tenant, fn); + } + + /** 获取当前租户 */ + static getTenant(): TenantInfo { + const tenant = this.storage.getStore(); + if (!tenant) throw new Error('Tenant context not initialized'); + return tenant; + } + + static getTenantId(): string { + return this.getTenant().tenantId; + } + + static getSchemaName(): string { + return this.getTenant().schemaName; + } +} +``` + +### 7.3 请求级租户注入 + +```typescript +// packages/shared/tenant/tenant.middleware.ts + +/** + * NestJS 中间件:从 JWT / API Key / 请求头提取 tenantId + * 注入到 AsyncLocalStorage,后续所有数据库操作自动切换 Schema + */ +@Injectable() +export class TenantMiddleware implements NestMiddleware { + constructor(private readonly tenantRepo: TenantRepository) {} + + async use(req: Request, res: Response, next: NextFunction) { + // 优先级:JWT claim > X-Tenant-Id header > API Key 关联 + const tenantId = this.extractTenantId(req); + if (!tenantId) throw new UnauthorizedException('Tenant not identified'); + + const tenant = await this.tenantRepo.findById(tenantId); + if (!tenant) throw new NotFoundException('Tenant not found'); + + TenantContextService.run({ + tenantId: tenant.id, + tenantName: tenant.name, + plan: tenant.plan, + schemaName: `it0_t_${tenant.id}`, + }, () => next()); + } +} +``` + +### 7.4 TypeORM 动态 Schema 切换 + +```typescript +// packages/shared/tenant/tenant-aware.repository.ts + +/** + * 所有租户隔离的 Repository 基类 + * 自动在查询前切换 PostgreSQL search_path 到租户 Schema + */ +export abstract class TenantAwareRepository { + constructor( + protected readonly dataSource: DataSource, + protected readonly entity: EntityTarget, + ) {} + + protected async getRepository(): Promise> { + const schema = TenantContextService.getSchemaName(); + const queryRunner = this.dataSource.createQueryRunner(); + await queryRunner.query(`SET search_path TO "${schema}", public`); + return queryRunner.manager.getRepository(this.entity); + } + + // CRUD 方法都通过 getRepository() 获取租户隔离的仓储 + async findById(id: string): Promise { + const repo = await this.getRepository(); + return repo.findOneBy({ id } as any); + } + + async save(entity: T): Promise { + const repo = await this.getRepository(); + return repo.save(entity); + } +} +``` + +### 7.5 租户生命周期管理 + +```sql +-- it0_shared schema + +CREATE TABLE tenants ( + id VARCHAR(20) PRIMARY KEY, -- 短ID如 't001' + name VARCHAR(200) NOT NULL, + slug VARCHAR(50) NOT NULL UNIQUE, -- URL 友好标识 + plan VARCHAR(20) NOT NULL DEFAULT 'free', -- 'free' | 'pro' | 'enterprise' + owner_user_id UUID NOT NULL, + status VARCHAR(20) NOT NULL DEFAULT 'active', -- 'active' | 'suspended' | 'deleted' + -- 资源配额 + max_servers INTEGER NOT NULL DEFAULT 5, + max_skills INTEGER NOT NULL DEFAULT 10, + max_users INTEGER NOT NULL DEFAULT 3, + max_sessions_per_day INTEGER NOT NULL DEFAULT 50, + -- 引擎配置 + allowed_engines VARCHAR(20)[] NOT NULL DEFAULT '{claude_code_cli}', + -- 审计 + created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), + updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW() +); + +-- 租户创建时自动初始化 Schema +-- 通过 NestJS migration 或 tenant-provisioning.service.ts 执行: +-- CREATE SCHEMA it0_t_{tenantId}; +-- 运行租户 Schema 的迁移脚本(创建 servers, credentials, tasks 等表) +``` + +### 7.6 Kong 多租户路由 + +在 Kong 层面,通过 JWT claim 中的 `tenant_id` 传递租户标识: + +```yaml +# kong.yml 追加:租户限流(按 tenant 维度) +plugins: + - name: rate-limiting + config: + minute: 120 + policy: redis + redis_host: redis + # 按 JWT 中的 tenant_id 限流 + limit_by: credential +``` + +### 7.7 Agent 引擎的多租户隔离 + +``` +┌─────────────────────────────────────────────────┐ +│ agent-service │ +│ │ +│ 租户 A 的任务 ──→ spawn claude -p "..." │ +│ --system-prompt-file /tmp/a/ │ +│ 使用租户 A 的 .claude/skills/ │ +│ SSH 到租户 A 的服务器 │ +│ │ +│ 租户 B 的任务 ──→ spawn claude -p "..." │ +│ --system-prompt-file /tmp/b/ │ +│ 使用租户 B 的 .claude/skills/ │ +│ SSH 到租户 B 的服务器 │ +│ │ +│ 关键隔离点: │ +│ 1. System Prompt 包含租户专属的服务器清单 │ +│ 2. Skills 目录按租户隔离 │ +│ 3. SSH 凭证从租户 Schema 读取 │ +│ 4. Hook 脚本验证命令目标在租户范围内 │ +│ 5. 并发控制:按租户 plan 限制同时执行的任务数 │ +└─────────────────────────────────────────────────┘ +``` + +--- + +## 8. API Gateway — Kong 声明式配置 + +### 8.1 架构选择 + +使用 **Kong DB-less 模式**(声明式 YAML 配置),不依赖额外数据库,配置文件即基础设施: + +```yaml +# packages/services/api-gateway/kong.yml + +_format_version: "3.0" + +services: + # ===== auth-service ===== + - name: auth-service + url: http://auth-service:3001 + routes: + - name: auth-routes + paths: + - /api/v1/auth + strip_path: false + + # ===== agent-service ===== + - name: agent-service + url: http://agent-service:3002 + routes: + - name: agent-routes + paths: + - /api/v1/agent + strip_path: false + - name: agent-ws + paths: + - /ws/agent + strip_path: false + protocols: + - http + - https + - ws + - wss + + # ===== ops-service ===== + - name: ops-service + url: http://ops-service:3003 + routes: + - name: ops-routes + paths: + - /api/v1/ops + strip_path: false + + # ===== inventory-service ===== + - name: inventory-service + url: http://inventory-service:3004 + routes: + - name: inventory-routes + paths: + - /api/v1/inventory + strip_path: false + + # ===== monitor-service ===== + - name: monitor-service + url: http://monitor-service:3005 + routes: + - name: monitor-routes + paths: + - /api/v1/monitor + strip_path: false + + # ===== comm-service ===== + - name: comm-service + url: http://comm-service:3006 + routes: + - name: comm-routes + paths: + - /api/v1/comm + strip_path: false + - name: comm-ws + paths: + - /ws/comm + strip_path: false + protocols: + - http + - https + - ws + - wss + + # ===== voice-service ===== + - name: voice-service + url: http://voice-service:3008 + routes: + - name: voice-ws + paths: + - /ws/voice + strip_path: false + protocols: + - ws + - wss + - name: voice-api + paths: + - /api/v1/voice + strip_path: false + - name: twilio-webhook + paths: + - /api/v1/twilio + strip_path: false + + # ===== audit-service ===== + - name: audit-service + url: http://audit-service:3007 + routes: + - name: audit-routes + paths: + - /api/v1/audit + strip_path: false + +# ===== 全局插件 ===== +plugins: + # JWT 认证(全局) + - name: jwt + config: + key_claim_name: kid + claims_to_verify: + - exp + # 排除登录和健康检查路由 + route: null + service: null + + # CORS + - name: cors + config: + origins: + - http://localhost:3000 # Web Admin (Next.js dev) + - https://admin.it0.your-domain.com + methods: + - GET + - POST + - PUT + - DELETE + - PATCH + - OPTIONS + headers: + - Authorization + - Content-Type + credentials: true + + # 限流(全局默认) + - name: rate-limiting + config: + minute: 120 + policy: redis + redis_host: redis + redis_port: 6379 + + # 请求日志 + - name: file-log + config: + path: /dev/stdout + reopen: true + +# ===== 路由级插件覆盖 ===== + + # auth 路由不需要 JWT(登录端点) + - name: jwt + route: auth-routes + enabled: false + + # agent WebSocket 更高限流 + - name: rate-limiting + route: agent-ws + config: + minute: 30 + policy: redis + redis_host: redis + + # audit 路由只允许 admin 角色 + - name: acl + route: audit-routes + config: + allow: + - admin +``` + +### 8.2 Kong Docker 配置 + +```dockerfile +# packages/services/api-gateway/Dockerfile + +FROM kong:3.7-alpine + +# 复制声明式配置 +COPY kong.yml /etc/kong/kong.yml + +# 环境变量设为 DB-less 模式 +ENV KONG_DATABASE=off +ENV KONG_DECLARATIVE_CONFIG=/etc/kong/kong.yml +ENV KONG_PROXY_LISTEN=0.0.0.0:8000 +ENV KONG_ADMIN_LISTEN=0.0.0.0:8001 +ENV KONG_LOG_LEVEL=info +``` + +```yaml +# deploy/docker-compose.yml 中的 Kong 服务 +services: + api-gateway: + build: ../packages/services/api-gateway + ports: + - "8000:8000" # Proxy(客户端访问) + - "8001:8001" # Admin API(仅内部使用) + depends_on: + - auth-service + - agent-service + - ops-service + - inventory-service + - monitor-service + - comm-service + - audit-service + - redis + healthcheck: + test: ["CMD", "kong", "health"] + interval: 10s + timeout: 5s + retries: 3 + networks: + - it0-network +``` + +### 8.3 WebSocket 代理注意事项 + +Kong 原生支持 WebSocket 代理,关键配置: +- `protocols` 包含 `ws` 和 `wss` +- 建议为 WebSocket 路由单独设置更宽松的超时:连接超时 60s、读写超时 3600s +- Agent 流式输出和实时通信都走 WebSocket,需要确保 Kong 不会过早断开长连接 + +--- + +## 9. Claude Skills 支持(Anthropic Claude Skills) + +### 9.1 Skills 与 Runbook 的关系 + +``` +Claude Skills Runbook +(底层能力单元) (高层业务流程) +┌─────────────┐ ┌──────────────────┐ +│ /inspect │─── 被组合使用 ──→ │ 每日巡检 Runbook │ +│ /cleanup │─── 被组合使用 ──→ │ (调用多个 Skills) │ +│ /diagnose │ │ │ +└─────────────┘ └──────────────────┘ + +Skills = 原子操作(单个技能,Claude Code 原生识别) +Runbook = 编排流程(多步骤,包含审批、通知、条件分支) +``` + +### 9.2 Skill 定义规范 + +每个 Skill 是一个目录,包含 `SKILL.md`(YAML frontmatter + Markdown 指令): + +```yaml +# .claude/skills/inspect/SKILL.md + +--- +name: inspect +description: 对指定服务器执行全面健康检查(CPU/MEM/DISK/网络/服务状态),返回结构化报告 +argument-hint: "[server-name|all]" +allowed-tools: Bash, Read, Grep +disable-model-invocation: false +--- + +# 服务器健康检查 + +## 参数 +- `$0`: 目标服务器名称,或 `all` 检查所有服务器 + +## 执行步骤 + +1. **确认目标**:如果 `$0` 是 `all`,从服务器清单获取所有在线服务器 +2. **基础指标采集**(对每台服务器): + - `ssh {server} 'top -bn1 | head -5'` — CPU 负载 + - `ssh {server} 'free -h'` — 内存使用 + - `ssh {server} 'df -h'` — 磁盘使用 + - `ssh {server} 'ss -tuln'` — 网络端口 + - `ssh {server} 'uptime'` — 运行时间 +3. **服务状态**: + - `ssh {server} 'systemctl list-units --failed'` — 失败的服务 + - `ssh {server} 'docker ps --format "table {{.Names}}\t{{.Status}}"'` — 容器状态(如适用) +4. **异常检测**:标记 CPU>80%、MEM>85%、DISK>90% 的指标 +5. **输出结构化报告**:按服务器分组,标记异常项 + +## 输出格式 +```markdown +## 巡检报告 — {timestamp} + +### {server-name} ● 正常 / ⚠ 告警 / 🔴 异常 +| 指标 | 值 | 状态 | +|------|-----|------| +| CPU | 45% | ✓ | +| MEM | 62% | ✓ | +| DISK | 92% | ⚠ | +... +``` +``` + +```yaml +# .claude/skills/deploy/SKILL.md + +--- +name: deploy +description: 将指定应用部署到目标环境(支持 Docker 和 K8s) +argument-hint: "[app-name] [environment]" +allowed-tools: Bash, Read, Grep, Glob +disable-model-invocation: true +--- + +# 应用部署 + +## 参数 +- `$0`: 应用名称 +- `$1`: 目标环境 (dev/staging/prod) + +## 前置检查 +1. 确认应用 `$0` 存在于部署配置中 +2. 确认目标环境 `$1` 的服务器可达 +3. 如果是 prod 环境,明确提示需要审批 + +## 部署流程 +1. 拉取最新镜像 / 构建产物 +2. 备份当前版本信息(用于回滚) +3. 执行滚动更新 +4. 等待健康检查通过 +5. 验证服务正常响应 +6. 输出部署摘要(版本号、耗时、服务状态) + +## 回滚预案 +如果部署后健康检查失败,自动执行 /rollback $0 $1 +``` + +```yaml +# .claude/skills/diagnose/SKILL.md + +--- +name: diagnose +description: 诊断服务器或服务的异常问题,自动收集日志和上下文 +argument-hint: "[server-name] [symptom-description]" +allowed-tools: Bash, Read, Grep, Glob +disable-model-invocation: false +--- + +# 故障诊断 + +## 参数 +- `$0`: 目标服务器 +- `$ARGUMENTS`: 症状描述(如 "nginx 502 错误"、"磁盘 IO 高") + +## 诊断框架 + +### 1. 收集上下文 +- 系统负载: `uptime`, `top -bn1`, `vmstat 1 5` +- 磁盘 IO: `iostat -x 1 5` +- 网络: `netstat -s`, `ss -s` +- 最近错误日志: `journalctl -p err --since "1 hour ago"` + +### 2. 针对症状深入 +根据 `$ARGUMENTS` 描述的症状,针对性收集: +- HTTP 错误 → Nginx/Apache 错误日志 + upstream 状态 +- 高负载 → 进程列表 + 资源占用排行 +- 磁盘满 → 大文件查找 + 日志增长速率 +- 内存泄漏 → 进程内存排行 + OOM killer 日志 + +### 3. 分析与建议 +- 总结根因分析 +- 提供修复建议(标明风险等级) +- 如果有明确的修复命令,提示是否执行 +``` + +### 9.3 Skills 管理在 agent-service 中的实现 + +``` +agent-service/ +├── src/ +│ ├── infrastructure/ +│ │ ├── engines/ +│ │ │ └── claude-code-cli/ +│ │ │ ├── claude-code-engine.ts +│ │ │ └── ... +│ │ └── skills/ # ★ Skills 管理层 +│ │ ├── skill-manager.service.ts # Skill CRUD(读写 .claude/skills/) +│ │ ├── skill-sync.service.ts # DB ↔ 文件系统双向同步 +│ │ ├── skill-template-renderer.ts # 模板变量渲染($0, $ARGUMENTS 等) +│ │ └── skill-validator.service.ts # Skill 格式校验(frontmatter + 内容) +``` + +```typescript +// infrastructure/skills/skill-manager.service.ts + +@Injectable() +export class SkillManagerService { + private readonly skillsDir: string; + + constructor(private readonly configService: ConfigService) { + // Skills 目录:项目根目录下的 .claude/skills/ + this.skillsDir = path.join( + this.configService.get('PROJECT_ROOT', process.cwd()), + '.claude', 'skills', + ); + } + + /** 列出所有已注册的 Skills */ + async listSkills(): Promise { + const dirs = await fs.readdir(this.skillsDir, { withFileTypes: true }); + const skills: SkillDefinition[] = []; + + for (const dir of dirs.filter(d => d.isDirectory())) { + const skillPath = path.join(this.skillsDir, dir.name, 'SKILL.md'); + if (await this.fileExists(skillPath)) { + const content = await fs.readFile(skillPath, 'utf-8'); + skills.push(this.parseSkillMd(dir.name, content)); + } + } + return skills; + } + + /** 创建/更新 Skill(从 Web Admin 配置写入文件系统) */ + async upsertSkill(skill: CreateSkillDto): Promise { + const skillDir = path.join(this.skillsDir, skill.name); + await fs.mkdir(skillDir, { recursive: true }); + + const content = this.buildSkillMd(skill); + await fs.writeFile(path.join(skillDir, 'SKILL.md'), content, 'utf-8'); + + // 如果有附带脚本 + if (skill.scripts?.length) { + const scriptsDir = path.join(skillDir, 'scripts'); + await fs.mkdir(scriptsDir, { recursive: true }); + for (const script of skill.scripts) { + await fs.writeFile(path.join(scriptsDir, script.name), script.content, 'utf-8'); + } + } + } + + /** 删除 Skill */ + async deleteSkill(name: string): Promise { + const skillDir = path.join(this.skillsDir, name); + await fs.rm(skillDir, { recursive: true, force: true }); + } + + /** 通过 Claude Code CLI 调用 Skill */ + invokeSkillPrompt(skillName: string, args: string): string { + // 返回格式化的 prompt,让 Claude Code 识别为 Skill 调用 + return `/${skillName} ${args}`; + } + + /** 解析 SKILL.md (YAML frontmatter + Markdown body) */ + private parseSkillMd(dirName: string, content: string): SkillDefinition { + const frontmatterMatch = content.match(/^---\n([\s\S]*?)\n---\n([\s\S]*)$/); + if (!frontmatterMatch) { + return { name: dirName, description: '', content, frontmatter: {} }; + } + const frontmatter = yaml.parse(frontmatterMatch[1]); + const body = frontmatterMatch[2]; + return { + name: frontmatter.name || dirName, + description: frontmatter.description || '', + argumentHint: frontmatter['argument-hint'], + allowedTools: frontmatter['allowed-tools'], + disableModelInvocation: frontmatter['disable-model-invocation'] ?? false, + content: body, + frontmatter, + }; + } + + /** 构建 SKILL.md 内容 */ + private buildSkillMd(skill: CreateSkillDto): string { + const frontmatter = yaml.stringify({ + name: skill.name, + description: skill.description, + 'argument-hint': skill.argumentHint, + 'allowed-tools': skill.allowedTools, + 'disable-model-invocation': skill.disableModelInvocation ?? false, + }); + return `---\n${frontmatter}---\n\n${skill.content}`; + } +} +``` + +### 9.4 Skill 与引擎的集成 + +```typescript +// Claude Code CLI 引擎中调用 Skill 的两种方式: + +// 方式 1: 直接在 prompt 中使用 /skill-name(Claude Code 自动识别) +const args = ['-p', `/inspect all`, '--output-format', 'stream-json', ...]; + +// 方式 2: Skill 内容已通过 .claude/skills/ 目录自动加载到 Claude Code 上下文中 +// Claude Code 启动时读取所有 Skills 的 description,自主决定何时使用 +// 当用户说"检查一下服务器"时,Claude Code 会自动匹配 /inspect skill + +// 方式 3: Claude API 引擎 — 手动注入 Skill 内容到 system prompt +// 当切换到 Claude API 引擎时,SkillManager 读取 Skill 内容,拼接到 system prompt 中 +``` + +### 9.5 Skill 数据库表(agent-service) + +```sql +-- 追加到 it0_agent schema + +-- Skills 注册表(与文件系统双向同步) +CREATE TABLE skills ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + name VARCHAR(64) NOT NULL UNIQUE, -- kebab-case 名称 + description TEXT NOT NULL, + argument_hint VARCHAR(200), + allowed_tools VARCHAR(50)[], + disable_model_invocation BOOLEAN NOT NULL DEFAULT FALSE, + content TEXT NOT NULL, -- Markdown 指令体 + frontmatter JSONB NOT NULL DEFAULT '{}', -- 完整 frontmatter + version INTEGER NOT NULL DEFAULT 1, + is_active BOOLEAN NOT NULL DEFAULT TRUE, + created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), + updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW() +); + +-- Skill 执行记录 +CREATE TABLE skill_executions ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + skill_name VARCHAR(64) NOT NULL, + session_id UUID REFERENCES agent_sessions(id), + arguments TEXT, + engine_type VARCHAR(20) NOT NULL, + status VARCHAR(20) NOT NULL, -- 'success' | 'failure' | 'cancelled' + duration_ms INTEGER, + executed_at TIMESTAMPTZ NOT NULL DEFAULT NOW() +); + +CREATE INDEX idx_skill_executions_name ON skill_executions(skill_name, executed_at); +``` + +### 9.6 引擎兼容性:Skills 在三种引擎下的行为 + +| 引擎 | Skill 加载方式 | Skill 调用方式 | +|------|---------------|---------------| +| **Claude Code CLI** | `.claude/skills/` 目录自动发现 | `/skill-name args`(原生支持) | +| **Claude API** | `SkillManager` 读取 → 拼接到 system prompt | prompt 中包含 Skill 指令文本 | +| **Custom Agent** | `SkillManager` 读取 → 注入到自研引擎上下文 | 自定义调用机制 | + +当切换引擎时,`SkillManager` 确保 Skills 的跨引擎可移植性: +- Claude Code CLI 原生读 `.claude/skills/` 目录 +- 其他引擎通过 `SkillManager.getSkillContent(name)` 获取 Skill 内容,手动注入 + +--- + +## 10. 运维专用 System Prompt 模板 + +```typescript +// infrastructure/engines/claude-code-cli/system-prompt-builder.ts + +export class SystemPromptBuilder { + build(context: OpsContext): string { + return ` +你是 IT0 运维智能体,负责管理以下服务器集群。 + +## 你管理的环境 +${context.servers.map(s => `- ${s.name} (${s.host}) [${s.environment}/${s.role}]`).join('\n')} + +## 集群信息 +${context.clusters.map(c => `- ${c.name} (${c.type}) — ${c.description}`).join('\n')} + +## 操作规范 + +### 权限分级 +- Level 0(自动执行):查看类命令(df, free, top, kubectl get, docker ps, cat/grep 日志) +- Level 1(需确认):服务重启、日志清理、配置查看 +- Level 2(需审批):部署、扩缩容、数据库操作、配置修改 +- Level 3(禁止):rm -rf /, DROP DATABASE, shutdown, reboot, 格式化磁盘 + +### 执行原则 +1. 每次操作前先说明你要做什么、为什么 +2. 执行破坏性操作前,先展示完整命令 +3. 每次操作后验证结果(检查返回码、确认服务状态) +4. 发现异常时自动收集诊断信息(日志末尾、资源使用、进程列表、网络状态) +5. 如果不确定,宁可多查一步也不要盲目执行 +6. 操作完成后给出简洁总结 + +### 禁止行为 +- 绝不猜测密码或凭证 +- 绝不修改 SSH 配置或防火墙规则(除非明确指示) +- 绝不安装未经授权的软件包 +- 绝不在生产环境执行未经验证的脚本 + +### SSH 访问 +使用预配置的 SSH 别名:ssh ${context.sshUser}@{server_host} +`.trim(); + } +} +``` + +--- + +## 11. 项目初始化与目录结构 + +### 11.1 Monorepo 结构 + +``` +IT0/ +├── docs/ # 文档 +│ ├── backend-guide.md # 本文档 +│ ├── flutter-guide.md # Flutter 前端指导 +│ └── web-admin-guide.md # PC Web 管理前端指导 +│ +├── packages/ +│ ├── shared/ # 共享包 +│ │ ├── proto/ # gRPC Protobuf 定义 +│ │ ├── events/ # 事件类型定义 +│ │ └── utils/ # 公共工具 +│ │ +│ └── services/ # 微服务 +│ ├── api-gateway/ # Kong 声明式配置 +│ │ ├── kong.yml # 路由/服务/插件声明 +│ │ ├── Dockerfile # Kong 自定义镜像 +│ │ └── plugins/ # 自定义插件(如需) +│ ├── auth-service/ +│ ├── agent-service/ # ★ 核心智能体服务 +│ ├── ops-service/ +│ ├── inventory-service/ +│ ├── monitor-service/ +│ ├── comm-service/ +│ ├── voice-service/ # ★ Pipecat 语音对话引擎 (Python) +│ └── audit-service/ +│ +├── .claude/ # ★ Claude Code Skills 目录 +│ ├── skills/ # 运维技能定义 +│ │ ├── inspect/SKILL.md # 全面巡检 Skill +│ │ ├── deploy/SKILL.md # 部署 Skill +│ │ ├── rollback/SKILL.md # 回滚 Skill +│ │ ├── cleanup/SKILL.md # 清理 Skill +│ │ ├── diagnose/SKILL.md # 故障诊断 Skill +│ │ ├── scale/SKILL.md # 扩缩容 Skill +│ │ ├── backup-check/SKILL.md # 备份验证 Skill +│ │ └── security-audit/SKILL.md # 安全审计 Skill +│ ├── commands/ # 兼容旧版 slash commands +│ └── settings.json # Hook + 权限配置 +│ +├── ops-config/ # 运维配置 +│ ├── system-prompts/ # System Prompt 模板 +│ ├── hooks/ # Claude Code Hook 脚本 +│ │ └── pre-tool-use-hook.py +│ ├── runbooks/ # Runbook 模板(高级,多步骤) +│ └── risk-patterns.json # 危险命令模式库 +│ +├── deploy/ +│ ├── docker-compose.yml # 开发环境 +│ ├── docker-compose.prod.yml # 生产环境 +│ └── k8s/ # Kubernetes 部署清单 +│ +├── package.json # Monorepo root (pnpm workspace) +├── pnpm-workspace.yaml +├── tsconfig.base.json +└── .env.example +``` + +### 11.2 单个微服务标准结构(Clean Architecture) + +``` +{service-name}/ +├── src/ +│ ├── domain/ # 领域层 — 零外部依赖 +│ │ ├── entities/ # 聚合根 & 实体 +│ │ ├── value-objects/ # 值对象 +│ │ ├── ports/ # 端口(接口定义) +│ │ │ ├── inbound/ # 入站端口(用例接口) +│ │ │ └── outbound/ # 出站端口(依赖接口) +│ │ ├── events/ # 领域事件 +│ │ └── services/ # 领域服务 +│ │ +│ ├── application/ # 应用层 — 用例编排 +│ │ ├── use-cases/ # 用例实现 +│ │ └── dto/ # 数据传输对象 +│ │ +│ ├── infrastructure/ # 基础设施层 — 适配器实现 +│ │ ├── persistence/ # TypeORM 实体 & 仓储 +│ │ ├── messaging/ # 消息队列 +│ │ ├── clients/ # 外部服务客户端 +│ │ └── config/ # 配置 +│ │ +│ ├── interfaces/ # 接口层 — 控制器 +│ │ ├── rest/ # REST API +│ │ ├── grpc/ # gRPC 服务 +│ │ └── ws/ # WebSocket 网关 +│ │ +│ ├── app.module.ts +│ └── main.ts +│ +├── test/ +│ ├── unit/ +│ ├── integration/ +│ └── e2e/ +│ +├── Dockerfile +├── package.json +└── tsconfig.json +``` + +--- + +## 12. 开发路线图 + +### Phase 1: 基础骨架(Week 1-2) +- [ ] Monorepo 初始化(pnpm workspace + NestJS CLI) +- [ ] auth-service: JWT 认证 + RBAC +- [ ] agent-service: Claude Code CLI 引擎 + 流式输出 +- [ ] 命令风险分级 + Hook 拦截脚本 +- [ ] WebSocket 实时推送 + +### Phase 2: 运维核心(Week 3-4) +- [ ] inventory-service: 服务器/凭证管理 +- [ ] ops-service: 任务创建 + 审批工作流 +- [ ] 端到端流程:前端发起任务 → Agent 执行 → 审批 → 完成 +- [ ] Runbook 模板系统 + +### Phase 3: 监控告警(Week 5-6) +- [ ] monitor-service: 健康检查 + 指标采集 +- [ ] 告警规则 + 自动触发 Runbook +- [ ] 告警 → 通知升级链路 + +### Phase 4: 多渠道通信 + 语音对话(Week 7-9) +- [ ] comm-service: 推送 + 短信 + 邮件 + IM(Telegram/企业微信) +- [ ] voice-service: Pipecat + faster-whisper + Kokoro-82M 部署 +- [ ] Flutter App WebSocket 音频流 ←→ voice-service 对接 +- [ ] Pipecat 主动拨号(Twilio 电话线路)+ Media Streams 语音对话 +- [ ] 三层递进升级链路端到端测试 + +### Phase 5: 引擎扩展(Week 9-10) +- [ ] Claude API 引擎实现(备选方案) +- [ ] 引擎运行时切换 + 性能对比 +- [ ] audit-service: 完整审计链 + +--- + +## 13. 环境变量配置 + +```env +# .env.example + +# ===== 全局 ===== +NODE_ENV=development +LOG_LEVEL=debug + +# ===== PostgreSQL ===== +POSTGRES_HOST=localhost +POSTGRES_PORT=5432 +POSTGRES_USER=it0 +POSTGRES_PASSWORD=changeme +POSTGRES_DB=it0 + +# ===== Redis ===== +REDIS_HOST=localhost +REDIS_PORT=6379 + +# ===== Agent Engine ===== +AGENT_ENGINE_TYPE=claude_code_cli # claude_code_cli | claude_api | custom +CLAUDE_CODE_PATH=/usr/local/bin/claude # Claude Code CLI 路径 +ANTHROPIC_API_KEY=sk-ant-xxx # Claude API 备选方案 + +# ===== 安全 ===== +VAULT_MASTER_KEY=your-256-bit-key # 凭证加密密钥 +JWT_SECRET=your-jwt-secret +JWT_EXPIRES_IN=24h + +# ===== 通信渠道(comm-service)===== +SMTP_HOST=smtp.gmail.com +SMTP_PORT=587 +SMTP_USER=xxx +SMTP_PASSWORD=xxx +TELEGRAM_BOT_TOKEN=xxx +WECHAT_CORP_ID=xxx +WECHAT_CORP_SECRET=xxx + +# ===== voice-service(Pipecat 语音对话引擎)===== +VOICE_SERVICE_URL=http://voice-service:3008 # voice-service 内部地址 +WHISPER_MODEL=large-v3 # faster-whisper 模型 (base/small/medium/large-v3) +KOKORO_MODEL=kokoro-82m # Kokoro TTS 模型 +VOICE_DEVICE=cuda # STT/TTS 推理设备 (cuda/cpu) + +# ===== Twilio(电话线路载体:Pipecat 通过它拨号)===== +TWILIO_ACCOUNT_SID=ACxxx +TWILIO_AUTH_TOKEN=xxx +TWILIO_PHONE_NUMBER=+1234567890 +TWILIO_MEDIA_STREAMS_URL=wss://voice-service:3008/twilio-stream # Media Streams 转接地址 +``` + +--- + +## 14. 关键设计决策备忘 + +| 决策 | 选择 | 理由 | +|------|------|------| +| 引擎抽象层 | Strategy + Adapter | 运行时切换,不需要重启服务 | +| 服务间同步通信 | gRPC | 类型安全、高性能、双向流 | +| 服务间异步通信 | Redis Streams | 轻量、可靠、不需要额外中间件 | +| 数据库 | PostgreSQL(每服务独立 schema) | 同一个 PG 实例多 schema,开发简单;生产可拆分 | +| 凭证存储 | AES-256-GCM + PostgreSQL | 不引入 HashiCorp Vault 的复杂度,够用 | +| 审计日志 | Append-Only + 数据库级权限控制 | 防篡改,满足合规需求 | +| 定时任务 | Bull Queue (Redis-backed) | NestJS 原生支持,可靠的延迟/重试机制 | +| 监控指标存储 | PostgreSQL 分区表 | 初期够用;后期可迁移 TimescaleDB | +| 前后端通信 | REST + WebSocket | REST 用于 CRUD,WebSocket 用于实时流 | +| API 网关 | Kong DB-less 声明式 | 无需额外 DB,YAML 配置即基础设施,原生支持 WebSocket/JWT/限流 | +| AI Skills | Anthropic Claude Skills (`.claude/skills/`) | 原生 Claude Code 支持 + DB 同步实现跨引擎可移植 | +| Skills vs Runbook | Skills=原子技能,Runbook=编排流程 | Skills 是底层能力单元,Runbook 组合 Skills 实现多步骤业务流程 | +| 多租户隔离 | Schema-per-Tenant + 行级 tenant_id | Schema 级隔离安全性高,tenant_id 列防御纵深;避免 Database-per-Tenant 的运维复杂度 | +| 租户上下文 | AsyncLocalStorage | 无侵入式传播,不需要每个方法显式传参;NestJS 中间件统一注入 | +| 租户 Schema 管理 | TypeORM `SET search_path` | 动态切换,无需维护多 DataSource;QueryRunner 级别隔离 | +| 自治运维范式 | 驻留指令(Standing Orders) | 对话定义 → 结构化提取 → 自主执行 → 智能升级;避免 24/7 人工在线 | +| 驻留指令执行 | Bull Queue + cron 调度 | 可靠的定时触发、重试、并发控制;执行记录完整审计 | +| 智能升级通道 | 三层递进(推送→电话唤醒→扩大通知) | App 推送优先;2分钟无响应→电话唤醒;5分钟→IM/短信;10分钟→备用联系人/邮件 | +| 语音对话引擎 | Pipecat (Python, 10.1k stars) | 原生 Claude 集成 + 自部署 STT/TTS + 打断支持 + < 500ms 延迟;不依赖外部语音 API | +| STT 选型 | faster-whisper (large-v3) | 20.6k stars,4x 快于原版 Whisper,支持 GPU + 8bit 量化,中英文准确率高 | +| TTS 选型 | Kokoro-82M | Apache 开源,82M 参数质量媲美大模型,中英双语,CPU/GPU 均可运行 | +| Twilio 定位 | 纯电话线路载体 | Pipecat 通过 Twilio REST API 拨号 + Media Streams 传输音频;无 IVR,Agent 接通即说话 | diff --git a/docs/flutter-guide.md b/docs/flutter-guide.md new file mode 100644 index 0000000..3714c25 --- /dev/null +++ b/docs/flutter-guide.md @@ -0,0 +1,2105 @@ +# IT0 Flutter 前端开发指导文档 + +> IT Operations Intelligent Agent — Flutter Android 客户端(Riverpod + Clean Architecture) + +## 1. 项目概述 + +### 1.1 定位 + +IT0 Flutter App 是服务器集群运维智能体的**驾驶舱**,提供: +- 实时运维仪表盘 +- AI 对话交互(文字 + 语音) +- 任务发起与审批 +- 多渠道通知接收 +- 服务器状态监控 + +### 1.2 技术栈 + +| 层面 | 技术选型 | +|------|---------| +| 框架 | Flutter 3.x (Android) | +| 状态管理 | Riverpod 2.x (flutter_riverpod + riverpod_annotation) | +| 架构 | Clean Architecture 三层 | +| 网络 | Dio (REST) + web_socket_channel (WebSocket) | +| 本地存储 | Hive / SharedPreferences | +| 语音 | speech_to_text + flutter_tts | +| 路由 | go_router | +| 代码生成 | freezed + json_serializable + riverpod_generator | +| 推送 | Firebase Cloud Messaging (FCM) | +| 图表 | fl_chart | +| 终端模拟 | xterm (flutter_xterm) | + +### 1.3 设计原则 + +- **Clean Architecture**:Data → Domain → Presentation 严格分层,依赖方向向内 +- **Riverpod**:全局状态管理,无 context 依赖,可测试 +- **离线优先**:关键数据本地缓存,断网时仍可查看历史 +- **实时优先**:WebSocket 长连接,Agent 输出实时流式展示 +- **多租户感知**:登录后获取可访问租户列表,Dio 拦截器自动注入 `X-Tenant-Id`,切换租户刷新全部数据 + +--- + +## 2. 项目结构 + +### 2.1 整体目录 + +``` +it0_app/ +├── lib/ +│ ├── main.dart # App 入口 +│ ├── app.dart # MaterialApp + Router 配置 +│ │ +│ ├── core/ # 核心基础设施(跨 Feature 共享) +│ │ ├── config/ +│ │ │ ├── app_config.dart # 环境配置 +│ │ │ └── api_endpoints.dart # API 端点常量 +│ │ ├── network/ +│ │ │ ├── dio_client.dart # Dio 配置(拦截器、token 注入) +│ │ │ ├── websocket_client.dart # WebSocket 管理(自动重连) +│ │ │ └── api_result.dart # Result 统一响应类型 +│ │ ├── error/ +│ │ │ ├── failures.dart # 失败类型定义 +│ │ │ └── error_handler.dart # 全局异常处理 +│ │ ├── theme/ +│ │ │ ├── app_theme.dart # 主题配置(暗色为主) +│ │ │ ├── app_colors.dart # 颜色常量 +│ │ │ └── app_typography.dart # 字体样式 +│ │ ├── router/ +│ │ │ └── app_router.dart # go_router 路由配置 +│ │ ├── widgets/ # 通用组件 +│ │ │ ├── status_badge.dart # 状态徽章(running/error/warning) +│ │ │ ├── risk_level_chip.dart # 风险等级标签 +│ │ │ ├── loading_overlay.dart +│ │ │ └── empty_state.dart +│ │ ├── tenant/ # ★ 多租户上下文 +│ │ │ ├── tenant_provider.dart # 当前租户 Riverpod Provider +│ │ │ └── tenant_interceptor.dart # Dio 拦截器(注入 X-Tenant-Id) +│ │ └── utils/ +│ │ ├── date_formatter.dart +│ │ └── logger.dart +│ │ +│ └── features/ # 功能模块(按 Feature 划分) +│ ├── auth/ # 认证 +│ ├── dashboard/ # 仪表盘 +│ ├── chat/ # AI 对话(核心交互 + 驻留指令定义) +│ ├── tasks/ # 运维任务 +│ ├── standing_orders/ # ★ 驻留指令管理与监控 +│ ├── approvals/ # 审批中心 +│ ├── servers/ # 服务器管理 +│ ├── alerts/ # 告警中心 +│ ├── agent_call/ # ★ Agent 来电响应(Pipecat 语音对话 + 通知) +│ ├── terminal/ # 远程终端 +│ └── settings/ # 设置 +│ +├── test/ +│ ├── unit/ +│ ├── widget/ +│ └── integration/ +│ +├── pubspec.yaml +└── analysis_options.yaml +``` + +### 2.2 单个 Feature 模块结构(Clean Architecture 三层) + +以 `chat` 模块为例: + +``` +features/chat/ +├── data/ # 数据层 +│ ├── datasources/ +│ │ ├── chat_remote_datasource.dart # REST API 调用 +│ │ └── chat_local_datasource.dart # 本地缓存(Hive) +│ ├── models/ +│ │ ├── chat_message_model.dart # API 响应模型(fromJson/toJson) +│ │ ├── stream_event_model.dart # WebSocket 流事件模型 +│ │ └── session_model.dart +│ └── repositories/ +│ └── chat_repository_impl.dart # 仓储实现 +│ +├── domain/ # 领域层(零依赖) +│ ├── entities/ +│ │ ├── chat_message.dart # 消息实体(freezed) +│ │ ├── chat_session.dart # 会话实体 +│ │ └── stream_event.dart # 流事件实体 +│ ├── repositories/ +│ │ └── chat_repository.dart # 仓储接口(抽象类) +│ └── usecases/ +│ ├── send_message.dart # 发送消息 +│ ├── start_voice_input.dart # 开始语音输入 +│ ├── get_session_history.dart # 获取历史 +│ └── cancel_task.dart # 取消任务 +│ +└── presentation/ # 展示层 + ├── providers/ + │ ├── chat_providers.dart # Riverpod providers + │ └── voice_providers.dart # 语音相关 providers + ├── pages/ + │ └── chat_page.dart # 对话页面 + ├── widgets/ + │ ├── message_bubble.dart # 消息气泡 + │ ├── agent_thinking_indicator.dart # AI 思考动画 + │ ├── tool_execution_card.dart # 工具执行卡片(显示 Bash 命令和输出) + │ ├── approval_action_card.dart # 审批操作卡片 + │ ├── voice_input_button.dart # 语音输入按钮 + │ └── stream_text_widget.dart # 流式文字渲染 + └── controllers/ + └── chat_controller.dart # 页面控制器(如需要) +``` + +--- + +## 3. 核心基础设施详细设计 + +### 3.1 网络层 — Dio 客户端 + +```dart +// core/network/dio_client.dart + +class DioClient { + late final Dio _dio; + + DioClient({required AppConfig config, required TokenStorage tokenStorage}) { + _dio = Dio(BaseOptions( + baseUrl: config.apiBaseUrl, + connectTimeout: const Duration(seconds: 10), + receiveTimeout: const Duration(seconds: 30), + )); + + _dio.interceptors.addAll([ + // Token 注入 + InterceptorsWrapper( + onRequest: (options, handler) async { + final token = await tokenStorage.getAccessToken(); + if (token != null) { + options.headers['Authorization'] = 'Bearer $token'; + } + handler.next(options); + }, + ), + // ★ 多租户:自动注入 X-Tenant-Id + TenantInterceptor(), + // 日志(debug 模式) + if (config.isDebug) LogInterceptor(responseBody: true), + // 错误统一处理 + ErrorInterceptor(), + ]); + } + + Dio get dio => _dio; +} +``` + +### 3.2 网络层 — WebSocket 客户端 + +```dart +// core/network/websocket_client.dart + +class WebSocketClient { + WebSocketChannel? _channel; + final StreamController> _eventController = + StreamController.broadcast(); + Timer? _heartbeatTimer; + Timer? _reconnectTimer; + int _reconnectAttempts = 0; + static const _maxReconnectAttempts = 10; + + final AppConfig _config; + final TokenStorage _tokenStorage; + + WebSocketClient({required AppConfig config, required TokenStorage tokenStorage}) + : _config = config, + _tokenStorage = tokenStorage; + + /// 事件流 — 所有 Feature 监听此 Stream + Stream> get eventStream => _eventController.stream; + + /// 连接 + Future connect() async { + final token = await _tokenStorage.getAccessToken(); + final uri = Uri.parse('${_config.wsBaseUrl}/ws?token=$token'); + + _channel = WebSocketChannel.connect(uri); + _reconnectAttempts = 0; + + _channel!.stream.listen( + (data) { + final event = jsonDecode(data as String) as Map; + _eventController.add(event); + }, + onDone: () => _handleDisconnect(), + onError: (error) => _handleDisconnect(), + ); + + _startHeartbeat(); + } + + /// 发送消息 + void send(Map data) { + _channel?.sink.add(jsonEncode(data)); + } + + /// 自动重连(指数退避) + void _handleDisconnect() { + _heartbeatTimer?.cancel(); + if (_reconnectAttempts < _maxReconnectAttempts) { + final delay = Duration(seconds: math.pow(2, _reconnectAttempts).toInt()); + _reconnectTimer = Timer(delay, () { + _reconnectAttempts++; + connect(); + }); + } + } + + void _startHeartbeat() { + _heartbeatTimer = Timer.periodic(const Duration(seconds: 30), (_) { + send({'type': 'ping'}); + }); + } + + void dispose() { + _heartbeatTimer?.cancel(); + _reconnectTimer?.cancel(); + _channel?.sink.close(); + _eventController.close(); + } +} +``` + +### 3.3 统一结果类型 + +```dart +// core/network/api_result.dart + +@freezed +sealed class ApiResult with _$ApiResult { + const factory ApiResult.success(T data) = Success; + const factory ApiResult.failure(Failure failure) = FailureResult; +} + +// core/error/failures.dart + +@freezed +sealed class Failure with _$Failure { + const factory Failure.network({required String message}) = NetworkFailure; + const factory Failure.server({required String message, required int statusCode}) = ServerFailure; + const factory Failure.auth({required String message}) = AuthFailure; + const factory Failure.unknown({required String message}) = UnknownFailure; +} +``` + +--- + +## 4. Feature 模块详细设计 + +### 4.1 Dashboard(仪表盘) + +#### 页面布局 + +``` +┌──────────────────────────────────────────┐ +│ IT0 Dashboard [⚙️] │ +├──────────────────────────────────────────┤ +│ │ +│ ┌─────────┐ ┌─────────┐ ┌─────────┐ │ +│ │ Servers │ │ Active │ │ Alerts │ │ +│ │ 12 │ │ Tasks 3 │ │ 2 │ │ +│ │ ● 10 OK │ │ 1 await │ │ ⚠ 1 🔴1│ │ +│ └─────────┘ └─────────┘ └─────────┘ │ +│ │ +│ ── 服务器状态 ────────────────────── │ +│ ┌──────────────────────────────────┐ │ +│ │ prod-1 CPU 45% MEM 62% DISK 71% │ │ +│ │ prod-2 CPU 23% MEM 48% DISK 92% │ │ ← 磁盘高亮红色 +│ │ prod-3 CPU 67% MEM 75% DISK 55% │ │ +│ └──────────────────────────────────┘ │ +│ │ +│ ── 最近操作 ────────────────────── │ +│ │ 14:23 巡检完成 — 全部正常 │ +│ │ 13:15 prod-2 日志清理 — 成功 │ +│ │ 12:01 告警: prod-2 磁盘 > 90% │ +│ │ +│ ── 快捷操作 ────────────────────── │ +│ ┌──────┐ ┌──────┐ ┌──────┐ ┌──────┐ │ +│ │ 巡检 │ │ 部署 │ │ 日志 │ │ 终端 │ │ +│ └──────┘ └──────┘ └──────┘ └──────┘ │ +│ │ +├──────────────────────────────────────────┤ +│ [Dashboard] [Chat] [Tasks] [Alerts] │ +└──────────────────────────────────────────┘ +``` + +#### Riverpod Providers + +```dart +// features/dashboard/presentation/providers/dashboard_providers.dart + +/// 服务器状态列表(实时更新) +@riverpod +Stream> serverStatusStream(Ref ref) { + final wsClient = ref.watch(webSocketClientProvider); + return wsClient.eventStream + .where((event) => event['type'] == 'server_status_update') + .map((event) => (event['servers'] as List) + .map((s) => ServerStatus.fromJson(s)) + .toList()); +} + +/// 仪表盘摘要(聚合数据) +@riverpod +Future dashboardSummary(Ref ref) async { + final repo = ref.watch(dashboardRepositoryProvider); + return repo.getSummary(); +} + +/// 最近操作日志 +@riverpod +Future> recentOperations(Ref ref) async { + final repo = ref.watch(dashboardRepositoryProvider); + return repo.getRecentOperations(limit: 20); +} + +/// 活跃告警 +@riverpod +Stream> activeAlerts(Ref ref) { + final wsClient = ref.watch(webSocketClientProvider); + return wsClient.eventStream + .where((event) => event['type'] == 'alert_update') + .map((event) => (event['alerts'] as List) + .map((a) => Alert.fromJson(a)) + .toList()); +} +``` + +--- + +### 4.2 Chat(AI 对话交互)— 核心 Feature + +#### 4.2.1 Domain 层 + +```dart +// features/chat/domain/entities/chat_message.dart + +@freezed +class ChatMessage with _$ChatMessage { + const factory ChatMessage({ + required String id, + required MessageRole role, // user / assistant / system + required String content, + required DateTime timestamp, + MessageType? type, // text / tool_use / tool_result / approval / thinking + ToolExecution? toolExecution, // 工具执行详情 + ApprovalRequest? approvalRequest, // 审批请求 + bool? isStreaming, // 是否正在流式输出 + }) = _ChatMessage; +} + +@freezed +class ToolExecution with _$ToolExecution { + const factory ToolExecution({ + required String toolName, // Bash / Read / Grep / etc. + required String input, // 命令内容 + String? output, // 执行结果 + required RiskLevel riskLevel, + required ToolStatus status, // executing / completed / blocked / awaiting_approval + }) = _ToolExecution; +} + +@freezed +class ApprovalRequest with _$ApprovalRequest { + const factory ApprovalRequest({ + required String taskId, + required String command, + required RiskLevel riskLevel, + required String targetServer, + required DateTime expiresAt, + ApprovalStatus? status, // pending / approved / rejected + }) = _ApprovalRequest; +} + +// features/chat/domain/entities/stream_event.dart + +@freezed +sealed class StreamEvent with _$StreamEvent { + const factory StreamEvent.thinking({required String content}) = ThinkingEvent; + const factory StreamEvent.text({required String content}) = TextEvent; + const factory StreamEvent.toolUse({ + required String toolName, + required Map input, + }) = ToolUseEvent; + const factory StreamEvent.toolResult({ + required String toolName, + required String output, + required bool isError, + }) = ToolResultEvent; + const factory StreamEvent.approvalRequired({ + required String taskId, + required String command, + required int riskLevel, + }) = ApprovalRequiredEvent; + const factory StreamEvent.completed({required String summary}) = CompletedEvent; + const factory StreamEvent.error({required String message}) = ErrorEvent; + // ★ 驻留指令相关事件 + const factory StreamEvent.standingOrderDraft({ + required Map draft, // Agent 提取的指令草案 + }) = StandingOrderDraftEvent; + const factory StreamEvent.standingOrderConfirmed({ + required String orderId, + required String orderName, + }) = StandingOrderConfirmedEvent; +} +``` + +```dart +// features/chat/domain/repositories/chat_repository.dart + +abstract class ChatRepository { + /// 发送消息并获取流式响应 + Stream sendMessage({ + required String sessionId, + required String message, + List? attachments, + }); + + /// 语音输入转文字后发送 + Stream sendVoiceMessage({ + required String sessionId, + required String audioPath, + }); + + /// 获取会话历史 + Future>> getSessionHistory(String sessionId); + + /// 审批操作 + Future> approveCommand(String taskId); + Future> rejectCommand(String taskId, {String? reason}); + + /// 取消正在执行的任务 + Future> cancelTask(String sessionId); + + /// ★ 驻留指令:确认 Agent 提取的指令草案 + Future> confirmStandingOrder(String sessionId, Map draft); + + /// ★ 驻留指令:获取当前租户的所有驻留指令 + Future>> getStandingOrders({String? statusFilter}); +} +``` + +#### 4.2.2 Data 层 + +```dart +// features/chat/data/repositories/chat_repository_impl.dart + +class ChatRepositoryImpl implements ChatRepository { + final ChatRemoteDataSource _remote; + final ChatLocalDataSource _local; + final WebSocketClient _wsClient; + + ChatRepositoryImpl({ + required ChatRemoteDataSource remote, + required ChatLocalDataSource local, + required WebSocketClient wsClient, + }) : _remote = remote, + _local = local, + _wsClient = wsClient; + + @override + Stream sendMessage({ + required String sessionId, + required String message, + List? attachments, + }) async* { + // 1. 通过 REST 发起任务 + final taskResult = await _remote.createTask( + sessionId: sessionId, + message: message, + attachments: attachments, + ); + + // 2. 通过 WebSocket 接收流式事件 + yield* _wsClient.eventStream + .where((event) => event['sessionId'] == sessionId) + .map((event) => StreamEventModel.fromJson(event).toDomain()) + .takeWhile((event) => + event is! CompletedEvent && event is! ErrorEvent) + .handleError((error) { + // 错误处理 + }); + } + + @override + Future> approveCommand(String taskId) async { + try { + await _remote.approveCommand(taskId); + return const ApiResult.success(null); + } catch (e) { + return ApiResult.failure(Failure.unknown(message: e.toString())); + } + } +} +``` + +#### 4.2.3 Presentation 层 + +```dart +// features/chat/presentation/providers/chat_providers.dart + +/// 当前会话 ID +@riverpod +class CurrentSessionId extends _$CurrentSessionId { + @override + String? build() => null; + + void set(String id) => state = id; + void clear() => state = null; +} + +/// 消息列表(历史 + 实时) +@riverpod +class ChatMessages extends _$ChatMessages { + @override + List build() => []; + + void addMessage(ChatMessage message) { + state = [...state, message]; + } + + void updateLastAssistantMessage(String content) { + if (state.isEmpty) return; + final last = state.last; + if (last.role == MessageRole.assistant) { + state = [ + ...state.sublist(0, state.length - 1), + last.copyWith(content: last.content + content), + ]; + } + } + + void addToolExecution(ToolExecution tool) { + state = [ + ...state, + ChatMessage( + id: const Uuid().v4(), + role: MessageRole.assistant, + content: '', + timestamp: DateTime.now(), + type: MessageType.toolUse, + toolExecution: tool, + ), + ]; + } + + void updateToolResult(String toolName, String output, bool isError) { + // 找到最后一个匹配的 tool_use 消息并更新 + final index = state.lastIndexWhere( + (m) => m.toolExecution?.toolName == toolName && + m.toolExecution?.status == ToolStatus.executing, + ); + if (index >= 0) { + final msg = state[index]; + state = [ + ...state.sublist(0, index), + msg.copyWith( + toolExecution: msg.toolExecution!.copyWith( + output: output, + status: isError ? ToolStatus.error : ToolStatus.completed, + ), + ), + ...state.sublist(index + 1), + ]; + } + } +} + +/// Agent 状态 +@riverpod +class AgentStatus extends _$AgentStatus { + @override + AgentState build() => AgentState.idle; + + void setThinking() => state = AgentState.thinking; + void setExecuting(String toolName) => state = AgentState.executing; + void setWaitingApproval() => state = AgentState.waitingApproval; + void setIdle() => state = AgentState.idle; +} + +enum AgentState { idle, thinking, executing, waitingApproval } + +/// 发送消息用例 +@riverpod +class SendMessage extends _$SendMessage { + @override + FutureOr build() {} + + Future send(String message) async { + final sessionId = ref.read(currentSessionIdProvider); + if (sessionId == null) return; + + final repo = ref.read(chatRepositoryProvider); + final messages = ref.read(chatMessagesProvider.notifier); + final agentStatus = ref.read(agentStatusProvider.notifier); + + // 添加用户消息 + messages.addMessage(ChatMessage( + id: const Uuid().v4(), + role: MessageRole.user, + content: message, + timestamp: DateTime.now(), + )); + + // 创建空的 assistant 消息(用于流式填充) + messages.addMessage(ChatMessage( + id: const Uuid().v4(), + role: MessageRole.assistant, + content: '', + timestamp: DateTime.now(), + isStreaming: true, + )); + + agentStatus.setThinking(); + + // 监听流式事件 + await for (final event in repo.sendMessage( + sessionId: sessionId, + message: message, + )) { + switch (event) { + case ThinkingEvent(:final content): + agentStatus.setThinking(); + // 可选:显示思考过程 + break; + + case TextEvent(:final content): + messages.updateLastAssistantMessage(content); + break; + + case ToolUseEvent(:final toolName, :final input): + agentStatus.setExecuting(toolName); + messages.addToolExecution(ToolExecution( + toolName: toolName, + input: jsonEncode(input), + riskLevel: RiskLevel.l0, + status: ToolStatus.executing, + )); + break; + + case ToolResultEvent(:final toolName, :final output, :final isError): + messages.updateToolResult(toolName, output, isError); + break; + + case ApprovalRequiredEvent(:final taskId, :final command, :final riskLevel): + agentStatus.setWaitingApproval(); + messages.addMessage(ChatMessage( + id: const Uuid().v4(), + role: MessageRole.system, + content: '需要您的审批', + timestamp: DateTime.now(), + type: MessageType.approval, + approvalRequest: ApprovalRequest( + taskId: taskId, + command: command, + riskLevel: RiskLevel.fromInt(riskLevel), + targetServer: '', + expiresAt: DateTime.now().add(const Duration(minutes: 30)), + ), + )); + break; + + case CompletedEvent(): + case ErrorEvent(): + agentStatus.setIdle(); + break; + + // ★ 驻留指令:Agent 提取出指令草案,展示确认卡片 + case StandingOrderDraftEvent(:final draft): + messages.addMessage(ChatMessage( + id: const Uuid().v4(), + role: MessageRole.system, + content: '驻留指令草案', + timestamp: DateTime.now(), + type: MessageType.standingOrderDraft, + metadata: draft, + )); + break; + + case StandingOrderConfirmedEvent(:final orderId, :final orderName): + messages.addMessage(ChatMessage( + id: const Uuid().v4(), + role: MessageRole.assistant, + content: '✅ 驻留指令「$orderName」已创建($orderId)', + timestamp: DateTime.now(), + )); + break; + } + } + } +} +``` + +#### 4.2.4 对话页面 UI + +```dart +// features/chat/presentation/pages/chat_page.dart + +class ChatPage extends ConsumerWidget { + const ChatPage({super.key}); + + @override + Widget build(BuildContext context, WidgetRef ref) { + final messages = ref.watch(chatMessagesProvider); + final agentStatus = ref.watch(agentStatusProvider); + + return Scaffold( + appBar: AppBar( + title: const Text('IT0 运维助手'), + actions: [ + // Agent 状态指示器 + AgentStatusIndicator(status: agentStatus), + // 引擎切换按钮 + IconButton( + icon: const Icon(Icons.swap_horiz), + onPressed: () => _showEngineSelector(context, ref), + ), + ], + ), + body: Column( + children: [ + // 消息列表 + Expanded( + child: ListView.builder( + reverse: true, + itemCount: messages.length, + itemBuilder: (context, index) { + final message = messages[messages.length - 1 - index]; + return _buildMessageWidget(message, ref); + }, + ), + ), + // 输入区域 + ChatInputBar( + onSendText: (text) => ref.read(sendMessageProvider.notifier).send(text), + onVoiceStart: () => ref.read(voiceInputProvider.notifier).startListening(), + onVoiceEnd: () => ref.read(voiceInputProvider.notifier).stopListening(), + isAgentBusy: agentStatus != AgentState.idle, + ), + ], + ), + ); + } + + Widget _buildMessageWidget(ChatMessage message, WidgetRef ref) { + return switch (message.type) { + MessageType.toolUse => ToolExecutionCard( + execution: message.toolExecution!, + ), + MessageType.approval => ApprovalActionCard( + request: message.approvalRequest!, + onApprove: () => ref.read(chatRepositoryProvider) + .approveCommand(message.approvalRequest!.taskId), + onReject: () => ref.read(chatRepositoryProvider) + .rejectCommand(message.approvalRequest!.taskId), + ), + MessageType.standingOrderDraft => StandingOrderDraftCard( + draft: message.metadata!, + onConfirm: () => ref.read(chatRepositoryProvider) + .confirmStandingOrder( + ref.read(currentSessionIdProvider)!, + message.metadata!, + ), + onModify: (feedback) => ref.read(sendMessageProvider.notifier) + .send(feedback), // 用户反馈修改意见,继续对话 + ), + _ => MessageBubble(message: message), + }; + } +} +``` + +#### 4.2.5 ★ 驻留指令草案确认卡片 + +```dart +// features/chat/presentation/widgets/standing_order_draft_card.dart + +/// 当 Agent 从对话中提取出驻留指令草案时,展示此确认卡片 +/// +/// ┌─────────────────────────────────────────┐ +/// │ 📋 驻留指令草案 │ +/// │ │ +/// │ 名称: 每日生产巡检 │ +/// │ ⏰ 触发: 每天 08:00 │ +/// │ 🎯 目标: 所有 prod 服务器 │ +/// │ │ +/// │ 📌 自治操作: │ +/// │ • 检查 CPU/内存/磁盘 │ +/// │ • 磁盘 >85% 自动清理日志 │ +/// │ │ +/// │ 🚨 升级规则: │ +/// │ • 磁盘 >95% → 📞 电话通知 │ +/// │ • 其他异常 → 📱 短信通知 │ +/// │ │ +/// │ ⚠️ 不会执行: 重启服务、修改配置 │ +/// │ │ +/// │ [修改] [确认创建] │ +/// │ (语音/文字反馈) │ +/// └─────────────────────────────────────────┘ +/// +/// 用户可以: +/// - 点击「确认创建」→ 调用 confirmStandingOrder API +/// - 点击「修改」→ 弹出输入框,用户语音/文字说明修改意见 +/// - 直接语音说 "把时间改成7点" → Agent 更新草案后重新展示 +class StandingOrderDraftCard extends StatelessWidget { + final Map draft; + final VoidCallback onConfirm; + final ValueChanged onModify; + // ... +} +``` + +--- + +### 4.3 语音交互 + +#### 4.3.1 语音输入(STT) + +```dart +// features/chat/presentation/providers/voice_providers.dart + +@riverpod +class VoiceInput extends _$VoiceInput { + SpeechToText? _speech; + + @override + VoiceInputState build() => const VoiceInputState.idle(); + + Future startListening() async { + _speech ??= SpeechToText(); + final available = await _speech!.initialize( + onError: (error) => state = VoiceInputState.error(error.errorMsg), + ); + + if (!available) { + state = const VoiceInputState.error('语音识别不可用'); + return; + } + + state = const VoiceInputState.listening(partialText: ''); + + _speech!.listen( + onResult: (result) { + if (result.finalResult) { + state = VoiceInputState.completed(text: result.recognizedWords); + // 自动发送 + ref.read(sendMessageProvider.notifier).send(result.recognizedWords); + } else { + state = VoiceInputState.listening(partialText: result.recognizedWords); + } + }, + localeId: 'zh_CN', // 中文识别 + listenMode: ListenMode.dictation, + ); + } + + Future stopListening() async { + await _speech?.stop(); + } +} + +@freezed +sealed class VoiceInputState with _$VoiceInputState { + const factory VoiceInputState.idle() = VoiceIdle; + const factory VoiceInputState.listening({required String partialText}) = VoiceListening; + const factory VoiceInputState.completed({required String text}) = VoiceCompleted; + const factory VoiceInputState.error(String message) = VoiceError; +} +``` + +#### 4.3.2 语音输出(TTS) + +```dart +// features/chat/presentation/providers/tts_provider.dart + +@riverpod +class TtsPlayer extends _$TtsPlayer { + FlutterTts? _tts; + + @override + bool build() => false; // isPlaying + + Future speak(String text) async { + _tts ??= FlutterTts(); + await _tts!.setLanguage('zh-CN'); + await _tts!.setSpeechRate(0.5); + await _tts!.setVolume(1.0); + state = true; + await _tts!.speak(text); + _tts!.setCompletionHandler(() => state = false); + } + + Future stop() async { + await _tts?.stop(); + state = false; + } +} +``` + +--- + +### 4.4 Tasks(运维任务) + +```dart +// features/tasks/domain/entities/ops_task.dart + +@freezed +class OpsTask with _$OpsTask { + const factory OpsTask({ + required String id, + required TaskType type, // inspection / deployment / rollback / scaling / recovery + required String title, + String? description, + required TaskStatus status, // pending / running / waitingApproval / completed / failed + required TaskPriority priority, + required List targetServerIds, + String? runbookId, + String? agentSessionId, + required DateTime createdAt, + DateTime? completedAt, + String? resultSummary, + }) = _OpsTask; +} +``` + +#### 预设快捷任务 + +```dart +// features/tasks/presentation/widgets/quick_task_panel.dart + +class QuickTaskPanel extends ConsumerWidget { + // 预设的常用运维任务 + static const quickTasks = [ + QuickTask( + icon: Icons.health_and_safety, + label: '全面巡检', + prompt: '对所有服务器执行全面健康检查,包括 CPU、内存、磁盘、网络、关键服务状态,给出摘要报告', + type: TaskType.inspection, + ), + QuickTask( + icon: Icons.cleaning_services, + label: '日志清理', + prompt: '检查所有服务器的日志目录大小,清理 30 天前的旧日志文件,报告释放的空间', + type: TaskType.inspection, + ), + QuickTask( + icon: Icons.security, + label: '安全检查', + prompt: '检查各服务器的安全状态:SSH 登录记录、异常进程、端口开放情况、SSL 证书到期时间', + type: TaskType.inspection, + ), + QuickTask( + icon: Icons.backup, + label: '备份验证', + prompt: '检查所有数据库备份的状态、最后备份时间、备份文件大小,验证备份完整性', + type: TaskType.inspection, + ), + QuickTask( + icon: Icons.rocket_launch, + label: '部署应用', + prompt: '请告诉我要部署什么应用到哪个环境,我会帮你执行部署流程', + type: TaskType.deployment, + ), + QuickTask( + icon: Icons.terminal, + label: '自由对话', + prompt: null, // 直接打开对话页面 + type: null, + ), + ]; + + @override + Widget build(BuildContext context, WidgetRef ref) { + return GridView.builder( + shrinkWrap: true, + gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount( + crossAxisCount: 3, + mainAxisSpacing: 12, + crossAxisSpacing: 12, + ), + itemCount: quickTasks.length, + itemBuilder: (context, index) { + final task = quickTasks[index]; + return QuickTaskCard( + task: task, + onTap: () => _executeQuickTask(context, ref, task), + ); + }, + ); + } +} +``` + +--- + +### 4.5 Approvals(审批中心) + +```dart +// features/approvals/presentation/pages/approval_page.dart + +/// 审批中心 — 展示所有待审批的命令 +/// 每个审批卡片包含: +/// - 命令内容(高亮语法) +/// - 风险等级标签(L1/L2 颜色区分) +/// - 目标服务器 +/// - 剩余审批时间(倒计时) +/// - 批准 / 拒绝按钮 +/// - 可选:拒绝理由输入 + +/// 审批列表 provider +@riverpod +Stream> pendingApprovals(Ref ref) { + final wsClient = ref.watch(webSocketClientProvider); + + // 初始加载 + WebSocket 实时更新 + return wsClient.eventStream + .where((event) => event['type'] == 'approval_update') + .map((event) => (event['approvals'] as List) + .map((a) => ApprovalRequest.fromJson(a)) + .toList()); +} +``` + +--- + +### 4.6 Servers(服务器管理) + +```dart +// features/servers/domain/entities/server.dart + +@freezed +class Server with _$Server { + const factory Server({ + required String id, + required String name, + required String host, + required int port, + required Environment environment, // dev / staging / prod + required ServerRole role, // web / db / cache / worker / gateway + String? clusterId, + required ServerStatus status, // online / offline / warning / error + ServerMetrics? latestMetrics, + }) = _Server; +} + +@freezed +class ServerMetrics with _$ServerMetrics { + const factory ServerMetrics({ + required double cpuPercent, + required double memoryPercent, + required double diskPercent, + required double networkInMbps, + required double networkOutMbps, + required int uptimeSeconds, + required DateTime recordedAt, + }) = _ServerMetrics; +} +``` + +#### 服务器详情页 + +``` +┌──────────────────────────────────────────┐ +│ ← prod-1 [SSH] [More] │ +├──────────────────────────────────────────┤ +│ Status: ● Online Uptime: 45d 12h │ +│ IP: 10.0.1.11 Role: Web Server │ +│ Env: Production Cluster: main-k8s │ +├──────────────────────────────────────────┤ +│ │ +│ CPU ▓▓▓▓▓▓▓▓░░░░░░░░ 45% │ +│ MEM ▓▓▓▓▓▓▓▓▓▓░░░░░░ 62% │ +│ DISK ▓▓▓▓▓▓▓▓▓▓▓░░░░░ 71% │ +│ NET ↑ 12.3 Mbps ↓ 45.6 Mbps │ +│ │ +│ ── CPU 趋势 (24h) ────────────── │ +│ [========== 折线图 ==========] │ +│ │ +│ ── 最近告警 ────────────────── │ +│ ⚠ 14:23 内存使用率超过 80% │ +│ ● 13:15 已恢复 │ +│ │ +│ ── 快捷操作 ────────────────── │ +│ [查看日志] [重启服务] [健康检查] │ +│ │ +└──────────────────────────────────────────┘ +``` + +--- + +### 4.7 Alerts(告警中心) + +```dart +// features/alerts/presentation/providers/alert_providers.dart + +/// 实时告警流 +@riverpod +class AlertNotifier extends _$AlertNotifier { + @override + List build() => []; + + /// 监听 WebSocket 告警事件 + void startListening() { + final wsClient = ref.read(webSocketClientProvider); + wsClient.eventStream + .where((event) => event['type'] == 'alert_fired' || event['type'] == 'alert_resolved') + .listen((event) { + if (event['type'] == 'alert_fired') { + final alert = Alert.fromJson(event); + state = [alert, ...state]; + // 触发本地通知 + _showLocalNotification(alert); + } else { + // 更新已恢复的告警 + state = state.map((a) => + a.id == event['alertId'] ? a.copyWith(status: AlertStatus.resolved) : a + ).toList(); + } + }); + } + + void _showLocalNotification(Alert alert) { + // 使用 flutter_local_notifications 弹出系统通知 + // 严重告警振动 + 声音 + // 紧急告警使用全屏通知 + } +} +``` + +--- + +### 4.8 Terminal(远程终端) + +```dart +// features/terminal/presentation/pages/terminal_page.dart + +/// 远程终端 — 通过 Agent 执行 SSH 命令 +/// 使用 xterm.dart 渲染终端 UI +/// 用户输入的命令通过 WebSocket 发送到 agent-service +/// agent-service 通过 Claude Code 的 Bash 工具执行 +/// 输出实时回传渲染 + +class TerminalPage extends ConsumerStatefulWidget { + final String serverId; + + const TerminalPage({super.key, required this.serverId}); + + @override + ConsumerState createState() => _TerminalPageState(); +} + +class _TerminalPageState extends ConsumerState { + late Terminal terminal; + // xterm 终端实例 + WebSocket 双向绑定 + // 用户输入 → WebSocket → agent-service → SSH → 服务器 + // 服务器输出 → SSH → agent-service → WebSocket → xterm 渲染 +} +``` + +--- + +### 4.9 Settings(设置) + +```dart +// features/settings/presentation/pages/settings_page.dart + +/// 设置页面包含: +/// +/// 1. 引擎配置 +/// - 当前引擎类型(Claude Code CLI / Claude API / Custom) +/// - 切换引擎(下拉选择 + 确认对话框) +/// - Claude Code 路径配置 +/// - API Key 配置(仅 Claude API 模式) +/// +/// 2. 安全设置 +/// - 风险等级策略(Level 1/2 的超时时间) +/// - 命令黑名单管理 +/// - 自动审批的 Runbook 列表 +/// +/// 3. 通知设置 +/// - 各渠道开关(推送/短信/电话/邮件/Telegram/微信) +/// - 升级策略配置 +/// - 免打扰时段 +/// +/// 4. 语音设置 +/// - STT 语言选择 +/// - TTS 语速/音色 +/// - 语音唤醒词(可选) +/// +/// 5. 外观 +/// - 主题(深色/浅色/跟随系统) +/// - 终端字体大小 +/// - 仪表盘布局自定义 +/// +/// 6. ★ 租户管理 +/// - 当前租户名称 + ID +/// - 切换租户(弹出 TenantSelectPage 底部弹窗) +/// - 租户资源用量概览(服务器/用户/Sessions 配额) +``` + +--- + +### 4.10 ★ Standing Orders(驻留指令监控) + +```dart +// features/standing_orders/domain/entities/standing_order.dart + +@freezed +class StandingOrder with _$StandingOrder { + const factory StandingOrder({ + required String id, // SO-20260208-001 + required String name, + required String description, + required TriggerType triggerType, // cron / event / threshold / manual + required Map triggerConfig, + required Map targets, + required String agentPrompt, + required List skills, + required int maxRiskLevel, + required List escalationRules, + required OrderStatus status, // active / paused / archived + required DateTime createdAt, + DateTime? lastExecutedAt, + ExecutionSummary? lastExecution, + }) = _StandingOrder; +} + +@freezed +class StandingOrderExecution with _$StandingOrderExecution { + const factory StandingOrderExecution({ + required String id, + required String orderId, + required ExecutionStatus status, // running / completed / failed / awaiting_human / aborted + required DateTime startedAt, + DateTime? completedAt, + String? resultSummary, + List? actionsTaken, + String? error, + String? escalationChannel, + }) = _StandingOrderExecution; +} +``` + +```dart +// features/standing_orders/presentation/pages/standing_orders_page.dart + +/// 驻留指令列表页 +/// +/// ┌──────────────────────────────────────────┐ +/// │ 驻留指令 [筛选 ▾]│ +/// ├──────────────────────────────────────────┤ +/// │ │ +/// │ ┌──────────────────────────────────┐ │ +/// │ │ 📋 每日生产巡检 │ │ +/// │ │ ⏰ 每天 08:00 · ● 活跃 │ │ +/// │ │ 上次: 今天 08:00 ✓ 正常 │ │ +/// │ │ 下次: 明天 08:00 │ │ +/// │ │ [详情] [⏸ 暂停] │ │ +/// │ └──────────────────────────────────┘ │ +/// │ │ +/// │ ┌──────────────────────────────────┐ │ +/// │ │ 📋 磁盘空间监控 │ │ +/// │ │ ⚡ 告警触发 · ● 活跃 │ │ +/// │ │ 上次: 1小时前 ⚠ 已升级(电话) │ │ +/// │ │ [详情] [⏸ 暂停] │ │ +/// │ └──────────────────────────────────┘ │ +/// │ │ +/// │ ─── 今日执行摘要 ─── │ +/// │ 执行 5 次 · 成功 4 · 升级 1 │ +/// │ │ +/// │ 提示: 通过 AI 对话创建新的驻留指令 │ +/// │ [打开对话 →] │ +/// │ │ +/// ├──────────────────────────────────────────┤ +/// │ [Dashboard] [Chat] [Tasks] [Orders] │ +/// └──────────────────────────────────────────┘ +``` + +--- + +### 4.11 ★ Agent Call(Agent 来电响应) + +当 Agent 在无人值守执行中遇到紧急情况,通过 `comm-service` 发起语音电话或 IM 消息联系管理员。Flutter App 需要处理这种「**被动接收 Agent 通知**」的场景。 + +#### 4.11.1 来电通知流程(三层递进) + +``` +Agent 执行驻留指令 → 遇到紧急状况 + ↓ report_escalation tool +ops-service → comm-service → 三层递进升级 + │ + ├── ⏱ 第一层:App 推送唤醒 (0s) + │ ├── FCM 推送 + WebSocket → Flutter App 全屏通知 + │ ├── 用户点击「接听」→ 进入 App 内语音对话 + │ │ └── WebSocket 音频流 ←→ voice-service (Pipecat) + │ │ Agent 用语音详细汇报 + 听取指示(支持打断) + │ ├── 用户直接点击「批准/拒绝」→ 快速决策 + │ └── 用户点击「给 Agent 指示」→ 文字/语音输入 + │ + ├── ⏱ 第二层:Pipecat 电话语音对话 (2分钟无响应) + │ ├── Pipecat 通过 Twilio 拨出电话 → 手机铃声响起 + │ ├── 管理员接听 → Agent 直接开口说话(无 IVR 菜单): + │ │ "您好,我是 IT0 Agent。prod-2 磁盘 97%, + │ │ 我建议清理 /var/log 下超过7天的日志。您同意吗?" + │ ├── 管理员语音回复 → Agent 理解并执行 + │ └── 使用与 App 内完全相同的 Pipecat 引擎(Twilio 只是电话线路) + │ + └── ⏱ 第三层:扩大通知 (5+ 分钟无响应) + ├── IM (Telegram/企业微信) → 结构化消息 + 操作按钮 + ├── 短信 → 简要描述 + 回复 Y/N + ├── 通知备用联系人(重复第一层) + └── 10+ 分钟 → 邮件通知管理组 +``` + +**语音对话引擎说明**: +IT0 使用 **Pipecat**(GitHub 10.1k stars)自部署实时语音对话引擎: +- **STT**:faster-whisper(本地 GPU,中英文自动识别) +- **TTS**:Kokoro-82M(82M 参数,中英双语,Apache 开源) +- **LLM**:Claude API(Pipecat 原生 Anthropic 集成) +- **VAD**:Silero(语音活动检测,支持打断 barge-in) +- **延迟**:< 500ms 端到端(本地 GPU 推理) +- **电话**:2 分钟 App 无响应 → Pipecat 通过 Twilio 拨号 → Agent 接通即说话(无 IVR) + +#### 4.11.2 App 内 Agent 通知卡片 + +```dart +// features/agent_call/presentation/widgets/agent_escalation_card.dart + +/// 当 Agent 通过 WebSocket/FCM 推送紧急升级通知时, +/// App 展示全屏覆盖式通知(类似来电界面) +/// +/// ┌──────────────────────────────────────────┐ +/// │ │ +/// │ 🤖 IT0 Agent │ +/// │ 紧 急 通 知 │ +/// │ │ +/// │ ───────────────────────────────── │ +/// │ │ +/// │ 驻留指令: 每日生产巡检 │ +/// │ 服务器: prod-2 (10.0.1.22) │ +/// │ │ +/// │ ⚠️ 磁盘使用率已达 97% │ +/// │ Agent 建议: 紧急清理 /var/log 目录 │ +/// │ │ +/// │ 当前等待您的决策... │ +/// │ │ +/// │ ───────────────────────────────── │ +/// │ │ +/// │ ┌──────────┐ ┌──────────────┐ │ +/// │ │ ❌ 拒绝 │ │ ✅ 批准执行 │ │ +/// │ │ (中止) │ │ │ │ +/// │ └──────────┘ └──────────────┘ │ +/// │ │ +/// │ ┌──────────────────────────────┐ │ +/// │ │ 🎙️ 语音对话(详细沟通) │ │ +/// │ │ → 接入 voice-service Pipecat │ │ +/// │ └──────────────────────────────┘ │ +/// │ │ +/// │ ┌──────────────────────────────┐ │ +/// │ │ 💬 文字指示... │ │ +/// │ └──────────────────────────────┘ │ +/// │ │ +/// │ ⏱ 2 分钟内未响应将升级为电话通知 │ +/// │ │ +/// └──────────────────────────────────────────┘ +/// +/// 交互方式: +/// - 「批准执行」→ 调用 API 回复 approve → Agent 继续执行 +/// - 「拒绝」→ 调用 API 回复 reject → Agent 中止任务 +/// - 「语音对话」→ 跳转 VoiceCallPage,建立 WebSocket 音频流到 voice-service +/// Agent 用语音详细汇报 + 听取管理员语音指示(支持打断) +/// - 「文字指示」→ 弹出输入框,输入文字指令 +/// 例如 "不要清理,先查看哪些日志最大" → Agent 按指示执行 +/// - 2分钟无响应 → Pipecat 通过 Twilio 拨出电话,Agent 接通即语音对话 +class AgentEscalationCard extends ConsumerWidget { /* ... */ } +``` + +#### 4.11.3 可打断式语音对话(Pipecat Voice Call) + +```dart +// features/agent_call/presentation/pages/voice_call_page.dart + +/// App 内实时语音对话页面 — 连接 voice-service (Pipecat) +/// +/// ┌──────────────────────────────────────────┐ +/// │ │ +/// │ 🤖 IT0 Agent │ +/// │ 实 时 语 音 对 话 │ +/// │ │ +/// │ ◉ 正在通话 02:35 │ +/// │ │ +/// │ Agent: "prod-2 的磁盘使用率已达97%, │ +/// │ 我建议清理 /var/log 下超过7天的 │ +/// │ 日志文件。您同意吗?" │ +/// │ │ +/// │ [===== 语音波形可视化 =====] │ +/// │ │ +/// │ 💡 您可以随时打断说话 │ +/// │ │ +/// │ ┌────┐ ┌──────┐ ┌────┐ │ +/// │ │ 🔇 │ │ ✅ │ │ 📞 │ │ +/// │ │静音 │ │快速批准│ │挂断 │ │ +/// │ └────┘ └──────┘ └────┘ │ +/// │ │ +/// │ ── 对话记录(实时转写)── │ +/// │ Agent: prod-2 磁盘使用率97%... │ +/// │ You: 先看看是哪些文件最大 │ +/// │ Agent: 好的,正在执行 du -sh... │ +/// │ Agent: 最大的文件是 access.log 2.1GB... │ +/// │ You: 清理超过3天的日志吧 │ +/// │ Agent: 好的,正在执行清理... │ +/// │ │ +/// └──────────────────────────────────────────┘ +/// +/// ★ 技术实现(基于 Pipecat voice-service): +/// +/// ┌─ Flutter App ─────────────────────────────────────┐ +/// │ │ +/// │ 麦克风录音 (PCM 16kHz) │ +/// │ ↓ │ +/// │ WebSocket 发送音频帧 ───→ voice-service:3008 │ +/// │ │ │ +/// │ ├─ Silero VAD │ +/// │ │ (检测说话/沉默) │ +/// │ ├─ faster-whisper │ +/// │ │ (语音→文字) │ +/// │ ├─ Claude API │ +/// │ │ (理解+决策+回复) │ +/// │ └─ Kokoro-82M │ +/// │ (文字→语音) │ +/// │ ↓ │ +/// │ WebSocket 接收音频帧 ←──── 回复音频流 │ +/// │ ↓ │ +/// │ AudioPlayer 播放回复语音 │ +/// │ + │ +/// │ 实时转写文字显示在底部(辅助理解 + 留痕) │ +/// │ │ +/// └────────────────────────────────────────────────────┘ +/// +/// 关键特性: +/// - 打断(barge-in):用户开始说话 → Pipecat VAD 检测 → +/// 自动停止 TTS 播放 + 取消 LLM 正在生成的回复 +/// - 延迟 < 500ms:所有 STT/TTS 在 voice-service 本地 GPU 运行 +/// - 实时转写:voice-service 通过 WebSocket 同时发送音频帧 + 文字转写 +/// - 自动检测语言:中文/英文自动切换(faster-whisper 多语言支持) +/// - 对话上下文:voice-service 从 agent-service 获取驻留指令上下文 +class VoiceCallPage extends ConsumerStatefulWidget { /* ... */ } +``` + +```dart +// features/agent_call/data/voice_session_service.dart + +/// 管理与 voice-service 的 WebSocket 音频流连接 +class VoiceSessionService { + WebSocketChannel? _channel; + final AudioRecorder _recorder; // record 包 + final AudioPlayer _player; // just_audio 包 + + /// 建立语音对话连接 + Future connect({ + required String voiceSessionId, + required String baseUrl, // voice-service URL + }) async { + _channel = WebSocketChannel.connect( + Uri.parse('wss://$baseUrl/ws/voice/$voiceSessionId'), + ); + + // 接收 voice-service 的消息(音频帧 + 转写文字) + _channel!.stream.listen((data) { + if (data is List) { + // 二进制:音频帧 → 播放 + _player.feedPcmData(Uint8List.fromList(data)); + } else { + // JSON:转写文字 / 状态更新 + final msg = jsonDecode(data as String); + switch (msg['type']) { + case 'transcript': // Agent 或用户的转写文字 + onTranscript?.call(msg['role'], msg['text']); + case 'agent_action': // Agent 正在执行的操作 + onAgentAction?.call(msg['action']); + case 'session_end': // 对话结束 + disconnect(); + } + } + }); + + // 开始录音,持续发送音频帧到 voice-service + await _startStreaming(); + } + + Future _startStreaming() async { + final stream = await _recorder.startStream(RecordConfig( + encoder: AudioEncoder.pcm16bits, + sampleRate: 16000, + numChannels: 1, + )); + stream.listen((data) { + _channel?.sink.add(data); // PCM 音频帧 → voice-service + }); + } + + Future disconnect() async { + await _recorder.stop(); + await _channel?.sink.close(); + } +} +``` + +#### 4.11.4 Agent Call Provider + +```dart +// features/agent_call/presentation/providers/agent_call_providers.dart + +/// 监听 WebSocket 的升级通知事件 +@Riverpod(keepAlive: true) +class AgentCallListener extends _$AgentCallListener { + VoiceSessionService? _voiceSession; + + @override + AgentCallState build() => const AgentCallState.idle(); + + void initialize() { + final wsClient = ref.read(webSocketClientProvider); + wsClient.eventStream + .where((event) => event['type'] == 'escalation_notification') + .listen((event) { + final notification = EscalationNotification.fromJson(event); + + switch (notification.priority) { + case 'urgent': + // 紧急:全屏通知 + 振动 + 声音(类似来电) + state = AgentCallState.incoming(notification: notification); + _showFullScreenNotification(notification); + break; + case 'high': + // 高优先级:弹出通知卡片 + state = AgentCallState.incoming(notification: notification); + _showOverlayNotification(notification); + break; + case 'normal': + // 普通:系统通知栏 + _showSystemNotification(notification); + break; + case 'low': + // 低优先级:静默记录 + break; + } + }); + } + + /// 用户快速响应(批准/拒绝) + Future respond(String action, {String? instruction}) async { + final notification = (state as AgentCallIncoming).notification; + await ref.read(chatRepositoryProvider).respondToEscalation( + executionId: notification.executionId, + action: action, // 'approve' | 'reject' | 'instruct' + instruction: instruction, + ); + state = const AgentCallState.idle(); + } + + /// ★ 用户选择「语音对话」→ 连接 voice-service (Pipecat) + Future startVoiceCall() async { + final notification = (state as AgentCallIncoming).notification; + _voiceSession = VoiceSessionService(); + + state = AgentCallState.inCall( + notification: notification, + duration: Duration.zero, + ); + + // 连接 Pipecat voice-service WebSocket 音频流 + await _voiceSession!.connect( + voiceSessionId: notification.voiceSessionId, // 预创建的语音会话 ID + baseUrl: ref.read(configProvider).voiceServiceUrl, + ); + + // 启动通话计时 + _startTimer(); + } + + /// 挂断语音对话 + Future endVoiceCall() async { + await _voiceSession?.disconnect(); + _voiceSession = null; + state = const AgentCallState.idle(); + } +} + +@freezed +sealed class AgentCallState with _$AgentCallState { + const factory AgentCallState.idle() = AgentCallIdle; + const factory AgentCallState.incoming({ + required EscalationNotification notification, + }) = AgentCallIncoming; + const factory AgentCallState.inCall({ + required EscalationNotification notification, + required Duration duration, + }) = AgentCallInProgress; +} + +/// 升级通知数据 +@freezed +class EscalationNotification with _$EscalationNotification { + const factory EscalationNotification({ + required String executionId, + required String orderId, + required String orderName, + required String serverName, + required String summary, + required String suggestion, + required String priority, // urgent/high/normal/low + required String voiceSessionId, // ★ 预创建的 voice-service 会话 ID + }) = _EscalationNotification; + + factory EscalationNotification.fromJson(Map json) => + _$EscalationNotificationFromJson(json); +} +``` + +--- + +## 5. 路由配置 + +```dart +// core/router/app_router.dart + +final appRouter = GoRouter( + initialLocation: '/dashboard', + redirect: (context, state) { + // 未登录重定向到登录页 + final isLoggedIn = /* check auth state */; + if (!isLoggedIn && state.matchedLocation != '/login') { + return '/login'; + } + // ★ 多租户:已登录但未选择租户 → 跳转租户选择页 + final hasTenant = /* check currentTenantProvider != null */; + if (isLoggedIn && !hasTenant && state.matchedLocation != '/select-tenant') { + return '/select-tenant'; + } + return null; + }, + routes: [ + GoRoute(path: '/login', builder: (_, __) => const LoginPage()), + // ★ 登录后的租户选择页(多租户时才显示,单租户自动跳过) + GoRoute(path: '/select-tenant', builder: (_, __) => const TenantSelectPage()), + + // 底部导航 Shell + ShellRoute( + builder: (_, __, child) => MainShell(child: child), + routes: [ + GoRoute( + path: '/dashboard', + builder: (_, __) => const DashboardPage(), + ), + GoRoute( + path: '/chat', + builder: (_, __) => const ChatPage(), + routes: [ + GoRoute( + path: ':sessionId', + builder: (_, state) => ChatPage( + sessionId: state.pathParameters['sessionId'], + ), + ), + ], + ), + GoRoute( + path: '/tasks', + builder: (_, __) => const TasksPage(), + routes: [ + GoRoute( + path: ':taskId', + builder: (_, state) => TaskDetailPage( + taskId: state.pathParameters['taskId']!, + ), + ), + ], + ), + GoRoute( + path: '/alerts', + builder: (_, __) => const AlertsPage(), + ), + GoRoute( + path: '/servers', + builder: (_, __) => const ServersPage(), + routes: [ + GoRoute( + path: ':serverId', + builder: (_, state) => ServerDetailPage( + serverId: state.pathParameters['serverId']!, + ), + ), + GoRoute( + path: ':serverId/terminal', + builder: (_, state) => TerminalPage( + serverId: state.pathParameters['serverId']!, + ), + ), + ], + ), + GoRoute( + path: '/approvals', + builder: (_, __) => const ApprovalsPage(), + ), + // ★ 驻留指令 + GoRoute( + path: '/standing-orders', + builder: (_, __) => const StandingOrdersPage(), + routes: [ + GoRoute( + path: ':orderId', + builder: (_, state) => StandingOrderDetailPage( + orderId: state.pathParameters['orderId']!, + ), + ), + ], + ), + ], + ), + + GoRoute( + path: '/settings', + builder: (_, __) => const SettingsPage(), + ), + // ★ Agent 来电响应(全屏覆盖式,通过 overlay 或 push 导航进入) + GoRoute( + path: '/agent-call', + builder: (_, __) => const AgentEscalationPage(), + ), + GoRoute( + path: '/agent-voice-call', + builder: (_, __) => const VoiceCallPage(), + ), + ], +); +``` + +--- + +## 6. 主题设计 + +```dart +// core/theme/app_theme.dart + +class AppTheme { + /// IT0 使用深色主题为主(运维工具审美) + static ThemeData get dark => ThemeData( + brightness: Brightness.dark, + colorScheme: ColorScheme.dark( + primary: const Color(0xFF6C63FF), // 主色:偏紫的蓝 + secondary: const Color(0xFF03DAC6), // 辅助色:青绿 + surface: const Color(0xFF1E1E2E), // 卡片背景 + error: const Color(0xFFCF6679), + ), + scaffoldBackgroundColor: const Color(0xFF0D0D1A), // 深色背景 + cardTheme: CardTheme( + color: const Color(0xFF1E1E2E), + elevation: 2, + shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)), + ), + // 终端风格字体 + textTheme: const TextTheme( + bodyMedium: TextStyle(fontFamily: 'JetBrains Mono', fontSize: 14), + ), + ); + + /// 状态颜色 + static const statusColors = { + 'online': Color(0xFF4CAF50), // 绿色 + 'warning': Color(0xFFFF9800), // 橙色 + 'error': Color(0xFFF44336), // 红色 + 'offline': Color(0xFF9E9E9E), // 灰色 + }; + + /// 风险等级颜色 + static const riskColors = { + 0: Color(0xFF4CAF50), // L0 绿色 — 安全 + 1: Color(0xFFFF9800), // L1 橙色 — 需确认 + 2: Color(0xFFF44336), // L2 红色 — 需审批 + 3: Color(0xFF9C27B0), // L3 紫色 — 禁止 + }; +} +``` + +--- + +## 7. Dependency Injection(Riverpod Provider 树) + +```dart +// 全局基础设施 Providers + +@Riverpod(keepAlive: true) +AppConfig appConfig(Ref ref) => AppConfig.fromEnvironment(); + +@Riverpod(keepAlive: true) +DioClient dioClient(Ref ref) { + final config = ref.watch(appConfigProvider); + final tokenStorage = ref.watch(tokenStorageProvider); + return DioClient(config: config, tokenStorage: tokenStorage); +} + +@Riverpod(keepAlive: true) +WebSocketClient webSocketClient(Ref ref) { + final config = ref.watch(appConfigProvider); + final tokenStorage = ref.watch(tokenStorageProvider); + final client = WebSocketClient(config: config, tokenStorage: tokenStorage); + // App 启动时自动连接 + client.connect(); + ref.onDispose(() => client.dispose()); + return client; +} + +@Riverpod(keepAlive: true) +TokenStorage tokenStorage(Ref ref) => TokenStorage(); + +// ★ 多租户 Providers + +/// 当前用户可访问的租户列表(登录后获取) +@Riverpod(keepAlive: true) +class TenantList extends _$TenantList { + @override + Future> build() async { + final dio = ref.watch(dioClientProvider); + final response = await dio.dio.get('/tenants'); + return (response.data as List) + .map((e) => TenantSummary.fromJson(e)) + .toList(); + } +} + +/// 当前选中的租户(持久化到本地存储) +@Riverpod(keepAlive: true) +class CurrentTenant extends _$CurrentTenant { + @override + String? build() { + // 从 SharedPreferences 恢复上次选中的租户 + final prefs = ref.watch(sharedPreferencesProvider); + return prefs.getString('current_tenant_id'); + } + + void switchTenant(String tenantId) { + state = tenantId; + final prefs = ref.read(sharedPreferencesProvider); + prefs.setString('current_tenant_id', tenantId); + // 切换后使所有数据 Provider 失效 → 触发重新拉取 + ref.invalidateSelf(); + } +} + +/// Dio 拦截器:自动读取 currentTenantProvider 并注入到每个请求 +// core/tenant/tenant_interceptor.dart +class TenantInterceptor extends Interceptor { + // 通过 ProviderContainer 全局访问(非 widget 上下文) + static String? _currentTenantId; + static void updateTenantId(String? id) => _currentTenantId = id; + + @override + void onRequest(RequestOptions options, RequestInterceptorHandler handler) { + if (_currentTenantId != null) { + options.headers['X-Tenant-Id'] = _currentTenantId; + } + handler.next(options); + } +} + +// Feature-level Providers(以 chat 为例) + +@riverpod +ChatRemoteDataSource chatRemoteDataSource(Ref ref) { + final dio = ref.watch(dioClientProvider); + return ChatRemoteDataSource(dio: dio.dio); +} + +@riverpod +ChatLocalDataSource chatLocalDataSource(Ref ref) { + return ChatLocalDataSource(); +} + +@riverpod +ChatRepository chatRepository(Ref ref) { + return ChatRepositoryImpl( + remote: ref.watch(chatRemoteDataSourceProvider), + local: ref.watch(chatLocalDataSourceProvider), + wsClient: ref.watch(webSocketClientProvider), + ); +} +``` + +--- + +## 8. 推送通知集成 + +```dart +// core/notifications/fcm_service.dart + +/// Firebase Cloud Messaging 集成 +/// +/// 1. 后端 comm-service 在以下场景发送 FCM 推送: +/// - 告警触发(severity >= warning) +/// - 审批请求(需要用户操作) +/// - 任务完成/失败 +/// - 升级通知(短信/电话后仍需 App 确认) +/// +/// 2. 前端处理推送: +/// - 前台:显示应用内 Snackbar / Banner +/// - 后台:系统通知栏 +/// - 点击通知:导航到对应页面(告警详情/审批/任务详情) +/// +/// 3. 通知 channel 分级: +/// - high: 紧急告警、审批请求(振动+声音+全屏) +/// - default: 一般告警、任务完成 +/// - low: 信息通知 +``` + +--- + +## 9. 开发规范 + +### 9.1 命名规范 + +| 类型 | 规范 | 示例 | +|------|------|------| +| 文件名 | snake_case | `chat_repository_impl.dart` | +| 类名 | PascalCase | `ChatRepositoryImpl` | +| 变量/方法 | camelCase | `sendMessage()` | +| 常量 | camelCase | `maxReconnectAttempts` | +| Provider | camelCase + Provider 后缀 | `chatRepositoryProvider` | +| 枚举值 | camelCase | `TaskStatus.waitingApproval` | + +### 9.2 分层规则 + +``` +presentation → domain ← data + ↓ ↑ ↓ + widgets entities models + providers usecases datasources + pages repos(抽象) repos(实现) +``` + +- **domain 层零依赖**:不导入 Flutter、Riverpod、Dio 等任何外部包 +- **data 层实现 domain 接口**:Repository 实现、Model ↔ Entity 转换 +- **presentation 层**:只依赖 domain 层的 entities 和 usecases + +### 9.3 代码生成 + +```bash +# freezed + json_serializable + riverpod_generator +flutter pub run build_runner build --delete-conflicting-outputs +``` + +每个 `@freezed` 类都需要 `part '文件名.freezed.dart'` 和 `part '文件名.g.dart'`。 + +--- + +## 10. 开发路线图 + +### Phase 1: 基础骨架(Week 1-2) +- [ ] Flutter 项目初始化(Riverpod + go_router + Dio) +- [ ] 核心基础设施(DioClient、WebSocketClient、主题) +- [ ] auth feature(登录页、JWT 存储、自动刷新) +- [ ] 底部导航 Shell + 空白页面框架 + +### Phase 2: 核心交互(Week 3-4) +- [ ] chat feature 完整实现(文字对话 + 流式渲染) +- [ ] 工具执行卡片(显示 Bash 命令和输出) +- [ ] 审批操作卡片(内联批准/拒绝) +- [ ] Agent 状态指示器(thinking/executing/waiting) + +### Phase 3: 运维功能(Week 5-6) +- [ ] dashboard feature(仪表盘 + 快捷操作) +- [ ] servers feature(服务器列表 + 详情 + 指标图表) +- [ ] tasks feature(任务列表 + 创建 + 详情) +- [ ] 预设快捷任务面板 + +### Phase 4: 语音与通知(Week 7-8) +- [ ] 语音输入(speech_to_text) +- [ ] 语音输出(flutter_tts) +- [ ] FCM 推送集成 +- [ ] 告警中心 + 本地通知 + +### Phase 5: 高级功能(Week 9-10) +- [ ] terminal feature(远程终端) +- [ ] approvals feature(审批中心独立页面) +- [ ] settings feature(引擎切换、通知配置、安全策略) +- [ ] 离线缓存 + 暗色/亮色主题切换 + +--- + +## 11. pubspec.yaml 参考 + +```yaml +name: it0_app +description: IT Operations Intelligent Agent + +environment: + sdk: '>=3.2.0 <4.0.0' + +dependencies: + flutter: + sdk: flutter + + # 状态管理 + flutter_riverpod: ^2.5.0 + riverpod_annotation: ^2.3.0 + + # 路由 + go_router: ^14.0.0 + + # 网络 + dio: ^5.4.0 + web_socket_channel: ^3.0.0 + + # 数据模型 + freezed_annotation: ^2.4.0 + json_annotation: ^4.9.0 + + # 本地存储 + hive: ^4.0.0 + hive_flutter: ^2.0.0 + shared_preferences: ^2.3.0 + + # 语音 + speech_to_text: ^7.0.0 + flutter_tts: ^4.0.0 + + # 推送通知 + firebase_messaging: ^15.0.0 + flutter_local_notifications: ^18.0.0 + + # UI 组件 + fl_chart: ^0.69.0 # 图表 + flutter_xterm: ^4.0.0 # 终端模拟 + flutter_syntax_view: ^4.0.0 # 代码高亮 + shimmer: ^3.0.0 # 加载骨架屏 + lottie: ^3.0.0 # 动画 + + # 工具 + uuid: ^4.4.0 + intl: ^0.19.0 + logger: ^2.4.0 + connectivity_plus: ^6.0.0 # 网络状态检测 + +dev_dependencies: + flutter_test: + sdk: flutter + + # 代码生成 + build_runner: ^2.4.0 + freezed: ^2.5.0 + json_serializable: ^6.8.0 + riverpod_generator: ^2.4.0 + + # Lint + flutter_lints: ^4.0.0 + + # 测试 + mockito: ^5.4.0 + mocktail: ^1.0.0 +``` diff --git a/docs/web-admin-guide.md b/docs/web-admin-guide.md new file mode 100644 index 0000000..0bd1e99 --- /dev/null +++ b/docs/web-admin-guide.md @@ -0,0 +1,1984 @@ +# IT0 PC Web 管理前端开发指导文档 + +> IT Operations Intelligent Agent — PC Web 管理控制台(React + Next.js + Zustand/Redux Toolkit) + +## 1. 项目概述 + +### 1.1 定位 + +IT0 Web Admin 是**管理员级别的 PC 端控制台**,与 Flutter App(移动端运维驾驶舱)互补: + +| 维度 | Flutter App(移动端) | Web Admin(PC 端) | +|------|---------------------|-------------------| +| 角色 | 一线运维操作 | 管理配置 + 深度分析 | +| 场景 | 随时随地审批、巡检、对话 | 坐在电脑前做系统配置 | +| 核心功能 | AI 对话、快捷任务、审批、告警 | Agent 配置、Runbook 管理、权限控制、审计回溯 | +| 数据展示 | 简洁仪表盘 | 复杂表格、多维图表、终端全屏 | + +### 1.2 技术栈 + +| 层面 | 技术选型 | +|------|---------| +| 框架 | Next.js 14+ (App Router) | +| 语言 | TypeScript (strict mode) | +| 状态管理 | Zustand(轻量级 UI 状态) + Redux Toolkit(复杂业务状态) | +| 样式 | Tailwind CSS + shadcn/ui 组件库 | +| 数据请求 | TanStack Query (React Query) v5 | +| 表单 | React Hook Form + Zod | +| 表格 | TanStack Table v8 | +| 图表 | Recharts / Tremor | +| 终端 | xterm.js | +| 代码编辑器 | Monaco Editor (Runbook/Prompt 编辑) | +| WebSocket | 原生 WebSocket + 自动重连 | +| 测试 | Vitest + React Testing Library | +| Lint | ESLint + Prettier | + +### 1.3 设计原则 + +- **Clean Architecture**:严格分层(domain → application → infrastructure → presentation) +- **Zustand + Redux Toolkit 混合**:Zustand 管理 UI/交互状态(侧边栏、模态框、主题),Redux Toolkit 管理复杂业务状态(Agent 配置、Runbook 编辑、权限矩阵) +- **Server Components 优先**:Next.js App Router 的 RSC 用于静态/数据密集型页面,Client Components 用于交互 +- **管理员体验**:大屏优化、键盘快捷键、批量操作、深度搜索 +- **多租户感知**:顶栏租户选择器 + 所有 API 请求自动携带 `X-Tenant-Id`,超级管理员可切换租户视角 + +--- + +## 2. 项目结构 + +### 2.1 整体目录 + +``` +it0-web-admin/ +├── src/ +│ ├── app/ # Next.js App Router(路由层) +│ │ ├── layout.tsx # 根布局(Providers、主题) +│ │ ├── page.tsx # 首页(重定向到 dashboard) +│ │ ├── (auth)/ +│ │ │ ├── login/page.tsx +│ │ │ └── layout.tsx +│ │ ├── (admin)/ # 管理后台布局组 +│ │ │ ├── layout.tsx # 侧边栏 + 顶栏布局 +│ │ │ ├── dashboard/page.tsx +│ │ │ ├── agent-config/ # Agent 配置 +│ │ │ │ ├── page.tsx # 引擎管理 +│ │ │ │ ├── prompts/page.tsx # System Prompt 管理 +│ │ │ │ ├── hooks/page.tsx # Hook 脚本管理 +│ │ │ │ └── skills/ # ★ Claude Skills 管理 +│ │ │ │ ├── page.tsx # Skill 列表 +│ │ │ │ ├── [name]/page.tsx # Skill 详情/编辑 +│ │ │ │ └── new/page.tsx # 新建 Skill +│ │ │ ├── runbooks/ # Runbook 管理 +│ │ │ │ ├── page.tsx # 列表 +│ │ │ │ ├── [id]/page.tsx # 详情/编辑 +│ │ │ │ └── new/page.tsx # 新建 +│ │ │ ├── standing-orders/ # ★ 驻留指令管理 +│ │ │ │ ├── page.tsx # 指令列表 + 执行概况 +│ │ │ │ ├── [id]/page.tsx # 指令详情/编辑/执行历史 +│ │ │ │ └── [id]/executions/page.tsx # 执行记录详情 +│ │ │ ├── servers/ # 服务器管理 +│ │ │ │ ├── page.tsx +│ │ │ │ ├── [id]/page.tsx +│ │ │ │ └── clusters/page.tsx +│ │ │ ├── security/ # 安全配置 +│ │ │ │ ├── risk-rules/page.tsx # 风险规则管理 +│ │ │ │ ├── credentials/page.tsx # 凭证管理 +│ │ │ │ └── permissions/page.tsx # RBAC 权限管理 +│ │ │ ├── monitoring/ # 监控配置 +│ │ │ │ ├── alert-rules/page.tsx # 告警规则 +│ │ │ │ ├── health-checks/page.tsx # 健康检查配置 +│ │ │ │ └── metrics/page.tsx # 指标面板 +│ │ │ ├── communication/ # 通信配置 +│ │ │ │ ├── channels/page.tsx # 渠道管理 +│ │ │ │ ├── contacts/page.tsx # 联系人管理 +│ │ │ │ └── escalation/page.tsx # 升级策略 +│ │ │ ├── audit/ # 审计日志 +│ │ │ │ └── page.tsx +│ │ │ ├── sessions/ # 会话历史 +│ │ │ │ ├── page.tsx +│ │ │ │ └── [id]/page.tsx # 会话回放 +│ │ │ ├── tenants/ # ★ 多租户管理(超级管理员) +│ │ │ │ ├── page.tsx # 租户列表 +│ │ │ │ ├── [id]/page.tsx # 租户详情/编辑 +│ │ │ │ └── new/page.tsx # 新建租户 +│ │ │ └── terminal/ # Web 终端 +│ │ │ └── page.tsx +│ │ └── api/ # Next.js API Routes(BFF 层) +│ │ ├── auth/[...nextauth]/route.ts +│ │ └── proxy/[...path]/route.ts # 反向代理到后端微服务 +│ │ +│ ├── core/ # 核心基础设施 +│ │ ├── config/ +│ │ │ └── app-config.ts # 环境配置 +│ │ ├── api/ +│ │ │ ├── api-client.ts # Fetch 封装(token 注入) +│ │ │ ├── websocket-client.ts # WebSocket 管理 +│ │ │ └── query-keys.ts # TanStack Query key 管理 +│ │ ├── auth/ +│ │ │ ├── auth-provider.tsx # 认证上下文 +│ │ │ └── use-auth.ts # 认证 hook +│ │ ├── theme/ +│ │ │ └── theme-config.ts +│ │ └── utils/ +│ │ ├── date.ts +│ │ ├── format.ts +│ │ └── cn.ts # Tailwind className 合并 +│ │ +│ ├── domain/ # 领域层(纯 TypeScript,零框架依赖) +│ │ ├── entities/ +│ │ │ ├── agent-engine.ts +│ │ │ ├── skill.ts # ★ Claude Skill 实体 +│ │ │ ├── server.ts +│ │ │ ├── runbook.ts +│ │ │ ├── alert-rule.ts +│ │ │ ├── risk-rule.ts +│ │ │ ├── credential.ts +│ │ │ ├── user.ts +│ │ │ ├── role.ts +│ │ │ ├── audit-log.ts +│ │ │ ├── session.ts +│ │ │ ├── contact.ts +│ │ │ ├── escalation-policy.ts +│ │ │ ├── tenant.ts # ★ 租户实体 +│ │ │ └── standing-order.ts # ★ 驻留指令实体 +│ │ ├── value-objects/ +│ │ │ ├── engine-type.ts +│ │ │ ├── risk-level.ts +│ │ │ ├── server-environment.ts +│ │ │ ├── alert-severity.ts +│ │ │ └── channel-type.ts +│ │ └── repositories/ # 仓储接口(抽象) +│ │ ├── agent-config.repository.ts +│ │ ├── server.repository.ts +│ │ ├── runbook.repository.ts +│ │ ├── alert-rule.repository.ts +│ │ ├── security.repository.ts +│ │ ├── audit.repository.ts +│ │ └── communication.repository.ts +│ │ +│ ├── application/ # 应用层(用例) +│ │ ├── use-cases/ +│ │ │ ├── agent-config/ +│ │ │ │ ├── switch-engine.ts +│ │ │ │ ├── update-system-prompt.ts +│ │ │ │ └── manage-hooks.ts +│ │ │ ├── runbooks/ +│ │ │ │ ├── create-runbook.ts +│ │ │ │ ├── update-runbook.ts +│ │ │ │ ├── delete-runbook.ts +│ │ │ │ └── test-runbook.ts +│ │ │ ├── servers/ +│ │ │ │ ├── add-server.ts +│ │ │ │ ├── update-server.ts +│ │ │ │ └── remove-server.ts +│ │ │ ├── security/ +│ │ │ │ ├── manage-risk-rules.ts +│ │ │ │ ├── manage-credentials.ts +│ │ │ │ └── manage-permissions.ts +│ │ │ └── monitoring/ +│ │ │ ├── create-alert-rule.ts +│ │ │ └── configure-health-check.ts +│ │ └── dto/ +│ │ ├── agent-config.dto.ts +│ │ ├── runbook.dto.ts +│ │ └── server.dto.ts +│ │ +│ ├── infrastructure/ # 基础设施层(适配器实现) +│ │ └── repositories/ +│ │ ├── api-agent-config.repository.ts +│ │ ├── api-server.repository.ts +│ │ ├── api-runbook.repository.ts +│ │ ├── api-alert-rule.repository.ts +│ │ ├── api-security.repository.ts +│ │ ├── api-audit.repository.ts +│ │ ├── api-communication.repository.ts +│ │ └── api-tenant.repository.ts # ★ 租户管理 API +│ │ +│ ├── stores/ # 状态管理 +│ │ ├── zustand/ # Zustand — UI/交互状态 +│ │ │ ├── ui-store.ts # 侧边栏、模态框、面包屑 +│ │ │ ├── theme-store.ts # 主题切换 +│ │ │ ├── terminal-store.ts # 终端会话状态 +│ │ │ ├── notification-store.ts # Toast 通知队列 +│ │ │ └── tenant-store.ts # ★ 当前租户上下文(选中的租户ID) +│ │ └── redux/ # Redux Toolkit — 复杂业务状态 +│ │ ├── store.ts # Redux store 配置 +│ │ ├── slices/ +│ │ │ ├── agent-config.slice.ts # Agent 引擎配置状态 +│ │ │ ├── runbook-editor.slice.ts # Runbook 编辑器状态(复杂表单+版本控制) +│ │ │ ├── permission-matrix.slice.ts # RBAC 权限矩阵(多维度交叉编辑) +│ │ │ ├── risk-rules.slice.ts # 风险规则集管理 +│ │ │ └── session-replay.slice.ts # 会话回放状态(时间线+步骤) +│ │ └── middleware/ +│ │ └── audit-middleware.ts # 配置变更自动记录审计 +│ │ +│ ├── components/ # 共享 UI 组件 +│ │ ├── layout/ +│ │ │ ├── sidebar.tsx # 侧边导航 +│ │ │ ├── header.tsx # 顶栏(搜索、通知、用户菜单、★ 租户选择器) +│ │ │ ├── breadcrumb.tsx +│ │ │ └── main-layout.tsx +│ │ ├── ui/ # shadcn/ui 基础组件 +│ │ │ ├── button.tsx +│ │ │ ├── input.tsx +│ │ │ ├── dialog.tsx +│ │ │ ├── data-table.tsx # TanStack Table 封装 +│ │ │ ├── command.tsx # 命令面板 (Cmd+K) +│ │ │ ├── toast.tsx +│ │ │ └── ... +│ │ └── domain/ # 业务组件 +│ │ ├── server-status-badge.tsx +│ │ ├── risk-level-tag.tsx +│ │ ├── engine-type-badge.tsx +│ │ ├── alert-severity-icon.tsx +│ │ ├── command-syntax-highlight.tsx # Bash 命令高亮 +│ │ ├── json-viewer.tsx # JSON 树形查看器 +│ │ └── metrics-chart.tsx # 指标图表组件 +│ │ +│ └── hooks/ # 自定义 Hooks +│ ├── use-websocket.ts # WebSocket 连接管理 +│ ├── use-debounce.ts +│ ├── use-keyboard-shortcut.ts # 键盘快捷键 +│ └── use-confirmation.ts # 危险操作确认弹窗 +│ +├── public/ +├── next.config.ts +├── tailwind.config.ts +├── tsconfig.json +├── package.json +└── vitest.config.ts +``` + +--- + +## 3. Clean Architecture 分层规则 + +``` +┌─────────────────────────────────────────┐ +│ Presentation Layer │ +│ (app/ pages, components/, hooks/) │ +│ - Next.js 页面 & React 组件 │ +│ - 只能调用 application 层的 use-cases │ +│ - 通过 Zustand/Redux 管理 UI 状态 │ +│ - 通过 TanStack Query 管理服务端状态 │ +└────────────────┬────────────────────────┘ + │ 调用 +┌────────────────▼────────────────────────┐ +│ Application Layer │ +│ (application/use-cases/) │ +│ - 用例编排(业务流程) │ +│ - 调用 domain 层 repository 接口 │ +│ - DTO 转换 │ +└────────────────┬────────────────────────┘ + │ 依赖倒置 +┌────────────────▼────────────────────────┐ +│ Domain Layer │ +│ (domain/entities, value-objects, │ +│ repositories 接口) │ +│ - 纯 TypeScript,零框架依赖 │ +│ - Entity 定义、业务规则 │ +│ - Repository 接口(抽象类/interface) │ +└────────────────△────────────────────────┘ + │ 实现 +┌────────────────┴────────────────────────┐ +│ Infrastructure Layer │ +│ (infrastructure/repositories/) │ +│ - Repository 接口的具体实现 │ +│ - 调用后端 API (fetch/axios) │ +│ - 数据模型 ↔ 领域实体转换 │ +└─────────────────────────────────────────┘ +``` + +**依赖方向**:`presentation → application → domain ← infrastructure` + +--- + +## 4. 状态管理策略:Zustand + Redux Toolkit 混合 + +### 4.1 分工原则 + +``` +┌────────────────────────────────────────────────────────┐ +│ Zustand │ +│ 管理「轻量级 UI 状态」— 直接、简单、无样板代码 │ +│ │ +│ • 侧边栏展开/折叠 │ +│ • 模态框开关 │ +│ • 当前主题(暗色/亮色) │ +│ • Toast 通知队列 │ +│ • 终端会话(当前标签页、连接状态) │ +│ • 命令面板(Cmd+K)开关 │ +│ • 面包屑路径 │ +│ │ +│ 特点:一个 store = 一个 create(),直接 get/set │ +└────────────────────────────────────────────────────────┘ + +┌────────────────────────────────────────────────────────┐ +│ Redux Toolkit │ +│ 管理「复杂业务状态」— 可预测、可追溯、中间件生态 │ +│ │ +│ • Agent 配置管理 │ +│ (引擎选择、参数配置、提示词版本控制) │ +│ • Runbook 编辑器 │ +│ (多步骤表单、版本对比、草稿保存、撤销/重做) │ +│ • RBAC 权限矩阵 │ +│ (角色×权限×资源 三维编辑、批量操作、变更对比) │ +│ • 风险规则集管理 │ +│ (规则排序、优先级、条件组合、测试模拟) │ +│ • 会话回放 │ +│ (时间线导航、步骤切换、工具执行展开/折叠) │ +│ │ +│ 特点:slice + createAsyncThunk + middleware │ +│ 优势:DevTools 调试、时间旅行、审计中间件 │ +└────────────────────────────────────────────────────────┘ + +┌────────────────────────────────────────────────────────┐ +│ TanStack Query │ +│ 管理「服务端状态」— 缓存、失效、重新获取 │ +│ │ +│ • 服务器列表、详情 │ +│ • 告警规则列表 │ +│ • 审计日志查询 │ +│ • 联系人列表 │ +│ • 所有 GET 请求的缓存管理 │ +│ │ +│ 特点:声明式数据获取,自动缓存失效 │ +└────────────────────────────────────────────────────────┘ +``` + +### 4.2 Zustand Store 示例 + +```typescript +// stores/zustand/ui-store.ts + +interface UiState { + sidebarCollapsed: boolean; + commandPaletteOpen: boolean; + activeModal: string | null; + breadcrumbs: BreadcrumbItem[]; +} + +interface UiActions { + toggleSidebar: () => void; + openCommandPalette: () => void; + closeCommandPalette: () => void; + openModal: (id: string) => void; + closeModal: () => void; + setBreadcrumbs: (items: BreadcrumbItem[]) => void; +} + +export const useUiStore = create((set) => ({ + // State + sidebarCollapsed: false, + commandPaletteOpen: false, + activeModal: null, + breadcrumbs: [], + + // Actions + toggleSidebar: () => set((s) => ({ sidebarCollapsed: !s.sidebarCollapsed })), + openCommandPalette: () => set({ commandPaletteOpen: true }), + closeCommandPalette: () => set({ commandPaletteOpen: false }), + openModal: (id) => set({ activeModal: id }), + closeModal: () => set({ activeModal: null }), + setBreadcrumbs: (items) => set({ breadcrumbs: items }), +})); +``` + +```typescript +// stores/zustand/theme-store.ts + +type Theme = 'light' | 'dark' | 'system'; + +interface ThemeState { + theme: Theme; + setTheme: (theme: Theme) => void; +} + +export const useThemeStore = create()( + persist( + (set) => ({ + theme: 'dark', + setTheme: (theme) => set({ theme }), + }), + { name: 'it0-theme' }, + ), +); +``` + +### 4.3 Redux Toolkit Slice 示例 + +```typescript +// stores/redux/slices/agent-config.slice.ts + +interface AgentConfigState { + activeEngine: EngineType; + engines: Record; + systemPromptVersions: SystemPromptVersion[]; + currentPromptDraft: string | null; + hookScripts: HookScript[]; + isLoading: boolean; + error: string | null; + // 变更追踪 + unsavedChanges: boolean; + lastSavedAt: string | null; +} + +const initialState: AgentConfigState = { + activeEngine: 'claude_code_cli', + engines: { + claude_code_cli: { path: '/usr/local/bin/claude', maxTurns: 30, maxBudgetUsd: 5 }, + claude_api: { model: 'claude-sonnet-4-5-20250929', apiKey: '', maxTokens: 8192 }, + custom: { endpoint: '', authToken: '' }, + }, + systemPromptVersions: [], + currentPromptDraft: null, + hookScripts: [], + isLoading: false, + error: null, + unsavedChanges: false, + lastSavedAt: null, +}; + +export const agentConfigSlice = createSlice({ + name: 'agentConfig', + initialState, + reducers: { + setActiveEngine: (state, action: PayloadAction) => { + state.activeEngine = action.payload; + state.unsavedChanges = true; + }, + updateEngineConfig: (state, action: PayloadAction<{ type: EngineType; config: Partial }>) => { + state.engines[action.payload.type] = { + ...state.engines[action.payload.type], + ...action.payload.config, + }; + state.unsavedChanges = true; + }, + setPromptDraft: (state, action: PayloadAction) => { + state.currentPromptDraft = action.payload; + state.unsavedChanges = true; + }, + addHookScript: (state, action: PayloadAction) => { + state.hookScripts.push(action.payload); + state.unsavedChanges = true; + }, + removeHookScript: (state, action: PayloadAction) => { + state.hookScripts = state.hookScripts.filter(h => h.id !== action.payload); + state.unsavedChanges = true; + }, + markSaved: (state) => { + state.unsavedChanges = false; + state.lastSavedAt = new Date().toISOString(); + }, + }, + extraReducers: (builder) => { + builder + .addCase(fetchAgentConfig.pending, (state) => { + state.isLoading = true; + state.error = null; + }) + .addCase(fetchAgentConfig.fulfilled, (state, action) => { + state.isLoading = false; + Object.assign(state, action.payload); + state.unsavedChanges = false; + }) + .addCase(fetchAgentConfig.rejected, (state, action) => { + state.isLoading = false; + state.error = action.error.message ?? 'Failed to load config'; + }) + .addCase(saveAgentConfig.fulfilled, (state) => { + state.unsavedChanges = false; + state.lastSavedAt = new Date().toISOString(); + }); + }, +}); + +// Async Thunks +export const fetchAgentConfig = createAsyncThunk( + 'agentConfig/fetch', + async (_, { extra }) => { + const repo = extra as { agentConfigRepo: AgentConfigRepository }; + return repo.agentConfigRepo.getConfig(); + }, +); + +export const saveAgentConfig = createAsyncThunk( + 'agentConfig/save', + async (_, { getState, extra }) => { + const state = (getState() as RootState).agentConfig; + const repo = extra as { agentConfigRepo: AgentConfigRepository }; + return repo.agentConfigRepo.saveConfig({ + activeEngine: state.activeEngine, + engines: state.engines, + hookScripts: state.hookScripts, + }); + }, +); + +export const { setActiveEngine, updateEngineConfig, setPromptDraft, addHookScript, removeHookScript, markSaved } = + agentConfigSlice.actions; +``` + +```typescript +// stores/redux/slices/runbook-editor.slice.ts + +interface RunbookEditorState { + currentRunbook: Runbook | null; + draft: Partial; + originalSnapshot: string | null; // JSON snapshot 用于变更对比 + validationErrors: Record; + testResult: RunbookTestResult | null; + isTesting: boolean; + history: RunbookHistoryEntry[]; // 撤销/重做栈 + historyIndex: number; +} + +const runbookEditorSlice = createSlice({ + name: 'runbookEditor', + initialState: { /* ... */ } as RunbookEditorState, + reducers: { + loadRunbook: (state, action: PayloadAction) => { + state.currentRunbook = action.payload; + state.draft = { ...action.payload }; + state.originalSnapshot = JSON.stringify(action.payload); + state.validationErrors = {}; + state.history = []; + state.historyIndex = -1; + }, + updateDraftField: (state, action: PayloadAction<{ field: string; value: unknown }>) => { + const { field, value } = action.payload; + // 保存历史(撤销支持) + state.history = state.history.slice(0, state.historyIndex + 1); + state.history.push({ field, oldValue: (state.draft as any)[field], newValue: value }); + state.historyIndex = state.history.length - 1; + // 更新值 + (state.draft as any)[field] = value; + }, + undo: (state) => { + if (state.historyIndex < 0) return; + const entry = state.history[state.historyIndex]; + (state.draft as any)[entry.field] = entry.oldValue; + state.historyIndex--; + }, + redo: (state) => { + if (state.historyIndex >= state.history.length - 1) return; + state.historyIndex++; + const entry = state.history[state.historyIndex]; + (state.draft as any)[entry.field] = entry.newValue; + }, + setValidationErrors: (state, action: PayloadAction>) => { + state.validationErrors = action.payload; + }, + clearEditor: (state) => { + state.currentRunbook = null; + state.draft = {}; + state.originalSnapshot = null; + state.history = []; + state.historyIndex = -1; + }, + }, + extraReducers: (builder) => { + builder + .addCase(testRunbook.pending, (state) => { state.isTesting = true; }) + .addCase(testRunbook.fulfilled, (state, action) => { + state.isTesting = false; + state.testResult = action.payload; + }) + .addCase(testRunbook.rejected, (state) => { state.isTesting = false; }); + }, +}); +``` + +### 4.4 Redux Store 配置 + +```typescript +// stores/redux/store.ts + +import { configureStore } from '@reduxjs/toolkit'; +import { agentConfigSlice } from './slices/agent-config.slice'; +import { runbookEditorSlice } from './slices/runbook-editor.slice'; +import { permissionMatrixSlice } from './slices/permission-matrix.slice'; +import { riskRulesSlice } from './slices/risk-rules.slice'; +import { sessionReplaySlice } from './slices/session-replay.slice'; +import { auditMiddleware } from './middleware/audit-middleware'; + +// 依赖注入:通过 extra 参数注入 repository 实现 +export const createStore = (repositories: Repositories) => + configureStore({ + reducer: { + agentConfig: agentConfigSlice.reducer, + runbookEditor: runbookEditorSlice.reducer, + permissionMatrix: permissionMatrixSlice.reducer, + riskRules: riskRulesSlice.reducer, + sessionReplay: sessionReplaySlice.reducer, + }, + middleware: (getDefaultMiddleware) => + getDefaultMiddleware({ + thunk: { extraArgument: repositories }, + serializableCheck: { + ignoredActions: ['sessionReplay/loadSession'], + }, + }).concat(auditMiddleware), + }); + +export type RootState = ReturnType['getState']>; +export type AppDispatch = ReturnType['dispatch']; +``` + +### 4.5 审计中间件(配置变更自动追踪) + +```typescript +// stores/redux/middleware/audit-middleware.ts + +import { Middleware } from '@reduxjs/toolkit'; + +/** + * 审计中间件: + * 拦截所有「配置变更」类 action,自动向 audit-service 发送记录 + * 这是使用 Redux 的核心价值之一 — 所有状态变更经过统一管道 + */ +export const auditMiddleware: Middleware = (store) => (next) => (action) => { + const result = next(action); + + // 只追踪配置变更类 action + const auditableActions = [ + 'agentConfig/setActiveEngine', + 'agentConfig/updateEngineConfig', + 'agentConfig/saveAgentConfig/fulfilled', + 'riskRules/addRule', + 'riskRules/removeRule', + 'riskRules/updateRule', + 'permissionMatrix/updatePermission', + ]; + + if (auditableActions.includes(action.type)) { + // 异步发送审计记录(不阻塞 UI) + fetch('/api/proxy/audit-service/logs', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + actionType: 'config_change', + actorType: 'user', + detail: { + action: action.type, + payload: action.payload, + timestamp: new Date().toISOString(), + }, + }), + }).catch(console.error); + } + + return result; +}; +``` + +--- + +## 5. 核心页面详细设计 + +### 5.1 Agent 配置页(核心) + +这是 Web Admin 最重要的页面 — 配置 AI Agent 的行为。 + +``` +┌──────────────────────────────────────────────────────────────────┐ +│ IT0 Admin > Agent Configuration │ +├──────────────────────────────────────────────────────────────────┤ +│ │ +│ ── 引擎选择 ──────────────────────────────────────────── │ +│ │ +│ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ │ +│ │ ● Claude Code │ │ ○ Claude API │ │ ○ Custom Agent │ │ +│ │ CLI │ │ │ │ (Coming Soon) │ │ +│ │ Active ✓ │ │ │ │ │ │ +│ └─────────────────┘ └─────────────────┘ └─────────────────┘ │ +│ │ +│ ── Claude Code CLI 配置 ──────────────────────────────── │ +│ │ +│ CLI 路径: [/usr/local/bin/claude ] │ +│ 最大轮次: [30 ] │ +│ 预算上限: [$5.00 ] / 每次任务 │ +│ 允许工具: [✓ Bash] [✓ Read] [✓ Grep] [✓ Glob] [□ Write] │ +│ │ +│ ── System Prompt 管理 ────────────────────────────────── │ +│ │ +│ ┌────────────────────────────────────────────────────────┐ │ +│ │ [Monaco Editor] │ │ +│ │ │ │ +│ │ 你是 IT0 运维智能体,负责管理以下服务器集群... │ │ +│ │ │ │ +│ │ ## 操作规范 │ │ +│ │ ### 权限分级 │ │ +│ │ - Level 0(自动执行):... │ │ +│ │ │ │ +│ └────────────────────────────────────────────────────────┘ │ +│ 版本: v3 (2026-02-08) [变更历史] [还原上一版] [保存新版本] │ +│ │ +│ ── Hook 脚本管理 ─────────────────────────────────────── │ +│ │ +│ ┌──────────────────────────────────────────────────┐ │ +│ │ PreToolUse │ Bash │ validate.py [编辑][删除] │ │ +│ │ PostToolUse │ * │ audit-logger.py [编辑][删除] │ │ +│ └──────────────────────────────────────────────────┘ │ +│ [+ 添加 Hook 脚本] │ +│ │ +│ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ │ +│ [有未保存的更改] [取消] [保存配置] │ +└──────────────────────────────────────────────────────────────────┘ +``` + +### 5.2 Runbook 管理页 + +``` +┌──────────────────────────────────────────────────────────────────┐ +│ IT0 Admin > Runbooks [+ 新建 Runbook] │ +├──────────────────────────────────────────────────────────────────┤ +│ │ +│ 搜索: [________________] 筛选: [全部类型 ▼] [全部状态 ▼] │ +│ │ +│ ┌────────────────────────────────────────────────────────────┐ │ +│ │ 名称 │ 触发方式 │ 最高风险│ 自动审批│ 状态 │ │ +│ ├────────────────────┼──────────┼─────────┼────────┼───────┤ │ +│ │ 全面健康检查 │ 手动/定时 │ L0 │ ✓ │ 启用 │ │ +│ │ 磁盘空间清理 │ 告警触发 │ L1 │ ✓ │ 启用 │ │ +│ │ 应用滚动部署 │ 手动 │ L2 │ ✗ │ 启用 │ │ +│ │ 数据库主从切换 │ 手动 │ L2 │ ✗ │ 禁用 │ │ +│ │ 紧急回滚 │ 手动 │ L2 │ ✗ │ 启用 │ │ +│ └────────────────────────────────────────────────────────────┘ │ +│ │ +│ 显示 1-5 共 5 条 │ +└──────────────────────────────────────────────────────────────────┘ +``` + +#### Runbook 编辑页 + +``` +┌──────────────────────────────────────────────────────────────────┐ +│ IT0 Admin > Runbooks > 磁盘空间清理 │ +├──────────────────────────────────────────────────────────────────┤ +│ │ +│ 名称: [磁盘空间清理 ] │ +│ 描述: [当磁盘使用率超过阈值时自动清理旧日志文件 ] │ +│ │ +│ 触发方式: ○ 手动 ● 告警触发 ○ 定时 │ +│ 关联告警: [disk_usage_high ▼] │ +│ │ +│ 最高风险等级: [L1 ▼] │ +│ 自动审批: [✓] (仅 Level 0-1 的命令自动通过) │ +│ 允许工具: [✓ Bash] [✓ Read] [✓ Grep] [□ Write] │ +│ │ +│ ── Prompt 模板 ──────────────────────────────────────── │ +│ ┌──────────────────────────────────────────────────────┐ │ +│ │ [Monaco Editor] │ │ +│ │ │ │ +│ │ 请检查以下服务器的磁盘使用情况: │ │ +│ │ {{target_servers}} │ │ +│ │ │ │ +│ │ 执行步骤: │ │ +│ │ 1. 检查 df -h 输出,找到使用率 > 80% 的分区 │ │ +│ │ 2. 查看 /var/log 下的大文件 │ │ +│ │ 3. 清理 30 天前的日志文件 │ │ +│ │ 4. 验证清理后的磁盘使用率 │ │ +│ │ 5. 汇报清理结果和释放的空间 │ │ +│ │ │ │ +│ └──────────────────────────────────────────────────────┘ │ +│ 模板变量: {{target_servers}} {{alert_message}} {{threshold}} │ +│ │ +│ ── 测试 ─────────────────────────────────────────────── │ +│ 目标服务器: [prod-1, prod-2 ▼] │ +│ [模拟测试(Dry Run)] [实际测试] │ +│ │ +│ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ │ +│ [Ctrl+Z 撤销] [Ctrl+Y 重做] [取消] [保存草稿] [发布] │ +└──────────────────────────────────────────────────────────────────┘ +``` + +### 5.3 ★ 驻留指令管理(Standing Orders) + +#### 驻留指令列表页 + +``` +┌──────────────────────────────────────────────────────────────────┐ +│ IT0 Admin > Standing Orders │ +├──────────────────────────────────────────────────────────────────┤ +│ │ +│ 驻留指令 — Agent 自治执行的任务定义 │ +│ (通过 Flutter App 对话创建,此处管理和监控) │ +│ │ +│ 状态: [全部 ▾] 触发: [全部 ▾] [搜索...] │ +│ │ +│ ┌────────────────────────────────────────────────────────────┐ │ +│ │ 指令ID │ 名称 │ 触发 │ 上次执行 │ 下次 │ 状态 │ +│ ├─────────────────┼────────────────┼──────────┼───────────┼──────┼────────┤ +│ │ SO-20260208-001 │ 每日生产巡检 │ ⏰ 08:00 │ 今天 08:00│ 明天 │ ✓ 活跃 │ +│ │ │ │ (每日) │ ✓ 正常 │08:00 │ │ +│ │ SO-20260208-002 │ 磁盘空间监控 │ ⚡ 事件 │ 1h 前 │ — │ ✓ 活跃 │ +│ │ │ │ (告警触发)│ ⚠ 已升级 │ │ │ +│ │ SO-20260210-001 │ 周末日志归档 │ ⏰ 02:00 │ 上周六 │ 本周 │ ✓ 活跃 │ +│ │ │ │ (每周六) │ ✓ 正常 │ 六 │ │ +│ │ SO-20260205-001 │ 部署后烟雾测试 │ ⚡ 事件 │ 2天前 │ — │ ⏸ 暂停 │ +│ │ │ │ (部署后) │ ✓ 正常 │ │ │ +│ └────────────────────────────────────────────────────────────┘ │ +│ │ +│ 共 4 条指令 · 活跃 3 · 暂停 1 │ +│ 今日已执行 5 次 · 成功 4 · 升级 1 │ +│ │ +└──────────────────────────────────────────────────────────────────┘ +``` + +#### 驻留指令详情页 + +``` +┌──────────────────────────────────────────────────────────────────┐ +│ IT0 Admin > Standing Orders > 每日生产巡检 │ +├──────────────────────────────────────────────────────────────────┤ +│ │ +│ ── 基本信息 ────────────────────────────────────────────────── │ +│ ID: SO-20260208-001 │ +│ 名称: 每日生产巡检 │ +│ 状态: ● 活跃 [⏸ 暂停] [🗑 归档] │ +│ 创建方式: 对话定义(会话 sess-abc-123) [查看原始对话] │ +│ 最后修改: 对话修改(会话 sess-def-456) [查看修改对话] │ +│ │ +│ ── 触发配置 ────────────────────────────────────────────────── │ +│ 触发方式: [⏰ 定时 (Cron) ▾] │ +│ Cron 表达式: [0 8 * * * ] → 每天 08:00 │ +│ 生效时间: 2026-02-09 起 │ +│ 过期时间: [无 (永久) ▾] │ +│ │ +│ ── 目标范围 ────────────────────────────────────────────────── │ +│ ☑ 按环境筛选: [prod ▾] │ +│ ☐ 指定服务器 ☐ 指定集群 ☐ 所有服务器 │ +│ 匹配服务器: prod-1, prod-2, prod-3 (共 3 台) │ +│ │ +│ ── Agent 执行指令 ──────────────────────────────────────────── │ +│ ┌──────────────────────────────────────────────────────────┐ │ +│ │ (Monaco Editor — 可编辑 Agent Prompt) │ │ +│ │ │ │ +│ │ 检查所有目标服务器的 CPU、内存、磁盘使用率。 │ │ +│ │ 如果磁盘使用率超过 80%,清理 7 天前的日志文件。 │ │ +│ │ 汇总所有指标,生成巡检报告。 │ │ +│ └──────────────────────────────────────────────────────────┘ │ +│ 关联 Skills: [cleanup_logs] [inspect] │ +│ 关联 Runbook: (无) │ +│ 最高风险等级: [L1 — 低风险写入 ▾] │ +│ 最大轮次: [20] 预算上限: [$0.50] │ +│ │ +│ ── 决策边界 ────────────────────────────────────────────────── │ +│ ✅ 允许的操作: │ +│ ┌────────────────────────────────────────────────────┐ │ +│ │ cleanup_logs (清理日志) [✕ 移除]│ │ +│ │ report_metrics (汇报指标) [✕ 移除]│ │ +│ │ [+ 添加操作] │ │ +│ └────────────────────────────────────────────────────┘ │ +│ │ +│ 🚨 升级规则: │ +│ ┌────────────────────────────────────────────────────────────┐ │ +│ │ 条件 │ 渠道 │ 优先级 │ 操作 │ │ +│ ├─────────────────────────┼─────────────┼───────────┼────────┤ │ +│ │ disk_usage > 95% │ 🔔 推送+电话│ 🔴 紧急 │ [编辑] │ │ +│ │ cpu > 90% for 10min │ 🔔 推送+电话│ 🔴 紧急 │ [编辑] │ │ +│ │ other_anomaly │ 📱 推送+短信│ 🟡 普通 │ [编辑] │ │ +│ │ [+ 添加规则] │ │ │ │ │ +│ └────────────────────────────────────────────────────────────┘ │ +│ │ +│ ── 执行历史 ────────────────────────────────────────────────── │ +│ ┌────────────────────────────────────────────────────────────┐ │ +│ │ 时间 │ 状态 │ 耗时 │ 操作 │ 详情 │ │ +│ ├───────────────────┼──────────┼───────┼──────────────┼──────┤ │ +│ │ 2026-02-08 08:00 │ ✓ 完成 │ 45s │ 清理 3.2GB │ [▶] │ │ +│ │ 2026-02-07 08:00 │ ✓ 完成 │ 38s │ 全部正常 │ [▶] │ │ +│ │ 2026-02-06 08:00 │ ⚠ 已升级 │ 12min │ 磁盘 97% → │ [▶] │ │ +│ │ │ │ │ 电话通知 │ │ │ +│ └────────────────────────────────────────────────────────────┘ │ +│ [查看全部执行记录] │ +│ │ +│ ─────────────────────────────────────────────────────────── │ +│ [取消] [保存修改] │ +│ │ +└──────────────────────────────────────────────────────────────────┘ +``` + +> **注意**:驻留指令主要通过 **Flutter App 对话** 创建(更自然的交互方式)。 +> Web Admin 侧重于**管理、监控和微调**已有的指令,提供更精确的配置编辑能力。 + +--- + +### 5.4 服务器管理 — 添加/编辑/凭证/跳板机 + +#### 服务器列表页 + +``` +┌──────────────────────────────────────────────────────────────────┐ +│ IT0 Admin > Servers [+ 添加服务器] │ +├──────────────────────────────────────────────────────────────────┤ +│ │ +│ 搜索: [________________] 环境: [全部 ▼] 角色: [全部 ▼] │ +│ │ +│ ┌────────────────────────────────────────────────────────────┐ │ +│ │ 名称 │ IP/主机 │环境 │角色 │网络 │跳板 │状态 │ │ +│ ├──────────┼──────────────┼─────┼──────┼──────┼─────┼──────┤ │ +│ │ jump-01 │ 203.0.113.10 │prod │gateway│公网 │ — │● 在线│ │ +│ │ prod-1 │ 10.0.1.11 │prod │web │内网 │jump-01│● 在线│ │ +│ │ prod-2 │ 10.0.1.12 │prod │web │内网 │jump-01│● 在线│ │ +│ │ prod-db │ 10.0.1.20 │prod │db │内网 │jump-01│● 在线│ │ +│ │ staging-1│ 10.0.2.11 │stg │web │内网 │jump-01│● 在线│ │ +│ │ dev-1 │ 192.168.1.100│dev │web │公网 │ — │● 在线│ │ +│ └────────────────────────────────────────────────────────────┘ │ +│ │ +│ [批量测试连接] [导出 SSH Config] │ +└──────────────────────────────────────────────────────────────────┘ +``` + +#### 添加/编辑服务器表单 + +``` +┌──────────────────────────────────────────────────────────────────┐ +│ IT0 Admin > Servers > 添加服务器 │ +├──────────────────────────────────────────────────────────────────┤ +│ │ +│ ── 基本信息 ──────────────────────────────────────────── │ +│ │ +│ 服务器名称: [prod-web-03 ] │ +│ 描述: [生产环境 Web 服务器 #3 ] │ +│ 环境: ○ dev ○ staging ● production │ +│ 角色: [Web Server ▼] │ +│ 所属集群: [main-cluster ▼] (可选) │ +│ 标签: [region:us-east] [tier:1] [+ 添加] │ +│ │ +│ ── SSH 连接配置 ──────────────────────────────────────── │ +│ │ +│ 主机地址: [10.0.1.13 ] │ +│ SSH 端口: [22 ] │ +│ SSH 用户名: [ops-agent ] │ +│ │ +│ ── 认证方式 ──────────────────────────────────────────── │ +│ │ +│ ● SSH 密钥 ○ 密码 │ +│ │ +│ ┌─ SSH 密钥认证 ──────────────────────────────────────────┐ │ +│ │ │ │ +│ │ 选择已有凭证: [prod-ssh-key-ed25519 ▼] │ │ +│ │ 指纹: SHA256:abc123... 类型: ed25519 │ │ +│ │ │ │ +│ │ 或上传新密钥: │ │ +│ │ ┌─────────────────────────────────────────────────┐ │ │ +│ │ │ 将私钥文件拖拽到此处,或 [点击上传] │ │ │ +│ │ │ 支持: id_rsa, id_ed25519, id_ecdsa │ │ │ +│ │ └─────────────────────────────────────────────────┘ │ │ +│ │ 密钥名称: [prod-key-03 ] │ │ +│ │ 密钥密码: [●●●●●●●● ] (如果密钥有 passphrase) │ │ +│ │ │ │ +│ └─────────────────────────────────────────────────────────┘ │ +│ │ +│ ┌─ 密码认证(不推荐,仅用于遗留系统)─────────────────────┐ │ +│ │ │ │ +│ │ 选择已有凭证: [选择... ▼] │ │ +│ │ 或输入新密码: [●●●●●●●● ] │ │ +│ │ 凭证名称: [ ] │ │ +│ │ │ │ +│ └─────────────────────────────────────────────────────────┘ │ +│ │ +│ ── 网络拓扑 ──────────────────────────────────────────── │ +│ │ +│ 网络类型: ○ 公网(可直接连接) │ +│ ● 内网(需要跳板机) │ +│ │ +│ ┌─ 跳板机配置(SSH ProxyJump)────────────────────────────┐ │ +│ │ │ │ +│ │ 跳板机: [jump-01 (203.0.113.10) ▼] │ │ +│ │ 已配置的跳板机/网关服务器列表 │ │ +│ │ │ │ +│ │ 连接链路预览: │ │ +│ │ ┌─────────────────────────────────────────────────┐ │ │ +│ │ │ 你的管理服务器 │ │ │ +│ │ │ ↓ SSH (203.0.113.10:22, user: jump-user) │ │ │ +│ │ │ jump-01 (跳板机) │ │ │ +│ │ │ ↓ SSH (10.0.1.13:22, user: ops-agent) │ │ │ +│ │ │ prod-web-03 (目标服务器) ✓ │ │ │ +│ │ └─────────────────────────────────────────────────┘ │ │ +│ │ │ │ +│ │ ⚠ 多级跳板: 如果跳板机本身也在内网,将自动递归构建 │ │ +│ │ ProxyJump 链 (如 jump-01 → jump-02 → target) │ │ +│ │ │ │ +│ └─────────────────────────────────────────────────────────┘ │ +│ │ +│ ── 高级 SSH 选项(可选)──────────────────────────────── │ +│ │ +│ [✓] StrictHostKeyChecking=no (首次连接不验证指纹) │ +│ [ ] ServerAliveInterval=60 (保持连接) │ +│ [ ] ServerAliveCountMax=3 │ +│ 自定义选项: [ ] │ +│ │ +│ ── 连接测试 ──────────────────────────────────────────── │ +│ │ +│ [测试 SSH 连接] │ +│ │ +│ ┌──────────────────────────────────────────────────────┐ │ +│ │ ✓ 跳板机 jump-01 连接成功 (延迟 23ms) │ │ +│ │ ✓ 目标 prod-web-03 连接成功 (延迟 45ms) │ │ +│ │ ✓ 用户 ops-agent 权限验证通过 │ │ +│ │ ✓ 基本命令执行正常 (whoami, uname -a) │ │ +│ └──────────────────────────────────────────────────────┘ │ +│ │ +│ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ │ +│ [取消] [保存服务器] │ +└──────────────────────────────────────────────────────────────────┘ +``` + +#### 凭证管理页(Security > Credentials 的补充说明) + +``` +┌──────────────────────────────────────────────────────────────────┐ +│ IT0 Admin > Security > Credentials [+ 添加凭证] │ +├──────────────────────────────────────────────────────────────────┤ +│ │ +│ ┌────────────────────────────────────────────────────────────┐ │ +│ │ 名称 │ 类型 │ 指纹/信息 │ 引用 │ 操作 │ │ +│ ├─────────────────┼──────────┼────────────────┼──────┼──────┤ │ +│ │ prod-ssh-key │ ed25519 │ SHA256:abc1... │ 4台 │[编辑]│ │ +│ │ staging-ssh-key │ rsa-4096 │ SHA256:def4... │ 2台 │[编辑]│ │ +│ │ dev-password │ 密码 │ ●●●●●●●● │ 1台 │[编辑]│ │ +│ │ jump-key │ ed25519 │ SHA256:ghi7... │ 1台 │[编辑]│ │ +│ └────────────────────────────────────────────────────────────┘ │ +│ │ +│ ⚠ 凭证内容加密存储(AES-256-GCM),仅在 Agent 执行 SSH 时 │ +│ 临时解密到内存,绝不写入日志或磁盘。 │ +│ ⚠ 「引用」列显示使用该凭证的服务器数量,删除前需先解除关联。 │ +│ │ +└──────────────────────────────────────────────────────────────────┘ +``` + +#### SSH 连接拓扑可视化 + +``` +┌──────────────────────────────────────────────────────────────────┐ +│ IT0 Admin > Servers > Network Topology │ +├──────────────────────────────────────────────────────────────────┤ +│ │ +│ ┌───────────┐ │ +│ │ IT0 管理机 │ │ +│ └─────┬─────┘ │ +│ │ │ +│ ┌─────▼─────┐ │ +│ │ jump-01 │ 203.0.113.10 (公网) │ +│ │ (跳板机) │ │ +│ └──┬──┬──┬──┘ │ +│ ┌─────┘ │ └─────┐ │ +│ ┌─────▼───┐┌───▼────┐┌──▼──────┐ │ +│ │ prod-1 ││ prod-2 ││ prod-db │ 10.0.1.x (内网) │ +│ │ 10.0.1.11│ 10.0.1.12│ 10.0.1.20│ │ +│ └─────────┘└────────┘└─────────┘ │ +│ │ +│ ┌─────────┐ │ +│ │ dev-1 │ 192.168.1.100 (公网直连) │ +│ └─────────┘ │ +│ │ +│ 图例: ● 在线 ○ 离线 ⚠ 告警 │ +└──────────────────────────────────────────────────────────────────┘ +``` + +### 5.5 安全配置 — 风险规则管理 + +``` +┌──────────────────────────────────────────────────────────────────┐ +│ IT0 Admin > Security > Risk Rules [+ 添加规则] │ +├──────────────────────────────────────────────────────────────────┤ +│ │ +│ ── 命令风险分级规则(按优先级排序,可拖拽排序)────────── │ +│ │ +│ ┌────────────────────────────────────────────────────────────┐ │ +│ │ ⠿ │ Level 3 🟣│ rm\s+-rf\s+/ │ 禁止 │ [编辑] │ │ +│ │ ⠿ │ Level 3 🟣│ DROP\s+(DATABASE|TABLE) │ 禁止 │ [编辑] │ │ +│ │ ⠿ │ Level 3 🟣│ shutdown|reboot │ 禁止 │ [编辑] │ │ +│ │ ⠿ │ Level 3 🟣│ mkfs|dd\s+if= │ 禁止 │ [编辑] │ │ +│ │ ⠿ │ Level 2 🔴│ systemctl\s+(stop|restart) │ 审批│ [编辑] │ │ +│ │ ⠿ │ Level 2 🔴│ kubectl\s+delete │ 审批 │ [编辑] │ │ +│ │ ⠿ │ Level 2 🔴│ docker\s+(rm|stop) │ 审批 │ [编辑] │ │ +│ │ ⠿ │ Level 1 🟠│ systemctl\s+restart │ 确认 │ [编辑] │ │ +│ │ ⠿ │ Level 1 🟠│ find.*-delete │ 确认 │ [编辑] │ │ +│ │ ⠿ │ Level 0 🟢│ *(默认) │ 自动 │ — │ │ +│ └────────────────────────────────────────────────────────────┘ │ +│ │ +│ ── 规则测试 ──────────────────────────────────────────── │ +│ 输入命令: [ssh prod-1 'systemctl restart nginx' ] │ +│ 结果: Level 2 🔴 — 匹配规则 "systemctl\s+(stop|restart)" │ +│ 动作: 需要审批 │ +│ │ +└──────────────────────────────────────────────────────────────────┘ +``` + +### 5.6 审计日志页 + +``` +┌──────────────────────────────────────────────────────────────────┐ +│ IT0 Admin > Audit Logs │ +├──────────────────────────────────────────────────────────────────┤ +│ │ +│ 时间范围: [2026-02-01] ~ [2026-02-08] │ +│ 操作类型: [全部 ▼] 执行者: [全部 ▼] 搜索: [___________] │ +│ │ +│ ┌────────────────────────────────────────────────────────────┐ │ +│ │ 时间 │ 操作类型 │ 执行者 │ 详情 │ │ +│ ├────────────────┼───────────────┼──────────┼──────────────┤ │ +│ │ 02-08 15:23:01 │ command_exec │ agent │ df -h (prod-1)│ │ +│ │ 02-08 15:22:45 │ task_created │ admin │ 全面巡检 │ │ +│ │ 02-08 14:15:32 │ approval_ok │ admin │ restart nginx │ │ +│ │ 02-08 14:15:10 │ approval_req │ agent │ restart nginx │ │ +│ │ 02-08 13:00:00 │ config_change │ admin │ 引擎切换→CLI │ │ +│ │ 02-08 12:01:15 │ alert_fired │ system │ disk > 90% │ │ +│ └────────────────────────────────────────────────────────────┘ │ +│ │ +│ [← 上一页] 第 1 页 / 共 12 页 [下一页 →] │ +│ │ +│ [导出 CSV] │ +└──────────────────────────────────────────────────────────────────┘ +``` + +### 5.7 会话回放页 + +``` +┌──────────────────────────────────────────────────────────────────┐ +│ IT0 Admin > Sessions > Session #a3f8c2 │ +├──────────────────────────────────────────────────────────────────┤ +│ │ +│ 任务: 全面巡检 引擎: Claude Code CLI 时间: 2026-02-08 15:22│ +│ 状态: ✓ 完成 耗时: 3m 42s 命令数: 12 │ +│ │ +│ ── 执行时间线 ───────────────────────────────────────── │ +│ │ +│ ●──○──○──○──○──○──○──○──○──○──○──● │ +│ ↑ ↑ │ +│ 开始 完成 │ +│ │ +│ ── 步骤详情 ─────────────────────────────────────────── │ +│ │ +│ [Step 1] 🧠 思考 │ +│ "我需要检查所有服务器的 CPU、内存、磁盘状态..." │ +│ │ +│ [Step 2] 🔧 Bash: ssh prod-1 'top -bn1 | head -5' L0 🟢 │ +│ ┌──────────────────────────────────────────────────────┐ │ +│ │ top - 15:22:35 up 45 days, 12:34, 2 users │ │ +│ │ Tasks: 234 total, 1 running, 233 sleeping │ │ +│ │ %Cpu(s): 45.2 us, 3.1 sy, 0.0 ni, 51.7 id │ │ +│ └──────────────────────────────────────────────────────┘ │ +│ │ +│ [Step 3] 🔧 Bash: ssh prod-1 'df -h' L0 🟢 │ +│ ┌──────────────────────────────────────────────────────┐ │ +│ │ Filesystem Size Used Avail Use% Mounted on │ │ +│ │ /dev/sda1 100G 71G 29G 71% / │ │ +│ └──────────────────────────────────────────────────────┘ │ +│ │ +│ [Step 4] 🧠 思考 │ +│ "prod-1 状态正常,继续检查 prod-2..." │ +│ │ +│ ... (可展开/折叠每一步) │ +│ │ +│ [Step 12] 📝 总结 │ +│ "巡检完成。prod-2 磁盘使用率 92%,建议清理旧日志..." │ +│ │ +└──────────────────────────────────────────────────────────────────┘ +``` + +### 5.8 通信配置页 + +``` +┌──────────────────────────────────────────────────────────────────┐ +│ IT0 Admin > Communication > Channels │ +├──────────────────────────────────────────────────────────────────┤ +│ │ +│ ── 通信渠道配置 ──────────────────────────────────────── │ +│ │ +│ ┌──────────────────────────────────────────────────────────┐ │ +│ │ 渠道 │ 状态 │ 配置 │ 操作 │ │ +│ ├─────────────┼───────┼─────────────────────────┼─────────┤ │ +│ │ App 推送 │ ✓ 启用│ FCM + WebSocket 自动启用 │ — │ │ +│ │ 语音对话 │ ✓ 启用│ Pipecat (voice-service) │ [状态] │ │ +│ │ 短信 (SMS) │ ✓ 启用│ Twilio +1-234-567-8900 │ [配置] │ │ +│ │ 电话语音 │ ✓ 启用│ Pipecat 拨号 (via Twilio) │ [配置] │ │ +│ │ 邮件 │ ✓ 启用│ smtp.gmail.com:587 │ [配置] │ │ +│ │ Telegram │ ✓ 启用│ @IT0OpsBot │ [配置] │ │ +│ │ 企业微信 │ ○ 禁用│ 未配置 │ [配置] │ │ +│ └──────────────────────────────────────────────────────────┘ │ +│ │ +│ ── 升级策略 ──────────────────────────────────────────── │ +│ │ +│ [默认策略 — 三层递进] │ +│ Layer 1: App 推送 (FCM) → 立即 │ +│ └─ 用户打开 App → 语音对话 (Pipecat voice-service) │ +│ Layer 2: Pipecat 电话 (via Twilio) → 2 分钟无响应后 │ +│ └─ Agent 接通即说话,直接语音对话(无 IVR 菜单) │ +│ Layer 3: IM + 短信 → 5 分钟无响应后 │ +│ Layer 4: 通知备用联系人 → 10 分钟无响应后 │ +│ Layer 5: 邮件通知管理组 → 20 分钟无响应后 │ +│ │ +│ [编辑策略] [+ 新建策略] │ +│ │ +│ ── 联系人 ────────────────────────────────────────────── │ +│ ┌────────────────────────────────────────────────────────┐ │ +│ │ 姓名 │ 手机 │ 邮箱 │ Telegram │ 角色 │ │ +│ ├───────┼──────────────┼───────────────┼──────────┼──────┤ │ +│ │ Admin │ 138-xxxx-xxxx│ admin@xx.com │ @admin │ 主要 │ │ +│ │ Ops-1 │ 139-xxxx-xxxx│ ops1@xx.com │ — │ 备用 │ │ +│ └────────────────────────────────────────────────────────┘ │ +│ [+ 添加联系人] │ +│ │ +└──────────────────────────────────────────────────────────────────┘ +``` + +### 5.9 多租户管理(超级管理员) + +> 仅 `super_admin` 角色可见此模块。普通租户管理员只能在自己的租户范围内操作。 + +#### 5.8.1 顶栏租户选择器 + +``` +┌──────────────────────────────────────────────────────────────────┐ +│ IT0 Admin [🔍 搜索...] [🔔 3] [租户: Acme Corp ▾] [👤 Admin] │ +│ ┌─────────────────────────┐ │ +│ │ ★ Acme Corp (当前) │ │ +│ │ Globex Inc │ │ +│ │ Initech Ltd │ │ +│ │ ────────────────────── │ │ +│ │ [管理所有租户] │ │ +│ └─────────────────────────┘ │ +└──────────────────────────────────────────────────────────────────┘ +``` + +**行为**: +- 切换租户后,所有页面数据刷新为目标租户的上下文 +- API 请求自动注入 `X-Tenant-Id` 请求头 +- Zustand `tenant-store` 持久化当前选中的租户 ID(`localStorage`) +- 页面 URL 不包含 tenantId(通过 header 传递,避免 URL 泄露) + +#### 5.8.2 租户列表页 + +``` +┌──────────────────────────────────────────────────────────────────┐ +│ IT0 Admin > Tenants │ +├──────────────────────────────────────────────────────────────────┤ +│ │ +│ [+ 新建租户] [搜索租户...] 状态: [全部 ▾] 计划: [全部 ▾] │ +│ │ +│ ┌────────────────────────────────────────────────────────────┐ │ +│ │ 租户ID │ 名称 │ 计划 │ 服务器 │ 用户 │ 状态 │ │ +│ ├────────┼─────────────┼─────────────┼────────┼──────┼────────┤ │ +│ │ t001 │ Acme Corp │ Enterprise │ 15/50 │ 8/20 │ ✓ 活跃 │ │ +│ │ t002 │ Globex Inc │ Pro │ 5/20 │ 3/10 │ ✓ 活跃 │ │ +│ │ t003 │ Initech Ltd │ Free │ 2/5 │ 1/3 │ ✓ 活跃 │ │ +│ │ t004 │ Umbrella │ Pro │ 0/20 │ 1/10 │ ⏸ 暂停 │ │ +│ └────────────────────────────────────────────────────────────┘ │ +│ │ +│ 共 4 个租户 · 活跃 3 · 暂停 1 │ +│ │ +└──────────────────────────────────────────────────────────────────┘ +``` + +#### 5.8.3 租户详情/编辑页 + +``` +┌──────────────────────────────────────────────────────────────────┐ +│ IT0 Admin > Tenants > Acme Corp │ +├──────────────────────────────────────────────────────────────────┤ +│ │ +│ ── 基本信息 ────────────────────────────────────────────────── │ +│ │ +│ 租户 ID: t001 (不可修改) │ +│ 名称: [Acme Corp ] │ +│ URL Slug: [acme-corp ] │ +│ 状态: [● 活跃 ▾] (活跃 / 暂停 / 已删除) │ +│ 所有者: admin@acme.com (user_id: xxx) │ +│ │ +│ ── 订阅计划与配额 ──────────────────────────────────────────── │ +│ │ +│ 当前计划: [Enterprise ▾] │ +│ │ +│ ┌──────────────────────────────────────────────────────────┐ │ +│ │ 资源 │ 配额 │ 已用 │ 使用率 │ 操作 │ │ +│ ├───────────────┼────────┼────────┼─────────────────┼───────┤ │ +│ │ 服务器数量 │ 50 │ 15 │ ████░░░░ 30% │ [调整]│ │ +│ │ Skills 数量 │ 100 │ 23 │ ██░░░░░░ 23% │ [调整]│ │ +│ │ 用户数量 │ 20 │ 8 │ ████░░░░ 40% │ [调整]│ │ +│ │ 日会话上限 │ 500 │ 127 │ ██░░░░░░ 25% │ [调整]│ │ +│ └──────────────────────────────────────────────────────────┘ │ +│ │ +│ ── 允许的引擎 ────────────────────────────────────────────── │ +│ ☑ Claude Code CLI ☑ Claude API ☐ Custom Agent │ +│ │ +│ ── 危险操作区 ────────────────────────────────────────────── │ +│ [⏸ 暂停租户] [🗑 删除租户] (需二次确认 + 输入租户名称) │ +│ │ +│ ─────────────────────────────────────────────────────────── │ +│ [取消] [保存修改] │ +│ │ +└──────────────────────────────────────────────────────────────────┘ +``` + +#### 5.8.4 新建租户向导 + +``` +┌──────────────────────────────────────────────────────────────────┐ +│ IT0 Admin > Tenants > 新建租户 │ +├──────────────────────────────────────────────────────────────────┤ +│ │ +│ Step 1 of 3: 基本信息 │ +│ ───────────────────────────────────────── │ +│ 租户名称: [ ] │ +│ URL Slug: [ ] (自动生成) │ +│ 管理员邮箱: [ ] │ +│ 管理员密码: [ ] │ +│ │ +│ Step 2 of 3: 选择计划 │ +│ ───────────────────────────────────────── │ +│ ┌────────────┐ ┌────────────┐ ┌────────────┐ │ +│ │ Free │ │ Pro │ │ Enterprise │ │ +│ │ 5 服务器 │ │ 20 服务器 │ │ 50 服务器 │ │ +│ │ 3 用户 │ │ 10 用户 │ │ 20 用户 │ │ +│ │ 10 Skills │ │ 50 Skills │ │ 100 Skills │ │ +│ │ [选择] │ │ [★ 选择] │ │ [选择] │ │ +│ └────────────┘ └────────────┘ └────────────┘ │ +│ │ +│ Step 3 of 3: 确认 │ +│ ───────────────────────────────────────── │ +│ 系统将自动: │ +│ • 创建租户 Schema (it0_t_xxx) │ +│ • 运行数据库迁移 │ +│ • 创建管理员账户 │ +│ • 初始化默认 System Prompt 和 Skills │ +│ │ +│ [上一步] [创建租户] │ +│ │ +└──────────────────────────────────────────────────────────────────┘ +``` + +#### 5.8.5 Tenant Store(Zustand) + +```typescript +// stores/zustand/tenant-store.ts + +interface TenantState { + currentTenantId: string | null; + tenants: TenantSummary[]; // 当前用户可访问的租户列表 + isLoading: boolean; + + // Actions + switchTenant: (tenantId: string) => void; + fetchTenants: () => Promise; +} + +export const useTenantStore = create()( + persist( + (set, get) => ({ + currentTenantId: null, + tenants: [], + isLoading: false, + + switchTenant: (tenantId) => { + set({ currentTenantId: tenantId }); + // 触发全局数据刷新 — invalidate 所有 TanStack Query + queryClient.invalidateQueries(); + }, + + fetchTenants: async () => { + set({ isLoading: true }); + const tenants = await apiClient.get('/tenants'); + set({ tenants, isLoading: false }); + // 如果尚未选择租户,自动选中第一个 + if (!get().currentTenantId && tenants.length > 0) { + set({ currentTenantId: tenants[0].id }); + } + }, + }), + { name: 'it0-tenant' } // localStorage key + ) +); +``` + +#### 5.8.6 API Client 自动注入租户 Header + +```typescript +// core/api/api-client.ts — 拦截器追加 + +const apiClient = { + async fetch(url: string, options: RequestInit = {}) { + const tenantId = useTenantStore.getState().currentTenantId; + const headers = new Headers(options.headers); + + // ★ 自动注入租户标识 + if (tenantId) { + headers.set('X-Tenant-Id', tenantId); + } + + // JWT token 注入(已有逻辑) + const token = getAuthToken(); + if (token) { + headers.set('Authorization', `Bearer ${token}`); + } + + return fetch(`${API_BASE_URL}${url}`, { ...options, headers }); + }, +}; +``` + +--- + +## 6. BFF 层(Next.js API Routes) + +Web Admin 不直接调用后端微服务,而是通过 Next.js API Routes 做 BFF 代理: + +``` +Flutter App → API Gateway → 微服务 +Web Admin → Next.js BFF → API Gateway → 微服务 +``` + +```typescript +// app/api/proxy/[...path]/route.ts + +import { NextRequest, NextResponse } from 'next/server'; + +/** + * 通用代理路由: + * /api/proxy/agent-service/config → http://api-gateway:8000/agent-service/config + * /api/proxy/ops-service/tasks → http://api-gateway:8000/ops-service/tasks + * + * 好处: + * 1. 前端不需要知道后端微服务的地址 + * 2. 可以在 BFF 层做额外的数据聚合 + * 3. SSR 页面可以在服务端直接调用 + * 4. 统一处理认证 token 注入 + */ +export async function GET(request: NextRequest, { params }: { params: { path: string[] } }) { + const backendUrl = `${process.env.API_GATEWAY_URL}/${params.path.join('/')}`; + const token = request.cookies.get('access_token')?.value; + + const response = await fetch(backendUrl, { + headers: { + 'Authorization': `Bearer ${token}`, + 'Content-Type': 'application/json', + }, + }); + + const data = await response.json(); + return NextResponse.json(data, { status: response.status }); +} + +export async function POST(request: NextRequest, { params }: { params: { path: string[] } }) { + const backendUrl = `${process.env.API_GATEWAY_URL}/${params.path.join('/')}`; + const token = request.cookies.get('access_token')?.value; + const body = await request.json(); + + const response = await fetch(backendUrl, { + method: 'POST', + headers: { + 'Authorization': `Bearer ${token}`, + 'Content-Type': 'application/json', + }, + body: JSON.stringify(body), + }); + + const data = await response.json(); + return NextResponse.json(data, { status: response.status }); +} + +// PUT, DELETE 同理... +``` + +--- + +## 7. TanStack Query 数据请求层 + +```typescript +// core/api/query-keys.ts + +export const queryKeys = { + servers: { + all: ['servers'] as const, + list: (filters?: ServerFilters) => [...queryKeys.servers.all, 'list', filters] as const, + detail: (id: string) => [...queryKeys.servers.all, 'detail', id] as const, + metrics: (id: string) => [...queryKeys.servers.all, 'metrics', id] as const, + }, + runbooks: { + all: ['runbooks'] as const, + list: (filters?: RunbookFilters) => [...queryKeys.runbooks.all, 'list', filters] as const, + detail: (id: string) => [...queryKeys.runbooks.all, 'detail', id] as const, + }, + alertRules: { + all: ['alertRules'] as const, + list: () => [...queryKeys.alertRules.all, 'list'] as const, + }, + audit: { + all: ['audit'] as const, + list: (filters?: AuditFilters) => [...queryKeys.audit.all, 'list', filters] as const, + }, + sessions: { + all: ['sessions'] as const, + list: (filters?: SessionFilters) => [...queryKeys.sessions.all, 'list', filters] as const, + detail: (id: string) => [...queryKeys.sessions.all, 'detail', id] as const, + }, +} as const; +``` + +```typescript +// 使用示例 — Server 列表页 + +// hooks/use-servers.ts +export function useServers(filters?: ServerFilters) { + const apiClient = useApiClient(); + + return useQuery({ + queryKey: queryKeys.servers.list(filters), + queryFn: () => apiClient.get>('/api/proxy/inventory-service/servers', { params: filters }), + staleTime: 30 * 1000, // 30 秒内不重新请求 + refetchInterval: 60 * 1000, // 每 60 秒自动刷新 + }); +} + +export function useAddServer() { + const queryClient = useQueryClient(); + const apiClient = useApiClient(); + + return useMutation({ + mutationFn: (data: CreateServerDto) => + apiClient.post('/api/proxy/inventory-service/servers', data), + onSuccess: () => { + queryClient.invalidateQueries({ queryKey: queryKeys.servers.all }); + }, + }); +} +``` + +--- + +## 8. WebSocket 实时通信 + +```typescript +// core/api/websocket-client.ts + +class WebSocketClient { + private ws: WebSocket | null = null; + private listeners = new Map void>>(); + private reconnectAttempts = 0; + private maxReconnectAttempts = 10; + + constructor(private baseUrl: string) {} + + connect(token: string) { + this.ws = new WebSocket(`${this.baseUrl}/ws?token=${token}`); + + this.ws.onmessage = (event) => { + const data = JSON.parse(event.data); + const type = data.type as string; + this.listeners.get(type)?.forEach((cb) => cb(data)); + this.listeners.get('*')?.forEach((cb) => cb(data)); // 通配符 + }; + + this.ws.onclose = () => this.handleReconnect(token); + this.ws.onerror = () => this.handleReconnect(token); + } + + on(eventType: string, callback: (data: any) => void) { + if (!this.listeners.has(eventType)) { + this.listeners.set(eventType, new Set()); + } + this.listeners.get(eventType)!.add(callback); + return () => this.listeners.get(eventType)?.delete(callback); + } + + private handleReconnect(token: string) { + if (this.reconnectAttempts < this.maxReconnectAttempts) { + const delay = Math.pow(2, this.reconnectAttempts) * 1000; + setTimeout(() => { + this.reconnectAttempts++; + this.connect(token); + }, delay); + } + } + + disconnect() { + this.ws?.close(); + this.listeners.clear(); + } +} + +// React Hook 封装 +export function useWebSocketEvent(eventType: string, callback: (data: T) => void) { + const wsClient = useWebSocketClient(); // 从 Context 获取 + + useEffect(() => { + const unsubscribe = wsClient.on(eventType, callback); + return unsubscribe; + }, [wsClient, eventType, callback]); +} +``` + +--- + +## 9. 命令面板(Cmd+K) + +类似 VS Code 的命令面板,提供全局快速操作: + +```typescript +// components/ui/command-palette.tsx + +const commands: Command[] = [ + // 导航 + { id: 'nav-dashboard', label: '转到仪表盘', action: () => router.push('/dashboard'), category: '导航' }, + { id: 'nav-servers', label: '转到服务器管理', action: () => router.push('/servers'), category: '导航' }, + { id: 'nav-runbooks', label: '转到 Runbook', action: () => router.push('/runbooks'), category: '导航' }, + { id: 'nav-audit', label: '转到审计日志', action: () => router.push('/audit'), category: '导航' }, + + // 快捷操作 + { id: 'action-inspect', label: '发起全面巡检', action: () => createQuickTask('inspection'), category: '操作' }, + { id: 'action-terminal', label: '打开终端', action: () => router.push('/terminal'), category: '操作' }, + { id: 'action-switch-engine', label: '切换 Agent 引擎', action: () => openEngineSelector(), category: '配置' }, + + // 搜索 + { id: 'search-server', label: '搜索服务器...', action: () => openServerSearch(), category: '搜索' }, + { id: 'search-audit', label: '搜索审计日志...', action: () => openAuditSearch(), category: '搜索' }, +]; + +// 使用 shadcn/ui 的 Command 组件 +// Cmd+K (Mac) / Ctrl+K (Windows) 触发 +``` + +--- + +## 10. 键盘快捷键 + +```typescript +// hooks/use-keyboard-shortcut.ts + +export const globalShortcuts: KeyboardShortcut[] = [ + { key: 'k', meta: true, action: 'openCommandPalette', description: '命令面板' }, + { key: 'b', meta: true, action: 'toggleSidebar', description: '切换侧边栏' }, + { key: '1', meta: true, action: 'navDashboard', description: '仪表盘' }, + { key: '2', meta: true, action: 'navServers', description: '服务器' }, + { key: '3', meta: true, action: 'navRunbooks', description: 'Runbook' }, + { key: '4', meta: true, action: 'navAudit', description: '审计日志' }, + { key: '`', meta: true, action: 'openTerminal', description: '终端' }, + { key: 's', meta: true, action: 'save', description: '保存当前编辑' }, + { key: 'z', meta: true, action: 'undo', description: '撤销' }, + { key: 'z', meta: true, shift: true, action: 'redo', description: '重做' }, +]; +``` + +--- + +## 11. Provider 配置(根布局) + +```typescript +// app/layout.tsx + +import { ReduxProvider } from '@/stores/redux/provider'; +import { QueryProvider } from '@/core/api/query-provider'; +import { AuthProvider } from '@/core/auth/auth-provider'; +import { WebSocketProvider } from '@/core/api/websocket-provider'; +import { ThemeProvider } from '@/core/theme/theme-provider'; + +export default function RootLayout({ children }: { children: React.ReactNode }) { + return ( + + + + + + + + {children} + + + + + + + + ); +} +``` + +```typescript +// stores/redux/provider.tsx + +'use client'; + +import { Provider } from 'react-redux'; +import { createStore } from './store'; +import { useRepositories } from '@/infrastructure/di'; + +export function ReduxProvider({ children }: { children: React.ReactNode }) { + const repositories = useRepositories(); + const store = useMemo(() => createStore(repositories), [repositories]); + + return {children}; +} +``` + +--- + +## 12. 侧边栏导航结构 + +```typescript +// components/layout/sidebar.tsx + +const navigationItems: NavItem[] = [ + { + label: '仪表盘', + icon: LayoutDashboard, + href: '/dashboard', + }, + { + label: 'Agent 配置', + icon: Bot, + children: [ + { label: '引擎管理', href: '/agent-config', icon: Cpu }, + { label: 'Skills 管理', href: '/agent-config/skills', icon: Zap }, + { label: 'System Prompt', href: '/agent-config/prompts', icon: FileText }, + { label: 'Hook 脚本', href: '/agent-config/hooks', icon: Webhook }, + ], + }, + { + label: 'Runbook', + icon: BookOpen, + href: '/runbooks', + }, + { + label: '驻留指令', + icon: CalendarClock, // lucide-react + href: '/standing-orders', + }, + { + label: '服务器', + icon: Server, + children: [ + { label: '服务器列表', href: '/servers', icon: List }, + { label: '集群管理', href: '/servers/clusters', icon: Network }, + ], + }, + { + label: '安全', + icon: Shield, + children: [ + { label: '风险规则', href: '/security/risk-rules', icon: AlertTriangle }, + { label: '凭证管理', href: '/security/credentials', icon: Key }, + { label: '权限管理', href: '/security/permissions', icon: Users }, + ], + }, + { + label: '监控', + icon: Activity, + children: [ + { label: '告警规则', href: '/monitoring/alert-rules', icon: Bell }, + { label: '健康检查', href: '/monitoring/health-checks', icon: HeartPulse }, + { label: '指标面板', href: '/monitoring/metrics', icon: BarChart }, + ], + }, + { + label: '通信', + icon: MessageSquare, + children: [ + { label: '渠道配置', href: '/communication/channels', icon: Radio }, + { label: '联系人', href: '/communication/contacts', icon: UserCircle }, + { label: '升级策略', href: '/communication/escalation', icon: ArrowUpCircle }, + ], + }, + { + label: '会话历史', + icon: History, + href: '/sessions', + }, + { + label: '审计日志', + icon: ScrollText, + href: '/audit', + }, + { + label: '终端', + icon: Terminal, + href: '/terminal', + }, + // ★ 仅超级管理员可见 + { + label: '租户管理', + icon: Building2, + href: '/tenants', + requiredRole: 'super_admin', // 路由守卫检查 + }, +]; +``` + +--- + +## 13. 开发规范 + +### 13.1 文件命名 + +| 类型 | 规范 | 示例 | +|------|------|------| +| 组件文件 | kebab-case | `server-status-badge.tsx` | +| 页面文件 | `page.tsx` (Next.js 约定) | `app/(admin)/servers/page.tsx` | +| Store 文件 | kebab-case + 后缀 | `agent-config.slice.ts`, `ui-store.ts` | +| Hook 文件 | `use-` 前缀 | `use-servers.ts` | +| 实体 | kebab-case | `agent-engine.ts` | +| 工具函数 | kebab-case | `date.ts`, `format.ts` | + +### 13.2 组件规范 + +```typescript +// 标准组件模板 +interface Props { + // 明确 props 类型 +} + +export function ComponentName({ prop1, prop2 }: Props) { + // Hooks 在顶部 + // 事件处理函数 + // 渲染 + return (/* JSX */); +} +``` + +- **Server Components 优先**:数据密集型列表页、详情页用 RSC +- **Client Components**:带交互的表单、实时更新、状态管理相关 +- 文件顶部 `'use client'` 声明客户端组件 + +### 13.3 Zustand vs Redux 判断清单 + +| 问题 | 是 → Redux Toolkit | 否 → Zustand | +|------|-------------------|--------------| +| 状态变更需要审计追踪? | ✓ | | +| 需要撤销/重做? | ✓ | | +| 多个组件需要协调复杂的状态转换? | ✓ | | +| 状态涉及异步工作流(多步骤)? | ✓ | | +| 需要 Redux DevTools 调试? | ✓ | | +| 只是简单的 UI 开关/计数器? | | ✓ | +| 状态只在单个组件树内使用? | | ✓ | + +--- + +## 14. 环境变量 + +```env +# .env.local + +# API +NEXT_PUBLIC_APP_URL=http://localhost:3000 +API_GATEWAY_URL=http://localhost:8000 + +# Auth +NEXTAUTH_SECRET=your-secret +NEXTAUTH_URL=http://localhost:3000 + +# WebSocket +NEXT_PUBLIC_WS_URL=ws://localhost:8000 +``` + +--- + +## 15. 开发路线图 + +### Phase 1: 基础骨架(Week 1-2) +- [ ] Next.js 项目初始化(App Router + Tailwind + shadcn/ui) +- [ ] Clean Architecture 目录结构搭建 +- [ ] Zustand + Redux Toolkit 配置 +- [ ] TanStack Query 配置 +- [ ] 认证流程(登录页 + JWT) +- [ ] 布局框架(侧边栏 + 顶栏 + 面包屑) +- [ ] BFF 代理路由 + +### Phase 2: Agent 配置(Week 3-4) +- [ ] Agent 引擎管理页 +- [ ] System Prompt 编辑器(Monaco Editor) +- [ ] Hook 脚本管理 +- [ ] 配置保存 + 审计中间件 + +### Phase 3: 运维管理(Week 5-6) +- [ ] 服务器列表 + 详情 + 指标图表 +- [ ] Runbook 列表 + 编辑器(含模板变量、测试) +- [ ] 仪表盘(摘要卡片 + 快捷操作) + +### Phase 4: 安全与监控(Week 7-8) +- [ ] 风险规则管理(拖拽排序 + 规则测试) +- [ ] 凭证管理(加密展示 + 过期提醒) +- [ ] RBAC 权限矩阵 +- [ ] 告警规则配置 +- [ ] 健康检查配置 + +### Phase 5: 通信与审计(Week 9-10) +- [ ] 通信渠道配置 +- [ ] 联系人管理 +- [ ] 升级策略编辑器 +- [ ] 审计日志(搜索 + 过滤 + 导出) +- [ ] 会话回放页面 + +### Phase 6: 高级功能(Week 11-12) +- [ ] 命令面板(Cmd+K) +- [ ] Web 终端(xterm.js) +- [ ] 全局键盘快捷键 +- [ ] 暗色/亮色主题切换 +- [ ] 响应式布局优化 + +--- + +## 16. package.json 参考 + +```json +{ + "name": "it0-web-admin", + "version": "0.1.0", + "private": true, + "scripts": { + "dev": "next dev", + "build": "next build", + "start": "next start", + "lint": "next lint", + "test": "vitest", + "test:ui": "vitest --ui" + }, + "dependencies": { + "next": "^14.2.0", + "react": "^18.3.0", + "react-dom": "^18.3.0", + + "@reduxjs/toolkit": "^2.2.0", + "react-redux": "^9.1.0", + "zustand": "^4.5.0", + + "@tanstack/react-query": "^5.50.0", + "@tanstack/react-table": "^8.19.0", + + "react-hook-form": "^7.52.0", + "zod": "^3.23.0", + "@hookform/resolvers": "^3.9.0", + + "tailwindcss": "^3.4.0", + "class-variance-authority": "^0.7.0", + "clsx": "^2.1.0", + "tailwind-merge": "^2.4.0", + "lucide-react": "^0.400.0", + + "@monaco-editor/react": "^4.6.0", + "recharts": "^2.12.0", + "@xterm/xterm": "^5.5.0", + "@xterm/addon-fit": "^0.10.0", + "@xterm/addon-web-links": "^0.11.0", + + "next-auth": "^5.0.0", + "cmdk": "^1.0.0", + "sonner": "^1.5.0", + "@dnd-kit/core": "^6.1.0", + "@dnd-kit/sortable": "^8.0.0", + "date-fns": "^3.6.0" + }, + "devDependencies": { + "typescript": "^5.5.0", + "@types/react": "^18.3.0", + "@types/react-dom": "^18.3.0", + "vitest": "^2.0.0", + "@testing-library/react": "^16.0.0", + "@testing-library/jest-dom": "^6.4.0", + "eslint": "^9.0.0", + "eslint-config-next": "^14.2.0", + "prettier": "^3.3.0", + "prettier-plugin-tailwindcss": "^0.6.0" + } +} +``` diff --git a/it0-web-admin/next.config.js b/it0-web-admin/next.config.js new file mode 100644 index 0000000..4817751 --- /dev/null +++ b/it0-web-admin/next.config.js @@ -0,0 +1,16 @@ +/** @type {import('next').NextConfig} */ +const nextConfig = { + experimental: { + serverActions: true, + }, + async rewrites() { + return [ + { + source: '/api/proxy/:path*', + destination: `${process.env.API_BASE_URL || 'http://localhost:8000'}/:path*`, + }, + ]; + }, +}; + +module.exports = nextConfig; diff --git a/it0-web-admin/package-lock.json b/it0-web-admin/package-lock.json new file mode 100644 index 0000000..808c5fc --- /dev/null +++ b/it0-web-admin/package-lock.json @@ -0,0 +1,9512 @@ +{ + "name": "it0-web-admin", + "version": "0.1.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "it0-web-admin", + "version": "0.1.0", + "dependencies": { + "@hookform/resolvers": "^3.3.0", + "@monaco-editor/react": "^4.6.0", + "@radix-ui/react-dialog": "^1.0.0", + "@radix-ui/react-dropdown-menu": "^2.0.0", + "@radix-ui/react-select": "^2.0.0", + "@radix-ui/react-tabs": "^1.0.0", + "@radix-ui/react-toast": "^1.1.0", + "@radix-ui/react-tooltip": "^1.0.0", + "@reduxjs/toolkit": "^2.2.0", + "@tanstack/react-query": "^5.45.0", + "@tanstack/react-table": "^8.17.0", + "@xterm/addon-fit": "^0.10.0", + "@xterm/addon-web-links": "^0.11.0", + "@xterm/xterm": "^5.5.0", + "class-variance-authority": "^0.7.0", + "clsx": "^2.1.0", + "date-fns": "^3.6.0", + "lucide-react": "^0.378.0", + "next": "^14.2.0", + "react": "^18.3.0", + "react-dom": "^18.3.0", + "react-hook-form": "^7.51.0", + "react-redux": "^9.1.0", + "recharts": "^2.12.0", + "sonner": "^1.4.0", + "tailwind-merge": "^2.3.0", + "tailwindcss": "^3.4.0", + "zod": "^3.23.0", + "zustand": "^4.5.0" + }, + "devDependencies": { + "@testing-library/react": "^15.0.0", + "@types/node": "^20.0.0", + "@types/react": "^18.3.0", + "@types/react-dom": "^18.3.0", + "autoprefixer": "^10.4.0", + "eslint": "^8.57.0", + "eslint-config-next": "^14.2.0", + "postcss": "^8.4.0", + "prettier": "^3.2.0", + "typescript": "^5.4.0", + "vitest": "^1.6.0" + } + }, + "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==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.0.tgz", + "integrity": "sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.28.5", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + }, + "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/runtime": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.28.6.tgz", + "integrity": "sha512-05WQkdpL9COIMz4LjTxGpPNCdlpyimKppYNoJ5Di5EUObifl8t4tuLuUBBZEpoLYOmfvIWrsp9fCl0HoPRVTdA==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@emnapi/core": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.8.1.tgz", + "integrity": "sha512-AvT9QFpxK0Zd8J0jopedNm+w/2fIzvtPKPjqyw9jwvBaReTTqPBk9Hixaz7KbjimP+QNz605/XnjFcDAL2pqBg==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/wasi-threads": "1.1.0", + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/runtime": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.8.1.tgz", + "integrity": "sha512-mehfKSMWjjNol8659Z8KxEMrdSJDDot5SXMq00dM8BN4o+CLNXQ0xH2V7EchNHV4RmbZLmmPdEaXZc5H2FXmDg==", + "dev": true, + "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/@esbuild/aix-ppc64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz", + "integrity": "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.21.5.tgz", + "integrity": "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz", + "integrity": "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.21.5.tgz", + "integrity": "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz", + "integrity": "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz", + "integrity": "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz", + "integrity": "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz", + "integrity": "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz", + "integrity": "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz", + "integrity": "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz", + "integrity": "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz", + "integrity": "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz", + "integrity": "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz", + "integrity": "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz", + "integrity": "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz", + "integrity": "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz", + "integrity": "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz", + "integrity": "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz", + "integrity": "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz", + "integrity": "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz", + "integrity": "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz", + "integrity": "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz", + "integrity": "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.9.1", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.1.tgz", + "integrity": "sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==", + "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/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/eslintrc": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz", + "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^9.6.0", + "globals": "^13.19.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint/js": { + "version": "8.57.1", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.1.tgz", + "integrity": "sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/@floating-ui/core": { + "version": "1.7.4", + "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.7.4.tgz", + "integrity": "sha512-C3HlIdsBxszvm5McXlB8PeOEWfBhcGBTZGkGlWc2U0KFY5IwG5OQEuQ8rq52DZmcHDlPLd+YFBK+cZcytwIFWg==", + "license": "MIT", + "dependencies": { + "@floating-ui/utils": "^0.2.10" + } + }, + "node_modules/@floating-ui/dom": { + "version": "1.7.5", + "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.7.5.tgz", + "integrity": "sha512-N0bD2kIPInNHUHehXhMke1rBGs1dwqvC9O9KYMyyjK7iXt7GAhnro7UlcuYcGdS/yYOlq0MAVgrow8IbWJwyqg==", + "license": "MIT", + "dependencies": { + "@floating-ui/core": "^1.7.4", + "@floating-ui/utils": "^0.2.10" + } + }, + "node_modules/@floating-ui/react-dom": { + "version": "2.1.7", + "resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-2.1.7.tgz", + "integrity": "sha512-0tLRojf/1Go2JgEVm+3Frg9A3IW8bJgKgdO0BN5RkF//ufuz2joZM63Npau2ff3J6lUVYgDSNzNkR+aH3IVfjg==", + "license": "MIT", + "dependencies": { + "@floating-ui/dom": "^1.7.5" + }, + "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/@hookform/resolvers": { + "version": "3.10.0", + "resolved": "https://registry.npmjs.org/@hookform/resolvers/-/resolvers-3.10.0.tgz", + "integrity": "sha512-79Dv+3mDF7i+2ajj7SkypSKHhl1cbln1OGavqrsF7p6mbUv11xpqpacPsGDCTRvCSjEEIez2ef1NveSVL3b0Ag==", + "license": "MIT", + "peerDependencies": { + "react-hook-form": "^7.0.0" + } + }, + "node_modules/@humanwhocodes/config-array": { + "version": "0.13.0", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.13.0.tgz", + "integrity": "sha512-DZLEEqFWQFiyK6h5YIeynKx7JlvCYWL0cImfSRXZ9l4Sg2efkFGTuFf6vzXjK1cq6IYkU+Eg/JizXw+TD2vRNw==", + "deprecated": "Use @eslint/config-array instead", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@humanwhocodes/object-schema": "^2.0.3", + "debug": "^4.3.1", + "minimatch": "^3.0.5" + }, + "engines": { + "node": ">=10.10.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/object-schema": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz", + "integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==", + "deprecated": "Use @eslint/object-schema instead", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@isaacs/cliui/node_modules/ansi-regex": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", + "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/strip-ansi": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.2.tgz", + "integrity": "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/@jest/schemas": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz", + "integrity": "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@sinclair/typebox": "^0.27.8" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "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==", + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@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==", + "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==", + "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==", + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@monaco-editor/loader": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/@monaco-editor/loader/-/loader-1.7.0.tgz", + "integrity": "sha512-gIwR1HrJrrx+vfyOhYmCZ0/JcWqG5kbfG7+d3f/C1LXk2EvzAbHSg3MQ5lO2sMlo9izoAZ04shohfKLVT6crVA==", + "license": "MIT", + "dependencies": { + "state-local": "^1.0.6" + } + }, + "node_modules/@monaco-editor/react": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/@monaco-editor/react/-/react-4.7.0.tgz", + "integrity": "sha512-cyzXQCtO47ydzxpQtCGSQGOC8Gk3ZUeBXFAxD+CWXYFo5OqZyZUonFl0DwUlTyAfRHntBfw2p3w4s9R6oe1eCA==", + "license": "MIT", + "dependencies": { + "@monaco-editor/loader": "^1.5.0" + }, + "peerDependencies": { + "monaco-editor": ">= 0.25.0 < 1", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", + "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, + "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": "14.2.35", + "resolved": "https://registry.npmjs.org/@next/env/-/env-14.2.35.tgz", + "integrity": "sha512-DuhvCtj4t9Gwrx80dmz2F4t/zKQ4ktN8WrMwOuVzkJfBilwAwGr6v16M5eI8yCuZ63H9TTuEU09Iu2HqkzFPVQ==", + "license": "MIT" + }, + "node_modules/@next/eslint-plugin-next": { + "version": "14.2.35", + "resolved": "https://registry.npmjs.org/@next/eslint-plugin-next/-/eslint-plugin-next-14.2.35.tgz", + "integrity": "sha512-Jw9A3ICz2183qSsqwi7fgq4SBPiNfmOLmTPXKvlnzstUwyvBrtySiY+8RXJweNAs9KThb1+bYhZh9XWcNOr2zQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "glob": "10.3.10" + } + }, + "node_modules/@next/swc-darwin-arm64": { + "version": "14.2.33", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-14.2.33.tgz", + "integrity": "sha512-HqYnb6pxlsshoSTubdXKu15g3iivcbsMXg4bYpjL2iS/V6aQot+iyF4BUc2qA/J/n55YtvE4PHMKWBKGCF/+wA==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-darwin-x64": { + "version": "14.2.33", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-14.2.33.tgz", + "integrity": "sha512-8HGBeAE5rX3jzKvF593XTTFg3gxeU4f+UWnswa6JPhzaR6+zblO5+fjltJWIZc4aUalqTclvN2QtTC37LxvZAA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-arm64-gnu": { + "version": "14.2.33", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-14.2.33.tgz", + "integrity": "sha512-JXMBka6lNNmqbkvcTtaX8Gu5by9547bukHQvPoLe9VRBx1gHwzf5tdt4AaezW85HAB3pikcvyqBToRTDA4DeLw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-arm64-musl": { + "version": "14.2.33", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-14.2.33.tgz", + "integrity": "sha512-Bm+QulsAItD/x6Ih8wGIMfRJy4G73tu1HJsrccPW6AfqdZd0Sfm5Imhgkgq2+kly065rYMnCOxTBvmvFY1BKfg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-x64-gnu": { + "version": "14.2.33", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-14.2.33.tgz", + "integrity": "sha512-FnFn+ZBgsVMbGDsTqo8zsnRzydvsGV8vfiWwUo1LD8FTmPTdV+otGSWKc4LJec0oSexFnCYVO4hX8P8qQKaSlg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-x64-musl": { + "version": "14.2.33", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-14.2.33.tgz", + "integrity": "sha512-345tsIWMzoXaQndUTDv1qypDRiebFxGYx9pYkhwY4hBRaOLt8UGfiWKr9FSSHs25dFIf8ZqIFaPdy5MljdoawA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-win32-arm64-msvc": { + "version": "14.2.33", + "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-14.2.33.tgz", + "integrity": "sha512-nscpt0G6UCTkrT2ppnJnFsYbPDQwmum4GNXYTeoTIdsmMydSKFz9Iny2jpaRupTb+Wl298+Rh82WKzt9LCcqSQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-win32-ia32-msvc": { + "version": "14.2.33", + "resolved": "https://registry.npmjs.org/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-14.2.33.tgz", + "integrity": "sha512-pc9LpGNKhJ0dXQhZ5QMmYxtARwwmWLpeocFmVG5Z0DzWq5Uf0izcI8tLc+qOpqxO1PWqZ5A7J1blrUIKrIFc7Q==", + "cpu": [ + "ia32" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-win32-x64-msvc": { + "version": "14.2.33", + "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-14.2.33.tgz", + "integrity": "sha512-nOjfZMy8B94MdisuzZo9/57xuFVLHJaDj5e/xrduJp9CV2/HrfxTRH2fbyLe+K9QT41WBLUd4iXX3R7jBp0EUg==", + "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==", + "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==", + "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==", + "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/@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=14" + } + }, + "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-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-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-dropdown-menu": { + "version": "2.1.16", + "resolved": "https://registry.npmjs.org/@radix-ui/react-dropdown-menu/-/react-dropdown-menu-2.1.16.tgz", + "integrity": "sha512-1PLGQEynI/3OX/ftV54COn+3Sud/Mn8vALg2rWnBLnRaGtJDduNW/22XjlGgPdpcIbiQxjKtb7BkcjP00nqfJw==", + "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-id": "1.1.1", + "@radix-ui/react-menu": "2.1.16", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-use-controllable-state": "1.2.2" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "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-menu": { + "version": "2.1.16", + "resolved": "https://registry.npmjs.org/@radix-ui/react-menu/-/react-menu-2.1.16.tgz", + "integrity": "sha512-72F2T+PLlphrqLcAotYPp0uJMr5SjP5SL01wfEspJbru5Zs5vQaSHb4VB3ZMJPimgHHCHG7gMOeOB9H3Hdmtxg==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.3", + "@radix-ui/react-collection": "1.1.7", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-direction": "1.1.1", + "@radix-ui/react-dismissable-layer": "1.1.11", + "@radix-ui/react-focus-guards": "1.1.3", + "@radix-ui/react-focus-scope": "1.1.7", + "@radix-ui/react-id": "1.1.1", + "@radix-ui/react-popper": "1.2.8", + "@radix-ui/react-portal": "1.1.9", + "@radix-ui/react-presence": "1.1.5", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-roving-focus": "1.1.11", + "@radix-ui/react-slot": "1.2.3", + "@radix-ui/react-use-callback-ref": "1.1.1", + "aria-hidden": "^1.2.4", + "react-remove-scroll": "^2.6.3" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "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-roving-focus": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/@radix-ui/react-roving-focus/-/react-roving-focus-1.1.11.tgz", + "integrity": "sha512-7A6S9jSgm/S+7MdtNDSb+IU859vQqJ/QAtcYQcfFC6W8RS4IxIZDldLR0xqCFZ6DCyrQLjLPsxtTNch5jVA4lA==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.3", + "@radix-ui/react-collection": "1.1.7", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-direction": "1.1.1", + "@radix-ui/react-id": "1.1.1", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-use-callback-ref": "1.1.1", + "@radix-ui/react-use-controllable-state": "1.2.2" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "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-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-tabs": { + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/@radix-ui/react-tabs/-/react-tabs-1.1.13.tgz", + "integrity": "sha512-7xdcatg7/U+7+Udyoj2zodtI9H/IIopqo+YOIcZOq1nJwXWBZ9p8xiu5llXlekDbZkca79a/fozEYQXIA4sW6A==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.3", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-direction": "1.1.1", + "@radix-ui/react-id": "1.1.1", + "@radix-ui/react-presence": "1.1.5", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-roving-focus": "1.1.11", + "@radix-ui/react-use-controllable-state": "1.2.2" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-toast": { + "version": "1.2.15", + "resolved": "https://registry.npmjs.org/@radix-ui/react-toast/-/react-toast-1.2.15.tgz", + "integrity": "sha512-3OSz3TacUWy4WtOXV38DggwxoqJK4+eDkNMl5Z/MJZaoUPaP4/9lf81xXMe1I2ReTAptverZUpbPY4wWwWyL5g==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.3", + "@radix-ui/react-collection": "1.1.7", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-dismissable-layer": "1.1.11", + "@radix-ui/react-portal": "1.1.9", + "@radix-ui/react-presence": "1.1.5", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-use-callback-ref": "1.1.1", + "@radix-ui/react-use-controllable-state": "1.2.2", + "@radix-ui/react-use-layout-effect": "1.1.1", + "@radix-ui/react-visually-hidden": "1.2.3" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-tooltip": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@radix-ui/react-tooltip/-/react-tooltip-1.2.8.tgz", + "integrity": "sha512-tY7sVt1yL9ozIxvmbtN5qtmH2krXcBCfjEiCgKGLqunJHvgvZG2Pcl2oQ3kbcZARb1BGEHdkLzcYGO8ynVlieg==", + "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-id": "1.1.1", + "@radix-ui/react-popper": "1.2.8", + "@radix-ui/react-portal": "1.1.9", + "@radix-ui/react-presence": "1.1.5", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-slot": "1.2.3", + "@radix-ui/react-use-controllable-state": "1.2.2", + "@radix-ui/react-visually-hidden": "1.2.3" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "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/@reduxjs/toolkit": { + "version": "2.11.2", + "resolved": "https://registry.npmjs.org/@reduxjs/toolkit/-/toolkit-2.11.2.tgz", + "integrity": "sha512-Kd6kAHTA6/nUpp8mySPqj3en3dm0tdMIgbttnQ1xFMVpufoj+ADi8pXLBsd4xzTRHQa7t/Jv8W5UnCuW4kuWMQ==", + "license": "MIT", + "dependencies": { + "@standard-schema/spec": "^1.0.0", + "@standard-schema/utils": "^0.3.0", + "immer": "^11.0.0", + "redux": "^5.0.1", + "redux-thunk": "^3.1.0", + "reselect": "^5.1.0" + }, + "peerDependencies": { + "react": "^16.9.0 || ^17.0.0 || ^18 || ^19", + "react-redux": "^7.2.1 || ^8.1.3 || ^9.0.0" + }, + "peerDependenciesMeta": { + "react": { + "optional": true + }, + "react-redux": { + "optional": true + } + } + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.57.1.tgz", + "integrity": "sha512-A6ehUVSiSaaliTxai040ZpZ2zTevHYbvu/lDoeAteHI8QnaosIzm4qwtezfRg1jOYaUmnzLX1AOD6Z+UJjtifg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.57.1.tgz", + "integrity": "sha512-dQaAddCY9YgkFHZcFNS/606Exo8vcLHwArFZ7vxXq4rigo2bb494/xKMMwRRQW6ug7Js6yXmBZhSBRuBvCCQ3w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.57.1.tgz", + "integrity": "sha512-crNPrwJOrRxagUYeMn/DZwqN88SDmwaJ8Cvi/TN1HnWBU7GwknckyosC2gd0IqYRsHDEnXf328o9/HC6OkPgOg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.57.1.tgz", + "integrity": "sha512-Ji8g8ChVbKrhFtig5QBV7iMaJrGtpHelkB3lsaKzadFBe58gmjfGXAOfI5FV0lYMH8wiqsxKQ1C9B0YTRXVy4w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.57.1.tgz", + "integrity": "sha512-R+/WwhsjmwodAcz65guCGFRkMb4gKWTcIeLy60JJQbXrJ97BOXHxnkPFrP+YwFlaS0m+uWJTstrUA9o+UchFug==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.57.1.tgz", + "integrity": "sha512-IEQTCHeiTOnAUC3IDQdzRAGj3jOAYNr9kBguI7MQAAZK3caezRrg0GxAb6Hchg4lxdZEI5Oq3iov/w/hnFWY9Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.57.1.tgz", + "integrity": "sha512-F8sWbhZ7tyuEfsmOxwc2giKDQzN3+kuBLPwwZGyVkLlKGdV1nvnNwYD0fKQ8+XS6hp9nY7B+ZeK01EBUE7aHaw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.57.1.tgz", + "integrity": "sha512-rGfNUfn0GIeXtBP1wL5MnzSj98+PZe/AXaGBCRmT0ts80lU5CATYGxXukeTX39XBKsxzFpEeK+Mrp9faXOlmrw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.57.1.tgz", + "integrity": "sha512-MMtej3YHWeg/0klK2Qodf3yrNzz6CGjo2UntLvk2RSPlhzgLvYEB3frRvbEF2wRKh1Z2fDIg9KRPe1fawv7C+g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.57.1.tgz", + "integrity": "sha512-1a/qhaaOXhqXGpMFMET9VqwZakkljWHLmZOX48R0I/YLbhdxr1m4gtG1Hq7++VhVUmf+L3sTAf9op4JlhQ5u1Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-gnu": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.57.1.tgz", + "integrity": "sha512-QWO6RQTZ/cqYtJMtxhkRkidoNGXc7ERPbZN7dVW5SdURuLeVU7lwKMpo18XdcmpWYd0qsP1bwKPf7DNSUinhvA==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-musl": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.57.1.tgz", + "integrity": "sha512-xpObYIf+8gprgWaPP32xiN5RVTi/s5FCR+XMXSKmhfoJjrpRAjCuuqQXyxUa/eJTdAE6eJ+KDKaoEqjZQxh3Gw==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-gnu": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.57.1.tgz", + "integrity": "sha512-4BrCgrpZo4hvzMDKRqEaW1zeecScDCR+2nZ86ATLhAoJ5FQ+lbHVD3ttKe74/c7tNT9c6F2viwB3ufwp01Oh2w==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-musl": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.57.1.tgz", + "integrity": "sha512-NOlUuzesGauESAyEYFSe3QTUguL+lvrN1HtwEEsU2rOwdUDeTMJdO5dUYl/2hKf9jWydJrO9OL/XSSf65R5+Xw==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.57.1.tgz", + "integrity": "sha512-ptA88htVp0AwUUqhVghwDIKlvJMD/fmL/wrQj99PRHFRAG6Z5nbWoWG4o81Nt9FT+IuqUQi+L31ZKAFeJ5Is+A==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.57.1.tgz", + "integrity": "sha512-S51t7aMMTNdmAMPpBg7OOsTdn4tySRQvklmL3RpDRyknk87+Sp3xaumlatU+ppQ+5raY7sSTcC2beGgvhENfuw==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.57.1.tgz", + "integrity": "sha512-Bl00OFnVFkL82FHbEqy3k5CUCKH6OEJL54KCyx2oqsmZnFTR8IoNqBF+mjQVcRCT5sB6yOvK8A37LNm/kPJiZg==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.57.1.tgz", + "integrity": "sha512-ABca4ceT4N+Tv/GtotnWAeXZUZuM/9AQyCyKYyKnpk4yoA7QIAuBt6Hkgpw8kActYlew2mvckXkvx0FfoInnLg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.57.1.tgz", + "integrity": "sha512-HFps0JeGtuOR2convgRRkHCekD7j+gdAuXM+/i6kGzQtFhlCtQkpwtNzkNj6QhCDp7DRJ7+qC/1Vg2jt5iSOFw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-openbsd-x64": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.57.1.tgz", + "integrity": "sha512-H+hXEv9gdVQuDTgnqD+SQffoWoc0Of59AStSzTEj/feWTBAnSfSD3+Dql1ZruJQxmykT/JVY0dE8Ka7z0DH1hw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ] + }, + "node_modules/@rollup/rollup-openharmony-arm64": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.57.1.tgz", + "integrity": "sha512-4wYoDpNg6o/oPximyc/NG+mYUejZrCU2q+2w6YZqrAs2UcNUChIZXjtafAiiZSUc7On8v5NyNj34Kzj/Ltk6dQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.57.1.tgz", + "integrity": "sha512-O54mtsV/6LW3P8qdTcamQmuC990HDfR71lo44oZMZlXU4tzLrbvTii87Ni9opq60ds0YzuAlEr/GNwuNluZyMQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.57.1.tgz", + "integrity": "sha512-P3dLS+IerxCT/7D2q2FYcRdWRl22dNbrbBEtxdWhXrfIMPP9lQhb5h4Du04mdl5Woq05jVCDPCMF7Ub0NAjIew==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-gnu": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.57.1.tgz", + "integrity": "sha512-VMBH2eOOaKGtIJYleXsi2B8CPVADrh+TyNxJ4mWPnKfLB/DBUmzW+5m1xUrcwWoMfSLagIRpjUFeW5CO5hyciQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.57.1.tgz", + "integrity": "sha512-mxRFDdHIWRxg3UfIIAwCm6NzvxG0jDX/wBN6KsQFTvKFqqg9vTrWUE68qEjHt19A5wwx5X5aUi2zuZT7YR0jrA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "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/@rushstack/eslint-patch": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/@rushstack/eslint-patch/-/eslint-patch-1.15.0.tgz", + "integrity": "sha512-ojSshQPKwVvSMR8yT2L/QtUkV5SXi/IfDiJ4/8d6UbTPjiHVmxZzUAzGD8Tzks1b9+qQkZa0isUOvYObedITaw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@sinclair/typebox": { + "version": "0.27.10", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.10.tgz", + "integrity": "sha512-MTBk/3jGLNB2tVxv6uLlFh1iu64iYOQ2PbdOSK3NW8JZsmlaOh2q6sdtKowBhfw8QFLmYNzTW4/oK4uATIi6ZA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@standard-schema/spec": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.1.0.tgz", + "integrity": "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==", + "license": "MIT" + }, + "node_modules/@standard-schema/utils": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@standard-schema/utils/-/utils-0.3.0.tgz", + "integrity": "sha512-e7Mew686owMaPJVNNLs55PUvgz371nKgwsc4vxE49zsODpJEnxgxRo2y/OKrqueavXgZNMDVj3DdHFlaSAeU8g==", + "license": "MIT" + }, + "node_modules/@swc/counter": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@swc/counter/-/counter-0.1.3.tgz", + "integrity": "sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==", + "license": "Apache-2.0" + }, + "node_modules/@swc/helpers": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.5.tgz", + "integrity": "sha512-KGYxvIOXcceOAbEk4bi/dVLEK9z8sZ0uBB3Il5b1rhfClSpcX0yfRO0KmTkqR2cnQDymwLB+25ZyMzICg/cm/A==", + "license": "Apache-2.0", + "dependencies": { + "@swc/counter": "^0.1.3", + "tslib": "^2.4.0" + } + }, + "node_modules/@tanstack/query-core": { + "version": "5.90.20", + "resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-5.90.20.tgz", + "integrity": "sha512-OMD2HLpNouXEfZJWcKeVKUgQ5n+n3A2JFmBaScpNDUqSrQSjiveC7dKMe53uJUg1nDG16ttFPz2xfilz6i2uVg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + } + }, + "node_modules/@tanstack/react-query": { + "version": "5.90.20", + "resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-5.90.20.tgz", + "integrity": "sha512-vXBxa+qeyveVO7OA0jX1z+DeyCA4JKnThKv411jd5SORpBKgkcVnYKCiBgECvADvniBX7tobwBmg01qq9JmMJw==", + "license": "MIT", + "dependencies": { + "@tanstack/query-core": "5.90.20" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + }, + "peerDependencies": { + "react": "^18 || ^19" + } + }, + "node_modules/@tanstack/react-table": { + "version": "8.21.3", + "resolved": "https://registry.npmjs.org/@tanstack/react-table/-/react-table-8.21.3.tgz", + "integrity": "sha512-5nNMTSETP4ykGegmVkhjcS8tTLW6Vl4axfEGQN3v0zdHYbK4UfoqfPChclTrJ4EoK9QynqAu9oUf8VEmrpZ5Ww==", + "license": "MIT", + "dependencies": { + "@tanstack/table-core": "8.21.3" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + }, + "peerDependencies": { + "react": ">=16.8", + "react-dom": ">=16.8" + } + }, + "node_modules/@tanstack/table-core": { + "version": "8.21.3", + "resolved": "https://registry.npmjs.org/@tanstack/table-core/-/table-core-8.21.3.tgz", + "integrity": "sha512-ldZXEhOBb8Is7xLs01fR3YEc3DERiz5silj8tnGkFZytt1abEvl/GhUmCE0PMLaMPTa3Jk4HbKmRlHmu+gCftg==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + } + }, + "node_modules/@testing-library/dom": { + "version": "10.4.1", + "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-10.4.1.tgz", + "integrity": "sha512-o4PXJQidqJl82ckFaXUeoAW+XysPLauYI43Abki5hABd853iMhitooc6znOnczgbTYmEP6U6/y1ZyKAIsvMKGg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.10.4", + "@babel/runtime": "^7.12.5", + "@types/aria-query": "^5.0.1", + "aria-query": "5.3.0", + "dom-accessibility-api": "^0.5.9", + "lz-string": "^1.5.0", + "picocolors": "1.1.1", + "pretty-format": "^27.0.2" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@testing-library/react": { + "version": "15.0.7", + "resolved": "https://registry.npmjs.org/@testing-library/react/-/react-15.0.7.tgz", + "integrity": "sha512-cg0RvEdD1TIhhkm1IeYMQxrzy0MtUNfa3minv4MjbgcYzJAZ7yD0i0lwoPOTPr+INtiXFezt2o8xMSnyHhEn2Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.12.5", + "@testing-library/dom": "^10.0.0", + "@types/react-dom": "^18.0.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/react": "^18.0.0", + "react": "^18.0.0", + "react-dom": "^18.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "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/aria-query": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-5.0.4.tgz", + "integrity": "sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/d3-array": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/@types/d3-array/-/d3-array-3.2.2.tgz", + "integrity": "sha512-hOLWVbm7uRza0BYXpIIW5pxfrKe0W+D5lrFiAEYR+pb6w3N2SwSMaJbXdUfSEv+dT4MfHBLtn5js0LAWaO6otw==", + "license": "MIT" + }, + "node_modules/@types/d3-color": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/@types/d3-color/-/d3-color-3.1.3.tgz", + "integrity": "sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A==", + "license": "MIT" + }, + "node_modules/@types/d3-ease": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/d3-ease/-/d3-ease-3.0.2.tgz", + "integrity": "sha512-NcV1JjO5oDzoK26oMzbILE6HW7uVXOHLQvHshBUW4UMdZGfiY6v5BeQwh9a9tCzv+CeefZQHJt5SRgK154RtiA==", + "license": "MIT" + }, + "node_modules/@types/d3-interpolate": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/d3-interpolate/-/d3-interpolate-3.0.4.tgz", + "integrity": "sha512-mgLPETlrpVV1YRJIglr4Ez47g7Yxjl1lj7YKsiMCb27VJH9W8NVM6Bb9d8kkpG/uAQS5AmbA48q2IAolKKo1MA==", + "license": "MIT", + "dependencies": { + "@types/d3-color": "*" + } + }, + "node_modules/@types/d3-path": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@types/d3-path/-/d3-path-3.1.1.tgz", + "integrity": "sha512-VMZBYyQvbGmWyWVea0EHs/BwLgxc+MKi1zLDCONksozI4YJMcTt8ZEuIR4Sb1MMTE8MMW49v0IwI5+b7RmfWlg==", + "license": "MIT" + }, + "node_modules/@types/d3-scale": { + "version": "4.0.9", + "resolved": "https://registry.npmjs.org/@types/d3-scale/-/d3-scale-4.0.9.tgz", + "integrity": "sha512-dLmtwB8zkAeO/juAMfnV+sItKjlsw2lKdZVVy6LRr0cBmegxSABiLEpGVmSJJ8O08i4+sGR6qQtb6WtuwJdvVw==", + "license": "MIT", + "dependencies": { + "@types/d3-time": "*" + } + }, + "node_modules/@types/d3-shape": { + "version": "3.1.8", + "resolved": "https://registry.npmjs.org/@types/d3-shape/-/d3-shape-3.1.8.tgz", + "integrity": "sha512-lae0iWfcDeR7qt7rA88BNiqdvPS5pFVPpo5OfjElwNaT2yyekbM0C9vK+yqBqEmHr6lDkRnYNoTBYlAgJa7a4w==", + "license": "MIT", + "dependencies": { + "@types/d3-path": "*" + } + }, + "node_modules/@types/d3-time": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/d3-time/-/d3-time-3.0.4.tgz", + "integrity": "sha512-yuzZug1nkAAaBlBBikKZTgzCeA+k1uy4ZFwWANOfKw5z5LRhV0gNA7gNkKm7HoK+HRN0wX3EkxGk0fpbWhmB7g==", + "license": "MIT" + }, + "node_modules/@types/d3-timer": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/d3-timer/-/d3-timer-3.0.2.tgz", + "integrity": "sha512-Ps3T8E8dZDam6fUyNiMkekK3XUsaUEik+idO9/YjPtfj2qruF8tFBXS7XhtE4iIXBLxhmLjP3SXpLhVf21I9Lw==", + "license": "MIT" + }, + "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/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": "20.19.33", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.33.tgz", + "integrity": "sha512-Rs1bVAIdBs5gbTIKza/tgpMuG1k3U/UMJLWecIMxNdJFDMzcM5LOiLVRYh3PilWEYDIeUDv7bpiHPLPsbydGcw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "undici-types": "~6.21.0" + } + }, + "node_modules/@types/prop-types": { + "version": "15.7.15", + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.15.tgz", + "integrity": "sha512-F6bEyamV9jKGAFBEmlQnesRPGOQqS2+Uwi0Em15xenOxHaf2hv6L8YCVn3rPdPJOiJfPiCnLIRyvwVaqMY3MIw==", + "devOptional": true, + "license": "MIT" + }, + "node_modules/@types/react": { + "version": "18.3.28", + "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.28.tgz", + "integrity": "sha512-z9VXpC7MWrhfWipitjNdgCauoMLRdIILQsAEV+ZesIzBq/oUlxk0m3ApZuMFCXdnS4U7KrI+l3WRUEGQ8K1QKw==", + "devOptional": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@types/prop-types": "*", + "csstype": "^3.2.2" + } + }, + "node_modules/@types/react-dom": { + "version": "18.3.7", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.3.7.tgz", + "integrity": "sha512-MEe3UeoENYVFXzoXEWsvcpg6ZvlrFNlOQ7EOsvhI3CfAXwzPfO8Qwuxd40nepsYKqyyVQnTdEfv68q91yLcKrQ==", + "devOptional": true, + "license": "MIT", + "peer": true, + "peerDependencies": { + "@types/react": "^18.0.0" + } + }, + "node_modules/@types/trusted-types": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.7.tgz", + "integrity": "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==", + "license": "MIT", + "optional": true + }, + "node_modules/@types/use-sync-external-store": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/@types/use-sync-external-store/-/use-sync-external-store-0.0.6.tgz", + "integrity": "sha512-zFDAD+tlpf2r4asuHEj0XH6pY6i0g5NeAHPn+15wk3BV6JA69eERFXC1gyGThDkVa1zCyKr5jox1+2LbV/AMLg==", + "license": "MIT" + }, + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "8.54.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.54.0.tgz", + "integrity": "sha512-hAAP5io/7csFStuOmR782YmTthKBJ9ND3WVL60hcOjvtGFb+HJxH4O5huAcmcZ9v9G8P+JETiZ/G1B8MALnWZQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/regexpp": "^4.12.2", + "@typescript-eslint/scope-manager": "8.54.0", + "@typescript-eslint/type-utils": "8.54.0", + "@typescript-eslint/utils": "8.54.0", + "@typescript-eslint/visitor-keys": "8.54.0", + "ignore": "^7.0.5", + "natural-compare": "^1.4.0", + "ts-api-utils": "^2.4.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.54.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.54.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.54.0.tgz", + "integrity": "sha512-BtE0k6cjwjLZoZixN0t5AKP0kSzlGu7FctRXYuPAm//aaiZhmfq1JwdYpYr1brzEspYyFeF+8XF5j2VK6oalrA==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@typescript-eslint/scope-manager": "8.54.0", + "@typescript-eslint/types": "8.54.0", + "@typescript-eslint/typescript-estree": "8.54.0", + "@typescript-eslint/visitor-keys": "8.54.0", + "debug": "^4.4.3" + }, + "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.54.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.54.0.tgz", + "integrity": "sha512-YPf+rvJ1s7MyiWM4uTRhE4DvBXrEV+d8oC3P9Y2eT7S+HBS0clybdMIPnhiATi9vZOYDc7OQ1L/i6ga6NFYK/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/tsconfig-utils": "^8.54.0", + "@typescript-eslint/types": "^8.54.0", + "debug": "^4.4.3" + }, + "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.54.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.54.0.tgz", + "integrity": "sha512-27rYVQku26j/PbHYcVfRPonmOlVI6gihHtXFbTdB5sb6qA0wdAQAbyXFVarQ5t4HRojIz64IV90YtsjQSSGlQg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.54.0", + "@typescript-eslint/visitor-keys": "8.54.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.54.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.54.0.tgz", + "integrity": "sha512-dRgOyT2hPk/JwxNMZDsIXDgyl9axdJI3ogZ2XWhBPsnZUv+hPesa5iuhdYt2gzwA9t8RE5ytOJ6xB0moV0Ujvw==", + "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.54.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.54.0.tgz", + "integrity": "sha512-hiLguxJWHjjwL6xMBwD903ciAwd7DmK30Y9Axs/etOkftC3ZNN9K44IuRD/EB08amu+Zw6W37x9RecLkOo3pMA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.54.0", + "@typescript-eslint/typescript-estree": "8.54.0", + "@typescript-eslint/utils": "8.54.0", + "debug": "^4.4.3", + "ts-api-utils": "^2.4.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.54.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.54.0.tgz", + "integrity": "sha512-PDUI9R1BVjqu7AUDsRBbKMtwmjWcn4J3le+5LpcFgWULN3LvHC5rkc9gCVxbrsrGmO1jfPybN5s6h4Jy+OnkAA==", + "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.54.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.54.0.tgz", + "integrity": "sha512-BUwcskRaPvTk6fzVWgDPdUndLjB87KYDrN5EYGetnktoeAvPtO4ONHlAZDnj5VFnUANg0Sjm7j4usBlnoVMHwA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/project-service": "8.54.0", + "@typescript-eslint/tsconfig-utils": "8.54.0", + "@typescript-eslint/types": "8.54.0", + "@typescript-eslint/visitor-keys": "8.54.0", + "debug": "^4.4.3", + "minimatch": "^9.0.5", + "semver": "^7.7.3", + "tinyglobby": "^0.2.15", + "ts-api-utils": "^2.4.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.54.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.54.0.tgz", + "integrity": "sha512-9Cnda8GS57AQakvRyG0PTejJNlA2xhvyNtEVIMlDWOOeEyBkYWhGPnfrIAnqxLMTSTo6q8g12XVjjev5l1NvMA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.9.1", + "@typescript-eslint/scope-manager": "8.54.0", + "@typescript-eslint/types": "8.54.0", + "@typescript-eslint/typescript-estree": "8.54.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.54.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.54.0.tgz", + "integrity": "sha512-VFlhGSl4opC0bprJiItPQ1RfUhGDIBokcPwaFH4yiBCaNPeld/9VeXbiPO1cLyorQi1G1vL+ecBk1x8o1axORA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.54.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/@typescript-eslint/visitor-keys/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/@ungap/structured-clone": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.0.tgz", + "integrity": "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==", + "dev": true, + "license": "ISC" + }, + "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/@vitest/expect": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-1.6.1.tgz", + "integrity": "sha512-jXL+9+ZNIJKruofqXuuTClf44eSpcHlgj3CiuNihUF3Ioujtmc0zIa3UJOW5RjDK1YLBJZnWBlPuqhYycLioog==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/spy": "1.6.1", + "@vitest/utils": "1.6.1", + "chai": "^4.3.10" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/runner": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-1.6.1.tgz", + "integrity": "sha512-3nSnYXkVkf3mXFfE7vVyPmi3Sazhb/2cfZGGs0JRzFsPFvAMBEcrweV1V1GsrstdXeKCTXlJbvnQwGWgEIHmOA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/utils": "1.6.1", + "p-limit": "^5.0.0", + "pathe": "^1.1.1" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/runner/node_modules/p-limit": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-5.0.0.tgz", + "integrity": "sha512-/Eaoq+QyLSiXQ4lyYV23f14mZRQcXnxfHrN0vCai+ak9G0pp9iEQukIIZq5NccEvwRB8PUnZT0KsOoDCINS1qQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "yocto-queue": "^1.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@vitest/runner/node_modules/yocto-queue": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-1.2.2.tgz", + "integrity": "sha512-4LCcse/U2MHZ63HAJVE+v71o7yOdIe4cZ70Wpf8D/IyjDKYQLV5GD46B+hSTjJsvV5PztjvHoU580EftxjDZFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@vitest/snapshot": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-1.6.1.tgz", + "integrity": "sha512-WvidQuWAzU2p95u8GAKlRMqMyN1yOJkGHnx3M1PL9Raf7AQ1kwLKg04ADlCa3+OXUZE7BceOhVZiuWAbzCKcUQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "magic-string": "^0.30.5", + "pathe": "^1.1.1", + "pretty-format": "^29.7.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/snapshot/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@vitest/snapshot/node_modules/pretty-format": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", + "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/schemas": "^29.6.3", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@vitest/snapshot/node_modules/react-is": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", + "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@vitest/spy": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-1.6.1.tgz", + "integrity": "sha512-MGcMmpGkZebsMZhbQKkAf9CX5zGvjkBTqf8Zx3ApYWXr3wG+QvEu2eXWfnIIWYSJExIp4V9FCKDEeygzkYrXMw==", + "dev": true, + "license": "MIT", + "dependencies": { + "tinyspy": "^2.2.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/utils": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-1.6.1.tgz", + "integrity": "sha512-jOrrUvXM4Av9ZWiG1EajNto0u96kWAhJ1LmPmJhXXQx/32MecEKd10pOLYgS2BQx1TgkGhloPU1ArDW2vvaY6g==", + "dev": true, + "license": "MIT", + "dependencies": { + "diff-sequences": "^29.6.3", + "estree-walker": "^3.0.3", + "loupe": "^2.3.7", + "pretty-format": "^29.7.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/utils/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@vitest/utils/node_modules/pretty-format": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", + "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/schemas": "^29.6.3", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@vitest/utils/node_modules/react-is": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", + "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@xterm/addon-fit": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/@xterm/addon-fit/-/addon-fit-0.10.0.tgz", + "integrity": "sha512-UFYkDm4HUahf2lnEyHvio51TNGiLK66mqP2JoATy7hRZeXaGMRDr00JiSF7m63vR5WKATF605yEggJKsw0JpMQ==", + "license": "MIT", + "peerDependencies": { + "@xterm/xterm": "^5.0.0" + } + }, + "node_modules/@xterm/addon-web-links": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@xterm/addon-web-links/-/addon-web-links-0.11.0.tgz", + "integrity": "sha512-nIHQ38pQI+a5kXnRaTgwqSHnX7KE6+4SVoceompgHL26unAxdfP6IPqUTSYPQgSwM56hsElfoNrrW5V7BUED/Q==", + "license": "MIT", + "peerDependencies": { + "@xterm/xterm": "^5.0.0" + } + }, + "node_modules/@xterm/xterm": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@xterm/xterm/-/xterm-5.5.0.tgz", + "integrity": "sha512-hqJHYaQb5OptNunnyAnkHyM8aCjZ1MEIDTQu1iIbbTD/xops91NB5yq1ZK/dC2JDbVWtF23zUtl9JE2NqwT87A==", + "license": "MIT", + "peer": true + }, + "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/acorn-walk": { + "version": "8.3.4", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.4.tgz", + "integrity": "sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==", + "dev": true, + "license": "MIT", + "dependencies": { + "acorn": "^8.11.0" + }, + "engines": { + "node": ">=0.4.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-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "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/any-promise": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", + "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==", + "license": "MIT" + }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "license": "ISC", + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/arg": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz", + "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==", + "license": "MIT" + }, + "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.0", + "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.0.tgz", + "integrity": "sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "dequal": "^2.0.3" + } + }, + "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/assertion-error": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", + "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==", + "dev": true, + "license": "MIT", + "engines": { + "node": "*" + } + }, + "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.24", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.24.tgz", + "integrity": "sha512-uHZg7N9ULTVbutaIsDRoUkoS8/h3bdsmVJYZ5l3wv8Cp/6UIIoRDm90hZ+BwxUj/hGBEzLxdHNSKuFpn8WOyZw==", + "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.30001766", + "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.1", + "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.11.1.tgz", + "integrity": "sha512-BASOg+YwO2C+346x3LZOeoovTIoTrRqEsqMa6fmfAV0P+U9mFr9NsyOEpiYvFjbc64NMrSswhV50WdXzdb/Z5A==", + "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.19", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.9.19.tgz", + "integrity": "sha512-ipDqC8FrAl/76p2SSWKSI+H9tFwm7vYqXQrItCuiVPt26Km0jS+NzSsBWAaBusvSbQcfJG+JitdMm+wZAgTYqg==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "baseline-browser-mapping": "dist/cli.js" + } + }, + "node_modules/binary-extensions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", + "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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==", + "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/busboy": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz", + "integrity": "sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==", + "dependencies": { + "streamsearch": "^1.1.0" + }, + "engines": { + "node": ">=10.16.0" + } + }, + "node_modules/cac": { + "version": "6.7.14", + "resolved": "https://registry.npmjs.org/cac/-/cac-6.7.14.tgz", + "integrity": "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "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/camelcase-css": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz", + "integrity": "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==", + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001769", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001769.tgz", + "integrity": "sha512-BCfFL1sHijQlBGWBMuJyhZUhzo7wer5sVj9hqekB/7xn0Ypy+pER/edCYQm4exbXj4WiySGp40P8UuTh6w1srg==", + "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/chai": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/chai/-/chai-4.5.0.tgz", + "integrity": "sha512-RITGBfijLkBddZvnn8jdqoTypxvqbOLYQkGGxXzeFjVHvudaPw0HNFD9x928/eUwYWd2dPCugVqspGALTZZQKw==", + "dev": true, + "license": "MIT", + "dependencies": { + "assertion-error": "^1.1.0", + "check-error": "^1.0.3", + "deep-eql": "^4.1.3", + "get-func-name": "^2.0.2", + "loupe": "^2.3.6", + "pathval": "^1.1.1", + "type-detect": "^4.1.0" + }, + "engines": { + "node": ">=4" + } + }, + "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/check-error": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.3.tgz", + "integrity": "sha512-iKEoDYaRmd1mxM90a2OEfWhjsjPpYPuQ+lMYsoxB126+t8fw7ySEO48nmDg5COTjxDI65/Y2OWpeEHk3ZOe8zg==", + "dev": true, + "license": "MIT", + "dependencies": { + "get-func-name": "^2.0.2" + }, + "engines": { + "node": "*" + } + }, + "node_modules/chokidar": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "license": "MIT", + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/chokidar/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==", + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "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/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/commander": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", + "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "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/confbox": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/confbox/-/confbox-0.1.8.tgz", + "integrity": "sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w==", + "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/cssesc": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", + "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", + "license": "MIT", + "bin": { + "cssesc": "bin/cssesc" + }, + "engines": { + "node": ">=4" + } + }, + "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==", + "license": "MIT" + }, + "node_modules/d3-array": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-3.2.4.tgz", + "integrity": "sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==", + "license": "ISC", + "dependencies": { + "internmap": "1 - 2" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-color": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-color/-/d3-color-3.1.0.tgz", + "integrity": "sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-ease": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-ease/-/d3-ease-3.0.1.tgz", + "integrity": "sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-format": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/d3-format/-/d3-format-3.1.2.tgz", + "integrity": "sha512-AJDdYOdnyRDV5b6ArilzCPPwc1ejkHcoyFarqlPqT7zRYjhavcT3uSrqcMvsgh2CgoPbK3RCwyHaVyxYcP2Arg==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-interpolate": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-3.0.1.tgz", + "integrity": "sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==", + "license": "ISC", + "dependencies": { + "d3-color": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-path": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-3.1.0.tgz", + "integrity": "sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-scale": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-4.0.2.tgz", + "integrity": "sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==", + "license": "ISC", + "dependencies": { + "d3-array": "2.10.0 - 3", + "d3-format": "1 - 3", + "d3-interpolate": "1.2.0 - 3", + "d3-time": "2.1.1 - 3", + "d3-time-format": "2 - 4" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-shape": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-3.2.0.tgz", + "integrity": "sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==", + "license": "ISC", + "dependencies": { + "d3-path": "^3.1.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-time": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-time/-/d3-time-3.1.0.tgz", + "integrity": "sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q==", + "license": "ISC", + "dependencies": { + "d3-array": "2 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-time-format": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-4.1.0.tgz", + "integrity": "sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==", + "license": "ISC", + "dependencies": { + "d3-time": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-timer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-timer/-/d3-timer-3.0.1.tgz", + "integrity": "sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "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/date-fns": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-3.6.0.tgz", + "integrity": "sha512-fRHTG8g/Gif+kSh50gaGEdToemgfj74aRX3swtiouboip5JDLAyDE9F11nHMIcvOaXeOC6D7SpNhi7uFyB7Uww==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/kossnocorp" + } + }, + "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/decimal.js-light": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/decimal.js-light/-/decimal.js-light-2.5.1.tgz", + "integrity": "sha512-qIMFpTMZmny+MMIitAB6D7iVPEorVw6YQRWkvarTkT4tBeSLLiHzcwj6q0MmYSFCiVpiqPJTJEYIrpcPzVEIvg==", + "license": "MIT" + }, + "node_modules/deep-eql": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-4.1.4.tgz", + "integrity": "sha512-SUwdGfqdKOwxCPeVYjwSyRpJ7Z+fhpwIAtmCUdZIWZ/YP5R9WAsyuSgpLVDi9bjWoN2LXHNss/dk3urXtdQxGg==", + "dev": true, + "license": "MIT", + "dependencies": { + "type-detect": "^4.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "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/dequal": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", + "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "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/didyoumean": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz", + "integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==", + "license": "Apache-2.0" + }, + "node_modules/diff-sequences": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.6.3.tgz", + "integrity": "sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/dlv": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz", + "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==", + "license": "MIT" + }, + "node_modules/doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/dom-accessibility-api": { + "version": "0.5.16", + "resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.5.16.tgz", + "integrity": "sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==", + "dev": true, + "license": "MIT" + }, + "node_modules/dom-helpers": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.2.1.tgz", + "integrity": "sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.8.7", + "csstype": "^3.0.2" + } + }, + "node_modules/dompurify": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.2.7.tgz", + "integrity": "sha512-WhL/YuveyGXJaerVlMYGWhvQswa7myDG17P7Vu65EWC05o8vfeNbvNf4d/BOvH99+ZW+LlQsc1GDKMa1vNK6dw==", + "license": "(MPL-2.0 OR Apache-2.0)", + "optionalDependencies": { + "@types/trusted-types": "^2.0.7" + } + }, + "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/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "dev": true, + "license": "MIT" + }, + "node_modules/electron-to-chromium": { + "version": "1.5.286", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.286.tgz", + "integrity": "sha512-9tfDXhJ4RKFNerfjdCcZfufu49vg620741MNs26a9+bhLThdB+plgMeou98CAaHu/WATj2iHOOHTp1hWtABj2A==", + "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/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/esbuild": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz", + "integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=12" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.21.5", + "@esbuild/android-arm": "0.21.5", + "@esbuild/android-arm64": "0.21.5", + "@esbuild/android-x64": "0.21.5", + "@esbuild/darwin-arm64": "0.21.5", + "@esbuild/darwin-x64": "0.21.5", + "@esbuild/freebsd-arm64": "0.21.5", + "@esbuild/freebsd-x64": "0.21.5", + "@esbuild/linux-arm": "0.21.5", + "@esbuild/linux-arm64": "0.21.5", + "@esbuild/linux-ia32": "0.21.5", + "@esbuild/linux-loong64": "0.21.5", + "@esbuild/linux-mips64el": "0.21.5", + "@esbuild/linux-ppc64": "0.21.5", + "@esbuild/linux-riscv64": "0.21.5", + "@esbuild/linux-s390x": "0.21.5", + "@esbuild/linux-x64": "0.21.5", + "@esbuild/netbsd-x64": "0.21.5", + "@esbuild/openbsd-x64": "0.21.5", + "@esbuild/sunos-x64": "0.21.5", + "@esbuild/win32-arm64": "0.21.5", + "@esbuild/win32-ia32": "0.21.5", + "@esbuild/win32-x64": "0.21.5" + } + }, + "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": "8.57.1", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.1.tgz", + "integrity": "sha512-ypowyDxpVSYpkXr9WPv2PAZCtNip1Mv5KTW0SCurXv/9iOpcrH9PaqUElksqEB6pChqHGDRCFTyrZlGhnLNGiA==", + "deprecated": "This version is no longer supported. Please see https://eslint.org/version-support for other options.", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.6.1", + "@eslint/eslintrc": "^2.1.4", + "@eslint/js": "8.57.1", + "@humanwhocodes/config-array": "^0.13.0", + "@humanwhocodes/module-importer": "^1.0.1", + "@nodelib/fs.walk": "^1.2.8", + "@ungap/structured-clone": "^1.2.0", + "ajv": "^6.12.4", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "debug": "^4.3.2", + "doctrine": "^3.0.0", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^7.2.2", + "eslint-visitor-keys": "^3.4.3", + "espree": "^9.6.1", + "esquery": "^1.4.2", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^6.0.1", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "globals": "^13.19.0", + "graphemer": "^1.4.0", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "is-path-inside": "^3.0.3", + "js-yaml": "^4.1.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.4.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3", + "strip-ansi": "^6.0.1", + "text-table": "^0.2.0" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-config-next": { + "version": "14.2.35", + "resolved": "https://registry.npmjs.org/eslint-config-next/-/eslint-config-next-14.2.35.tgz", + "integrity": "sha512-BpLsv01UisH193WyT/1lpHqq5iJ/Orfz9h/NOOlAmTUq4GY349PextQ62K4XpnaM9supeiEn3TaOTeQO07gURg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@next/eslint-plugin-next": "14.2.35", + "@rushstack/eslint-patch": "^1.3.3", + "@typescript-eslint/eslint-plugin": "^5.4.2 || ^6.0.0 || ^7.0.0 || ^8.0.0", + "@typescript-eslint/parser": "^5.4.2 || ^6.0.0 || ^7.0.0 || ^8.0.0", + "eslint-import-resolver-node": "^0.3.6", + "eslint-import-resolver-typescript": "^3.5.2", + "eslint-plugin-import": "^2.28.1", + "eslint-plugin-jsx-a11y": "^6.7.1", + "eslint-plugin-react": "^7.33.2", + "eslint-plugin-react-hooks": "^4.5.0 || 5.0.0-canary-7118f5dd7-20230705" + }, + "peerDependencies": { + "eslint": "^7.23.0 || ^8.0.0", + "typescript": ">=3.3.1" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "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", + "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/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/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-jsx-a11y/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/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": "5.0.0-canary-7118f5dd7-20230705", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-5.0.0-canary-7118f5dd7-20230705.tgz", + "integrity": "sha512-AZYbMo/NW9chdL7vk6HQzQhT+PvTAEVqWk9ziruUoW2kAOcN5qNyelv70e0F1VNQAbvutOC9oc+xfWycI9FxDw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0" + } + }, + "node_modules/eslint-plugin-react/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/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": "7.2.2", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", + "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "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/espree": { + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", + "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "acorn": "^8.9.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^3.4.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esquery": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.7.0.tgz", + "integrity": "sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==", + "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/estree-walker": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", + "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.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/eventemitter3": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", + "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==", + "license": "MIT" + }, + "node_modules/execa": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-8.0.1.tgz", + "integrity": "sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==", + "dev": true, + "license": "MIT", + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^8.0.1", + "human-signals": "^5.0.0", + "is-stream": "^3.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^5.1.0", + "onetime": "^6.0.0", + "signal-exit": "^4.1.0", + "strip-final-newline": "^3.0.0" + }, + "engines": { + "node": ">=16.17" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "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-equals": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/fast-equals/-/fast-equals-5.4.0.tgz", + "integrity": "sha512-jt2DW/aNFNwke7AUd+Z+e6pz39KO5rzdbbFCg2sGafS4mk13MI7Z8O5z9cADNn5lhGODIgLwug6TZO2ctf7kcw==", + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/fast-glob": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", + "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", + "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.8" + }, + "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==", + "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.20.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.20.1.tgz", + "integrity": "sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==", + "license": "ISC", + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/file-entry-cache": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", + "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", + "dev": true, + "license": "MIT", + "dependencies": { + "flat-cache": "^3.0.4" + }, + "engines": { + "node": "^10.12.0 || >=12.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==", + "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": "3.2.0", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz", + "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==", + "dev": true, + "license": "MIT", + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.3", + "rimraf": "^3.0.2" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "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/foreground-child": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", + "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", + "dev": true, + "license": "ISC", + "dependencies": { + "cross-spawn": "^7.0.6", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "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/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true, + "license": "ISC" + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "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==", + "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/get-func-name": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.2.tgz", + "integrity": "sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "*" + } + }, + "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-stream": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-8.0.1.tgz", + "integrity": "sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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.6", + "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.13.6.tgz", + "integrity": "sha512-shZT/QMiSHc/YBLxxOkMtgSid5HFoauqCE3/exfsEcwg1WkeqjG+V40yBbBrsD+jW2HDXcs28xOfcbm2jI8Ddw==", + "dev": true, + "license": "MIT", + "dependencies": { + "resolve-pkg-maps": "^1.0.0" + }, + "funding": { + "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" + } + }, + "node_modules/glob": { + "version": "10.3.10", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.10.tgz", + "integrity": "sha512-fa46+tv1Ak0UPK1TOy/pZrIybNNt4HCv7SDzwyfiOZkvZLEbjsZkJBPtDHVshZjbecAoAGSC20MjLDG/qr679g==", + "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", + "dev": true, + "license": "ISC", + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^2.3.5", + "minimatch": "^9.0.1", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0", + "path-scurry": "^1.10.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "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==", + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/glob/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/glob/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/globals": { + "version": "13.24.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", + "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "type-fest": "^0.20.2" + }, + "engines": { + "node": ">=8" + }, + "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==", + "license": "ISC" + }, + "node_modules/graphemer": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", + "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", + "dev": true, + "license": "MIT" + }, + "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==", + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/human-signals": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-5.0.0.tgz", + "integrity": "sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=16.17.0" + } + }, + "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/immer": { + "version": "11.1.3", + "resolved": "https://registry.npmjs.org/immer/-/immer-11.1.3.tgz", + "integrity": "sha512-6jQTc5z0KJFtr1UgFpIL3N9XSC3saRaI9PwWtzM2pSqkNGtiNkYY2OSwkOGDK2XcTRcLb1pi/aNkKZz0nxVH4Q==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/immer" + } + }, + "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/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", + "dev": true, + "license": "ISC", + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true, + "license": "ISC" + }, + "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/internmap": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/internmap/-/internmap-2.0.3.tgz", + "integrity": "sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "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-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "license": "MIT", + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "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==", + "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==", + "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-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "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==", + "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==", + "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-path-inside": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", + "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "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-stream": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz", + "integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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/jackspeak": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-2.3.6.tgz", + "integrity": "sha512-N3yCS/NegsOBokc8GAdM8UcmfsKiSS8cipheD/nivzr700H+nsMOxJjQnvwOcRYVuFkdH0wGUvW2WbXGmrZGbQ==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, + "node_modules/jiti": { + "version": "1.21.7", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.7.tgz", + "integrity": "sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A==", + "license": "MIT", + "peer": true, + "bin": { + "jiti": "bin/jiti.js" + } + }, + "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==", + "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/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": "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/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/lilconfig": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz", + "integrity": "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==", + "license": "MIT", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/antonk52" + } + }, + "node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "license": "MIT" + }, + "node_modules/local-pkg": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/local-pkg/-/local-pkg-0.5.1.tgz", + "integrity": "sha512-9rrA30MRRP3gBD3HTGnC6cDFpaE1kVDWxWgqWJUN0RvDNAo+Nz/9GxB+nHOH0ifbVFy0hSA1V6vFDvnx54lTEQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "mlly": "^1.7.3", + "pkg-types": "^1.2.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "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": { + "version": "4.17.23", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.23.tgz", + "integrity": "sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w==", + "license": "MIT" + }, + "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==", + "license": "MIT", + "dependencies": { + "js-tokens": "^3.0.0 || ^4.0.0" + }, + "bin": { + "loose-envify": "cli.js" + } + }, + "node_modules/loupe": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/loupe/-/loupe-2.3.7.tgz", + "integrity": "sha512-zSMINGVYkdpYSOBmLi0D1Uo7JU9nVdQKrHxC8eYlV+9YKK9WePqAlL7lSlorG/U2Fw1w0hTBmaa/jrQ3UbPHtA==", + "dev": true, + "license": "MIT", + "dependencies": { + "get-func-name": "^2.0.1" + } + }, + "node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/lucide-react": { + "version": "0.378.0", + "resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.378.0.tgz", + "integrity": "sha512-u6EPU8juLUk9ytRcyapkWI18epAv3RU+6+TC23ivjR0e+glWKBobFeSgRwOIJihzktILQuy6E0E80P2jVTDR5g==", + "license": "ISC", + "peerDependencies": { + "react": "^16.5.1 || ^17.0.0 || ^18.0.0" + } + }, + "node_modules/lz-string": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/lz-string/-/lz-string-1.5.0.tgz", + "integrity": "sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==", + "dev": true, + "license": "MIT", + "bin": { + "lz-string": "bin/bin.js" + } + }, + "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/marked": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/marked/-/marked-14.0.0.tgz", + "integrity": "sha512-uIj4+faQ+MgHgwUW1l2PsPglZLOLOT1uErt06dAPtx2kjteLAkbsd/0FiYg/MGS+i7ZKLb7w2WClxHkzOOuryQ==", + "license": "MIT", + "bin": { + "marked": "bin/marked.js" + }, + "engines": { + "node": ">= 18" + } + }, + "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/merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true, + "license": "MIT" + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "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==", + "license": "MIT", + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/mimic-fn": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz", + "integrity": "sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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/minipass": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/mlly": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/mlly/-/mlly-1.8.0.tgz", + "integrity": "sha512-l8D9ODSRWLe2KHJSifWGwBqpTZXIXTeo8mlKjY+E2HAakaTeNpqAyBZ8GSqLzHgw4XmHmC8whvpjJNMbFZN7/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "acorn": "^8.15.0", + "pathe": "^2.0.3", + "pkg-types": "^1.3.1", + "ufo": "^1.6.1" + } + }, + "node_modules/mlly/node_modules/pathe": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", + "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", + "dev": true, + "license": "MIT" + }, + "node_modules/monaco-editor": { + "version": "0.55.1", + "resolved": "https://registry.npmjs.org/monaco-editor/-/monaco-editor-0.55.1.tgz", + "integrity": "sha512-jz4x+TJNFHwHtwuV9vA9rMujcZRb0CEilTEwG2rRSpe/A7Jdkuj8xPKttCgOh+v/lkHy7HsZ64oj+q3xoAFl9A==", + "license": "MIT", + "peer": true, + "dependencies": { + "dompurify": "3.2.7", + "marked": "14.0.0" + } + }, + "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/mz": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", + "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==", + "license": "MIT", + "dependencies": { + "any-promise": "^1.0.0", + "object-assign": "^4.0.1", + "thenify-all": "^1.0.0" + } + }, + "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": "14.2.35", + "resolved": "https://registry.npmjs.org/next/-/next-14.2.35.tgz", + "integrity": "sha512-KhYd2Hjt/O1/1aZVX3dCwGXM1QmOV4eNM2UTacK5gipDdPN/oHHK/4oVGy7X8GMfPMsUTUEmGlsy0EY1YGAkig==", + "license": "MIT", + "dependencies": { + "@next/env": "14.2.35", + "@swc/helpers": "0.5.5", + "busboy": "1.6.0", + "caniuse-lite": "^1.0.30001579", + "graceful-fs": "^4.2.11", + "postcss": "8.4.31", + "styled-jsx": "5.1.1" + }, + "bin": { + "next": "dist/bin/next" + }, + "engines": { + "node": ">=18.17.0" + }, + "optionalDependencies": { + "@next/swc-darwin-arm64": "14.2.33", + "@next/swc-darwin-x64": "14.2.33", + "@next/swc-linux-arm64-gnu": "14.2.33", + "@next/swc-linux-arm64-musl": "14.2.33", + "@next/swc-linux-x64-gnu": "14.2.33", + "@next/swc-linux-x64-musl": "14.2.33", + "@next/swc-win32-arm64-msvc": "14.2.33", + "@next/swc-win32-ia32-msvc": "14.2.33", + "@next/swc-win32-x64-msvc": "14.2.33" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.1.0", + "@playwright/test": "^1.41.2", + "react": "^18.2.0", + "react-dom": "^18.2.0", + "sass": "^1.3.0" + }, + "peerDependenciesMeta": { + "@opentelemetry/api": { + "optional": true + }, + "@playwright/test": { + "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/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/npm-run-path": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.3.0.tgz", + "integrity": "sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^4.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/npm-run-path/node_modules/path-key": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz", + "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-hash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz", + "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==", + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "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/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/onetime": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-6.0.0.tgz", + "integrity": "sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "mimic-fn": "^4.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "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==", + "license": "MIT" + }, + "node_modules/path-scurry": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/pathe": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-1.1.2.tgz", + "integrity": "sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/pathval": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.1.tgz", + "integrity": "sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "*" + } + }, + "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==", + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/pirates": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.7.tgz", + "integrity": "sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==", + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/pkg-types": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-1.3.1.tgz", + "integrity": "sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "confbox": "^0.1.8", + "mlly": "^1.7.4", + "pathe": "^2.0.1" + } + }, + "node_modules/pkg-types/node_modules/pathe": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", + "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", + "dev": true, + "license": "MIT" + }, + "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==", + "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-import": { + "version": "15.1.0", + "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-15.1.0.tgz", + "integrity": "sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==", + "license": "MIT", + "dependencies": { + "postcss-value-parser": "^4.0.0", + "read-cache": "^1.0.0", + "resolve": "^1.1.7" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "postcss": "^8.0.0" + } + }, + "node_modules/postcss-js": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/postcss-js/-/postcss-js-4.1.0.tgz", + "integrity": "sha512-oIAOTqgIo7q2EOwbhb8UalYePMvYoIeRY2YKntdpFQXNosSu3vLrniGgmH9OKs/qAkfoj5oB3le/7mINW1LCfw==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "camelcase-css": "^2.0.1" + }, + "engines": { + "node": "^12 || ^14 || >= 16" + }, + "peerDependencies": { + "postcss": "^8.4.21" + } + }, + "node_modules/postcss-load-config": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-6.0.1.tgz", + "integrity": "sha512-oPtTM4oerL+UXmx+93ytZVN82RrlY/wPUV8IeDxFrzIjXOLF1pN+EmKPLbubvKHT2HC20xXsCAH2Z+CKV6Oz/g==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "lilconfig": "^3.1.1" + }, + "engines": { + "node": ">= 18" + }, + "peerDependencies": { + "jiti": ">=1.21.0", + "postcss": ">=8.0.9", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + }, + "postcss": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, + "node_modules/postcss-nested": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-6.2.0.tgz", + "integrity": "sha512-HQbt28KulC5AJzG+cZtj9kvKB93CFCdLvog1WFLf1D+xmMvPGlBstkpTEZfK5+AN9hfJocyBFCNiqyS48bpgzQ==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "postcss-selector-parser": "^6.1.1" + }, + "engines": { + "node": ">=12.0" + }, + "peerDependencies": { + "postcss": "^8.2.14" + } + }, + "node_modules/postcss-selector-parser": { + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz", + "integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==", + "license": "MIT", + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "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==", + "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/prettier": { + "version": "3.8.1", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.8.1.tgz", + "integrity": "sha512-UOnG6LftzbdaHZcKoPFtOcCKztrQ57WkHDeRD9t/PTQtmT0NHSeWWepj6pS0z/N7+08BHFDQVUrfmfMRcZwbMg==", + "dev": true, + "license": "MIT", + "bin": { + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, + "node_modules/pretty-format": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.5.1.tgz", + "integrity": "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1", + "ansi-styles": "^5.0.0", + "react-is": "^17.0.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/pretty-format/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "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==", + "license": "MIT", + "dependencies": { + "loose-envify": "^1.4.0", + "object-assign": "^4.1.1", + "react-is": "^16.13.1" + } + }, + "node_modules/prop-types/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==", + "license": "MIT" + }, + "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==", + "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": "18.3.1", + "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", + "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==", + "license": "MIT", + "peer": true, + "dependencies": { + "loose-envify": "^1.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-dom": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz", + "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==", + "license": "MIT", + "peer": true, + "dependencies": { + "loose-envify": "^1.1.0", + "scheduler": "^0.23.2" + }, + "peerDependencies": { + "react": "^18.3.1" + } + }, + "node_modules/react-hook-form": { + "version": "7.71.1", + "resolved": "https://registry.npmjs.org/react-hook-form/-/react-hook-form-7.71.1.tgz", + "integrity": "sha512-9SUJKCGKo8HUSsCO+y0CtqkqI5nNuaDqTxyqPsZPqIwudpj4rCrAz/jZV+jn57bx5gtZKOh3neQu94DXMc+w5w==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/react-hook-form" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17 || ^18 || ^19" + } + }, + "node_modules/react-is": { + "version": "17.0.2", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", + "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==", + "dev": true, + "license": "MIT" + }, + "node_modules/react-redux": { + "version": "9.2.0", + "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-9.2.0.tgz", + "integrity": "sha512-ROY9fvHhwOD9ySfrF0wmvu//bKCQ6AeZZq1nJNtbDC+kk5DuSuNX/n6YWYF/SYy7bSba4D4FSz8DJeKY/S/r+g==", + "license": "MIT", + "peer": true, + "dependencies": { + "@types/use-sync-external-store": "^0.0.6", + "use-sync-external-store": "^1.4.0" + }, + "peerDependencies": { + "@types/react": "^18.2.25 || ^19", + "react": "^18.0 || ^19", + "redux": "^5.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "redux": { + "optional": true + } + } + }, + "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-smooth": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/react-smooth/-/react-smooth-4.0.4.tgz", + "integrity": "sha512-gnGKTpYwqL0Iii09gHobNolvX4Kiq4PKx6eWBCYYix+8cdw+cGo3do906l1NBPKkSWx1DghC1dlWG9L2uGd61Q==", + "license": "MIT", + "dependencies": { + "fast-equals": "^5.0.1", + "prop-types": "^15.8.1", + "react-transition-group": "^4.4.5" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", + "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, + "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/react-transition-group": { + "version": "4.4.5", + "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz", + "integrity": "sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==", + "license": "BSD-3-Clause", + "dependencies": { + "@babel/runtime": "^7.5.5", + "dom-helpers": "^5.0.1", + "loose-envify": "^1.4.0", + "prop-types": "^15.6.2" + }, + "peerDependencies": { + "react": ">=16.6.0", + "react-dom": ">=16.6.0" + } + }, + "node_modules/read-cache": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", + "integrity": "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==", + "license": "MIT", + "dependencies": { + "pify": "^2.3.0" + } + }, + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "license": "MIT", + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/recharts": { + "version": "2.15.4", + "resolved": "https://registry.npmjs.org/recharts/-/recharts-2.15.4.tgz", + "integrity": "sha512-UT/q6fwS3c1dHbXv2uFgYJ9BMFHu3fwnd7AYZaEQhXuYQ4hgsxLvsUXzGdKeZrW5xopzDCvuA2N41WJ88I7zIw==", + "license": "MIT", + "dependencies": { + "clsx": "^2.0.0", + "eventemitter3": "^4.0.1", + "lodash": "^4.17.21", + "react-is": "^18.3.1", + "react-smooth": "^4.0.4", + "recharts-scale": "^0.4.4", + "tiny-invariant": "^1.3.1", + "victory-vendor": "^36.6.8" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "react": "^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", + "react-dom": "^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, + "node_modules/recharts-scale": { + "version": "0.4.5", + "resolved": "https://registry.npmjs.org/recharts-scale/-/recharts-scale-0.4.5.tgz", + "integrity": "sha512-kivNFO+0OcUNu7jQquLXAxz1FIwZj8nrj+YkOKc5694NbjCvcT6aSZiIzNzd2Kul4o4rTto8QVR9lMNtxD4G1w==", + "license": "MIT", + "dependencies": { + "decimal.js-light": "^2.4.1" + } + }, + "node_modules/recharts/node_modules/react-is": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", + "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", + "license": "MIT" + }, + "node_modules/redux": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/redux/-/redux-5.0.1.tgz", + "integrity": "sha512-M9/ELqF6fy8FwmkpnF0S3YKOqMyoWJ4+CS5Efg2ct3oY9daQvd/Pc71FpGZsVsbl3Cpb+IIcjBDUnnyBdQbq4w==", + "license": "MIT", + "peer": true + }, + "node_modules/redux-thunk": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/redux-thunk/-/redux-thunk-3.1.0.tgz", + "integrity": "sha512-NW2r5T6ksUKXCabzhL9z+h206HQw/NJkcLm1GPImRQ8IzfXwRGqjVhKJGauHirT0DAuyy6hjdnMZaRoAcy0Klw==", + "license": "MIT", + "peerDependencies": { + "redux": "^5.0.0" + } + }, + "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/reselect": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/reselect/-/reselect-5.1.1.tgz", + "integrity": "sha512-K/BG6eIky/SBpzfHZv/dd+9JBFiS4SWV7FIujVyJRux6e45+73RaUHXLmIR1f7WOMaQ0U1km6qwklRQxpJJY0w==", + "license": "MIT" + }, + "node_modules/resolve": { + "version": "1.22.11", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.11.tgz", + "integrity": "sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ==", + "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==", + "license": "MIT", + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "deprecated": "Rimraf versions prior to v4 are no longer supported", + "dev": true, + "license": "ISC", + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/rimraf/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", + "dev": true, + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/rollup": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.57.1.tgz", + "integrity": "sha512-oQL6lgK3e2QZeQ7gcgIkS2YZPg5slw37hYufJ3edKlfQSGGm8ICoxswK15ntSzF/a8+h7ekRy7k7oWc3BQ7y8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "1.0.8" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.57.1", + "@rollup/rollup-android-arm64": "4.57.1", + "@rollup/rollup-darwin-arm64": "4.57.1", + "@rollup/rollup-darwin-x64": "4.57.1", + "@rollup/rollup-freebsd-arm64": "4.57.1", + "@rollup/rollup-freebsd-x64": "4.57.1", + "@rollup/rollup-linux-arm-gnueabihf": "4.57.1", + "@rollup/rollup-linux-arm-musleabihf": "4.57.1", + "@rollup/rollup-linux-arm64-gnu": "4.57.1", + "@rollup/rollup-linux-arm64-musl": "4.57.1", + "@rollup/rollup-linux-loong64-gnu": "4.57.1", + "@rollup/rollup-linux-loong64-musl": "4.57.1", + "@rollup/rollup-linux-ppc64-gnu": "4.57.1", + "@rollup/rollup-linux-ppc64-musl": "4.57.1", + "@rollup/rollup-linux-riscv64-gnu": "4.57.1", + "@rollup/rollup-linux-riscv64-musl": "4.57.1", + "@rollup/rollup-linux-s390x-gnu": "4.57.1", + "@rollup/rollup-linux-x64-gnu": "4.57.1", + "@rollup/rollup-linux-x64-musl": "4.57.1", + "@rollup/rollup-openbsd-x64": "4.57.1", + "@rollup/rollup-openharmony-arm64": "4.57.1", + "@rollup/rollup-win32-arm64-msvc": "4.57.1", + "@rollup/rollup-win32-ia32-msvc": "4.57.1", + "@rollup/rollup-win32-x64-gnu": "4.57.1", + "@rollup/rollup-win32-x64-msvc": "4.57.1", + "fsevents": "~2.3.2" + } + }, + "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==", + "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.23.2", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz", + "integrity": "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==", + "license": "MIT", + "dependencies": { + "loose-envify": "^1.1.0" + } + }, + "node_modules/semver": { + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", + "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", + "dev": 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/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/siginfo": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz", + "integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==", + "dev": true, + "license": "ISC" + }, + "node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/sonner": { + "version": "1.7.4", + "resolved": "https://registry.npmjs.org/sonner/-/sonner-1.7.4.tgz", + "integrity": "sha512-DIS8z4PfJRbIyfVFDVnK9rO3eYDtse4Omcm6bt0oEr5/jtLgysmjuBl1frJ9E/EQZrFmKx2A8m/s5s9CRXIzhw==", + "license": "MIT", + "peerDependencies": { + "react": "^18.0.0 || ^19.0.0 || ^19.0.0-rc", + "react-dom": "^18.0.0 || ^19.0.0 || ^19.0.0-rc" + } + }, + "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/stackback": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz", + "integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==", + "dev": true, + "license": "MIT" + }, + "node_modules/state-local": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/state-local/-/state-local-1.0.7.tgz", + "integrity": "sha512-HTEHMNieakEnoe33shBYcZ7NX83ACUjCu8c40iOGEZsngj9zRnkqS9j1pqQPXwobB0ZcVTk27REb7COQ0UR59w==", + "license": "MIT" + }, + "node_modules/std-env": { + "version": "3.10.0", + "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.10.0.tgz", + "integrity": "sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg==", + "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/streamsearch": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz", + "integrity": "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==", + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/string-width-cjs": { + "name": "string-width", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/string-width/node_modules/ansi-regex": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", + "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/string-width/node_modules/strip-ansi": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.2.tgz", + "integrity": "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "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-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi-cjs": { + "name": "strip-ansi", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "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-final-newline": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-3.0.0.tgz", + "integrity": "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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/strip-literal": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/strip-literal/-/strip-literal-2.1.1.tgz", + "integrity": "sha512-631UJ6O00eNGfMiWG78ck80dfBab8X6IVFB51jZK5Icd7XAs60Z5y7QdSd/wGIklnWvRbUNloVzhOKKmutxQ6Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "js-tokens": "^9.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/strip-literal/node_modules/js-tokens": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-9.0.1.tgz", + "integrity": "sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/styled-jsx": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/styled-jsx/-/styled-jsx-5.1.1.tgz", + "integrity": "sha512-pW7uC1l4mBZ8ugbiZrcIsiIvVx1UmTfw7UkC3Um2tmfUq9Bhk8IiyEIPl6F8agHgjzku6j0xQEZbfA5uSgSaCw==", + "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" + }, + "peerDependenciesMeta": { + "@babel/core": { + "optional": true + }, + "babel-plugin-macros": { + "optional": true + } + } + }, + "node_modules/sucrase": { + "version": "3.35.1", + "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.35.1.tgz", + "integrity": "sha512-DhuTmvZWux4H1UOnWMB3sk0sbaCVOoQZjv8u1rDoTV0HTdGem9hkAZtl4JZy8P2z4Bg0nT+YMeOFyVr4zcG5Tw==", + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.2", + "commander": "^4.0.0", + "lines-and-columns": "^1.1.6", + "mz": "^2.7.0", + "pirates": "^4.0.1", + "tinyglobby": "^0.2.11", + "ts-interface-checker": "^0.1.9" + }, + "bin": { + "sucrase": "bin/sucrase", + "sucrase-node": "bin/sucrase-node" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "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==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/tailwind-merge": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-2.6.1.tgz", + "integrity": "sha512-Oo6tHdpZsGpkKG88HJ8RR1rg/RdnEkQEfMoEk2x1XRI3F1AxeU+ijRXpiVUF4UbLfcxxRGw6TbUINKYdWVsQTQ==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/dcastil" + } + }, + "node_modules/tailwindcss": { + "version": "3.4.19", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.19.tgz", + "integrity": "sha512-3ofp+LL8E+pK/JuPLPggVAIaEuhvIz4qNcf3nA1Xn2o/7fb7s/TYpHhwGDv1ZU3PkBluUVaF8PyCHcm48cKLWQ==", + "license": "MIT", + "dependencies": { + "@alloc/quick-lru": "^5.2.0", + "arg": "^5.0.2", + "chokidar": "^3.6.0", + "didyoumean": "^1.2.2", + "dlv": "^1.1.3", + "fast-glob": "^3.3.2", + "glob-parent": "^6.0.2", + "is-glob": "^4.0.3", + "jiti": "^1.21.7", + "lilconfig": "^3.1.3", + "micromatch": "^4.0.8", + "normalize-path": "^3.0.0", + "object-hash": "^3.0.0", + "picocolors": "^1.1.1", + "postcss": "^8.4.47", + "postcss-import": "^15.1.0", + "postcss-js": "^4.0.1", + "postcss-load-config": "^4.0.2 || ^5.0 || ^6.0", + "postcss-nested": "^6.2.0", + "postcss-selector-parser": "^6.1.2", + "resolve": "^1.22.8", + "sucrase": "^3.35.0" + }, + "bin": { + "tailwind": "lib/cli.js", + "tailwindcss": "lib/cli.js" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", + "dev": true, + "license": "MIT" + }, + "node_modules/thenify": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz", + "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==", + "license": "MIT", + "dependencies": { + "any-promise": "^1.0.0" + } + }, + "node_modules/thenify-all": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz", + "integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==", + "license": "MIT", + "dependencies": { + "thenify": ">= 3.1.0 < 4" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/tiny-invariant": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.3.3.tgz", + "integrity": "sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==", + "license": "MIT" + }, + "node_modules/tinybench": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz", + "integrity": "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==", + "dev": true, + "license": "MIT" + }, + "node_modules/tinyglobby": { + "version": "0.2.15", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", + "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==", + "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==", + "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==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/tinypool": { + "version": "0.8.4", + "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-0.8.4.tgz", + "integrity": "sha512-i11VH5gS6IFeLY3gMBQ00/MmLncVP7JLXOw1vlgkytLmJK7QnEr7NXf0LBdxfmNPAeyetukOk0bOYrJrFGjYJQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/tinyspy": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/tinyspy/-/tinyspy-2.2.1.tgz", + "integrity": "sha512-KYad6Vy5VDWV4GH3fjpseMQ/XU2BhIYP7Vzd0LG44qRWm/Yt2WCOTicFdvmgo6gWaqooMQCawTtILVQJupKu7A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, + "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==", + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/ts-api-utils": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.4.0.tgz", + "integrity": "sha512-3TaVTaAv2gTiMB35i3FiGJaRfwb3Pyn/j3m/bfAvGe8FB7CF6u+LMYqYlDh7reQf7UNvoTvdfAqHGmPGOSsPmA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18.12" + }, + "peerDependencies": { + "typescript": ">=4.8.4" + } + }, + "node_modules/ts-interface-checker": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz", + "integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==", + "license": "Apache-2.0" + }, + "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/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/type-detect": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.1.0.tgz", + "integrity": "sha512-Acylog8/luQ8L7il+geoSxhEkazvkslg7PSNKOX59mbB9cOveP5aq9h74Y7YU8yDpJwetzQQrfIwtf4Wp4LKcw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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/ufo": { + "version": "1.6.3", + "resolved": "https://registry.npmjs.org/ufo/-/ufo-1.6.3.tgz", + "integrity": "sha512-yDJTmhydvl5lJzBmy/hyOAA0d+aqCBuwl818haVdYCRrWV84o7YyeVm4QlVHStqNrrJSTb6jKuFAVqAFsr+K3Q==", + "dev": true, + "license": "MIT" + }, + "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": "6.21.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", + "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", + "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/use-sync-external-store": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.6.0.tgz", + "integrity": "sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w==", + "license": "MIT", + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "license": "MIT" + }, + "node_modules/victory-vendor": { + "version": "36.9.2", + "resolved": "https://registry.npmjs.org/victory-vendor/-/victory-vendor-36.9.2.tgz", + "integrity": "sha512-PnpQQMuxlwYdocC8fIJqVXvkeViHYzotI+NJrCuav0ZYFoq912ZHBk3mCeuj+5/VpodOjPe1z0Fk2ihgzlXqjQ==", + "license": "MIT AND ISC", + "dependencies": { + "@types/d3-array": "^3.0.3", + "@types/d3-ease": "^3.0.0", + "@types/d3-interpolate": "^3.0.1", + "@types/d3-scale": "^4.0.2", + "@types/d3-shape": "^3.1.0", + "@types/d3-time": "^3.0.0", + "@types/d3-timer": "^3.0.0", + "d3-array": "^3.1.6", + "d3-ease": "^3.0.1", + "d3-interpolate": "^3.0.1", + "d3-scale": "^4.0.2", + "d3-shape": "^3.1.0", + "d3-time": "^3.0.0", + "d3-timer": "^3.0.1" + } + }, + "node_modules/vite": { + "version": "5.4.21", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.21.tgz", + "integrity": "sha512-o5a9xKjbtuhY6Bi5S3+HvbRERmouabWbyUcpXXUA1u+GNUKoROi9byOJ8M0nHbHYHkYICiMlqxkg1KkYmm25Sw==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "^0.21.3", + "postcss": "^8.4.43", + "rollup": "^4.20.0" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^18.0.0 || >=20.0.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^18.0.0 || >=20.0.0", + "less": "*", + "lightningcss": "^1.21.0", + "sass": "*", + "sass-embedded": "*", + "stylus": "*", + "sugarss": "*", + "terser": "^5.4.0" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + } + } + }, + "node_modules/vite-node": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-1.6.1.tgz", + "integrity": "sha512-YAXkfvGtuTzwWbDSACdJSg4A4DZiAqckWe90Zapc/sEX3XvHcw1NdurM/6od8J207tSDqNbSsgdCacBgvJKFuA==", + "dev": true, + "license": "MIT", + "dependencies": { + "cac": "^6.7.14", + "debug": "^4.3.4", + "pathe": "^1.1.1", + "picocolors": "^1.0.0", + "vite": "^5.0.0" + }, + "bin": { + "vite-node": "vite-node.mjs" + }, + "engines": { + "node": "^18.0.0 || >=20.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/vitest": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-1.6.1.tgz", + "integrity": "sha512-Ljb1cnSJSivGN0LqXd/zmDbWEM0RNNg2t1QW/XUhYl/qPqyu7CsqeWtqQXHVaJsecLPuDoak2oJcZN2QoRIOag==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/expect": "1.6.1", + "@vitest/runner": "1.6.1", + "@vitest/snapshot": "1.6.1", + "@vitest/spy": "1.6.1", + "@vitest/utils": "1.6.1", + "acorn-walk": "^8.3.2", + "chai": "^4.3.10", + "debug": "^4.3.4", + "execa": "^8.0.1", + "local-pkg": "^0.5.0", + "magic-string": "^0.30.5", + "pathe": "^1.1.1", + "picocolors": "^1.0.0", + "std-env": "^3.5.0", + "strip-literal": "^2.0.0", + "tinybench": "^2.5.1", + "tinypool": "^0.8.3", + "vite": "^5.0.0", + "vite-node": "1.6.1", + "why-is-node-running": "^2.2.2" + }, + "bin": { + "vitest": "vitest.mjs" + }, + "engines": { + "node": "^18.0.0 || >=20.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "@edge-runtime/vm": "*", + "@types/node": "^18.0.0 || >=20.0.0", + "@vitest/browser": "1.6.1", + "@vitest/ui": "1.6.1", + "happy-dom": "*", + "jsdom": "*" + }, + "peerDependenciesMeta": { + "@edge-runtime/vm": { + "optional": true + }, + "@types/node": { + "optional": true + }, + "@vitest/browser": { + "optional": true + }, + "@vitest/ui": { + "optional": true + }, + "happy-dom": { + "optional": true + }, + "jsdom": { + "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.20", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.20.tgz", + "integrity": "sha512-LYfpUkmqwl0h9A2HL09Mms427Q1RZWuOHsukfVcKRq9q95iQxdw0ix1JQrqbcDR9PH1QDwf5Qo8OZb5lksZ8Xg==", + "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/why-is-node-running": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.3.0.tgz", + "integrity": "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==", + "dev": true, + "license": "MIT", + "dependencies": { + "siginfo": "^2.0.0", + "stackback": "0.0.2" + }, + "bin": { + "why-is-node-running": "cli.js" + }, + "engines": { + "node": ">=8" + } + }, + "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/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs": { + "name": "wrap-ansi", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/wrap-ansi-cjs/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi/node_modules/ansi-regex": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", + "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/wrap-ansi/node_modules/ansi-styles": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", + "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/wrap-ansi/node_modules/strip-ansi": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.2.tgz", + "integrity": "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "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": "3.25.76", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", + "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, + "node_modules/zustand": { + "version": "4.5.7", + "resolved": "https://registry.npmjs.org/zustand/-/zustand-4.5.7.tgz", + "integrity": "sha512-CHOUy7mu3lbD6o6LJLfllpjkzhHXSBlX8B9+qPddUsIfeF5S/UZ5q0kmCsnRqT1UHFQZchNFDDzMbQsuesHWlw==", + "license": "MIT", + "dependencies": { + "use-sync-external-store": "^1.2.2" + }, + "engines": { + "node": ">=12.7.0" + }, + "peerDependencies": { + "@types/react": ">=16.8", + "immer": ">=9.0.6", + "react": ">=16.8" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "immer": { + "optional": true + }, + "react": { + "optional": true + } + } + } + } +} diff --git a/it0-web-admin/package.json b/it0-web-admin/package.json new file mode 100644 index 0000000..f89099b --- /dev/null +++ b/it0-web-admin/package.json @@ -0,0 +1,62 @@ +{ + "name": "it0-web-admin", + "version": "0.1.0", + "private": true, + "scripts": { + "dev": "next dev", + "build": "next build", + "start": "next start", + "lint": "next lint", + "test": "vitest", + "test:e2e": "playwright test" + }, + "dependencies": { + "next": "^14.2.0", + "react": "^18.3.0", + "react-dom": "^18.3.0", + + "@reduxjs/toolkit": "^2.2.0", + "react-redux": "^9.1.0", + "zustand": "^4.5.0", + "@tanstack/react-query": "^5.45.0", + "@tanstack/react-table": "^8.17.0", + + "tailwindcss": "^3.4.0", + "class-variance-authority": "^0.7.0", + "clsx": "^2.1.0", + "tailwind-merge": "^2.3.0", + "@radix-ui/react-dialog": "^1.0.0", + "@radix-ui/react-dropdown-menu": "^2.0.0", + "@radix-ui/react-select": "^2.0.0", + "@radix-ui/react-tabs": "^1.0.0", + "@radix-ui/react-toast": "^1.1.0", + "@radix-ui/react-tooltip": "^1.0.0", + "lucide-react": "^0.378.0", + + "react-hook-form": "^7.51.0", + "zod": "^3.23.0", + "@hookform/resolvers": "^3.3.0", + + "@monaco-editor/react": "^4.6.0", + "@xterm/xterm": "^5.5.0", + "@xterm/addon-fit": "^0.10.0", + "@xterm/addon-web-links": "^0.11.0", + + "recharts": "^2.12.0", + "date-fns": "^3.6.0", + "sonner": "^1.4.0" + }, + "devDependencies": { + "@types/node": "^20.0.0", + "@types/react": "^18.3.0", + "@types/react-dom": "^18.3.0", + "typescript": "^5.4.0", + "eslint": "^8.57.0", + "eslint-config-next": "^14.2.0", + "prettier": "^3.2.0", + "vitest": "^1.6.0", + "@testing-library/react": "^15.0.0", + "autoprefixer": "^10.4.0", + "postcss": "^8.4.0" + } +} diff --git a/it0-web-admin/postcss.config.js b/it0-web-admin/postcss.config.js new file mode 100644 index 0000000..12a703d --- /dev/null +++ b/it0-web-admin/postcss.config.js @@ -0,0 +1,6 @@ +module.exports = { + plugins: { + tailwindcss: {}, + autoprefixer: {}, + }, +}; diff --git a/it0-web-admin/src/app/(admin)/agent-config/hooks/page.tsx b/it0-web-admin/src/app/(admin)/agent-config/hooks/page.tsx new file mode 100644 index 0000000..fae8122 --- /dev/null +++ b/it0-web-admin/src/app/(admin)/agent-config/hooks/page.tsx @@ -0,0 +1,601 @@ +'use client'; + +import { useState, useCallback } from 'react'; +import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'; +import { apiClient } from '@/infrastructure/api/api-client'; +import { queryKeys } from '@/infrastructure/api/query-keys'; +import { cn } from '@/lib/utils'; + +// --------------------------------------------------------------------------- +// Types +// --------------------------------------------------------------------------- + +interface HookScript { + id: string; + name: string; + event: 'PreToolUse' | 'PostToolUse' | 'PreNotification' | 'PostNotification'; + toolPattern: string; + script: string; + timeout: number; + enabled: boolean; + description: string; + createdAt: string; + updatedAt: string; +} + +interface HookFormData { + name: string; + event: HookScript['event']; + toolPattern: string; + script: string; + timeout: number; + enabled: boolean; + description: string; +} + +interface HooksResponse { + data: HookScript[]; + total: number; +} + +// --------------------------------------------------------------------------- +// Constants +// --------------------------------------------------------------------------- + +const EVENT_TYPES: HookScript['event'][] = [ + 'PreToolUse', + 'PostToolUse', + 'PreNotification', + 'PostNotification', +]; + +const EVENT_BADGE_STYLES: Record = { + PreToolUse: 'bg-blue-100 text-blue-800 dark:bg-blue-900/30 dark:text-blue-400', + PostToolUse: 'bg-green-100 text-green-800 dark:bg-green-900/30 dark:text-green-400', + PreNotification: 'bg-purple-100 text-purple-800 dark:bg-purple-900/30 dark:text-purple-400', + PostNotification: 'bg-indigo-100 text-indigo-800 dark:bg-indigo-900/30 dark:text-indigo-400', +}; + +const EMPTY_FORM: HookFormData = { + name: '', + event: 'PreToolUse', + toolPattern: '*', + script: '', + timeout: 30, + enabled: true, + description: '', +}; + +// --------------------------------------------------------------------------- +// Event badge +// --------------------------------------------------------------------------- + +function EventBadge({ event }: { event: HookScript['event'] }) { + return ( + + {event} + + ); +} + +// --------------------------------------------------------------------------- +// Hook form dialog +// --------------------------------------------------------------------------- + +function HookDialog({ + open, + title, + form, + errors, + saving, + onClose, + onChange, + onSubmit, +}: { + open: boolean; + title: string; + form: HookFormData; + errors: Partial>; + saving: boolean; + onClose: () => void; + onChange: (field: keyof HookFormData, value: string | number | boolean) => void; + onSubmit: () => void; +}) { + if (!open) return null; + + return ( +
+ {/* backdrop */} +
+ + {/* dialog */} +
+

{title}

+ +
+ {/* name */} +
+ + onChange('name', e.target.value)} + className={cn( + 'w-full px-3 py-2 rounded-md border bg-background text-sm', + errors.name ? 'border-destructive' : 'border-input', + )} + placeholder="pre-bash-audit" + /> + {errors.name && ( +

{errors.name}

+ )} +
+ + {/* event type */} +
+ + +
+ + {/* tool pattern */} +
+ + onChange('toolPattern', e.target.value)} + className="w-full px-3 py-2 rounded-md border border-input bg-background text-sm" + placeholder="Bash, *, Read,Write" + /> +

+ Comma-separated tool names or * for all tools +

+
+ + {/* script content */} +
+ +