feat: ci complexity linters (#236)

This commit is contained in:
Carlos Florêncio
2026-02-17 09:19:42 +00:00
committed by GitHub
parent acaae931e2
commit 82ea7578dc
9 changed files with 599 additions and 288 deletions
+1
View File
@@ -51,6 +51,7 @@ jobs:
version: v2.9 version: v2.9
args: ./... args: ./...
working-directory: apps/backend working-directory: apps/backend
only-new-issues: true
- name: Run tests - name: Run tests
run: | run: |
+94 -29
View File
@@ -5,19 +5,18 @@
## Repo Layout ## Repo Layout
``` ```
kandev-3/ apps/
├── apps/ ├── backend/ # Go backend (orchestrator, lifecycle, agentctl, WS gateway)
│ ├── backend/ # Go backend (orchestrator, lifecycle, agentctl, WS gateway) ├── web/ # Next.js frontend (SSR + WS + Zustand)
│ ├── web/ # Next.js frontend (SSR + WS + Zustand) ├── cli/ # CLI tool (TypeScript)
│ ├── packages/ # Shared packages/types └── packages/ # Shared packages/types
│ └── landing/ # Marketing site (shares UI with web)
``` ```
## Tooling ## Tooling
- **Package manager**: `pnpm` workspace (root) - **Package manager**: `pnpm` workspace (run from `apps/`, not repo root)
- **Backend**: Go with Make (`make -C apps/backend test|lint|build`) - **Backend**: Go with Make (`make -C apps/backend test|lint|build`)
- **Frontend**: Next.js (`pnpm -C apps/web dev|lint|typecheck`) - **Frontend**: Next.js (`cd apps && pnpm --filter @kandev/web dev|lint|typecheck`)
- **UI**: Shadcn components via `@kandev/ui` - **UI**: Shadcn components via `@kandev/ui`
--- ---
@@ -30,31 +29,64 @@ kandev-3/
apps/backend/ apps/backend/
├── cmd/ ├── cmd/
│ ├── kandev/ # Main backend binary entry point │ ├── kandev/ # Main backend binary entry point
│ └── agentctl/ # Agentctl binary (runs inside containers or standalone) │ ├── agentctl/ # Agentctl binary (runs inside containers or standalone)
│ └── mock-agent/ # Mock agent for testing
├── internal/ ├── internal/
│ ├── agent/ │ ├── agent/
│ │ ├── lifecycle/ # Agent instance management (see below) │ │ ├── lifecycle/ # Agent instance management (see below)
│ │ ├── agents/ # Agent type implementations
│ │ ├── controller/ # Agent control operations
│ │ ├── credentials/ # Agent credential management
│ │ ├── discovery/ # Agent discovery
│ │ ├── docker/ # Docker-specific agent logic
│ │ ├── dto/ # Agent data transfer objects
│ │ ├── handlers/ # Agent event handlers
│ │ ├── registry/ # Agent type registry and defaults │ │ ├── registry/ # Agent type registry and defaults
│ │ ├── runtime/ # Runtime name constants │ │ ├── runtime/ # Runtime name constants
│ │ ├── settings/ # Agent settings
│ │ └── mcpconfig/ # MCP server configuration │ │ └── mcpconfig/ # MCP server configuration
│ ├── agentctl/ │ ├── agentctl/
│ │ ├── client/ # HTTP client for talking to agentctl │ │ ├── client/ # HTTP client for talking to agentctl
│ │ └── server/ # agentctl HTTP server │ │ └── server/ # agentctl HTTP server
│ │ ├── api/ # HTTP endpoints │ │ ├── api/ # HTTP endpoints
│ │ ├── adapter/ # Protocol adapters (ACP, Codex, Copilot, Amp)
│ │ ├── instance/ # Multi-instance management │ │ ├── instance/ # Multi-instance management
│ │ ├── mcp/ # MCP server integration
│ │ ├── process/ # Agent subprocess management │ │ ├── process/ # Agent subprocess management
│ │ └── adapter/ # Protocol adapters (ACP, Codex, REST, MCP) │ │ └── shell/ # Shell session management
│ ├── orchestrator/ # Task execution coordination │ ├── orchestrator/ # Task execution coordination
│ │ ├── executor/ # Launches agents via lifecycle manager │ │ ├── executor/ # Launches agents via lifecycle manager
│ │ ├── scheduler/ # Task scheduling │ │ ├── handlers/ # Orchestrator event handlers
│ │ ├── messagequeue/ # Message queue for agent prompts
│ │ ├── queue/ # Task queue │ │ ├── queue/ # Task queue
│ │ ├── scheduler/ # Task scheduling
│ │ └── watcher/ # Event handlers │ │ └── watcher/ # Event handlers
│ ├── task/ │ ├── task/
│ │ ├── controller/ # Task HTTP/WS controllers
│ │ ├── dto/ # Task data transfer objects
│ │ ├── events/ # Task event types
│ │ ├── handlers/ # Task event handlers
│ │ ├── models/ # Task, Session, Executor, Message models │ │ ├── models/ # Task, Session, Executor, Message models
│ │ ├── service/ # Task business logic │ │ ├── repository/ # Database access (SQLite)
│ │ └── repository/ # Database access (SQLite) │ │ └── service/ # Task business logic
│ ├── worktree/ # Git worktree management for workspace isolation │ ├── analytics/ # Usage analytics
│ └── events/ # Event bus for internal pub/sub │ ├── clarification/ # Agent clarification handling
│ ├── common/ # Shared utilities
│ ├── db/ # Database initialization
│ ├── debug/ # Debug tooling
│ ├── editors/ # Editor integration
│ ├── events/ # Event bus for internal pub/sub
│ ├── gateway/ # WebSocket gateway
│ ├── integration/ # External integrations
│ ├── lsp/ # LSP server
│ ├── mcp/ # MCP protocol support
│ ├── notifications/ # Notification system
│ ├── persistence/ # Persistence layer
│ ├── prompts/ # Prompt management
│ ├── sysprompt/ # System prompt injection
│ ├── user/ # User management
│ ├── workflow/ # Workflow engine
│ └── worktree/ # Git worktree management for workspace isolation
``` ```
### Key Concepts ### Key Concepts
@@ -66,13 +98,15 @@ apps/backend/
- Located in `internal/orchestrator/` - Located in `internal/orchestrator/`
**Lifecycle Manager** (`internal/agent/lifecycle/`) manages agent instances: **Lifecycle Manager** (`internal/agent/lifecycle/`) manages agent instances:
- `Manager` - central coordinator for agent lifecycle - `Manager` (`manager.go`) - central coordinator for agent lifecycle
- `Runtime` interface - abstracts execution environment (Docker, Standalone) - `Runtime` interface (`runtime.go`) - abstracts execution environment (Docker, Standalone, Remote Docker)
- `ExecutionStore` - thread-safe in-memory execution tracking - `ExecutionStore` (`execution_store.go`) - thread-safe in-memory execution tracking
- `SessionManager` - ACP session initialization and resume - `session.go` - ACP session initialization and resume
- `StreamManager` - WebSocket stream connections to agentctl - `streams.go` - WebSocket stream connections to agentctl
- `EventPublisher` - publishes events to the event bus - `events.go` - publishes events to the event bus
- `ContainerManager` - Docker container operations - `container.go` - Docker container operations
- `process_runner.go` - agent process launch and management
- `profile_resolver.go` - resolves agent profiles/settings
**agentctl** is an HTTP server that: **agentctl** is an HTTP server that:
- Runs inside Docker containers or as standalone process - Runs inside Docker containers or as standalone process
@@ -148,9 +182,12 @@ Client (WS) Orchestrator Lifecycle Manager Runtime age
Protocol adapters normalize different agent CLIs: Protocol adapters normalize different agent CLIs:
- `AgentAdapter` interface defines `Start()`, `Stop()`, `Prompt()`, `Cancel()` - `AgentAdapter` interface defines `Start()`, `Stop()`, `Prompt()`, `Cancel()`
- `ACPAdapter` - ACP JSON-RPC over stdio - `ACP` - ACP JSON-RPC over stdio (Claude Code)
- `CodexAdapter` - Codex-style JSON-RPC - `Codex` - Codex-style JSON-RPC (OpenAI Codex)
- `CopilotAdapter` - GitHub Copilot SDK
- `AmpAdapter` - Sourcegraph Amp CLI
- `process.Manager` owns subprocess, wires stdio to adapter - `process.Manager` owns subprocess, wires stdio to adapter
- Factory pattern in `adapter/factory.go` selects adapter by agent type
--- ---
@@ -196,19 +233,22 @@ SSR Fetch -> Hydrate Store -> Components Read Store -> Hooks Subscribe
``` ```
lib/state/ lib/state/
├── store.ts (~370 lines) # Root composition ├── store.ts # Root composition
├── slices/ # Domain slices ├── slices/ # Domain slices
│ ├── kanban/ # boards, tasks, columns │ ├── kanban/ # boards, tasks, columns
│ ├── session/ # sessions, messages, turns, worktrees │ ├── session/ # sessions, messages, turns, worktrees
│ ├── session-runtime/ # shell, processes, git, context │ ├── session-runtime/ # shell, processes, git, context
│ ├── workspace/ # workspaces, repos, branches │ ├── workspace/ # workspaces, repos, branches
│ ├── settings/ # executors, agents, editors, prompts │ ├── settings/ # executors, agents, editors, prompts
│ ├── diff-comments/ # code review diff comments
│ └── ui/ # preview, connection, active state │ └── ui/ # preview, connection, active state
├── hydration/ # SSR merge strategies ├── hydration/ # SSR merge strategies
└── selectors/ # Memoized selectors (future)
hooks/domains/{kanban,session,workspace,settings}/ # Domain-organized hooks hooks/domains/{kanban,session,workspace,settings}/ # Domain-organized hooks
lib/api/domains/{kanban,session,workspace,settings,process}-api.ts # API clients lib/api/domains/ # API clients
├── kanban-api, session-api, workspace-api, settings-api, process-api
├── plan-api, queue-api, workflow-api, stats-api
├── user-shell-api, debug-api
``` ```
**Key State Paths:** **Key State Paths:**
@@ -222,12 +262,31 @@ lib/api/domains/{kanban,session,workspace,settings,process}-api.ts # API client
### WS ### WS
**Format:** `{id, type, action, payload, timestamp}`. See `docs/asyncapi.yaml` for all actions. **Format:** `{id, type, action, payload, timestamp}`.
--- ---
## Best Practices ## Best Practices
### Code Quality (enforced by linters)
Static analysis runs in CI and pre-commit. New code **must** stay within these limits:
**Go** (`apps/backend/.golangci.yml` — errors on new code only):
- Functions: **≤80 lines**, **≤50 statements**
- Cyclomatic complexity: **≤15** · Cognitive complexity: **≤30**
- Nesting depth: **≤5** · Naked returns only in functions **≤30 lines**
- No duplicated blocks (**≥150 tokens**) · Repeated strings → constants (**≥3 occurrences**)
**TypeScript** (`apps/web/eslint.config.mjs` — warnings, will become errors):
- Files: **≤600 lines** · Functions: **≤100 lines**
- Cyclomatic complexity: **≤15** · Cognitive complexity: **≤20**
- Nesting depth: **≤4** · Parameters: **≤5**
- No duplicated strings (**≥4 occurrences**) · No identical functions · No unused imports
- No nested ternaries
**When you hit a limit:** extract a helper function, custom hook, or sub-component. Prefer composition over growing a single function.
### Backend ### Backend
- Provider pattern for DI; stderr for logs, stdout for ACP only - Provider pattern for DI; stderr for logs, stdout for ACP only
- Pass context through chains; event bus for cross-component comm - Pass context through chains; event bus for cross-component comm
@@ -250,4 +309,10 @@ lib/api/domains/{kanban,session,workspace,settings,process}-api.ts # API client
--- ---
**Last Updated**: 2026-02-01 ## Maintaining This File
This file is read by AI coding agents (Claude Code via `CLAUDE.md` symlink, Codex via `AGENTS.md`). If your changes make any section of this file outdated or inaccurate — e.g., you add/remove/rename packages, change architectural patterns, add new adapters, modify store slices, or change conventions — **update the relevant sections of this file as part of the same PR**. Keep descriptions concise and factual. Do not add speculative or aspirational content.
---
**Last Updated**: 2026-02-17
Symlink
+1
View File
@@ -0,0 +1 @@
AGENTS.md
+49 -1
View File
@@ -1,2 +1,50 @@
version: "2" version: "2"
# Default golangci-lint configuration (uses built-in defaults).
linters:
enable:
- funlen
- cyclop
- gocognit
- nestif
- dupl
- goconst
- gocritic
- misspell
- nakedret
- unconvert
- dupword
- maintidx
settings:
funlen:
lines: 80
statements: 50
cyclop:
max-complexity: 15
gocognit:
min-complexity: 30
nestif:
min-complexity: 5
dupl:
threshold: 150
goconst:
min-occurrences: 3
min-len: 3
nakedret:
max-func-lines: 30
maintidx:
under: 15
exclusions:
rules:
- path: _test\.go
linters:
- funlen
- cyclop
- gocognit
- dupl
- goconst
- maintidx
issues:
new: true
+171 -13
View File
@@ -373,7 +373,7 @@ importers:
version: 0.55.1 version: 0.55.1
next: next:
specifier: 16.1.1 specifier: 16.1.1
version: 16.1.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3) version: 16.1.1(@babel/core@7.28.6)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
next-themes: next-themes:
specifier: ^0.4.6 specifier: ^0.4.6
version: 0.4.6(react-dom@19.2.3(react@19.2.3))(react@19.2.3) version: 0.4.6(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
@@ -412,7 +412,7 @@ importers:
version: 2.0.7(react-dom@19.2.3(react@19.2.3))(react@19.2.3) version: 2.0.7(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
styled-jsx: styled-jsx:
specifier: 5.1.6 specifier: 5.1.6
version: 5.1.6(react@19.2.3) version: 5.1.6(@babel/core@7.28.6)(react@19.2.3)
tailwind-merge: tailwind-merge:
specifier: ^3.4.0 specifier: ^3.4.0
version: 3.4.0 version: 3.4.0
@@ -453,6 +453,12 @@ importers:
eslint-config-next: eslint-config-next:
specifier: 16.1.1 specifier: 16.1.1
version: 16.1.1(@typescript-eslint/parser@8.53.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3) version: 16.1.1(@typescript-eslint/parser@8.53.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3)
eslint-plugin-sonarjs:
specifier: ^3.0.7
version: 3.0.7(eslint@9.39.2(jiti@2.6.1))
eslint-plugin-unused-imports:
specifier: ^4.4.1
version: 4.4.1(@typescript-eslint/eslint-plugin@8.53.0(@typescript-eslint/parser@8.53.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.2(jiti@2.6.1))
happy-dom: happy-dom:
specifier: ^20.1.0 specifier: ^20.1.0
version: 20.1.0 version: 20.1.0
@@ -1157,89 +1163,105 @@ packages:
resolution: {integrity: sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw==} resolution: {integrity: sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw==}
cpu: [arm64] cpu: [arm64]
os: [linux] os: [linux]
libc: [glibc]
'@img/sharp-libvips-linux-arm@1.2.4': '@img/sharp-libvips-linux-arm@1.2.4':
resolution: {integrity: sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A==} resolution: {integrity: sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A==}
cpu: [arm] cpu: [arm]
os: [linux] os: [linux]
libc: [glibc]
'@img/sharp-libvips-linux-ppc64@1.2.4': '@img/sharp-libvips-linux-ppc64@1.2.4':
resolution: {integrity: sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA==} resolution: {integrity: sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA==}
cpu: [ppc64] cpu: [ppc64]
os: [linux] os: [linux]
libc: [glibc]
'@img/sharp-libvips-linux-riscv64@1.2.4': '@img/sharp-libvips-linux-riscv64@1.2.4':
resolution: {integrity: sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA==} resolution: {integrity: sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA==}
cpu: [riscv64] cpu: [riscv64]
os: [linux] os: [linux]
libc: [glibc]
'@img/sharp-libvips-linux-s390x@1.2.4': '@img/sharp-libvips-linux-s390x@1.2.4':
resolution: {integrity: sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ==} resolution: {integrity: sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ==}
cpu: [s390x] cpu: [s390x]
os: [linux] os: [linux]
libc: [glibc]
'@img/sharp-libvips-linux-x64@1.2.4': '@img/sharp-libvips-linux-x64@1.2.4':
resolution: {integrity: sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw==} resolution: {integrity: sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw==}
cpu: [x64] cpu: [x64]
os: [linux] os: [linux]
libc: [glibc]
'@img/sharp-libvips-linuxmusl-arm64@1.2.4': '@img/sharp-libvips-linuxmusl-arm64@1.2.4':
resolution: {integrity: sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw==} resolution: {integrity: sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw==}
cpu: [arm64] cpu: [arm64]
os: [linux] os: [linux]
libc: [musl]
'@img/sharp-libvips-linuxmusl-x64@1.2.4': '@img/sharp-libvips-linuxmusl-x64@1.2.4':
resolution: {integrity: sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg==} resolution: {integrity: sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg==}
cpu: [x64] cpu: [x64]
os: [linux] os: [linux]
libc: [musl]
'@img/sharp-linux-arm64@0.34.5': '@img/sharp-linux-arm64@0.34.5':
resolution: {integrity: sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg==} resolution: {integrity: sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
cpu: [arm64] cpu: [arm64]
os: [linux] os: [linux]
libc: [glibc]
'@img/sharp-linux-arm@0.34.5': '@img/sharp-linux-arm@0.34.5':
resolution: {integrity: sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw==} resolution: {integrity: sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
cpu: [arm] cpu: [arm]
os: [linux] os: [linux]
libc: [glibc]
'@img/sharp-linux-ppc64@0.34.5': '@img/sharp-linux-ppc64@0.34.5':
resolution: {integrity: sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA==} resolution: {integrity: sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
cpu: [ppc64] cpu: [ppc64]
os: [linux] os: [linux]
libc: [glibc]
'@img/sharp-linux-riscv64@0.34.5': '@img/sharp-linux-riscv64@0.34.5':
resolution: {integrity: sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw==} resolution: {integrity: sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
cpu: [riscv64] cpu: [riscv64]
os: [linux] os: [linux]
libc: [glibc]
'@img/sharp-linux-s390x@0.34.5': '@img/sharp-linux-s390x@0.34.5':
resolution: {integrity: sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg==} resolution: {integrity: sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
cpu: [s390x] cpu: [s390x]
os: [linux] os: [linux]
libc: [glibc]
'@img/sharp-linux-x64@0.34.5': '@img/sharp-linux-x64@0.34.5':
resolution: {integrity: sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ==} resolution: {integrity: sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
cpu: [x64] cpu: [x64]
os: [linux] os: [linux]
libc: [glibc]
'@img/sharp-linuxmusl-arm64@0.34.5': '@img/sharp-linuxmusl-arm64@0.34.5':
resolution: {integrity: sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg==} resolution: {integrity: sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
cpu: [arm64] cpu: [arm64]
os: [linux] os: [linux]
libc: [musl]
'@img/sharp-linuxmusl-x64@0.34.5': '@img/sharp-linuxmusl-x64@0.34.5':
resolution: {integrity: sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q==} resolution: {integrity: sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
cpu: [x64] cpu: [x64]
os: [linux] os: [linux]
libc: [musl]
'@img/sharp-wasm32@0.34.5': '@img/sharp-wasm32@0.34.5':
resolution: {integrity: sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw==} resolution: {integrity: sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw==}
@@ -1307,6 +1329,10 @@ packages:
resolution: {integrity: sha512-ZT55BDLV0yv0RBm2czMiZ+SqCGO7AvmOM3G/w2xhVPH+te0aKgFjmBvGlL1dH+ql2tgGO3MVrbb3jCKyvpgnxA==} resolution: {integrity: sha512-ZT55BDLV0yv0RBm2czMiZ+SqCGO7AvmOM3G/w2xhVPH+te0aKgFjmBvGlL1dH+ql2tgGO3MVrbb3jCKyvpgnxA==}
engines: {node: 20 || >=22} engines: {node: 20 || >=22}
'@isaacs/brace-expansion@5.0.1':
resolution: {integrity: sha512-WMz71T1JS624nWj2n2fnYAuPovhv7EUhk69R6i9dsVyzxt5eM3bjwvgk9L+APE1TRscGysAVMANkB0jh0LQZrQ==}
engines: {node: 20 || >=22}
'@isaacs/fs-minipass@4.0.1': '@isaacs/fs-minipass@4.0.1':
resolution: {integrity: sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==} resolution: {integrity: sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==}
engines: {node: '>=18.0.0'} engines: {node: '>=18.0.0'}
@@ -1429,24 +1455,28 @@ packages:
engines: {node: '>= 10'} engines: {node: '>= 10'}
cpu: [arm64] cpu: [arm64]
os: [linux] os: [linux]
libc: [glibc]
'@next/swc-linux-arm64-musl@16.1.1': '@next/swc-linux-arm64-musl@16.1.1':
resolution: {integrity: sha512-MFHrgL4TXNQbBPzkKKur4Fb5ICEJa87HM7fczFs2+HWblM7mMLdco3dvyTI+QmLBU9xgns/EeeINSZD6Ar+oLg==} resolution: {integrity: sha512-MFHrgL4TXNQbBPzkKKur4Fb5ICEJa87HM7fczFs2+HWblM7mMLdco3dvyTI+QmLBU9xgns/EeeINSZD6Ar+oLg==}
engines: {node: '>= 10'} engines: {node: '>= 10'}
cpu: [arm64] cpu: [arm64]
os: [linux] os: [linux]
libc: [musl]
'@next/swc-linux-x64-gnu@16.1.1': '@next/swc-linux-x64-gnu@16.1.1':
resolution: {integrity: sha512-20bYDfgOQAPUkkKBnyP9PTuHiJGM7HzNBbuqmD0jiFVZ0aOldz+VnJhbxzjcSabYsnNjMPsE0cyzEudpYxsrUQ==} resolution: {integrity: sha512-20bYDfgOQAPUkkKBnyP9PTuHiJGM7HzNBbuqmD0jiFVZ0aOldz+VnJhbxzjcSabYsnNjMPsE0cyzEudpYxsrUQ==}
engines: {node: '>= 10'} engines: {node: '>= 10'}
cpu: [x64] cpu: [x64]
os: [linux] os: [linux]
libc: [glibc]
'@next/swc-linux-x64-musl@16.1.1': '@next/swc-linux-x64-musl@16.1.1':
resolution: {integrity: sha512-9pRbK3M4asAHQRkwaXwu601oPZHghuSC8IXNENgbBSyImHv/zY4K5udBusgdHkvJ/Tcr96jJwQYOll0qU8+fPA==} resolution: {integrity: sha512-9pRbK3M4asAHQRkwaXwu601oPZHghuSC8IXNENgbBSyImHv/zY4K5udBusgdHkvJ/Tcr96jJwQYOll0qU8+fPA==}
engines: {node: '>= 10'} engines: {node: '>= 10'}
cpu: [x64] cpu: [x64]
os: [linux] os: [linux]
libc: [musl]
'@next/swc-win32-arm64-msvc@16.1.1': '@next/swc-win32-arm64-msvc@16.1.1':
resolution: {integrity: sha512-bdfQkggaLgnmYrFkSQfsHfOhk/mCYmjnrbRCGgkMcoOBZ4n+TRRSLmT/CU5SATzlBJ9TpioUyBW/vWFXTqQRiA==} resolution: {integrity: sha512-bdfQkggaLgnmYrFkSQfsHfOhk/mCYmjnrbRCGgkMcoOBZ4n+TRRSLmT/CU5SATzlBJ9TpioUyBW/vWFXTqQRiA==}
@@ -2278,66 +2308,79 @@ packages:
resolution: {integrity: sha512-Rn3n+FUk2J5VWx+ywrG/HGPTD9jXNbicRtTM11e/uorplArnXZYsVifnPPqNNP5BsO3roI4n8332ukpY/zN7rQ==} resolution: {integrity: sha512-Rn3n+FUk2J5VWx+ywrG/HGPTD9jXNbicRtTM11e/uorplArnXZYsVifnPPqNNP5BsO3roI4n8332ukpY/zN7rQ==}
cpu: [arm] cpu: [arm]
os: [linux] os: [linux]
libc: [glibc]
'@rollup/rollup-linux-arm-musleabihf@4.55.1': '@rollup/rollup-linux-arm-musleabihf@4.55.1':
resolution: {integrity: sha512-grPNWydeKtc1aEdrJDWk4opD7nFtQbMmV7769hiAaYyUKCT1faPRm2av8CX1YJsZ4TLAZcg9gTR1KvEzoLjXkg==} resolution: {integrity: sha512-grPNWydeKtc1aEdrJDWk4opD7nFtQbMmV7769hiAaYyUKCT1faPRm2av8CX1YJsZ4TLAZcg9gTR1KvEzoLjXkg==}
cpu: [arm] cpu: [arm]
os: [linux] os: [linux]
libc: [musl]
'@rollup/rollup-linux-arm64-gnu@4.55.1': '@rollup/rollup-linux-arm64-gnu@4.55.1':
resolution: {integrity: sha512-a59mwd1k6x8tXKcUxSyISiquLwB5pX+fJW9TkWU46lCqD/GRDe9uDN31jrMmVP3feI3mhAdvcCClhV8V5MhJFQ==} resolution: {integrity: sha512-a59mwd1k6x8tXKcUxSyISiquLwB5pX+fJW9TkWU46lCqD/GRDe9uDN31jrMmVP3feI3mhAdvcCClhV8V5MhJFQ==}
cpu: [arm64] cpu: [arm64]
os: [linux] os: [linux]
libc: [glibc]
'@rollup/rollup-linux-arm64-musl@4.55.1': '@rollup/rollup-linux-arm64-musl@4.55.1':
resolution: {integrity: sha512-puS1MEgWX5GsHSoiAsF0TYrpomdvkaXm0CofIMG5uVkP6IBV+ZO9xhC5YEN49nsgYo1DuuMquF9+7EDBVYu4uA==} resolution: {integrity: sha512-puS1MEgWX5GsHSoiAsF0TYrpomdvkaXm0CofIMG5uVkP6IBV+ZO9xhC5YEN49nsgYo1DuuMquF9+7EDBVYu4uA==}
cpu: [arm64] cpu: [arm64]
os: [linux] os: [linux]
libc: [musl]
'@rollup/rollup-linux-loong64-gnu@4.55.1': '@rollup/rollup-linux-loong64-gnu@4.55.1':
resolution: {integrity: sha512-r3Wv40in+lTsULSb6nnoudVbARdOwb2u5fpeoOAZjFLznp6tDU8kd+GTHmJoqZ9lt6/Sys33KdIHUaQihFcu7g==} resolution: {integrity: sha512-r3Wv40in+lTsULSb6nnoudVbARdOwb2u5fpeoOAZjFLznp6tDU8kd+GTHmJoqZ9lt6/Sys33KdIHUaQihFcu7g==}
cpu: [loong64] cpu: [loong64]
os: [linux] os: [linux]
libc: [glibc]
'@rollup/rollup-linux-loong64-musl@4.55.1': '@rollup/rollup-linux-loong64-musl@4.55.1':
resolution: {integrity: sha512-MR8c0+UxAlB22Fq4R+aQSPBayvYa3+9DrwG/i1TKQXFYEaoW3B5b/rkSRIypcZDdWjWnpcvxbNaAJDcSbJU3Lw==} resolution: {integrity: sha512-MR8c0+UxAlB22Fq4R+aQSPBayvYa3+9DrwG/i1TKQXFYEaoW3B5b/rkSRIypcZDdWjWnpcvxbNaAJDcSbJU3Lw==}
cpu: [loong64] cpu: [loong64]
os: [linux] os: [linux]
libc: [musl]
'@rollup/rollup-linux-ppc64-gnu@4.55.1': '@rollup/rollup-linux-ppc64-gnu@4.55.1':
resolution: {integrity: sha512-3KhoECe1BRlSYpMTeVrD4sh2Pw2xgt4jzNSZIIPLFEsnQn9gAnZagW9+VqDqAHgm1Xc77LzJOo2LdigS5qZ+gw==} resolution: {integrity: sha512-3KhoECe1BRlSYpMTeVrD4sh2Pw2xgt4jzNSZIIPLFEsnQn9gAnZagW9+VqDqAHgm1Xc77LzJOo2LdigS5qZ+gw==}
cpu: [ppc64] cpu: [ppc64]
os: [linux] os: [linux]
libc: [glibc]
'@rollup/rollup-linux-ppc64-musl@4.55.1': '@rollup/rollup-linux-ppc64-musl@4.55.1':
resolution: {integrity: sha512-ziR1OuZx0vdYZZ30vueNZTg73alF59DicYrPViG0NEgDVN8/Jl87zkAPu4u6VjZST2llgEUjaiNl9JM6HH1Vdw==} resolution: {integrity: sha512-ziR1OuZx0vdYZZ30vueNZTg73alF59DicYrPViG0NEgDVN8/Jl87zkAPu4u6VjZST2llgEUjaiNl9JM6HH1Vdw==}
cpu: [ppc64] cpu: [ppc64]
os: [linux] os: [linux]
libc: [musl]
'@rollup/rollup-linux-riscv64-gnu@4.55.1': '@rollup/rollup-linux-riscv64-gnu@4.55.1':
resolution: {integrity: sha512-uW0Y12ih2XJRERZ4jAfKamTyIHVMPQnTZcQjme2HMVDAHY4amf5u414OqNYC+x+LzRdRcnIG1YodLrrtA8xsxw==} resolution: {integrity: sha512-uW0Y12ih2XJRERZ4jAfKamTyIHVMPQnTZcQjme2HMVDAHY4amf5u414OqNYC+x+LzRdRcnIG1YodLrrtA8xsxw==}
cpu: [riscv64] cpu: [riscv64]
os: [linux] os: [linux]
libc: [glibc]
'@rollup/rollup-linux-riscv64-musl@4.55.1': '@rollup/rollup-linux-riscv64-musl@4.55.1':
resolution: {integrity: sha512-u9yZ0jUkOED1BFrqu3BwMQoixvGHGZ+JhJNkNKY/hyoEgOwlqKb62qu+7UjbPSHYjiVy8kKJHvXKv5coH4wDeg==} resolution: {integrity: sha512-u9yZ0jUkOED1BFrqu3BwMQoixvGHGZ+JhJNkNKY/hyoEgOwlqKb62qu+7UjbPSHYjiVy8kKJHvXKv5coH4wDeg==}
cpu: [riscv64] cpu: [riscv64]
os: [linux] os: [linux]
libc: [musl]
'@rollup/rollup-linux-s390x-gnu@4.55.1': '@rollup/rollup-linux-s390x-gnu@4.55.1':
resolution: {integrity: sha512-/0PenBCmqM4ZUd0190j7J0UsQ/1nsi735iPRakO8iPciE7BQ495Y6msPzaOmvx0/pn+eJVVlZrNrSh4WSYLxNg==} resolution: {integrity: sha512-/0PenBCmqM4ZUd0190j7J0UsQ/1nsi735iPRakO8iPciE7BQ495Y6msPzaOmvx0/pn+eJVVlZrNrSh4WSYLxNg==}
cpu: [s390x] cpu: [s390x]
os: [linux] os: [linux]
libc: [glibc]
'@rollup/rollup-linux-x64-gnu@4.55.1': '@rollup/rollup-linux-x64-gnu@4.55.1':
resolution: {integrity: sha512-a8G4wiQxQG2BAvo+gU6XrReRRqj+pLS2NGXKm8io19goR+K8lw269eTrPkSdDTALwMmJp4th2Uh0D8J9bEV1vg==} resolution: {integrity: sha512-a8G4wiQxQG2BAvo+gU6XrReRRqj+pLS2NGXKm8io19goR+K8lw269eTrPkSdDTALwMmJp4th2Uh0D8J9bEV1vg==}
cpu: [x64] cpu: [x64]
os: [linux] os: [linux]
libc: [glibc]
'@rollup/rollup-linux-x64-musl@4.55.1': '@rollup/rollup-linux-x64-musl@4.55.1':
resolution: {integrity: sha512-bD+zjpFrMpP/hqkfEcnjXWHMw5BIghGisOKPj+2NaNDuVT+8Ds4mPf3XcPHuat1tz89WRL+1wbcxKY3WSbiT7w==} resolution: {integrity: sha512-bD+zjpFrMpP/hqkfEcnjXWHMw5BIghGisOKPj+2NaNDuVT+8Ds4mPf3XcPHuat1tz89WRL+1wbcxKY3WSbiT7w==}
cpu: [x64] cpu: [x64]
os: [linux] os: [linux]
libc: [musl]
'@rollup/rollup-openbsd-x64@4.55.1': '@rollup/rollup-openbsd-x64@4.55.1':
resolution: {integrity: sha512-eLXw0dOiqE4QmvikfQ6yjgkg/xDM+MdU9YJuP4ySTibXU0oAvnEWXt7UDJmD4UkYialMfOGFPJnIHSe/kdzPxg==} resolution: {integrity: sha512-eLXw0dOiqE4QmvikfQ6yjgkg/xDM+MdU9YJuP4ySTibXU0oAvnEWXt7UDJmD4UkYialMfOGFPJnIHSe/kdzPxg==}
@@ -2455,24 +2498,28 @@ packages:
engines: {node: '>= 10'} engines: {node: '>= 10'}
cpu: [arm64] cpu: [arm64]
os: [linux] os: [linux]
libc: [glibc]
'@tailwindcss/oxide-linux-arm64-musl@4.1.18': '@tailwindcss/oxide-linux-arm64-musl@4.1.18':
resolution: {integrity: sha512-1px92582HkPQlaaCkdRcio71p8bc8i/ap5807tPRDK/uw953cauQBT8c5tVGkOwrHMfc2Yh6UuxaH4vtTjGvHg==} resolution: {integrity: sha512-1px92582HkPQlaaCkdRcio71p8bc8i/ap5807tPRDK/uw953cauQBT8c5tVGkOwrHMfc2Yh6UuxaH4vtTjGvHg==}
engines: {node: '>= 10'} engines: {node: '>= 10'}
cpu: [arm64] cpu: [arm64]
os: [linux] os: [linux]
libc: [musl]
'@tailwindcss/oxide-linux-x64-gnu@4.1.18': '@tailwindcss/oxide-linux-x64-gnu@4.1.18':
resolution: {integrity: sha512-v3gyT0ivkfBLoZGF9LyHmts0Isc8jHZyVcbzio6Wpzifg/+5ZJpDiRiUhDLkcr7f/r38SWNe7ucxmGW3j3Kb/g==} resolution: {integrity: sha512-v3gyT0ivkfBLoZGF9LyHmts0Isc8jHZyVcbzio6Wpzifg/+5ZJpDiRiUhDLkcr7f/r38SWNe7ucxmGW3j3Kb/g==}
engines: {node: '>= 10'} engines: {node: '>= 10'}
cpu: [x64] cpu: [x64]
os: [linux] os: [linux]
libc: [glibc]
'@tailwindcss/oxide-linux-x64-musl@4.1.18': '@tailwindcss/oxide-linux-x64-musl@4.1.18':
resolution: {integrity: sha512-bhJ2y2OQNlcRwwgOAGMY0xTFStt4/wyU6pvI6LSuZpRgKQwxTec0/3Scu91O8ir7qCR3AuepQKLU/kX99FouqQ==} resolution: {integrity: sha512-bhJ2y2OQNlcRwwgOAGMY0xTFStt4/wyU6pvI6LSuZpRgKQwxTec0/3Scu91O8ir7qCR3AuepQKLU/kX99FouqQ==}
engines: {node: '>= 10'} engines: {node: '>= 10'}
cpu: [x64] cpu: [x64]
os: [linux] os: [linux]
libc: [musl]
'@tailwindcss/oxide-wasm32-wasi@4.1.18': '@tailwindcss/oxide-wasm32-wasi@4.1.18':
resolution: {integrity: sha512-LffYTvPjODiP6PT16oNeUQJzNVyJl1cjIebq/rWWBF+3eDst5JGEFSc5cWxyRCJ0Mxl+KyIkqRxk1XPEs9x8TA==} resolution: {integrity: sha512-LffYTvPjODiP6PT16oNeUQJzNVyJl1cjIebq/rWWBF+3eDst5JGEFSc5cWxyRCJ0Mxl+KyIkqRxk1XPEs9x8TA==}
@@ -3061,41 +3108,49 @@ packages:
resolution: {integrity: sha512-34gw7PjDGB9JgePJEmhEqBhWvCiiWCuXsL9hYphDF7crW7UgI05gyBAi6MF58uGcMOiOqSJ2ybEeCvHcq0BCmQ==} resolution: {integrity: sha512-34gw7PjDGB9JgePJEmhEqBhWvCiiWCuXsL9hYphDF7crW7UgI05gyBAi6MF58uGcMOiOqSJ2ybEeCvHcq0BCmQ==}
cpu: [arm64] cpu: [arm64]
os: [linux] os: [linux]
libc: [glibc]
'@unrs/resolver-binding-linux-arm64-musl@1.11.1': '@unrs/resolver-binding-linux-arm64-musl@1.11.1':
resolution: {integrity: sha512-RyMIx6Uf53hhOtJDIamSbTskA99sPHS96wxVE/bJtePJJtpdKGXO1wY90oRdXuYOGOTuqjT8ACccMc4K6QmT3w==} resolution: {integrity: sha512-RyMIx6Uf53hhOtJDIamSbTskA99sPHS96wxVE/bJtePJJtpdKGXO1wY90oRdXuYOGOTuqjT8ACccMc4K6QmT3w==}
cpu: [arm64] cpu: [arm64]
os: [linux] os: [linux]
libc: [musl]
'@unrs/resolver-binding-linux-ppc64-gnu@1.11.1': '@unrs/resolver-binding-linux-ppc64-gnu@1.11.1':
resolution: {integrity: sha512-D8Vae74A4/a+mZH0FbOkFJL9DSK2R6TFPC9M+jCWYia/q2einCubX10pecpDiTmkJVUH+y8K3BZClycD8nCShA==} resolution: {integrity: sha512-D8Vae74A4/a+mZH0FbOkFJL9DSK2R6TFPC9M+jCWYia/q2einCubX10pecpDiTmkJVUH+y8K3BZClycD8nCShA==}
cpu: [ppc64] cpu: [ppc64]
os: [linux] os: [linux]
libc: [glibc]
'@unrs/resolver-binding-linux-riscv64-gnu@1.11.1': '@unrs/resolver-binding-linux-riscv64-gnu@1.11.1':
resolution: {integrity: sha512-frxL4OrzOWVVsOc96+V3aqTIQl1O2TjgExV4EKgRY09AJ9leZpEg8Ak9phadbuX0BA4k8U5qtvMSQQGGmaJqcQ==} resolution: {integrity: sha512-frxL4OrzOWVVsOc96+V3aqTIQl1O2TjgExV4EKgRY09AJ9leZpEg8Ak9phadbuX0BA4k8U5qtvMSQQGGmaJqcQ==}
cpu: [riscv64] cpu: [riscv64]
os: [linux] os: [linux]
libc: [glibc]
'@unrs/resolver-binding-linux-riscv64-musl@1.11.1': '@unrs/resolver-binding-linux-riscv64-musl@1.11.1':
resolution: {integrity: sha512-mJ5vuDaIZ+l/acv01sHoXfpnyrNKOk/3aDoEdLO/Xtn9HuZlDD6jKxHlkN8ZhWyLJsRBxfv9GYM2utQ1SChKew==} resolution: {integrity: sha512-mJ5vuDaIZ+l/acv01sHoXfpnyrNKOk/3aDoEdLO/Xtn9HuZlDD6jKxHlkN8ZhWyLJsRBxfv9GYM2utQ1SChKew==}
cpu: [riscv64] cpu: [riscv64]
os: [linux] os: [linux]
libc: [musl]
'@unrs/resolver-binding-linux-s390x-gnu@1.11.1': '@unrs/resolver-binding-linux-s390x-gnu@1.11.1':
resolution: {integrity: sha512-kELo8ebBVtb9sA7rMe1Cph4QHreByhaZ2QEADd9NzIQsYNQpt9UkM9iqr2lhGr5afh885d/cB5QeTXSbZHTYPg==} resolution: {integrity: sha512-kELo8ebBVtb9sA7rMe1Cph4QHreByhaZ2QEADd9NzIQsYNQpt9UkM9iqr2lhGr5afh885d/cB5QeTXSbZHTYPg==}
cpu: [s390x] cpu: [s390x]
os: [linux] os: [linux]
libc: [glibc]
'@unrs/resolver-binding-linux-x64-gnu@1.11.1': '@unrs/resolver-binding-linux-x64-gnu@1.11.1':
resolution: {integrity: sha512-C3ZAHugKgovV5YvAMsxhq0gtXuwESUKc5MhEtjBpLoHPLYM+iuwSj3lflFwK3DPm68660rZ7G8BMcwSro7hD5w==} resolution: {integrity: sha512-C3ZAHugKgovV5YvAMsxhq0gtXuwESUKc5MhEtjBpLoHPLYM+iuwSj3lflFwK3DPm68660rZ7G8BMcwSro7hD5w==}
cpu: [x64] cpu: [x64]
os: [linux] os: [linux]
libc: [glibc]
'@unrs/resolver-binding-linux-x64-musl@1.11.1': '@unrs/resolver-binding-linux-x64-musl@1.11.1':
resolution: {integrity: sha512-rV0YSoyhK2nZ4vEswT/QwqzqQXw5I6CjoaYMOX0TqBlWhojUf8P94mvI7nuJTeaCkkds3QE4+zS8Ko+GdXuZtA==} resolution: {integrity: sha512-rV0YSoyhK2nZ4vEswT/QwqzqQXw5I6CjoaYMOX0TqBlWhojUf8P94mvI7nuJTeaCkkds3QE4+zS8Ko+GdXuZtA==}
cpu: [x64] cpu: [x64]
os: [linux] os: [linux]
libc: [musl]
'@unrs/resolver-binding-wasm32-wasi@1.11.1': '@unrs/resolver-binding-wasm32-wasi@1.11.1':
resolution: {integrity: sha512-5u4RkfxJm+Ng7IWgkzi3qrFOvLvQYnPBmjmZQ8+szTK/b31fQCnleNl1GgEt7nIsZRIf5PLhPwT0WM+q45x/UQ==} resolution: {integrity: sha512-5u4RkfxJm+Ng7IWgkzi3qrFOvLvQYnPBmjmZQ8+szTK/b31fQCnleNl1GgEt7nIsZRIf5PLhPwT0WM+q45x/UQ==}
@@ -3301,6 +3356,10 @@ packages:
engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7}
hasBin: true hasBin: true
builtin-modules@3.3.0:
resolution: {integrity: sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw==}
engines: {node: '>=6'}
bundle-name@4.1.0: bundle-name@4.1.0:
resolution: {integrity: sha512-tjwM5exMg6BGRI+kNmTntNsvdZS1X8BFYS6tnJ2hdH0kVxM6/eVZ2xy+FqStSWvYmtfFMDLIxurorHwDKfDz5Q==} resolution: {integrity: sha512-tjwM5exMg6BGRI+kNmTntNsvdZS1X8BFYS6tnJ2hdH0kVxM6/eVZ2xy+FqStSWvYmtfFMDLIxurorHwDKfDz5Q==}
engines: {node: '>=18'} engines: {node: '>=18'}
@@ -3995,6 +4054,20 @@ packages:
peerDependencies: peerDependencies:
eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9.7 eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9.7
eslint-plugin-sonarjs@3.0.7:
resolution: {integrity: sha512-62jB20krIPvcwBLAyG3VVKa2ce2j2lL1yCb8Y0ylMRR/dLvCCTiQx8gQbXb+G81k1alPZ2/I3muZinqWQdBbzw==}
peerDependencies:
eslint: ^8.0.0 || ^9.0.0
eslint-plugin-unused-imports@4.4.1:
resolution: {integrity: sha512-oZGYUz1X3sRMGUB+0cZyK2VcvRX5lm/vB56PgNNcU+7ficUCKm66oZWKUubXWnOuPjQ8PvmXtCViXBMONPe7tQ==}
peerDependencies:
'@typescript-eslint/eslint-plugin': ^8.0.0-0 || ^7.0.0 || ^6.0.0 || ^5.0.0
eslint: ^10.0.0 || ^9.0.0 || ^8.0.0
peerDependenciesMeta:
'@typescript-eslint/eslint-plugin':
optional: true
eslint-scope@8.4.0: eslint-scope@8.4.0:
resolution: {integrity: sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==} resolution: {integrity: sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
@@ -4187,6 +4260,9 @@ packages:
resolution: {integrity: sha512-e5iwyodOHhbMr/yNrc7fDYG4qlbIvI5gajyzPnb5TCwyhjApznQh1BMFou9b30SevY43gCJKXycoCBjMbsuW0Q==} resolution: {integrity: sha512-e5iwyodOHhbMr/yNrc7fDYG4qlbIvI5gajyzPnb5TCwyhjApznQh1BMFou9b30SevY43gCJKXycoCBjMbsuW0Q==}
engines: {node: '>= 0.4'} engines: {node: '>= 0.4'}
functional-red-black-tree@1.0.1:
resolution: {integrity: sha512-dsKNQNdj6xA3T+QlADDA7mOSlX0qiMINjn0cgr+eGHGsbSHzTabcIogz2+p/iqP1Xs6EP/sS2SbqH+brGTbq0g==}
functions-have-names@1.2.3: functions-have-names@1.2.3:
resolution: {integrity: sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==} resolution: {integrity: sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==}
@@ -4669,6 +4745,10 @@ packages:
jsonfile@6.2.0: jsonfile@6.2.0:
resolution: {integrity: sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==} resolution: {integrity: sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==}
jsx-ast-utils-x@0.1.0:
resolution: {integrity: sha512-eQQBjBnsVtGacsG9uJNB8qOr3yA8rga4wAaGG1qRcBzSIvfhERLrWxMAM1hp5fcS6Abo8M4+bUBTekYR0qTPQw==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
jsx-ast-utils@3.3.5: jsx-ast-utils@3.3.5:
resolution: {integrity: sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==} resolution: {integrity: sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==}
engines: {node: '>=4.0'} engines: {node: '>=4.0'}
@@ -4747,24 +4827,28 @@ packages:
engines: {node: '>= 12.0.0'} engines: {node: '>= 12.0.0'}
cpu: [arm64] cpu: [arm64]
os: [linux] os: [linux]
libc: [glibc]
lightningcss-linux-arm64-musl@1.30.2: lightningcss-linux-arm64-musl@1.30.2:
resolution: {integrity: sha512-5Vh9dGeblpTxWHpOx8iauV02popZDsCYMPIgiuw97OJ5uaDsL86cnqSFs5LZkG3ghHoX5isLgWzMs+eD1YzrnA==} resolution: {integrity: sha512-5Vh9dGeblpTxWHpOx8iauV02popZDsCYMPIgiuw97OJ5uaDsL86cnqSFs5LZkG3ghHoX5isLgWzMs+eD1YzrnA==}
engines: {node: '>= 12.0.0'} engines: {node: '>= 12.0.0'}
cpu: [arm64] cpu: [arm64]
os: [linux] os: [linux]
libc: [musl]
lightningcss-linux-x64-gnu@1.30.2: lightningcss-linux-x64-gnu@1.30.2:
resolution: {integrity: sha512-Cfd46gdmj1vQ+lR6VRTTadNHu6ALuw2pKR9lYq4FnhvgBc4zWY1EtZcAc6EffShbb1MFrIPfLDXD6Xprbnni4w==} resolution: {integrity: sha512-Cfd46gdmj1vQ+lR6VRTTadNHu6ALuw2pKR9lYq4FnhvgBc4zWY1EtZcAc6EffShbb1MFrIPfLDXD6Xprbnni4w==}
engines: {node: '>= 12.0.0'} engines: {node: '>= 12.0.0'}
cpu: [x64] cpu: [x64]
os: [linux] os: [linux]
libc: [glibc]
lightningcss-linux-x64-musl@1.30.2: lightningcss-linux-x64-musl@1.30.2:
resolution: {integrity: sha512-XJaLUUFXb6/QG2lGIW6aIk6jKdtjtcffUT0NKvIqhSBY3hh9Ch+1LCeH80dR9q9LBjG3ewbDjnumefsLsP6aiA==} resolution: {integrity: sha512-XJaLUUFXb6/QG2lGIW6aIk6jKdtjtcffUT0NKvIqhSBY3hh9Ch+1LCeH80dR9q9LBjG3ewbDjnumefsLsP6aiA==}
engines: {node: '>= 12.0.0'} engines: {node: '>= 12.0.0'}
cpu: [x64] cpu: [x64]
os: [linux] os: [linux]
libc: [musl]
lightningcss-win32-arm64-msvc@1.30.2: lightningcss-win32-arm64-msvc@1.30.2:
resolution: {integrity: sha512-FZn+vaj7zLv//D/192WFFVA0RgHawIcHqLX9xuWiQt7P0PtdFEVaxgF9rjM/IRYHQXNnk61/H/gb2Ei+kUQ4xQ==} resolution: {integrity: sha512-FZn+vaj7zLv//D/192WFFVA0RgHawIcHqLX9xuWiQt7P0PtdFEVaxgF9rjM/IRYHQXNnk61/H/gb2Ei+kUQ4xQ==}
@@ -5042,6 +5126,10 @@ packages:
resolution: {integrity: sha512-enIvLvRAFZYXJzkCYG5RKmPfrFArdLv+R+lbQ53BmIMLIry74bjKzX6iHAm8WYamJkhSSEabrWN5D97XnKObjQ==} resolution: {integrity: sha512-enIvLvRAFZYXJzkCYG5RKmPfrFArdLv+R+lbQ53BmIMLIry74bjKzX6iHAm8WYamJkhSSEabrWN5D97XnKObjQ==}
engines: {node: 20 || >=22} engines: {node: 20 || >=22}
minimatch@10.1.2:
resolution: {integrity: sha512-fu656aJ0n2kcXwsnwnv9g24tkU5uSmOlTjd6WyyaKm2Z+h1qmY6bAjrcaIxF/BslFqbZ8UBtbJi7KgQOZD2PTw==}
engines: {node: 20 || >=22}
minimatch@3.1.2: minimatch@3.1.2:
resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==}
@@ -5557,6 +5645,10 @@ packages:
react: ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 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 react-dom: ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0
refa@0.12.1:
resolution: {integrity: sha512-J8rn6v4DBb2nnFqkqwy6/NnTYMcgLA+sLr0iIO41qpv0n+ngb7ksag2tMRl0inb1bbO/esUwzW1vbJi7K0sI0g==}
engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0}
reflect.getprototypeof@1.0.10: reflect.getprototypeof@1.0.10:
resolution: {integrity: sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw==} resolution: {integrity: sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw==}
engines: {node: '>= 0.4'} engines: {node: '>= 0.4'}
@@ -5570,6 +5662,10 @@ packages:
regex@6.1.0: regex@6.1.0:
resolution: {integrity: sha512-6VwtthbV4o/7+OaAF9I5L5V3llLEsoPyq9P1JVXkedTP33c7MfCG0/5NOPcSJn0TzXcG9YUrR0gQSWioew3LDg==} resolution: {integrity: sha512-6VwtthbV4o/7+OaAF9I5L5V3llLEsoPyq9P1JVXkedTP33c7MfCG0/5NOPcSJn0TzXcG9YUrR0gQSWioew3LDg==}
regexp-ast-analysis@0.7.1:
resolution: {integrity: sha512-sZuz1dYW/ZsfG17WSAG7eS85r5a0dDsvg+7BiiYR5o6lKCAtUrEwdmRmaGF6rwVj3LcmAeYkOWKEPlbPzN3Y3A==}
engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0}
regexp.prototype.flags@1.5.4: regexp.prototype.flags@1.5.4:
resolution: {integrity: sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==} resolution: {integrity: sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==}
engines: {node: '>= 0.4'} engines: {node: '>= 0.4'}
@@ -5673,6 +5769,10 @@ packages:
scheduler@0.27.0: scheduler@0.27.0:
resolution: {integrity: sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==} resolution: {integrity: sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==}
scslre@0.3.0:
resolution: {integrity: sha512-3A6sD0WYP7+QrjbfNA2FN3FsOaGGFoekCVgTyypy53gPxhbkCIjtO6YWgdrfM+n/8sI8JeXZOIxsHjMTNxQ4nQ==}
engines: {node: ^14.0.0 || >=16.0.0}
semver@6.3.1: semver@6.3.1:
resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==}
hasBin: true hasBin: true
@@ -5682,6 +5782,11 @@ packages:
engines: {node: '>=10'} engines: {node: '>=10'}
hasBin: true hasBin: true
semver@7.7.4:
resolution: {integrity: sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==}
engines: {node: '>=10'}
hasBin: true
send@1.2.1: send@1.2.1:
resolution: {integrity: sha512-1gnZf7DFcoIcajTjTwjwuDjzuz4PPcY2StKPlsGAQ1+YH20IRVrBaXSWmdjowTJ6u8Rc01PoYOGHXfP1mYcZNQ==} resolution: {integrity: sha512-1gnZf7DFcoIcajTjTwjwuDjzuz4PPcY2StKPlsGAQ1+YH20IRVrBaXSWmdjowTJ6u8Rc01PoYOGHXfP1mYcZNQ==}
engines: {node: '>= 18'} engines: {node: '>= 18'}
@@ -7204,6 +7309,10 @@ snapshots:
dependencies: dependencies:
'@isaacs/balanced-match': 4.0.1 '@isaacs/balanced-match': 4.0.1
'@isaacs/brace-expansion@5.0.1':
dependencies:
'@isaacs/balanced-match': 4.0.1
'@isaacs/fs-minipass@4.0.1': '@isaacs/fs-minipass@4.0.1':
dependencies: dependencies:
minipass: 7.1.2 minipass: 7.1.2
@@ -9294,6 +9403,8 @@ snapshots:
node-releases: 2.0.27 node-releases: 2.0.27
update-browserslist-db: 1.2.3(browserslist@4.28.1) update-browserslist-db: 1.2.3(browserslist@4.28.1)
builtin-modules@3.3.0: {}
bundle-name@4.1.0: bundle-name@4.1.0:
dependencies: dependencies:
run-applescript: 7.1.0 run-applescript: 7.1.0
@@ -10014,8 +10125,8 @@ snapshots:
'@next/eslint-plugin-next': 16.1.1 '@next/eslint-plugin-next': 16.1.1
eslint: 9.39.2(jiti@2.6.1) eslint: 9.39.2(jiti@2.6.1)
eslint-import-resolver-node: 0.3.9 eslint-import-resolver-node: 0.3.9
eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.53.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.2(jiti@2.6.1)))(eslint@9.39.2(jiti@2.6.1)) eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0)(eslint@9.39.2(jiti@2.6.1))
eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.53.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.53.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.2(jiti@2.6.1)))(eslint@9.39.2(jiti@2.6.1)))(eslint@9.39.2(jiti@2.6.1)) eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.53.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.2(jiti@2.6.1))
eslint-plugin-jsx-a11y: 6.10.2(eslint@9.39.2(jiti@2.6.1)) eslint-plugin-jsx-a11y: 6.10.2(eslint@9.39.2(jiti@2.6.1))
eslint-plugin-react: 7.37.5(eslint@9.39.2(jiti@2.6.1)) eslint-plugin-react: 7.37.5(eslint@9.39.2(jiti@2.6.1))
eslint-plugin-react-hooks: 7.0.1(eslint@9.39.2(jiti@2.6.1)) eslint-plugin-react-hooks: 7.0.1(eslint@9.39.2(jiti@2.6.1))
@@ -10037,7 +10148,7 @@ snapshots:
transitivePeerDependencies: transitivePeerDependencies:
- supports-color - supports-color
eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.53.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.2(jiti@2.6.1)))(eslint@9.39.2(jiti@2.6.1)): eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0)(eslint@9.39.2(jiti@2.6.1)):
dependencies: dependencies:
'@nolyfill/is-core-module': 1.0.39 '@nolyfill/is-core-module': 1.0.39
debug: 4.4.3 debug: 4.4.3
@@ -10048,22 +10159,22 @@ snapshots:
tinyglobby: 0.2.15 tinyglobby: 0.2.15
unrs-resolver: 1.11.1 unrs-resolver: 1.11.1
optionalDependencies: optionalDependencies:
eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.53.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.53.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.2(jiti@2.6.1)))(eslint@9.39.2(jiti@2.6.1)))(eslint@9.39.2(jiti@2.6.1)) eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.53.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.2(jiti@2.6.1))
transitivePeerDependencies: transitivePeerDependencies:
- supports-color - supports-color
eslint-module-utils@2.12.1(@typescript-eslint/parser@8.53.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.53.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.2(jiti@2.6.1)))(eslint@9.39.2(jiti@2.6.1)))(eslint@9.39.2(jiti@2.6.1)): eslint-module-utils@2.12.1(@typescript-eslint/parser@8.53.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.2(jiti@2.6.1)):
dependencies: dependencies:
debug: 3.2.7 debug: 3.2.7
optionalDependencies: optionalDependencies:
'@typescript-eslint/parser': 8.53.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3) '@typescript-eslint/parser': 8.53.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3)
eslint: 9.39.2(jiti@2.6.1) eslint: 9.39.2(jiti@2.6.1)
eslint-import-resolver-node: 0.3.9 eslint-import-resolver-node: 0.3.9
eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.53.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.2(jiti@2.6.1)))(eslint@9.39.2(jiti@2.6.1)) eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0)(eslint@9.39.2(jiti@2.6.1))
transitivePeerDependencies: transitivePeerDependencies:
- supports-color - supports-color
eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.53.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.53.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.2(jiti@2.6.1)))(eslint@9.39.2(jiti@2.6.1)))(eslint@9.39.2(jiti@2.6.1)): eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.53.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.2(jiti@2.6.1)):
dependencies: dependencies:
'@rtsao/scc': 1.1.0 '@rtsao/scc': 1.1.0
array-includes: 3.1.9 array-includes: 3.1.9
@@ -10074,7 +10185,7 @@ snapshots:
doctrine: 2.1.0 doctrine: 2.1.0
eslint: 9.39.2(jiti@2.6.1) eslint: 9.39.2(jiti@2.6.1)
eslint-import-resolver-node: 0.3.9 eslint-import-resolver-node: 0.3.9
eslint-module-utils: 2.12.1(@typescript-eslint/parser@8.53.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.53.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.2(jiti@2.6.1)))(eslint@9.39.2(jiti@2.6.1)))(eslint@9.39.2(jiti@2.6.1)) eslint-module-utils: 2.12.1(@typescript-eslint/parser@8.53.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.2(jiti@2.6.1))
hasown: 2.0.2 hasown: 2.0.2
is-core-module: 2.16.1 is-core-module: 2.16.1
is-glob: 4.0.3 is-glob: 4.0.3
@@ -10144,6 +10255,26 @@ snapshots:
string.prototype.matchall: 4.0.12 string.prototype.matchall: 4.0.12
string.prototype.repeat: 1.0.0 string.prototype.repeat: 1.0.0
eslint-plugin-sonarjs@3.0.7(eslint@9.39.2(jiti@2.6.1)):
dependencies:
'@eslint-community/regexpp': 4.12.2
builtin-modules: 3.3.0
bytes: 3.1.2
eslint: 9.39.2(jiti@2.6.1)
functional-red-black-tree: 1.0.1
jsx-ast-utils-x: 0.1.0
lodash.merge: 4.6.2
minimatch: 10.1.2
scslre: 0.3.0
semver: 7.7.4
typescript: 5.9.3
eslint-plugin-unused-imports@4.4.1(@typescript-eslint/eslint-plugin@8.53.0(@typescript-eslint/parser@8.53.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.2(jiti@2.6.1)):
dependencies:
eslint: 9.39.2(jiti@2.6.1)
optionalDependencies:
'@typescript-eslint/eslint-plugin': 8.53.0(@typescript-eslint/parser@8.53.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3)
eslint-scope@8.4.0: eslint-scope@8.4.0:
dependencies: dependencies:
esrecurse: 4.3.0 esrecurse: 4.3.0
@@ -10414,6 +10545,8 @@ snapshots:
hasown: 2.0.2 hasown: 2.0.2
is-callable: 1.2.7 is-callable: 1.2.7
functional-red-black-tree@1.0.1: {}
functions-have-names@1.2.3: {} functions-have-names@1.2.3: {}
fuzzysort@3.1.0: {} fuzzysort@3.1.0: {}
@@ -10860,6 +10993,8 @@ snapshots:
optionalDependencies: optionalDependencies:
graceful-fs: 4.2.11 graceful-fs: 4.2.11
jsx-ast-utils-x@0.1.0: {}
jsx-ast-utils@3.3.5: jsx-ast-utils@3.3.5:
dependencies: dependencies:
array-includes: 3.1.9 array-includes: 3.1.9
@@ -11431,6 +11566,10 @@ snapshots:
dependencies: dependencies:
'@isaacs/brace-expansion': 5.0.0 '@isaacs/brace-expansion': 5.0.0
minimatch@10.1.2:
dependencies:
'@isaacs/brace-expansion': 5.0.1
minimatch@3.1.2: minimatch@3.1.2:
dependencies: dependencies:
brace-expansion: 1.1.12 brace-expansion: 1.1.12
@@ -11501,7 +11640,7 @@ snapshots:
react: 19.2.3 react: 19.2.3
react-dom: 19.2.3(react@19.2.3) react-dom: 19.2.3(react@19.2.3)
next@16.1.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3): next@16.1.1(@babel/core@7.28.6)(react-dom@19.2.3(react@19.2.3))(react@19.2.3):
dependencies: dependencies:
'@next/env': 16.1.1 '@next/env': 16.1.1
'@swc/helpers': 0.5.15 '@swc/helpers': 0.5.15
@@ -11510,7 +11649,7 @@ snapshots:
postcss: 8.4.31 postcss: 8.4.31
react: 19.2.3 react: 19.2.3
react-dom: 19.2.3(react@19.2.3) react-dom: 19.2.3(react@19.2.3)
styled-jsx: 5.1.6(react@19.2.3) styled-jsx: 5.1.6(@babel/core@7.28.6)(react@19.2.3)
optionalDependencies: optionalDependencies:
'@next/swc-darwin-arm64': 16.1.1 '@next/swc-darwin-arm64': 16.1.1
'@next/swc-darwin-x64': 16.1.1 '@next/swc-darwin-x64': 16.1.1
@@ -12088,6 +12227,10 @@ snapshots:
tiny-invariant: 1.3.3 tiny-invariant: 1.3.3
victory-vendor: 36.9.2 victory-vendor: 36.9.2
refa@0.12.1:
dependencies:
'@eslint-community/regexpp': 4.12.2
reflect.getprototypeof@1.0.10: reflect.getprototypeof@1.0.10:
dependencies: dependencies:
call-bind: 1.0.8 call-bind: 1.0.8
@@ -12109,6 +12252,11 @@ snapshots:
dependencies: dependencies:
regex-utilities: 2.3.0 regex-utilities: 2.3.0
regexp-ast-analysis@0.7.1:
dependencies:
'@eslint-community/regexpp': 4.12.2
refa: 0.12.1
regexp.prototype.flags@1.5.4: regexp.prototype.flags@1.5.4:
dependencies: dependencies:
call-bind: 1.0.8 call-bind: 1.0.8
@@ -12272,10 +12420,18 @@ snapshots:
scheduler@0.27.0: {} scheduler@0.27.0: {}
scslre@0.3.0:
dependencies:
'@eslint-community/regexpp': 4.12.2
refa: 0.12.1
regexp-ast-analysis: 0.7.1
semver@6.3.1: {} semver@6.3.1: {}
semver@7.7.3: {} semver@7.7.3: {}
semver@7.7.4: {}
send@1.2.1: send@1.2.1:
dependencies: dependencies:
debug: 4.4.3 debug: 4.4.3
@@ -12589,10 +12745,12 @@ snapshots:
dependencies: dependencies:
inline-style-parser: 0.2.7 inline-style-parser: 0.2.7
styled-jsx@5.1.6(react@19.2.3): styled-jsx@5.1.6(@babel/core@7.28.6)(react@19.2.3):
dependencies: dependencies:
client-only: 0.0.1 client-only: 0.0.1
react: 19.2.3 react: 19.2.3
optionalDependencies:
'@babel/core': 7.28.6
stylis@4.3.6: {} stylis@4.3.6: {}
+203 -181
View File
@@ -16,11 +16,18 @@ import { IconChevronDown, IconChevronRight, IconRefresh } from '@tabler/icons-re
type ToolFilter = 'all' | 'tool_edit' | 'tool_read' | 'tool_execute' | 'tool_call'; type ToolFilter = 'all' | 'tool_edit' | 'tool_read' | 'tool_execute' | 'tool_call';
type ViewMode = 'fixtures' | 'events'; type ViewMode = 'fixtures' | 'events';
const TOOL_TABS: { value: ToolFilter; label: string }[] = [
{ value: 'all', label: 'All' },
{ value: 'tool_edit', label: 'Edit' },
{ value: 'tool_read', label: 'Read' },
{ value: 'tool_execute', label: 'Execute' },
{ value: 'tool_call', label: 'Call' },
];
function fixtureToMessage(fixture: NormalizedFixture, index: number): Message { function fixtureToMessage(fixture: NormalizedFixture, index: number): Message {
const payload = fixture.payload as Record<string, unknown>; const payload = fixture.payload as Record<string, unknown>;
const toolType = fixture.tool_type as MessageType; const toolType = fixture.tool_type as MessageType;
// Build content based on tool type
let content = ''; let content = '';
if (payload.file_path) { if (payload.file_path) {
content = `${fixture.tool_type}: ${payload.file_path}`; content = `${fixture.tool_type}: ${payload.file_path}`;
@@ -46,6 +53,102 @@ function fixtureToMessage(fixture: NormalizedFixture, index: number): Message {
}; };
} }
// --- Hooks ---
function useAgentMessages() {
const [viewMode, setViewMode] = useState<ViewMode>('events');
const [fixtureFiles, setFixtureFiles] = useState<DiscoveredFile[]>([]);
const [selectedFixtureFile, setSelectedFixtureFile] = useState<string>('');
const [fixtures, setFixtures] = useState<NormalizedFixture[]>([]);
const [eventFiles, setEventFiles] = useState<DiscoveredFile[]>([]);
const [selectedEventFile, setSelectedEventFile] = useState<string>('');
const [messages, setMessages] = useState<Message[]>([]);
const [loading, setLoading] = useState(true);
const [error, setError] = useState<string | null>(null);
const [toolFilter, setToolFilter] = useState<ToolFilter>('all');
useEffect(() => {
async function loadFiles() {
try {
const [fixtureFilesData, eventFilesData] = await Promise.all([
fetchFixtureFiles(),
fetchNormalizedFiles(),
]);
setFixtureFiles(fixtureFilesData);
setEventFiles(eventFilesData);
if (eventFilesData.length > 0 && !selectedEventFile) {
setSelectedEventFile(eventFilesData[0].path);
}
if (fixtureFilesData.length > 0 && !selectedFixtureFile) {
setSelectedFixtureFile(fixtureFilesData[0].path);
}
} catch (err) {
setError(err instanceof Error ? err.message : 'Failed to load files');
} finally {
setLoading(false);
}
}
loadFiles();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
const loadFixtures = useCallback(async () => {
if (!selectedFixtureFile) return;
setLoading(true);
setError(null);
try {
const data = await fetchNormalizedMessages(selectedFixtureFile);
setFixtures(data);
} catch (err) {
setError(err instanceof Error ? err.message : 'Failed to load fixtures');
} finally {
setLoading(false);
}
}, [selectedFixtureFile]);
const loadMessages = useCallback(async () => {
if (!selectedEventFile) return;
setLoading(true);
setError(null);
try {
const data = await fetchNormalizedEventsAsMessages(selectedEventFile);
setMessages(data);
} catch (err) {
setError(err instanceof Error ? err.message : 'Failed to load messages');
} finally {
setLoading(false);
}
}, [selectedEventFile]);
useEffect(() => {
if (viewMode === 'fixtures' && selectedFixtureFile) {
loadFixtures();
} else if (viewMode === 'events' && selectedEventFile) {
loadMessages();
}
}, [viewMode, selectedFixtureFile, selectedEventFile, loadFixtures, loadMessages]);
const filteredFixtures = fixtures.filter((f) => toolFilter === 'all' || f.tool_type === toolFilter);
const currentFiles = viewMode === 'fixtures' ? fixtureFiles : eventFiles;
const selectedFile = viewMode === 'fixtures' ? selectedFixtureFile : selectedEventFile;
const setSelectedFile = viewMode === 'fixtures' ? setSelectedFixtureFile : setSelectedEventFile;
const loadContent = viewMode === 'fixtures' ? loadFixtures : loadMessages;
const itemCount = viewMode === 'fixtures' ? filteredFixtures.length : messages.length;
const totalCount = viewMode === 'fixtures' ? fixtures.length : messages.length;
return {
viewMode, setViewMode,
loading, error,
toolFilter, setToolFilter,
currentFiles, selectedFile, setSelectedFile,
loadContent,
filteredFixtures, messages,
itemCount, totalCount,
};
}
// --- UI Components ---
function JsonPanel({ title, data, defaultExpanded = false }: { title: string; data: unknown; defaultExpanded?: boolean }) { function JsonPanel({ title, data, defaultExpanded = false }: { title: string; data: unknown; defaultExpanded?: boolean }) {
const [expanded, setExpanded] = useState(defaultExpanded); const [expanded, setExpanded] = useState(defaultExpanded);
@@ -72,7 +175,6 @@ function FixtureCard({ fixture, index }: { fixture: NormalizedFixture; index: nu
return ( return (
<div className="border rounded-lg overflow-hidden bg-card"> <div className="border rounded-lg overflow-hidden bg-card">
{/* Header */}
<div className="px-4 py-2 bg-muted/30 border-b flex items-center justify-between"> <div className="px-4 py-2 bg-muted/30 border-b flex items-center justify-between">
<div className="flex items-center gap-3"> <div className="flex items-center gap-3">
<span className="text-xs font-mono px-2 py-0.5 rounded bg-primary/10 text-primary"> <span className="text-xs font-mono px-2 py-0.5 rounded bg-primary/10 text-primary">
@@ -86,18 +188,10 @@ function FixtureCard({ fixture, index }: { fixture: NormalizedFixture; index: nu
</span> </span>
</div> </div>
</div> </div>
{/* Rendered Message */}
<div className="p-4 border-b"> <div className="p-4 border-b">
<div className="text-xs text-muted-foreground mb-2 font-medium">Rendered Output:</div> <div className="text-xs text-muted-foreground mb-2 font-medium">Rendered Output:</div>
<MessageRenderer <MessageRenderer comment={message} isTaskDescription={false} taskId="demo-task" />
comment={message}
isTaskDescription={false}
taskId="demo-task"
/>
</div> </div>
{/* JSON Panels */}
<div className="p-4 space-y-2"> <div className="p-4 space-y-2">
<div className="grid grid-cols-1 md:grid-cols-2 gap-2"> <div className="grid grid-cols-1 md:grid-cols-2 gap-2">
<JsonPanel title="Raw Input" data={fixture.input} /> <JsonPanel title="Raw Input" data={fixture.input} />
@@ -114,7 +208,6 @@ function MessageCard({ message }: { message: Message }) {
return ( return (
<div className="border rounded-lg overflow-hidden bg-card"> <div className="border rounded-lg overflow-hidden bg-card">
{/* Header */}
<div className="px-4 py-2 bg-muted/30 border-b flex items-center justify-between"> <div className="px-4 py-2 bg-muted/30 border-b flex items-center justify-between">
<div className="flex items-center gap-3"> <div className="flex items-center gap-3">
<span className="text-xs font-mono px-2 py-0.5 rounded bg-primary/10 text-primary"> <span className="text-xs font-mono px-2 py-0.5 rounded bg-primary/10 text-primary">
@@ -132,8 +225,6 @@ function MessageCard({ message }: { message: Message }) {
)} )}
</div> </div>
</div> </div>
{/* Rendered Message */}
<div className="p-4 border-b"> <div className="p-4 border-b">
<div className="text-xs text-muted-foreground mb-2 font-medium">Rendered Output:</div> <div className="text-xs text-muted-foreground mb-2 font-medium">Rendered Output:</div>
<MessageRenderer <MessageRenderer
@@ -142,8 +233,6 @@ function MessageCard({ message }: { message: Message }) {
taskId={message.task_id || 'demo-task'} taskId={message.task_id || 'demo-task'}
/> />
</div> </div>
{/* JSON Panel */}
<div className="p-4"> <div className="p-4">
<JsonPanel title="Message Data" data={message} /> <JsonPanel title="Message Data" data={message} />
</div> </div>
@@ -151,163 +240,40 @@ function MessageCard({ message }: { message: Message }) {
); );
} }
export default function AgentMessagesPage() { function ViewModeTabs({ viewMode, setViewMode }: { viewMode: ViewMode; setViewMode: (v: ViewMode) => void }) {
const [viewMode, setViewMode] = useState<ViewMode>('events');
// Fixture mode state
const [fixtureFiles, setFixtureFiles] = useState<DiscoveredFile[]>([]);
const [selectedFixtureFile, setSelectedFixtureFile] = useState<string>('');
const [fixtures, setFixtures] = useState<NormalizedFixture[]>([]);
// Events mode state
const [eventFiles, setEventFiles] = useState<DiscoveredFile[]>([]);
const [selectedEventFile, setSelectedEventFile] = useState<string>('');
const [messages, setMessages] = useState<Message[]>([]);
// Shared state
const [loading, setLoading] = useState(true);
const [error, setError] = useState<string | null>(null);
const [toolFilter, setToolFilter] = useState<ToolFilter>('all');
// Load available files on mount
useEffect(() => {
async function loadFiles() {
try {
// Load both fixture files and normalized event files
const [fixtureFilesData, eventFilesData] = await Promise.all([
fetchFixtureFiles(),
fetchNormalizedFiles(),
]);
setFixtureFiles(fixtureFilesData);
setEventFiles(eventFilesData);
// Auto-select first file based on current mode
if (eventFilesData.length > 0 && !selectedEventFile) {
setSelectedEventFile(eventFilesData[0].path);
}
if (fixtureFilesData.length > 0 && !selectedFixtureFile) {
setSelectedFixtureFile(fixtureFilesData[0].path);
}
} catch (err) {
setError(err instanceof Error ? err.message : 'Failed to load files');
} finally {
setLoading(false);
}
}
loadFiles();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
const loadFixtures = useCallback(async () => {
if (!selectedFixtureFile) return;
setLoading(true);
setError(null);
try {
const data = await fetchNormalizedMessages(selectedFixtureFile);
setFixtures(data);
} catch (err) {
setError(err instanceof Error ? err.message : 'Failed to load fixtures');
} finally {
setLoading(false);
}
}, [selectedFixtureFile]);
const loadMessages = useCallback(async () => {
if (!selectedEventFile) return;
setLoading(true);
setError(null);
try {
const data = await fetchNormalizedEventsAsMessages(selectedEventFile);
setMessages(data);
} catch (err) {
setError(err instanceof Error ? err.message : 'Failed to load messages');
} finally {
setLoading(false);
}
}, [selectedEventFile]);
// Load content when selected file changes
useEffect(() => {
if (viewMode === 'fixtures' && selectedFixtureFile) {
loadFixtures();
} else if (viewMode === 'events' && selectedEventFile) {
loadMessages();
}
}, [viewMode, selectedFixtureFile, selectedEventFile, loadFixtures, loadMessages]);
const filteredFixtures = fixtures.filter((f) => {
if (toolFilter === 'all') return true;
return f.tool_type === toolFilter;
});
const currentFiles = viewMode === 'fixtures' ? fixtureFiles : eventFiles;
const selectedFile = viewMode === 'fixtures' ? selectedFixtureFile : selectedEventFile;
const setSelectedFile = viewMode === 'fixtures' ? setSelectedFixtureFile : setSelectedEventFile;
const loadContent = viewMode === 'fixtures' ? loadFixtures : loadMessages;
const toolTabs: { value: ToolFilter; label: string }[] = [
{ value: 'all', label: 'All' },
{ value: 'tool_edit', label: 'Edit' },
{ value: 'tool_read', label: 'Read' },
{ value: 'tool_execute', label: 'Execute' },
{ value: 'tool_call', label: 'Call' },
];
const itemCount = viewMode === 'fixtures' ? filteredFixtures.length : messages.length;
const totalCount = viewMode === 'fixtures' ? fixtures.length : messages.length;
return ( return (
<div className="min-h-screen bg-background p-8">
<div className="max-w-5xl mx-auto">
{/* Header */}
<div className="mb-8">
<div className="flex items-center justify-between mb-2">
<h1 className="text-2xl font-bold">Agent Messages QA</h1>
<button
onClick={loadContent}
disabled={loading || !selectedFile}
className="flex items-center gap-2 px-3 py-1.5 text-sm rounded-md border hover:bg-muted/50 disabled:opacity-50 transition-colors"
>
<IconRefresh className={`h-4 w-4 ${loading ? 'animate-spin' : ''}`} />
Refresh
</button>
</div>
<p className="text-muted-foreground">
Visual inspection of agent message normalization. Each card shows the rendered output alongside raw JSON.
</p>
</div>
{/* View Mode Tabs */}
<div className="mb-6"> <div className="mb-6">
<div className="flex gap-1 p-1 bg-muted/30 rounded-lg w-fit"> <div className="flex gap-1 p-1 bg-muted/30 rounded-lg w-fit">
{(['events', 'fixtures'] as const).map((mode) => (
<button <button
onClick={() => setViewMode('events')} key={mode}
onClick={() => setViewMode(mode)}
className={`px-4 py-2 text-sm rounded-md transition-colors ${ className={`px-4 py-2 text-sm rounded-md transition-colors ${
viewMode === 'events' viewMode === mode
? 'bg-background shadow-sm font-medium' ? 'bg-background shadow-sm font-medium'
: 'hover:bg-muted/50 text-muted-foreground' : 'hover:bg-muted/50 text-muted-foreground'
}`} }`}
> >
Normalized Events {mode === 'events' ? 'Normalized Events' : 'Test Fixtures'}
</button>
<button
onClick={() => setViewMode('fixtures')}
className={`px-4 py-2 text-sm rounded-md transition-colors ${
viewMode === 'fixtures'
? 'bg-background shadow-sm font-medium'
: 'hover:bg-muted/50 text-muted-foreground'
}`}
>
Test Fixtures
</button> </button>
))}
</div> </div>
</div> </div>
);
}
{/* Filters */} function FiltersBar({
viewMode, currentFiles, selectedFile, setSelectedFile, toolFilter, setToolFilter,
}: {
viewMode: ViewMode;
currentFiles: DiscoveredFile[];
selectedFile: string;
setSelectedFile: (v: string) => void;
toolFilter: ToolFilter;
setToolFilter: (v: ToolFilter) => void;
}) {
return (
<div className="mb-6 space-y-4"> <div className="mb-6 space-y-4">
{/* File Selector */}
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
<span className="text-sm font-medium text-muted-foreground"> <span className="text-sm font-medium text-muted-foreground">
{viewMode === 'events' ? 'Event File:' : 'Fixture File:'} {viewMode === 'events' ? 'Event File:' : 'Fixture File:'}
@@ -317,9 +283,7 @@ export default function AgentMessagesPage() {
onChange={(e) => setSelectedFile(e.target.value)} onChange={(e) => setSelectedFile(e.target.value)}
className="px-3 py-1.5 text-sm rounded-md border bg-background hover:bg-muted/50 transition-colors min-w-[300px]" className="px-3 py-1.5 text-sm rounded-md border bg-background hover:bg-muted/50 transition-colors min-w-[300px]"
> >
{currentFiles.length === 0 && ( {currentFiles.length === 0 && <option value="">No files found</option>}
<option value="">No files found</option>
)}
{currentFiles.map((file) => ( {currentFiles.map((file) => (
<option key={file.path} value={file.path}> <option key={file.path} value={file.path}>
{file.protocol} - {file.agent || 'unknown'} ({file.message_count} messages) {file.protocol} - {file.agent || 'unknown'} ({file.message_count} messages)
@@ -327,13 +291,11 @@ export default function AgentMessagesPage() {
))} ))}
</select> </select>
</div> </div>
{/* Tool Filter - only show for fixtures mode */}
{viewMode === 'fixtures' && ( {viewMode === 'fixtures' && (
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
<span className="text-sm font-medium text-muted-foreground">Tool Type:</span> <span className="text-sm font-medium text-muted-foreground">Tool Type:</span>
<div className="flex gap-1"> <div className="flex gap-1">
{toolTabs.map((tab) => ( {TOOL_TABS.map((tab) => (
<button <button
key={tab.value} key={tab.value}
onClick={() => setToolFilter(tab.value)} onClick={() => setToolFilter(tab.value)}
@@ -350,49 +312,109 @@ export default function AgentMessagesPage() {
</div> </div>
)} )}
</div> </div>
);
}
{/* Stats */} function ContentArea({
<div className="mb-6 text-sm text-muted-foreground"> viewMode, loading, error, itemCount, filteredFixtures, messages,
Showing {itemCount} of {totalCount} {viewMode === 'events' ? 'messages' : 'fixtures'} }: {
</div> viewMode: ViewMode;
loading: boolean;
{/* Content */} error: string | null;
{loading && ( itemCount: number;
filteredFixtures: NormalizedFixture[];
messages: Message[];
}) {
if (loading) {
return (
<div className="flex items-center justify-center py-12"> <div className="flex items-center justify-center py-12">
<IconRefresh className="h-6 w-6 animate-spin text-muted-foreground" /> <IconRefresh className="h-6 w-6 animate-spin text-muted-foreground" />
</div> </div>
)} );
}
{error && ( if (error) {
return (
<div className="rounded-lg border border-destructive/50 bg-destructive/10 p-4 text-destructive"> <div className="rounded-lg border border-destructive/50 bg-destructive/10 p-4 text-destructive">
<div className="font-medium">Error loading {viewMode}</div> <div className="font-medium">Error loading {viewMode}</div>
<div className="text-sm">{error}</div> <div className="text-sm">{error}</div>
</div> </div>
)} );
}
{!loading && !error && itemCount === 0 && ( if (itemCount === 0) {
return (
<div className="text-center py-12 text-muted-foreground"> <div className="text-center py-12 text-muted-foreground">
{viewMode === 'events' {viewMode === 'events'
? 'No normalized event files found. Run an agent with KANDEV_DEBUG_AGENT_MESSAGES=1 to generate event logs.' ? 'No normalized event files found. Run an agent with KANDEV_DEBUG_AGENT_MESSAGES=1 to generate event logs.'
: 'No fixtures found. Select a fixture file from the dropdown above.'} : 'No fixtures found. Select a fixture file from the dropdown above.'}
</div> </div>
)} );
}
{!loading && !error && viewMode === 'fixtures' && filteredFixtures.length > 0 && ( if (viewMode === 'fixtures') {
return (
<div className="space-y-4"> <div className="space-y-4">
{filteredFixtures.map((fixture, index) => ( {filteredFixtures.map((fixture, index) => (
<FixtureCard key={index} fixture={fixture} index={index} /> <FixtureCard key={index} fixture={fixture} index={index} />
))} ))}
</div> </div>
)} );
}
{!loading && !error && viewMode === 'events' && messages.length > 0 && ( return (
<div className="space-y-4"> <div className="space-y-4">
{messages.map((message, index) => ( {messages.map((message, index) => (
<MessageCard key={message.id || index} message={message} /> <MessageCard key={message.id || index} message={message} />
))} ))}
</div> </div>
)} );
}
// --- Page ---
export default function AgentMessagesPage() {
const data = useAgentMessages();
return (
<div className="min-h-screen bg-background p-8">
<div className="max-w-5xl mx-auto">
<div className="mb-8">
<div className="flex items-center justify-between mb-2">
<h1 className="text-2xl font-bold">Agent Messages QA</h1>
<button
onClick={data.loadContent}
disabled={data.loading || !data.selectedFile}
className="flex items-center gap-2 px-3 py-1.5 text-sm rounded-md border hover:bg-muted/50 disabled:opacity-50 transition-colors"
>
<IconRefresh className={`h-4 w-4 ${data.loading ? 'animate-spin' : ''}`} />
Refresh
</button>
</div>
<p className="text-muted-foreground">
Visual inspection of agent message normalization. Each card shows the rendered output alongside raw JSON.
</p>
</div>
<ViewModeTabs viewMode={data.viewMode} setViewMode={data.setViewMode} />
<FiltersBar
viewMode={data.viewMode}
currentFiles={data.currentFiles}
selectedFile={data.selectedFile}
setSelectedFile={data.setSelectedFile}
toolFilter={data.toolFilter}
setToolFilter={data.setToolFilter}
/>
<div className="mb-6 text-sm text-muted-foreground">
Showing {data.itemCount} of {data.totalCount} {data.viewMode === 'events' ? 'messages' : 'fixtures'}
</div>
<ContentArea
viewMode={data.viewMode}
loading={data.loading}
error={data.error}
itemCount={data.itemCount}
filteredFixtures={data.filteredFixtures}
messages={data.messages}
/>
</div> </div>
</div> </div>
); );
+20
View File
@@ -1,6 +1,8 @@
import { defineConfig, globalIgnores } from "eslint/config"; import { defineConfig, globalIgnores } from "eslint/config";
import nextVitals from "eslint-config-next/core-web-vitals"; import nextVitals from "eslint-config-next/core-web-vitals";
import nextTs from "eslint-config-next/typescript"; import nextTs from "eslint-config-next/typescript";
import sonarjs from "eslint-plugin-sonarjs";
import unusedImports from "eslint-plugin-unused-imports";
const eslintConfig = defineConfig([ const eslintConfig = defineConfig([
...nextVitals, ...nextVitals,
@@ -13,6 +15,24 @@ const eslintConfig = defineConfig([
"build/**", "build/**",
"next-env.d.ts", "next-env.d.ts",
]), ]),
{
plugins: {
sonarjs,
"unused-imports": unusedImports,
},
rules: {
"max-lines": ["warn", { max: 600, skipBlankLines: true, skipComments: true }],
"max-lines-per-function": ["warn", { max: 100, skipBlankLines: true, skipComments: true }],
"complexity": ["warn", 15],
"max-depth": ["warn", 4],
"max-params": ["warn", 5],
"no-nested-ternary": "warn",
"sonarjs/cognitive-complexity": ["warn", 20],
"sonarjs/no-duplicate-string": ["warn", { threshold: 4 }],
"sonarjs/no-identical-functions": "warn",
"unused-imports/no-unused-imports": "warn",
},
},
]); ]);
export default eslintConfig; export default eslintConfig;
+3 -1
View File
@@ -107,9 +107,11 @@
"@types/node": "^20", "@types/node": "^20",
"@types/react": "^19", "@types/react": "^19",
"@types/react-dom": "^19", "@types/react-dom": "^19",
"cross-env": "^7.0.3",
"eslint": "^9", "eslint": "^9",
"eslint-config-next": "16.1.1", "eslint-config-next": "16.1.1",
"cross-env": "^7.0.3", "eslint-plugin-sonarjs": "^3.0.7",
"eslint-plugin-unused-imports": "^4.4.1",
"happy-dom": "^20.1.0", "happy-dom": "^20.1.0",
"tailwindcss": "^4", "tailwindcss": "^4",
"typescript": "^5", "typescript": "^5",
-6
View File
@@ -1,6 +0,0 @@
{
"name": "kandev-3",
"lockfileVersion": 3,
"requires": true,
"packages": {}
}