diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile new file mode 100644 index 00000000..639446c0 --- /dev/null +++ b/.devcontainer/Dockerfile @@ -0,0 +1,16 @@ +FROM mcr.microsoft.com/devcontainers/go:1-1.24-bookworm + +# The base image ships a yarn apt repo with an expired GPG key that breaks +# `apt-get update`; drop it before installing anything. +RUN rm -f /etc/apt/sources.list.d/yarn.list \ + && apt-get update \ + && apt-get install -y --no-install-recommends shellcheck \ + && rm -rf /var/lib/apt/lists/* + +# The image pins GOTOOLCHAIN=local but go.mod requires a newer patch release; +# let Go download the exact toolchain version on demand. +ENV GOTOOLCHAIN=auto + +# proxy.golang.org is DNS-hijacked on this network (HiNet safebrowsing) and +# goproxy.io lacks toolchain modules; goproxy.cn mirrors both. +ENV GOPROXY=https://goproxy.cn,direct diff --git a/.devcontainer/devcontainer-lock.json b/.devcontainer/devcontainer-lock.json new file mode 100644 index 00000000..098c8d64 --- /dev/null +++ b/.devcontainer/devcontainer-lock.json @@ -0,0 +1,14 @@ +{ + "features": { + "ghcr.io/devcontainers/features/node:1": { + "version": "1.7.1", + "resolved": "ghcr.io/devcontainers/features/node@sha256:8c0de46939b61958041700ee89e3493f3b2e4131a06dc46b4d9423427d06e5f6", + "integrity": "sha256:8c0de46939b61958041700ee89e3493f3b2e4131a06dc46b4d9423427d06e5f6" + }, + "ghcr.io/shyim/devcontainers-features/bun:0": { + "version": "0.0.1", + "resolved": "ghcr.io/shyim/devcontainers-features/bun@sha256:689eae681aa08981175829a59953ba67a7d311f6a05c15d1bbbcb2da2839827e", + "integrity": "sha256:689eae681aa08981175829a59953ba67a7d311f6a05c15d1bbbcb2da2839827e" + } + } +} diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json new file mode 100644 index 00000000..88b4bc31 --- /dev/null +++ b/.devcontainer/devcontainer.json @@ -0,0 +1,45 @@ +{ + "name": "agentapi", + // Dockerfile removes the base image's expired yarn apt repo and installs + // shellcheck (required by `make lint/shellcheck`) + "build": { + "dockerfile": "Dockerfile" + }, + "features": { + // Bun builds the chat UI (see Makefile: `bun run build`, `bun lint`) + "ghcr.io/shyim/devcontainers-features/bun:0": {}, + // Node is still needed by Next.js/ESLint tooling under the hood + "ghcr.io/devcontainers/features/node:1": { + "version": "lts" + } + }, + "remoteUser": "root", + "containerUser": "root", + // safe.directory: running as root, git refuses the host-owned repo without it + "postCreateCommand": "git config --global --add safe.directory ${containerWorkspaceFolder} && cd chat && bun install", + "forwardPorts": [3284, 3000, 6006, 2345], + "portsAttributes": { + "3284": { + "label": "agentapi server" + }, + "3000": { + "label": "chat UI (next dev)" + }, + "6006": { + "label": "storybook" + }, + "2345": { + "label": "delve debugger" + } + }, + "customizations": { + "vscode": { + "extensions": [ + "golang.go", + "esbenp.prettier-vscode", + "dbaeumer.vscode-eslint", + "bradlc.vscode-tailwindcss" + ] + } + } +} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index ece71e18..f801b959 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -1,74 +1,76 @@ name: Build Release Binaries +# Compiles agentapi binaries (with the chat UI embedded) and attaches them +# to the release when one is published. Adapted from upstream coder/agentapi +# for this fork: no repository-owner gate and standard hosted runners. +# make build handles the chat UI build + embed via the sources stamp, so the +# chat UI is only built once across all target platforms. + +permissions: + contents: write + on: release: types: [published] - push: - branches: [ main ] workflow_dispatch: - inputs: - create-artifact: - description: 'Create build artifact' - required: true - type: boolean - default: false jobs: build: name: Build Release Binaries - runs-on: ubuntu-latest-8-cores - if: ${{ github.repository_owner == 'coder' }} + runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v4 + + - name: Set up Go + uses: actions/setup-go@v5 + with: + go-version-file: go.mod - - name: Set up Go - uses: actions/setup-go@v5 - with: - go-version: 'stable' + - name: Set up Bun + uses: oven-sh/setup-bun@v2 - - name: Set up Bun - uses: oven-sh/setup-bun@v2 + - name: Install Chat Dependencies + run: cd chat && bun install --frozen-lockfile - - name: Install Chat Dependencies - run: cd chat && bun install + - name: Run make gen and check for unstaged changes + run: | + make gen + ./check_unstaged.sh - - name: Run make gen and check for unstaged changes - run: | - make gen - ./check_unstaged.sh + - name: Build binaries + shell: bash + run: | + build_variants=( + "linux amd64 agentapi-linux-amd64" + "linux arm64 agentapi-linux-arm64" + "darwin amd64 agentapi-darwin-amd64" + "darwin arm64 agentapi-darwin-arm64" + "windows amd64 agentapi-windows-amd64.exe" + ) - - name: Build and Upload - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - shell: bash - run: | - build_variants=( - "linux amd64 agentapi-linux-amd64" - "linux arm64 agentapi-linux-arm64" - "darwin amd64 agentapi-darwin-amd64" - "darwin arm64 agentapi-darwin-arm64" - "windows amd64 agentapi-windows-amd64.exe" - ) + for variant in "${build_variants[@]}"; do + read -r goos goarch artifact_name <<< "$variant" - for variant in "${build_variants[@]}"; do - read -r goos goarch artifact_name <<< "$variant" + echo "Building for GOOS=$goos GOARCH=$goarch..." + GOOS="$goos" GOARCH="$goarch" BINPATH="out/$artifact_name" make build + done - echo "Building for GOOS=$goos GOARCH=$goarch..." - CGO_ENABLED=0 GOOS=$goos GOARCH=$goarch BINPATH="out/$artifact_name" make build - done + ( + cd out + sha256sum agentapi-* > checksums.txt + ) - - name: Upload Build Artifact - if: ${{ inputs.create-artifact }} - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 - with: - name: agentapi-build - path: ${{ github.workspace }}/out - retention-days: 7 + - name: Upload build artifact + if: ${{ github.event_name == 'workflow_dispatch' }} + uses: actions/upload-artifact@v4 + with: + name: agentapi-build + path: out/ + retention-days: 7 - - name: Upload Release Assets - if: ${{ github.event_name == 'release' || github.ref == 'refs/heads/main' }} - run: gh release upload "$RELEASE_TAG" "$GITHUB_WORKSPACE"/out/* --clobber - env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - RELEASE_TAG: ${{ github.event_name == 'release' && github.event.release.tag_name || 'preview' }} + - name: Upload release assets + if: ${{ github.event_name == 'release' }} + run: gh release upload "${{ github.event.release.tag_name }}" out/* --clobber + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/CHANGELOG.md b/CHANGELOG.md index c025dd8a..7b6aaad4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,16 @@ # Changelog +## v0.13.0 + +### Features +- Render Claude thinking blocks as collapsible sections in the task timeline +- Per-message markdown/raw render toggle (replaces global toolbar toggle) + +### Fixes +- Merge rich message content blocks on re-emit instead of dropping earlier blocks during Claude delta streaming +- Switch tailed JSONL file at runtime when Claude Code parks a session to a new path +- Reorder resolver priority so ParkedJobID scan runs before direct file check, preventing stale session selection + ## v0.12.2 ### Fixes diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 00000000..8c59867c --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,75 @@ +# CLAUDE.md + +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. + +## What This Is + +AgentAPI is a Go HTTP server that controls coding agents (Claude Code, Aider, Goose, Codex, Gemini, Copilot, Amp, Cursor, Auggie, AmazonQ, Opencode) through terminal emulation. It runs agents in an in-memory terminal emulator, translates HTTP API calls into terminal keystrokes, and parses terminal output into structured messages. It also embeds a Next.js chat web UI. + +## Build & Run + +```bash +make build # Build binary to out/agentapi (includes chat UI build) +go build -o out/agentapi main.go # Go-only build without chat UI +make embed # Build chat UI and copy into lib/httpapi/chat/ for embedding +make fmt # Format Go code with gofumpt +make gen # Regenerate OpenAPI schema and version (go generate ./...) +make lint # Run all linters (Go, TypeScript, shellcheck, actionlint) +``` + +Chat UI development: +```bash +cd chat && bun install # Install chat dependencies +cd chat && bun run dev # Start Next.js dev server with Turbopack +cd chat && bun lint # Lint TypeScript +``` + +## Testing + +```bash +go test ./... # Run all Go tests +go test ./lib/httpapi/... # Run tests in a specific package +go test -run TestOpenAPISchema ./lib/httpapi/... # Run a single test +go test ./e2e # Run e2e tests (smoke test) +``` + +Tests use `CGO_ENABLED=0`. The project uses `testify` (assert/require) and `coder/quartz` for deterministic time mocking. Tests are colocated with source files. E2e tests in `e2e/` use a scripted echo agent that simulates real agent behavior. + +## Architecture + +### Message Flow +1. User sends message via `POST /message` +2. Server takes a terminal snapshot, sends keystrokes to the agent process +3. A polling loop compares new terminal snapshots against the baseline +4. New content below the baseline becomes the agent's response message +5. SSE events (`GET /events`) stream message and status updates to clients + +### Key Packages +- **`lib/httpapi/`** — HTTP server (chi router + huma for OpenAPI). Routes: `/messages`, `/message`, `/status`, `/events` (SSE), `/queue`, `/upload`, `/rich-messages`. The chat UI is embedded via `//go:embed` from `lib/httpapi/chat/`. +- **`lib/screentracker/`** — Core conversation engine. `Conversation` interface with `PTYConversation` implementation. Manages terminal snapshots, screen diffing, message splitting, and status detection (stable vs. changing). +- **`lib/termexec/`** — Terminal process execution. Wraps PTY creation and process lifecycle. +- **`lib/msgfmt/`** — Agent-specific message formatting. Strips echoed user input and TUI elements (input boxes, borders) from terminal output. Each agent type has different formatting quirks. +- **`lib/jsonlwatcher/`** — Watches agent JSONL session logs (Claude, Codex) for rich structured messages (tool calls, thinking, usage data). Runs as a sidecar alongside PTY. +- **`x/acpio/`** — Experimental ACP (Agent Communication Protocol) transport, alternative to PTY. +- **`cmd/`** — CLI commands via cobra/viper. `server` and `attach` subcommands. + +### Two Transport Modes +- **PTY (default)**: Runs the agent in a terminal emulator, parses screen output. +- **ACP (experimental)**: Uses the Agent Communication Protocol for structured communication (`--experimental-acp`). + +### Adding a New Agent Type +1. Add the `AgentType` constant in `lib/msgfmt/msgfmt.go` +2. Add formatting logic in `lib/msgfmt/` (message box removal, user input stripping) +3. Add readiness detection in `lib/msgfmt/agent_readiness.go` +4. Add alias mapping in `cmd/server/server.go` (`agentTypeAliases`) +5. Add display name in `chat/src/components/chat-provider.tsx` + +### Exhaustive Switch/Map Enforcement +The `exhaustive` golangci-lint checker is enabled for both switches and maps. When adding a new `AgentType` or enum value, all switch statements and map literals over that type must be updated or the linter will fail. + +## Conventions + +- OpenAPI schema is auto-generated: `go run main.go server --print-openapi dummy > openapi.json` (via `go generate`) +- The chat UI build output goes to `lib/httpapi/chat/` with a magic base path placeholder that gets replaced at runtime +- Environment variables use `AGENTAPI_` prefix (e.g., `AGENTAPI_ALLOWED_HOSTS`) +- Server defaults: port 3284, chat at `/chat`, docs at `/docs` diff --git a/README.md b/README.md index e6a75618..f46adeba 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # AgentAPI -Control [Claude Code](https://github.com/anthropics/claude-code), [AmazonQ](https://aws.amazon.com/developer/learning/q-developer-cli/), [Opencode](https://opencode.ai/), [Goose](https://github.com/block/goose), [Aider](https://github.com/Aider-AI/aider), [Gemini](https://github.com/google-gemini/gemini-cli), [GitHub Copilot](https://github.com/github/copilot-cli), [Sourcegraph Amp](https://ampcode.com/), [Codex](https://github.com/openai/codex), [Auggie](https://docs.augmentcode.com/cli/overview), and [Cursor CLI](https://cursor.com/en/cli) with an HTTP API. +Control [Claude Code](https://github.com/anthropics/claude-code), [AmazonQ](https://aws.amazon.com/developer/learning/q-developer-cli/), [Opencode](https://opencode.ai/), [Goose](https://github.com/block/goose), [Aider](https://github.com/Aider-AI/aider), [Gemini](https://github.com/google-gemini/gemini-cli), [GitHub Copilot](https://github.com/github/copilot-cli), [Sourcegraph Amp](https://ampcode.com/), [Codex](https://github.com/openai/codex), [Kimi Code](https://www.kimi.com/zh-tw/help/kimi-code/cli-getting-started), [Auggie](https://docs.augmentcode.com/cli/overview), and [Cursor CLI](https://cursor.com/en/cli) with an HTTP API. ![agentapi-chat](https://github.com/user-attachments/assets/57032c9f-4146-4b66-b219-09e38ab7690d) @@ -11,6 +11,193 @@ You can use AgentAPI: - to create a tool that submits pull request reviews to an agent - and much more! +## Changes in this fork + +This fork started from the upstream v0.12.2 codebase. It keeps the original +terminal-emulation and HTTP API model, while extending AgentAPI into a more +complete workspace for operating long-running coding agents. + +### Session workspace and chat UI + +The embedded chat UI is organized around tasks instead of a single flat +transcript. Each user request becomes a navigable task with its associated +response, thinking blocks, tool calls, and background activity. + +The Session Explorer provides: + +- links and file paths discovered in the current conversation; +- an index for jumping directly to earlier tasks; +- Markdown preview and export for individual tasks; +- access to the live terminal when the parsed conversation is not sufficient; +- MCP server and webhook configuration without leaving the chat UI. + +Each message has its own markdown/raw toggle so you can switch render +modes without affecting the rest of the conversation. + +The UI also includes improved mobile layouts, attachment handling, searchable +tool activity, connection-state indicators, and more compact tool-call cards. + +### Message queue and connection recovery + +Messages submitted while an agent is busy are placed in a FIFO queue instead +of being rejected. Queued messages can be inspected, edited, or deleted before +delivery through both the API and chat UI. + +Long-running browser sessions are protected by SSE heartbeats, automatic +reconnection, stale-connection detection, and local recovery of messages that +failed before reaching the server. The document title and session header expose +the current task, agent status, and connection state. + +Relevant APIs include: + +- `GET /queue` and `PUT/DELETE /queue/{id}` for queue management; +- `GET /events` for live messages, status, errors, rich activity, and heartbeat + events; +- `GET /title` for the current human-readable session title. + +### Structured Claude and Codex activity + +In addition to parsing terminal snapshots, this fork can watch Claude and Codex +session logs. This provides structured thinking blocks, tool invocations, tool +results, usage information, and stable message identifiers that cannot always +be reconstructed reliably from terminal output alone. + +- `GET /rich-messages` returns the structured conversation. +- `GET /timeline` returns normalized session events suitable for export, + auditing, or building another UI. +- Background and delegated tasks remain visible with their running, completed, + or failed state and output details. +- Claude thinking blocks are rendered inline as collapsible sections in the + task timeline. + +Terminal parsing remains the fallback for other agents and for environments +where a session log is unavailable. + +### MCP management + +Claude and Codex MCP servers can be managed while AgentAPI is running. The +implementation preserves unrelated settings in `.mcp.json` or +`$CODEX_HOME/config.toml`. + +The API and Session Explorer support: + +- reading or replacing the complete MCP server map; +- creating, updating, and deleting individual servers; +- checking remote HTTP connectivity and resolving local stdio executables; +- saving, importing, exporting, and applying reusable MCP profiles; +- optionally restarting the PTY agent to apply changes immediately. + +When an agent is restarted, AgentAPI itself and its HTTP/SSE clients remain +online. The child agent receives a new process and session-log watcher, although +its previous in-memory conversation context is not retained. + +### Run-status webhooks + +AgentAPI can send an HTTP POST whenever a run changes between `running` and +`stable`. Webhooks can be initialized with CLI flags or `AGENTAPI_WEBHOOK_*` +environment variables, then inspected or changed through `GET/PUT /webhook` or +the Session Explorer. + +Delivery runs asynchronously with a configurable timeout and retry count. +An optional Go `text/template` payload template lets you reshape the POST body +for any receiver — available fields are `.ID`, `.Type`, `.CreatedAt`, `.RunID`, +`.Status`, `.PreviousStatus`, `.AgentType`, and `.Transport`. When no template +is set, the default JSON payload is sent unchanged. + +```bash +agentapi server \ + --webhook-url https://example.com/agentapi/events \ + -- claude +``` + +### Self-update + +AgentAPI can update itself from the command line: + +```bash +agentapi update # download and install the latest release +agentapi update --check # check without downloading +agentapi update --force # skip version comparison +``` + +Release binaries are verified against the release's `checksums.txt` SHA-256 +manifest before the running executable is replaced. An update is rejected if +the manifest is missing, malformed, or does not match the download. + +### API token authentication + +All API endpoints can be protected with a Bearer token. Authentication is +**disabled by default** for backward compatibility. + +```bash +# Auto-generate a random token (printed to stderr on startup) +agentapi server --api-token -- claude + +# Use a specific token +agentapi server --api-token=my-secret -- claude + +# Via environment variable +AGENTAPI_API_TOKEN=my-secret agentapi server -- claude +``` + +When enabled, every API request must include `Authorization: Bearer `. +Static file routes (`/`, `/chat/*`) are exempt so browsers can open the chat +UI without a token. + +### Rate limit usage + +`GET /usage` returns real-time rate limit utilization from the upstream API +provider. The endpoint dispatches automatically based on the running agent +type: + +- **Claude** — reads the OAuth token from `~/.claude/.credentials.json` and + extracts Anthropic's unified rate limit headers (5-hour / 7-day / overage + utilization, subscription type, reset times). +- **Codex** — reads `OPENAI_API_KEY` and extracts OpenAI's `x-ratelimit-*` + headers (request and token limits, remaining quota, reset durations). + +```bash +curl http://localhost:3284/usage +``` + +### Interactive prompt support + +The chat UI detects interactive TUI prompts — such as Claude Code's plan +approval dialog or permission confirmation — and renders them as clickable +buttons. Previously these prompts were invisible when structured JSONL +messages were available, causing the agent to appear stuck. + +### Kimi Code CLI + +This fork adds the `kimi` agent type, automatic detection for the `kimi` +executable, chat UI labeling, terminal message formatting, readiness detection, +and tests for its startup state. + +Kimi can use the regular interactive PTY transport: + +```bash +agentapi server -- kimi +``` + +It can also use Kimi's native ACP server after completing `/login` once: + +```bash +agentapi server --type=kimi --experimental-acp -- kimi acp +``` + +### Runtime reliability + +The fork also includes fixes for wide-character terminal cursor tracking, +PTY lifecycle leaks, concurrent event delivery, message tracking races, ACP +shutdown, TUI re-render artifacts, JSONL watcher flushing, rich message +content-block merging during Claude delta streaming, and runtime session-file +switching when Claude Code parks a session to a new JSONL. These changes are +intended to keep AgentAPI stable across long sessions, process replacement, and +temporary browser or network interruptions. + +See the [full comparison with upstream](https://github.com/coder/agentapi/compare/main...k1dav-c:agentapi:main) +for the complete commit history. + ## Quickstart 1. Install `agentapi`: @@ -18,10 +205,13 @@ You can use AgentAPI: ```bash OS=$(uname -s | tr "[:upper:]" "[:lower:]"); ARCH=$(uname -m | sed "s/x86_64/amd64/;s/aarch64/arm64/"); - curl -fsSL "https://github.com/coder/agentapi/releases/latest/download/agentapi-${OS}-${ARCH}" -o agentapi && chmod +x agentapi + curl -fsSL "https://github.com/k1dav-c/agentapi/releases/latest/download/agentapi-${OS}-${ARCH}" -o agentapi && chmod +x agentapi ``` - Alternatively, you can download the latest release binary from the [releases page](https://github.com/coder/agentapi/releases). + Alternatively, you can download this fork's latest binary from the + [releases page](https://github.com/k1dav-c/agentapi/releases). Upstream + `coder/agentapi` release binaries do not include the features documented in + the **Changes in this fork** section. 1. Verify the installation: @@ -72,19 +262,58 @@ agentapi server -- aider --model sonnet --api-key anthropic=sk-ant-apio3-XXX agentapi server -- goose ``` +Kimi Code can run through its interactive terminal UI: + +```bash +agentapi server -- kimi +``` + +Kimi Code also provides a native ACP server. After logging in once with +`kimi` and `/login`, you can use AgentAPI's ACP transport: + +```bash +agentapi server --type=kimi --experimental-acp -- kimi acp +``` + > [!NOTE] -> When using Claude, Codex, Opencode, Copilot, Gemini, Amp or CursorCLI, always specify the agent type explicitly (eg: `agentapi server --type=codex -- codex`), or message formatting may break. +> When using Claude, Codex, Opencode, Copilot, Gemini, Amp or CursorCLI, always specify the agent type explicitly (eg: `agentapi server --type=codex -- codex`), or message formatting may break. Kimi is auto-detected when the executable name is `kimi`; use `--type=kimi` for wrappers or ACP mode. An OpenAPI schema is available in [openapi.json](openapi.json). By default, the server runs on port 3284. Additionally, the server exposes the same OpenAPI schema at http://localhost:3284/openapi.json and the available endpoints in a documentation UI at http://localhost:3284/docs. -There are 4 endpoints: +Endpoints: - GET `/messages` - returns a list of all messages in the conversation with the agent - POST `/message` - sends a message to the agent. When a 200 response is returned, AgentAPI has detected that the agent started processing the message -- GET `/status` - returns the current status of the agent, either "stable" or "running" +- GET `/status` - returns the backward-compatible `stable`/`running` status, + detailed lifecycle (`starting`, `ready`, `running`, `restarting`, `exited`, or + `failed`), a session ID, and a monotonically increasing run ID - GET `/events` - an SSE stream of events from the agent: message and status updates +- DELETE `/messages` - clears all conversation state (messages, rich messages, timeline, errors) and restarts the agent process +- GET `/usage` - returns real-time rate limit utilization from the upstream API (Anthropic or OpenAI) +- GET/PUT `/webhook` - reads or updates run-status webhook delivery without restarting the agent +- GET `/mcp` - returns configured MCP servers and the managed config path for Claude or Codex +- PUT `/mcp` - replaces the complete MCP server set; pass `?restart=true` to restart the PTY agent and apply immediately +- POST `/mcp/check` - checks remote HTTP connectivity and resolves stdio executables +- POST `/mcp/servers`, PATCH/DELETE `/mcp/servers/{name}` - creates, updates, or removes one MCP server +- GET `/mcp/profiles` - exports project-scoped MCP configuration profiles +- PUT/DELETE `/mcp/profiles/{name}` - imports, replaces, or removes a profile +- POST `/mcp/profiles/{name}/apply` - replaces the active MCP configuration with a saved profile + +#### API token authentication + +Set `--api-token` to require a Bearer token on all API requests (static chat +UI routes are exempt): + +```bash +agentapi server --api-token -- claude # auto-generate and print to stderr +agentapi server --api-token=my-secret -- claude # use a specific token +``` + +The equivalent environment variable is `AGENTAPI_API_TOKEN`. When set, clients +must include `Authorization: Bearer ` on every API call. Without +`--api-token`, authentication is disabled (backward compatible). #### Allowed hosts @@ -134,6 +363,56 @@ agentapi server --allowed-origins 'https://example.com,http://localhost:3000' -- AGENTAPI_ALLOWED_ORIGINS='https://example.com http://localhost:3000' agentapi server -- claude ``` +#### Run status webhooks + +Set `--webhook-url` to send an HTTP POST whenever the run status changes between +`running` and `stable`: + +```bash +agentapi server \ + --webhook-url 'https://example.com/agentapi/events' \ + -- claude +``` + +The equivalent environment variables are `AGENTAPI_WEBHOOK_URL`, +`AGENTAPI_WEBHOOK_PAYLOAD_TEMPLATE`, `AGENTAPI_WEBHOOK_TIMEOUT`, and +`AGENTAPI_WEBHOOK_MAX_ATTEMPTS`. The timeout defaults to `10s`, and delivery is +attempted up to 3 times. + +The initial values can also be changed while AgentAPI is running from the +Webhook tab in the chat UI's Session Explorer. Saving an empty URL disables +delivery. + +The request body has this format: + +```json +{ + "id": "unique-delivery-id", + "type": "run.status_changed", + "created_at": "2026-07-26T12:00:00Z", + "data": { + "run_id": "agentapi-process-run-id", + "status": "stable", + "previous_status": "running", + "agent_type": "claude", + "transport": "pty" + } +} +``` + +Each request includes `X-AgentAPI-Delivery`, `X-AgentAPI-Event`, and +`X-AgentAPI-Timestamp` headers. + +### `agentapi update` + +Update the agentapi binary to the latest release from GitHub. + +```bash +agentapi update # download and install the latest version +agentapi update --check # only check if an update is available +agentapi update --force # update even if already at the latest version +``` + ### `agentapi attach` Attach to a running agent's terminal session. diff --git a/chat/.gitignore b/chat/.gitignore index 5e195f39..38c74737 100644 --- a/chat/.gitignore +++ b/chat/.gitignore @@ -16,6 +16,7 @@ # next.js /.next/ /out/ +/out-*/ # production /build diff --git a/chat/bun.lock b/chat/bun.lock index 9f5e053a..e0ce0bb1 100644 --- a/chat/bun.lock +++ b/chat/bun.lock @@ -5,21 +5,28 @@ "": { "name": "chat", "dependencies": { - "@radix-ui/react-checkbox": "^1.3.3", "@radix-ui/react-dialog": "^1.1.15", "@radix-ui/react-dropdown-menu": "^2.1.14", "@radix-ui/react-slot": "^1.2.2", "@radix-ui/react-tabs": "^1.1.11", + "@radix-ui/react-tooltip": "^1.1.8", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", + "highlight.js": "^11.11.0", "jszip": "^3.10.1", + "linkify-it": "^6.1.0", + "lowlight": "^3.3.0", "lucide-react": "^0.511.0", "next": "15.4.10", "next-themes": "^0.4.6", "react": "^19.0.0", "react-dom": "^19.0.0", "react-dropzone": "^14.3.8", + "react-markdown": "^10.1.0", "react-textarea-autosize": "^8.5.9", + "rehype-highlight": "^7.0.2", + "remark-breaks": "^4.0.0", + "remark-gfm": "^4.0.1", "sonner": "^2.0.3", "tailwind-merge": "^3.3.0", }, @@ -28,6 +35,7 @@ "@storybook/addon-themes": "^9.0.17", "@storybook/nextjs": "^9.0.17", "@tailwindcss/postcss": "^4", + "@types/linkify-it": "^5.0.0", "@types/node": "^20", "@types/react": "^19", "@types/react-dom": "^19", @@ -433,9 +441,7 @@ "@radix-ui/primitive": ["@radix-ui/primitive@1.1.3", "", {}, "sha512-JTF99U/6XIjCBo0wqkU5sK10glYe27MRRsfwoiq5zzOEZLHU3A3KCMa5X/azekYRCJ0HlwI0crAXS/5dEHTzDg=="], - "@radix-ui/react-arrow": ["@radix-ui/react-arrow@1.1.6", "", { "dependencies": { "@radix-ui/react-primitive": "2.1.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-2JMfHJf/eVnwq+2dewT3C0acmCWD3XiVA1Da+jTDqo342UlU13WvXtqHhG+yJw5JeQmu4ue2eMy6gcEArLBlcw=="], - - "@radix-ui/react-checkbox": ["@radix-ui/react-checkbox@1.3.3", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-presence": "1.1.5", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-controllable-state": "1.2.2", "@radix-ui/react-use-previous": "1.1.1", "@radix-ui/react-use-size": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-wBbpv+NQftHDdG86Qc0pIyXk5IR3tM8Vd0nWLKDcX8nNn4nXFOFwsKuqw2okA/1D/mpaAkmuyndrPJTYDNZtFw=="], + "@radix-ui/react-arrow": ["@radix-ui/react-arrow@1.1.15", "", { "dependencies": { "@radix-ui/react-primitive": "2.1.10" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-v4zggRcjadnI+ClKDuijlQEW4tw3NoaeHc/PwpKnLoLLKNUG4InLegkstooLcRIUWCs+8L22dGURCVuFfOKfnA=="], "@radix-ui/react-collection": ["@radix-ui/react-collection@1.1.6", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-primitive": "2.1.2", "@radix-ui/react-slot": "1.2.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-PbhRFK4lIEw9ADonj48tiYWzkllz81TM7KVYyyMMw2cwHO7D5h4XKEblL8NlaRisTK3QTe6tBEhDccFUryxHBQ=="], @@ -459,7 +465,7 @@ "@radix-ui/react-menu": ["@radix-ui/react-menu@2.1.14", "", { "dependencies": { "@radix-ui/primitive": "1.1.2", "@radix-ui/react-collection": "1.1.6", "@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.9", "@radix-ui/react-focus-guards": "1.1.2", "@radix-ui/react-focus-scope": "1.1.6", "@radix-ui/react-id": "1.1.1", "@radix-ui/react-popper": "1.2.6", "@radix-ui/react-portal": "1.1.8", "@radix-ui/react-presence": "1.1.4", "@radix-ui/react-primitive": "2.1.2", "@radix-ui/react-roving-focus": "1.1.9", "@radix-ui/react-slot": "1.2.2", "@radix-ui/react-use-callback-ref": "1.1.1", "aria-hidden": "^1.2.4", "react-remove-scroll": "^2.6.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-0zSiBAIFq9GSKoSH5PdEaQeRB3RnEGxC+H2P0egtnKoKKLNBH8VBHyVO6/jskhjAezhOIplyRUj7U2lds9A+Yg=="], - "@radix-ui/react-popper": ["@radix-ui/react-popper@1.2.6", "", { "dependencies": { "@floating-ui/react-dom": "^2.0.0", "@radix-ui/react-arrow": "1.1.6", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-primitive": "2.1.2", "@radix-ui/react-use-callback-ref": "1.1.1", "@radix-ui/react-use-layout-effect": "1.1.1", "@radix-ui/react-use-rect": "1.1.1", "@radix-ui/react-use-size": "1.1.1", "@radix-ui/rect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-7iqXaOWIjDBfIG7aq8CUEeCSsQMLFdn7VEE8TaFz704DtEzpPHR7w/uuzRflvKgltqSAImgcmxQ7fFX3X7wasg=="], + "@radix-ui/react-popper": ["@radix-ui/react-popper@1.3.7", "", { "dependencies": { "@floating-ui/react-dom": "^2.0.0", "@radix-ui/react-arrow": "1.1.15", "@radix-ui/react-compose-refs": "1.1.5", "@radix-ui/react-context": "1.2.2", "@radix-ui/react-primitive": "2.1.10", "@radix-ui/react-use-callback-ref": "1.1.4", "@radix-ui/react-use-layout-effect": "1.1.4", "@radix-ui/react-use-rect": "1.1.4", "@radix-ui/react-use-size": "1.1.4", "@radix-ui/rect": "1.1.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-UsJrrd7w4wuKKTdvd/DNERVlwSlUcyXzjhyDwBk+3aPOsCjOY6ZSbxuw8E6lZTjjfP8Cpd0J8VVkrYUWyGYXyg=="], "@radix-ui/react-portal": ["@radix-ui/react-portal@1.1.9", "", { "dependencies": { "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-bpIxvq03if6UNwXZ+HTK71JLh4APvnXntDc6XOX8UVq4XQOVl7lwok0AvIl+b8zgCw3fSaVTZMpAPPagXbKmHQ=="], @@ -473,6 +479,8 @@ "@radix-ui/react-tabs": ["@radix-ui/react-tabs@1.1.11", "", { "dependencies": { "@radix-ui/primitive": "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-presence": "1.1.4", "@radix-ui/react-primitive": "2.1.2", "@radix-ui/react-roving-focus": "1.1.9", "@radix-ui/react-use-controllable-state": "1.2.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-4FiKSVoXqPP/KfzlB7lwwqoFV6EPwkrrqGp9cUYXjwDYHhvpnqq79P+EPHKcdoTE7Rl8w/+6s9rTlsfXHES9GA=="], + "@radix-ui/react-tooltip": ["@radix-ui/react-tooltip@1.2.16", "", { "dependencies": { "@radix-ui/primitive": "1.1.7", "@radix-ui/react-compose-refs": "1.1.5", "@radix-ui/react-context": "1.2.2", "@radix-ui/react-dismissable-layer": "1.1.19", "@radix-ui/react-id": "1.1.4", "@radix-ui/react-popper": "1.3.7", "@radix-ui/react-portal": "1.1.17", "@radix-ui/react-presence": "1.1.10", "@radix-ui/react-primitive": "2.1.10", "@radix-ui/react-slot": "1.3.3", "@radix-ui/react-use-controllable-state": "1.2.6", "@radix-ui/react-use-layout-effect": "1.1.4", "@radix-ui/react-visually-hidden": "1.2.11" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-6EamKFRRnlpdadndbZ6LMwycfwkwPte1B42hs6QA0gYhjaOKqW4PZ4pjaW9UrlDX5eVt/OjncE7BFTPL5nmZhg=="], + "@radix-ui/react-use-callback-ref": ["@radix-ui/react-use-callback-ref@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-FkBMwD+qbGQeMu1cOHnuGB6x4yzPjho8ap5WtbEJ26umhgqVXbhekKUQO+hZEL1vU92a3wHwdp0HAcqAUF5iDg=="], "@radix-ui/react-use-controllable-state": ["@radix-ui/react-use-controllable-state@1.2.2", "", { "dependencies": { "@radix-ui/react-use-effect-event": "0.0.2", "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-BjasUjixPFdS+NKkypcyyN5Pmg83Olst0+c6vGov0diwTEo6mgdqVR6hxcEgFuh4QrAs7Rc+9KuGJ9TVCj0Zzg=="], @@ -481,15 +489,15 @@ "@radix-ui/react-use-escape-keydown": ["@radix-ui/react-use-escape-keydown@1.1.1", "", { "dependencies": { "@radix-ui/react-use-callback-ref": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-Il0+boE7w/XebUHyBjroE+DbByORGR9KKmITzbR7MyQ4akpORYP/ZmbhAr0DG7RmmBqoOnZdy2QlvajJ2QA59g=="], - "@radix-ui/react-use-layout-effect": ["@radix-ui/react-use-layout-effect@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ=="], + "@radix-ui/react-use-layout-effect": ["@radix-ui/react-use-layout-effect@1.1.4", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-K20DkRkUwDnxEYMBPcg3Y6voLkEy5p5QQmszZgLngKKiC7dzBR/aEuK3w1qlx2JWDUNH6FluahYdgR3BP+QbYw=="], - "@radix-ui/react-use-previous": ["@radix-ui/react-use-previous@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-2dHfToCj/pzca2Ck724OZ5L0EVrr3eHRNsG/b3xQJLA2hZpVCS99bLAX+hm1IHXDEnzU6by5z/5MIY794/a8NQ=="], + "@radix-ui/react-use-rect": ["@radix-ui/react-use-rect@1.1.4", "", { "dependencies": { "@radix-ui/rect": "1.1.3" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-cSOCh6JlkmfjLyNcLiu2nB4v+nm+dkZ+Q5KHWk/soo4U7ZLiEQFKHK9/YmtBHjfCEaU43IBKQOc4/uJmCaiCTQ=="], - "@radix-ui/react-use-rect": ["@radix-ui/react-use-rect@1.1.1", "", { "dependencies": { "@radix-ui/rect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-QTYuDesS0VtuHNNvMh+CjlKJ4LJickCMUAqjlE3+j8w+RlRpwyX3apEQKGFzbZGdo7XNG1tXa+bQqIE7HIXT2w=="], + "@radix-ui/react-use-size": ["@radix-ui/react-use-size@1.1.4", "", { "dependencies": { "@radix-ui/react-use-layout-effect": "1.1.4" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-D3anSY15EJoxrihpsXI6SMrmmonnQtR2ni7arO+Lfdg3O95b9hNXxONk8jA5C8ANdF/h5HMAxejgs8PWJ6rlhw=="], - "@radix-ui/react-use-size": ["@radix-ui/react-use-size@1.1.1", "", { "dependencies": { "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-ewrXRDTAqAXlkl6t/fkXWNAhFX9I+CkKlw6zjEwk86RSPKwZr3xpBRso655aqYafwtnbpHLj6toFzmd6xdVptQ=="], + "@radix-ui/react-visually-hidden": ["@radix-ui/react-visually-hidden@1.2.11", "", { "dependencies": { "@radix-ui/react-primitive": "2.1.10" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-NFS86RYYZb4/exihaESBGOpMJFz8MGLAfu3mOBSGByVnVPC9JPASfYubxd/8KbkQK0sYAv8lVQDEQukDX/qXvQ=="], - "@radix-ui/rect": ["@radix-ui/rect@1.1.1", "", {}, "sha512-HPwpGIzkl28mWyZqG52jiqDJ12waP11Pa1lGoiyUkIEuMLBP0oeK/C89esbXrxsky5we7dfd8U58nm0SgAWpVw=="], + "@radix-ui/rect": ["@radix-ui/rect@1.1.3", "", {}, "sha512-JtyZR+mqgBibTo8xea3B6ZRmzZiM/YeVBtUkas6zMuXjAlfIFIW2FgqeM9eLyvEaYX66vr6DJMK+4U6LV0KhNw=="], "@rtsao/scc": ["@rtsao/scc@1.1.0", "", {}, "sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g=="], @@ -563,6 +571,8 @@ "@types/chai": ["@types/chai@5.2.2", "", { "dependencies": { "@types/deep-eql": "*" } }, "sha512-8kB30R7Hwqf40JPiKhVzodJs2Qc1ZJ5zuT3uzw5Hq/dhNCl3G3l83jfpdI1e20BP348+fV7VIL/+FxaXkqBmWg=="], + "@types/debug": ["@types/debug@4.1.13", "", { "dependencies": { "@types/ms": "*" } }, "sha512-KSVgmQmzMwPlmtljOomayoR89W4FynCAi3E8PPs7vmDVPe84hT+vGPKkJfThkmXs0x0jAaa9U8uW8bbfyS2fWw=="], + "@types/deep-eql": ["@types/deep-eql@4.0.2", "", {}, "sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw=="], "@types/doctrine": ["@types/doctrine@0.0.9", "", {}, "sha512-eOIHzCUSH7SMfonMG1LsC2f8vxBFtho6NGBznK41R84YzPuvSBzrhEps33IsQiOW9+VL6NQ9DbjQJznk/S4uRA=="], @@ -573,12 +583,22 @@ "@types/estree": ["@types/estree@1.0.7", "", {}, "sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ=="], + "@types/estree-jsx": ["@types/estree-jsx@1.0.5", "", { "dependencies": { "@types/estree": "*" } }, "sha512-52CcUVNFyfb1A2ALocQw/Dd1BQFNmSdkuC3BkZ6iqhdMfQz7JWOFRuJFloOzjk+6WijU56m9oKXFAXc7o3Towg=="], + + "@types/hast": ["@types/hast@3.0.5", "", { "dependencies": { "@types/unist": "*" } }, "sha512-rp/ezSWaD1m44dPKICGhiskI13nVr7qTloFwDa/IYkhhf5nzwP+zIQcIJh3WIFSBOy/H1PzB40jPjMDksN4F+g=="], + "@types/html-minifier-terser": ["@types/html-minifier-terser@6.1.0", "", {}, "sha512-oh/6byDPnL1zeNXFrDXFLyZjkr1MsBG667IM792caf1L2UPOOMf65NFzjUH/ltyfwjAGfs1rsX1eftK0jC/KIg=="], "@types/json-schema": ["@types/json-schema@7.0.15", "", {}, "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA=="], "@types/json5": ["@types/json5@0.0.29", "", {}, "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ=="], + "@types/linkify-it": ["@types/linkify-it@5.0.0", "", {}, "sha512-sVDA58zAw4eWAffKOaQH5/5j3XeayukzDk+ewSsnv3p4yJEZHCCzMDiZM8e0OUrRvmpGZ85jf4yDHkHsgBNr9Q=="], + + "@types/mdast": ["@types/mdast@4.0.4", "", { "dependencies": { "@types/unist": "*" } }, "sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA=="], + + "@types/ms": ["@types/ms@2.1.0", "", {}, "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA=="], + "@types/node": ["@types/node@20.17.30", "", { "dependencies": { "undici-types": "~6.19.2" } }, "sha512-7zf4YyHA+jvBNfVrk2Gtvs6x7E8V+YDW05bNfG2XkWDJfYRXrTiP/DsB2zSYTaHX0bGIujTBQdMVAhb+j7mwpg=="], "@types/parse-json": ["@types/parse-json@4.0.2", "", {}, "sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw=="], @@ -591,6 +611,8 @@ "@types/semver": ["@types/semver@7.7.0", "", {}, "sha512-k107IF4+Xr7UHjwDc7Cfd6PRQfbdkiRabXGRjo07b4WyPahFBZCZ1sE+BNxYIJPPg73UkfOsVOLwqVc/6ETrIA=="], + "@types/unist": ["@types/unist@3.0.3", "", {}, "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q=="], + "@typescript-eslint/eslint-plugin": ["@typescript-eslint/eslint-plugin@8.29.0", "", { "dependencies": { "@eslint-community/regexpp": "^4.10.0", "@typescript-eslint/scope-manager": "8.29.0", "@typescript-eslint/type-utils": "8.29.0", "@typescript-eslint/utils": "8.29.0", "@typescript-eslint/visitor-keys": "8.29.0", "graphemer": "^1.4.0", "ignore": "^5.3.1", "natural-compare": "^1.4.0", "ts-api-utils": "^2.0.1" }, "peerDependencies": { "@typescript-eslint/parser": "^8.0.0 || ^8.0.0-alpha.0", "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <5.9.0" } }, "sha512-PAIpk/U7NIS6H7TEtN45SPGLQaHNgB7wSjsQV/8+KYokAb2T/gloOA/Bee2yd4/yKVhPKe5LlaUGhAZk5zmSaQ=="], "@typescript-eslint/parser": ["@typescript-eslint/parser@8.29.0", "", { "dependencies": { "@typescript-eslint/scope-manager": "8.29.0", "@typescript-eslint/types": "8.29.0", "@typescript-eslint/typescript-estree": "8.29.0", "@typescript-eslint/visitor-keys": "8.29.0", "debug": "^4.3.4" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <5.9.0" } }, "sha512-8C0+jlNJOwQso2GapCVWWfW/rzaq7Lbme+vGUFKE31djwNncIpgXD7Cd4weEsDdkoZDjH0lwwr3QDQFuyrMg9g=="], @@ -607,6 +629,8 @@ "@typescript-eslint/visitor-keys": ["@typescript-eslint/visitor-keys@8.29.0", "", { "dependencies": { "@typescript-eslint/types": "8.29.0", "eslint-visitor-keys": "^4.2.0" } }, "sha512-Sne/pVz8ryR03NFK21VpN88dZ2FdQXOlq3VIklbrTYEt8yXtRFr9tvUhqvCeKjqYk5FSim37sHbooT6vzBTZcg=="], + "@ungap/structured-clone": ["@ungap/structured-clone@1.3.3", "", {}, "sha512-60YRaenCQcVjYEKOcG824+DRGGIQ3VKErcBoAEDJZz5bKIs2ZG+X/H9Nk+Q6EVkwJk5QNApxbrc5QtBSwtrXAg=="], + "@unrs/resolver-binding-darwin-arm64": ["@unrs/resolver-binding-darwin-arm64@1.4.1", "", { "os": "darwin", "cpu": "arm64" }, "sha512-8Tv+Bsd0BjGwfEedIyor4inw8atppRxM5BdUnIt+3mAm/QXUm7Dw74CHnXpfZKXkp07EXJGiA8hStqCINAWhdw=="], "@unrs/resolver-binding-darwin-x64": ["@unrs/resolver-binding-darwin-x64@1.4.1", "", { "os": "darwin", "cpu": "x64" }, "sha512-X8c3PhWziEMKAzZz+YAYWfwawi5AEgzy/hmfizAB4C70gMHLKmInJcp1270yYAOs7z07YVFI220pp50z24Jk3A=="], @@ -759,6 +783,8 @@ "babel-plugin-polyfill-regenerator": ["babel-plugin-polyfill-regenerator@0.6.5", "", { "dependencies": { "@babel/helper-define-polyfill-provider": "^0.6.5" }, "peerDependencies": { "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" } }, "sha512-ISqQ2frbiNU9vIJkzg7dlPpznPZ4jOiUQ1uSmB0fEHeowtN3COYRsXr/xexn64NpU13P06jc/L5TgiJXOgrbEg=="], + "bail": ["bail@2.0.2", "", {}, "sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw=="], + "balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="], "base64-js": ["base64-js@1.5.1", "", {}, "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA=="], @@ -815,10 +841,20 @@ "case-sensitive-paths-webpack-plugin": ["case-sensitive-paths-webpack-plugin@2.4.0", "", {}, "sha512-roIFONhcxog0JSSWbvVAh3OocukmSgpqOH6YpMkCvav/ySIV3JKg4Dc8vYtQjYi/UxpNE36r/9v+VqTQqgkYmw=="], + "ccount": ["ccount@2.0.1", "", {}, "sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg=="], + "chai": ["chai@5.2.1", "", { "dependencies": { "assertion-error": "^2.0.1", "check-error": "^2.1.1", "deep-eql": "^5.0.1", "loupe": "^3.1.0", "pathval": "^2.0.0" } }, "sha512-5nFxhUrX0PqtyogoYOA8IPswy5sZFTOsBFl/9bNsmDLgsxYTzSZQJDPppDnZPTQbzSEm0hqGjWPzRemQCYbD6A=="], "chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="], + "character-entities": ["character-entities@2.0.2", "", {}, "sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ=="], + + "character-entities-html4": ["character-entities-html4@2.1.0", "", {}, "sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA=="], + + "character-entities-legacy": ["character-entities-legacy@3.0.0", "", {}, "sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ=="], + + "character-reference-invalid": ["character-reference-invalid@2.0.1", "", {}, "sha512-iBZ4F4wRbyORVsu0jPV7gXkOsGYjGHPmAyv+HiHG8gi5PtC9KI2j1+v8/tlibRvjoWX027ypmG/n0HtO5t7unw=="], + "check-error": ["check-error@2.1.1", "", {}, "sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw=="], "chokidar": ["chokidar@3.6.0", "", { "dependencies": { "anymatch": "~3.1.2", "braces": "~3.0.2", "glob-parent": "~5.1.2", "is-binary-path": "~2.1.0", "is-glob": "~4.0.1", "normalize-path": "~3.0.0", "readdirp": "~3.6.0" }, "optionalDependencies": { "fsevents": "~2.3.2" } }, "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw=="], @@ -847,6 +883,8 @@ "colorette": ["colorette@2.0.20", "", {}, "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w=="], + "comma-separated-tokens": ["comma-separated-tokens@2.0.3", "", {}, "sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg=="], + "commander": ["commander@13.1.0", "", {}, "sha512-/rFeCpNJQbhSZjGVwO9RFV3xPqbnERS8MmIQzCtD/zl6gpJuV/bMLuN92oG3F7d8oDEHHRrujSXNUr8fpjntKw=="], "common-path-prefix": ["common-path-prefix@3.0.0", "", {}, "sha512-QE33hToZseCH3jS0qN96O/bSh3kaw/h+Tq7ngyY9eWDUnTlTNUyqfqvCXioLe5Na5jFsL78ra/wuBU4iuEgd4w=="], @@ -901,6 +939,8 @@ "debug": ["debug@4.4.0", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA=="], + "decode-named-character-reference": ["decode-named-character-reference@1.3.0", "", { "dependencies": { "character-entities": "^2.0.0" } }, "sha512-GtpQYB283KrPp6nRw50q3U9/VfOutZOe103qlN7BPP6Ad27xYnOIWv4lPzo8HCAL+mMZofJ9KEy30fq6MfaK6Q=="], + "dedent": ["dedent@0.7.0", "", {}, "sha512-Q6fKUPqnAHAyhiUgFU7BUzLiv0kd8saH9al7tnu5Q/okj6dnupxyTgFIBjVzJATdfIAm9NAsvXNzjaKa+bxVyA=="], "deep-eql": ["deep-eql@5.0.2", "", {}, "sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q=="], @@ -923,6 +963,8 @@ "detect-node-es": ["detect-node-es@1.1.0", "", {}, "sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ=="], + "devlop": ["devlop@1.1.0", "", { "dependencies": { "dequal": "^2.0.0" } }, "sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA=="], + "diffie-hellman": ["diffie-hellman@5.0.3", "", { "dependencies": { "bn.js": "^4.1.0", "miller-rabin": "^4.0.0", "randombytes": "^2.0.0" } }, "sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg=="], "dir-glob": ["dir-glob@3.0.1", "", { "dependencies": { "path-type": "^4.0.0" } }, "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA=="], @@ -1029,6 +1071,8 @@ "estraverse": ["estraverse@5.3.0", "", {}, "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA=="], + "estree-util-is-identifier-name": ["estree-util-is-identifier-name@3.0.0", "", {}, "sha512-hFtqIDZTIUZ9BXLb8y4pYGyk6+wekIivNVTcmvk8NoOh+VeRn5y6cEHzbURrWbfp1fIqdVipilzj+lfaadNZmg=="], + "esutils": ["esutils@2.0.3", "", {}, "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g=="], "event-target-shim": ["event-target-shim@5.0.1", "", {}, "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ=="], @@ -1037,6 +1081,8 @@ "evp_bytestokey": ["evp_bytestokey@1.0.3", "", { "dependencies": { "md5.js": "^1.3.4", "safe-buffer": "^5.1.1" } }, "sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA=="], + "extend": ["extend@3.0.2", "", {}, "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g=="], + "fast-deep-equal": ["fast-deep-equal@3.1.3", "", {}, "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="], "fast-glob": ["fast-glob@3.3.1", "", { "dependencies": { "@nodelib/fs.stat": "^2.0.2", "@nodelib/fs.walk": "^1.2.3", "glob-parent": "^5.1.2", "merge2": "^1.3.0", "micromatch": "^4.0.4" } }, "sha512-kNFPyjhh5cKjrUltxs+wFx+ZkbRaxxmZ+X0ZU31SOsxCEtP9VPgtq2teZw1DebupL5GmDaNQ6yKMMVcM41iqDg=="], @@ -1141,14 +1187,26 @@ "hasown": ["hasown@2.0.2", "", { "dependencies": { "function-bind": "^1.1.2" } }, "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ=="], + "hast-util-is-element": ["hast-util-is-element@3.0.0", "", { "dependencies": { "@types/hast": "^3.0.0" } }, "sha512-Val9mnv2IWpLbNPqc/pUem+a7Ipj2aHacCwgNfTiK0vJKl0LF+4Ba4+v1oPHFpf3bLYmreq0/l3Gud9S5OH42g=="], + + "hast-util-to-jsx-runtime": ["hast-util-to-jsx-runtime@2.3.6", "", { "dependencies": { "@types/estree": "^1.0.0", "@types/hast": "^3.0.0", "@types/unist": "^3.0.0", "comma-separated-tokens": "^2.0.0", "devlop": "^1.0.0", "estree-util-is-identifier-name": "^3.0.0", "hast-util-whitespace": "^3.0.0", "mdast-util-mdx-expression": "^2.0.0", "mdast-util-mdx-jsx": "^3.0.0", "mdast-util-mdxjs-esm": "^2.0.0", "property-information": "^7.0.0", "space-separated-tokens": "^2.0.0", "style-to-js": "^1.0.0", "unist-util-position": "^5.0.0", "vfile-message": "^4.0.0" } }, "sha512-zl6s8LwNyo1P9uw+XJGvZtdFF1GdAkOg8ujOw+4Pyb76874fLps4ueHXDhXWdk6YHQ6OgUtinliG7RsYvCbbBg=="], + + "hast-util-to-text": ["hast-util-to-text@4.0.2", "", { "dependencies": { "@types/hast": "^3.0.0", "@types/unist": "^3.0.0", "hast-util-is-element": "^3.0.0", "unist-util-find-after": "^5.0.0" } }, "sha512-KK6y/BN8lbaq654j7JgBydev7wuNMcID54lkRav1P0CaE1e47P72AWWPiGKXTJU271ooYzcvTAn/Zt0REnvc7A=="], + + "hast-util-whitespace": ["hast-util-whitespace@3.0.0", "", { "dependencies": { "@types/hast": "^3.0.0" } }, "sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw=="], + "he": ["he@1.2.0", "", { "bin": { "he": "bin/he" } }, "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw=="], + "highlight.js": ["highlight.js@11.12.0", "", {}, "sha512-nbfWpyRMcMrPMmDwJB+dhX/eiaPKtc2RB+0QZskqJ3WjRA/FDS0e9hZrx8EC/lbEv8gXy98FcDbNa/dspAaJMg=="], + "hmac-drbg": ["hmac-drbg@1.0.1", "", { "dependencies": { "hash.js": "^1.0.3", "minimalistic-assert": "^1.0.0", "minimalistic-crypto-utils": "^1.0.1" } }, "sha512-Tti3gMqLdZfhOQY1Mzf/AanLiqh1WTiJgEj26ZuYQ9fbkLomzGchCws4FyrSd4VkpBfiNhaE1On+lOz894jvXg=="], "html-entities": ["html-entities@2.6.0", "", {}, "sha512-kig+rMn/QOVRvr7c86gQ8lWXq+Hkv6CbAH1hLu+RG338StTpE8Z0b44SDVaqVu7HGKf27frdmUYEs9hTUX/cLQ=="], "html-minifier-terser": ["html-minifier-terser@6.1.0", "", { "dependencies": { "camel-case": "^4.1.2", "clean-css": "^5.2.2", "commander": "^8.3.0", "he": "^1.2.0", "param-case": "^3.0.4", "relateurl": "^0.2.7", "terser": "^5.10.0" }, "bin": { "html-minifier-terser": "cli.js" } }, "sha512-YXxSlJBZTP7RS3tWnQw74ooKa6L9b9i9QYXY21eUEvhZ3u9XLfv6OnFsQq6RxkhHygsaUMvYsZRV5rU/OVNZxw=="], + "html-url-attributes": ["html-url-attributes@3.0.1", "", {}, "sha512-ol6UPyBWqsrO6EJySPz2O7ZSr856WDrEzM5zMqp+FJJLGMW35cLYmmZnl0vztAZxRUoNZJFTCohfjuIJ8I4QBQ=="], + "html-webpack-plugin": ["html-webpack-plugin@5.6.3", "", { "dependencies": { "@types/html-minifier-terser": "^6.0.0", "html-minifier-terser": "^6.0.2", "lodash": "^4.17.21", "pretty-error": "^4.0.0", "tapable": "^2.0.0" }, "peerDependencies": { "@rspack/core": "0.x || 1.x", "webpack": "^5.20.0" }, "optionalPeers": ["@rspack/core", "webpack"] }, "sha512-QSf1yjtSAsmf7rYBV7XX86uua4W/vkhIt0xNXKbsi2foEeW7vjJQz4bhnpL3xH+l1ryl1680uNv968Z+X6jSYg=="], "htmlparser2": ["htmlparser2@6.1.0", "", { "dependencies": { "domelementtype": "^2.0.1", "domhandler": "^4.0.0", "domutils": "^2.5.2", "entities": "^2.0.0" } }, "sha512-gyyPk6rgonLFEDGoeRgQNaEUvdJ4ktTmmUh/h2t7s+M8oPpIPxgNACWa+6ESR57kXstwqPiCut0V8NRpcwgU7A=="], @@ -1175,8 +1233,14 @@ "inherits": ["inherits@2.0.4", "", {}, "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="], + "inline-style-parser": ["inline-style-parser@0.2.7", "", {}, "sha512-Nb2ctOyNR8DqQoR0OwRG95uNWIC0C1lCgf5Naz5H6Ji72KZ8OcFZLz2P5sNgwlyoJ8Yif11oMuYs5pBQa86csA=="], + "internal-slot": ["internal-slot@1.1.0", "", { "dependencies": { "es-errors": "^1.3.0", "hasown": "^2.0.2", "side-channel": "^1.1.0" } }, "sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw=="], + "is-alphabetical": ["is-alphabetical@2.0.1", "", {}, "sha512-FWyyY60MeTNyeSRpkM2Iry0G9hpr7/9kD40mD/cGQEuilcZYS4okz8SN2Q6rLCJ8gbCt6fN+rC+6tMGS99LaxQ=="], + + "is-alphanumerical": ["is-alphanumerical@2.0.1", "", { "dependencies": { "is-alphabetical": "^2.0.0", "is-decimal": "^2.0.0" } }, "sha512-hmbYhX/9MUMF5uh7tOXyK/n0ZvWpad5caBA17GsC6vyuCqaWliRG5K1qS9inmUhEMaOBIW7/whAnSwveW/LtZw=="], + "is-arguments": ["is-arguments@1.2.0", "", { "dependencies": { "call-bound": "^1.0.2", "has-tostringtag": "^1.0.2" } }, "sha512-7bVbi0huj/wrIAOzb8U1aszg9kdi3KN/CyU19CTI7tAoZYEZoL9yCDXpbXN+uPsuWnP02cyug1gleqq+TU+YCA=="], "is-array-buffer": ["is-array-buffer@3.0.5", "", { "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.3", "get-intrinsic": "^1.2.6" } }, "sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A=="], @@ -1201,6 +1265,8 @@ "is-date-object": ["is-date-object@1.1.0", "", { "dependencies": { "call-bound": "^1.0.2", "has-tostringtag": "^1.0.2" } }, "sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg=="], + "is-decimal": ["is-decimal@2.0.1", "", {}, "sha512-AAB9hiomQs5DXWcRB1rqsxGUstbRroFOPPVAomNk/3XHR5JyEZChOyTWe2oayKnsSsr/kcGqF+z6yuH6HHpN0A=="], + "is-docker": ["is-docker@2.2.1", "", { "bin": { "is-docker": "cli.js" } }, "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ=="], "is-extglob": ["is-extglob@2.1.1", "", {}, "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ=="], @@ -1211,6 +1277,8 @@ "is-glob": ["is-glob@4.0.3", "", { "dependencies": { "is-extglob": "^2.1.1" } }, "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg=="], + "is-hexadecimal": ["is-hexadecimal@2.0.1", "", {}, "sha512-DgZQp241c8oO6cA1SbTEWiXeoxV42vlcJxgH+B3hi1AiqqKruZR3ZGF8In3fj4+/y/7rHvlOZLZtgJ/4ttYGZg=="], + "is-map": ["is-map@2.0.3", "", {}, "sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw=="], "is-nan": ["is-nan@1.3.2", "", { "dependencies": { "call-bind": "^1.0.0", "define-properties": "^1.1.3" } }, "sha512-E+zBKpQ2t6MEo1VsonYmluk9NxGrbzpeeLC2xIViuO2EjU2xsXsBPwTr3Ykv9l08UYEVEdWeRZNouaZqF6RN0w=="], @@ -1219,6 +1287,8 @@ "is-number-object": ["is-number-object@1.1.1", "", { "dependencies": { "call-bound": "^1.0.3", "has-tostringtag": "^1.0.2" } }, "sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw=="], + "is-plain-obj": ["is-plain-obj@4.1.0", "", {}, "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg=="], + "is-regex": ["is-regex@1.2.1", "", { "dependencies": { "call-bound": "^1.0.2", "gopd": "^1.2.0", "has-tostringtag": "^1.0.2", "hasown": "^2.0.2" } }, "sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g=="], "is-set": ["is-set@2.0.3", "", {}, "sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg=="], @@ -1305,6 +1375,8 @@ "lines-and-columns": ["lines-and-columns@1.2.4", "", {}, "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg=="], + "linkify-it": ["linkify-it@6.1.0", "", { "dependencies": { "uc.micro": "^3.0.0" } }, "sha512-wJ/TwpSDTLepCrQoYWYIExIKg5Zchex2Nn5yk2mFnB+6PtdkHtyLx742md9csRjjOnGkKIS/RrbY7l8D6gT9Vw=="], + "loader-runner": ["loader-runner@4.3.0", "", {}, "sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg=="], "loader-utils": ["loader-utils@3.3.1", "", {}, "sha512-FMJTLMXfCLMLfJxcX9PFqX5qD88Z5MRGaZCVzfuqeZSPsyiBzs+pahDQjbIWz2QIzPZz0NX9Zy4FX3lmK6YHIg=="], @@ -1317,12 +1389,16 @@ "lodash.merge": ["lodash.merge@4.6.2", "", {}, "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ=="], + "longest-streak": ["longest-streak@3.1.0", "", {}, "sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g=="], + "loose-envify": ["loose-envify@1.4.0", "", { "dependencies": { "js-tokens": "^3.0.0 || ^4.0.0" }, "bin": "cli.js" }, "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q=="], "loupe": ["loupe@3.1.4", "", {}, "sha512-wJzkKwJrheKtknCOKNEtDK4iqg/MxmZheEMtSTYvnzRdEYaZzmgH976nenp8WdJRdx5Vc1X/9MO0Oszl6ezeXg=="], "lower-case": ["lower-case@2.0.2", "", { "dependencies": { "tslib": "^2.0.3" } }, "sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg=="], + "lowlight": ["lowlight@3.3.0", "", { "dependencies": { "@types/hast": "^3.0.0", "devlop": "^1.0.0", "highlight.js": "~11.11.0" } }, "sha512-0JNhgFoPvP6U6lE/UdVsSq99tn6DhjjpAj5MxG49ewd2mOBVtwWYIT8ClyABhq198aXXODMU6Ox8DrGy/CpTZQ=="], + "lru-cache": ["lru-cache@5.1.1", "", { "dependencies": { "yallist": "^3.0.2" } }, "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w=="], "lucide-react": ["lucide-react@0.511.0", "", { "peerDependencies": { "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-VK5a2ydJ7xm8GvBeKLS9mu1pVK6ucef9780JVUjw6bAjJL/QXnd4Y0p7SPeOUMC27YhzNCZvm5d/QX0Tp3rc0w=="], @@ -1333,16 +1409,106 @@ "make-dir": ["make-dir@3.1.0", "", { "dependencies": { "semver": "^6.0.0" } }, "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw=="], + "markdown-table": ["markdown-table@3.0.4", "", {}, "sha512-wiYz4+JrLyb/DqW2hkFJxP7Vd7JuTDm77fvbM8VfEQdmSMqcImWeeRbHwZjBjIFki/VaMK2BhFi7oUUZeM5bqw=="], + "math-intrinsics": ["math-intrinsics@1.1.0", "", {}, "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g=="], "md5.js": ["md5.js@1.3.5", "", { "dependencies": { "hash-base": "^3.0.0", "inherits": "^2.0.1", "safe-buffer": "^5.1.2" } }, "sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg=="], + "mdast-util-find-and-replace": ["mdast-util-find-and-replace@3.0.2", "", { "dependencies": { "@types/mdast": "^4.0.0", "escape-string-regexp": "^5.0.0", "unist-util-is": "^6.0.0", "unist-util-visit-parents": "^6.0.0" } }, "sha512-Tmd1Vg/m3Xz43afeNxDIhWRtFZgM2VLyaf4vSTYwudTyeuTneoL3qtWMA5jeLyz/O1vDJmmV4QuScFCA2tBPwg=="], + + "mdast-util-from-markdown": ["mdast-util-from-markdown@2.0.3", "", { "dependencies": { "@types/mdast": "^4.0.0", "@types/unist": "^3.0.0", "decode-named-character-reference": "^1.0.0", "devlop": "^1.0.0", "mdast-util-to-string": "^4.0.0", "micromark": "^4.0.0", "micromark-util-decode-numeric-character-reference": "^2.0.0", "micromark-util-decode-string": "^2.0.0", "micromark-util-normalize-identifier": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0", "unist-util-stringify-position": "^4.0.0" } }, "sha512-W4mAWTvSlKvf8L6J+VN9yLSqQ9AOAAvHuoDAmPkz4dHf553m5gVj2ejadHJhoJmcmxEnOv6Pa8XJhpxE93kb8Q=="], + + "mdast-util-gfm": ["mdast-util-gfm@3.1.0", "", { "dependencies": { "mdast-util-from-markdown": "^2.0.0", "mdast-util-gfm-autolink-literal": "^2.0.0", "mdast-util-gfm-footnote": "^2.0.0", "mdast-util-gfm-strikethrough": "^2.0.0", "mdast-util-gfm-table": "^2.0.0", "mdast-util-gfm-task-list-item": "^2.0.0", "mdast-util-to-markdown": "^2.0.0" } }, "sha512-0ulfdQOM3ysHhCJ1p06l0b0VKlhU0wuQs3thxZQagjcjPrlFRqY215uZGHHJan9GEAXd9MbfPjFJz+qMkVR6zQ=="], + + "mdast-util-gfm-autolink-literal": ["mdast-util-gfm-autolink-literal@2.0.1", "", { "dependencies": { "@types/mdast": "^4.0.0", "ccount": "^2.0.0", "devlop": "^1.0.0", "mdast-util-find-and-replace": "^3.0.0", "micromark-util-character": "^2.0.0" } }, "sha512-5HVP2MKaP6L+G6YaxPNjuL0BPrq9orG3TsrZ9YXbA3vDw/ACI4MEsnoDpn6ZNm7GnZgtAcONJyPhOP8tNJQavQ=="], + + "mdast-util-gfm-footnote": ["mdast-util-gfm-footnote@2.1.0", "", { "dependencies": { "@types/mdast": "^4.0.0", "devlop": "^1.1.0", "mdast-util-from-markdown": "^2.0.0", "mdast-util-to-markdown": "^2.0.0", "micromark-util-normalize-identifier": "^2.0.0" } }, "sha512-sqpDWlsHn7Ac9GNZQMeUzPQSMzR6Wv0WKRNvQRg0KqHh02fpTz69Qc1QSseNX29bhz1ROIyNyxExfawVKTm1GQ=="], + + "mdast-util-gfm-strikethrough": ["mdast-util-gfm-strikethrough@2.0.0", "", { "dependencies": { "@types/mdast": "^4.0.0", "mdast-util-from-markdown": "^2.0.0", "mdast-util-to-markdown": "^2.0.0" } }, "sha512-mKKb915TF+OC5ptj5bJ7WFRPdYtuHv0yTRxK2tJvi+BDqbkiG7h7u/9SI89nRAYcmap2xHQL9D+QG/6wSrTtXg=="], + + "mdast-util-gfm-table": ["mdast-util-gfm-table@2.0.0", "", { "dependencies": { "@types/mdast": "^4.0.0", "devlop": "^1.0.0", "markdown-table": "^3.0.0", "mdast-util-from-markdown": "^2.0.0", "mdast-util-to-markdown": "^2.0.0" } }, "sha512-78UEvebzz/rJIxLvE7ZtDd/vIQ0RHv+3Mh5DR96p7cS7HsBhYIICDBCu8csTNWNO6tBWfqXPWekRuj2FNOGOZg=="], + + "mdast-util-gfm-task-list-item": ["mdast-util-gfm-task-list-item@2.0.0", "", { "dependencies": { "@types/mdast": "^4.0.0", "devlop": "^1.0.0", "mdast-util-from-markdown": "^2.0.0", "mdast-util-to-markdown": "^2.0.0" } }, "sha512-IrtvNvjxC1o06taBAVJznEnkiHxLFTzgonUdy8hzFVeDun0uTjxxrRGVaNFqkU1wJR3RBPEfsxmU6jDWPofrTQ=="], + + "mdast-util-mdx-expression": ["mdast-util-mdx-expression@2.0.1", "", { "dependencies": { "@types/estree-jsx": "^1.0.0", "@types/hast": "^3.0.0", "@types/mdast": "^4.0.0", "devlop": "^1.0.0", "mdast-util-from-markdown": "^2.0.0", "mdast-util-to-markdown": "^2.0.0" } }, "sha512-J6f+9hUp+ldTZqKRSg7Vw5V6MqjATc+3E4gf3CFNcuZNWD8XdyI6zQ8GqH7f8169MM6P7hMBRDVGnn7oHB9kXQ=="], + + "mdast-util-mdx-jsx": ["mdast-util-mdx-jsx@3.2.0", "", { "dependencies": { "@types/estree-jsx": "^1.0.0", "@types/hast": "^3.0.0", "@types/mdast": "^4.0.0", "@types/unist": "^3.0.0", "ccount": "^2.0.0", "devlop": "^1.1.0", "mdast-util-from-markdown": "^2.0.0", "mdast-util-to-markdown": "^2.0.0", "parse-entities": "^4.0.0", "stringify-entities": "^4.0.0", "unist-util-stringify-position": "^4.0.0", "vfile-message": "^4.0.0" } }, "sha512-lj/z8v0r6ZtsN/cGNNtemmmfoLAFZnjMbNyLzBafjzikOM+glrjNHPlf6lQDOTccj9n5b0PPihEBbhneMyGs1Q=="], + + "mdast-util-mdxjs-esm": ["mdast-util-mdxjs-esm@2.0.1", "", { "dependencies": { "@types/estree-jsx": "^1.0.0", "@types/hast": "^3.0.0", "@types/mdast": "^4.0.0", "devlop": "^1.0.0", "mdast-util-from-markdown": "^2.0.0", "mdast-util-to-markdown": "^2.0.0" } }, "sha512-EcmOpxsZ96CvlP03NghtH1EsLtr0n9Tm4lPUJUBccV9RwUOneqSycg19n5HGzCf+10LozMRSObtVr3ee1WoHtg=="], + + "mdast-util-newline-to-break": ["mdast-util-newline-to-break@2.0.0", "", { "dependencies": { "@types/mdast": "^4.0.0", "mdast-util-find-and-replace": "^3.0.0" } }, "sha512-MbgeFca0hLYIEx/2zGsszCSEJJ1JSCdiY5xQxRcLDDGa8EPvlLPupJ4DSajbMPAnC0je8jfb9TiUATnxxrHUog=="], + + "mdast-util-phrasing": ["mdast-util-phrasing@4.1.0", "", { "dependencies": { "@types/mdast": "^4.0.0", "unist-util-is": "^6.0.0" } }, "sha512-TqICwyvJJpBwvGAMZjj4J2n0X8QWp21b9l0o7eXyVJ25YNWYbJDVIyD1bZXE6WtV6RmKJVYmQAKWa0zWOABz2w=="], + + "mdast-util-to-hast": ["mdast-util-to-hast@13.2.1", "", { "dependencies": { "@types/hast": "^3.0.0", "@types/mdast": "^4.0.0", "@ungap/structured-clone": "^1.0.0", "devlop": "^1.0.0", "micromark-util-sanitize-uri": "^2.0.0", "trim-lines": "^3.0.0", "unist-util-position": "^5.0.0", "unist-util-visit": "^5.0.0", "vfile": "^6.0.0" } }, "sha512-cctsq2wp5vTsLIcaymblUriiTcZd0CwWtCbLvrOzYCDZoWyMNV8sZ7krj09FSnsiJi3WVsHLM4k6Dq/yaPyCXA=="], + + "mdast-util-to-markdown": ["mdast-util-to-markdown@2.1.2", "", { "dependencies": { "@types/mdast": "^4.0.0", "@types/unist": "^3.0.0", "longest-streak": "^3.0.0", "mdast-util-phrasing": "^4.0.0", "mdast-util-to-string": "^4.0.0", "micromark-util-classify-character": "^2.0.0", "micromark-util-decode-string": "^2.0.0", "unist-util-visit": "^5.0.0", "zwitch": "^2.0.0" } }, "sha512-xj68wMTvGXVOKonmog6LwyJKrYXZPvlwabaryTjLh9LuvovB/KAH+kvi8Gjj+7rJjsFi23nkUxRQv1KqSroMqA=="], + + "mdast-util-to-string": ["mdast-util-to-string@4.0.0", "", { "dependencies": { "@types/mdast": "^4.0.0" } }, "sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg=="], + "memfs": ["memfs@3.6.0", "", { "dependencies": { "fs-monkey": "^1.0.4" } }, "sha512-EGowvkkgbMcIChjMTMkESFDbZeSh8xZ7kNSF0hAiAN4Jh6jgHCRS0Ga/+C8y6Au+oqpezRHCfPsmJ2+DwAgiwQ=="], "merge-stream": ["merge-stream@2.0.0", "", {}, "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w=="], "merge2": ["merge2@1.4.1", "", {}, "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg=="], + "micromark": ["micromark@4.0.2", "", { "dependencies": { "@types/debug": "^4.0.0", "debug": "^4.0.0", "decode-named-character-reference": "^1.0.0", "devlop": "^1.0.0", "micromark-core-commonmark": "^2.0.0", "micromark-factory-space": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-chunked": "^2.0.0", "micromark-util-combine-extensions": "^2.0.0", "micromark-util-decode-numeric-character-reference": "^2.0.0", "micromark-util-encode": "^2.0.0", "micromark-util-normalize-identifier": "^2.0.0", "micromark-util-resolve-all": "^2.0.0", "micromark-util-sanitize-uri": "^2.0.0", "micromark-util-subtokenize": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-zpe98Q6kvavpCr1NPVSCMebCKfD7CA2NqZ+rykeNhONIJBpc1tFKt9hucLGwha3jNTNI8lHpctWJWoimVF4PfA=="], + + "micromark-core-commonmark": ["micromark-core-commonmark@2.0.3", "", { "dependencies": { "decode-named-character-reference": "^1.0.0", "devlop": "^1.0.0", "micromark-factory-destination": "^2.0.0", "micromark-factory-label": "^2.0.0", "micromark-factory-space": "^2.0.0", "micromark-factory-title": "^2.0.0", "micromark-factory-whitespace": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-chunked": "^2.0.0", "micromark-util-classify-character": "^2.0.0", "micromark-util-html-tag-name": "^2.0.0", "micromark-util-normalize-identifier": "^2.0.0", "micromark-util-resolve-all": "^2.0.0", "micromark-util-subtokenize": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-RDBrHEMSxVFLg6xvnXmb1Ayr2WzLAWjeSATAoxwKYJV94TeNavgoIdA0a9ytzDSVzBy2YKFK+emCPOEibLeCrg=="], + + "micromark-extension-gfm": ["micromark-extension-gfm@3.0.0", "", { "dependencies": { "micromark-extension-gfm-autolink-literal": "^2.0.0", "micromark-extension-gfm-footnote": "^2.0.0", "micromark-extension-gfm-strikethrough": "^2.0.0", "micromark-extension-gfm-table": "^2.0.0", "micromark-extension-gfm-tagfilter": "^2.0.0", "micromark-extension-gfm-task-list-item": "^2.0.0", "micromark-util-combine-extensions": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-vsKArQsicm7t0z2GugkCKtZehqUm31oeGBV/KVSorWSy8ZlNAv7ytjFhvaryUiCUJYqs+NoE6AFhpQvBTM6Q4w=="], + + "micromark-extension-gfm-autolink-literal": ["micromark-extension-gfm-autolink-literal@2.1.0", "", { "dependencies": { "micromark-util-character": "^2.0.0", "micromark-util-sanitize-uri": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-oOg7knzhicgQ3t4QCjCWgTmfNhvQbDDnJeVu9v81r7NltNCVmhPy1fJRX27pISafdjL+SVc4d3l48Gb6pbRypw=="], + + "micromark-extension-gfm-footnote": ["micromark-extension-gfm-footnote@2.1.0", "", { "dependencies": { "devlop": "^1.0.0", "micromark-core-commonmark": "^2.0.0", "micromark-factory-space": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-normalize-identifier": "^2.0.0", "micromark-util-sanitize-uri": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-/yPhxI1ntnDNsiHtzLKYnE3vf9JZ6cAisqVDauhp4CEHxlb4uoOTxOCJ+9s51bIB8U1N1FJ1RXOKTIlD5B/gqw=="], + + "micromark-extension-gfm-strikethrough": ["micromark-extension-gfm-strikethrough@2.1.0", "", { "dependencies": { "devlop": "^1.0.0", "micromark-util-chunked": "^2.0.0", "micromark-util-classify-character": "^2.0.0", "micromark-util-resolve-all": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-ADVjpOOkjz1hhkZLlBiYA9cR2Anf8F4HqZUO6e5eDcPQd0Txw5fxLzzxnEkSkfnD0wziSGiv7sYhk/ktvbf1uw=="], + + "micromark-extension-gfm-table": ["micromark-extension-gfm-table@2.1.1", "", { "dependencies": { "devlop": "^1.0.0", "micromark-factory-space": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-t2OU/dXXioARrC6yWfJ4hqB7rct14e8f7m0cbI5hUmDyyIlwv5vEtooptH8INkbLzOatzKuVbQmAYcbWoyz6Dg=="], + + "micromark-extension-gfm-tagfilter": ["micromark-extension-gfm-tagfilter@2.0.0", "", { "dependencies": { "micromark-util-types": "^2.0.0" } }, "sha512-xHlTOmuCSotIA8TW1mDIM6X2O1SiX5P9IuDtqGonFhEK0qgRI4yeC6vMxEV2dgyr2TiD+2PQ10o+cOhdVAcwfg=="], + + "micromark-extension-gfm-task-list-item": ["micromark-extension-gfm-task-list-item@2.1.0", "", { "dependencies": { "devlop": "^1.0.0", "micromark-factory-space": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-qIBZhqxqI6fjLDYFTBIa4eivDMnP+OZqsNwmQ3xNLE4Cxwc+zfQEfbs6tzAo2Hjq+bh6q5F+Z8/cksrLFYWQQw=="], + + "micromark-factory-destination": ["micromark-factory-destination@2.0.1", "", { "dependencies": { "micromark-util-character": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-Xe6rDdJlkmbFRExpTOmRj9N3MaWmbAgdpSrBQvCFqhezUn4AHqJHbaEnfbVYYiexVSs//tqOdY/DxhjdCiJnIA=="], + + "micromark-factory-label": ["micromark-factory-label@2.0.1", "", { "dependencies": { "devlop": "^1.0.0", "micromark-util-character": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-VFMekyQExqIW7xIChcXn4ok29YE3rnuyveW3wZQWWqF4Nv9Wk5rgJ99KzPvHjkmPXF93FXIbBp6YdW3t71/7Vg=="], + + "micromark-factory-space": ["micromark-factory-space@2.0.1", "", { "dependencies": { "micromark-util-character": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-zRkxjtBxxLd2Sc0d+fbnEunsTj46SWXgXciZmHq0kDYGnck/ZSGj9/wULTV95uoeYiK5hRXP2mJ98Uo4cq/LQg=="], + + "micromark-factory-title": ["micromark-factory-title@2.0.1", "", { "dependencies": { "micromark-factory-space": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-5bZ+3CjhAd9eChYTHsjy6TGxpOFSKgKKJPJxr293jTbfry2KDoWkhBb6TcPVB4NmzaPhMs1Frm9AZH7OD4Cjzw=="], + + "micromark-factory-whitespace": ["micromark-factory-whitespace@2.0.1", "", { "dependencies": { "micromark-factory-space": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-Ob0nuZ3PKt/n0hORHyvoD9uZhr+Za8sFoP+OnMcnWK5lngSzALgQYKMr9RJVOWLqQYuyn6ulqGWSXdwf6F80lQ=="], + + "micromark-util-character": ["micromark-util-character@2.1.1", "", { "dependencies": { "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q=="], + + "micromark-util-chunked": ["micromark-util-chunked@2.0.1", "", { "dependencies": { "micromark-util-symbol": "^2.0.0" } }, "sha512-QUNFEOPELfmvv+4xiNg2sRYeS/P84pTW0TCgP5zc9FpXetHY0ab7SxKyAQCNCc1eK0459uoLI1y5oO5Vc1dbhA=="], + + "micromark-util-classify-character": ["micromark-util-classify-character@2.0.1", "", { "dependencies": { "micromark-util-character": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-K0kHzM6afW/MbeWYWLjoHQv1sgg2Q9EccHEDzSkxiP/EaagNzCm7T/WMKZ3rjMbvIpvBiZgwR3dKMygtA4mG1Q=="], + + "micromark-util-combine-extensions": ["micromark-util-combine-extensions@2.0.1", "", { "dependencies": { "micromark-util-chunked": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-OnAnH8Ujmy59JcyZw8JSbK9cGpdVY44NKgSM7E9Eh7DiLS2E9RNQf0dONaGDzEG9yjEl5hcqeIsj4hfRkLH/Bg=="], + + "micromark-util-decode-numeric-character-reference": ["micromark-util-decode-numeric-character-reference@2.0.2", "", { "dependencies": { "micromark-util-symbol": "^2.0.0" } }, "sha512-ccUbYk6CwVdkmCQMyr64dXz42EfHGkPQlBj5p7YVGzq8I7CtjXZJrubAYezf7Rp+bjPseiROqe7G6foFd+lEuw=="], + + "micromark-util-decode-string": ["micromark-util-decode-string@2.0.1", "", { "dependencies": { "decode-named-character-reference": "^1.0.0", "micromark-util-character": "^2.0.0", "micromark-util-decode-numeric-character-reference": "^2.0.0", "micromark-util-symbol": "^2.0.0" } }, "sha512-nDV/77Fj6eH1ynwscYTOsbK7rR//Uj0bZXBwJZRfaLEJ1iGBR6kIfNmlNqaqJf649EP0F3NWNdeJi03elllNUQ=="], + + "micromark-util-encode": ["micromark-util-encode@2.0.1", "", {}, "sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw=="], + + "micromark-util-html-tag-name": ["micromark-util-html-tag-name@2.0.1", "", {}, "sha512-2cNEiYDhCWKI+Gs9T0Tiysk136SnR13hhO8yW6BGNyhOC4qYFnwF1nKfD3HFAIXA5c45RrIG1ub11GiXeYd1xA=="], + + "micromark-util-normalize-identifier": ["micromark-util-normalize-identifier@2.0.1", "", { "dependencies": { "micromark-util-symbol": "^2.0.0" } }, "sha512-sxPqmo70LyARJs0w2UclACPUUEqltCkJ6PhKdMIDuJ3gSf/Q+/GIe3WKl0Ijb/GyH9lOpUkRAO2wp0GVkLvS9Q=="], + + "micromark-util-resolve-all": ["micromark-util-resolve-all@2.0.1", "", { "dependencies": { "micromark-util-types": "^2.0.0" } }, "sha512-VdQyxFWFT2/FGJgwQnJYbe1jjQoNTS4RjglmSjTUlpUMa95Htx9NHeYW4rGDJzbjvCsl9eLjMQwGeElsqmzcHg=="], + + "micromark-util-sanitize-uri": ["micromark-util-sanitize-uri@2.0.1", "", { "dependencies": { "micromark-util-character": "^2.0.0", "micromark-util-encode": "^2.0.0", "micromark-util-symbol": "^2.0.0" } }, "sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ=="], + + "micromark-util-subtokenize": ["micromark-util-subtokenize@2.1.0", "", { "dependencies": { "devlop": "^1.0.0", "micromark-util-chunked": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-XQLu552iSctvnEcgXw6+Sx75GflAPNED1qx7eBJ+wydBb2KCbRZe+NwvIEEMM83uml1+2WSXpBAcp9IUCgCYWA=="], + + "micromark-util-symbol": ["micromark-util-symbol@2.0.1", "", {}, "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q=="], + + "micromark-util-types": ["micromark-util-types@2.0.2", "", {}, "sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA=="], + "micromatch": ["micromatch@4.0.8", "", { "dependencies": { "braces": "^3.0.3", "picomatch": "^2.3.1" } }, "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA=="], "miller-rabin": ["miller-rabin@4.0.1", "", { "dependencies": { "bn.js": "^4.0.0", "brorand": "^1.0.1" }, "bin": { "miller-rabin": "bin/miller-rabin" } }, "sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA=="], @@ -1429,6 +1595,8 @@ "parse-asn1": ["parse-asn1@5.1.7", "", { "dependencies": { "asn1.js": "^4.10.1", "browserify-aes": "^1.2.0", "evp_bytestokey": "^1.0.3", "hash-base": "~3.0", "pbkdf2": "^3.1.2", "safe-buffer": "^5.2.1" } }, "sha512-CTM5kuWR3sx9IFamcl5ErfPl6ea/N8IYwiJ+vpeB2g+1iknv7zBl5uPwbMbRVznRVbrNY6lGuDoE5b30grmbqg=="], + "parse-entities": ["parse-entities@4.0.2", "", { "dependencies": { "@types/unist": "^2.0.0", "character-entities-legacy": "^3.0.0", "character-reference-invalid": "^2.0.0", "decode-named-character-reference": "^1.0.0", "is-alphanumerical": "^2.0.0", "is-decimal": "^2.0.0", "is-hexadecimal": "^2.0.0" } }, "sha512-GG2AQYWoLgL877gQIKeRPGO1xF9+eG1ujIb5soS5gPvLQ1y2o8FL90w2QWNdf9I361Mpp7726c+lj3U0qK1uGw=="], + "parse-json": ["parse-json@5.2.0", "", { "dependencies": { "@babel/code-frame": "^7.0.0", "error-ex": "^1.3.1", "json-parse-even-better-errors": "^2.3.0", "lines-and-columns": "^1.1.6" } }, "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg=="], "pascal-case": ["pascal-case@3.1.2", "", { "dependencies": { "no-case": "^3.0.4", "tslib": "^2.0.3" } }, "sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g=="], @@ -1485,6 +1653,8 @@ "prop-types": ["prop-types@15.8.1", "", { "dependencies": { "loose-envify": "^1.4.0", "object-assign": "^4.1.1", "react-is": "^16.13.1" } }, "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg=="], + "property-information": ["property-information@7.2.0", "", {}, "sha512-IAtzIB6sUiWaJYrX9smp3V46pBGbBeLFRGdh25kg1334VcBlD8HzhPeNIWQH9zhGmo2itIe25EHt9dQP7G5hmg=="], + "public-encrypt": ["public-encrypt@4.0.3", "", { "dependencies": { "bn.js": "^4.1.0", "browserify-rsa": "^4.0.0", "create-hash": "^1.1.0", "parse-asn1": "^5.0.0", "randombytes": "^2.0.1", "safe-buffer": "^5.1.2" } }, "sha512-zVpa8oKZSz5bTMTFClc1fQOnyyEzpl5ozpi1B5YcvBrdohMjH2rfsBtyXcuNuwjsDIXmBYlF2N5FlJYhR29t8Q=="], "punycode": ["punycode@2.3.1", "", {}, "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg=="], @@ -1513,6 +1683,8 @@ "react-is": ["react-is@16.13.1", "", {}, "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="], + "react-markdown": ["react-markdown@10.1.0", "", { "dependencies": { "@types/hast": "^3.0.0", "@types/mdast": "^4.0.0", "devlop": "^1.0.0", "hast-util-to-jsx-runtime": "^2.0.0", "html-url-attributes": "^3.0.0", "mdast-util-to-hast": "^13.0.0", "remark-parse": "^11.0.0", "remark-rehype": "^11.0.0", "unified": "^11.0.0", "unist-util-visit": "^5.0.0", "vfile": "^6.0.0" }, "peerDependencies": { "@types/react": ">=18", "react": ">=18" } }, "sha512-qKxVopLT/TyA6BX3Ue5NwabOsAzm0Q7kAPwq6L+wWDwisYs7R8vZ0nRXqq6rkueboxpkjvLGU9fWifiX/ZZFxQ=="], + "react-refresh": ["react-refresh@0.14.2", "", {}, "sha512-jCvmsr+1IUSMUyzOkRcvnVbX3ZYC6g9TDrDbFuFmRDq7PD4yaGbLKNQL6k2jnArV8hjYxh7hVhAZB6s9HDGpZA=="], "react-remove-scroll": ["react-remove-scroll@2.7.0", "", { "dependencies": { "react-remove-scroll-bar": "^2.3.7", "react-style-singleton": "^2.2.3", "tslib": "^2.1.0", "use-callback-ref": "^1.3.3", "use-sidecar": "^1.1.3" }, "peerDependencies": { "@types/react": "*", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-sGsQtcjMqdQyijAHytfGEELB8FufGbfXIsvUTe+NLx1GDRJCXtCFLBLUI1eyZCKXXvbEU2C6gai0PZKoIE9Vbg=="], @@ -1547,8 +1719,20 @@ "regjsparser": ["regjsparser@0.12.0", "", { "dependencies": { "jsesc": "~3.0.2" }, "bin": { "regjsparser": "bin/parser" } }, "sha512-cnE+y8bz4NhMjISKbgeVJtqNbtf5QpjZP+Bslo+UqkIt9QPnX9q095eiRRASJG1/tz6dlNr6Z5NsBiWYokp6EQ=="], + "rehype-highlight": ["rehype-highlight@7.0.2", "", { "dependencies": { "@types/hast": "^3.0.0", "hast-util-to-text": "^4.0.0", "lowlight": "^3.0.0", "unist-util-visit": "^5.0.0", "vfile": "^6.0.0" } }, "sha512-k158pK7wdC2qL3M5NcZROZ2tR/l7zOzjxXd5VGdcfIyoijjQqpHd3JKtYSBDpDZ38UI2WJWuFAtkMDxmx5kstA=="], + "relateurl": ["relateurl@0.2.7", "", {}, "sha512-G08Dxvm4iDN3MLM0EsP62EDV9IuhXPR6blNz6Utcp7zyV3tr4HVNINt6MpaRWbxoOHT3Q7YN2P+jaHX8vUbgog=="], + "remark-breaks": ["remark-breaks@4.0.0", "", { "dependencies": { "@types/mdast": "^4.0.0", "mdast-util-newline-to-break": "^2.0.0", "unified": "^11.0.0" } }, "sha512-IjEjJOkH4FuJvHZVIW0QCDWxcG96kCq7An/KVH2NfJe6rKZU2AsHeB3OEjPNRxi4QC34Xdx7I2KGYn6IpT7gxQ=="], + + "remark-gfm": ["remark-gfm@4.0.1", "", { "dependencies": { "@types/mdast": "^4.0.0", "mdast-util-gfm": "^3.0.0", "micromark-extension-gfm": "^3.0.0", "remark-parse": "^11.0.0", "remark-stringify": "^11.0.0", "unified": "^11.0.0" } }, "sha512-1quofZ2RQ9EWdeN34S79+KExV1764+wCUGop5CPL1WGdD0ocPpu91lzPGbwWMECpEpd42kJGQwzRfyov9j4yNg=="], + + "remark-parse": ["remark-parse@11.0.0", "", { "dependencies": { "@types/mdast": "^4.0.0", "mdast-util-from-markdown": "^2.0.0", "micromark-util-types": "^2.0.0", "unified": "^11.0.0" } }, "sha512-FCxlKLNGknS5ba/1lmpYijMUzX2esxW5xQqjWxw2eHFfS2MSdaHVINFmhjo+qN1WhZhNimq0dZATN9pH0IDrpA=="], + + "remark-rehype": ["remark-rehype@11.1.2", "", { "dependencies": { "@types/hast": "^3.0.0", "@types/mdast": "^4.0.0", "mdast-util-to-hast": "^13.0.0", "unified": "^11.0.0", "vfile": "^6.0.0" } }, "sha512-Dh7l57ianaEoIpzbp0PC9UKAdCSVklD8E5Rpw7ETfbTl3FqcOOgq5q2LVDhgGCkaBv7p24JXikPdvhhmHvKMsw=="], + + "remark-stringify": ["remark-stringify@11.0.0", "", { "dependencies": { "@types/mdast": "^4.0.0", "mdast-util-to-markdown": "^2.0.0", "unified": "^11.0.0" } }, "sha512-1OSmLd3awB/t8qdoEOMazZkNsfVTeY4fTsgzcQFdXNq8ToTN4ZGwrMnlda4K6smTFKD+GRV6O48i6Z4iKgPPpw=="], + "renderkid": ["renderkid@3.0.0", "", { "dependencies": { "css-select": "^4.1.3", "dom-converter": "^0.2.0", "htmlparser2": "^6.1.0", "lodash": "^4.17.21", "strip-ansi": "^6.0.1" } }, "sha512-q/7VIQA8lmM1hF+jn+sFSPWGlMkSAeNYcPLmDQx2zzuiDfaLrOmumR8iaUKlenFgh0XRPIUeSPlH3A+AW3Z5pg=="], "require-from-string": ["require-from-string@2.0.2", "", {}, "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw=="], @@ -1623,6 +1807,8 @@ "source-map-support": ["source-map-support@0.5.21", "", { "dependencies": { "buffer-from": "^1.0.0", "source-map": "^0.6.0" } }, "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w=="], + "space-separated-tokens": ["space-separated-tokens@2.0.2", "", {}, "sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q=="], + "stable-hash": ["stable-hash@0.0.5", "", {}, "sha512-+L3ccpzibovGXFK+Ap/f8LOS0ahMrHTf3xu7mMLSpEGU0EO9ucaysSylKo9eRDFNhWve/y275iPmIZ4z39a9iA=="], "stackframe": ["stackframe@1.3.4", "", {}, "sha512-oeVtt7eWQS+Na6F//S4kJ2K2VbRlS9D43mAlMyVpVWovy9o+jfgH8O9agzANzaiLjclA0oYzUXEM4PurhSUChw=="], @@ -1647,6 +1833,8 @@ "string_decoder": ["string_decoder@1.3.0", "", { "dependencies": { "safe-buffer": "~5.2.0" } }, "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA=="], + "stringify-entities": ["stringify-entities@4.0.4", "", { "dependencies": { "character-entities-html4": "^2.0.0", "character-entities-legacy": "^3.0.0" } }, "sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg=="], + "strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="], "strip-bom": ["strip-bom@3.0.0", "", {}, "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA=="], @@ -1659,6 +1847,10 @@ "style-loader": ["style-loader@3.3.4", "", { "peerDependencies": { "webpack": "^5.0.0" } }, "sha512-0WqXzrsMTyb8yjZJHDqwmnwRJvhALK9LfRtRc6B4UTWe8AijYLZYZ9thuJTZc2VfQWINADW/j+LiJnfy2RoC1w=="], + "style-to-js": ["style-to-js@1.1.21", "", { "dependencies": { "style-to-object": "1.0.14" } }, "sha512-RjQetxJrrUJLQPHbLku6U/ocGtzyjbJMP9lCNK7Ag0CNh690nSH8woqWH9u16nMjYBAok+i7JO1NP2pOy8IsPQ=="], + + "style-to-object": ["style-to-object@1.0.14", "", { "dependencies": { "inline-style-parser": "0.2.7" } }, "sha512-LIN7rULI0jBscWQYaSswptyderlarFkjQ+t79nzty8tcIAceVomEVlLzH5VP4Cmsv6MtKhs7qaAiwlcp+Mgaxw=="], + "styled-jsx": ["styled-jsx@5.1.6", "", { "dependencies": { "client-only": "0.0.1" }, "peerDependencies": { "react": ">= 16.8.0 || 17.x.x || ^18.0.0-0 || ^19.0.0-0" } }, "sha512-qSVyDTeMotdvQYoHWLNGwRFJHC+i+ZvdBRYosOFgC+Wg1vx4frN2/RG/NA7SYqqvKNLf39P2LSRA2pu6n0XYZA=="], "supports-color": ["supports-color@7.2.0", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw=="], @@ -1689,8 +1881,12 @@ "to-regex-range": ["to-regex-range@5.0.1", "", { "dependencies": { "is-number": "^7.0.0" } }, "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ=="], + "trim-lines": ["trim-lines@3.0.1", "", {}, "sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg=="], + "trim-repeated": ["trim-repeated@1.0.0", "", { "dependencies": { "escape-string-regexp": "^1.0.2" } }, "sha512-pkonvlKk8/ZuR0D5tLW8ljt5I8kmxp2XKymhepUeOdCEfKpZaktSArkLHZt76OB1ZvO9bssUsDty4SWhLvZpLg=="], + "trough": ["trough@2.2.0", "", {}, "sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw=="], + "ts-api-utils": ["ts-api-utils@2.1.0", "", { "peerDependencies": { "typescript": ">=4.8.4" } }, "sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ=="], "ts-dedent": ["ts-dedent@2.2.0", "", {}, "sha512-q5W7tVM71e2xjHZTlgfTDoPF/SmqKG5hddq9SzR49CH2hayqRKJtQ4mtRlSxKaJlR/+9rEM+mnBHf7I2/BQcpQ=="], @@ -1719,6 +1915,8 @@ "typescript": ["typescript@5.8.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ=="], + "uc.micro": ["uc.micro@3.0.0", "", {}, "sha512-U3PppEkleoTnIfi8BozMx3yju3qc/L6SwqWo2Sw+54PX+PX0q9I+r1Um5HCmqD7n9VDX5/v3vQH/AjA6deDdtw=="], + "unbox-primitive": ["unbox-primitive@1.1.0", "", { "dependencies": { "call-bound": "^1.0.3", "has-bigints": "^1.0.2", "has-symbols": "^1.1.0", "which-boxed-primitive": "^1.1.1" } }, "sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw=="], "undici-types": ["undici-types@6.19.8", "", {}, "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw=="], @@ -1733,6 +1931,20 @@ "unicorn-magic": ["unicorn-magic@0.1.0", "", {}, "sha512-lRfVq8fE8gz6QMBuDM6a+LO3IAzTi05H6gCVaUpir2E1Rwpo4ZUog45KpNXKC/Mn3Yb9UDuHumeFTo9iV/D9FQ=="], + "unified": ["unified@11.0.5", "", { "dependencies": { "@types/unist": "^3.0.0", "bail": "^2.0.0", "devlop": "^1.0.0", "extend": "^3.0.0", "is-plain-obj": "^4.0.0", "trough": "^2.0.0", "vfile": "^6.0.0" } }, "sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA=="], + + "unist-util-find-after": ["unist-util-find-after@5.0.0", "", { "dependencies": { "@types/unist": "^3.0.0", "unist-util-is": "^6.0.0" } }, "sha512-amQa0Ep2m6hE2g72AugUItjbuM8X8cGQnFoHk0pGfrFeT9GZhzN5SW8nRsiGKK7Aif4CrACPENkA6P/Lw6fHGQ=="], + + "unist-util-is": ["unist-util-is@6.0.1", "", { "dependencies": { "@types/unist": "^3.0.0" } }, "sha512-LsiILbtBETkDz8I9p1dQ0uyRUWuaQzd/cuEeS1hoRSyW5E5XGmTzlwY1OrNzzakGowI9Dr/I8HVaw4hTtnxy8g=="], + + "unist-util-position": ["unist-util-position@5.0.0", "", { "dependencies": { "@types/unist": "^3.0.0" } }, "sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA=="], + + "unist-util-stringify-position": ["unist-util-stringify-position@4.0.0", "", { "dependencies": { "@types/unist": "^3.0.0" } }, "sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ=="], + + "unist-util-visit": ["unist-util-visit@5.1.0", "", { "dependencies": { "@types/unist": "^3.0.0", "unist-util-is": "^6.0.0", "unist-util-visit-parents": "^6.0.0" } }, "sha512-m+vIdyeCOpdr/QeQCu2EzxX/ohgS8KbnPDgFni4dQsfSCtpz8UqDyY5GjRru8PDKuYn7Fq19j1CQ+nJSsGKOzg=="], + + "unist-util-visit-parents": ["unist-util-visit-parents@6.0.2", "", { "dependencies": { "@types/unist": "^3.0.0", "unist-util-is": "^6.0.0" } }, "sha512-goh1s1TBrqSqukSc8wrjwWhL0hiJxgA8m4kFxGlQ+8FYQ3C/m11FcTs4YYem7V664AhHVvgoQLk890Ssdsr2IQ=="], + "universalify": ["universalify@2.0.1", "", {}, "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw=="], "unrs-resolver": ["unrs-resolver@1.4.1", "", { "optionalDependencies": { "@unrs/resolver-binding-darwin-arm64": "1.4.1", "@unrs/resolver-binding-darwin-x64": "1.4.1", "@unrs/resolver-binding-freebsd-x64": "1.4.1", "@unrs/resolver-binding-linux-arm-gnueabihf": "1.4.1", "@unrs/resolver-binding-linux-arm-musleabihf": "1.4.1", "@unrs/resolver-binding-linux-arm64-gnu": "1.4.1", "@unrs/resolver-binding-linux-arm64-musl": "1.4.1", "@unrs/resolver-binding-linux-ppc64-gnu": "1.4.1", "@unrs/resolver-binding-linux-s390x-gnu": "1.4.1", "@unrs/resolver-binding-linux-x64-gnu": "1.4.1", "@unrs/resolver-binding-linux-x64-musl": "1.4.1", "@unrs/resolver-binding-wasm32-wasi": "1.4.1", "@unrs/resolver-binding-win32-arm64-msvc": "1.4.1", "@unrs/resolver-binding-win32-ia32-msvc": "1.4.1", "@unrs/resolver-binding-win32-x64-msvc": "1.4.1" } }, "sha512-MhPB3wBI5BR8TGieTb08XuYlE8oFVEXdSAgat3psdlRyejl8ojQ8iqPcjh094qCZ1r+TnkxzP6BeCd/umfHckQ=="], @@ -1759,6 +1971,10 @@ "utila": ["utila@0.4.0", "", {}, "sha512-Z0DbgELS9/L/75wZbro8xAnT50pBVFQZ+hUEueGDU5FN51YSCYM+jdxsfCiHjwNP/4LCDD0i/graKpeBnOXKRA=="], + "vfile": ["vfile@6.0.3", "", { "dependencies": { "@types/unist": "^3.0.0", "vfile-message": "^4.0.0" } }, "sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q=="], + + "vfile-message": ["vfile-message@4.0.3", "", { "dependencies": { "@types/unist": "^3.0.0", "unist-util-stringify-position": "^4.0.0" } }, "sha512-QTHzsGd1EhbZs4AsQ20JX1rC3cOlt/IWJruk893DfLRr57lcnOeMaWG4K0JrRta4mIJZKth2Au3mM3u03/JWKw=="], + "vm-browserify": ["vm-browserify@1.1.2", "", {}, "sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ=="], "watchpack": ["watchpack@2.4.4", "", { "dependencies": { "glob-to-regexp": "^0.4.1", "graceful-fs": "^4.1.2" } }, "sha512-c5EGNOiyxxV5qmTtAB7rbiXxi1ooX1pQKMLX/MIabJjRA0SJBQOjKF+KSVfHkr9U1cADPon0mRiVe/riyaiDUA=="], @@ -1797,6 +2013,8 @@ "yocto-queue": ["yocto-queue@0.1.0", "", {}, "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q=="], + "zwitch": ["zwitch@2.0.4", "", {}, "sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A=="], + "@babel/core/semver": ["semver@6.3.1", "", { "bin": "bin/semver.js" }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="], "@babel/helper-compilation-targets/semver": ["semver@6.3.1", "", { "bin": "bin/semver.js" }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="], @@ -1821,7 +2039,7 @@ "@pmmmwh/react-refresh-webpack-plugin/loader-utils": ["loader-utils@2.0.4", "", { "dependencies": { "big.js": "^5.2.2", "emojis-list": "^3.0.0", "json5": "^2.1.2" } }, "sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw=="], - "@radix-ui/react-arrow/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.2", "", { "dependencies": { "@radix-ui/react-slot": "1.2.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-uHa+l/lKfxuDD2zjN/0peM/RhhSmRjr5YWdk/37EnSv1nJ88uvG85DPexSm8HdFQROd2VdERJ6ynXbkCFi+APw=="], + "@radix-ui/react-arrow/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.10", "", { "dependencies": { "@radix-ui/react-slot": "1.3.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-MucOnzh6hR5mid6VpkbglRAMYMjKLqRnGBbjXkzjK52fuQDd1qbkx78a5P40mkcnVXJdEVxm26E9OPAiUq7nBg=="], "@radix-ui/react-collection/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.2", "", { "dependencies": { "@radix-ui/react-slot": "1.2.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-uHa+l/lKfxuDD2zjN/0peM/RhhSmRjr5YWdk/37EnSv1nJ88uvG85DPexSm8HdFQROd2VdERJ6ynXbkCFi+APw=="], @@ -1831,6 +2049,8 @@ "@radix-ui/react-dropdown-menu/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.2", "", { "dependencies": { "@radix-ui/react-slot": "1.2.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-uHa+l/lKfxuDD2zjN/0peM/RhhSmRjr5YWdk/37EnSv1nJ88uvG85DPexSm8HdFQROd2VdERJ6ynXbkCFi+APw=="], + "@radix-ui/react-id/@radix-ui/react-use-layout-effect": ["@radix-ui/react-use-layout-effect@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ=="], + "@radix-ui/react-menu/@radix-ui/primitive": ["@radix-ui/primitive@1.1.2", "", {}, "sha512-XnbHrrprsNqZKQhStrSwgRUQzoCI1glLzdw79xiZPoofhGICeZRSQ3dIxAKH1gb3OHfNf4d6f+vAv3kil2eggA=="], "@radix-ui/react-menu/@radix-ui/react-dismissable-layer": ["@radix-ui/react-dismissable-layer@1.1.9", "", { "dependencies": { "@radix-ui/primitive": "1.1.2", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-primitive": "2.1.2", "@radix-ui/react-use-callback-ref": "1.1.1", "@radix-ui/react-use-escape-keydown": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-way197PiTvNp+WBP7svMJasHl+vibhWGQDb6Mgf5mhEWJkgb85z7Lfl9TUdkqpWsf8GRNmoopx9ZxCyDzmgRMQ=="], @@ -1839,13 +2059,25 @@ "@radix-ui/react-menu/@radix-ui/react-focus-scope": ["@radix-ui/react-focus-scope@1.1.6", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-primitive": "2.1.2", "@radix-ui/react-use-callback-ref": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-r9zpYNUQY+2jWHWZGyddQLL9YHkM/XvSFHVcWs7bdVuxMAnCwTAuy6Pf47Z4nw7dYcUou1vg/VgjjrrH03VeBw=="], + "@radix-ui/react-menu/@radix-ui/react-popper": ["@radix-ui/react-popper@1.2.6", "", { "dependencies": { "@floating-ui/react-dom": "^2.0.0", "@radix-ui/react-arrow": "1.1.6", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-primitive": "2.1.2", "@radix-ui/react-use-callback-ref": "1.1.1", "@radix-ui/react-use-layout-effect": "1.1.1", "@radix-ui/react-use-rect": "1.1.1", "@radix-ui/react-use-size": "1.1.1", "@radix-ui/rect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-7iqXaOWIjDBfIG7aq8CUEeCSsQMLFdn7VEE8TaFz704DtEzpPHR7w/uuzRflvKgltqSAImgcmxQ7fFX3X7wasg=="], + "@radix-ui/react-menu/@radix-ui/react-portal": ["@radix-ui/react-portal@1.1.8", "", { "dependencies": { "@radix-ui/react-primitive": "2.1.2", "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-hQsTUIn7p7fxCPvao/q6wpbxmCwgLrlz+nOrJgC+RwfZqWY/WN+UMqkXzrtKbPrF82P43eCTl3ekeKuyAQbFeg=="], "@radix-ui/react-menu/@radix-ui/react-presence": ["@radix-ui/react-presence@1.1.4", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-ueDqRbdc4/bkaQT3GIpLQssRlFgWaL/U2z/S31qRwwLWoxHLgry3SIfCwhxeQNbirEUXFa+lq3RL3oBYXtcmIA=="], "@radix-ui/react-menu/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.2", "", { "dependencies": { "@radix-ui/react-slot": "1.2.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-uHa+l/lKfxuDD2zjN/0peM/RhhSmRjr5YWdk/37EnSv1nJ88uvG85DPexSm8HdFQROd2VdERJ6ynXbkCFi+APw=="], - "@radix-ui/react-popper/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.2", "", { "dependencies": { "@radix-ui/react-slot": "1.2.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-uHa+l/lKfxuDD2zjN/0peM/RhhSmRjr5YWdk/37EnSv1nJ88uvG85DPexSm8HdFQROd2VdERJ6ynXbkCFi+APw=="], + "@radix-ui/react-popper/@radix-ui/react-compose-refs": ["@radix-ui/react-compose-refs@1.1.5", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-+48PbAAbq3didjJxa+OaWY2ZwgAKsNiRGyeHKszblZMQ+kcpd9pAaT11cMkGEie0vsOi3QdeTE6d5Fe3Gn61kA=="], + + "@radix-ui/react-popper/@radix-ui/react-context": ["@radix-ui/react-context@1.2.2", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-RHCUGwKHDr0hDGg4X7ma4JG4/+12qxw8rkh5QKdDldlCvtja6nUx1Ef/8HVrJze81lEsgLQlqjzjGNHantgnQA=="], + + "@radix-ui/react-popper/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.10", "", { "dependencies": { "@radix-ui/react-slot": "1.3.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-MucOnzh6hR5mid6VpkbglRAMYMjKLqRnGBbjXkzjK52fuQDd1qbkx78a5P40mkcnVXJdEVxm26E9OPAiUq7nBg=="], + + "@radix-ui/react-popper/@radix-ui/react-use-callback-ref": ["@radix-ui/react-use-callback-ref@1.1.4", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-R6OUY2e2fA6Yn6s+VSx5KBV6Nx8LQEhu+cz7LCej18rQ1HLyg9PSC9jP/ZNx0o6FAIK9c0F1kHylzSxKsdlkrQ=="], + + "@radix-ui/react-portal/@radix-ui/react-use-layout-effect": ["@radix-ui/react-use-layout-effect@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ=="], + + "@radix-ui/react-presence/@radix-ui/react-use-layout-effect": ["@radix-ui/react-use-layout-effect@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ=="], "@radix-ui/react-primitive/@radix-ui/react-slot": ["@radix-ui/react-slot@1.2.3", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A=="], @@ -1859,6 +2091,32 @@ "@radix-ui/react-tabs/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.2", "", { "dependencies": { "@radix-ui/react-slot": "1.2.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-uHa+l/lKfxuDD2zjN/0peM/RhhSmRjr5YWdk/37EnSv1nJ88uvG85DPexSm8HdFQROd2VdERJ6ynXbkCFi+APw=="], + "@radix-ui/react-tooltip/@radix-ui/primitive": ["@radix-ui/primitive@1.1.7", "", {}, "sha512-rqWnm76nYT8HoNNqEjpgJ7Pw/DrBj5iBTrmEPo6HTX5+VJyBNOqTdv4g89G63HuR5g0AaENoAcH7Is5fF2kZ8Q=="], + + "@radix-ui/react-tooltip/@radix-ui/react-compose-refs": ["@radix-ui/react-compose-refs@1.1.5", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-+48PbAAbq3didjJxa+OaWY2ZwgAKsNiRGyeHKszblZMQ+kcpd9pAaT11cMkGEie0vsOi3QdeTE6d5Fe3Gn61kA=="], + + "@radix-ui/react-tooltip/@radix-ui/react-context": ["@radix-ui/react-context@1.2.2", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-RHCUGwKHDr0hDGg4X7ma4JG4/+12qxw8rkh5QKdDldlCvtja6nUx1Ef/8HVrJze81lEsgLQlqjzjGNHantgnQA=="], + + "@radix-ui/react-tooltip/@radix-ui/react-dismissable-layer": ["@radix-ui/react-dismissable-layer@1.1.19", "", { "dependencies": { "@radix-ui/primitive": "1.1.7", "@radix-ui/react-compose-refs": "1.1.5", "@radix-ui/react-primitive": "2.1.10", "@radix-ui/react-use-callback-ref": "1.1.4", "@radix-ui/react-use-effect-event": "0.0.5" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-8g4pfOL9HoKKLWGiypT+dphVqjFfmcXO5GBnhsG6zI+lxAx/8feQpr+1LSN8Re3hiZ+XkLNS4O9ztK11/LzQ6w=="], + + "@radix-ui/react-tooltip/@radix-ui/react-id": ["@radix-ui/react-id@1.1.4", "", { "dependencies": { "@radix-ui/react-use-layout-effect": "1.1.4" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-TMQp2llA+RYn7JcjnrMnz7wN4pcVttPZnRZo52PLQsoLVKzNlVwUeHmfePgTgRluXFvlD3GD5g5MOVVTJCO0qA=="], + + "@radix-ui/react-tooltip/@radix-ui/react-portal": ["@radix-ui/react-portal@1.1.17", "", { "dependencies": { "@radix-ui/react-primitive": "2.1.10", "@radix-ui/react-use-layout-effect": "1.1.4" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-vKQLcWypUnwZVvfV7UkGahH2g6ySe8M8R+zYBwPrv5byZ9QAW6cQVvNKo7GgmD+p8aYb6D9JBuvy8/WhOno2wQ=="], + + "@radix-ui/react-tooltip/@radix-ui/react-presence": ["@radix-ui/react-presence@1.1.10", "", { "dependencies": { "@radix-ui/react-use-layout-effect": "1.1.4" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-3wyzCQ6+ubRA+D4uv9m95JYLXxmOHp05qjrkjeA7uKHHtjpPggQzc6DAb0URl7j67oR0K2foO4ip27TiX037Bw=="], + + "@radix-ui/react-tooltip/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.10", "", { "dependencies": { "@radix-ui/react-slot": "1.3.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-MucOnzh6hR5mid6VpkbglRAMYMjKLqRnGBbjXkzjK52fuQDd1qbkx78a5P40mkcnVXJdEVxm26E9OPAiUq7nBg=="], + + "@radix-ui/react-tooltip/@radix-ui/react-slot": ["@radix-ui/react-slot@1.3.3", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.5" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-qx7oqnYbxnK9kYI9m317qmFmEgo6ywqWvbTogdj7cL9p3/yx4M48p7Rnw5z3H890cL/ow/EeWJsuTykeZVXP5Q=="], + + "@radix-ui/react-tooltip/@radix-ui/react-use-controllable-state": ["@radix-ui/react-use-controllable-state@1.2.6", "", { "dependencies": { "@radix-ui/primitive": "1.1.7", "@radix-ui/react-use-effect-event": "0.0.5", "@radix-ui/react-use-layout-effect": "1.1.4" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-uEQJGT97ZA/TgP/Hydw47lHu+/vQj6z/0jA+WeTbK1o9Rx45GImjpD0tc3W5ad3D6XTSR6e1yEO0FvGq6WQfVQ=="], + + "@radix-ui/react-use-controllable-state/@radix-ui/react-use-layout-effect": ["@radix-ui/react-use-layout-effect@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ=="], + + "@radix-ui/react-use-effect-event/@radix-ui/react-use-layout-effect": ["@radix-ui/react-use-layout-effect@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ=="], + + "@radix-ui/react-visually-hidden/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.10", "", { "dependencies": { "@radix-ui/react-slot": "1.3.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-MucOnzh6hR5mid6VpkbglRAMYMjKLqRnGBbjXkzjK52fuQDd1qbkx78a5P40mkcnVXJdEVxm26E9OPAiUq7nBg=="], + "@storybook/preset-react-webpack/find-up": ["find-up@7.0.0", "", { "dependencies": { "locate-path": "^7.2.0", "path-exists": "^5.0.0", "unicorn-magic": "^0.1.0" } }, "sha512-YyZM99iHrqLKjmt4LJDj58KI+fYyufRLBSYcqycxf//KpBk9FoewoGX0450m9nB44qrZnovzC2oeP5hUibxc/g=="], "@storybook/react-docgen-typescript-plugin/debug": ["debug@4.4.1", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ=="], @@ -1875,6 +2133,8 @@ "@types/eslint-scope/@types/estree": ["@types/estree@1.0.8", "", {}, "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w=="], + "@types/estree-jsx/@types/estree": ["@types/estree@1.0.8", "", {}, "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w=="], + "@typescript-eslint/typescript-estree/fast-glob": ["fast-glob@3.3.3", "", { "dependencies": { "@nodelib/fs.stat": "^2.0.2", "@nodelib/fs.walk": "^1.2.3", "glob-parent": "^5.1.2", "merge2": "^1.3.0", "micromatch": "^4.0.8" } }, "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg=="], "@typescript-eslint/typescript-estree/minimatch": ["minimatch@9.0.5", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow=="], @@ -1941,16 +2201,24 @@ "hash-base/safe-buffer": ["safe-buffer@5.2.1", "", {}, "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="], + "hast-util-to-jsx-runtime/@types/estree": ["@types/estree@1.0.8", "", {}, "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w=="], + "html-minifier-terser/commander": ["commander@8.3.0", "", {}, "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww=="], "jest-worker/supports-color": ["supports-color@8.1.1", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q=="], "lightningcss/detect-libc": ["detect-libc@2.0.3", "", {}, "sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw=="], + "lowlight/highlight.js": ["highlight.js@11.11.2", "", {}, "sha512-oaXMACAU0kzOMXBjWpNcX+vlwSBCIAiZ9BHa7gA15NOTtT2L/l8OSZDuqS2XppOhZBPJ7hm4o8ep2kyuip2uEQ=="], + "make-dir/semver": ["semver@6.3.1", "", { "bin": "bin/semver.js" }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="], "md5.js/safe-buffer": ["safe-buffer@5.2.1", "", {}, "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="], + "mdast-util-find-and-replace/escape-string-regexp": ["escape-string-regexp@5.0.0", "", {}, "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw=="], + + "micromark/debug": ["debug@4.4.1", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ=="], + "micromatch/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="], "miller-rabin/bn.js": ["bn.js@4.12.2", "", {}, "sha512-n4DSx829VRTRByMRGdjQ9iqsN0Bh4OolPsFnaZBLcbi8iXcB+kJ9s7EnRt4wILZNV3kPLHkRVfOc/HvhC3ovDw=="], @@ -1961,6 +2229,8 @@ "parse-asn1/safe-buffer": ["safe-buffer@5.2.1", "", {}, "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="], + "parse-entities/@types/unist": ["@types/unist@2.0.11", "", {}, "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA=="], + "pbkdf2/create-hash": ["create-hash@1.1.3", "", { "dependencies": { "cipher-base": "^1.0.1", "inherits": "^2.0.1", "ripemd160": "^2.0.0", "sha.js": "^2.4.0" } }, "sha512-snRpch/kwQhcdlnZKYanNF1m0RDlrCdSKQaH87w1FCFPVPNCQ/Il9QJKAX2jVBZddRdaHBMC+zXa9Gw9tmkNUA=="], "pbkdf2/ripemd160": ["ripemd160@2.0.1", "", { "dependencies": { "hash-base": "^2.0.0", "inherits": "^2.0.1" } }, "sha512-J7f4wutN8mdbV08MJnXibYpCOPHR+yzy+iQ/AsjMv2j8cLavQ8VGagDFUwwTAdF8FmRKVeNpbTTEwNHCW1g94w=="], @@ -2043,6 +2313,34 @@ "which-builtin-type/isarray": ["isarray@2.0.5", "", {}, "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw=="], + "@radix-ui/react-arrow/@radix-ui/react-primitive/@radix-ui/react-slot": ["@radix-ui/react-slot@1.3.3", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.5" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-qx7oqnYbxnK9kYI9m317qmFmEgo6ywqWvbTogdj7cL9p3/yx4M48p7Rnw5z3H890cL/ow/EeWJsuTykeZVXP5Q=="], + + "@radix-ui/react-menu/@radix-ui/react-popper/@radix-ui/react-arrow": ["@radix-ui/react-arrow@1.1.6", "", { "dependencies": { "@radix-ui/react-primitive": "2.1.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-2JMfHJf/eVnwq+2dewT3C0acmCWD3XiVA1Da+jTDqo342UlU13WvXtqHhG+yJw5JeQmu4ue2eMy6gcEArLBlcw=="], + + "@radix-ui/react-menu/@radix-ui/react-popper/@radix-ui/react-use-layout-effect": ["@radix-ui/react-use-layout-effect@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ=="], + + "@radix-ui/react-menu/@radix-ui/react-popper/@radix-ui/react-use-rect": ["@radix-ui/react-use-rect@1.1.1", "", { "dependencies": { "@radix-ui/rect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-QTYuDesS0VtuHNNvMh+CjlKJ4LJickCMUAqjlE3+j8w+RlRpwyX3apEQKGFzbZGdo7XNG1tXa+bQqIE7HIXT2w=="], + + "@radix-ui/react-menu/@radix-ui/react-popper/@radix-ui/react-use-size": ["@radix-ui/react-use-size@1.1.1", "", { "dependencies": { "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-ewrXRDTAqAXlkl6t/fkXWNAhFX9I+CkKlw6zjEwk86RSPKwZr3xpBRso655aqYafwtnbpHLj6toFzmd6xdVptQ=="], + + "@radix-ui/react-menu/@radix-ui/react-popper/@radix-ui/rect": ["@radix-ui/rect@1.1.1", "", {}, "sha512-HPwpGIzkl28mWyZqG52jiqDJ12waP11Pa1lGoiyUkIEuMLBP0oeK/C89esbXrxsky5we7dfd8U58nm0SgAWpVw=="], + + "@radix-ui/react-menu/@radix-ui/react-portal/@radix-ui/react-use-layout-effect": ["@radix-ui/react-use-layout-effect@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ=="], + + "@radix-ui/react-menu/@radix-ui/react-presence/@radix-ui/react-use-layout-effect": ["@radix-ui/react-use-layout-effect@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ=="], + + "@radix-ui/react-popper/@radix-ui/react-primitive/@radix-ui/react-slot": ["@radix-ui/react-slot@1.3.3", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.5" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-qx7oqnYbxnK9kYI9m317qmFmEgo6ywqWvbTogdj7cL9p3/yx4M48p7Rnw5z3H890cL/ow/EeWJsuTykeZVXP5Q=="], + + "@radix-ui/react-tabs/@radix-ui/react-presence/@radix-ui/react-use-layout-effect": ["@radix-ui/react-use-layout-effect@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ=="], + + "@radix-ui/react-tooltip/@radix-ui/react-dismissable-layer/@radix-ui/react-use-callback-ref": ["@radix-ui/react-use-callback-ref@1.1.4", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-R6OUY2e2fA6Yn6s+VSx5KBV6Nx8LQEhu+cz7LCej18rQ1HLyg9PSC9jP/ZNx0o6FAIK9c0F1kHylzSxKsdlkrQ=="], + + "@radix-ui/react-tooltip/@radix-ui/react-dismissable-layer/@radix-ui/react-use-effect-event": ["@radix-ui/react-use-effect-event@0.0.5", "", { "dependencies": { "@radix-ui/react-use-layout-effect": "1.1.4" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-7cshFL8HGS/7HEiHH+9kL9HBwp2sa9yX18Knwek6KYWmXwM7pegMgta2AXMQKI+rq3JnfSj9x8wYqFMTdG1Jgg=="], + + "@radix-ui/react-tooltip/@radix-ui/react-use-controllable-state/@radix-ui/react-use-effect-event": ["@radix-ui/react-use-effect-event@0.0.5", "", { "dependencies": { "@radix-ui/react-use-layout-effect": "1.1.4" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-7cshFL8HGS/7HEiHH+9kL9HBwp2sa9yX18Knwek6KYWmXwM7pegMgta2AXMQKI+rq3JnfSj9x8wYqFMTdG1Jgg=="], + + "@radix-ui/react-visually-hidden/@radix-ui/react-primitive/@radix-ui/react-slot": ["@radix-ui/react-slot@1.3.3", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.5" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-qx7oqnYbxnK9kYI9m317qmFmEgo6ywqWvbTogdj7cL9p3/yx4M48p7Rnw5z3H890cL/ow/EeWJsuTykeZVXP5Q=="], + "@storybook/preset-react-webpack/find-up/locate-path": ["locate-path@7.2.0", "", { "dependencies": { "p-locate": "^6.0.0" } }, "sha512-gvVijfZvn7R+2qyPX8mAuKcFGDf6Nc61GdvGafQsHL0sBIxfKzA+usWn4GFC/bk+QdwPUD4kWFJLhElipq+0VA=="], "@storybook/preset-react-webpack/find-up/path-exists": ["path-exists@5.0.0", "", {}, "sha512-RjhtfwJOxzcFmNOi6ltcbcu4Iu+FL3zEj83dk4kAS+fVpTxXLO1b38RvJgT/0QwvV/L3aY9TAnyv0EOqW4GoMQ=="], @@ -2071,6 +2369,10 @@ "webpack/eslint-scope/estraverse": ["estraverse@4.3.0", "", {}, "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw=="], + "@radix-ui/react-arrow/@radix-ui/react-primitive/@radix-ui/react-slot/@radix-ui/react-compose-refs": ["@radix-ui/react-compose-refs@1.1.5", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-+48PbAAbq3didjJxa+OaWY2ZwgAKsNiRGyeHKszblZMQ+kcpd9pAaT11cMkGEie0vsOi3QdeTE6d5Fe3Gn61kA=="], + + "@radix-ui/react-visually-hidden/@radix-ui/react-primitive/@radix-ui/react-slot/@radix-ui/react-compose-refs": ["@radix-ui/react-compose-refs@1.1.5", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-+48PbAAbq3didjJxa+OaWY2ZwgAKsNiRGyeHKszblZMQ+kcpd9pAaT11cMkGEie0vsOi3QdeTE6d5Fe3Gn61kA=="], + "@storybook/preset-react-webpack/find-up/locate-path/p-locate": ["p-locate@6.0.0", "", { "dependencies": { "p-limit": "^4.0.0" } }, "sha512-wPrq66Llhl7/4AGC6I+cqxT07LhXvWL08LNXz1fENOw0Ap4sRZZ/gZpTTJ5jpurzzzfS2W/Ge9BY3LgLjCShcw=="], "babel-loader/find-cache-dir/pkg-dir/find-up": ["find-up@6.3.0", "", { "dependencies": { "locate-path": "^7.1.0", "path-exists": "^5.0.0" } }, "sha512-v2ZsoEuVHYy8ZIlYqwPe/39Cy+cFDzp4dXPaxNvkEuouymu+2Jbz0PxpKarJHYJTmv2HWT3O382qY8l4jMWthw=="], diff --git a/chat/package.json b/chat/package.json index d382b3a3..fffcbf01 100644 --- a/chat/package.json +++ b/chat/package.json @@ -1,20 +1,27 @@ { "dependencies": { - "@radix-ui/react-checkbox": "^1.3.3", "@radix-ui/react-dialog": "^1.1.15", "@radix-ui/react-dropdown-menu": "^2.1.14", + "@radix-ui/react-tooltip": "^1.1.8", "@radix-ui/react-slot": "^1.2.2", "@radix-ui/react-tabs": "^1.1.11", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", "jszip": "^3.10.1", + "highlight.js": "^11.11.0", + "linkify-it": "^6.1.0", + "lowlight": "^3.3.0", "lucide-react": "^0.511.0", "next": "15.4.10", "next-themes": "^0.4.6", "react": "^19.0.0", "react-dom": "^19.0.0", "react-dropzone": "^14.3.8", + "react-markdown": "^10.1.0", + "rehype-highlight": "^7.0.2", "react-textarea-autosize": "^8.5.9", + "remark-breaks": "^4.0.0", + "remark-gfm": "^4.0.1", "sonner": "^2.0.3", "tailwind-merge": "^3.3.0" }, @@ -23,6 +30,7 @@ "@storybook/addon-themes": "^9.0.17", "@storybook/nextjs": "^9.0.17", "@tailwindcss/postcss": "^4", + "@types/linkify-it": "^5.0.0", "@types/node": "^20", "@types/react": "^19", "@types/react-dom": "^19", @@ -43,10 +51,11 @@ "deploy-gh-pages": "GITHUB_PAGES=true next build && gh-pages -d out --nojekyll -e chat -f", "dev": "next dev --turbopack", "export": "GITHUB_PAGES=true next build", - "lint": "next lint", + "lint": "eslint src", "serve-static": "npx serve out", "start": "next start", - "storybook": "storybook dev -p 6006" + "storybook": "storybook dev -p 6006", + "test": "bun test" }, - "version": "0.12.2" + "version": "0.13.0" } diff --git a/chat/src/app/embed/page.tsx b/chat/src/app/embed/page.tsx index 768acdd1..50934e96 100644 --- a/chat/src/app/embed/page.tsx +++ b/chat/src/app/embed/page.tsx @@ -1,5 +1,6 @@ import { Chat } from "@/components/chat"; import { ChatProvider } from "@/components/chat-provider"; +import { EmbedStatusBar } from "@/components/embed-status-bar"; import { Suspense } from "react"; export default function EmbedPage() { @@ -10,9 +11,10 @@ export default function EmbedPage() { } > -
+
+ -
+
); diff --git a/chat/src/app/globals.css b/chat/src/app/globals.css index b628ac37..01d828be 100644 --- a/chat/src/app/globals.css +++ b/chat/src/app/globals.css @@ -8,20 +8,15 @@ --color-foreground: var(--foreground); --font-sans: var(--font-geist-sans); --font-mono: "Menlo", "DejaVu Sans Mono", "Consolas", "Courier New", monospace; - --color-sidebar-ring: var(--sidebar-ring); - --color-sidebar-border: var(--sidebar-border); - --color-sidebar-accent-foreground: var(--sidebar-accent-foreground); - --color-sidebar-accent: var(--sidebar-accent); - --color-sidebar-primary-foreground: var(--sidebar-primary-foreground); - --color-sidebar-primary: var(--sidebar-primary); - --color-sidebar-foreground: var(--sidebar-foreground); - --color-sidebar: var(--sidebar); - --color-chart-5: var(--chart-5); - --color-chart-4: var(--chart-4); - --color-chart-3: var(--chart-3); - --color-chart-2: var(--chart-2); - --color-chart-1: var(--chart-1); --color-ring: var(--ring); + --color-status-success: var(--status-success); + --color-status-warning: var(--status-warning); + --color-status-error: var(--status-error); + --color-code-block-bg: var(--code-block-bg); + --color-code-block-border: var(--code-block-border); + --color-code-block-text: var(--code-block-text); + --color-search-highlight: var(--search-highlight); + --color-search-highlight-text: var(--search-highlight-text); --color-input: var(--input); --color-border: var(--border); --color-destructive: var(--destructive); @@ -63,19 +58,24 @@ --border: oklch(0.929 0.013 255.508); --input: oklch(0.929 0.013 255.508); --ring: oklch(0.704 0.04 256.788); - --chart-1: oklch(0.646 0.222 41.116); - --chart-2: oklch(0.6 0.118 184.704); - --chart-3: oklch(0.398 0.07 227.392); - --chart-4: oklch(0.828 0.189 84.429); - --chart-5: oklch(0.769 0.188 70.08); - --sidebar: oklch(0.984 0.003 247.858); - --sidebar-foreground: oklch(0.129 0.042 264.695); - --sidebar-primary: oklch(0.208 0.042 265.755); - --sidebar-primary-foreground: oklch(0.984 0.003 247.858); - --sidebar-accent: oklch(0.968 0.007 247.896); - --sidebar-accent-foreground: oklch(0.208 0.042 265.755); - --sidebar-border: oklch(0.929 0.013 255.508); - --sidebar-ring: oklch(0.704 0.04 256.788); + --status-success: oklch(0.55 0.16 145); + --status-warning: oklch(0.75 0.18 55); + --status-error: var(--destructive); + --code-block-bg: oklch(0.14 0.005 285); + --code-block-border: oklch(0.3 0.01 260); + --code-block-text: oklch(0.92 0 0); + --search-highlight: oklch(0.85 0.15 85 / 0.5); + --search-highlight-text: var(--foreground); + --hl-keyword: oklch(0.55 0.2 300); + --hl-string: oklch(0.55 0.16 145); + --hl-comment: oklch(0.58 0.02 250); + --hl-function: oklch(0.55 0.18 250); + --hl-number: oklch(0.6 0.18 40); + --hl-operator: oklch(0.5 0.1 350); + --hl-type: oklch(0.55 0.15 200); + --hl-variable: oklch(0.45 0.08 260); + --surface-glow: oklch(0.93 0.025 250 / 0.75); + color-scheme: light; } .dark { @@ -97,19 +97,24 @@ --border: oklch(0.27 0.01 0); --input: oklch(1 0 0 / 15%); --ring: oklch(0.551 0.027 264.364); - --chart-1: oklch(0.488 0.243 264.376); - --chart-2: oklch(0.696 0.17 162.48); - --chart-3: oklch(0.769 0.188 70.08); - --chart-4: oklch(0.627 0.265 303.9); - --chart-5: oklch(0.645 0.246 16.439); - --sidebar: oklch(0.208 0.042 265.755); - --sidebar-foreground: oklch(0.984 0.003 247.858); - --sidebar-primary: oklch(0.488 0.243 264.376); - --sidebar-primary-foreground: oklch(0.984 0.003 247.858); - --sidebar-accent: oklch(0.279 0.041 260.031); - --sidebar-accent-foreground: oklch(0.984 0.003 247.858); - --sidebar-border: oklch(1 0 0 / 10%); - --sidebar-ring: oklch(0.551 0.027 264.364); + --status-success: oklch(0.7 0.17 155); + --status-warning: oklch(0.8 0.16 70); + --status-error: var(--destructive); + --code-block-bg: oklch(0.16 0.01 270); + --code-block-border: oklch(0.28 0.015 260); + --code-block-text: oklch(0.9 0.005 250); + --search-highlight: oklch(0.65 0.15 85 / 0.4); + --search-highlight-text: var(--foreground); + --hl-keyword: oklch(0.75 0.18 300); + --hl-string: oklch(0.72 0.17 150); + --hl-comment: oklch(0.55 0.02 250); + --hl-function: oklch(0.75 0.16 220); + --hl-number: oklch(0.78 0.16 55); + --hl-operator: oklch(0.72 0.12 350); + --hl-type: oklch(0.72 0.14 195); + --hl-variable: oklch(0.78 0.06 260); + --surface-glow: oklch(0.24 0.025 260 / 0.7); + color-scheme: dark; } @layer base { @@ -118,5 +123,33 @@ } body { @apply bg-background text-foreground; + min-width: 320px; + overscroll-behavior: none; + text-rendering: optimizeLegibility; + } + + button, + [role="button"] { + -webkit-tap-highlight-color: transparent; + } + + button:not(:disabled), + [role="button"]:not([aria-disabled="true"]) { + cursor: pointer; + } + + ::selection { + @apply bg-primary/15; + } +} + +@media (prefers-reduced-motion: reduce) { + *, + *::before, + *::after { + scroll-behavior: auto !important; + animation-duration: 0.01ms !important; + animation-iteration-count: 1 !important; + transition-duration: 0.01ms !important; } } diff --git a/chat/src/app/header.tsx b/chat/src/app/header.tsx index 47b370fe..907fd7b8 100644 --- a/chat/src/app/header.tsx +++ b/chat/src/app/header.tsx @@ -1,37 +1,268 @@ "use client"; -import {AgentType, useChat} from "@/components/chat-provider"; -import {ModeToggle} from "@/components/mode-toggle"; +import { type ComponentType, useEffect, useMemo, useState } from "react"; +import { AgentType, useChat } from "@/components/chat-provider"; +import { ModeToggle } from "@/components/mode-toggle"; +import { Activity, Bot, CircleAlert, CircleCheck, Download, Hash, Keyboard, LoaderCircle, WifiOff } from "lucide-react"; +import { computeTokenTotals, formatTokenCount } from "@/lib/session-status"; +import { KeyboardShortcutsDialog, useKeyboardShortcutsKey } from "@/components/keyboard-shortcuts"; +import { + DropdownMenu, + DropdownMenuContent, + DropdownMenuItem, + DropdownMenuLabel, + DropdownMenuSeparator, + DropdownMenuTrigger, +} from "@/components/ui/dropdown-menu"; export function Header() { - const {serverStatus, agentType} = useChat(); + const { + serverStatus, + connectionStatus, + agentType, + queuedMessages, + richMessages, + messages, + downloadSession, + customTitle, + } = useChat(); + const [runningSince, setRunningSince] = useState(null); + const [elapsedSeconds, setElapsedSeconds] = useState(0); + const [downloading, setDownloading] = useState(false); + const [shortcutsOpen, setShortcutsOpen] = useState(false); + useKeyboardShortcutsKey(() => setShortcutsOpen(true)); + + useEffect(() => { + if (serverStatus !== "running") { + setRunningSince(null); + setElapsedSeconds(0); + return; + } + setRunningSince((current) => current ?? Date.now()); + }, [serverStatus]); + + useEffect(() => { + if (runningSince === null) return; + const updateElapsed = () => + setElapsedSeconds(Math.floor((Date.now() - runningSince) / 1000)); + updateElapsed(); + const timer = window.setInterval(updateElapsed, 1000); + return () => window.clearInterval(timer); + }, [runningSince]); + + const latestTool = useMemo(() => { + const latestTaskTime = [...messages] + .reverse() + .find((message) => message.role === "user")?.time; + for (const message of [...richMessages].reverse()) { + if ( + latestTaskTime && + message.timestamp && + Date.parse(message.timestamp) < Date.parse(latestTaskTime) + ) { + continue; + } + for (const block of [...message.content].reverse()) { + if (block.type === "tool_use" && block.tool_name) return block.tool_name; + } + } + return null; + }, [messages, richMessages]); + + const tokenTotals = useMemo(() => computeTokenTotals(richMessages), [richMessages]); + + const agentStatus = { + stable: { + label: "Ready", + detail: "Agent is ready", + icon: CircleCheck, + className: "text-status-success", + }, + running: { + label: "Working", + detail: "Agent is processing", + icon: LoaderCircle, + className: "text-status-warning", + }, + offline: { + label: "Offline", + detail: "Reconnecting to server", + icon: WifiOff, + className: "text-destructive", + }, + unknown: { + label: "Connecting", + detail: "Waiting for agent status", + icon: CircleAlert, + className: "text-muted-foreground", + }, + }[serverStatus]; + const status = + connectionStatus === "offline" + ? { + label: "Offline", + detail: "Network connection lost", + icon: WifiOff, + className: "text-destructive", + } + : connectionStatus === "reconnecting" + ? { + label: "Reconnecting", + detail: "Reconnecting to agent server", + icon: LoaderCircle, + className: "text-status-warning", + } + : agentStatus; + const StatusIcon = status.icon; + const elapsed = + elapsedSeconds < 60 + ? `${elapsedSeconds}s` + : `${Math.floor(elapsedSeconds / 60)}m ${elapsedSeconds % 60}s`; + const activityDetail = + serverStatus === "running" + ? [latestTool ? `Using ${latestTool}` : "Processing task", elapsed] + .filter(Boolean) + .join(" · ") + : status.detail; return ( -
- AgentAPI Chat - -
- {serverStatus !== "unknown" && ( -
- - Status: - {serverStatus} +
+
+
+ +
+
+
+ {customTitle || "AgentAPI"}
- )} +

+ {agentType === "unknown" + ? "Remote coding agent" + : AgentType[agentType].displayName} +

+
+
- {agentType !== "unknown" && ( -
- {AgentType[agentType].displayName} -
+
+ + + + + + Session details + +
+ + + + + {tokenTotals.total > 0 && ( + + )} +
+ + { + setDownloading(true); + void downloadSession() + .catch(() => { + // The provider reports request failures through the + // rejected promise; keep the menu action retryable. + }) + .finally(() => setDownloading(false)); + }} + > + {downloading ? ( + + ) : ( + + )} + Download session JSONL + + setShortcutsOpen(true)}> + + Keyboard shortcuts + +
+
+ {tokenTotals.total > 0 && ( + + + {formatTokenCount(tokenTotals.total)} + )} - +
+
); } + +function SessionDetail({ + icon: Icon, + label, + value, + valueClassName = "", +}: { + icon: ComponentType<{className?: string}>; + label: string; + value: string; + valueClassName?: string; +}) { + return ( +
+ + {label} + + {value} + +
+ ); +} diff --git a/chat/src/app/hljs-theme.css b/chat/src/app/hljs-theme.css new file mode 100644 index 00000000..f394b3b1 --- /dev/null +++ b/chat/src/app/hljs-theme.css @@ -0,0 +1,80 @@ +/* + * Syntax highlighting theme using design-system tokens. + * Maps highlight.js / rehype-highlight classes to --hl-* CSS custom properties + * defined in globals.css (light + dark variants). + */ + +.hljs-keyword, +.hljs-selector-tag, +.hljs-built_in, +.hljs-literal { + color: var(--hl-keyword); +} + +.hljs-string, +.hljs-doctag, +.hljs-template-variable, +.hljs-template-tag { + color: var(--hl-string); +} + +.hljs-comment, +.hljs-quote { + color: var(--hl-comment); + font-style: italic; +} + +.hljs-title, +.hljs-title\.function_, +.hljs-section { + color: var(--hl-function); +} + +.hljs-number, +.hljs-regexp { + color: var(--hl-number); +} + +.hljs-symbol, +.hljs-operator, +.hljs-punctuation { + color: var(--hl-operator); +} + +.hljs-type, +.hljs-title\.class_, +.hljs-name, +.hljs-tag { + color: var(--hl-type); +} + +.hljs-variable, +.hljs-attr, +.hljs-attribute, +.hljs-property, +.hljs-params { + color: var(--hl-variable); +} + +.hljs-meta, +.hljs-meta .hljs-keyword { + color: var(--hl-operator); +} + +.hljs-deletion { + color: var(--destructive); + background: oklch(from var(--destructive) l c h / 0.1); +} + +.hljs-addition { + color: var(--hl-string); + background: oklch(from var(--hl-string) l c h / 0.1); +} + +.hljs-emphasis { + font-style: italic; +} + +.hljs-strong { + font-weight: bold; +} diff --git a/chat/src/app/layout.tsx b/chat/src/app/layout.tsx index 7c44c440..0d6db7eb 100644 --- a/chat/src/app/layout.tsx +++ b/chat/src/app/layout.tsx @@ -1,8 +1,11 @@ -import type { Metadata } from "next"; +import type { Metadata, Viewport } from "next"; import { Geist } from "next/font/google"; import "./globals.css"; +import "./hljs-theme.css"; import { Toaster } from "@/components/ui/sonner"; +import { TooltipProvider } from "@/components/ui/tooltip"; import { ThemeProvider } from "@/components/theme-provider"; +import {defaultLocale, uiCopy} from "@/lib/ui-copy"; const geistSans = Geist({ variable: "--font-geist-sans", @@ -10,8 +13,14 @@ const geistSans = Geist({ }); export const metadata: Metadata = { - title: "AgentAPI Chat", - description: "A ChatGPT-like interface for AgentAPI", + title: uiCopy.metadata.title, + description: uiCopy.metadata.description, +}; + +export const viewport: Viewport = { + width: "device-width", + initialScale: 1, + viewportFit: "cover", }; export default function RootLayout({ @@ -20,7 +29,7 @@ export default function RootLayout({ children: React.ReactNode; }>) { return ( - + - {children} + + {children} + diff --git a/chat/src/app/page.tsx b/chat/src/app/page.tsx index 1530885d..6540e566 100644 --- a/chat/src/app/page.tsx +++ b/chat/src/app/page.tsx @@ -7,14 +7,39 @@ export default function Home() { return ( Loading chat interface...
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
} > -
+
-
+
); diff --git a/chat/src/components/chat-provider.tsx b/chat/src/components/chat-provider.tsx index 34e04363..e299a875 100644 --- a/chat/src/components/chat-provider.tsx +++ b/chat/src/components/chat-provider.tsx @@ -5,23 +5,40 @@ import { useState, useEffect, useRef, + useCallback, + useMemo, createContext, PropsWithChildren, useContext, } from "react"; import {toast} from "sonner"; import {getErrorMessage} from "@/lib/error-utils"; - -interface Message { +import {getDocumentTitle} from "@/lib/document-title"; +import {getReconnectDelay} from "@/lib/reconnect"; +import {parseFailedMessages} from "@/lib/failed-messages"; +import { + createChatAPI, + type MCPCheckResult, + type MCPConfig, + type MCPProfiles, + type UploadOptions, + type WebhookConfig, +} from "@/lib/chat-api"; + +export interface Message { id: number; role: string; content: string; + time?: string; } // Draft messages are used to optmistically update the UI // before the server responds. -interface DraftMessage extends Omit { +export interface DraftMessage extends Omit { id?: number; + clientId: string; + deliveryStatus: "sending" | "failed"; + error?: string; } interface MessageUpdateEvent { @@ -31,6 +48,34 @@ interface MessageUpdateEvent { time: string; } +export interface RichContentBlock { + type: "text" | "thinking" | "tool_use" | "tool_result"; + text?: string; + thinking?: string; + tool_use_id?: string; + tool_name?: string; + tool_input?: unknown; + status?: "running" | "completed" | "failed"; + is_error?: boolean; +} + +export interface Usage { + input_tokens: number; + output_tokens: number; + cache_creation_input_tokens: number; + cache_read_input_tokens: number; +} + +export interface RichMessage { + message_id: string; + role: string; + content: RichContentBlock[]; + timestamp: string; + usage?: Usage; + model?: string; + stop_reason?: string; +} + interface StatusChangeEvent { status: string; agent_type: string; @@ -42,36 +87,33 @@ interface ErrorEventData { time: string; } -interface APIErrorDetail { - location: string; - message: string; - value: null | string | number | boolean | object; -} - -interface APIErrorModel { - $schema: string; - detail: string; - errors: APIErrorDetail[]; - instance: string; - status: number; - title: string; - type: string; -} - function isDraftMessage(message: Message | DraftMessage): boolean { return message.id === undefined; } type MessageType = "user" | "raw"; +export interface SendResult { + ok: boolean; + queued: boolean; +} + export type ServerStatus = "stable" | "running" | "offline" | "unknown"; +export type ConnectionStatus = "connected" | "reconnecting" | "offline"; export interface FileUploadResponse { ok: boolean; filePath?: string; + error?: string; +} + +export interface QueuedMessage { + id: number; + content: string; + time: string; } -export type AgentType = "claude" | "goose" | "aider" | "gemini" | "amp" | "codex" | "cursor" | "cursor-agent" | "copilot" | "auggie" | "amazonq" | "opencode" | "custom" | "unknown"; +export type AgentType = "claude" | "goose" | "aider" | "gemini" | "amp" | "codex" | "cursor" | "cursor-agent" | "copilot" | "auggie" | "amazonq" | "opencode" | "kimi" | "custom" | "unknown"; export type AgentColorDisplayNamePair = { displayName: string; @@ -90,21 +132,65 @@ export const AgentType: Record, AgentColorDisplayN auggie: {displayName: "Auggie"}, amazonq: {displayName: "Amazon Q"}, opencode: {displayName: "Opencode"}, + kimi: {displayName: "Kimi Code"}, custom: { displayName: "Custom"} } interface ChatContextValue { messages: (Message | DraftMessage)[]; + richMessages: RichMessage[]; loading: boolean; serverStatus: ServerStatus; - sendMessage: (message: string, type?: MessageType) => void; - uploadFiles: (formData: FormData) => Promise; + connectionStatus: ConnectionStatus; + queuedMessages: QueuedMessage[]; + sendMessage: (message: string, type?: MessageType) => Promise; + retryFailedMessage: (clientId: string) => Promise; + dismissFailedMessage: (clientId: string) => void; + updateQueuedMessage: (id: number, content: string) => Promise; + deleteQueuedMessage: (id: number) => Promise; + uploadFiles: ( + formData: FormData, + options?: UploadOptions, + ) => Promise; + reconnectAttempt: number; + nextReconnectAt: number | null; + reconnectNow: () => void; + downloadSession: () => Promise; + deleteMessages: () => Promise; + getWebhook: () => Promise; + updateWebhook: (config: { + url: string; + timeout_seconds: number; + max_attempts: number; + payload_template?: string; + }) => Promise; + getMCP: () => Promise; + updateMCP: ( + servers: Record, + restart?: boolean, + ) => Promise; + checkMCP: (servers?: Record) => Promise; + createMCPServer: (name: string, config: unknown, restart?: boolean) => Promise; + updateMCPServer: (name: string, config: unknown, restart?: boolean) => Promise; + deleteMCPServer: (name: string, restart?: boolean) => Promise; + getMCPProfiles: () => Promise; + saveMCPProfile: (name: string, servers: Record) => Promise; + deleteMCPProfile: (name: string) => Promise; + applyMCPProfile: (name: string, restart?: boolean) => Promise; + storageScope: string; agentType: AgentType; + customTitle?: string; } +// The server sends a heartbeat event every 15s. If nothing (heartbeat or +// otherwise) arrives for this long, the connection is considered dead even +// when the EventSource still reports itself as open — which happens when +// the TCP connection dies without a FIN, e.g. after system sleep. +const STALE_CONNECTION_MS = 45_000; + const ChatContext = createContext(undefined); -const useAgentAPIUrl = (): string => { +export const useAgentAPIUrl = (): string => { const searchParams = useSearchParams(); const paramsUrl = searchParams.get("url"); if (paramsUrl) { @@ -140,24 +226,170 @@ const useAgentAPIUrl = (): string => { }; export function ChatProvider({ children }: PropsWithChildren) { + const searchParams = useSearchParams(); + const customTitle = searchParams.get("title") ?? undefined; const [messages, setMessages] = useState<(Message | DraftMessage)[]>([]); + const [richMessages, setRichMessages] = useState([]); const [loading, setLoading] = useState(false); const [serverStatus, setServerStatus] = useState("unknown"); + const [connectionStatus, setConnectionStatus] = + useState("reconnecting"); + const [queuedMessages, setQueuedMessages] = useState([]); const [agentType, setAgentType] = useState("custom"); const eventSourceRef = useRef(null); + const lastEventAtRef = useRef(Date.now()); + const reconnectTimeoutRef = useRef | null>(null); + const reconnectAttemptRef = useRef(0); + const [reconnectAttempt, setReconnectAttempt] = useState(0); + const [nextReconnectAt, setNextReconnectAt] = useState(null); + const [reconnectNonce, setReconnectNonce] = useState(0); + const [failedMessagesHydrated, setFailedMessagesHydrated] = useState(false); const agentAPIUrl = useAgentAPIUrl(); + const api = useMemo(() => createChatAPI(agentAPIUrl), [agentAPIUrl]); + const failedMessagesStorageKey = `agentapi.chat.failed-messages:${agentAPIUrl}`; + + const reconnectNow = useCallback(() => { + if (reconnectTimeoutRef.current) { + clearTimeout(reconnectTimeoutRef.current); + reconnectTimeoutRef.current = null; + } + eventSourceRef.current?.close(); + reconnectAttemptRef.current = 0; + setReconnectAttempt(0); + setNextReconnectAt(null); + setConnectionStatus("reconnecting"); + setReconnectNonce((value) => value + 1); + }, []); + const lastQueueRefreshRef = useRef(0); + const refreshQueue = useCallback(async (force = false) => { + const now = Date.now(); + // Throttle: skip if last refresh was less than 30s ago, unless forced. + if (!force && now - lastQueueRefreshRef.current < 30_000) return; + lastQueueRefreshRef.current = now; + try { + setQueuedMessages(await api.getQueue()); + } catch { + // The connection status handler reports connectivity failures. + } + }, [api]); + const currentTask = [...messages] + .reverse() + .find((message) => message.role === "user") + ?.content; + + useEffect(() => { + document.title = getDocumentTitle({ + connectionStatus, + serverStatus, + task: currentTask, + customTitle, + }); + }, [connectionStatus, currentTask, customTitle, serverStatus]); + + useEffect(() => { + try { + const saved = window.localStorage.getItem(failedMessagesStorageKey); + if (!saved) { + setFailedMessagesHydrated(true); + return; + } + const failed = parseFailedMessages(saved) as DraftMessage[]; + setMessages((previous) => { + const existing = new Set( + previous + .filter(isDraftMessage) + .map((message) => (message as DraftMessage).clientId), + ); + return [ + ...previous, + ...failed.filter((message) => !existing.has(message.clientId)), + ]; + }); + } catch { + window.localStorage.removeItem(failedMessagesStorageKey); + } finally { + setFailedMessagesHydrated(true); + } + }, [failedMessagesStorageKey]); + + useEffect(() => { + if (!failedMessagesHydrated) return; + const failed = messages.filter( + (message): message is DraftMessage => + isDraftMessage(message) && + (message as DraftMessage).deliveryStatus === "failed", + ); + try { + if (failed.length > 0) { + window.localStorage.setItem( + failedMessagesStorageKey, + JSON.stringify(failed), + ); + } else { + window.localStorage.removeItem(failedMessagesStorageKey); + } + } catch { + // Keep failed messages in memory when storage is unavailable. + } + }, [failedMessagesHydrated, failedMessagesStorageKey, messages]); // Set up SSE connection to the events endpoint useEffect(() => { + let disposed = false; + + const handleOffline = () => { + if (reconnectTimeoutRef.current) { + clearTimeout(reconnectTimeoutRef.current); + reconnectTimeoutRef.current = null; + } + eventSourceRef.current?.close(); + setNextReconnectAt(null); + setConnectionStatus("offline"); + }; + const handleOnline = () => { + if (reconnectTimeoutRef.current) { + clearTimeout(reconnectTimeoutRef.current); + reconnectTimeoutRef.current = null; + } + reconnectAttemptRef.current = 0; + setReconnectAttempt(0); + setNextReconnectAt(null); + setConnectionStatus("reconnecting"); + setupEventSource(); + }; + window.addEventListener("offline", handleOffline); + window.addEventListener("online", handleOnline); + + // Reconnect promptly when the tab becomes visible again. Browsers + // throttle timers in background tabs, so a scheduled reconnect may not + // have fired yet, and a connection that died without a FIN (e.g. after + // system sleep) never fires onerror at all. Server heartbeats let us + // detect the silent case: no event for STALE_CONNECTION_MS means the + // connection is dead even if the EventSource still reports itself open. + const handleVisible = () => { + if (document.visibilityState !== "visible") return; + const eventSource = eventSourceRef.current; + const stale = + Date.now() - lastEventAtRef.current > STALE_CONNECTION_MS; + if ( + !eventSource || + eventSource.readyState === EventSource.CLOSED || + stale + ) { + reconnectNow(); + } + }; + document.addEventListener("visibilitychange", handleVisible); + window.addEventListener("focus", handleVisible); + // Function to create and set up EventSource const setupEventSource = () => { + if (disposed) return null; + if (eventSourceRef.current) { eventSourceRef.current.close(); } - // Reset messages when establishing a new connection - setMessages([]); - if (!agentAPIUrl) { console.warn( "agentAPIUrl is not set, SSE connection cannot be established." @@ -169,14 +401,22 @@ export function ChatProvider({ children }: PropsWithChildren) { const eventSource = new EventSource(`${agentAPIUrl}/events`); eventSourceRef.current = eventSource; + // Server-sent keep-alive; only used to detect dead connections. + eventSource.addEventListener("heartbeat", () => { + lastEventAtRef.current = Date.now(); + }); + // Handle message updates eventSource.addEventListener("message_update", (event) => { + lastEventAtRef.current = Date.now(); const data: MessageUpdateEvent = JSON.parse(event.data); setMessages((prevMessages) => { // Clean up draft messages const updatedMessages = [...prevMessages].filter( - (m) => !isDraftMessage(m) + (message) => + !isDraftMessage(message) || + (message as DraftMessage).deliveryStatus === "failed", ); // Check if message with this ID already exists @@ -190,6 +430,7 @@ export function ChatProvider({ children }: PropsWithChildren) { role: data.role, content: data.message, id: data.id, + time: data.time, }; return updatedMessages; } else { @@ -200,14 +441,32 @@ export function ChatProvider({ children }: PropsWithChildren) { role: data.role, content: data.message, id: data.id, + time: data.time, }, ]; } }); }); + eventSource.addEventListener("rich_message_update", (event) => { + const data: RichMessage = JSON.parse(event.data); + setRichMessages((previous) => { + const existingIndex = previous.findIndex( + (message) => + message.message_id === data.message_id && + message.role === data.role, + ); + if (existingIndex === -1) return [...previous, data]; + + const updated = [...previous]; + updated[existingIndex] = data; + return updated; + }); + }); + // Handle status changes eventSource.addEventListener("status_change", (event) => { + lastEventAtRef.current = Date.now(); const data: StatusChangeEvent = JSON.parse(event.data); if (data.status === "stable") { setServerStatus("stable"); @@ -219,6 +478,7 @@ export function ChatProvider({ children }: PropsWithChildren) { // Set agent type setAgentType(data.agent_type === "" ? "unknown" : data.agent_type as AgentType); + void refreshQueue(); }); // Handle agent error events @@ -242,6 +502,12 @@ export function ChatProvider({ children }: PropsWithChildren) { // Handle connection open (server is online) eventSource.onopen = () => { + lastEventAtRef.current = Date.now(); + reconnectAttemptRef.current = 0; + setReconnectAttempt(0); + setNextReconnectAt(null); + setConnectionStatus("connected"); + void refreshQueue(); // Connection is established, but we'll wait for status_change event // for the actual server status console.log("EventSource connection established - messages reset"); @@ -250,76 +516,86 @@ export function ChatProvider({ children }: PropsWithChildren) { // Handle connection errors eventSource.onerror = (error) => { console.error("EventSource error:", error); - setServerStatus("offline"); + setConnectionStatus(navigator.onLine ? "reconnecting" : "offline"); + eventSource.close(); - // Try to reconnect after delay - setTimeout(() => { - if (eventSourceRef.current) { + if (reconnectTimeoutRef.current) { + clearTimeout(reconnectTimeoutRef.current); + } + const attempt = reconnectAttemptRef.current + 1; + reconnectAttemptRef.current = attempt; + setReconnectAttempt(attempt); + const delay = getReconnectDelay(attempt); + setNextReconnectAt(Date.now() + delay); + reconnectTimeoutRef.current = setTimeout(() => { + if (!disposed) { + setNextReconnectAt(null); setupEventSource(); } - }, 3000); + }, delay); }; return eventSource; }; // Initial setup - const eventSource = setupEventSource(); + setupEventSource(); // Clean up on component unmount return () => { - if (eventSource) { - // Check if eventSource was successfully created - eventSource.close(); + disposed = true; + window.removeEventListener("offline", handleOffline); + window.removeEventListener("online", handleOnline); + document.removeEventListener("visibilitychange", handleVisible); + window.removeEventListener("focus", handleVisible); + if (reconnectTimeoutRef.current) { + clearTimeout(reconnectTimeoutRef.current); + reconnectTimeoutRef.current = null; } + eventSourceRef.current?.close(); + eventSourceRef.current = null; }; - }, [agentAPIUrl]); + }, [agentAPIUrl, reconnectNonce, reconnectNow, refreshQueue]); // Send a new message const sendMessage = async ( content: string, type: "user" | "raw" = "user" - ) => { + ): Promise => { // For user messages, require non-empty content - if (type === "user" && !content.trim()) return; + if (type === "user" && !content.trim()) return {ok: false, queued: false}; + const clientId = crypto.randomUUID(); // For raw messages, don't set loading state as it's usually fast if (type === "user") { setMessages((prevMessages) => [ ...prevMessages, - { role: "user", content }, + { + role: "user", + content, + clientId, + deliveryStatus: "sending", + }, ]); setLoading(true); } try { - const response = await fetch(`${agentAPIUrl}/message`, { - method: "POST", - headers: { - "Content-Type": "application/json", - }, - body: JSON.stringify({ - content: content, - type, - }), - }); - - if (!response.ok) { - const errorData = await response.json() as APIErrorModel; - console.error("Failed to send message:", errorData); - const detail = errorData.detail; - const messages = - "errors" in errorData - ? - errorData.errors.map((e: APIErrorDetail) => e.message).join(", ") - : ""; - - const fullDetail = `${detail}: ${messages}`; - toast.error(`Failed to send message`, { - description: fullDetail, - }); + const result = await api.sendMessage(content, type); + await refreshQueue(true); + if (type === "user") { + setMessages((previous) => + previous.filter( + (message) => + !isDraftMessage(message) || + (message as DraftMessage).clientId !== clientId, + ), + ); } - + return { + ok: result.ok, + queued: result.queued, + }; } catch (error) { console.error("Error sending message:", error); const message = getErrorMessage(error) @@ -327,64 +603,128 @@ export function ChatProvider({ children }: PropsWithChildren) { toast.error(`Error sending message`, { description: message, }); + if (type === "user") { + setMessages((previous) => + previous.map((item) => + isDraftMessage(item) && + (item as DraftMessage).clientId === clientId + ? { + ...(item as DraftMessage), + deliveryStatus: "failed", + error: message, + } + : item, + ), + ); + } + return {ok: false, queued: false}; } finally { - // Remove optimistic draft message if still present (may have been replaced by server response via SSE). - setMessages((prev) => prev.filter((m) => !isDraftMessage(m))); if (type === "user") { setLoading(false); } } }; + const dismissFailedMessage = (clientId: string) => { + setMessages((previous) => + previous.filter( + (message) => + !isDraftMessage(message) || + (message as DraftMessage).clientId !== clientId, + ), + ); + }; + + const retryFailedMessage = async (clientId: string) => { + const failedMessage = messages.find( + (message) => + isDraftMessage(message) && + (message as DraftMessage).clientId === clientId, + ); + if (!failedMessage) return false; + dismissFailedMessage(clientId); + const result = await sendMessage(failedMessage.content, "user"); + return result.ok; + }; + // Upload files to workspace - const uploadFiles = async (formData: FormData): Promise => { - let result: FileUploadResponse = {ok: true}; - try{ - const response = await fetch(`${agentAPIUrl}/upload`, { - method: 'POST', - body: formData, - }); + const uploadFiles = (formData: FormData, options: UploadOptions = {}) => + api.uploadFiles(formData, options); - if (!response.ok) { - result.ok = false; - const errorData = await response.json() as APIErrorModel; - console.error("Failed to send message:", errorData); - const detail = errorData.detail; - const messages = - "errors" in errorData - ? - errorData.errors.map((e: APIErrorDetail) => e.message).join(", ") - : ""; - - const fullDetail = `${detail}: ${messages}`; - toast.error(`Failed to upload files`, { - description: fullDetail, - }); - } else { - result = (await response.json()) as FileUploadResponse; - } + const updateQueuedMessage = async (id: number, content: string) => { + await api.updateQueuedMessage(id, content); + await refreshQueue(true); + }; - } catch (error) { - result.ok = false; - console.error("Error uploading files:", error); - const message = getErrorMessage(error) + const deleteQueuedMessage = async (id: number) => { + await api.deleteQueuedMessage(id); + await refreshQueue(true); + }; - toast.error(`Error uploading files`, { - description: message, + const downloadSession = async () => { + try { + const events = await api.getTimelineEvents(); + const jsonl = events.map((event) => JSON.stringify(event)).join("\n"); + const blob = new Blob([jsonl === "" ? "" : `${jsonl}\n`], { + type: "application/x-ndjson", + }); + const url = URL.createObjectURL(blob); + const link = document.createElement("a"); + const timestamp = new Date().toISOString().replaceAll(":", "-"); + link.href = url; + link.download = `agentapi-session-${timestamp}.jsonl`; + document.body.appendChild(link); + link.click(); + link.remove(); + URL.revokeObjectURL(url); + toast.success("Session JSONL downloaded"); + } catch (error) { + toast.error("Session download failed", { + description: getErrorMessage(error), }); + throw error; } - return result; - } + }; return ( { + await api.deleteMessages(); + setMessages([]); + setRichMessages([]); + }, + getWebhook: api.getWebhook, + updateWebhook: api.updateWebhook, + getMCP: api.getMCP, + updateMCP: api.updateMCP, + checkMCP: api.checkMCP, + createMCPServer: api.createMCPServer, + updateMCPServer: api.updateMCPServer, + deleteMCPServer: api.deleteMCPServer, + getMCPProfiles: api.getMCPProfiles, + saveMCPProfile: api.saveMCPProfile, + deleteMCPProfile: api.deleteMCPProfile, + applyMCPProfile: api.applyMCPProfile, + storageScope: agentAPIUrl, agentType, + customTitle, }} > {children} diff --git a/chat/src/components/chat.tsx b/chat/src/components/chat.tsx index be1a720d..46ca3f65 100644 --- a/chat/src/components/chat.tsx +++ b/chat/src/components/chat.tsx @@ -1,20 +1,118 @@ "use client"; +import {useEffect, useState} from "react"; +import {RefreshCw} from "lucide-react"; import {useChat} from "./chat-provider"; import MessageInput from "./message-input"; import MessageList from "./message-list"; +import {Explorer} from "./explorer"; +import {Button} from "./ui/button"; +import {KeyboardShortcutsDialog, useKeyboardShortcutsKey} from "./keyboard-shortcuts"; export function Chat() { - const {messages, loading, sendMessage, serverStatus} = useChat(); + const [suggestedPrompt, setSuggestedPrompt] = useState(""); + const [shortcutsOpen, setShortcutsOpen] = useState(false); + useKeyboardShortcutsKey(() => setShortcutsOpen(true)); + const { + messages, + richMessages, + loading, + sendMessage, + serverStatus, + agentType, + retryFailedMessage, + dismissFailedMessage, + connectionStatus, + reconnectAttempt, + nextReconnectAt, + reconnectNow, + } = useChat(); + const [reconnectSeconds, setReconnectSeconds] = useState(0); + + useEffect(() => { + if (!nextReconnectAt) { + setReconnectSeconds(0); + return; + } + const update = () => + setReconnectSeconds( + Math.max(0, Math.ceil((nextReconnectAt - Date.now()) / 1000)), + ); + update(); + const timer = window.setInterval(update, 250); + return () => window.clearInterval(timer); + }, [nextReconnectAt]); return ( - <> - +
+
+ {serverStatus === "running" + ? "Agent is working" + : serverStatus === "stable" + ? "Agent is ready" + : "Agent connection is unavailable"} + . {messages.length} conversation updates. +
+ {connectionStatus !== "connected" && ( +
+ + {connectionStatus === "offline" + ? "Network connection is offline." + : reconnectSeconds > 0 + ? `Reconnect attempt ${reconnectAttempt} in ${reconnectSeconds}s.` + : "Connecting to the agent server…"} + + +
+ )} + { + dismissFailedMessage(clientId); + setSuggestedPrompt(content); + }} + onDismissMessage={dismissFailedMessage} + onStopTask={() => void sendMessage("\x1b", "raw")} + onSendRaw={(data) => void sendMessage(data, "raw")} + headerAction={ + { + window.requestAnimationFrame(() => + document.getElementById(`task-${number}`)?.scrollIntoView({ + behavior: "smooth", + block: "start", + }), + ); + }} + /> + } + /> setSuggestedPrompt("")} /> - + +
); } diff --git a/chat/src/components/drag-drop.tsx b/chat/src/components/drag-drop.tsx index 0ffd88cf..3765272c 100644 --- a/chat/src/components/drag-drop.tsx +++ b/chat/src/components/drag-drop.tsx @@ -22,16 +22,16 @@ export function DragDrop({ onFilesAdded, disabled = false, children, className =
{isDragActive && !disabled && ( -
-

Drop the files here

+
+

Drop files to attach

)} {children}
); -} \ No newline at end of file +} diff --git a/chat/src/components/embed-status-bar.tsx b/chat/src/components/embed-status-bar.tsx new file mode 100644 index 00000000..fbc13cb4 --- /dev/null +++ b/chat/src/components/embed-status-bar.tsx @@ -0,0 +1,45 @@ +"use client"; + +import { useMemo } from "react"; +import { Hash } from "lucide-react"; +import { AgentType, useChat } from "./chat-provider"; +import { computeTokenTotals, formatTokenCount, getStatusMeta } from "@/lib/session-status"; + +export function EmbedStatusBar() { + const { serverStatus, connectionStatus, agentType, richMessages, customTitle } = useChat(); + + const status = useMemo( + () => getStatusMeta(serverStatus, connectionStatus), + [serverStatus, connectionStatus], + ); + const tokenTotals = useMemo( + () => computeTokenTotals(richMessages), + [richMessages], + ); + const StatusIcon = status.icon; + const agentName = customTitle + ?? (agentType !== "unknown" && AgentType[agentType] + ? AgentType[agentType].displayName + : "Remote agent"); + + return ( +
+
+ + {agentName} +
+ {tokenTotals.total > 0 && ( + + + {formatTokenCount(tokenTotals.total)} + + )} +
+ ); +} diff --git a/chat/src/components/explorer.test.ts b/chat/src/components/explorer.test.ts new file mode 100644 index 00000000..3dd82129 --- /dev/null +++ b/chat/src/components/explorer.test.ts @@ -0,0 +1,18 @@ +import {describe, expect, test} from "bun:test"; +import {reconstructWrappedURLs} from "./explorer"; + +describe("session explorer URL reconstruction", () => { + test("joins terminal-wrapped URL segments", () => { + expect( + reconstructWrappedURLs( + "Open https://example.com/a/very/long/path?query=one&\nvalue=two", + ), + ).toEqual(["https://example.com/a/very/long/path?query=one&value=two"]); + }); + + test("does not join ordinary following prose", () => { + expect( + reconstructWrappedURLs("See https://example.com/docs\nThis is another line"), + ).toEqual(["https://example.com/docs"]); + }); +}); diff --git a/chat/src/components/explorer.tsx b/chat/src/components/explorer.tsx new file mode 100644 index 00000000..207bbae5 --- /dev/null +++ b/chat/src/components/explorer.tsx @@ -0,0 +1,710 @@ +"use client"; + +import {useEffect, useMemo, useRef, useState} from "react"; +import { + Activity, + BellRing, + Download, + ExternalLink, + FileText, + FolderSearch, + Link as LinkIcon, + ListTree, + LoaderCircle, + Play, + RefreshCw, + RotateCw, + Save, + Server, + Trash2, + Upload, +} from "lucide-react"; +import {toast} from "sonner"; +import {editableMCPServers} from "@/lib/mcp-sample"; +import {useChat} from "./chat-provider"; +import {Button} from "./ui/button"; +import { + Dialog, + DialogContent, + DialogDescription, + DialogHeader, + DialogTitle, + DialogTrigger, +} from "./ui/dialog"; +import {Tabs, TabsContent, TabsList, TabsTrigger} from "./ui/tabs"; + +interface ExplorerProps { + onNavigateTask: (number: number) => void; +} + +interface DiscoveredLink { + url: string; + task: number; +} + +const pathPattern = /(?:^|[\s"'(])((?:\/[\w.@+-]+)+\.[a-zA-Z0-9]{1,10})(?=$|[\s"'),:;])/g; + +export function Explorer({onNavigateTask}: ExplorerProps) { + const { + messages, + deleteMessages, + getWebhook, + updateWebhook, + getMCP, + updateMCP, + checkMCP, + getMCPProfiles, + saveMCPProfile, + deleteMCPProfile, + applyMCPProfile, + } = useChat(); + const [open, setOpen] = useState(false); + const [activeTab, setActiveTab] = useState("links"); + const [mcpJSON, setMCPJSON] = useState("{}"); + const [mcpIsSample, setMCPIsSample] = useState(false); + const [mcpPath, setMCPPath] = useState(""); + const [mcpSupported, setMCPSupported] = useState(true); + const [mcpLoading, setMCPLoading] = useState(false); + const [mcpSaving, setMCPSaving] = useState(false); + const [mcpChecking, setMCPChecking] = useState(false); + const [mcpChecks, setMCPChecks] = useState>([]); + const [profiles, setProfiles] = useState>>({}); + const [profileName, setProfileName] = useState(""); + const [restartAfterSave, setRestartAfterSave] = useState(true); + const [webhookURL, setWebhookURL] = useState(""); + const [webhookTimeout, setWebhookTimeout] = useState(10); + const [webhookMaxAttempts, setWebhookMaxAttempts] = useState(3); + const [webhookPayloadTemplate, setWebhookPayloadTemplate] = useState(""); + const [webhookLoading, setWebhookLoading] = useState(false); + const [webhookSaving, setWebhookSaving] = useState(false); + const [restartingAgent, setRestartingAgent] = useState(false); + const mcpImportRef = useRef(null); + const profileImportRef = useRef(null); + const tasks = useMemo( + () => messages.filter((message) => message.role === "user"), + [messages], + ); + const links = useMemo(() => discoverLinks(messages), [messages]); + const files = useMemo(() => { + const found = new Map(); + let task = 0; + for (const message of messages) { + if (message.role === "user") task += 1; + for (const match of message.content.matchAll(pathPattern)) { + if (!found.has(match[1])) found.set(match[1], task); + } + } + return [...found].map(([path, sourceTask]) => ({path, sourceTask})); + }, [messages]); + + const navigate = (number: number) => { + setOpen(false); + onNavigateTask(number); + }; + const loadMCP = async () => { + setMCPLoading(true); + try { + const config = await getMCP(); + const editable = editableMCPServers(config.servers); + setMCPJSON(JSON.stringify(editable.servers, null, 2)); + setMCPIsSample(editable.isSample); + setMCPPath(config.path); + setMCPSupported(true); + const profileData = await getMCPProfiles(); + setProfiles(profileData.profiles); + } catch { + setMCPSupported(false); + } finally { + setMCPLoading(false); + } + }; + const loadWebhook = async () => { + setWebhookLoading(true); + try { + const config = await getWebhook(); + setWebhookURL(config.url); + setWebhookTimeout(config.timeout_seconds); + setWebhookMaxAttempts(config.max_attempts); + setWebhookPayloadTemplate(config.payload_template); + } catch (error) { + toast.error(error instanceof Error ? error.message : "Could not load webhook configuration"); + } finally { + setWebhookLoading(false); + } + }; + const saveWebhook = async () => { + setWebhookSaving(true); + try { + const config = await updateWebhook({ + url: webhookURL.trim(), + timeout_seconds: webhookTimeout, + max_attempts: webhookMaxAttempts, + payload_template: webhookPayloadTemplate, + }); + setWebhookURL(config.url); + setWebhookPayloadTemplate(config.payload_template); + toast.success(config.url ? "Webhook configuration updated" : "Webhook disabled"); + } catch (error) { + toast.error(error instanceof Error ? error.message : "Could not update webhook configuration"); + } finally { + setWebhookSaving(false); + } + }; + const handleRestart = async () => { + if (!window.confirm("Clear all messages and restart the agent?")) return; + setRestartingAgent(true); + try { + await deleteMessages(); + toast.success("Messages cleared and agent restarted"); + } catch (error) { + toast.error(error instanceof Error ? error.message : "Could not restart the agent"); + } finally { + setRestartingAgent(false); + } + }; + const parsedMCP = () => { + const servers = JSON.parse(mcpJSON) as unknown; + if (!servers || Array.isArray(servers) || typeof servers !== "object") { + throw new Error("MCP configuration must be an object"); + } + return servers as Record; + }; + const runMCPChecks = async () => { + setMCPChecking(true); + try { + setMCPChecks(await checkMCP(parsedMCP())); + } catch (error) { + toast.error(error instanceof Error ? error.message : "Could not check MCP servers"); + } finally { + setMCPChecking(false); + } + }; + const saveProfile = async () => { + const name = profileName.trim(); + if (!name) return toast.error("Enter a profile name"); + try { + const result = await saveMCPProfile(name, parsedMCP()); + setProfiles(result.profiles); + setProfileName(""); + toast.success(`Profile “${name}” saved`); + } catch (error) { + toast.error(error instanceof Error ? error.message : "Could not save profile"); + } + }; + const applyProfile = async (name: string) => { + try { + await applyMCPProfile(name, restartAfterSave); + await loadMCP(); + toast.success(`Profile “${name}” applied`); + } catch { + toast.error("Could not apply profile"); + } + }; + const removeProfile = async (name: string) => { + try { + const result = await deleteMCPProfile(name); + setProfiles(result.profiles); + } catch { + toast.error("Could not delete profile"); + } + }; + const exportProfiles = () => { + downloadJSON("agentapi-mcp-profiles.json", {profiles}); + }; + const exportMCP = () => { + try { + downloadJSON("agentapi-mcp-servers.json", {servers: parsedMCP()}); + } catch { + toast.error("MCP configuration must be valid JSON"); + } + }; + const importMCP = async (file: File) => { + try { + const value = JSON.parse(await file.text()) as { + servers?: Record; + }; + const servers = value.servers ?? value; + if (!servers || Array.isArray(servers) || typeof servers !== "object") { + throw new Error(); + } + setMCPJSON(JSON.stringify(servers, null, 2)); + setMCPIsSample(false); + setMCPChecks([]); + toast.success("MCP servers loaded for review", { + description: "Save servers to write the imported configuration.", + }); + } catch { + toast.error("Invalid MCP server export"); + } + }; + const importProfiles = async (file: File) => { + try { + const value = JSON.parse(await file.text()) as {profiles?: Record>}; + if (!value.profiles || typeof value.profiles !== "object") throw new Error(); + let latest = profiles; + for (const [name, servers] of Object.entries(value.profiles)) { + latest = (await saveMCPProfile(name, servers)).profiles; + } + setProfiles(latest); + toast.success("MCP profiles imported"); + } catch { + toast.error("Invalid MCP profile export"); + } + }; + const saveMCP = async () => { + let servers: unknown; + try { + servers = JSON.parse(mcpJSON); + } catch { + toast.error("MCP configuration must be valid JSON"); + return; + } + if (!servers || Array.isArray(servers) || typeof servers !== "object") { + toast.error("MCP configuration must be a JSON object keyed by server name"); + return; + } + setMCPSaving(true); + try { + const config = await updateMCP( + servers as Record, + restartAfterSave, + ); + setMCPPath(config.path); + setMCPJSON(JSON.stringify(config.servers, null, 2)); + setMCPIsSample(false); + toast.success("MCP servers updated", { + description: config.restarted + ? "The agent restarted and is loading the new configuration." + : "Changes apply when the agent starts its next session.", + }); + } catch { + toast.error("Could not update MCP servers"); + } finally { + setMCPSaving(false); + } + }; + + useEffect(() => { + if (open && activeTab === "mcp") void loadMCP(); + if (open && activeTab === "webhook") void loadWebhook(); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [activeTab, open]); + + return ( + + + + + + +
+ Session Explorer + +
+ + Links, files, task navigation, MCP, and webhook configuration. + +
+ +
+ + Links + Files + Index + MCP + Webhook + +
+ +
+ {links.map((link) => ( +
+ + + {link.url} + + {link.task > 0 && ( + + )} +
+ ))} + {links.length === 0 && } +
+
+ +
+ {files.map((file) => ( + + ))} + {files.length === 0 && } +
+
+ +
+ {tasks.map((task, index) => ( + + ))} +
+
+ + {mcpLoading ? ( +
+ + Loading MCP servers… +
+ ) : !mcpSupported ? ( + + ) : ( +
+
+
+

MCP servers

+

+ Enter the complete server map as JSON. Saving replaces the + existing MCP server set. +

+
+
+ { + const file = event.target.files?.[0]; + if (file) void importMCP(file); + event.target.value = ""; + }} + /> + + +
+
+