diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 4a53d97..bc538a1 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -43,21 +43,6 @@ jobs:
- run: pnpm --dir farhelm-console exec playwright install --with-deps chromium
- run: pnpm --dir farhelm-console test:e2e
- worker:
- runs-on: ubuntu-latest
- steps:
- - uses: actions/checkout@v4
- - uses: astral-sh/setup-uv@v6
- with:
- version: "0.12.9"
- - uses: actions/setup-python@v5
- with:
- python-version: "3.12"
- - run: uv sync --project farhelm-worker-codex --frozen --all-groups
- - run: uv run --project farhelm-worker-codex ruff check farhelm-worker-codex
- - run: uv run --project farhelm-worker-codex mypy farhelm-worker-codex/src
- - run: uv run --project farhelm-worker-codex pytest farhelm-worker-codex/tests
-
privacy:
runs-on: ubuntu-latest
steps:
@@ -66,18 +51,18 @@ jobs:
- run: ./scripts/check-readme-parity.sh
smoke:
- needs: [rust, worker]
+ needs: [rust]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@1.98.0
- - uses: actions/setup-python@v5
+ - uses: actions/setup-node@v4
with:
- python-version: "3.12"
+ node-version-file: farhelm-console/.node-version
- run: ./tests/smoke.sh
release-bundles:
- needs: [rust, console, worker, privacy, smoke]
+ needs: [rust, console, privacy, smoke]
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v4
@@ -92,17 +77,11 @@ jobs:
node-version-file: farhelm-console/.node-version
cache: pnpm
cache-dependency-path: farhelm-console/pnpm-lock.yaml
- - uses: actions/setup-python@v5
- with:
- python-version: "3.12"
- - uses: astral-sh/setup-uv@v6
- with:
- version: "0.12.9"
- run: ./scripts/build-release.sh
- run: ./tests/deployment-package.sh
- uses: actions/upload-artifact@v4
with:
- name: farhelm-V0.7.1-linux-x86_64
+ name: farhelm-V0.8.0-linux-x86_64
path: |
dist/release/farhelm-*
dist/release/SHA256SUMS
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index 94b5064..2fc688f 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -24,10 +24,6 @@ jobs:
node-version-file: farhelm-console/.node-version
cache: pnpm
cache-dependency-path: farhelm-console/pnpm-lock.yaml
- - uses: actions/setup-python@v5
- with:
- python-version: "3.12"
- - uses: astral-sh/setup-uv@v6
- name: Verify tag matches package version
run: |
./scripts/check-version-policy.sh
diff --git a/Cargo.lock b/Cargo.lock
index b739331..f43af68 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -2,12 +2,6 @@
# It is not intended for manual editing.
version = 4
-[[package]]
-name = "adler2"
-version = "2.0.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa"
-
[[package]]
name = "aead"
version = "0.5.2"
@@ -155,6 +149,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "31b698c5f9a010f6573133b09e0de5408834d0c82f8d7475a89fc1867a71cd90"
dependencies = [
"axum-core",
+ "base64",
"bytes",
"form_urlencoded",
"futures-util",
@@ -173,8 +168,10 @@ dependencies = [
"serde_json",
"serde_path_to_error",
"serde_urlencoded",
+ "sha1",
"sync_wrapper",
"tokio",
+ "tokio-tungstenite",
"tower",
"tower-layer",
"tower-service",
@@ -394,15 +391,6 @@ dependencies = [
"libc",
]
-[[package]]
-name = "crc32fast"
-version = "1.5.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8498c871161e1742aaa9d52551b2d6ebdd4c3d45a3be423e3728f33b955be550"
-dependencies = [
- "cfg-if",
-]
-
[[package]]
name = "crypto-bigint"
version = "0.5.5"
@@ -453,6 +441,12 @@ dependencies = [
"cmov",
]
+[[package]]
+name = "data-encoding"
+version = "2.11.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4583a4551df46e2792f82ceeac45e850d2e2d5debba0b91f102385cda5b11f06"
+
[[package]]
name = "der"
version = "0.7.10"
@@ -575,27 +569,29 @@ checksum = "7360491ce676a36bf9bb3c56c1aa791658183a54d2744120f27285738d90465a"
[[package]]
name = "farhelm-agent"
-version = "0.7.1"
+version = "0.8.0"
dependencies = [
"anyhow",
+ "base64",
"clap",
"farhelm-core",
"farhelm-lifecycle",
"farhelm-protocol",
"farhelm-updater",
- "flate2",
+ "futures-util",
"libc",
"rand 0.9.5",
"regex",
"reqwest",
"rpassword",
"rusqlite",
+ "semver",
"serde",
"serde_json",
"sha2 0.11.0",
- "tar",
"tempfile",
"tokio",
+ "tokio-tungstenite",
"toml",
"tracing",
"tracing-subscriber",
@@ -603,14 +599,14 @@ dependencies = [
[[package]]
name = "farhelm-core"
-version = "0.7.1"
+version = "0.8.0"
dependencies = [
"serde",
]
[[package]]
name = "farhelm-hub"
-version = "0.7.1"
+version = "0.8.0"
dependencies = [
"anyhow",
"argon2",
@@ -623,6 +619,7 @@ dependencies = [
"farhelm-lifecycle",
"farhelm-protocol",
"farhelm-updater",
+ "futures-util",
"httpdate",
"rand 0.9.5",
"reqwest",
@@ -634,6 +631,7 @@ dependencies = [
"tempfile",
"thiserror",
"tokio",
+ "tokio-tungstenite",
"toml",
"tower",
"tracing",
@@ -644,7 +642,7 @@ dependencies = [
[[package]]
name = "farhelm-lifecycle"
-version = "0.7.1"
+version = "0.8.0"
dependencies = [
"anyhow",
"tempfile",
@@ -652,25 +650,22 @@ dependencies = [
[[package]]
name = "farhelm-protocol"
-version = "0.7.1"
+version = "0.8.0"
dependencies = [
"serde",
"serde_json",
- "thiserror",
- "tokio",
]
[[package]]
name = "farhelm-test-support"
-version = "0.7.1"
+version = "0.8.0"
dependencies = [
"farhelm-protocol",
- "serde_json",
]
[[package]]
name = "farhelm-updater"
-version = "0.7.1"
+version = "0.8.0"
dependencies = [
"anyhow",
"reqwest",
@@ -698,33 +693,12 @@ dependencies = [
"subtle",
]
-[[package]]
-name = "filetime"
-version = "0.2.29"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5c287a33c7f0a620c38e641e7f60827713987b3c0f26e8ddc9462cc69cf75759"
-dependencies = [
- "cfg-if",
- "libc",
-]
-
[[package]]
name = "find-msvc-tools"
version = "0.1.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d45db016d36b838f563236e9193d0ee6ce38f3f68b6c94e914b4929c96bbb890"
-[[package]]
-name = "flate2"
-version = "1.1.10"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6e634e2e0ebac1ee034020da1ca582e17ffe4e0f5e985823721e168928136dcb"
-dependencies = [
- "crc32fast",
- "miniz_oxide",
- "zlib-rs",
-]
-
[[package]]
name = "form_urlencoded"
version = "1.2.2"
@@ -749,6 +723,23 @@ version = "0.3.34"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "92d699e522242e69e3003b94ecc1f960f3a5e015aa7c5d7486e65ad01dd94f5e"
+[[package]]
+name = "futures-macro"
+version = "0.3.34"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9fb9654ba8355388abeb8dcb4fc62f511300867002afc858860463bdd9fe0c44"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 3.0.4",
+]
+
+[[package]]
+name = "futures-sink"
+version = "0.3.34"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1944426bf7d03f1d14f708785e4b33efd750b36d48a157b836b3efc15ede8e1d"
+
[[package]]
name = "futures-task"
version = "0.3.34"
@@ -762,6 +753,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0d50a92467f8ba5dd6e3ee5d4bd04d73ab2e4e1c44474a0674821dfce14b79bc"
dependencies = [
"futures-core",
+ "futures-macro",
+ "futures-sink",
"futures-task",
"pin-project-lite",
"slab",
@@ -974,7 +967,7 @@ dependencies = [
"tokio",
"tokio-rustls",
"tower-service",
- "webpki-roots",
+ "webpki-roots 1.0.9",
]
[[package]]
@@ -1226,16 +1219,6 @@ version = "0.3.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a"
-[[package]]
-name = "miniz_oxide"
-version = "0.9.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b63fbc4a50860e98e7b2aa7804ded1db5cbc3aff9193adaff57a6931bf7c4b4c"
-dependencies = [
- "adler2",
- "simd-adler32",
-]
-
[[package]]
name = "mio"
version = "1.2.3"
@@ -1587,7 +1570,7 @@ dependencies = [
"wasm-bindgen",
"wasm-bindgen-futures",
"web-sys",
- "webpki-roots",
+ "webpki-roots 1.0.9",
]
[[package]]
@@ -1809,6 +1792,17 @@ dependencies = [
"serde",
]
+[[package]]
+name = "sha1"
+version = "0.10.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a978451301f4db1d02937a4ab3ccce137717b81826e79b7d49ffe3244a13c3b8"
+dependencies = [
+ "cfg-if",
+ "cpufeatures 0.2.17",
+ "digest 0.10.7",
+]
+
[[package]]
name = "sha2"
version = "0.10.9"
@@ -1866,12 +1860,6 @@ dependencies = [
"rand_core 0.6.4",
]
-[[package]]
-name = "simd-adler32"
-version = "0.3.10"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3a219298ac11a56ea9a6d2120044824d6f01aeb034955e7af7bc16858527deea"
-
[[package]]
name = "slab"
version = "0.4.12"
@@ -1964,17 +1952,6 @@ dependencies = [
"syn 2.0.119",
]
-[[package]]
-name = "tar"
-version = "0.4.46"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3f6221d9a6003c78398e3b239969f352578258df48c8eb051caadae0015bc840"
-dependencies = [
- "filetime",
- "libc",
- "xattr",
-]
-
[[package]]
name = "tempfile"
version = "3.27.0"
@@ -2079,6 +2056,22 @@ dependencies = [
"tokio",
]
+[[package]]
+name = "tokio-tungstenite"
+version = "0.29.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8f72a05e828585856dacd553fba484c242c46e391fb0e58917c942ee9202915c"
+dependencies = [
+ "futures-util",
+ "log",
+ "rustls",
+ "rustls-pki-types",
+ "tokio",
+ "tokio-rustls",
+ "tungstenite",
+ "webpki-roots 0.26.11",
+]
+
[[package]]
name = "toml"
version = "0.9.12+spec-1.1.0"
@@ -2232,6 +2225,24 @@ version = "0.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b"
+[[package]]
+name = "tungstenite"
+version = "0.29.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6c01152af293afb9c7c2a57e4b559c5620b421f6d133261c60dd2d0cdb38e6b8"
+dependencies = [
+ "bytes",
+ "data-encoding",
+ "http",
+ "httparse",
+ "log",
+ "rand 0.9.5",
+ "rustls",
+ "rustls-pki-types",
+ "sha1",
+ "thiserror",
+]
+
[[package]]
name = "typenum"
version = "1.20.1"
@@ -2421,6 +2432,15 @@ dependencies = [
"wasm-bindgen",
]
+[[package]]
+name = "webpki-roots"
+version = "0.26.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "521bc38abb08001b01866da9f51eb7c5d647a19260e00054a8c7fd5f9e57f7a9"
+dependencies = [
+ "webpki-roots 1.0.9",
+]
+
[[package]]
name = "webpki-roots"
version = "1.0.9"
@@ -2542,16 +2562,6 @@ version = "0.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3ad82d2a33cdc9674dc7465672f271e096168fcdbe0f799d9e6db8c5892679dc"
-[[package]]
-name = "xattr"
-version = "1.6.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "32e45ad4206f6d2479085147f02bc2ef834ac85886624a23575ae137c8aa8156"
-dependencies = [
- "libc",
- "rustix",
-]
-
[[package]]
name = "yoke"
version = "0.8.3"
@@ -2655,12 +2665,6 @@ dependencies = [
"syn 3.0.4",
]
-[[package]]
-name = "zlib-rs"
-version = "0.6.7"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "34b31d188d9d685a4f9c7b46d6e36631b07058d2cfe190267adce54dc230bf12"
-
[[package]]
name = "zmij"
version = "1.0.23"
diff --git a/Cargo.toml b/Cargo.toml
index d11f08d..9500d9f 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -11,7 +11,7 @@ members = [
]
[workspace.package]
-version = "0.7.1"
+version = "0.8.0"
edition = "2024"
rust-version = "1.98"
license = "Apache-2.0"
@@ -22,6 +22,8 @@ anyhow = "1.0"
async-stream = "0.3"
argon2 = "0.5"
axum = "0.8"
+futures-util = "0.3.32"
+tokio-tungstenite = { version = "0.29.0", features = ["rustls-tls-webpki-roots"] }
base64 = "0.22"
clap = { version = "4.5", features = ["derive", "env"] }
constant_time_eq = "0.4"
diff --git a/Makefile b/Makefile
index 64809c7..279fd2e 100644
--- a/Makefile
+++ b/Makefile
@@ -1,4 +1,4 @@
-.PHONY: check test test-ui run-hub run-console smoke smoke-worker privacy release test-release
+.PHONY: check test test-ui run-hub run-console smoke smoke-codex privacy release test-release
check:
./scripts/check-version-policy.sh
@@ -6,12 +6,10 @@ check:
cargo clippy --workspace --all-targets -- -D warnings
corepack pnpm@10.17.1 --dir farhelm-console lint
corepack pnpm@10.17.1 --dir farhelm-console typecheck
- cd farhelm-worker-codex && uv run ruff check . && uv run mypy src
test:
cargo test --workspace
corepack pnpm@10.17.1 --dir farhelm-console test
- cd farhelm-worker-codex && uv run pytest
test-ui:
corepack pnpm@10.17.1 --dir farhelm-console build
@@ -27,8 +25,8 @@ run-console:
smoke:
./tests/smoke.sh
-smoke-worker:
- cargo run -p farhelm-agent -- worker-smoke
+smoke-codex:
+ cargo run -p farhelm-agent -- codex-smoke
privacy:
./scripts/check-private-files.sh
diff --git a/README.en.md b/README.en.md
index bcf4fea..fd87030 100644
--- a/README.en.md
+++ b/README.en.md
@@ -4,14 +4,14 @@
A remote control plane for personal research and GPU training environments
See training-host status from your phone and safely extend remote control without exposing inbound ports on training machines.
- V0.7.1 ·
+ V0.8.0 ·
Deployment guide ·
简体中文
> [!IMPORTANT]
-> `V0.7.1` fixes the Codex workspace: global navigation stays visible, projects, sessions, and execution details have clear hierarchy, and Markdown, math, session labels, and search across all imported sessions render correctly. It also fixes composer placement, streamed message identity, and session-switch races. The Agent persists Codex content; Hub only relays it temporarily.
+> `V0.8.0` lets the Rust Agent manage your installed, authenticated Codex through one persistent App Server and an outbound WSS connection. The workspace uses Ant Design X, a shared memory cache, and virtualized history while preserving global navigation, experiments, schedules, and browser notifications.
## Quick install
@@ -44,6 +44,17 @@ chmod +x farhelm-agent
The program asks only for the Hub HTTPS URL and the eight-digit code shown by the Console. It obtains a dedicated 256-bit token and stores it in a mode-`0600` configuration automatically. The installed program is `${XDG_BIN_HOME:-$HOME/.local/bin}/farhelm-agent`; the downloaded copy may be deleted after installation succeeds.
+Agent reuses your existing Codex without downloading Python or a dedicated Codex runtime. Installation discovers a unique executable. If your terminal finds Codex but the service cannot (for example, with NVM), configure the path from that terminal:
+
+```bash
+codex --version
+farhelm-agent codex configure --bin "$(command -v codex)"
+farhelm-agent restart
+farhelm-agent status
+```
+
+Codex 0.153.4 is the initial acceptance version; core protocol compatibility tests cover 0.147.0. FarHelm does not update your Codex or change its authentication, model, or global configuration. Experiment reporting still works when Codex is missing or unavailable. `status` separately reports the service, Hub connection, and Codex readiness.
+
See the [deployment guide](deploy/README.en.md) for non-interactive installation, Caddy, systemd, migration, and removal details.
## Daily operations
@@ -118,16 +129,15 @@ Browser commands and schedules are acknowledged after Agent persistence. Failed
Desktop global navigation and mobile bottom navigation remain visible in Codex. Project groups collapse, and sessions use formal names or a temporary first-user-message summary. Search covers all imported sessions in the selected scope; offline Agents are clearly marked as incomplete results. Summaries and search results never enter Hub databases, logs, or notification titles.
-Assistant replies support Markdown tables, code copying, math, and safe HTTPS links. Consecutive execution details in a turn form a collapsed group with visible failures. The composer stays visible, and reading older content does not jump to the bottom on new messages. Large-message continuation is separate from loading earlier conversations. Each session retains its draft; steer and interrupt target the visible active turn.
+Assistant replies support Markdown tables, code copying, math, and safe HTTPS links. Execution details in each turn form a collapsed group with visible failures. The composer stays visible, and reading older content does not jump to the bottom on new messages. Large-message continuation is separate from loading earlier conversations. Switching sessions immediately displays cached history and retains each draft; failed refreshes preserve visible content. Inactive body and parsing caches total at most 32 MiB, with at most 20 inactive histories, and are cleared on logout. Steer and interrupt target the visible active turn.
-Upgrade Hub before Agent when moving from V0.7.0. SQLite schema stays at 7. Temporary labels and search require a V0.7.1 Agent; older Agents show an upgrade prompt.
+Upgrade Hub before Agent when moving from V0.7.1. SQLite schema remains 7. Older Agents retain HTTP compatibility; upgraded Agents use WSS without also claiming work over HTTP. Existing Python directories remain on installed machines for rollback validation and are never invoked by the new version.
## What is implemented
- `farhelm-hub`: Rust control plane with password login, SQLite-backed 30-day sessions, short-code pairing, Secure HttpOnly cookies, CSRF, login throttling, reliable events, SSE replay, and Web Push.
- `farhelm-agent`: regular-user outbound operation, automatic Codex project discovery, a local approval registry, explicit PID watches, PID-reuse protection, SQLite inbox/outbox, and isolated worktrees.
-- `farhelm-console`: React, TypeScript, Ant Design, and Vite PWA with mobile/desktop experiment and Codex views, streamed replies, and notification deep-link handling.
-- `farhelm-worker-codex`: Agent-private Python stdio adapter pinned to `openai-codex==0.147.0`, covering thread list/start/resume and turn start/steer/interrupt.
+- `farhelm-console`: React, TypeScript, Ant Design / Ant Design X, TanStack Query / Virtual, and Vite PWA with mobile/desktop experiment and Codex views, streamed replies, and notification deep-link handling.
All remote actions are fixed typed operations; action, cwd, argv, environment, and shell text are never accepted. Training is still started and stopped through the user's existing workflow.
@@ -135,20 +145,19 @@ All remote actions are fixed typed operations; action, cwd, argv, environment, a
```mermaid
flowchart LR
- Phone["Mobile PWA"] -->|"HTTPS"| Hub["farhelm-hub public control plane"]
- Agent["farhelm-agent training host"] -->|"outbound HTTPS"| Hub
- Agent -->|"length-prefixed JSON / stdio"| Worker["private Python Worker"]
+ Browser["Browser / mobile page"] <-->|"REST / SSE"| Hub["farhelm-hub"]
+ Agent["Rust Agent"] <-->|"Agent-initiated WSS"| Hub
+ Agent <-->|"JSONRPC / stdio"| Codex["Persistent local Codex App Server"]
```
-FarHelm is one monorepo, but Hub and Agent are compiled separately and retain distinct privileges and attack surfaces. Worker exposes no network socket and never receives the Hub token.
+FarHelm is one monorepo, but Hub and Agent are compiled separately and retain distinct privileges and attack surfaces. Codex communicates only over local stdio. Authentication remains local; Hub relays bodies only in bounded memory for at most 20 seconds.
## Local development
-You need Rust 1.98, Node.js 24, Corepack, Python 3.12, and [uv](https://docs.astral.sh/uv/).
+You need Rust 1.98, Node.js 24, Corepack, and a locally installed Codex.
```bash
corepack pnpm@10.17.1 --dir farhelm-console install
-uv sync --project farhelm-worker-codex --all-groups
corepack pnpm@10.17.1 --dir farhelm-console build
cargo run -p farhelm-hub -- serve --config /path/to/hub.toml
```
@@ -170,16 +179,18 @@ make test-release
- Agent needs no root access and opens no public inbound port. Hub binds only to loopback and is exposed through Caddy or an equivalent HTTPS reverse proxy.
- Administrator passwords use Argon2id. Hub stores only hashes of browser-session and Agent tokens; the raw Agent token crosses the network once in the pairing response.
- Hub does not store Codex login credentials, SSH private keys, project source, or complete local logs.
-- Worker communicates with Agent only over stdin/stdout. Mutations require allowlists, TTLs, idempotency, and auditing.
+- Native Codex communicates with Agent only over stdin/stdout. Mutations require allowlists, TTLs, idempotency, and auditing. Execution is serial within a session, with at most four active turns across sessions.
- Versions use `MAJOR.MINOR.PATCH`: only the user may decide the first number; features increase the second, and fixes increase the third.
- GitHub Releases retain only the latest formal version; historical Git tags remain and are never reused. Online update moves only to the latest version, while downgrade uses the machine-local previous program.
## Roadmap
-1. Complete the V0.7 deployment canary on A6000/CC08, Titan/work831, and 3090/work832.
+1. Expand upgrade acceptance across additional hosts and Codex versions.
2. Develop the iOS client, system notifications, and real-device background validation.
3. Evaluate GPU metrics and TensorBoard from real experiment needs, without adding remote training control.
## License
FarHelm is licensed under the [Apache License 2.0](LICENSE).
+
+Direct UI and cache dependencies use fixed versions; see the [third-party notices](farhelm-console/public/third-party-notices.txt).
diff --git a/README.md b/README.md
index 37e651c..41c083e 100644
--- a/README.md
+++ b/README.md
@@ -4,14 +4,14 @@
面向个人科研与 GPU 训练环境的远程控制平面
从手机查看训练服务器状态,并在不开放训练机入站端口的前提下安全扩展远程控制能力。
- V0.7.1 ·
+ V0.8.0 ·
部署文档 ·
English
> [!IMPORTANT]
-> `V0.7.1` 修复 Codex 工作区:保留系统导航,明确项目、会话与执行过程层级;补齐 Markdown、公式、会话名称与全部已导入会话搜索,并修复输入区、流式消息及切换竞态。Codex 正文由 Agent 持久化,Hub 仅短暂中转。
+> `V0.8.0` 由 Rust Agent 直接管理本机已安装、已登录的 Codex,通过常驻 App Server 和主动 WSS 连接传输请求与增量。工作区使用 Ant Design X、统一内存缓存和长历史虚拟化,保留系统导航、实验、调度及页面通知。
## 快速安装
@@ -44,6 +44,17 @@ chmod +x farhelm-agent
程序只询问 Hub HTTPS 地址和网页生成的 8 位配对码,独立 256-bit Token 会自动领取并写入 `0600` 配置。安装后的实际程序位于 `${XDG_BIN_HOME:-$HOME/.local/bin}/farhelm-agent`;成功后可以删除下载副本。
+Agent 复用已有 Codex,不下载 Python 或独立 Codex runtime。安装时自动发现唯一可用程序;如果终端能运行 Codex 而服务找不到(例如 NVM 安装),在该终端明确配置:
+
+```bash
+codex --version
+farhelm-agent codex configure --bin "$(command -v codex)"
+farhelm-agent restart
+farhelm-agent status
+```
+
+首个验收版本为 Codex 0.153.4,核心协议兼容测试覆盖 0.147.0。FarHelm 不更新用户 Codex,不修改其登录、模型或全局配置。未安装或未就绪时实验上报仍可运行;`status` 分别显示服务、Hub 连接和 Codex 状态。
+
更完整的非交互安装、Caddy、systemd、迁移和卸载说明见[部署文档](deploy/README.md)。
## 日常使用
@@ -118,16 +129,15 @@ printf '训练失败,请登录查看详情' | farhelm-agent experiment report
进入 Codex 后保留桌面全局侧栏和手机底部导航。项目分组可折叠,会话名称优先使用正式标题,没有标题时临时读取首条用户消息摘要。搜索覆盖筛选范围内全部已导入会话;离线 Agent 会明确提示结果不完整。摘要与搜索结果不写入 Hub 数据库或日志,也不用于通知标题。
-助手回复支持 Markdown 表格、代码复制、公式及安全 HTTPS 链接。同一轮连续执行过程默认合并折叠,失败可直接辨认。输入区固定可见,阅读旧内容时不会被新消息拉回底部;大消息的“继续加载此消息”与“加载更早对话”分别处理。切换会话保留各自草稿,补充与中断只针对可见的活动轮次。
+助手回复支持 Markdown 表格、代码复制、公式及安全 HTTPS 链接。每轮执行过程默认合并折叠,失败可直接辨认。输入区固定可见,阅读旧内容时不会被新消息拉回底部;大消息的“继续加载此消息”与“加载更早对话”分别处理。切换会话立即显示已有缓存,并保留各自草稿;刷新失败不清空已显示内容。非活动正文缓存与解析缓存合计不超过 32 MiB,最多保留 20 个非活动历史,退出登录清空。补充与中断只针对可见的活动轮次。
-从 V0.7.0 升级先更新 Hub,再更新 Agent。SQLite schema 保持 7,临时会话名称和搜索需要 V0.7.1 Agent;旧 Agent 会显示升级提示。
+从 V0.7.1 升级先更新 Hub,再更新 Agent。SQLite schema 保持 7,旧 Agent 的 HTTP 链路保留兼容;新 Agent 使用 WSS,连接期间不重复领取任务。旧 Python 目录保留在安装机供回退验证,新版本不再调用它。
## 当前实现
- `farhelm-hub`:Rust 控制平面、密码登录、SQLite 30 天会话、短码配对、Secure HttpOnly Cookie、CSRF、登录限速、可靠事件、SSE 补发和 Web Push。
- `farhelm-agent`:普通用户出站连接、自动发现 Codex 项目、本地项目授权表、明确登记的 PID 监视、PID 复用防护、SQLite inbox/outbox 和隔离 worktree。
-- `farhelm-console`:React、TypeScript、Ant Design、Vite PWA;提供实验与 Codex 手机/桌面界面、流式回复和通知深链处理。
-- `farhelm-worker-codex`:Agent 私有 Python stdio 适配层,固定 `openai-codex==0.147.0`,覆盖 thread list/start/resume 与 turn start/steer/interrupt。
+- `farhelm-console`:React、TypeScript、Ant Design / Ant Design X、TanStack Query / Virtual、Vite PWA;提供实验与 Codex 手机/桌面界面、流式回复和通知深链处理。
所有远程动作都是固定类型;不接受 action、cwd、argv、环境变量或 shell 文本。训练仍由用户原有方式启动和停止。
@@ -135,20 +145,19 @@ printf '训练失败,请登录查看详情' | farhelm-agent experiment report
```mermaid
flowchart LR
- Phone["手机 PWA"] -->|"HTTPS"| Hub["farhelm-hub 公网控制平面"]
- Agent["farhelm-agent 训练服务器"] -->|"主动出站 HTTPS"| Hub
- Agent -->|"长度前缀 JSON / stdio"| Worker["私有 Python Worker"]
+ Browser["浏览器 / 手机页面"] <-->|"REST / SSE"| Hub["farhelm-hub"]
+ Agent["Rust Agent"] <-->|"Agent 主动建立 WSS"| Hub
+ Agent <-->|"JSONRPC / stdio"| Codex["本机常驻 Codex App Server"]
```
-FarHelm 是一个 monorepo,但 Hub 与 Agent 分离编译并保持不同权限与攻击面。Worker 不监听网络,也不持有 Hub token。
+FarHelm 是一个 monorepo,但 Hub 与 Agent 分离编译并保持不同权限与攻击面。Codex 只通过本机 stdio 通信;登录凭据保留在本机,Hub 仅在最长 20 秒的有界内存中中转正文。
## 本地开发
-需要 Rust 1.98、Node.js 24、Corepack、Python 3.12 和 [uv](https://docs.astral.sh/uv/)。
+需要 Rust 1.98、Node.js 24 和 Corepack;原生联调另需已安装、已登录的 Codex。
```bash
corepack pnpm@10.17.1 --dir farhelm-console install
-uv sync --project farhelm-worker-codex --all-groups
corepack pnpm@10.17.1 --dir farhelm-console build
cargo run -p farhelm-hub -- serve --config /path/to/hub.toml
```
@@ -170,16 +179,18 @@ make test-release
- Agent 不需要 root,也不开放公网入站端口;Hub 只监听 loopback,由 Caddy 或等价 HTTPS 反向代理公开。
- 管理员密码使用 Argon2id;浏览器 session 和 Agent Token 在 Hub 只保存哈希,原始 Agent Token 仅在配对响应中传输一次。
- Hub 不保存 Codex 登录凭据、SSH 私钥、项目源码或完整本地日志。
-- Worker 只通过 stdin/stdout 与 Agent 通信;写操作必须经过白名单、TTL、幂等和审计。
+- Native Codex 只通过 stdin/stdout 与 Agent 通信;写操作必须经过白名单、TTL、幂等和审计。同一会话串行,跨会话最多 4 个活动 turn。
- 版本使用 `MAJOR.MINOR.PATCH`:第一段只能由用户决定,功能提升第二段,纯修复提升第三段。
- GitHub Releases 只保留最新正式版本;历史 Git 标签保留但不复用。在线更新只升级到最新版本,降级只使用本机 previous。
## 路线图
-1. 在 A6000/CC08、Titan/work831 与 3090/work832 完成 V0.7 部署 canary。
+1. 继续扩展不同服务器与 Codex 版本的升级验收。
2. 开发 iOS 客户端并补充系统通知与真机后台验收。
3. 按实际实验需求评估 GPU 指标与 TensorBoard;不加入远程训练控制。
## 许可证
FarHelm 使用 [Apache License 2.0](LICENSE)。
+
+直接使用的界面组件与缓存依赖固定版本,许可见[第三方声明](farhelm-console/public/third-party-notices.txt)。
diff --git a/VERSION b/VERSION
index 39e898a..a3df0a6 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-0.7.1
+0.8.0
diff --git a/crates/farhelm-core/src/lib.rs b/crates/farhelm-core/src/lib.rs
index 5b06981..21a4c19 100644
--- a/crates/farhelm-core/src/lib.rs
+++ b/crates/farhelm-core/src/lib.rs
@@ -29,6 +29,6 @@ mod tests {
fn build_info_uses_workspace_identity() {
let info = BuildInfo::current();
assert_eq!(info.product, "FarHelm");
- assert_eq!(info.version, "0.7.1");
+ assert_eq!(info.version, "0.8.0");
}
}
diff --git a/crates/farhelm-protocol/Cargo.toml b/crates/farhelm-protocol/Cargo.toml
index 781c584..90c8cd5 100644
--- a/crates/farhelm-protocol/Cargo.toml
+++ b/crates/farhelm-protocol/Cargo.toml
@@ -9,5 +9,3 @@ repository.workspace = true
[dependencies]
serde.workspace = true
serde_json.workspace = true
-thiserror.workspace = true
-tokio.workspace = true
diff --git a/crates/farhelm-protocol/src/lib.rs b/crates/farhelm-protocol/src/lib.rs
index aa6f104..f8ae02b 100644
--- a/crates/farhelm-protocol/src/lib.rs
+++ b/crates/farhelm-protocol/src/lib.rs
@@ -1,13 +1,10 @@
//! Versioned wire contracts shared by FarHelm components.
-use serde::{Deserialize, Serialize, de::DeserializeOwned};
+use serde::{Deserialize, Serialize};
use serde_json::Value;
-use thiserror::Error;
-use tokio::io::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt};
+pub mod live;
pub const FARHELM_PROTOCOL: &str = "farhelm/1";
-pub const WORKER_PROTOCOL: &str = "farhelm-worker/1";
-pub const MAX_FRAME_BYTES: usize = 8 * 1024 * 1024;
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
@@ -57,6 +54,8 @@ impl AgentHeartbeat {
"codex.ephemeral_submit".to_owned(),
"codex.session_display".to_owned(),
"codex.item_offsets".to_owned(),
+ "codex.native".to_owned(),
+ "agent.live".to_owned(),
],
protocol: FARHELM_PROTOCOL.to_owned(),
agent_id: agent_id.into(),
@@ -75,6 +74,8 @@ pub struct AgentHeartbeatAck {
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct AgentSummary {
+ #[serde(default, skip_serializing_if = "Option::is_none")]
+ pub codex: Option,
pub agent_id: String,
pub hostname: String,
pub agent_version: String,
@@ -277,6 +278,8 @@ pub struct CodexSessionSummary {
#[serde(skip_serializing_if = "Option::is_none")]
pub active_turn_id: Option,
pub updated_at_unix: u64,
+ #[serde(default)]
+ pub revision: u64,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
@@ -565,111 +568,6 @@ pub struct AgentEventAck {
pub accepted_event_ids: Vec,
}
-#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
-pub struct WorkerEvent {
- pub protocol: String,
- pub kind: String,
- pub event: String,
- pub data: Value,
-}
-
-#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
-pub struct WorkerRequest {
- pub protocol: String,
- pub kind: String,
- pub request_id: String,
- pub method: String,
- pub params: Value,
-}
-
-impl WorkerRequest {
- #[must_use]
- pub fn hello(request_id: impl Into, agent_version: impl Into) -> Self {
- Self {
- protocol: WORKER_PROTOCOL.to_owned(),
- kind: "request".to_owned(),
- request_id: request_id.into(),
- method: "worker.hello".to_owned(),
- params: serde_json::json!({
- "agent_version": agent_version.into(),
- "supported_protocols": [WORKER_PROTOCOL]
- }),
- }
- }
-}
-
-#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
-pub struct WorkerError {
- pub code: String,
- pub message: String,
-}
-
-#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
-pub struct WorkerResponse {
- pub protocol: String,
- pub kind: String,
- pub request_id: String,
- pub ok: bool,
- #[serde(skip_serializing_if = "Option::is_none")]
- pub result: Option,
- #[serde(skip_serializing_if = "Option::is_none")]
- pub error: Option,
-}
-
-#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
-pub struct WorkerHelloResult {
- pub worker: String,
- pub version: String,
- pub capabilities: Vec,
-}
-
-#[derive(Debug, Error)]
-pub enum FrameError {
- #[error("frame I/O failed: {0}")]
- Io(#[from] std::io::Error),
- #[error("frame length {actual} exceeds maximum {maximum}")]
- TooLarge { actual: usize, maximum: usize },
- #[error("frame JSON is invalid: {0}")]
- Json(#[from] serde_json::Error),
-}
-
-pub async fn write_frame(writer: &mut W, value: &T) -> Result<(), FrameError>
-where
- W: AsyncWrite + Unpin,
- T: Serialize + ?Sized,
-{
- let payload = serde_json::to_vec(value)?;
- if payload.len() > MAX_FRAME_BYTES {
- return Err(FrameError::TooLarge {
- actual: payload.len(),
- maximum: MAX_FRAME_BYTES,
- });
- }
-
- writer.write_u32(payload.len() as u32).await?;
- writer.write_all(&payload).await?;
- writer.flush().await?;
- Ok(())
-}
-
-pub async fn read_frame(reader: &mut R) -> Result
-where
- R: AsyncRead + Unpin,
- T: DeserializeOwned,
-{
- let length = reader.read_u32().await? as usize;
- if length > MAX_FRAME_BYTES {
- return Err(FrameError::TooLarge {
- actual: length,
- maximum: MAX_FRAME_BYTES,
- });
- }
-
- let mut payload = vec![0_u8; length];
- reader.read_exact(&mut payload).await?;
- Ok(serde_json::from_slice(&payload)?)
-}
-
/// Only this metadata crosses into durable Hub events. Transcript content uses the read relay.
#[must_use]
pub fn valid_session_title(title: &str) -> bool {
@@ -846,7 +744,7 @@ mod tests {
"agent_id": "gpu-a",
"hostname": "trainer-a",
"agent_version": "0.1.0",
- "capabilities": ["codex.ephemeral_submit", "codex.session_display", "codex.item_offsets"]
+ "capabilities": ["codex.ephemeral_submit", "codex.session_display", "codex.item_offsets", "codex.native", "agent.live"]
})
);
}
@@ -890,22 +788,4 @@ mod tests {
})
);
}
-
- #[tokio::test]
- async fn framed_json_round_trips() {
- let request = WorkerRequest::hello("req_test", "0.1.0");
- let mut buffer = Vec::new();
- write_frame(&mut buffer, &request).await.unwrap();
-
- let decoded: WorkerRequest = read_frame(&mut buffer.as_slice()).await.unwrap();
- assert_eq!(decoded, request);
- }
-
- #[tokio::test]
- async fn oversized_frame_is_rejected_before_allocation() {
- let bytes = (MAX_FRAME_BYTES as u32 + 1).to_be_bytes();
- let mut slice = bytes.as_slice();
- let error = read_frame::<_, Value>(&mut slice).await.unwrap_err();
- assert!(matches!(error, FrameError::TooLarge { .. }));
- }
}
diff --git a/crates/farhelm-protocol/src/live.rs b/crates/farhelm-protocol/src/live.rs
new file mode 100644
index 0000000..77304fb
--- /dev/null
+++ b/crates/farhelm-protocol/src/live.rs
@@ -0,0 +1,76 @@
+//! FarHelm's authenticated, outbound Agent channel. Vendor RPC is never forwarded.
+use crate::{
+ AgentCommand, AgentEvent, AgentEventBatch, AgentHeartbeat, AgentReadReportRequest,
+ AgentReadRequest, CommandReportRequest,
+};
+use serde::{Deserialize, Serialize};
+use serde_json::Value;
+pub const LIVE_PROTOCOL: &str = "farhelm/1.live";
+pub const LIVE_FRAME_BYTES: usize = 1024 * 1024;
+#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq, Eq)]
+pub struct CodexReadiness {
+ pub state: String,
+ #[serde(default)]
+ pub version: Option,
+ #[serde(default)]
+ pub reason: Option,
+}
+#[derive(Debug, Clone, Serialize, Deserialize)]
+#[serde(tag = "kind", rename_all = "snake_case")]
+pub enum AgentFrame {
+ Heartbeat {
+ request_id: u64,
+ heartbeat: AgentHeartbeat,
+ codex: CodexReadiness,
+ },
+ Events {
+ request_id: u64,
+ batch: AgentEventBatch,
+ },
+ CommandReport {
+ request_id: u64,
+ report: CommandReportRequest,
+ },
+ ReadReport {
+ request_id: u64,
+ report: AgentReadReportRequest,
+ },
+ Delta {
+ event: AgentEvent,
+ },
+}
+#[derive(Debug, Clone, Serialize, Deserialize)]
+#[serde(tag = "kind", rename_all = "snake_case")]
+pub enum HubFrame {
+ Reply {
+ request_id: u64,
+ status: u16,
+ data: Value,
+ },
+ Command {
+ command: AgentCommand,
+ },
+ Read {
+ request: AgentReadRequest,
+ expires_at_unix: u64,
+ },
+}
+#[cfg(test)]
+mod tests {
+ use super::*;
+ #[test]
+ fn live_frames_are_versioned_without_vendor_rpc_escape_hatch() {
+ let frame: HubFrame =
+ serde_json::from_str(include_str!("../tests/fixtures/live-channel.json")).unwrap();
+ assert!(
+ matches!(frame,HubFrame::Read {ref request,..} if request.method=="codex.session.history")
+ );
+ assert!(
+ serde_json::from_value::(
+ serde_json::json!({"kind":"vendor_rpc","method":"command/exec"})
+ )
+ .is_err()
+ );
+ assert_eq!(LIVE_PROTOCOL, "farhelm/1.live");
+ }
+}
diff --git a/crates/farhelm-protocol/tests/fixtures/live-channel.json b/crates/farhelm-protocol/tests/fixtures/live-channel.json
new file mode 100644
index 0000000..3f8672b
--- /dev/null
+++ b/crates/farhelm-protocol/tests/fixtures/live-channel.json
@@ -0,0 +1 @@
+{"kind":"read","request":{"request_id":"read_fixture","method":"codex.session.history","params":{"session_id":"session_fixture","limit":20}},"expires_at_unix":2000000020}
diff --git a/crates/farhelm-test-support/Cargo.toml b/crates/farhelm-test-support/Cargo.toml
index e681be4..4051190 100644
--- a/crates/farhelm-test-support/Cargo.toml
+++ b/crates/farhelm-test-support/Cargo.toml
@@ -8,4 +8,3 @@ repository.workspace = true
[dependencies]
farhelm-protocol.workspace = true
-serde_json.workspace = true
diff --git a/crates/farhelm-test-support/src/lib.rs b/crates/farhelm-test-support/src/lib.rs
index 40bb841..317f6f2 100644
--- a/crates/farhelm-test-support/src/lib.rs
+++ b/crates/farhelm-test-support/src/lib.rs
@@ -1,24 +1,6 @@
//! Cross-component fixtures for FarHelm integration tests.
-
-use farhelm_protocol::{HealthResponse, WORKER_PROTOCOL, WorkerResponse};
-
+use farhelm_protocol::HealthResponse;
#[must_use]
pub fn health_fixture() -> HealthResponse {
- HealthResponse::hub("0.7.1")
-}
-
-#[must_use]
-pub fn worker_hello_fixture(request_id: impl Into) -> WorkerResponse {
- WorkerResponse {
- protocol: WORKER_PROTOCOL.to_owned(),
- kind: "response".to_owned(),
- request_id: request_id.into(),
- ok: true,
- result: Some(serde_json::json!({
- "worker": "farhelm-worker-codex",
- "version": "0.7.1",
- "capabilities": ["worker.hello"]
- })),
- error: None,
- }
+ HealthResponse::hub(env!("CARGO_PKG_VERSION"))
}
diff --git a/crates/farhelm-updater/src/lib.rs b/crates/farhelm-updater/src/lib.rs
index aded05f..85e9889 100644
--- a/crates/farhelm-updater/src/lib.rs
+++ b/crates/farhelm-updater/src/lib.rs
@@ -14,7 +14,6 @@ const RELEASES_API: &str = "https://api.github.com/repos/Xiiiing/FarHelm/release
const DOWNLOAD_PREFIX: &str = "https://github.com/Xiiiing/FarHelm/releases/download/";
const API_VERSION: &str = "2026-03-10";
const MAX_REDIRECTS: usize = 8;
-const MAX_RUNTIME_BYTES: u64 = 512 * 1024 * 1024;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Role {
@@ -66,21 +65,6 @@ pub struct UpdateCandidate {
digest: [u8; 32],
}
-#[derive(Debug, Clone, PartialEq, Eq)]
-pub struct RuntimeCandidate {
- pub version: Version,
- pub tag: String,
- asset: ReleaseAsset,
- digest: [u8; 32],
-}
-
-impl RuntimeCandidate {
- #[must_use]
- pub fn asset_name(&self) -> &str {
- &self.asset.name
- }
-}
-
impl UpdateCandidate {
#[must_use]
pub fn asset_name(&self) -> &str {
@@ -95,13 +79,6 @@ pub struct VerifiedExecutable {
pub version: Version,
}
-#[derive(Debug)]
-pub struct VerifiedAsset {
- _temporary: TempDir,
- pub path: PathBuf,
- pub version: Version,
-}
-
#[derive(Debug, Clone, Deserialize)]
struct GithubRelease {
tag_name: String,
@@ -155,15 +132,6 @@ impl Updater {
select_candidate(&releases, role, current, requested, allow_major)
}
- pub async fn runtime(&self, version: &str) -> Result {
- let version = Version::parse(version).context("Worker runtime version is invalid")?;
- ensure!(
- version.pre.is_empty() && version.build.is_empty(),
- "Worker runtime requires a formal release version"
- );
- select_runtime_candidate(&self.releases().await?, &version)
- }
-
async fn releases(&self) -> Result> {
let response = self
.client
@@ -204,22 +172,6 @@ impl Updater {
})
}
- pub async fn download_runtime(&self, candidate: &RuntimeCandidate) -> Result {
- let (temporary, path) = self
- .download_asset(
- &candidate.asset,
- candidate.digest,
- MAX_RUNTIME_BYTES,
- "codex-runtime.tar.gz",
- )
- .await?;
- Ok(VerifiedAsset {
- _temporary: temporary,
- path,
- version: candidate.version.clone(),
- })
- }
-
async fn download_asset(
&self,
asset: &ReleaseAsset,
@@ -368,40 +320,6 @@ fn select_candidate(
Ok(candidate)
}
-fn select_runtime_candidate(
- releases: &[GithubRelease],
- version: &Version,
-) -> Result {
- let tag = format!("V{version}");
- let release = releases
- .iter()
- .find(|release| {
- release.tag_name == tag && !release.draft && !release.prerelease && release.immutable
- })
- .context("matching immutable FarHelm release is unavailable")?;
- let name = format!("farhelm-codex-runtime-{version}-linux-x86_64.tar.gz");
- let asset = release
- .assets
- .iter()
- .find(|asset| asset.name == name)
- .context("immutable release omits the Codex runtime asset")?;
- ensure!(asset.size > 0, "Codex runtime asset is empty");
- ensure!(
- asset.size <= MAX_RUNTIME_BYTES,
- "Codex runtime asset exceeds its size limit"
- );
- ensure!(
- asset.browser_download_url == format!("{DOWNLOAD_PREFIX}{tag}/{name}"),
- "Codex runtime URL is outside the official FarHelm release path"
- );
- Ok(RuntimeCandidate {
- version: version.clone(),
- tag,
- asset: asset.clone(),
- digest: parse_digest(asset.digest.as_deref())?,
- })
-}
-
fn parse_requested_version(value: &str) -> Result {
let value = value.strip_prefix('V').unwrap_or(value);
ensure!(
@@ -472,24 +390,14 @@ mod tests {
draft: false,
prerelease: false,
immutable,
- assets: vec![
- ReleaseAsset {
- name: format!("farhelm-agent-{version}-linux-x86_64"),
- browser_download_url: format!(
- "{DOWNLOAD_PREFIX}{tag}/farhelm-agent-{version}-linux-x86_64"
- ),
- size: 1024,
- digest: Some(format!("sha256:{}", "11".repeat(32))),
- },
- ReleaseAsset {
- name: format!("farhelm-codex-runtime-{version}-linux-x86_64.tar.gz"),
- browser_download_url: format!(
- "{DOWNLOAD_PREFIX}{tag}/farhelm-codex-runtime-{version}-linux-x86_64.tar.gz"
- ),
- size: 2048,
- digest: Some(format!("sha256:{}", "22".repeat(32))),
- },
- ],
+ assets: vec![ReleaseAsset {
+ name: format!("farhelm-agent-{version}-linux-x86_64"),
+ browser_download_url: format!(
+ "{DOWNLOAD_PREFIX}{tag}/farhelm-agent-{version}-linux-x86_64"
+ ),
+ size: 1024,
+ digest: Some(format!("sha256:{}", "11".repeat(32))),
+ }],
}
}
@@ -561,19 +469,4 @@ mod tests {
assert!(validate_download(&candidate, 1024, [0x22; 32]).is_err());
assert!(validate_download(&candidate, 1024, [0x11; 32]).is_ok());
}
-
- #[test]
- fn runtime_requires_exact_immutable_release_asset() {
- let candidate =
- select_runtime_candidate(&[release("V0.4.0", true)], &Version::new(0, 4, 0)).unwrap();
- assert_eq!(candidate.tag, "V0.4.0");
- assert_eq!(
- candidate.asset_name(),
- "farhelm-codex-runtime-0.4.0-linux-x86_64.tar.gz"
- );
- assert_eq!(candidate.digest, [0x22; 32]);
- assert!(
- select_runtime_candidate(&[release("V0.4.0", false)], &Version::new(0, 4, 0)).is_err()
- );
- }
}
diff --git a/deploy/README.en.md b/deploy/README.en.md
index e22c77c..219f9d2 100644
--- a/deploy/README.en.md
+++ b/deploy/README.en.md
@@ -1,4 +1,4 @@
-# FarHelm V0.7.1 deployment and lifecycle
+# FarHelm V0.8.0 deployment and lifecycle
[简体中文](README.md) · [English](README.en.md)
@@ -21,7 +21,7 @@ sudo env \
./farhelm-hub install
```
-V0.5.0 upgrades directly with `update`; SQLite session, pairing, project, and schedule tables are created idempotently on restart. Old TOTP and token fields remain for local rollback, but V0.7.1 does not require TOTP. A backup is still recommended before the first V0.3.0 cross-generation install:
+V0.5.0 upgrades directly with `update`; SQLite session, pairing, project, and schedule tables are created idempotently on restart. Old TOTP and token fields remain for local rollback, but V0.8.0 does not require TOTP. A backup is still recommended before the first V0.3.0 cross-generation install:
```bash
sudo cp /var/lib/farhelm/farhelm.db /var/lib/farhelm/farhelm.db.v0.3.bak
@@ -83,15 +83,19 @@ chmod +x farhelm-agent
./farhelm-agent install
```
-Agent downloads the independent Python 3.12/Codex runtime from the matching immutable Release and verifies its length and SHA-256. For an offline installation, transfer the matching runtime asset first and provide trusted verification metadata:
+V0.8 reuses the Codex already installed and authenticated by this user. It downloads neither Python nor Codex and changes no global model or login configuration. Installation discovers an unambiguous executable; multiple candidates require an explicit choice. The initial acceptance version is Codex 0.153.4, with core protocol compatibility checked against 0.147.0.
+
+If an NVM/npm installation works in your terminal but is missing from the service PATH, configure it from that terminal after installing Agent:
```bash
-FARHELM_CODEX_RUNTIME_ARCHIVE="$PWD/farhelm-codex-runtime-0.7.1-linux-x86_64.tar.gz" \
-FARHELM_CODEX_RUNTIME_SIZE="$(stat -c '%s' farhelm-codex-runtime-0.7.1-linux-x86_64.tar.gz)" \
-FARHELM_CODEX_RUNTIME_SHA256="copy-from-trusted-SHA256SUMS" \
-./farhelm-agent install
+farhelm-agent codex configure --bin "$(command -v codex)"
+farhelm-agent restart
+farhelm-agent status
+farhelm-agent doctor
```
+The configured absolute path and its sibling interpreter directory are used at startup. `status` distinguishes service activity, the Hub connection, and Codex readiness/version. Missing Codex, login failure, or an initialization failure does not stop experiment reporting. If the installed path moves, run `codex configure` again and restart. Offline installation only requires the verified Agent program and an existing local Codex installation.
+
First generate an eight-digit code under “Servers → Add server”. The installer asks only for the Hub HTTPS URL and pairing code, then stores its dedicated token automatically. For non-interactive installation:
```bash
@@ -105,7 +109,7 @@ Agent creates and manages:
- `${XDG_BIN_HOME:-$HOME/.local/bin}/farhelm-agent`: the actual program.
- `farhelm-agent.previous` in the same directory: the single rollback backup.
- `${XDG_CONFIG_HOME:-$HOME/.config}/farhelm/agent.toml`: the only configuration, mode `0600`.
-- `${XDG_DATA_HOME:-$HOME/.local/share}/farhelm/`: SQLite state and private Worker runtime.
+- `${XDG_DATA_HOME:-$HOME/.local/share}/farhelm/`: SQLite state and local connection status.
- `${XDG_CONFIG_HOME:-$HOME/.config}/systemd/user/farhelm-agent.service`: user service.
If `~/.local/bin` is not yet in the current shell's `PATH`, installation prints the complete command path. Ubuntu normally adds it after the next login; until then, use `~/.local/bin/farhelm-agent` directly.
@@ -150,17 +154,17 @@ farhelm-agent uninstall --keep-data
## Migrating from V0.2.0
-Hosts already on `V0.3.0` through `V0.5.0` can run `farhelm-hub update` or `farhelm-agent update` directly. `V0.2.0` must first upgrade to `V0.3.0` to migrate the old layout, then upgrade to V0.7.1.
+Hosts already on `V0.3.0` through `V0.7.1` can run `farhelm-hub update` or `farhelm-agent update` directly. `V0.2.0` must first upgrade to `V0.3.0` to migrate the old layout, then upgrade to V0.8.0.
-Lowercase legacy `v0.1.0/v0.2.0` releases are outside the formal update sequence. Remove them with their matching old uninstaller before installing V0.7.1.
+Lowercase legacy `v0.1.0/v0.2.0` releases are outside the formal update sequence. Remove them with their matching old uninstaller before installing V0.8.0.
## Security notes
- The downloaded file is the program; initial installation executes no dynamic remote script.
-- V0.3+ updater only downloads versioned assets and verifies the fixed official repository, immutable Release, length, SHA-256, role, and version; V0.4 applies the same checks to the independent Codex runtime.
+- V0.3+ updater only downloads versioned assets and verifies the fixed official repository, immutable Release, length, SHA-256, role, and version.
- A new program is fully written on the same filesystem before atomic replacement; failed service health restores previous.
-- Configuration, database, and Worker runtime are not overwritten with the executable; logs go to journald.
-- The current release permits only typed experiment-observation and Codex session/turn commands. It cannot start or stop training or accept arbitrary cwd/argv/env/shell values; the Codex Worker connects to the real SDK only through the Agent's local stdio channel.
+- Configuration and database are not overwritten with the executable; logs go to journald. Existing legacy Python directories remain available for rollback but are never invoked by V0.8.
+- The current release permits only typed experiment-observation and Codex session/turn commands. It cannot start or stop training or accept arbitrary cwd/argv/env/shell values; Rust Agent communicates with the installed Codex through local stdio. The Agent opens an outbound WSS connection to `/api/v1/agent/connect`; neither Agent nor Codex accepts inbound network connections. Ensure an upstream reverse proxy permits WebSocket upgrades (the supplied Caddy configuration already does).
## V0.7 data migration
@@ -169,3 +173,11 @@ Upgrade Hub before Agent. Each role has one schema migration entry point upgradi
Schema 7 makes V0.6 and older programs refuse the database. Do not overwrite current execution receipts with an old snapshot to force a downgrade. Retain the current database and use a schema-7-compatible repair build; restore the current binary if binary rollback fails. Restoring an old snapshot can replay completed work and is not a supported rollback path.
V0.7 in-page alerts use the existing SSE connection and durable notification center, with no VAPID or phone permission requirement. Existing Web Push APIs remain compatible; iOS system push is outside this release's acceptance scope.
+
+## V0.7.1 → V0.8.0
+
+Update Hub first, then Agent. The database stays at schema 7; project, session, receipt, experiment and schedule identities are preserved. New Agents use one live channel for reads, commands, receipts and events, without concurrent HTTP polling. Older Agents retain their HTTP compatibility path during migration.
+
+A binary rollback retains the current database and receipts. Never restore an older queue database: it could repeat completed operations. Running work becomes orphaned after restart and requires inspection instead of automatic replay. V0.8 does not downgrade your Codex executable; an older FarHelm installation can reuse its retained Python directory only after rollback verification.
+
+In the browser, verify the permanent system navigation, cached session switching, Markdown, queued sends, interrupts and completion alerts. Conversation caches are memory-only and cleared on logout; at most 20 inactive histories or 24 MiB plus up to 8 MiB of parsed Markdown are retained. Page notifications remain the supported notification experience for this release.
diff --git a/deploy/README.md b/deploy/README.md
index cd8a28a..5088009 100644
--- a/deploy/README.md
+++ b/deploy/README.md
@@ -1,4 +1,4 @@
-# FarHelm V0.7.1 部署与生命周期
+# FarHelm V0.8.0 部署与生命周期
[简体中文](README.md) · [English](README.en.md)
@@ -21,7 +21,7 @@ sudo env \
./farhelm-hub install
```
-V0.5.0 可直接执行 `update` 升级;SQLite 会话、配对、项目和调度表会在重启时幂等创建。旧 TOTP 与 Token 字段保留供本机回滚,但 V0.7.1 不再要求 TOTP。V0.3.0 首次跨代安装前仍建议备份:
+V0.5.0 可直接执行 `update` 升级;SQLite 会话、配对、项目和调度表会在重启时幂等创建。旧 TOTP 与 Token 字段保留供本机回滚,但 V0.8.0 不再要求 TOTP。V0.3.0 首次跨代安装前仍建议备份:
```bash
sudo cp /var/lib/farhelm/farhelm.db /var/lib/farhelm/farhelm.db.v0.3.bak
@@ -83,15 +83,19 @@ chmod +x farhelm-agent
./farhelm-agent install
```
-Agent 会从同版本不可变 Release 下载独立 Python 3.12/Codex runtime,并校验资产长度和 SHA-256。离线安装时,先传输同版本 runtime 资产,再提供受信任的校验元数据:
+V0.8 复用当前用户已安装、登录的 Codex,不下载 Python 或 Codex,不修改全局模型与登录配置。安装时自动发现唯一可用程序;存在多个候选时需要明确选择。首个验收版本是 Codex 0.153.4,核心协议兼容检查覆盖 0.147.0。
+
+如果终端可以使用 NVM/npm 安装的 Codex,但服务 PATH 找不到,请在该终端安装 Agent 后执行:
```bash
-FARHELM_CODEX_RUNTIME_ARCHIVE="$PWD/farhelm-codex-runtime-0.7.1-linux-x86_64.tar.gz" \
-FARHELM_CODEX_RUNTIME_SIZE="$(stat -c '%s' farhelm-codex-runtime-0.7.1-linux-x86_64.tar.gz)" \
-FARHELM_CODEX_RUNTIME_SHA256="从受信任的SHA256SUMS复制" \
-./farhelm-agent install
+farhelm-agent codex configure --bin "$(command -v codex)"
+farhelm-agent restart
+farhelm-agent status
+farhelm-agent doctor
```
+服务启动固定使用配置的绝对路径及同目录解释器。`status` 分别展示服务运行、Hub 连接与 Codex 就绪状态及版本。未安装 Codex、需要登录或初始化失败不影响实验上报。安装路径变化后重新运行 `codex configure` 并重启。离线安装只需已校验的 Agent 程序及已有本机 Codex。
+
先在网页“服务器 → 添加服务器”生成 8 位码。程序只询问 Hub HTTPS URL 和配对码,独立 Token 自动保存。非交互安装:
```bash
@@ -105,7 +109,7 @@ Agent 创建并管理:
- `${XDG_BIN_HOME:-$HOME/.local/bin}/farhelm-agent`:实际运行程序。
- 同目录的 `farhelm-agent.previous`:唯一回滚备份。
- `${XDG_CONFIG_HOME:-$HOME/.config}/farhelm/agent.toml`:唯一配置,权限 `0600`。
-- `${XDG_DATA_HOME:-$HOME/.local/share}/farhelm/`:SQLite 状态与私有 Worker runtime。
+- `${XDG_DATA_HOME:-$HOME/.local/share}/farhelm/`:SQLite 状态与本地连接状态。
- `${XDG_CONFIG_HOME:-$HOME/.config}/systemd/user/farhelm-agent.service`:用户服务。
如果 `~/.local/bin` 尚未进入当前 shell 的 `PATH`,安装程序会显示完整命令路径;重新登录后 Ubuntu 通常会自动加入,也可以暂时使用 `~/.local/bin/farhelm-agent`。
@@ -150,17 +154,17 @@ farhelm-agent uninstall --keep-data
## 从 V0.2.0 迁移
-已安装 `V0.3.0` 至 `V0.5.0` 的主机可以直接执行 `farhelm-hub update` 或 `farhelm-agent update`。`V0.2.0` 必须先升级到 `V0.3.0` 完成旧布局迁移,再升级到 V0.7.1。
+已安装 `V0.3.0` 至 `V0.7.1` 的主机可以直接执行 `farhelm-hub update` 或 `farhelm-agent update`。`V0.2.0` 必须先升级到 `V0.3.0` 完成旧布局迁移,再升级到 V0.8.0。
-旧小写 `v0.1.0/v0.2.0` 不属于正式升级序列,仍需先使用对应旧卸载器清理,再安装 V0.7.1。
+旧小写 `v0.1.0/v0.2.0` 不属于正式升级序列,仍需先使用对应旧卸载器清理,再安装 V0.8.0。
## 安全说明
- 下载文件就是程序;首次安装不执行远端动态脚本。
-- V0.3+ updater 只下载版本化资产,并验证固定官方仓库、immutable Release、长度、SHA-256、角色和版本;V0.4 对独立 Codex runtime 使用相同校验。
+- V0.3+ updater 只下载版本化资产,并验证固定官方仓库、immutable Release、长度、SHA-256、角色和版本。
- 新程序完整写入同一文件系统后才原子替换,服务健康失败自动恢复 previous。
-- 配置、数据库和 Worker runtime 不随二进制覆盖;日志进入 journald。
-- 当前只允许固定类型的实验观察和 Codex session/turn 命令;不能启动/停止训练、传入任意 cwd/argv/env/shell,Codex Worker 只通过 Agent 的本地 stdio 连接真实 SDK。
+- 配置、数据库不随二进制覆盖;日志进入 journald。已有 Python 目录保留供回退使用,但 V0.8 不再调用。
+- 当前只允许固定类型的实验观察和 Codex session/turn 命令;不能启动/停止训练、传入任意 cwd/argv/env/shell,Rust Agent 只通过本地 stdio 连接已安装的 Codex。Agent 主动建立到 `/api/v1/agent/connect` 的出站 WSS,Agent 和 Codex 不接受入站网络连接。上游反向代理需要允许 WebSocket 升级(提供的 Caddy 配置已经支持)。
## V0.7 数据升级
@@ -169,3 +173,11 @@ farhelm-agent uninstall --keep-data
schema 7 会让 V0.6 及更早程序拒绝打开数据库,因此不能用旧数据库快照覆盖当前执行记录来强行降级。保留当前数据库并使用兼容 schema 7 的修复版;二进制回滚失败时恢复当前程序。恢复旧快照可能重放已执行操作,不属于支持的回滚路径。
V0.7 的页面通知通过现有 SSE 和持久通知中心工作,无需 VAPID 或手机通知权限。已有 Web Push 接口保留兼容;iOS 系统推送不在本版验收范围。
+
+## V0.7.1 → V0.8.0
+
+先更新 Hub,再更新 Agent。数据库保持 schema 7,保留项目、会话、收据、实验和调度身份。新 Agent 通过一个长连接传输读取、命令、收据和事件,不同时进行 HTTP 轮询;旧 Agent 在迁移期间保留 HTTP 兼容链路。
+
+二进制回退始终保留当前数据库和收据。不能恢复旧队列数据库,否则可能重复执行已完成操作。重启中的运行任务标记为 orphaned,需检查结果,不自动重放。V0.8 不会降级用户 Codex;旧 FarHelm 只有在回退验证后才可复用保留的 Python 目录。
+
+在浏览器中检查始终可见的系统导航、会话缓存切换、Markdown、排队发送、中断和完成通知。对话缓存仅在内存中,退出登录时清除;非活动历史最多 20 个或 24 MiB,另保留最多 8 MiB 的 Markdown 解析缓存。本版继续使用页面通知。
diff --git a/farhelm-agent/Cargo.toml b/farhelm-agent/Cargo.toml
index a5e7c91..5e01ab7 100644
--- a/farhelm-agent/Cargo.toml
+++ b/farhelm-agent/Cargo.toml
@@ -8,12 +8,15 @@ repository.workspace = true
[dependencies]
anyhow.workspace = true
+base64.workspace = true
+semver.workspace = true
+futures-util.workspace = true
+tokio-tungstenite.workspace = true
clap.workspace = true
farhelm-core.workspace = true
farhelm-lifecycle.workspace = true
farhelm-protocol.workspace = true
farhelm-updater.workspace = true
-flate2.workspace = true
libc.workspace = true
regex.workspace = true
reqwest.workspace = true
@@ -23,7 +26,6 @@ rpassword.workspace = true
serde.workspace = true
serde_json.workspace = true
sha2.workspace = true
-tar.workspace = true
tempfile.workspace = true
tokio.workspace = true
toml.workspace = true
diff --git a/farhelm-agent/agent.example.toml b/farhelm-agent/agent.example.toml
index 5ff8513..ac33bae 100644
--- a/farhelm-agent/agent.example.toml
+++ b/farhelm-agent/agent.example.toml
@@ -7,8 +7,9 @@ heartbeat_seconds = 15
command_poll_seconds = 2
database = "/home/user/.local/share/farhelm/state/agent.db"
-[worker]
-python = "python3"
+# Select an existing Codex installation; its parent directory is added to the child PATH.
+[codex]
+bin = "/home/user/.local/bin/codex"
# Existing TOML projects migrate into the local registry; new Codex projects are discovered automatically.
[projects.cc08]
diff --git a/farhelm-agent/src/codex/history.rs b/farhelm-agent/src/codex/history.rs
new file mode 100644
index 0000000..daafa5b
--- /dev/null
+++ b/farhelm-agent/src/codex/history.rs
@@ -0,0 +1,472 @@
+//! Safe display projection shared by native history and streaming output.
+use anyhow::{Context, Result, ensure};
+use base64::{Engine, engine::general_purpose::URL_SAFE_NO_PAD};
+use serde_json::{Value, json};
+const PAGE_BYTES: usize = 500 * 1024;
+
+#[derive(Default, PartialEq)]
+enum TextMode {
+ #[default]
+ Normal,
+ Url,
+ LocalPath,
+}
+
+/// An incremental lexer shares exactly the same rules with history projection.
+/// It holds at most a protocol/drive prefix; ordinary text is immediately visible.
+#[derive(Default)]
+pub struct StreamText {
+ pending: String,
+ previous: Option,
+ mode: TextMode,
+ offset: usize,
+}
+fn url_character(c: char) -> bool {
+ !c.is_whitespace() && !"<>\"`".contains(c)
+}
+fn path_character(c: char) -> bool {
+ !c.is_whitespace() && !"[]()<>{}'\"`|,。;!?".contains(c)
+}
+impl StreamText {
+ pub fn feed(&mut self, delta: &str, final_chunk: bool) -> (usize, String) {
+ let mut output = String::new();
+ for c in delta.chars() {
+ if self.mode == TextMode::Url && url_character(c) {
+ output.push(c);
+ self.previous = Some(c);
+ continue;
+ }
+ if self.mode == TextMode::LocalPath && path_character(c) {
+ self.previous = Some(c);
+ continue;
+ }
+ self.mode = TextMode::Normal;
+ self.pending.push(c);
+ self.flush(false, &mut output);
+ }
+ if final_chunk {
+ self.flush(true, &mut output);
+ }
+ let offset = self.offset;
+ self.offset += output.chars().count();
+ (offset, output)
+ }
+ fn flush(&mut self, final_chunk: bool, output: &mut String) {
+ while !self.pending.is_empty() {
+ let mut wait = false;
+ let mut url = false;
+ let candidate = self.pending.to_ascii_lowercase();
+ for prefix in ["http://", "https://", "mailto:"] {
+ if prefix.starts_with(&candidate) {
+ wait = true;
+ }
+ if let Some(rest) = candidate.strip_prefix(prefix) {
+ if rest.is_empty() {
+ wait = true;
+ } else if rest.chars().next().is_some_and(url_character) {
+ url = true;
+ }
+ }
+ }
+ if url {
+ output.push_str(&self.pending);
+ self.previous = self.pending.chars().next_back();
+ self.pending.clear();
+ self.mode = TextMode::Url;
+ return;
+ }
+ let boundary = self
+ .previous
+ .is_none_or(|c| !c.is_alphanumeric() && c != '_' && c != '/');
+ let first = self.pending.chars().next().expect("nonempty pending");
+ let mut path_prefix = None;
+ if boundary {
+ if self.pending.starts_with('/') {
+ path_prefix = Some(1);
+ } else if self.pending.starts_with("~/") {
+ path_prefix = Some(2);
+ } else if first.is_ascii_alphabetic()
+ && (self.pending.starts_with(&format!("{first}:/"))
+ || self.pending.starts_with(&format!("{first}:\\")))
+ {
+ path_prefix = Some(3);
+ }
+ if self.pending == "~"
+ || (first.is_ascii_alphabetic()
+ && (self.pending.len() == 1 || self.pending == format!("{first}:")))
+ {
+ wait = true;
+ }
+ }
+ if let Some(length) = path_prefix {
+ let rest = &self.pending[length..];
+ if rest.is_empty() {
+ wait = true;
+ } else if rest.chars().next().is_some_and(path_character) {
+ output.push_str("[local path]");
+ self.previous = self.pending.chars().next_back();
+ self.pending.clear();
+ self.mode = TextMode::LocalPath;
+ return;
+ }
+ }
+ if wait && !final_chunk {
+ return;
+ }
+ output.push(first);
+ self.previous = Some(first);
+ self.pending.drain(..first.len_utf8());
+ }
+ }
+}
+pub fn redact_paths(text: &str) -> String {
+ StreamText::default().feed(text, true).1
+}
+
+pub fn formal_title(value: &Value) -> Option {
+ let text = value
+ .as_str()?
+ .split_whitespace()
+ .collect::>()
+ .join(" ");
+ if text.is_empty()
+ || [
+ "codex session",
+ "untitled",
+ "new conversation",
+ "new chat",
+ "未命名会话",
+ "新会话",
+ ]
+ .contains(&text.to_lowercase().as_str())
+ {
+ None
+ } else {
+ Some(redact_paths(&text))
+ }
+}
+
+fn summary(text: &str) -> String {
+ let text = text.replace('\0', "");
+ if text.len() <= 2048 {
+ return text;
+ }
+ let mut end = 2045;
+ while !text.is_char_boundary(end) {
+ end -= 1;
+ }
+ format!("{}…", &text[..end])
+}
+
+pub fn normalise_turn(turn: &Value) -> Value {
+ let mut items = Vec::new();
+ for (index, item) in turn["items"].as_array().into_iter().flatten().enumerate() {
+ let mut metadata = json!({});
+ let (kind, text) = match item["type"].as_str() {
+ Some("userMessage") => (
+ "user_message",
+ item["content"]
+ .as_array()
+ .into_iter()
+ .flatten()
+ .filter(|p| p["type"] == "text")
+ .filter_map(|p| p["text"].as_str())
+ .collect::>()
+ .join("\n"),
+ ),
+ Some("agentMessage") => (
+ "assistant_message",
+ item["text"].as_str().unwrap_or_default().to_owned(),
+ ),
+ Some("commandExecution") => {
+ if let Some(status) = item["status"]
+ .as_str()
+ .filter(|s| ["completed", "failed", "inProgress", "declined"].contains(s))
+ {
+ metadata["status"] = json!(status);
+ }
+ let exit = item["exitCode"].as_i64();
+ if let Some(exit) = exit {
+ metadata["exit_code"] = json!(exit);
+ metadata["status"] = json!(if exit == 0 { "completed" } else { "failed" });
+ }
+ let duration = item["durationMs"].as_f64().filter(|n| *n >= 0.0);
+ if let Some(duration) = duration {
+ metadata["duration_ms"] = json!(duration as u64);
+ }
+ let executable = item["command"]
+ .as_str()
+ .and_then(|s| s.split_whitespace().next())
+ .unwrap_or("command")
+ .trim_matches(['\'', '"'])
+ .rsplit(['/', '\\'])
+ .next()
+ .unwrap_or("command");
+ let mut text = format!("{executable} …");
+ if let Some(exit) = exit {
+ text.push_str(&format!(" · exit {exit}"));
+ }
+ if let Some(duration) = duration {
+ text.push_str(&format!(" · {duration} ms"));
+ }
+ ("command_summary", summary(&text))
+ }
+ Some("fileChange") => {
+ if let Some(status) = item["status"]
+ .as_str()
+ .filter(|s| ["completed", "failed", "inProgress", "declined"].contains(s))
+ {
+ metadata["status"] = json!(status);
+ }
+ let text = item["changes"]
+ .as_array()
+ .into_iter()
+ .flatten()
+ .map(|c| {
+ let name = c["path"]
+ .as_str()
+ .unwrap_or("file")
+ .rsplit(['/', '\\'])
+ .next()
+ .unwrap_or("file");
+ let kind = c["kind"]
+ .as_str()
+ .or_else(|| c["kind"]["type"].as_str())
+ .unwrap_or("update");
+ format!("{kind}: {name}")
+ })
+ .collect::>()
+ .join("\n");
+ ("file_change_summary", summary(&text))
+ }
+ _ => continue,
+ };
+ if text.is_empty() {
+ continue;
+ }
+ metadata["item_id"] = item["id"]
+ .as_str()
+ .map_or_else(|| json!(format!("item-{index}")), |s| json!(s));
+ metadata["kind"] = json!(kind);
+ metadata["text"] = json!(redact_paths(&text));
+ items.push(metadata);
+ }
+ if !turn["error"].is_null() {
+ items.push(json!({"item_id":format!("{}-error",turn["id"].as_str().unwrap_or("turn")),"kind":"error","text":summary(&redact_paths(turn["error"]["message"].as_str().unwrap_or("Codex turn failed")))}));
+ }
+ json!({"turn_id":turn["id"].as_str().unwrap_or_default(),"status":turn["status"].as_str().unwrap_or("unknown"),"started_at_unix":turn["startedAt"],"completed_at_unix":turn["completedAt"],"items":items})
+}
+
+pub fn decode_cursor(session: &str, cursor: Option<&str>) -> Result<(Option, Value)> {
+ let Some(raw) = cursor.and_then(|c| c.strip_prefix("fh1.")) else {
+ return Ok((cursor.map(str::to_owned), Value::Null));
+ };
+ ensure!(raw.len() < 8192, "invalid_history_cursor");
+ let value: Value = serde_json::from_slice(
+ &URL_SAFE_NO_PAD
+ .decode(raw)
+ .context("invalid_history_cursor")?,
+ )
+ .context("invalid_history_cursor")?;
+ ensure!(
+ value["session"] == session
+ && ["t", "i", "offset"]
+ .iter()
+ .all(|key| value[*key].as_u64().is_some()),
+ "invalid_history_cursor"
+ );
+ Ok((value["upstream"].as_str().map(str::to_owned), value))
+}
+
+pub fn bounded_page(
+ session: &str,
+ turns: &[Value],
+ upstream: Option<&str>,
+ next: Option<&str>,
+ resume: &Value,
+) -> Result {
+ let start = resume["t"].as_u64().unwrap_or(0) as usize;
+ ensure!(
+ resume.is_null() || start < turns.len(),
+ "history_changed_reload"
+ );
+ let mut page = json!({"session_id":session,"turns":[],"next_cursor":next});
+ for (ti, raw) in turns.iter().enumerate().skip(start) {
+ let mut turn = normalise_turn(raw);
+ ensure!(
+ resume.is_null() || ti != start || turn["turn_id"] == resume["turn"],
+ "history_changed_reload"
+ );
+ let items = turn["items"].take().as_array().cloned().unwrap_or_default();
+ let turn_id = turn["turn_id"].clone();
+ turn["items"] = json!([]);
+ page["turns"].as_array_mut().expect("turns").push(turn);
+ let pi = page["turns"].as_array().expect("turns").len() - 1;
+ let first = if ti == start {
+ resume["i"].as_u64().unwrap_or(0) as usize
+ } else {
+ 0
+ };
+ ensure!(
+ resume.is_null() || ti != start || first < items.len(),
+ "history_changed_reload"
+ );
+ for (ii, item) in items.iter().enumerate().skip(first) {
+ let offset = if ti == start && ii == first {
+ resume["offset"].as_u64().unwrap_or(0) as usize
+ } else {
+ 0
+ };
+ ensure!(
+ resume.is_null() || ti != start || ii != first || item["item_id"] == resume["item"],
+ "history_changed_reload"
+ );
+ let text = item["text"].as_str().unwrap_or_default();
+ let chars = text.chars().collect::>();
+ ensure!(offset <= chars.len(), "invalid_history_offset");
+ let mut fragment = item.clone();
+ fragment["text"] = json!(chars[offset..].iter().collect::());
+ fragment["text_offset"] = json!(offset);
+ fragment["text_complete"] = json!(true);
+ let target = page["turns"][pi]["items"].as_array_mut().expect("items");
+ let fi = target.len();
+ target.push(fragment);
+ if serde_json::to_vec(&page)?.len() <= PAGE_BYTES {
+ continue;
+ }
+ let (mut low, mut high) = (0, chars.len() - offset);
+ while low < high {
+ let mid = (low + high).div_ceil(2);
+ page["turns"][pi]["items"][fi]["text"] =
+ json!(chars[offset..offset + mid].iter().collect::());
+ if serde_json::to_vec(&page)?.len() <= PAGE_BYTES - 1024 {
+ low = mid;
+ } else {
+ high = mid - 1;
+ }
+ }
+ page["turns"][pi]["items"][fi]["text"] =
+ json!(chars[offset..offset + low].iter().collect::());
+ page["turns"][pi]["items"][fi]["text_complete"] = json!(false);
+ if low == 0 {
+ let target = page["turns"][pi]["items"].as_array_mut().expect("items");
+ target.pop();
+ if target.is_empty() {
+ page["turns"].as_array_mut().expect("turns").pop();
+ }
+ }
+ let cursor = json!({"session":session,"upstream":upstream,"t":ti,"i":ii,"offset":offset+low,"turn":turn_id,"item":item["item_id"]});
+ page["next_cursor"] = json!(format!(
+ "fh1.{}",
+ URL_SAFE_NO_PAD.encode(serde_json::to_vec(&cursor)?)
+ ));
+ page["continuation"] = json!({"kind":if offset+low>0 {"message"} else {"history"},"turn_id":turn_id,"item_id":item["item_id"],"text_offset":offset+low});
+ return Ok(page);
+ }
+ }
+ Ok(page)
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+ #[test]
+ fn paths_urls_math_and_split_stream_share_one_projection() {
+ assert_eq!(
+ redact_paths("HTTPS://example.org/a/b"),
+ "HTTPS://example.org/a/b"
+ );
+ let raw = "**训练🙂** [文档](https://example.org/a/b?q=1) [本地](/home/private/result.json) `curl https://example.org/api` 公式 \\(a/b\\) $x/y$ C:\\private\\file.py\n";
+ let expected = raw
+ .replace("/home/private/result.json", "[local path]")
+ .replace("C:\\private\\file.py", "[local path]");
+ assert_eq!(redact_paths(raw), expected);
+ let mut stream = StreamText::default();
+ let mut result = String::new();
+ for c in raw.chars() {
+ let (offset, text) = stream.feed(&c.to_string(), false);
+ assert_eq!(offset, result.chars().count());
+ result.push_str(&text);
+ assert!(!result.contains("private"));
+ }
+ result.push_str(&stream.feed("", true).1);
+ assert_eq!(result, expected);
+ }
+ #[test]
+ fn ordinary_streaming_text_does_not_wait_for_whitespace_and_paths_are_bounded() {
+ let mut stream = StreamText::default();
+ assert_eq!(stream.feed("正在检查项目", false).1, "正在检查项目");
+ assert_eq!(stream.feed(" /home/", false).1, " [local path]");
+ for _ in 0..10_000 {
+ assert!(stream.feed("private", false).1.is_empty());
+ assert!(stream.pending.len() <= 8);
+ }
+ assert_eq!(stream.feed(" 完成", false).1, " 完成");
+ for raw in [
+ "https://example.org/a/b",
+ "[x](/private/file)",
+ "C:\\private\\file.py",
+ "\\(a/b\\)",
+ "没有标点的中文",
+ "http:/private",
+ "~/secret",
+ "a/b",
+ "文本 C:/private 再继续",
+ ] {
+ let expected = redact_paths(raw);
+ for width in [1, 3, 17] {
+ let characters: Vec<_> = raw.chars().collect();
+ let mut stream = StreamText::default();
+ let mut text = String::new();
+ for chunk in characters.chunks(width) {
+ text.push_str(&stream.feed(&chunk.iter().collect::(), false).1);
+ }
+ text.push_str(&stream.feed("", true).1);
+ assert_eq!(text, expected);
+ }
+ }
+ }
+ #[test]
+ fn large_unicode_continuations_are_lossless_and_bound_to_identity() {
+ let text = "训练🙂结果\n".repeat(100_000);
+ let turns = vec![
+ json!({"id":"turn","status":"completed","items":[{"id":"item","type":"agentMessage","text":text}]}),
+ ];
+ let mut cursor = None;
+ let mut result = String::new();
+ let mut pages = 0;
+ loop {
+ let (upstream, resume) = decode_cursor("s", cursor.as_deref()).unwrap();
+ let page = bounded_page("s", &turns, upstream.as_deref(), None, &resume).unwrap();
+ assert!(serde_json::to_vec(&page).unwrap().len() < 512 * 1024);
+ for turn in page["turns"].as_array().unwrap() {
+ for item in turn["items"].as_array().unwrap() {
+ assert_eq!(
+ item["text_offset"].as_u64().unwrap() as usize,
+ result.chars().count()
+ );
+ result.push_str(item["text"].as_str().unwrap());
+ }
+ }
+ pages += 1;
+ cursor = page["next_cursor"].as_str().map(str::to_owned);
+ if cursor.is_none() {
+ break;
+ }
+ assert!(decode_cursor("other", cursor.as_deref()).is_err());
+ assert!(pages < 20);
+ }
+ assert!(pages > 1);
+ assert_eq!(result, text);
+ assert!(
+ bounded_page(
+ "s",
+ &turns,
+ None,
+ None,
+ &json!({"t":0,"i":0,"offset":0,"turn":"turn","item":"wrong"})
+ )
+ .is_err()
+ );
+ }
+}
diff --git a/farhelm-agent/src/codex/mod.rs b/farhelm-agent/src/codex/mod.rs
new file mode 100644
index 0000000..8d4a2be
--- /dev/null
+++ b/farhelm-agent/src/codex/mod.rs
@@ -0,0 +1,695 @@
+//! Local Codex is the transcript/authentication authority. No vendor body is a Hub record.
+pub mod history;
+mod protocol;
+#[cfg(test)]
+mod tests;
+pub mod transport;
+use anyhow::{Context, Result, bail, ensure};
+use futures_util::{
+ FutureExt,
+ future::{BoxFuture, WeakShared},
+};
+use serde::{Deserialize, Serialize};
+use serde_json::{Value, json};
+use std::{
+ collections::{BTreeMap, HashMap},
+ future::Future,
+ path::PathBuf,
+ sync::{Arc, atomic::Ordering},
+ time::{Duration, Instant},
+};
+use tokio::sync::{Mutex, RwLock, Semaphore, watch};
+type SharedRead = BoxFuture<'static, std::result::Result>;
+use protocol::*;
+use transport::Connection;
+
+#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
+pub struct CodexStatus {
+ pub state: String,
+ pub version: Option,
+ pub reason: Option,
+}
+
+#[derive(Default)]
+struct Index {
+ rows: HashMap,
+ refreshed: Option,
+ revision: u64,
+ changed: HashMap,
+}
+struct Inner {
+ bin: Option,
+ connection: Mutex>>,
+ index: RwLock,
+ refresh: Mutex<()>,
+ reads: Arc,
+ inflight: Mutex>>,
+ status: watch::Sender,
+ loaded: RwLock>,
+}
+struct LoadedThread {
+ thread: Value,
+ mode: String,
+ has_turns: bool,
+}
+#[derive(Clone)]
+pub struct Codex {
+ inner: Arc,
+}
+
+impl Codex {
+ pub fn new(bin: Option) -> Self {
+ Self {
+ inner: Arc::new(Inner {
+ bin,
+ connection: Mutex::new(None),
+ index: RwLock::new(Index::default()),
+ refresh: Mutex::new(()),
+ reads: Arc::new(Semaphore::new(4)),
+ inflight: Mutex::new(HashMap::new()),
+ loaded: RwLock::new(HashMap::new()),
+ status: watch::channel(CodexStatus {
+ state: "starting".into(),
+ version: None,
+ reason: None,
+ })
+ .0,
+ }),
+ }
+ }
+ pub fn status(&self) -> CodexStatus {
+ self.inner.status.borrow().clone()
+ }
+ pub fn subscribe_status(&self) -> watch::Receiver {
+ self.inner.status.subscribe()
+ }
+ pub async fn warm(&self) -> Result<()> {
+ let connection = self.connection().await?;
+ self.refresh_account(&connection).await
+ }
+ async fn refresh_account(&self, connection: &Connection) -> Result<()> {
+ let result = connection
+ .request("account/read", json!({"refreshToken":false}))
+ .await
+ .and_then(|value| {
+ serde_json::from_value::(value).map_err(Into::into)
+ });
+ let mut status = self.status();
+ match &result {
+ Ok(account) => {
+ let missing = account.requires_openai_auth && account.account.is_none();
+ status.state = if missing { "login_required" } else { "ready" }.into();
+ status.reason = missing.then(|| "codex_login_required".into());
+ }
+ _ => {
+ status.state = "unavailable".into();
+ status.reason = Some("codex_account_read_failed".into());
+ }
+ }
+ let ready = status.state != "unavailable";
+ self.inner.status.send_if_modified(|current| {
+ if current == &status {
+ false
+ } else {
+ *current = status;
+ true
+ }
+ });
+ ensure!(ready, "codex_account_read_failed");
+ Ok(())
+ }
+ pub async fn shutdown(&self) {
+ if let Some(connection) = self.inner.connection.lock().await.take() {
+ connection.shutdown().await;
+ }
+ }
+
+ pub async fn connection(&self) -> Result> {
+ let mut current = self.inner.connection.lock().await;
+ if let Some(connection) = current.as_ref()
+ && connection.alive.load(Ordering::Acquire)
+ {
+ return Ok(connection.clone());
+ }
+ if let Some(old) = current.take() {
+ old.shutdown().await;
+ }
+ self.inner.loaded.write().await.clear();
+ let result: Result<(Arc, String)> = async {
+ let explicit = self.inner.bin.clone();
+ let bin = tokio::task::spawn_blocking(move || transport::discover(explicit.as_deref()))
+ .await??;
+ let version = transport::version(&bin).await?;
+ let connection = Connection::open(&bin).await?;
+ Ok((connection, version))
+ }
+ .await;
+ match result {
+ Ok((connection, version)) => {
+ self.inner.status.send_replace(CodexStatus {
+ state: "starting".into(),
+ version: Some(version),
+ reason: None,
+ });
+ *current = Some(connection.clone());
+ let weak = Arc::downgrade(&self.inner);
+ let mut events = connection.events.subscribe();
+ tokio::spawn(async move {
+ loop {
+ let event = match events.recv().await {
+ Ok(e) => e,
+ Err(tokio::sync::broadcast::error::RecvError::Lagged(_)) => {
+ if let Some(inner) = weak.upgrade() {
+ inner.index.write().await.refreshed = None;
+ }
+ continue;
+ }
+ Err(_) => break,
+ };
+ let Some(inner) = weak.upgrade() else { break };
+ if event["method"] == "farhelm/disconnected" {
+ let mut status = inner.status.borrow().clone();
+ status.state = "unavailable".into();
+ status.reason = Some("codex_connection_closed".into());
+ inner.status.send_replace(status);
+ break;
+ }
+ let params = &event["params"];
+ let mut index = inner.index.write().await;
+ if event["method"] == "thread/started"
+ && let Some(id) = params["thread"]["id"].as_str()
+ {
+ let mut row = params["thread"].clone();
+ row["archived"] = json!(false);
+ index.rows.insert(id.to_owned(), row);
+ index.revision += 1;
+ let revision = index.revision;
+ index.changed.insert(id.to_owned(), revision);
+ } else if let Some(id) = params["threadId"].as_str() {
+ if let Some(row) = index.rows.get_mut(id) {
+ match event["method"].as_str() {
+ Some("thread/name/updated") => {
+ row["name"] = params["threadName"].clone();
+ }
+ Some("thread/archived") => row["archived"] = json!(true),
+ Some("thread/unarchived") => row["archived"] = json!(false),
+ Some("thread/status/changed") => {
+ row["status"] = params["status"].clone()
+ }
+ Some("turn/completed") => index.refreshed = None,
+ _ => {}
+ }
+ index.revision += 1;
+ let revision = index.revision;
+ index.changed.insert(id.to_owned(), revision);
+ } else if event["method"]
+ .as_str()
+ .is_some_and(|m| m.starts_with("thread/"))
+ {
+ index.refreshed = None;
+ }
+ }
+ }
+ });
+ self.refresh_account(&connection).await?;
+ Ok(connection)
+ }
+ Err(error) => {
+ let code = error.to_string();
+ let reason = code
+ .split(':')
+ .next()
+ .filter(|s| {
+ s.len() <= 128 && s.bytes().all(|b| b.is_ascii_lowercase() || b == b'_')
+ })
+ .unwrap_or("codex_unavailable")
+ .to_owned();
+ self.inner.status.send_replace(CodexStatus {
+ state: "unavailable".into(),
+ version: None,
+ reason: Some(reason),
+ });
+ Err(error)
+ }
+ }
+ }
+
+ async fn index(&self, force: bool) -> Result> {
+ let _refresh = self.inner.refresh.lock().await;
+ if !force
+ && self
+ .inner
+ .index
+ .read()
+ .await
+ .refreshed
+ .is_some_and(|time| time.elapsed() < Duration::from_secs(30))
+ {
+ return Ok(self
+ .inner
+ .index
+ .read()
+ .await
+ .rows
+ .values()
+ .cloned()
+ .collect());
+ }
+ let connection = self.connection().await?;
+ let revision = self.inner.index.read().await.revision;
+ let mut rows = HashMap::new();
+ for archived in [false, true] {
+ let mut cursor = Value::Null;
+ loop {
+ let result = connection
+ .request(
+ "thread/list",
+ serde_json::to_value(ThreadListParams {
+ archived,
+ limit: 100,
+ cursor: cursor.as_str().map(str::to_owned),
+ source_kinds: ["cli", "vscode", "exec", "appServer", "unknown"],
+ })?,
+ )
+ .await?;
+ for raw in result["data"].as_array().context("codex_index_invalid")? {
+ if let Some(id) = raw["id"].as_str() {
+ let mut row = raw.clone();
+ row["archived"] = json!(archived);
+ rows.insert(id.to_owned(), row);
+ }
+ }
+ let next = result["nextCursor"].clone();
+ if next.is_null() || next == cursor {
+ break;
+ }
+ ensure!(rows.len() < 100_000, "codex_index_capacity");
+ cursor = next;
+ }
+ }
+ let mut index = self.inner.index.write().await;
+ // Preserve native changes that arrived while paginated listing was in flight.
+ for (id, changed) in &index.changed {
+ if *changed > revision
+ && let Some(current) = index.rows.get(id)
+ {
+ if let Some(row) = rows.get_mut(id) {
+ for field in ["name", "status", "archived"] {
+ if let Some(value) = current.get(field) {
+ row[field] = value.clone();
+ }
+ }
+ } else {
+ rows.insert(id.clone(), current.clone());
+ }
+ }
+ }
+ index.rows = rows;
+ index.refreshed = Some(Instant::now());
+ index.changed.retain(|_, changed| *changed > revision);
+ Ok(index.rows.values().cloned().collect())
+ }
+
+ pub async fn call(&self, method: &str, params: Value) -> Result {
+ if !matches!(method, "codex.session.history" | "codex.session.display") {
+ return self.call_inner(method, params).await;
+ }
+ let key = serde_json::to_string(&(method, ¶ms))?;
+ let future = {
+ let mut inflight = self.inner.inflight.lock().await;
+ inflight.retain(|_, value| value.upgrade().is_some());
+ if let Some(future) = inflight.get(&key).and_then(WeakShared::upgrade) {
+ future
+ } else {
+ ensure!(inflight.len() < 32, "codex_read_capacity");
+ let native = self.clone();
+ let method = method.to_owned();
+ let future: SharedRead = async move {
+ native
+ .call_inner(&method, params)
+ .await
+ .map_err(|error| error.to_string())
+ }
+ .boxed();
+ let future = future.shared();
+ inflight.insert(key.clone(), future.downgrade().expect("live read future"));
+ future
+ }
+ };
+ let result = future.await;
+ self.inner.inflight.lock().await.remove(&key);
+ result.map_err(anyhow::Error::msg)
+ }
+ async fn call_inner(&self, method: &str, params: Value) -> Result {
+ let _permit = self.inner.reads.acquire().await?;
+ match method {
+ "codex.projects.discover" => {
+ let rows = self.index(true).await?;
+ let mut projects = BTreeMap::::new();
+ let mut sessions = Vec::new();
+ for row in rows {
+ let Some(cwd) = row["cwd"]
+ .as_str()
+ .filter(|s| std::path::Path::new(s).is_absolute())
+ else {
+ continue;
+ };
+ let archived = row["archived"].as_bool().unwrap_or(false);
+ let updated = row["updatedAt"].as_u64().unwrap_or(0);
+ let project = projects.entry(cwd.into()).or_insert_with(
+ || json!({"cwd":cwd,"session_count":0,"updated_at_unix":0}),
+ );
+ project["session_count"] =
+ json!(project["session_count"].as_u64().unwrap_or(0) + 1);
+ project["updated_at_unix"] = json!(
+ project["updated_at_unix"]
+ .as_u64()
+ .unwrap_or(0)
+ .max(updated)
+ );
+ sessions.push(json!({"session_id":row["id"],"title":history::formal_title(&row["name"]),"cwd":cwd,"archived":archived,"updated_at_unix":updated}));
+ }
+ Ok(
+ json!({"projects":projects.into_values().collect::>(),"sessions":sessions}),
+ )
+ }
+ "codex.sessions.list" => {
+ let rows = self.index(false).await?;
+ let archived = params["archived"].as_str().unwrap_or("false");
+ let sessions = rows
+ .iter()
+ .filter(|row| {
+ row["cwd"] == params["project_path"]
+ && (archived == "all"
+ || row["archived"].as_bool() == Some(archived == "true"))
+ })
+ .map(thread_result)
+ .collect::>();
+ Ok(json!({"sessions":sessions,"next_cursor":null}))
+ }
+ "codex.session.display" => Ok(display_page(&self.index(false).await?, ¶ms)),
+ "codex.session.history" => {
+ let session = params["session_id"]
+ .as_str()
+ .context("history_session_missing")?
+ .to_owned();
+ let (upstream, resume) =
+ history::decode_cursor(&session, params["cursor"].as_str())?;
+ let connection = self.connection().await?;
+ if let Some(loaded) = self.inner.loaded.read().await.get(&session)
+ && !loaded.has_turns
+ {
+ return Ok(json!({"session_id":session,"turns":[],"next_cursor":null}));
+ }
+ let limit = params["limit"].as_u64().unwrap_or(20).clamp(1, 50) as usize;
+ let legacy = self
+ .status()
+ .version
+ .as_deref()
+ .and_then(|v| semver::Version::parse(v).ok())
+ .is_some_and(|v| v < semver::Version::new(0, 153, 0));
+ let (turns, next) = if legacy {
+ let offset = upstream
+ .as_deref()
+ .map(|cursor| {
+ cursor
+ .strip_prefix("legacy:")
+ .context("invalid_history_cursor")?
+ .parse::()
+ .context("invalid_history_cursor")
+ })
+ .transpose()?
+ .unwrap_or(0);
+ let result = connection
+ .request(
+ "thread/read",
+ serde_json::to_value(ThreadReadParams {
+ thread_id: &session,
+ include_turns: true,
+ })?,
+ )
+ .await?;
+ let data = result["thread"]["turns"]
+ .as_array()
+ .context("codex_history_invalid")?;
+ ensure!(offset <= data.len(), "history_changed_reload");
+ let next =
+ (offset + limit < data.len()).then(|| format!("legacy:{}", offset + limit));
+ (
+ data.iter()
+ .rev()
+ .skip(offset)
+ .take(limit)
+ .cloned()
+ .collect::>(),
+ next,
+ )
+ } else {
+ let result: ThreadPage = serde_json::from_value(
+ connection
+ .request(
+ "thread/turns/list",
+ serde_json::to_value(ThreadTurnsListParams {
+ thread_id: &session,
+ cursor: upstream.as_deref(),
+ limit: limit as u32,
+ items_view: "full",
+ })?,
+ )
+ .await?,
+ )
+ .context("codex_history_invalid")?;
+ (result.data, result.next_cursor)
+ };
+ crate::runtime_tasks::blocking(move || {
+ history::bounded_page(
+ &session,
+ &turns,
+ upstream.as_deref(),
+ next.as_deref(),
+ &resume,
+ )
+ })
+ .await
+ }
+ "codex.session.start" | "codex.session.resume" => {
+ let connection = self.connection().await?;
+ let mode = params["mode"].as_str().unwrap_or("inspect");
+ let sandbox = match mode {
+ "inspect" => "read-only",
+ "edit" => "workspace-write",
+ _ => bail!("invalid_session_mode"),
+ };
+ if method == "codex.session.resume"
+ && let Some(id) = params["session_id"].as_str()
+ && let Some(loaded) = self.inner.loaded.read().await.get(id)
+ {
+ ensure!(
+ loaded.thread["cwd"] == params["cwd"] && loaded.mode == mode,
+ "session_project_mismatch"
+ );
+ return Ok(thread_result(&loaded.thread));
+ }
+ let mut request =
+ json!({"cwd":params["cwd"],"sandbox":sandbox,"approvalPolicy":"on-request"});
+ if method == "codex.session.resume" {
+ request["threadId"] = params["session_id"].clone();
+ if self
+ .status()
+ .version
+ .as_deref()
+ .and_then(|v| semver::Version::parse(v).ok())
+ .is_some_and(|v| v >= semver::Version::new(0, 153, 0))
+ {
+ request["excludeTurns"] = json!(true);
+ }
+ }
+ let response = connection
+ .request(
+ if method == "codex.session.start" {
+ "thread/start"
+ } else {
+ "thread/resume"
+ },
+ request,
+ )
+ .await?;
+ let thread = &response["thread"];
+ ensure!(thread["cwd"] == params["cwd"], "session_project_mismatch");
+ if method == "codex.session.start" {
+ connection.request("thread/name/set",json!({"threadId":thread["id"],"name":history::formal_title(&thread["name"]).unwrap_or_else(||"Codex session".into())})).await?;
+ }
+ let mut row = thread.clone();
+ row["archived"] = json!(false);
+ if let Some(id) = row["id"].as_str() {
+ let id = id.to_owned();
+ self.inner.loaded.write().await.insert(
+ id.clone(),
+ LoadedThread {
+ thread: row.clone(),
+ mode: mode.to_owned(),
+ has_turns: method == "codex.session.resume",
+ },
+ );
+ self.inner.index.write().await.rows.insert(id, row);
+ }
+ Ok(thread_result(thread))
+ }
+ "codex.turn.steer" => {
+ self.connection()
+ .await?
+ .request(
+ "turn/steer",
+ serde_json::to_value(TurnSteerParams {
+ thread_id: params["session_id"]
+ .as_str()
+ .context("session_id_missing")?,
+ expected_turn_id: params["turn_id"]
+ .as_str()
+ .context("turn_id_missing")?,
+ input: [UserInput::Text {
+ text: params["prompt"].as_str().context("prompt_missing")?,
+ }],
+ })?,
+ )
+ .await
+ }
+ "codex.turn.interrupt" => {
+ self.connection()
+ .await?
+ .request(
+ "turn/interrupt",
+ serde_json::to_value(TurnInterruptParams {
+ thread_id: params["session_id"]
+ .as_str()
+ .context("session_id_missing")?,
+ turn_id: params["turn_id"].as_str().context("turn_id_missing")?,
+ })?,
+ )
+ .await
+ }
+ _ => bail!("unsupported_codex_method"),
+ }
+ }
+
+ pub async fn turn(
+ &self,
+ session: &str,
+ prompt: &str,
+ operation: &str,
+ mut emit: F,
+ ) -> Result
+ where
+ F: FnMut(&'static str, Value) -> Fut,
+ Fut: Future>,
+ {
+ let connection = self.connection().await?;
+ let mut events = connection.events.subscribe();
+ if let Some(loaded) = self.inner.loaded.write().await.get_mut(session) {
+ loaded.has_turns = true;
+ }
+ let response = connection
+ .request(
+ "turn/start",
+ serde_json::to_value(TurnStartParams {
+ thread_id: session,
+ input: [UserInput::Text { text: prompt }],
+ client_user_message_id: operation,
+ })?,
+ )
+ .await
+ .map_err(|error| anyhow::Error::new(crate::CodexTurnOrphaned(error.to_string())))?;
+ let turn = response["turn"]["id"]
+ .as_str()
+ .context("turn_id_missing")?
+ .to_owned();
+ emit(
+ "codex.turn.started",
+ json!({"session_id":session,"turn_id":turn}),
+ )
+ .await?;
+ let mut buffers = HashMap::::new();
+ loop {
+ let event = events.recv().await.map_err(|_| {
+ anyhow::Error::new(crate::CodexTurnOrphaned("codex_event_stream_lost".into()))
+ })?;
+ if event["method"] == "farhelm/disconnected" {
+ return Err(crate::CodexTurnOrphaned("codex_connection_closed".into()).into());
+ }
+ let data = &event["params"];
+ if data["threadId"] != session
+ || data["turnId"]
+ .as_str()
+ .or_else(|| data["turn"]["id"].as_str())
+ != Some(turn.as_str())
+ {
+ continue;
+ }
+ if event["method"] == "item/agentMessage/delta" {
+ if let (Some(item), Some(delta)) = (data["itemId"].as_str(), data["delta"].as_str())
+ {
+ let (offset, text) = buffers
+ .entry(item.to_owned())
+ .or_default()
+ .feed(delta, false);
+ if !text.is_empty() {
+ emit("codex.message.delta",json!({"session_id":session,"turn_id":turn,"item_id":item,"text_offset":offset,"delta":text})).await?;
+ }
+ }
+ } else if event["method"] == "turn/completed" {
+ for (item, buffer) in &mut buffers {
+ let (offset, text) = buffer.feed("", true);
+ if !text.is_empty() {
+ emit("codex.message.delta",json!({"session_id":session,"turn_id":turn,"item_id":item,"text_offset":offset,"delta":text})).await?;
+ }
+ }
+ let status = data["turn"]["status"].as_str().unwrap_or("unknown");
+ emit(
+ if status == "completed" {
+ "codex.turn.completed"
+ } else {
+ "codex.turn.failed"
+ },
+ json!({"session_id":session,"turn_id":turn,"status":status}),
+ )
+ .await?;
+ ensure!(status == "completed", "codex_turn_failed");
+ return Ok(turn);
+ }
+ }
+ }
+}
+
+fn thread_result(row: &Value) -> Value {
+ json!({"session_id":row["id"],"cwd":row["cwd"],"title":history::formal_title(&row["name"]),"archived":row["archived"],"updated_at_unix":row["updatedAt"],"created_at_unix":row["createdAt"]})
+}
+
+fn display_page(rows: &[Value], params: &Value) -> Value {
+ let query = params["query"]
+ .as_str()
+ .unwrap_or_default()
+ .trim()
+ .to_lowercase();
+ let archived = params["archived"].as_str().unwrap_or("false");
+ let agent = params["agent_id"].as_str().unwrap_or_default();
+ let after = ¶ms["after"];
+ let mut matches=rows.iter().filter_map(|row|{
+ let id=row["id"].as_str()?;let binding=¶ms["bindings"][id];
+ if binding.is_null() || row["cwd"]!=binding["cwd"] || (archived!="all" && row["archived"].as_bool()!=Some(archived=="true")) {return None;}
+ let title=history::formal_title(&row["name"]);let preview=row["preview"].as_str().unwrap_or_default();
+ if !query.is_empty() && !title.as_deref().unwrap_or_default().to_lowercase().contains(&query) && !preview.to_lowercase().contains(&query) {return None;}
+ let updated=row["updatedAt"].as_u64().unwrap_or(0);
+ if let Some(at)=after["updated_at_unix"].as_u64() && (std::cmp::Reverse(updated),agent,id)<=(std::cmp::Reverse(at),after["agent_id"].as_str().unwrap_or_default(),after["session_id"].as_str().unwrap_or_default()) {return None;}
+ let label=title.unwrap_or_else(||history::redact_paths(preview).split_whitespace().collect::>().join(" ").chars().take(80).collect());
+ Some(json!({"session_id":id,"project_id":binding["project_id"],"display_label":if label.is_empty() {Value::Null} else {json!(label)},"updated_at_unix":updated}))
+ }).collect::>();
+ matches.sort_by(|a, b| {
+ b["updated_at_unix"]
+ .as_u64()
+ .cmp(&a["updated_at_unix"].as_u64())
+ .then_with(|| a["session_id"].as_str().cmp(&b["session_id"].as_str()))
+ });
+ let more = matches.len() > 50;
+ matches.truncate(51);
+ json!({"sessions":matches,"has_more":more})
+}
diff --git a/farhelm-agent/src/codex/protocol.rs b/farhelm-agent/src/codex/protocol.rs
new file mode 100644
index 0000000..777f101
--- /dev/null
+++ b/farhelm-agent/src/codex/protocol.rs
@@ -0,0 +1,88 @@
+// SPDX-License-Identifier: Apache-2.0
+// Field bindings adapted from OpenAI Codex App Server's generated schemas:
+// 0.153.4: 3d2ee51ca2d5db578f328aa75e20aa22c0197c9a
+// 0.147.0: be6e8eac029b183056b7e4402879f15d2c85f61b
+// Changes: only FarHelm's supported fields; serde-only dependencies; text input.
+// See third-party-notices.txt for attribution and the Apache-2.0 license.
+use serde::{Deserialize, Serialize};
+use serde_json::Value;
+
+#[derive(Serialize)]
+#[serde(rename_all = "camelCase")]
+pub struct ClientInfo<'a> {
+ pub name: &'a str,
+ pub title: &'a str,
+ pub version: &'a str,
+}
+#[derive(Serialize)]
+#[serde(rename_all = "camelCase")]
+pub struct InitializeParams<'a> {
+ pub client_info: ClientInfo<'a>,
+ pub capabilities: Capabilities,
+}
+#[derive(Serialize)]
+#[serde(rename_all = "camelCase")]
+pub struct Capabilities {
+ pub experimental_api: bool,
+}
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub struct GetAccountResponse {
+ pub requires_openai_auth: bool,
+ pub account: Option,
+}
+
+#[derive(Serialize)]
+#[serde(rename_all = "camelCase")]
+pub struct ThreadListParams {
+ pub archived: bool,
+ pub limit: u32,
+ pub cursor: Option,
+ pub source_kinds: [&'static str; 5],
+}
+#[derive(Serialize)]
+#[serde(rename_all = "camelCase")]
+pub struct ThreadTurnsListParams<'a> {
+ pub thread_id: &'a str,
+ pub cursor: Option<&'a str>,
+ pub limit: u32,
+ pub items_view: &'static str,
+}
+#[derive(Serialize)]
+#[serde(rename_all = "camelCase")]
+pub struct ThreadReadParams<'a> {
+ pub thread_id: &'a str,
+ pub include_turns: bool,
+}
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub struct ThreadPage {
+ pub data: Vec,
+ pub next_cursor: Option,
+}
+
+#[derive(Serialize)]
+#[serde(tag = "type", rename_all = "camelCase")]
+pub enum UserInput<'a> {
+ Text { text: &'a str },
+}
+#[derive(Serialize)]
+#[serde(rename_all = "camelCase")]
+pub struct TurnStartParams<'a> {
+ pub thread_id: &'a str,
+ pub input: [UserInput<'a>; 1],
+ pub client_user_message_id: &'a str,
+}
+#[derive(Serialize)]
+#[serde(rename_all = "camelCase")]
+pub struct TurnSteerParams<'a> {
+ pub thread_id: &'a str,
+ pub expected_turn_id: &'a str,
+ pub input: [UserInput<'a>; 1],
+}
+#[derive(Serialize)]
+#[serde(rename_all = "camelCase")]
+pub struct TurnInterruptParams<'a> {
+ pub thread_id: &'a str,
+ pub turn_id: &'a str,
+}
diff --git a/farhelm-agent/src/codex/tests.rs b/farhelm-agent/src/codex/tests.rs
new file mode 100644
index 0000000..fdaf02a
--- /dev/null
+++ b/farhelm-agent/src/codex/tests.rs
@@ -0,0 +1,155 @@
+use super::*;
+use std::os::unix::fs::PermissionsExt;
+
+fn fixture(directory: &std::path::Path, version: &str) -> PathBuf {
+ let path = directory.join("codex");
+ let script = r#"#!/bin/sh
+if [ "$1" = '--version' ]; then printf 'codex-cli VERSION\n'; exit 0; fi
+while IFS= read -r line; do
+ id=$(printf '%s' "$line" | sed -n 's/.*"id":\([0-9]*\).*/\1/p')
+ [ -n "$id" ] || continue
+ case "$line" in
+ *'"method":"initialize"'*) result='{"userAgent":"fixture"}' ;;
+ *'"method":"account/read"'*) result='{"requiresOpenaiAuth":false,"account":null}' ;;
+ *'"method":"thread/list"'*)
+ printf 'list\n' >> 'RECORD'
+ case "$line" in *'"archived":true'*) result='{"data":[],"nextCursor":null}' ;; *) result='{"data":[{"id":"s","cwd":"/tmp/project","name":null,"preview":"首条用户请求","updatedAt":2}],"nextCursor":null}' ;; esac ;;
+ *'"method":"thread/turns/list"'*)
+ printf 'pages\n' >> 'RECORD'; sleep 0.03
+ result='{"data":[{"id":"t1","status":"completed","items":[{"id":"u","type":"userMessage","content":[{"type":"text","text":"用户输入"}]},{"id":"i","type":"agentMessage","text":"**回复** https://example.org/a /private/file"}]}],"nextCursor":null}' ;;
+ *'"method":"thread/read"'*)
+ printf 'legacy\n' >> 'RECORD'; sleep 0.03
+ result='{"thread":{"turns":[{"id":"t1","status":"completed","items":[{"id":"u","type":"userMessage","content":[{"type":"text","text":"用户输入"}]},{"id":"i","type":"agentMessage","text":"**回复** https://example.org/a /private/file"}]}]}}' ;;
+ *'"method":"thread/start"'*) result='{"thread":{"id":"new","cwd":"/tmp/project","name":null,"turns":[]}}' ;;
+ *'"method":"thread/resume"'*) printf '{"id":%s,"error":{"code":-32600,"message":"Empty sessions must use their loaded thread"}}\n' "$id"; continue ;;
+ *'"method":"turn/start"'*)
+ printf 'turn\n' >> 'RECORD'
+ printf '{"id":%s,"result":{"turn":{"id":"active"}}}\n' "$id"
+ case "$line" in *'"text":"crash"'*) exit 1 ;; esac
+ printf '%s\n' '{"method":"item/agentMessage/delta","params":{"threadId":"new","turnId":"active","itemId":"i1","delta":"第一段"}}' '{"method":"item/agentMessage/delta","params":{"threadId":"new","turnId":"active","itemId":"i2","delta":"第二段"}}' '{"method":"item/agentMessage/delta","params":{"threadId":"new","turnId":"active","itemId":"i1","delta":"结束"}}' '{"method":"turn/completed","params":{"threadId":"new","turn":{"id":"active","status":"completed"}}}'
+ continue ;;
+ *) result='{}' ;;
+ esac
+ printf '{"id":%s,"result":%s}\n' "$id" "$result"
+done
+"#;
+ std::fs::write(
+ &path,
+ script
+ .replace("VERSION", version)
+ .replace("RECORD", directory.join("methods").to_str().unwrap()),
+ )
+ .unwrap();
+ std::fs::set_permissions(&path, std::fs::Permissions::from_mode(0o700)).unwrap();
+ path
+}
+
+#[tokio::test]
+async fn native_versions_share_index_and_coalesce_history_without_losing_text() {
+ for version in ["0.147.0", "0.153.4"] {
+ let directory = tempfile::tempdir().unwrap();
+ let native = Codex::new(Some(fixture(directory.path(), version)));
+ native.warm().await.unwrap();
+ assert_eq!(native.status().state, "ready");
+ assert_eq!(native.status().version.as_deref(), Some(version));
+ let display = json!({"mode":"labels","session_ids":["s"],"bindings":[{"session_id":"s","project_id":"project"}]});
+ for _ in 0..4 {
+ native
+ .call("codex.session.display", display.clone())
+ .await
+ .unwrap();
+ }
+ let pages = futures_util::future::join_all(
+ (0..8).map(|_| native.call("codex.session.history", json!({"session_id":"s"}))),
+ )
+ .await;
+ for page in pages {
+ let page = page.unwrap();
+ assert_eq!(
+ page["turns"][0]["items"][1]["text"],
+ "**回复** https://example.org/a [local path]"
+ );
+ }
+ let methods = std::fs::read_to_string(directory.path().join("methods")).unwrap();
+ assert_eq!(methods.lines().filter(|line| *line == "list").count(), 2);
+ assert_eq!(
+ methods
+ .lines()
+ .filter(|line| *line
+ == if version == "0.147.0" {
+ "legacy"
+ } else {
+ "pages"
+ })
+ .count(),
+ 1
+ );
+ native.shutdown().await;
+ }
+}
+
+#[tokio::test]
+async fn loaded_empty_thread_can_send_and_interleaved_items_keep_offsets_before_terminal() {
+ let directory = tempfile::tempdir().unwrap();
+ let native = Codex::new(Some(fixture(directory.path(), "0.153.4")));
+ let params = json!({"cwd":"/tmp/project","mode":"inspect","session_id":"new"});
+ native
+ .call("codex.session.start", params.clone())
+ .await
+ .unwrap();
+ native
+ .call("codex.session.resume", params.clone())
+ .await
+ .unwrap();
+ let empty = native
+ .call("codex.session.history", json!({"session_id":"new"}))
+ .await
+ .unwrap();
+ assert_eq!(empty["turns"], json!([]));
+ let events = Arc::new(std::sync::Mutex::new(Vec::new()));
+ native
+ .turn("new", "hello", "op", |kind, data| {
+ events.lock().unwrap().push((kind, data));
+ async { Ok(()) }
+ })
+ .await
+ .unwrap();
+ let events = events.lock().unwrap().clone();
+ assert_eq!(events.first().unwrap().0, "codex.turn.started");
+ assert_eq!(events.last().unwrap().0, "codex.turn.completed");
+ let item: Vec<_> = events
+ .iter()
+ .filter(|(kind, data)| *kind == "codex.message.delta" && data["item_id"] == "i1")
+ .map(|(_, data)| {
+ (
+ data["text_offset"].as_u64().unwrap(),
+ data["delta"].as_str().unwrap(),
+ )
+ })
+ .collect();
+ assert_eq!(item, vec![(0, "第一段"), (3, "结束")]);
+ drop(events);
+ assert!(
+ native
+ .call(
+ "codex.session.resume",
+ json!({"cwd":"/unapproved","mode":"edit","session_id":"new"})
+ )
+ .await
+ .is_err()
+ );
+ let error = native
+ .turn("new", "crash", "op-2", |_, _| async { Ok(()) })
+ .await
+ .unwrap_err();
+ assert!(error.downcast_ref::().is_some());
+ assert_eq!(
+ std::fs::read_to_string(directory.path().join("methods"))
+ .unwrap()
+ .lines()
+ .filter(|line| *line == "turn")
+ .count(),
+ 2
+ );
+ native.shutdown().await;
+}
diff --git a/farhelm-agent/src/codex/transport.rs b/farhelm-agent/src/codex/transport.rs
new file mode 100644
index 0000000..fc2a699
--- /dev/null
+++ b/farhelm-agent/src/codex/transport.rs
@@ -0,0 +1,428 @@
+//! The official app-server JSONL protocol, over a single private stdio process.
+use anyhow::{Context, Result, bail, ensure};
+use serde_json::{Value, json};
+use std::{
+ collections::HashMap,
+ path::{Path, PathBuf},
+ process::Stdio,
+ sync::{
+ Arc, Mutex,
+ atomic::{AtomicBool, AtomicU64, Ordering},
+ },
+ time::Duration,
+};
+use tokio::{
+ io::{AsyncBufReadExt, AsyncReadExt, AsyncWriteExt, BufReader},
+ process::{Child, ChildStdin, Command},
+ sync::{Mutex as AsyncMutex, broadcast, oneshot},
+};
+
+const MAX_FRAME: u64 = 8 * 1024 * 1024;
+type Reply = std::result::Result;
+type Pending = Arc>>>;
+
+pub struct Connection {
+ stdin: Arc>,
+ pending: Pending,
+ next_id: AtomicU64,
+ pub alive: Arc,
+ pub events: broadcast::Sender,
+ child: AsyncMutex,
+ reader: tokio::task::AbortHandle,
+}
+
+impl Connection {
+ pub async fn open(bin: &Path) -> Result> {
+ let mut command = command(bin)?;
+ let mut child = command
+ .args(["app-server", "--listen", "stdio://"])
+ .stdin(Stdio::piped())
+ .stdout(Stdio::piped())
+ .stderr(Stdio::null())
+ .kill_on_drop(true)
+ .spawn()
+ .context("codex_start_failed")?;
+ let stdin = Arc::new(AsyncMutex::new(
+ child.stdin.take().context("codex_stdin_missing")?,
+ ));
+ let stdout = child.stdout.take().context("codex_stdout_missing")?;
+ let pending: Pending = Arc::default();
+ let alive = Arc::new(AtomicBool::new(true));
+ let (events, _) = broadcast::channel(4096);
+ let (input, replies, healthy, notices) = (
+ stdin.clone(),
+ pending.clone(),
+ alive.clone(),
+ events.clone(),
+ );
+ let reader = tokio::spawn(async move {
+ let mut reader = BufReader::new(stdout);
+ loop {
+ let mut line = Vec::new();
+ match (&mut reader)
+ .take(MAX_FRAME + 1)
+ .read_until(b'\n', &mut line)
+ .await
+ {
+ Ok(0) | Err(_) => break,
+ Ok(_) if line.len() as u64 > MAX_FRAME => break,
+ Ok(_) => {}
+ }
+ let Ok(value) = serde_json::from_slice::(&line) else {
+ break;
+ };
+ if let Some(method) = value["method"].as_str() {
+ if !value["id"].is_null() {
+ // FarHelm does not offer an approval UI. Never silently grant
+ // elevated permissions or let an unanswered approval hang a turn.
+ let result = match method {
+ "item/commandExecution/requestApproval"
+ | "item/fileChange/requestApproval" => {
+ Some(json!({"decision":"decline"}))
+ }
+ "item/permissions/requestApproval" => {
+ Some(json!({"permissions":{},"scope":"turn"}))
+ }
+ "item/tool/requestUserInput" => Some(json!({"answers":{}})),
+ _ => None,
+ };
+ let response = match result {
+ Some(result) => json!({"id":value["id"],"result":result}),
+ None => {
+ json!({"id":value["id"],"error":{"code":-32601,"message":"Unsupported FarHelm client request"}})
+ }
+ };
+ if !matches!(
+ tokio::time::timeout(
+ Duration::from_secs(5),
+ write_json(&input, &response)
+ )
+ .await,
+ Ok(Ok(()))
+ ) {
+ break;
+ }
+ } else {
+ let _ = notices.send(value);
+ }
+ } else if let Some(id) = value["id"].as_u64() {
+ let sender = replies
+ .lock()
+ .expect("Codex reply map poisoned")
+ .remove(&id);
+ if let Some(sender) = sender {
+ let result = if value.get("error").is_some() {
+ // Vendor error text may contain prompts, paths or credentials.
+ Err(format!("codex_request_rejected:{}", value["error"]["code"]))
+ } else {
+ Ok(value["result"].clone())
+ };
+ let _ = sender.send(result);
+ }
+ } else {
+ break;
+ }
+ }
+ healthy.store(false, Ordering::Release);
+ for (_, sender) in
+ std::mem::take(&mut *replies.lock().expect("Codex reply map poisoned"))
+ {
+ let _ = sender.send(Err("codex_connection_closed".to_owned()));
+ }
+ let _ = notices.send(json!({"method":"farhelm/disconnected"}));
+ });
+ let connection = Arc::new(Self {
+ stdin,
+ pending,
+ next_id: AtomicU64::new(1),
+ alive,
+ events,
+ child: AsyncMutex::new(child),
+ reader: reader.abort_handle(),
+ });
+ let initialized = connection
+ .request(
+ "initialize",
+ serde_json::to_value(super::protocol::InitializeParams {
+ client_info: super::protocol::ClientInfo {
+ name: "farhelm",
+ title: "FarHelm",
+ version: farhelm_core::PRODUCT_VERSION,
+ },
+ capabilities: super::protocol::Capabilities {
+ experimental_api: true,
+ },
+ })?,
+ )
+ .await;
+ if let Err(error) = initialized {
+ connection.shutdown().await;
+ return Err(error);
+ }
+ write_json(&connection.stdin, &json!({"method":"initialized"})).await?;
+ Ok(connection)
+ }
+
+ pub async fn request(&self, method: &str, params: Value) -> Result {
+ ensure!(
+ self.alive.load(Ordering::Acquire),
+ "codex_connection_closed"
+ );
+ let id = self.next_id.fetch_add(1, Ordering::Relaxed);
+ let (sender, receiver) = oneshot::channel();
+ {
+ let mut pending = self.pending.lock().expect("Codex reply map poisoned");
+ ensure!(pending.len() < 64, "codex_request_capacity");
+ pending.insert(id, sender);
+ }
+ let _guard = PendingGuard {
+ pending: self.pending.clone(),
+ id,
+ };
+ let result = tokio::time::timeout(Duration::from_secs(18), async {
+ write_json(
+ &self.stdin,
+ &json!({"id":id,"method":method,"params":params}),
+ )
+ .await?;
+ receiver
+ .await
+ .context("codex_connection_closed")?
+ .map_err(anyhow::Error::msg)
+ })
+ .await
+ .context("codex_request_timeout")?;
+ result.map_err(|error| anyhow::anyhow!("{method}: {error}"))
+ }
+
+ pub async fn shutdown(&self) {
+ self.alive.store(false, Ordering::Release);
+ let mut child = self.child.lock().await;
+ let _ = child.start_kill();
+ let _ = child.wait().await;
+ self.reader.abort();
+ }
+}
+
+impl Drop for Connection {
+ fn drop(&mut self) {
+ self.reader.abort();
+ }
+}
+struct PendingGuard {
+ pending: Pending,
+ id: u64,
+}
+impl Drop for PendingGuard {
+ fn drop(&mut self) {
+ if let Ok(mut map) = self.pending.lock() {
+ map.remove(&self.id);
+ }
+ }
+}
+
+async fn write_json(stdin: &AsyncMutex, value: &Value) -> Result<()> {
+ let mut bytes = serde_json::to_vec(value)?;
+ ensure!(bytes.len() as u64 <= MAX_FRAME, "codex_request_too_large");
+ bytes.push(b'\n');
+ stdin
+ .lock()
+ .await
+ .write_all(&bytes)
+ .await
+ .context("codex_write_failed")
+}
+
+pub fn command(bin: &Path) -> Result {
+ let mut command = Command::new(bin);
+ for key in [
+ "FARHELM_AGENT_TOKEN",
+ "FARHELM_HUB_TOKEN",
+ "FARHELM_PAIRING_CODE",
+ "FARHELM_ADMIN_PASSWORD",
+ ] {
+ command.env_remove(key);
+ }
+ let mut paths = vec![bin.parent().context("codex_bin_parent_missing")?.to_owned()];
+ if let Some(path) = std::env::var_os("PATH") {
+ paths.extend(std::env::split_paths(&path));
+ }
+ command.env("PATH", std::env::join_paths(paths)?);
+ Ok(command)
+}
+
+pub async fn version(bin: &Path) -> Result {
+ let output = tokio::time::timeout(
+ Duration::from_secs(5),
+ command(bin)?.arg("--version").kill_on_drop(true).output(),
+ )
+ .await
+ .context("codex_version_timeout")??;
+ ensure!(output.status.success(), "codex_version_failed");
+ let text = std::str::from_utf8(&output.stdout)?;
+ let version = text
+ .trim()
+ .strip_prefix("codex-cli ")
+ .context("codex_version_unrecognized")?;
+ let parsed = semver::Version::parse(version).context("codex_version_unrecognized")?;
+ ensure!(
+ parsed >= semver::Version::new(0, 147, 0),
+ "codex_upgrade_required"
+ );
+ Ok(version.to_owned())
+}
+
+pub fn discover(explicit: Option<&Path>) -> Result {
+ use std::os::unix::fs::PermissionsExt;
+ let valid = |path: &Path| {
+ path.is_absolute()
+ && std::fs::metadata(path)
+ .is_ok_and(|m| m.is_file() && m.permissions().mode() & 0o111 != 0)
+ };
+ if let Some(path) = explicit {
+ ensure!(valid(path), "codex_configured_binary_unavailable");
+ return Ok(path.to_owned());
+ }
+ let mut candidates = Vec::new();
+ if let Some(path) = std::env::var_os("PATH") {
+ candidates.extend(std::env::split_paths(&path).map(|p| p.join("codex")));
+ }
+ if let Some(home) = std::env::var_os("HOME") {
+ let home = PathBuf::from(home);
+ candidates.push(home.join(".local/bin/codex"));
+ candidates.push(home.join(".npm-global/bin/codex"));
+ if let Ok(entries) = std::fs::read_dir(home.join(".nvm/versions/node")) {
+ candidates.extend(entries.flatten().map(|e| e.path().join("bin/codex")));
+ }
+ }
+ let mut unique = HashMap::new();
+ for path in candidates {
+ if valid(&path) {
+ unique.entry(std::fs::canonicalize(&path)?).or_insert(path);
+ }
+ }
+ match unique.len() {
+ 0 => bail!(
+ "codex_not_configured: install and log in to Codex, then run farhelm-agent codex configure --bin PATH"
+ ),
+ 1 => Ok(unique.into_values().next().expect("one binary")),
+ _ => bail!(
+ "codex_multiple_binaries: select one with farhelm-agent codex configure --bin PATH"
+ ),
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+ use std::os::unix::fs::PermissionsExt;
+ fn executable(directory: &Path, name: &str, script: &str) -> PathBuf {
+ let path = directory.join(name);
+ std::fs::write(&path, script).unwrap();
+ std::fs::set_permissions(&path, std::fs::Permissions::from_mode(0o700)).unwrap();
+ path
+ }
+ #[tokio::test]
+ async fn one_process_multiplexes_notifications_reordered_replies_and_cancelled_reads() {
+ let directory = tempfile::tempdir().unwrap();
+ let bin = executable(
+ directory.path(),
+ "codex",
+ r#"#!/bin/sh
+read -r line
+printf '%s\n' '{"id":1,"result":{"userAgent":"fixture"}}'
+read -r line
+while IFS= read -r line; do
+ id=$(printf '%s' "$line" | sed -n 's/.*"id":\([0-9]*\).*/\1/p')
+ case "$line" in
+ *'"method":"left"'*) left=$id ;;
+ *'"method":"right"'*)
+ printf '%s\n' '{"method":"thread/status/changed","params":{"threadId":"fixture","status":{"type":"idle"}}}'
+ printf '{"id":%s,"result":{"side":"right"}}\n' "$id"
+ printf '{"id":%s,"result":{"side":"left"}}\n' "$left" ;;
+ *'"method":"cancel"'*) ;;
+ *'"method":"approval"'*)
+ printf '%s\n' '{"id":900,"method":"item/commandExecution/requestApproval","params":{"command":"untrusted"}}'
+ IFS= read -r reply
+ case "$reply" in *'"decision":"decline"'*) printf '{"id":%s,"result":{"declined":true}}\n' "$id" ;; *) exit 1 ;; esac ;;
+ *) printf '{"id":%s,"result":{}}\n' "$id" ;;
+ esac
+done
+"#,
+ );
+ let connection = Connection::open(&bin).await.unwrap();
+ let mut events = connection.events.subscribe();
+ let (left, right) = tokio::join!(
+ connection.request("left", json!({})),
+ connection.request("right", json!({}))
+ );
+ assert_eq!(left.unwrap()["side"], "left");
+ assert_eq!(right.unwrap()["side"], "right");
+ assert_eq!(
+ events.recv().await.unwrap()["method"],
+ "thread/status/changed"
+ );
+ assert!(
+ tokio::time::timeout(
+ Duration::from_millis(10),
+ connection.request("cancel", json!({}))
+ )
+ .await
+ .is_err()
+ );
+ assert!(connection.pending.lock().unwrap().is_empty());
+ assert_eq!(
+ connection.request("approval", json!({})).await.unwrap()["declined"],
+ true
+ );
+ connection.shutdown().await;
+ assert!(!connection.alive.load(Ordering::Acquire));
+ }
+ #[tokio::test]
+ async fn malformed_frames_fail_waiters_and_binary_parent_resolves_npm_interpreters() {
+ let directory = tempfile::tempdir().unwrap();
+ let bin = executable(
+ directory.path(),
+ "codex",
+ "#!/usr/bin/env farhelm-fixture-node\n",
+ );
+ let interpreter = executable(
+ directory.path(),
+ "farhelm-fixture-node",
+ "#!/bin/sh\nprintf 'codex-cli 0.153.4\\n'\n",
+ );
+ assert_eq!(version(&bin).await.unwrap(), "0.153.4");
+ std::fs::write(&interpreter, "#!/bin/sh\nprintf 'codex-cli 0.147.0\\n'\n").unwrap();
+ assert_eq!(version(&bin).await.unwrap(), "0.147.0");
+ let malformed = executable(
+ directory.path(),
+ "malformed",
+ "#!/bin/sh\nread -r line\nprintf '%s\\n' '{\"id\":1,\"result\":{}}'\nread -r line\nread -r line\nprintf 'broken-json\\n'\n",
+ );
+ let connection = Connection::open(&malformed).await.unwrap();
+ assert!(
+ connection
+ .request("read", json!({}))
+ .await
+ .unwrap_err()
+ .to_string()
+ .contains("codex_connection_closed")
+ );
+ assert!(!connection.alive.load(Ordering::Acquire));
+ connection.shutdown().await;
+ }
+ #[tokio::test]
+ async fn oversized_native_frame_closes_connection_without_retaining_waiters() {
+ let directory = tempfile::tempdir().unwrap();
+ let bin = executable(
+ directory.path(),
+ "oversized",
+ "#!/bin/sh\nread -r line\nprintf '%s\\n' '{\"id\":1,\"result\":{}}'\nread -r line\nread -r line\nhead -c 8388609 /dev/zero | tr '\\000' 'x'\n",
+ );
+ let connection = Connection::open(&bin).await.unwrap();
+ assert!(connection.request("read", json!({})).await.is_err());
+ assert!(!connection.alive.load(Ordering::Acquire));
+ assert!(connection.pending.lock().unwrap().is_empty());
+ connection.shutdown().await;
+ }
+}
diff --git a/farhelm-agent/src/command_store.rs b/farhelm-agent/src/command_store.rs
index a99acec..94aaaaf 100644
--- a/farhelm-agent/src/command_store.rs
+++ b/farhelm-agent/src/command_store.rs
@@ -1,6 +1,6 @@
use std::{
path::Path,
- sync::{Mutex, MutexGuard},
+ sync::{Arc, Mutex, MutexGuard},
time::Duration,
};
@@ -20,11 +20,20 @@ pub struct PendingCommand {
pub reported: bool,
}
+#[derive(Clone)]
pub struct CommandStore {
- connection: Mutex,
+ connection: Arc>,
}
impl CommandStore {
+ pub async fn background(
+ &self,
+ task: impl FnOnce(&Self) -> Result + Send + 'static,
+ ) -> Result {
+ let store = self.clone();
+ crate::runtime_tasks::blocking(move || task(&store)).await
+ }
+
pub fn open(path: &Path) -> Result {
if path != Path::new(":memory:") {
let parent = path
@@ -46,7 +55,7 @@ impl CommandStore {
}
crate::migrations::apply(&connection)?;
Ok(Self {
- connection: Mutex::new(connection),
+ connection: Arc::new(Mutex::new(connection)),
})
}
diff --git a/farhelm-agent/src/config.rs b/farhelm-agent/src/config.rs
index 866819a..e30ccfe 100644
--- a/farhelm-agent/src/config.rs
+++ b/farhelm-agent/src/config.rs
@@ -15,7 +15,6 @@ pub struct AgentPaths {
pub config: PathBuf,
pub data: PathBuf,
pub database: PathBuf,
- pub worker: PathBuf,
pub unit: PathBuf,
pub legacy_root: PathBuf,
}
@@ -23,9 +22,11 @@ pub struct AgentPaths {
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AgentFileConfig {
pub agent: AgentSection,
- #[serde(default)]
+ #[serde(default, skip_serializing_if = "WorkerSection::is_default")]
pub worker: WorkerSection,
#[serde(default)]
+ pub codex: CodexSection,
+ #[serde(default)]
pub projects: BTreeMap,
}
@@ -52,12 +53,25 @@ pub struct AgentSection {
pub database: PathBuf,
}
+#[derive(Debug, Clone, Default, Serialize, Deserialize)]
+pub struct CodexSection {
+ #[serde(default, skip_serializing_if = "Option::is_none")]
+ pub bin: Option,
+}
+
+/// Retained only to preserve V0.7.1 rollback configuration. Never executed.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct WorkerSection {
#[serde(default = "default_python")]
pub python: String,
}
+impl WorkerSection {
+ fn is_default(&self) -> bool {
+ self.python == default_python()
+ }
+}
+
impl Default for WorkerSection {
fn default() -> Self {
Self {
@@ -101,10 +115,6 @@ impl AgentPaths {
previous: bin_home.join("farhelm-agent.previous"),
config: config_home.join("farhelm/agent.toml"),
database: data.join("state/agent.db"),
- worker: data.join(format!(
- "runtime/codex-worker/{}",
- farhelm_core::PRODUCT_VERSION
- )),
unit: config_home.join("systemd/user/farhelm-agent.service"),
legacy_root,
data,
@@ -159,6 +169,7 @@ impl AgentFileConfig {
database,
},
worker: WorkerSection::default(),
+ codex: CodexSection::default(),
projects: BTreeMap::new(),
};
config.validate()?;
@@ -192,6 +203,7 @@ impl AgentFileConfig {
database,
},
worker: WorkerSection::default(),
+ codex: CodexSection::default(),
projects: BTreeMap::new(),
};
config.validate()?;
@@ -223,7 +235,9 @@ impl AgentFileConfig {
!self.agent.database.as_os_str().is_empty(),
"agent.database is empty"
);
- ensure!(!self.worker.python.is_empty(), "worker.python is empty");
+ if let Some(bin) = &self.codex.bin {
+ ensure!(bin.is_absolute(), "codex.bin must be absolute");
+ }
for (project_id, project) in &self.projects {
ensure!(
!project_id.is_empty()
@@ -353,7 +367,6 @@ mod tests {
config: PathBuf::from("/tmp/config/farhelm/agent.toml"),
data: PathBuf::from("/tmp/data/farhelm"),
database: PathBuf::from("/tmp/data/farhelm/state/agent.db"),
- worker: PathBuf::from("/tmp/data/farhelm/runtime/codex-worker/0.7.1"),
unit: PathBuf::from("/tmp/config/systemd/user/farhelm-agent.service"),
legacy_root: PathBuf::from("/tmp/data/farhelm-agent"),
};
@@ -368,6 +381,7 @@ mod tests {
database: paths.database.clone(),
},
worker: WorkerSection::default(),
+ codex: CodexSection::default(),
projects: BTreeMap::new(),
};
let decoded: AgentFileConfig = toml::from_str(&config.encode().unwrap()).unwrap();
diff --git a/farhelm-agent/src/experiment_store.rs b/farhelm-agent/src/experiment_store.rs
index 90cba27..ce37730 100644
--- a/farhelm-agent/src/experiment_store.rs
+++ b/farhelm-agent/src/experiment_store.rs
@@ -3,7 +3,7 @@ use std::{
fs,
os::unix::fs::MetadataExt,
path::{Component, Path, PathBuf},
- sync::{Mutex, MutexGuard},
+ sync::{Arc, Mutex, MutexGuard},
time::Duration,
};
@@ -127,12 +127,25 @@ pub struct ProjectCandidate {
pub updated_at_unix: u64,
}
+#[derive(Clone)]
pub struct ExperimentStore {
- connection: Mutex,
+ connection: Arc>,
path: PathBuf,
}
impl ExperimentStore {
+ pub async fn open_async(path: &Path) -> Result {
+ let path = path.to_owned();
+ crate::runtime_tasks::blocking(move || Self::open(&path)).await
+ }
+ pub async fn background(
+ &self,
+ work: impl FnOnce(&Self) -> Result + Send + 'static,
+ ) -> Result {
+ let store = self.clone();
+ crate::runtime_tasks::blocking(move || work(&store)).await
+ }
+
pub fn open(path: &Path) -> Result {
if path != Path::new(":memory:") {
let parent = path
@@ -150,7 +163,7 @@ impl ExperimentStore {
}
crate::migrations::apply(&connection)?;
Ok(Self {
- connection: Mutex::new(connection),
+ connection: Arc::new(Mutex::new(connection)),
path: path.to_owned(),
})
}
@@ -860,6 +873,8 @@ impl ExperimentStore {
[&command.command_id], |row| Ok((row.get::<_,String>(0)?,row_u64(row,1)?,row.get::<_,String>(2)?)),
).optional()? {
ensure!(action == action_name(command.action) && expires == command.expires_at_unix && encoded == serde_json::to_string(payload)?, "duplicate command identity mismatch");
+ connection.execute("UPDATE remote_codex_commands SET terminal_reported=0 WHERE command_id=?1 AND state IN ('completed','failed','expired')", [&command.command_id])?;
+ connection.commit()?;
return Ok(());
}
connection.execute(
@@ -1034,6 +1049,34 @@ impl ExperimentStore {
.map_err(Into::into)
}
+ /// Retried deliveries return the actual persisted outcome, even after TTL.
+ pub fn remote_receipt(&self, command_id: &str) -> Result {
+ self.lock()?
+ .query_row(
+ "SELECT state,data_json,detail FROM remote_codex_commands WHERE command_id=?1",
+ [command_id],
+ |row| {
+ let state: String = row.get(0)?;
+ let data: Option = row.get(1)?;
+ Ok(RemoteCommandReport {
+ command_id: command_id.to_owned(),
+ state: match state.as_str() {
+ "completed" => farhelm_protocol::CommandState::Completed,
+ "failed" => farhelm_protocol::CommandState::Failed,
+ "expired" => farhelm_protocol::CommandState::Expired,
+ _ => farhelm_protocol::CommandState::Accepted,
+ },
+ data: data
+ .map(|value| serde_json::from_str(&value))
+ .transpose()
+ .map_err(json_conversion(1))?,
+ detail: row.get(2)?,
+ })
+ },
+ )
+ .map_err(Into::into)
+ }
+
pub fn mark_remote_terminal_reported(&self, command_id: &str, now: u64) -> Result<()> {
self.lock()?.execute(
"UPDATE remote_codex_commands SET terminal_reported=1,updated_at_unix=?1
@@ -1653,6 +1696,24 @@ mod tests {
.mark_remote_terminal_reported("cmd_terminal", 14)
.unwrap();
assert!(reopened.pending_remote_reports().unwrap().is_empty());
+ reopened
+ .receive_remote_command(
+ &AgentCommand {
+ protocol: FARHELM_PROTOCOL.into(),
+ command_id: "cmd_terminal".into(),
+ agent_id: "agent-a".into(),
+ action: CommandAction::CodexTurnStart,
+ created_at_unix: 10,
+ expires_at_unix: 100,
+ payload: Some(json!({"project_id":"p","session_id":"s","prompt":"go"})),
+ },
+ 200,
+ )
+ .unwrap();
+ let receipt = reopened.remote_receipt("cmd_terminal").unwrap();
+ assert_eq!(receipt.state, farhelm_protocol::CommandState::Completed);
+ assert_eq!(receipt.data.unwrap()["turn_id"], "t");
+ assert!(!reopened.claim_remote_command("cmd_terminal", 200).unwrap());
}
#[test]
diff --git a/farhelm-agent/src/live.rs b/farhelm-agent/src/live.rs
new file mode 100644
index 0000000..115f3fe
--- /dev/null
+++ b/farhelm-agent/src/live.rs
@@ -0,0 +1,232 @@
+//! One reconnecting outbound connection; commands are persisted by the receiver.
+use anyhow::{Context, Result, ensure};
+use farhelm_protocol::{
+ AgentCommand, AgentHeartbeat, AgentReadRequest,
+ live::{AgentFrame, CodexReadiness, HubFrame, LIVE_FRAME_BYTES, LIVE_PROTOCOL},
+};
+use futures_util::{SinkExt, StreamExt};
+use serde_json::Value;
+use std::{
+ collections::HashMap,
+ sync::{
+ Arc, Mutex,
+ atomic::{AtomicU64, Ordering},
+ },
+ time::Duration,
+};
+use tokio::sync::{mpsc, oneshot, watch};
+use tokio_tungstenite::tungstenite::{
+ Message, client::IntoClientRequest, protocol::WebSocketConfig,
+};
+
+type Waiters = Arc>>>>;
+#[derive(Clone)]
+pub struct Link {
+ outgoing: Arc>>>,
+ pending: Waiters,
+ next: Arc,
+ pub connected: watch::Sender,
+ reconnect: Arc,
+}
+impl Default for Link {
+ fn default() -> Self {
+ Self {
+ outgoing: Arc::default(),
+ pending: Arc::default(),
+ next: Arc::new(AtomicU64::new(1)),
+ connected: watch::channel(false).0,
+ reconnect: Arc::new(tokio::sync::Notify::new()),
+ }
+ }
+}
+impl Link {
+ pub fn reset(&self) {
+ self.reconnect.notify_one();
+ }
+ pub async fn request(&self, frame: impl FnOnce(u64) -> AgentFrame) -> Result {
+ let sender = self
+ .outgoing
+ .lock()
+ .expect("live sender poisoned")
+ .clone()
+ .context("hub_disconnected")?;
+ let id = self.next.fetch_add(1, Ordering::Relaxed);
+ let (reply, receiver) = oneshot::channel();
+ {
+ let mut pending = self.pending.lock().expect("live replies poisoned");
+ ensure!(pending.len() < 64, "live_capacity");
+ pending.insert(id, reply);
+ }
+ let _guard = WaiterGuard {
+ waiters: self.pending.clone(),
+ id,
+ };
+ tokio::time::timeout(Duration::from_secs(18), async {
+ sender.send(frame(id)).await.context("hub_disconnected")?;
+ receiver
+ .await
+ .context("hub_disconnected")?
+ .map_err(anyhow::Error::msg)
+ })
+ .await
+ .context("hub_reply_timeout")?
+ }
+ pub fn delta(&self, event: farhelm_protocol::AgentEvent) {
+ // Deltas can be reconstructed from history. Never persist transcript text.
+ if let Some(sender) = self.outgoing.lock().expect("live sender poisoned").as_ref()
+ && sender.try_send(AgentFrame::Delta { event }).is_err()
+ {
+ // Reconnection prompts an authoritative history reconciliation.
+ // Native execution and durable receipts remain independent.
+ self.reset();
+ }
+ }
+ fn disconnect(&self) {
+ self.outgoing.lock().expect("live sender poisoned").take();
+ self.connected.send_replace(false);
+ for (_, reply) in std::mem::take(&mut *self.pending.lock().expect("live replies poisoned"))
+ {
+ let _ = reply.send(Err("hub_disconnected".into()));
+ }
+ }
+}
+struct WaiterGuard {
+ waiters: Waiters,
+ id: u64,
+}
+impl Drop for WaiterGuard {
+ fn drop(&mut self) {
+ if let Ok(mut waiters) = self.waiters.lock() {
+ waiters.remove(&self.id);
+ }
+ }
+}
+
+pub async fn run(
+ hub: crate::HubArgs,
+ heartbeat: AgentHeartbeat,
+ codex: crate::codex::Codex,
+ commands: mpsc::Sender,
+ reads: mpsc::Sender<(AgentReadRequest, u64)>,
+ wake: Arc,
+) {
+ let mut delay = 1u64;
+ loop {
+ let started = std::time::Instant::now();
+ let result = connection(&hub, &heartbeat, &codex, &commands, &reads, &wake).await;
+ hub.link.disconnect();
+ if result.is_err() {
+ tracing::warn!("Hub live connection unavailable; queued local work is retained");
+ }
+ if started.elapsed() > Duration::from_secs(30) {
+ delay = 1;
+ }
+ tokio::time::sleep(Duration::from_millis(
+ delay * 1000 + rand::random_range(0..500),
+ ))
+ .await;
+ delay = (delay * 2).min(30);
+ }
+}
+
+async fn connection(
+ hub: &crate::HubArgs,
+ heartbeat: &AgentHeartbeat,
+ codex: &crate::codex::Codex,
+ commands: &mpsc::Sender,
+ reads: &mpsc::Sender<(AgentReadRequest, u64)>,
+ wake: &tokio::sync::Notify,
+) -> Result<()> {
+ let mut url = crate::hub_endpoint(&hub.hub, "/api/v1/agent/connect")?;
+ let scheme = if url.scheme() == "https" { "wss" } else { "ws" };
+ url.set_scheme(scheme)
+ .map_err(|_| anyhow::anyhow!("invalid_hub_scheme"))?;
+ let mut request = url.as_str().into_client_request()?;
+ request
+ .headers_mut()
+ .insert("authorization", format!("Bearer {}", hub.token).parse()?);
+ request
+ .headers_mut()
+ .insert("sec-websocket-protocol", LIVE_PROTOCOL.parse()?);
+ let configuration = WebSocketConfig::default()
+ .max_message_size(Some(LIVE_FRAME_BYTES))
+ .max_frame_size(Some(LIVE_FRAME_BYTES));
+ let (socket, response) = tokio::time::timeout(
+ Duration::from_secs(15),
+ tokio_tungstenite::connect_async_with_config(request, Some(configuration), true),
+ )
+ .await
+ .context("hub_connect_timeout")??;
+ ensure!(
+ response
+ .headers()
+ .get("sec-websocket-protocol")
+ .is_some_and(|p| p == LIVE_PROTOCOL),
+ "hub_upgrade_required"
+ );
+ let (mut sender, mut receiver) = socket.split();
+ let (outgoing, mut queued) = mpsc::channel::(128);
+ let mut status = codex.subscribe_status();
+ let heartbeat_frame = |status: crate::codex::CodexStatus| AgentFrame::Heartbeat {
+ request_id: 0,
+ heartbeat: heartbeat.clone(),
+ codex: CodexReadiness {
+ state: status.state,
+ version: status.version,
+ reason: status.reason,
+ },
+ };
+ tokio::time::timeout(
+ Duration::from_secs(5),
+ sender.send(Message::Text(
+ serde_json::to_string(&heartbeat_frame(codex.status()))?.into(),
+ )),
+ )
+ .await??;
+ // Do not expose the sender until Hub has acknowledged the initial heartbeat.
+ let mut initialized = false;
+ let mut ticker = tokio::time::interval(Duration::from_secs(15));
+ ticker.tick().await;
+ let mut last_received = std::time::Instant::now();
+ let initialize_deadline = tokio::time::Instant::now() + Duration::from_secs(20);
+ loop {
+ tokio::select! {
+ _ = tokio::time::sleep_until(initialize_deadline), if !initialized => anyhow::bail!("hub_heartbeat_timeout"),
+ _ = hub.link.reconnect.notified() => break,
+ message=receiver.next()=>{
+ let Some(message)=message else {break};let message=message?;last_received=std::time::Instant::now();
+ match message {
+ Message::Text(text)=>match serde_json::from_str::(&text).context("invalid_hub_frame")? {
+ HubFrame::Reply {request_id,status,data}=>{
+ if request_id==0 {
+ ensure!((200..300).contains(&status),"hub_heartbeat_rejected");
+ if !initialized {initialized=true;*hub.link.outgoing.lock().expect("live sender poisoned")=Some(outgoing.clone());hub.link.connected.send_replace(true);wake.notify_one();}
+ } else if let Some(reply)=hub.link.pending.lock().expect("live replies poisoned").remove(&request_id) {
+ let _=reply.send(if (200..300).contains(&status) {Ok(data)} else {Err(format!("hub_request_rejected:{status}"))});
+ }
+ },
+ HubFrame::Command {command}=>{ensure!(initialized,"hub_heartbeat_missing");commands.try_send(command).context("command_queue_full")?;},
+ HubFrame::Read {request,expires_at_unix}=>{ensure!(initialized,"hub_heartbeat_missing");reads.try_send((request,expires_at_unix)).context("read_queue_full")?;},
+ },
+ Message::Ping(bytes)=>{tokio::time::timeout(Duration::from_secs(5), sender.send(Message::Pong(bytes))).await??;},
+ Message::Pong(_)=>{},
+ Message::Close(_)=>break,
+ _=>anyhow::bail!("unsupported_hub_frame"),
+ }
+ },
+ frame=queued.recv()=>{
+ let Some(frame)=frame else {break};let text=serde_json::to_string(&frame)?;
+ ensure!(text.len()<=LIVE_FRAME_BYTES,"live_frame_too_large");
+ tokio::time::timeout(Duration::from_secs(5),sender.send(Message::Text(text.into()))).await??;
+ },
+ _=status.changed()=>{
+ tokio::time::timeout(Duration::from_secs(5), sender.send(Message::Text(serde_json::to_string(&heartbeat_frame(codex.status()))?.into()))).await??;
+ },
+ _=ticker.tick()=>{
+ ensure!(last_received.elapsed(),
},
/// Check for or install an immutable official Agent release.
#[command(visible_alias = "upgrade")]
@@ -117,7 +112,7 @@ enum CommandKind {
/// Only report whether an update is available.
#[arg(long)]
check: bool,
- /// Install one exact formal version, such as V0.7.1.
+ /// Install one exact formal version, such as V0.8.0.
#[arg(long)]
version: Option,
/// Permit a user-approved first-number version change.
@@ -197,6 +192,13 @@ enum ExperimentCommand {
#[derive(Subcommand)]
enum CodexCommand {
+ /// Select an existing Codex installation without changing its login or settings.
+ Configure {
+ #[arg(long, env = "FARHELM_AGENT_CONFIG")]
+ config: Option,
+ #[arg(long)]
+ bin: Option,
+ },
/// List resumable Codex threads whose cwd belongs to this project.
Sessions {
#[arg(long, env = "FARHELM_AGENT_CONFIG")]
@@ -222,6 +224,8 @@ struct ConnectionArgs {
#[derive(Clone)]
struct HubArgs {
+ link: live::Link,
+ live_required: bool,
hub: String,
token: String,
agent_id: String,
@@ -234,56 +238,24 @@ struct RuntimeArgs {
command_interval: u64,
database: PathBuf,
projects: BTreeMap,
- worker_python: String,
- worker_root: PathBuf,
-}
-
-type WorkerRegistry = Arc>>;
-type WorkerResult = std::result::Result;
-
-#[derive(Clone)]
-struct ActiveWorker {
- stdin: Arc>,
- waiters: Arc>>>,
-}
-
-struct ActiveWorkerRegistration {
- registry: WorkerRegistry,
- session_id: String,
+ codex_bin: Option,
}
#[derive(Debug)]
-struct WorkerTurnOrphaned(String);
-
-impl std::fmt::Display for WorkerTurnOrphaned {
+struct CodexTurnOrphaned(String);
+impl std::fmt::Display for CodexTurnOrphaned {
fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
formatter.write_str(&self.0)
}
}
-
-impl std::error::Error for WorkerTurnOrphaned {}
-
+impl std::error::Error for CodexTurnOrphaned {}
#[derive(Clone)]
-struct WorkerRuntime {
+struct CodexRuntime {
tasks: runtime_tasks::RuntimeTasks,
- python: String,
- root: PathBuf,
- registry: WorkerRegistry,
- idle: Arc>>,
-}
-
-struct IdleWorker {
- child: Child,
- stdout: ChildStdout,
- active: ActiveWorker,
-}
-
-impl Drop for ActiveWorkerRegistration {
- fn drop(&mut self) {
- if let Ok(mut registry) = self.registry.lock() {
- registry.remove(&self.session_id);
- }
- }
+ codex: codex::Codex,
+ link: live::Link,
+ agent_id: String,
+ wake: Arc,
}
#[tokio::main]
@@ -302,22 +274,14 @@ async fn main() -> Result<()> {
command_interval,
database,
} => {
- let mut runtime = resolve_runtime(connection, interval, command_interval, database)?;
- if let Ok(paths) = AgentPaths::discover() {
- runtime.worker_python =
- resources::ensure_worker_environment(&paths.worker, &runtime.worker_python)
- .await?
- .to_string_lossy()
- .into_owned();
- }
+ let runtime = resolve_runtime(connection, interval, command_interval, database)?;
run(
runtime.hub,
runtime.interval,
runtime.command_interval,
&runtime.database,
&runtime.projects,
- &runtime.worker_python,
- &runtime.worker_root,
+ runtime.codex_bin,
)
.await
}
@@ -341,10 +305,17 @@ async fn main() -> Result<()> {
CommandKind::Status => management::status(),
CommandKind::Doctor { config } => management::doctor(config.as_deref()).await.map(|_| ()),
CommandKind::Pair => management::pair().await,
- CommandKind::WorkerSmoke {
- python,
- worker_root,
- } => worker_smoke(&python, &worker_root).await,
+ CommandKind::CodexSmoke { bin } => {
+ let codex = codex::Codex::new(bin);
+ codex.warm().await?;
+ println!(
+ "Codex handshake ok: {} ({})",
+ codex.status().version.as_deref().unwrap_or("unknown"),
+ codex.status().state
+ );
+ codex.shutdown().await;
+ Ok(())
+ }
CommandKind::Update {
check,
version,
@@ -389,15 +360,11 @@ fn resolve_runtime(
.as_ref()
.and_then(|value| value.agent.hostname.clone())
});
- let worker_python = config
- .as_ref()
- .map_or_else(|| "python3".to_owned(), |value| value.worker.python.clone());
- let worker_root = paths.map_or_else(
- || PathBuf::from("farhelm-worker-codex"),
- |value| value.worker,
- );
+ let codex_bin = config.as_ref().and_then(|value| value.codex.bin.clone());
Ok(RuntimeArgs {
hub: HubArgs {
+ link: live::Link::default(),
+ live_required: false,
hub,
token,
agent_id,
@@ -417,19 +384,17 @@ fn resolve_runtime(
.or_else(|| config.as_ref().map(|value| value.agent.database.clone()))
.unwrap_or_else(|| PathBuf::from("farhelm-agent.db")),
projects: config.map(|value| value.projects).unwrap_or_default(),
- worker_python,
- worker_root,
+ codex_bin,
})
}
async fn run(
- hub: HubArgs,
+ mut hub: HubArgs,
interval_secs: u64,
command_interval_secs: u64,
database: &Path,
projects: &BTreeMap,
- worker_python: &str,
- worker_root: &Path,
+ codex_bin: Option,
) -> Result<()> {
ensure!(
interval_secs >= 5,
@@ -439,16 +404,29 @@ async fn run(
command_interval_secs >= 1,
"command poll interval must be at least 1 second"
);
- let (client, endpoint, heartbeat) = heartbeat_client(&hub)?;
+ hub.live_required = true;
+ let (client, _, heartbeat) = heartbeat_client(&hub)?;
let command_store = CommandStore::open(database)?;
let experiment_store = ExperimentStore::open(database)?;
experiment_store.import_config_projects(projects, unix_time())?;
- let worker_runtime = WorkerRuntime {
+ // Explicitly configured approvals remain visible even before Codex is ready.
+ for (id, project) in experiment_store.approved_projects()? {
+ let name = project
+ .path
+ .file_name()
+ .and_then(|s| s.to_str())
+ .unwrap_or(&id);
+ let (candidate, _) =
+ experiment_store.upsert_discovered_project(&project.path, name, &id, 0, unix_time())?;
+ experiment_store.enqueue_event(&format!("project:{}:configured:{}", candidate.candidate_id, unix_time()), "project.updated", &serde_json::json!({"candidate_id":candidate.candidate_id,"display_name":candidate.display_name,"suggested_project_id":candidate.suggested_project_id,"session_count":candidate.session_count,"state":candidate.state,"updated_at_unix":candidate.updated_at_unix}), unix_time())?;
+ }
+
+ let worker_runtime = CodexRuntime {
tasks: runtime_tasks::RuntimeTasks::new(),
- python: worker_python.to_owned(),
- root: worker_root.to_owned(),
- registry: WorkerRegistry::default(),
- idle: Arc::new(AsyncMutex::new(HashMap::new())),
+ codex: codex::Codex::new(codex_bin),
+ link: hub.link.clone(),
+ agent_id: hub.agent_id.clone(),
+ wake: Arc::new(tokio::sync::Notify::new()),
};
experiment_store.recover_recorded_turns(unix_time())?;
let orphaned = experiment_store.orphan_running_prompts(unix_time())?;
@@ -468,77 +446,214 @@ async fn run(
experiment_store.orphan_running_schedules(unix_time())?;
let command_store = Arc::new(command_store);
let experiment_store = Arc::new(experiment_store);
- for lane in 0..7 {
- let client = client.clone();
+ let (incoming_commands, mut commands_rx) = tokio::sync::mpsc::channel(32);
+ let (incoming_reads, mut reads_rx) = tokio::sync::mpsc::channel(16);
+ worker_runtime.tasks.spawn(live::run(
+ hub.clone(),
+ heartbeat,
+ worker_runtime.codex.clone(),
+ incoming_commands,
+ incoming_reads,
+ worker_runtime.wake.clone(),
+ ));
+ {
+ let store = experiment_store.clone();
+ let commands = command_store.clone();
+ let wake = worker_runtime.wake.clone();
+ let agent_id = hub.agent_id.clone();
+ let receipt_hub = hub.clone();
+ let receipt_client = client.clone();
+ worker_runtime.tasks.spawn(async move {
+ while let Some(command) = commands_rx.recv().await {
+ if command.agent_id != agent_id || command.protocol != FARHELM_PROTOCOL {
+ receipt_hub.link.reset();
+ continue;
+ }
+ let incoming = command.clone();
+ let store = store.clone();
+ let receipts = store.clone();
+ let commands = commands.clone();
+ let result = runtime_tasks::blocking(move || {
+ if command.action == CommandAction::AgentProbe {
+ commands.receive(&command, unix_time()).map(|_| ())
+ } else {
+ store.receive_remote_command(&command, unix_time())
+ }
+ })
+ .await;
+ match result {
+ Err(error) => {
+ warn!(%error, "Agent rejected an incoming command");
+ receipt_hub.link.reset();
+ }
+ Ok(()) if incoming.action != CommandAction::AgentProbe => {
+ let id = incoming.command_id.clone();
+ let Ok(saved) = receipts.background(move |s| s.remote_receipt(&id)).await
+ else {
+ receipt_hub.link.reset();
+ continue;
+ };
+ let state = saved.state;
+ let report = farhelm_protocol::CommandReportRequest {
+ protocol: FARHELM_PROTOCOL.into(),
+ agent_id: agent_id.clone(),
+ command_id: incoming.command_id.clone(),
+ state,
+ result: None,
+ data: saved.data,
+ detail: saved.detail,
+ };
+ if send_command_report(&receipt_client, &receipt_hub, &report)
+ .await
+ .is_ok()
+ && state == CommandState::Accepted
+ {
+ let id = incoming.command_id;
+ let _ = receipts
+ .background(move |s| {
+ s.mark_remote_accepted_reported(&id, unix_time())
+ })
+ .await;
+ }
+ }
+ Ok(()) => {}
+ }
+ wake.notify_one();
+ }
+ });
+ }
+ {
+ let store = experiment_store.clone();
+ let native = worker_runtime.clone();
let hub = hub.clone();
- let endpoint = endpoint.clone();
- let heartbeat = heartbeat.clone();
+ worker_runtime.tasks.spawn(async move {
+ let permits = Arc::new(tokio::sync::Semaphore::new(4));
+ while let Some((request, expires)) = reads_rx.recv().await {
+ let Ok(permit) = permits.clone().acquire_owned().await else {
+ break;
+ };
+ let store = store.clone();
+ let worker = native.clone();
+ let hub = hub.clone();
+ native.tasks.spawn(async move {
+ let _permit = permit;
+ if expires <= unix_time() {
+ return;
+ }
+ let id = request.request_id.clone();
+ let outcome = tokio::time::timeout(
+ Duration::from_secs(expires.saturating_sub(unix_time()).min(20)),
+ read_request(&hub, &worker, &store, request),
+ )
+ .await;
+ let report = match outcome {
+ Ok(Ok(report)) => report,
+ _ => farhelm_protocol::AgentReadReportRequest {
+ protocol: FARHELM_PROTOCOL.to_owned(),
+ agent_id: hub.agent_id.clone(),
+ request_id: id,
+ ok: false,
+ data: None,
+ detail: Some("codex_read_failed".into()),
+ },
+ };
+ let _ = hub
+ .link
+ .request(
+ |request_id| farhelm_protocol::live::AgentFrame::ReadReport {
+ request_id,
+ report,
+ },
+ )
+ .await;
+ });
+ }
+ });
+ }
+ {
+ let mut link = hub.link.connected.subscribe();
+ let mut native = worker_runtime.codex.subscribe_status();
+ let status_path = database.with_extension("status.json");
+ worker_runtime.tasks.spawn(async move {
+ let mut tick = tokio::time::interval(Duration::from_secs(15));
+ loop {
+ let status = serde_json::json!({"agent_version":PRODUCT_VERSION,"pid":std::process::id(),"updated_at_unix":unix_time(),"hub_connected":*link.borrow(),"codex":native.borrow().clone()});
+ let path = status_path.clone();
+ let _ = runtime_tasks::blocking(move || farhelm_lifecycle::write_atomic(&path, &serde_json::to_vec(&status)?, 0o600)).await;
+ tokio::select! { _ = link.changed() => {}, _ = native.changed() => {}, _ = tick.tick() => {} }
+ }
+ });
+ }
+ // A filesystem wake observes CLI commits as well as this service's outbox.
+ worker_runtime.tasks.spawn(runtime_tasks::watch_database(
+ database.to_owned(),
+ worker_runtime.wake.clone(),
+ ));
+ let report_wake = Arc::new(tokio::sync::Notify::new());
+ {
+ let wake = report_wake.clone();
+ let hub = hub.clone();
+ let client = client.clone();
let commands = command_store.clone();
let experiments = experiment_store.clone();
+ let runtime = worker_runtime.clone();
+ worker_runtime.tasks.spawn(async move {
+ loop {
+ wake.notified().await;
+ if !*hub.link.connected.borrow() {
+ continue;
+ }
+ let mut processed = 0;
+ let _ = drain_local_work(&client, &hub, &commands, &mut processed).await;
+ let _ = drain_remote_work(
+ &client,
+ &hub,
+ &experiments,
+ &BTreeMap::new(),
+ &runtime,
+ &mut processed,
+ )
+ .await;
+ let _ = upload_events(&client, &hub, &experiments).await;
+ }
+ });
+ }
+ for lane in 0..4 {
+ let report_wake = report_wake.clone();
+ let experiments = experiment_store.clone();
let worker = worker_runtime.clone();
let database = database.to_owned();
worker_runtime.tasks.spawn(async move {
- let period = match lane {
- 0 => interval_secs,
- 6 => 30,
- _ => command_interval_secs,
- };
- let mut ticker = tokio::time::interval(Duration::from_secs(period));
+ let mut ticker = tokio::time::interval(Duration::from_secs(match lane {0=>10,1=>2,2=>30,_=>15}));
ticker.set_missed_tick_behavior(tokio::time::MissedTickBehavior::Skip);
loop {
- ticker.tick().await;
- let result: Result<()> = async {
+ if lane == 0 {tokio::select! {_=ticker.tick()=>{},_=worker.wake.notified()=>{}}} else {ticker.tick().await;}
+ let result:Result<()> = async {
match lane {
0 => {
- send_heartbeat(&client, endpoint.clone(), &hub.token, &heartbeat).await
- }
+ report_wake.notify_one();
+ dispatch_work(&experiments,&worker).await
+ },
1 => {
- let projects = approved_project_sections(&experiments)?;
- process_command_cycle(
- &client,
- &hub,
- &commands,
- &experiments,
- &projects,
- &worker,
- )
- .await
- .map(|_| ())
- }
- 2 => process_read_once(&client, &hub, &worker, &experiments).await,
- 3 => {
- let projects = approved_project_sections(&experiments)?;
- let matchers = projects
- .into_iter()
- .map(|(id, p)| {
- (
- id,
- ProjectMatchers {
- success: p.success_patterns,
- failure: p.failure_patterns,
- },
- )
- })
- .collect();
- for watch in experiments.inspect(&matchers, unix_time())? { info!(watch_id=%watch.watch_id,state=?watch.state,"experiment finished"); }
- Ok(())
- }
- 4 => dispatch_work(&client, &hub, &experiments, &worker).await,
- 5 => upload_events(&client, &hub, &experiments).await,
- _ => discover_projects(&database, &worker.python, &worker.root).await,
+ let store=experiments.clone();
+ runtime_tasks::blocking(move|| {
+ let matchers=approved_project_sections(&store)?.into_iter().map(|(id,p)|(id,ProjectMatchers {success:p.success_patterns,failure:p.failure_patterns})).collect();
+ store.inspect(&matchers,unix_time()).map(|watches| {for watch in watches {info!(watch_id=%watch.watch_id,state=?watch.state,"experiment finished");}})
+ }).await?;
+ worker.wake.notify_one();Ok(())
+ },
+ 2 => {discover_projects(&database,&worker.codex).await?;worker.wake.notify_one();Ok(())},
+ _ => worker.codex.warm().await,
}
- }
- .await;
- if let Err(error) = result {
- warn!(lane, %error, "Agent service cycle failed; retrying");
- }
+ }.await;
+ if let Err(error)=result {warn!(lane,%error,"Agent service cycle failed; retrying");}
}
});
}
info!(version=PRODUCT_VERSION,agent_id=%hub.agent_id,"FarHelm Agent is running");
shutdown_signal().await;
worker_runtime.tasks.shutdown().await;
- worker_runtime.idle.lock().await.clear();
+ worker_runtime.codex.shutdown().await;
info!("FarHelm Agent stopped");
Ok(())
}
@@ -562,14 +677,12 @@ fn approved_project_sections(
.collect())
}
-async fn discover_projects(database: &Path, python: &str, worker_root: &Path) -> Result<()> {
- let value = worker_call_once(
- python,
- worker_root,
- "codex.projects.discover",
- serde_json::json!({}),
- )
- .await?;
+async fn discover_projects(database: &Path, codex: &codex::Codex) -> Result<()> {
+ let value = codex
+ .call("codex.projects.discover", serde_json::json!({}))
+ .await?;
+ let database = database.to_owned();
+ runtime_tasks::blocking(move || {
let projects = value
.get("projects")
.and_then(serde_json::Value::as_array)
@@ -579,7 +692,7 @@ async fn discover_projects(database: &Path, python: &str, worker_root: &Path) ->
.context("HOME is missing")?;
let home = fs::canonicalize(home).context("failed to resolve HOME")?;
let uid = unsafe { libc::geteuid() };
- let store = ExperimentStore::open(database)?;
+ let store = ExperimentStore::open(&database)?;
for project in projects {
let Some(raw_path) = project.get("cwd").and_then(serde_json::Value::as_str) else {
continue;
@@ -664,6 +777,7 @@ async fn discover_projects(database: &Path, python: &str, worker_root: &Path) ->
)?;
}
Ok(())
+ }).await
}
fn suggested_project_id(name: &str) -> String {
@@ -687,42 +801,46 @@ fn suggested_project_id(name: &str) -> String {
async fn sync_project_sessions(
database: &Path,
- python: &str,
- worker_root: &Path,
+ codex: &codex::Codex,
project_id: &str,
project_path: &Path,
) -> Result<()> {
- let value = worker_call_once(
- python,
- worker_root,
- "codex.sessions.list",
- serde_json::json!({"project_path":project_path,"archived":"all"}),
- )
- .await?;
- let sessions = value
- .get("sessions")
- .and_then(serde_json::Value::as_array)
- .context("Worker session list omitted sessions")?;
- let store = ExperimentStore::open(database)?;
- for session in sessions {
- let session_id = session
- .get("session_id")
- .and_then(serde_json::Value::as_str)
- .context("Worker returned a session without ID")?;
- let updated = session
- .get("updated_at_unix")
- .and_then(serde_json::Value::as_u64)
- .unwrap_or_else(unix_time);
- store.discover_session(
- session_id,
- project_id,
- project_path,
- &session["title"],
- session["archived"].as_bool().unwrap_or(false),
- updated,
- )?;
- }
- Ok(())
+ let value = codex
+ .call(
+ "codex.sessions.list",
+ serde_json::json!({"project_path":project_path,"archived":"all"}),
+ )
+ .await?;
+ let database = database.to_owned();
+ let project_id = project_id.to_owned();
+ let project_path = project_path.to_owned();
+ runtime_tasks::blocking(move || {
+ let sessions = value
+ .get("sessions")
+ .and_then(serde_json::Value::as_array)
+ .context("Worker session list omitted sessions")?;
+ let store = ExperimentStore::open(&database)?;
+ for session in sessions {
+ let session_id = session
+ .get("session_id")
+ .and_then(serde_json::Value::as_str)
+ .context("Worker returned a session without ID")?;
+ let updated = session
+ .get("updated_at_unix")
+ .and_then(serde_json::Value::as_u64)
+ .unwrap_or_else(unix_time);
+ store.discover_session(
+ session_id,
+ &project_id,
+ &project_path,
+ &session["title"],
+ session["archived"].as_bool().unwrap_or(false),
+ updated,
+ )?;
+ }
+ Ok(())
+ })
+ .await
}
fn load_local_config(path: Option) -> Result {
@@ -882,26 +1000,39 @@ fn experiment_command(command: ExperimentCommand) -> Result<()> {
async fn codex_command(command: CodexCommand) -> Result<()> {
match command {
+ CodexCommand::Configure { config, bin } => {
+ let path = config.unwrap_or(AgentPaths::discover()?.config);
+ let mut config = AgentFileConfig::load(&path)?;
+ let bin = codex::transport::discover(bin.as_deref())?;
+ let version = codex::transport::version(&bin).await?;
+ let native = codex::Codex::new(Some(bin.clone()));
+ native.warm().await?;
+ let state = native.status().state;
+ native.shutdown().await;
+ config.codex.bin = Some(bin.clone());
+ farhelm_lifecycle::write_atomic(&path, config.encode()?.as_bytes(), 0o600)?;
+ println!(
+ "Codex configured: {} ({version}, {state}). Restart the Agent service to apply.",
+ bin.display()
+ );
+ Ok(())
+ }
CodexCommand::Sessions { config, project } => {
let config = load_local_config(config)?;
let store = ExperimentStore::open(&config.agent.database)?;
store.import_config_projects(&config.projects, unix_time())?;
- let projects = store.approved_projects()?;
- let project = projects
- .get(&project)
- .context("project is not approved; import it in the Console first")?;
- let project_path = fs::canonicalize(&project.path)
- .context("failed to resolve approved project path")?;
- let worker_root = AgentPaths::discover()?.worker;
- resources::materialize_worker(&worker_root)?;
- let value = worker_call_once(
- &config.worker.python,
- &worker_root,
- "codex.sessions.list",
- serde_json::json!({"project_path":project_path,"archived":"false"}),
- )
- .await?;
- println!("{}", serde_json::to_string_pretty(&value)?);
+ let projects = approved_project_sections(&store)?;
+ let path =
+ fs::canonicalize(&projects.get(&project).context("unapproved project")?.path)?;
+ let native = codex::Codex::new(config.codex.bin);
+ let result = native
+ .call(
+ "codex.sessions.list",
+ serde_json::json!({"project_path":path,"archived":"all"}),
+ )
+ .await;
+ native.shutdown().await;
+ println!("{}", serde_json::to_string_pretty(&result?)?);
Ok(())
}
}
@@ -933,10 +1064,34 @@ fn prompt_regex(prompt: &str) -> Result {
}
async fn upload_events(client: &Client, hub: &HubArgs, store: &ExperimentStore) -> Result<()> {
- let events = store.pending_events(&hub.agent_id, 100)?;
+ let agent = hub.agent_id.clone();
+ let events = store
+ .background(move |s| s.pending_events(&agent, 100))
+ .await?;
if events.is_empty() {
return Ok(());
}
+ if hub.live_required {
+ let value = hub
+ .link
+ .request(|request_id| farhelm_protocol::live::AgentFrame::Events {
+ request_id,
+ batch: AgentEventBatch {
+ protocol: FARHELM_PROTOCOL.into(),
+ agent_id: hub.agent_id.clone(),
+ events,
+ },
+ })
+ .await?;
+ let ack: AgentEventAck = serde_json::from_value(value)?;
+ ensure!(
+ ack.protocol == FARHELM_PROTOCOL,
+ "Hub event protocol mismatch"
+ );
+ return store
+ .background(move |s| s.acknowledge_events(&ack.accepted_event_ids))
+ .await;
+ }
let response = client
.post(hub_endpoint(&hub.hub, "/api/v1/agent/events")?)
.bearer_auth(&hub.token)
@@ -958,7 +1113,9 @@ async fn upload_events(client: &Client, hub: &HubArgs, store: &ExperimentStore)
ack.protocol == FARHELM_PROTOCOL,
"Hub event protocol mismatch"
);
- store.acknowledge_events(&ack.accepted_event_ids)
+ store
+ .background(move |s| s.acknowledge_events(&ack.accepted_event_ids))
+ .await
}
const fn state_label(state: farhelm_protocol::ExperimentState) -> &'static str {
@@ -978,12 +1135,12 @@ async fn command_poll_once(runtime: &RuntimeArgs) -> Result<()> {
let experiments = ExperimentStore::open(&runtime.database)?;
experiments.import_config_projects(&runtime.projects, unix_time())?;
let live_projects = approved_project_sections(&experiments)?;
- let worker_runtime = WorkerRuntime {
+ let worker_runtime = CodexRuntime {
tasks: runtime_tasks::RuntimeTasks::new(),
- python: runtime.worker_python.clone(),
- root: runtime.worker_root.clone(),
- registry: WorkerRegistry::default(),
- idle: Arc::new(AsyncMutex::new(HashMap::new())),
+ codex: codex::Codex::new(runtime.codex_bin.clone()),
+ link: runtime.hub.link.clone(),
+ agent_id: runtime.hub.agent_id.clone(),
+ wake: Arc::new(tokio::sync::Notify::new()),
};
let processed = process_command_cycle(
&client,
@@ -1004,7 +1161,7 @@ async fn process_command_cycle(
store: &CommandStore,
experiments: &ExperimentStore,
projects: &BTreeMap,
- worker_runtime: &WorkerRuntime,
+ worker_runtime: &CodexRuntime,
) -> Result {
let mut processed = 0;
drain_local_work(client, hub, store, &mut processed).await?;
@@ -1068,109 +1225,6 @@ async fn process_command_cycle(
Ok(processed)
}
-async fn process_read_once(
- client: &Client,
- hub: &HubArgs,
- worker: &WorkerRuntime,
- store: &ExperimentStore,
-) -> Result<()> {
- let url = hub_endpoint(&hub.hub, "/api/v1/agent/reads/claim")?;
- let claim: AgentReadClaimResponse = client
- .post(url)
- .bearer_auth(&hub.token)
- .json(&AgentReadClaimRequest {
- protocol: FARHELM_PROTOCOL.to_owned(),
- agent_id: hub.agent_id.clone(),
- wait_secs: None,
- })
- .send()
- .await?
- .error_for_status()?
- .json()
- .await?;
- let Some(request) = claim.request else {
- return Ok(());
- };
- let outcome = if request.method == "codex.session.history" {
- let session_id = request
- .params
- .get("session_id")
- .and_then(serde_json::Value::as_str)
- .context("history read omitted session ID")?;
- ensure!(
- store.session_binding(session_id)?.is_some(),
- "history read references an unapproved session"
- );
- worker_call_once(
- &worker.python,
- &worker.root,
- &request.method,
- request.params,
- )
- .await
- .and_then(|value| {
- let page: farhelm_protocol::CodexTranscriptPage = serde_json::from_value(value)?;
- Ok(serde_json::to_value(page)?)
- })
- } else if request.method == "codex.session.display" {
- let ids: Option> = request
- .params
- .get("session_ids")
- .map(|value| serde_json::from_value(value.clone()))
- .transpose()?;
- let project = request
- .params
- .get("project_id")
- .and_then(serde_json::Value::as_str);
- let bindings = store.display_bindings(project, ids.as_deref())?;
- let mut params = request.params;
- params["bindings"] = bindings;
- params["agent_id"] = serde_json::json!(hub.agent_id);
- tokio::time::timeout(
- Duration::from_secs(18),
- worker_call_once(&worker.python, &worker.root, &request.method, params),
- )
- .await
- .context("display read timed out")?
- } else if request.method == "codex.schedule.detail" {
- let id = request
- .params
- .get("schedule_id")
- .and_then(serde_json::Value::as_str)
- .context("schedule detail omitted ID")?;
- store.schedule_detail(id)
- } else {
- bail!("Hub requested unsupported transient read")
- };
- let report = match outcome {
- Ok(data) => AgentReadReportRequest {
- protocol: FARHELM_PROTOCOL.to_owned(),
- agent_id: hub.agent_id.clone(),
- request_id: request.request_id.clone(),
- ok: true,
- data: Some(data),
- detail: None,
- },
- Err(error) => AgentReadReportRequest {
- protocol: FARHELM_PROTOCOL.to_owned(),
- agent_id: hub.agent_id.clone(),
- request_id: request.request_id.clone(),
- ok: false,
- data: None,
- detail: Some(error.to_string().chars().take(512).collect()),
- },
- };
- let path = format!("/api/v1/agent/reads/{}/report", request.request_id);
- client
- .post(hub_endpoint(&hub.hub, &path)?)
- .bearer_auth(&hub.token)
- .json(&report)
- .send()
- .await?
- .error_for_status()?;
- Ok(())
-}
-
async fn drain_local_work(
client: &Client,
hub: &HubArgs,
@@ -1178,28 +1232,35 @@ async fn drain_local_work(
processed: &mut u64,
) -> Result<()> {
for _ in 0..8 {
- let Some(pending) = store.next_work()? else {
+ let Some(pending) = store.background(|s| s.next_work()).await? else {
return Ok(());
};
if pending.state == CommandState::Accepted && unix_time() >= pending.expires_at_unix {
- store.expire(&pending.command_id, unix_time())?;
+ {
+ let id = pending.command_id.clone();
+ store
+ .background(move |s| s.expire(&id, unix_time()))
+ .await?
+ };
continue;
}
if pending.state == CommandState::Accepted && pending.reported {
- store.complete_probe(
- &pending.command_id,
- &ProbeResult {
- agent_version: PRODUCT_VERSION.to_owned(),
- hostname: resolve_hostname(hub.hostname.as_deref(), &hub.agent_id),
- },
- unix_time(),
- )?;
+ let id = pending.command_id.clone();
+ let result = ProbeResult {
+ agent_version: PRODUCT_VERSION.to_owned(),
+ hostname: resolve_hostname(hub.hostname.as_deref(), &hub.agent_id),
+ };
+ store
+ .background(move |s| s.complete_probe(&id, &result, unix_time()))
+ .await?;
*processed += 1;
continue;
}
let report = store.report(&pending, &hub.agent_id);
send_command_report(client, hub, &report).await?;
- store.mark_reported(&pending.command_id, pending.state, unix_time())?;
+ store
+ .background(move |s| s.mark_reported(&pending.command_id, pending.state, unix_time()))
+ .await?;
}
bail!("local command work exceeded the bounded cycle limit")
}
@@ -1209,13 +1270,18 @@ async fn drain_remote_work(
hub: &HubArgs,
store: &ExperimentStore,
projects: &BTreeMap,
- worker_runtime: &WorkerRuntime,
+ worker_runtime: &CodexRuntime,
processed: &mut u64,
) -> Result<()> {
let _ = (projects, worker_runtime, processed);
- for command in store.pending_remote_commands()? {
+ for command in store.background(|s| s.pending_remote_commands()).await? {
if unix_time() >= command.expires_at_unix {
- store.expire_remote_command(&command.command_id, unix_time())?;
+ {
+ let id = command.command_id.clone();
+ store
+ .background(move |s| s.expire_remote_command(&id, unix_time()))
+ .await?
+ };
continue;
}
if !command.accepted_reported {
@@ -1229,10 +1295,14 @@ async fn drain_remote_work(
data: None,
};
send_command_report(client, hub, &report).await?;
- store.mark_remote_accepted_reported(&command.command_id, unix_time())?;
+ store
+ .background(move |s| {
+ s.mark_remote_accepted_reported(&command.command_id, unix_time())
+ })
+ .await?;
}
}
- for terminal in store.pending_remote_reports()? {
+ for terminal in store.background(|s| s.pending_remote_reports()).await? {
send_command_report(
client,
hub,
@@ -1247,21 +1317,26 @@ async fn drain_remote_work(
},
)
.await?;
- store.mark_remote_terminal_reported(&terminal.command_id, unix_time())?;
+ store
+ .background(move |s| s.mark_remote_terminal_reported(&terminal.command_id, unix_time()))
+ .await?;
}
Ok(())
}
-async fn dispatch_work(
- client: &Client,
- hub: &HubArgs,
- store: &ExperimentStore,
- worker: &WorkerRuntime,
-) -> Result<()> {
- let projects = approved_project_sections(store)?;
- for command in store.runnable_remote_commands(unix_time())? {
+async fn dispatch_work(store: &ExperimentStore, worker: &CodexRuntime) -> Result<()> {
+ let projects = store.background(approved_project_sections).await?;
+ for command in store
+ .background(|s| s.runnable_remote_commands(unix_time()))
+ .await?
+ {
if unix_time() >= command.expires_at_unix {
- store.expire_remote_command(&command.command_id, unix_time())?;
+ {
+ let id = command.command_id.clone();
+ store
+ .background(move |s| s.expire_remote_command(&id, unix_time()))
+ .await?
+ };
continue;
}
let control = command.action != CommandAction::CodexTurnStart;
@@ -1270,35 +1345,45 @@ async fn dispatch_work(
.payload
.get("session_id")
.and_then(serde_json::Value::as_str)
- && store.remote_session_busy(session)?
+ && {
+ let session = session.to_owned();
+ store
+ .background(move |s| s.remote_session_busy(&session))
+ .await?
+ }
{
continue;
}
let Some(permit) = worker.tasks.permit(control) else {
continue;
};
- if store.claim_remote_command(&command.command_id, unix_time())? {
+ let id = command.command_id.clone();
+ if store
+ .background(move |s| s.claim_remote_command(&id, unix_time()))
+ .await?
+ {
let database = store.path().to_owned();
let projects = projects.clone();
let runtime = worker.clone();
- let client = client.clone();
- let hub = hub.clone();
worker.tasks.spawn(async move {
let _permit = permit;
if let Err(error) =
- execute_remote_command(&client, &hub, &database, &projects, &runtime, command)
- .await
+ execute_remote_command(&database, &projects, &runtime, command).await
{
warn!(%error,"Codex command failed");
}
});
}
}
- for prompt in store.pending_auto_prompts(unix_time())? {
+ for prompt in store
+ .background(|s| s.pending_auto_prompts(unix_time()))
+ .await?
+ {
let Some(permit) = worker.tasks.permit(false) else {
break;
};
- if store.claim_auto_prompt(&prompt.watch_id)? {
+ let id = prompt.watch_id.clone();
+ if store.background(move |s| s.claim_auto_prompt(&id)).await? {
let database = store.path().to_owned();
let runtime = worker.clone();
worker.tasks.spawn(async move {
@@ -1309,11 +1394,15 @@ async fn dispatch_work(
});
}
}
- for prompt in store.due_schedules(unix_time())? {
+ for prompt in store.background(|s| s.due_schedules(unix_time())).await? {
let Some(permit) = worker.tasks.permit(false) else {
break;
};
- if store.claim_schedule(&prompt.schedule_id, unix_time())? {
+ let id = prompt.schedule_id.clone();
+ if store
+ .background(move |s| s.claim_schedule(&id, unix_time()))
+ .await?
+ {
let database = store.path().to_owned();
let runtime = worker.clone();
let project = projects.get(&prompt.project_id).cloned();
@@ -1330,20 +1419,18 @@ async fn dispatch_work(
}
async fn execute_remote_command(
- client: &Client,
- hub: &HubArgs,
database: &Path,
projects: &BTreeMap,
- worker_runtime: &WorkerRuntime,
+ worker_runtime: &CodexRuntime,
command: RemoteCommand,
) -> Result<()> {
- let store = ExperimentStore::open(database)?;
+ let store = ExperimentStore::open_async(database).await?;
let outcome = execute_remote_command_inner(&store, projects, worker_runtime, &command).await;
let (state, data, detail) = match outcome {
Ok(data) => (CommandState::Completed, Some(data), None),
Err(error) => {
warn!(command_id = %command.command_id, %error, "local Codex operation failed");
- let event_type = if error.downcast_ref::().is_some() {
+ let event_type = if error.downcast_ref::().is_some() {
"codex.turn.orphaned"
} else {
"codex.turn.failed"
@@ -1376,34 +1463,20 @@ async fn execute_remote_command(
)
}
};
- store.finish_remote_command(
- &command.command_id,
- state,
- data.as_ref(),
- detail.as_deref(),
- unix_time(),
- )?;
- send_command_report(
- client,
- hub,
- &farhelm_protocol::CommandReportRequest {
- protocol: FARHELM_PROTOCOL.to_owned(),
- agent_id: hub.agent_id.clone(),
- command_id: command.command_id.clone(),
- state,
- result: None,
- detail,
- data,
- },
- )
- .await?;
- store.mark_remote_terminal_reported(&command.command_id, unix_time())
+ let id = command.command_id.clone();
+ store
+ .background(move |s| {
+ s.finish_remote_command(&id, state, data.as_ref(), detail.as_deref(), unix_time())
+ })
+ .await?;
+ worker_runtime.wake.notify_one();
+ Ok(())
}
async fn execute_remote_command_inner(
store: &ExperimentStore,
projects: &BTreeMap,
- worker_runtime: &WorkerRuntime,
+ worker_runtime: &CodexRuntime,
command: &RemoteCommand,
) -> Result {
if command.action == CommandAction::ProjectApprove {
@@ -1431,8 +1504,7 @@ async fn execute_remote_command_inner(
)?;
sync_project_sessions(
store.path(),
- &worker_runtime.python,
- &worker_runtime.root,
+ &worker_runtime.codex,
&project.suggested_project_id,
&project.path,
)
@@ -1499,7 +1571,7 @@ async fn execute_remote_command_inner(
} else {
project_root.clone()
};
- let value = worker_call_once(&worker_runtime.python, &worker_runtime.root, method, serde_json::json!({"session_id":command.payload.get("session_id"),"cwd":cwd.clone(),"mode":mode})).await?;
+ let value = worker_runtime.codex.call(method, serde_json::json!({"session_id":command.payload.get("session_id"),"cwd":cwd.clone(),"mode":mode})).await?;
let session_id = value
.get("session_id")
.and_then(serde_json::Value::as_str)
@@ -1544,18 +1616,10 @@ async fn execute_remote_command_inner(
} else {
"codex.turn.interrupt"
};
- active_worker_request(
- &worker_runtime.registry,
- command
- .payload
- .get("session_id")
- .and_then(serde_json::Value::as_str)
- .context("control command omitted session_id")?,
- &command.command_id,
- method,
- command.payload.clone(),
- )
- .await
+ worker_runtime
+ .codex
+ .call(method, command.payload.clone())
+ .await
}
CommandAction::CodexScheduleCreate => {
let schedule_id = store.create_schedule(&command.payload, unix_time())?;
@@ -1577,11 +1641,11 @@ async fn execute_remote_command_inner(
async fn run_scheduled_prompt(
database: &Path,
- worker: &WorkerRuntime,
+ worker: &CodexRuntime,
project: Option,
prompt: ScheduledPrompt,
) -> Result<()> {
- let store = ExperimentStore::open(database)?;
+ let store = ExperimentStore::open_async(database).await?;
let result = async {
let project = project.context("scheduled project is no longer approved")?;
let root =
@@ -1600,7 +1664,7 @@ async fn run_scheduled_prompt(
.await;
let state = match &result {
Ok(_) => farhelm_protocol::CodexScheduleState::Completed,
- Err(error) if error.downcast_ref::().is_some() => {
+ Err(error) if error.downcast_ref::().is_some() => {
farhelm_protocol::CodexScheduleState::Orphaned
}
Err(_) => farhelm_protocol::CodexScheduleState::Failed,
@@ -1614,6 +1678,24 @@ async fn send_command_report(
hub: &HubArgs,
report: &farhelm_protocol::CommandReportRequest,
) -> Result<()> {
+ if hub.live_required {
+ let frame = report.clone();
+ let value = hub
+ .link
+ .request(
+ |request_id| farhelm_protocol::live::AgentFrame::CommandReport {
+ request_id,
+ report: frame,
+ },
+ )
+ .await?;
+ let status: CommandStatusResponse = serde_json::from_value(value)?;
+ ensure!(
+ status.command_id == report.command_id && status.state == report.state,
+ "Hub command receipt mismatch"
+ );
+ return Ok(());
+ }
let report_url = hub_endpoint(&hub.hub, "/api/v1/agent/commands/report")?;
let response = client
.post(report_url)
@@ -1758,10 +1840,10 @@ async fn send_heartbeat(
async fn run_auto_prompt(
database: &Path,
- worker_runtime: &WorkerRuntime,
+ worker_runtime: &CodexRuntime,
job: AutoPrompt,
) -> Result<()> {
- let store = ExperimentStore::open(database)?;
+ let store = ExperimentStore::open_async(database).await?;
let result = run_auto_prompt_inner(&store, worker_runtime, &job).await;
let now = unix_time();
match result {
@@ -1776,7 +1858,7 @@ async fn run_auto_prompt(
),
Err(error) => {
let detail = error.to_string();
- let event_type = if error.downcast_ref::().is_some() {
+ let event_type = if error.downcast_ref::().is_some() {
"codex.turn.orphaned"
} else {
"codex.turn.failed"
@@ -1797,19 +1879,9 @@ async fn run_auto_prompt(
async fn run_auto_prompt_inner(
store: &ExperimentStore,
- worker_runtime: &WorkerRuntime,
+ worker_runtime: &CodexRuntime,
job: &AutoPrompt,
) -> Result<(String, String)> {
- let source_root = worker_runtime
- .root
- .join("src")
- .canonicalize()
- .with_context(|| {
- format!(
- "worker source not found below {}",
- worker_runtime.root.display()
- )
- })?;
let (cwd, mode) = if let Some(session_id) = &job.session_id {
if let Some(binding) = store.session_binding(session_id)? {
ensure!(
@@ -1842,71 +1914,22 @@ async fn run_auto_prompt_inner(
Some(_) => bail!("invalid new-session mode"),
}
};
- let cached = if let Some(session_id) = job.session_id.as_deref() {
- worker_runtime.idle.lock().await.remove(session_id)
- } else {
- None
- };
- let (child, mut stdout, active_worker, session_id, session_cwd) = if let Some(cached) = cached {
- (
- cached.child,
- cached.stdout,
- cached.active,
- job.session_id.clone().expect("cached worker has session"),
- cwd.clone(),
- )
- } else {
- let mut child = Command::new(&worker_runtime.python)
- .arg("-m")
- .arg("farhelm_worker_codex")
- .env("PYTHONPATH", source_root)
- .stdin(Stdio::piped())
- .stdout(Stdio::piped())
- .stderr(Stdio::inherit())
- .kill_on_drop(true)
- .spawn()
- .with_context(|| format!("failed to start Worker with `{}`", worker_runtime.python))?;
- let mut stdin = child.stdin.take().context("Worker stdin was not piped")?;
- let mut stdout = child.stdout.take().context("Worker stdout was not piped")?;
- let session_request = WorkerRequest {
- protocol: WORKER_PROTOCOL.to_owned(),
- kind: "request".to_owned(),
- request_id: format!("session:{}", job.watch_id),
- method: if job.session_id.is_some() {
+ let value = worker_runtime
+ .codex
+ .call(
+ if job.session_id.is_some() {
"codex.session.resume"
} else {
"codex.session.start"
- }
- .to_owned(),
- params: serde_json::json!({"session_id":job.session_id,"cwd":cwd.clone(),"mode":mode}),
- };
- write_frame(&mut stdin, &session_request).await?;
- let response: WorkerResponse = read_frame(&mut stdout).await?;
- ensure!(
- response.ok,
- "Worker failed to prepare session: {:?}",
- response.error
- );
- let session_id = response
- .result
- .as_ref()
- .and_then(|value| value.get("session_id"))
- .and_then(serde_json::Value::as_str)
- .context("Worker session response omitted session_id")?
- .to_owned();
- let session_cwd = response
- .result
- .as_ref()
- .and_then(|value| value.get("cwd"))
- .and_then(serde_json::Value::as_str)
- .map(PathBuf::from)
- .unwrap_or(cwd);
- let active = ActiveWorker {
- stdin: Arc::new(AsyncMutex::new(stdin)),
- waiters: Arc::new(Mutex::new(HashMap::new())),
- };
- (child, stdout, active, session_id, session_cwd)
- };
+ },
+ serde_json::json!({"session_id":job.session_id,"cwd":cwd,"mode":mode}),
+ )
+ .await?;
+ let session_id = value["session_id"]
+ .as_str()
+ .context("Codex session ID missing")?
+ .to_owned();
+ let session_cwd = PathBuf::from(value["cwd"].as_str().context("Codex cwd missing")?);
store.bind_session(
&session_id,
&job.project_id,
@@ -1917,113 +1940,43 @@ async fn run_auto_prompt_inner(
if job.session_id.is_none() {
store.link_watch_session(&job.watch_id, &session_id, unix_time())?;
}
- let registration =
- register_active_worker(&worker_runtime.registry, &session_id, &active_worker)?;
- store.enqueue_event(
- &format!("{}:session-ready", job.watch_id), "codex.session.updated",
- &serde_json::json!({"session_id":session_id,"project_id":job.project_id,"mode":mode,"state":"queued","title":null,"active_turn_id":null,"updated_at_unix":unix_time()}), unix_time(),
- )?;
-
- let turn_request = WorkerRequest {
- protocol: WORKER_PROTOCOL.to_owned(),
- kind: "request".to_owned(),
- request_id: format!("turn:{}", job.watch_id),
- method: "codex.turn.start".to_owned(),
- params: serde_json::json!({"session_id":session_id,"prompt":job.prompt,"idempotency_key":job.idempotency_key}),
- };
- let turn_result: Result = async {
- {
- let mut stdin = active_worker.stdin.lock().await;
- write_frame(&mut *stdin, &turn_request).await?;
- }
- let mut event_number = 0_u64;
- loop {
- let frame: serde_json::Value = read_frame(&mut stdout).await.map_err(|error| {
- anyhow::Error::new(WorkerTurnOrphaned(format!(
- "Worker exited or violated framing during turn: {error}"
- )))
- })?;
- match frame.get("kind").and_then(serde_json::Value::as_str) {
- Some("event") => {
- event_number += 1;
- let event_type = frame
- .get("event")
- .and_then(serde_json::Value::as_str)
- .unwrap_or("codex.event");
- let data = frame
- .get("data")
- .cloned()
- .unwrap_or(serde_json::Value::Null);
- store.enqueue_event(
- &format!("{}:worker:{event_number}", job.watch_id), event_type,
- &serde_json::json!({"operation_id":job.watch_id,"watch_id":job.watch_id,"project_id":job.project_id,"session_id":session_id,"data":data}), unix_time(),
- )?;
- if event_type == "codex.turn.started" {
- let turn_id = data.get("turn_id").and_then(serde_json::Value::as_str);
- store.enqueue_event(
- &format!("{}:session-running", job.watch_id), "codex.session.updated",
- &serde_json::json!({"session_id":session_id,"project_id":job.project_id,"mode":mode,"state":"running","title":null,"active_turn_id":turn_id,"updated_at_unix":unix_time()}), unix_time(),
- )?;
- }
- }
- Some("response") => {
- let response: WorkerResponse = serde_json::from_value(frame)?;
- if response.request_id != turn_request.request_id {
- complete_worker_waiter(&active_worker, response)?;
- continue;
- }
- ensure!(response.ok, "Worker turn failed");
- ensure!(response.result.as_ref().and_then(|v|v.get("status")).and_then(serde_json::Value::as_str).is_none_or(|v|v=="completed"),"Codex turn did not complete successfully");
- return response
- .result
- .as_ref()
- .and_then(|value| value.get("turn_id"))
- .and_then(serde_json::Value::as_str)
- .map(ToOwned::to_owned)
- .context("Worker turn response omitted turn_id");
- }
- _ => bail!("Worker emitted an invalid frame"),
+ enqueue_session_state(store, job, &session_id, &mode, "queued", "session-ready")?;
+ let sequence = std::sync::atomic::AtomicU64::new(0);
+ let result = worker_runtime.codex.turn(&session_id, &job.prompt, &job.idempotency_key, |event_type, data| {
+ let event_number = sequence.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
+ let payload = serde_json::json!({"operation_id":job.watch_id,"watch_id":job.watch_id,"project_id":job.project_id,"session_id":session_id,"data":data});
+ let session_id = &session_id;
+ let mode = &mode;
+ async move {
+ if event_type == "codex.message.delta" {
+ worker_runtime.link.delta(farhelm_protocol::AgentEvent {protocol:FARHELM_PROTOCOL.into(),sequence:0,event_id:format!("{}:delta:{event_number}",job.watch_id),agent_id:worker_runtime.agent_id.clone(),event_type:event_type.into(),payload,created_at_unix:unix_time()});
+ return Ok(());
}
+ store.enqueue_event(&format!("{}:native:{event_number}",job.watch_id), event_type, &payload, unix_time())?;
+ worker_runtime.wake.notify_one();
+ if event_type == "codex.turn.started" {
+ store.enqueue_event(&format!("{}:session-running",job.watch_id), "codex.session.updated", &serde_json::json!({"session_id":session_id,"project_id":job.project_id,"mode":mode,"state":"running","title":null,"active_turn_id":data["turn_id"],"updated_at_unix":unix_time()}),unix_time())?;
+ }
+ Ok(())
}
- }
- .await;
- let turn_result = if turn_result.is_err() {
- match store.recorded_turn(&job.watch_id)? {
- Some((event, payload)) if event == "codex.turn.completed" => payload
- .get("data")
- .unwrap_or(&payload)
- .get("turn_id")
- .and_then(serde_json::Value::as_str)
- .map(str::to_owned)
- .context("completed turn receipt omitted ID"),
- _ => turn_result,
- }
- } else {
- turn_result
- };
- match turn_result {
+ }).await;
+ match result {
Ok(turn_id) => {
enqueue_session_state(store, job, &session_id, &mode, "idle", "session-idle")?;
- drop(registration);
- let mut idle = worker_runtime.idle.lock().await;
- if idle.len() >= 4
- && let Some(key) = idle.keys().next().cloned()
- && let Some(mut evicted) = idle.remove(&key)
- {
- let _ = evicted.child.start_kill();
- }
- idle.insert(
- session_id.clone(),
- IdleWorker {
- child,
- stdout,
- active: active_worker,
- },
- );
Ok((session_id, turn_id))
}
Err(error) => {
- let state = if error.downcast_ref::().is_some() {
+ if let Some((event, payload)) = store.recorded_turn(&job.watch_id)?
+ && event == "codex.turn.completed"
+ {
+ let turn = payload.get("data").unwrap_or(&payload)["turn_id"]
+ .as_str()
+ .context("completed receipt missing ID")?
+ .to_owned();
+ enqueue_session_state(store, job, &session_id, &mode, "idle", "session-idle")?;
+ return Ok((session_id, turn));
+ }
+ let state = if error.downcast_ref::().is_some() {
"orphaned"
} else {
"failed"
@@ -2090,195 +2043,96 @@ async fn create_isolated_worktree(project_root: &Path, identifier: &str) -> Resu
Ok(worktree_root)
}
-fn register_active_worker(
- registry: &WorkerRegistry,
- session_id: &str,
- worker: &ActiveWorker,
-) -> Result {
- let mut workers = registry
- .lock()
- .map_err(|_| anyhow::anyhow!("active Worker registry lock was poisoned"))?;
- ensure!(
- !workers.contains_key(session_id),
- "session already has an active Worker"
- );
- workers.insert(session_id.to_owned(), worker.clone());
- Ok(ActiveWorkerRegistration {
- registry: registry.clone(),
- session_id: session_id.to_owned(),
- })
-}
-
-fn complete_worker_waiter(worker: &ActiveWorker, response: WorkerResponse) -> Result<()> {
- let sender = worker
- .waiters
- .lock()
- .map_err(|_| anyhow::anyhow!("Worker response registry lock was poisoned"))?
- .remove(&response.request_id)
- .context("Worker returned an unknown response ID")?;
- let result = if response.ok {
- Ok(response.result.unwrap_or(serde_json::Value::Null))
- } else {
- let error = response.error.map_or_else(
- || "Worker rejected request".to_owned(),
- |error| format!("Worker {}: {}", error.code, error.message),
+async fn read_request(
+ hub: &HubArgs,
+ worker: &CodexRuntime,
+ store: &ExperimentStore,
+ request: farhelm_protocol::AgentReadRequest,
+) -> Result {
+ let outcome = if request.method == "codex.session.history" {
+ let session_id = request
+ .params
+ .get("session_id")
+ .and_then(serde_json::Value::as_str)
+ .context("history read omitted session ID")?;
+ ensure!(
+ {
+ let id = session_id.to_owned();
+ store
+ .background(move |s| s.session_binding(&id))
+ .await?
+ .is_some()
+ },
+ "history read references an unapproved session"
);
- Err(error)
- };
- let _ = sender.send(result);
- Ok(())
-}
-
-async fn active_worker_request(
- registry: &WorkerRegistry,
- session_id: &str,
- request_id: &str,
- method: &str,
- params: serde_json::Value,
-) -> Result {
- let worker = registry
- .lock()
- .map_err(|_| anyhow::anyhow!("active Worker registry lock was poisoned"))?
- .get(session_id)
- .cloned()
- .context("session has no active Worker; the turn may be orphaned")?;
- let request = WorkerRequest {
- protocol: WORKER_PROTOCOL.to_owned(),
- kind: "request".to_owned(),
- request_id: request_id.to_owned(),
- method: method.to_owned(),
- params,
- };
- let (sender, receiver) = oneshot::channel();
- worker
- .waiters
- .lock()
- .map_err(|_| anyhow::anyhow!("Worker response registry lock was poisoned"))?
- .insert(request_id.to_owned(), sender);
- let write_result = {
- let mut stdin = worker.stdin.lock().await;
- write_frame(&mut *stdin, &request).await
- };
- if let Err(error) = write_result {
- if let Ok(mut waiters) = worker.waiters.lock() {
- waiters.remove(request_id);
- }
- return Err(error.into());
- }
- timeout(Duration::from_secs(30), receiver)
- .await
- .context("Worker control request timed out")?
- .context("active Worker exited before replying")?
- .map_err(anyhow::Error::msg)
-}
-
-async fn worker_smoke(python: &str, worker_root: &Path) -> Result<()> {
- let source_root = worker_root
- .join("src")
- .canonicalize()
- .with_context(|| format!("worker source not found below {}", worker_root.display()))?;
-
- let mut child = Command::new(python)
- .arg("-m")
- .arg("farhelm_worker_codex")
- .env("PYTHONPATH", source_root)
- .stdin(Stdio::piped())
- .stdout(Stdio::piped())
- .stderr(Stdio::inherit())
- .kill_on_drop(true)
- .spawn()
- .with_context(|| format!("failed to start Worker with `{python}`"))?;
-
- let mut stdin = child.stdin.take().context("Worker stdin was not piped")?;
- let mut stdout = child.stdout.take().context("Worker stdout was not piped")?;
- let request = WorkerRequest::hello("req_worker_smoke", PRODUCT_VERSION);
-
- write_frame(&mut stdin, &request)
- .await
- .context("failed to send Worker hello")?;
- let response: WorkerResponse = timeout(Duration::from_secs(5), read_frame(&mut stdout))
- .await
- .context("Worker hello timed out")??;
-
- ensure!(
- response.protocol == WORKER_PROTOCOL,
- "Worker protocol mismatch"
- );
- ensure!(
- response.request_id == request.request_id,
- "request ID mismatch"
- );
- if !response.ok {
- bail!("Worker rejected hello: {:?}", response.error);
- }
- let result: WorkerHelloResult =
- serde_json::from_value(response.result.context("Worker hello omitted its result")?)
- .context("Worker hello result was invalid")?;
- ensure!(
- result
- .capabilities
- .iter()
- .any(|item| item == "worker.hello"),
- "Worker did not advertise worker.hello"
- );
-
- drop(stdin);
- let status = timeout(Duration::from_secs(5), child.wait())
+ worker
+ .codex
+ .call(&request.method, request.params)
+ .await
+ .and_then(|value| {
+ let page: farhelm_protocol::CodexTranscriptPage = serde_json::from_value(value)?;
+ Ok(serde_json::to_value(page)?)
+ })
+ } else if request.method == "codex.session.display" {
+ let ids: Option> = request
+ .params
+ .get("session_ids")
+ .map(|value| serde_json::from_value(value.clone()))
+ .transpose()?;
+ let project = request
+ .params
+ .get("project_id")
+ .and_then(serde_json::Value::as_str);
+ let project = project.map(str::to_owned);
+ let bindings = store
+ .background(move |s| s.display_bindings(project.as_deref(), ids.as_deref()))
+ .await?;
+ let mut params = request.params;
+ params["bindings"] = bindings;
+ params["agent_id"] = serde_json::json!(hub.agent_id);
+ tokio::time::timeout(
+ Duration::from_secs(18),
+ worker.codex.call(&request.method, params),
+ )
.await
- .context("Worker did not stop after stdin closed")??;
- ensure!(status.success(), "Worker exited with {status}");
-
- println!(
- "Worker handshake ok: {} {} ({})",
- result.worker, result.version, response.protocol
- );
- Ok(())
-}
-
-async fn worker_call_once(
- python: &str,
- worker_root: &Path,
- method: &str,
- params: serde_json::Value,
-) -> Result {
- let source_root = worker_root
- .join("src")
- .canonicalize()
- .with_context(|| format!("worker source not found below {}", worker_root.display()))?;
- let mut child = Command::new(python)
- .arg("-m")
- .arg("farhelm_worker_codex")
- .env("PYTHONPATH", source_root)
- .stdin(Stdio::piped())
- .stdout(Stdio::piped())
- .stderr(Stdio::inherit())
- .kill_on_drop(true)
- .spawn()
- .with_context(|| format!("failed to start Worker with `{python}`"))?;
- let mut stdin = child.stdin.take().context("Worker stdin was not piped")?;
- let mut stdout = child.stdout.take().context("Worker stdout was not piped")?;
- let request = WorkerRequest {
- protocol: WORKER_PROTOCOL.to_owned(),
- kind: "request".to_owned(),
- request_id: format!("req_{:016x}", unix_time()),
- method: method.to_owned(),
- params,
+ .context("display read timed out")?
+ } else if request.method == "codex.schedule.detail" {
+ let id = request
+ .params
+ .get("schedule_id")
+ .and_then(serde_json::Value::as_str)
+ .context("schedule detail omitted ID")?;
+ let id = id.to_owned();
+ store.background(move |s| s.schedule_detail(&id)).await
+ } else {
+ bail!("Hub requested unsupported transient read")
};
- write_frame(&mut stdin, &request).await?;
- let response: WorkerResponse = timeout(Duration::from_secs(30), read_frame(&mut stdout))
- .await
- .context("Worker request timed out")??;
- ensure!(
- response.request_id == request.request_id,
- "Worker response ID mismatch"
- );
- if !response.ok {
- let error = response
- .error
- .context("Worker rejected request without error")?;
- bail!("Worker {}: {}", error.code, error.message);
- }
- response.result.context("Worker response omitted result")
+ let report = match outcome {
+ Ok(data) => AgentReadReportRequest {
+ protocol: FARHELM_PROTOCOL.to_owned(),
+ agent_id: hub.agent_id.clone(),
+ request_id: request.request_id.clone(),
+ ok: true,
+ data: Some(data),
+ detail: None,
+ },
+ Err(error) => AgentReadReportRequest {
+ protocol: FARHELM_PROTOCOL.to_owned(),
+ agent_id: hub.agent_id.clone(),
+ request_id: request.request_id.clone(),
+ ok: false,
+ data: None,
+ detail: Some(
+ if error.to_string().contains("not_configured") {
+ "codex_not_configured"
+ } else {
+ "codex_read_failed"
+ }
+ .into(),
+ ),
+ },
+ };
+ Ok(report)
}
#[cfg(test)]
diff --git a/farhelm-agent/src/management.rs b/farhelm-agent/src/management.rs
index e2eb9d8..6005d8f 100644
--- a/farhelm-agent/src/management.rs
+++ b/farhelm-agent/src/management.rs
@@ -14,8 +14,8 @@ use farhelm_protocol::{
use farhelm_updater::{Role, Updater};
use crate::{
+ codex,
config::{AgentFileConfig, AgentPaths},
- resources::ensure_worker_environment,
};
pub const UNIT_NAME: &str = "farhelm-agent.service";
@@ -44,10 +44,7 @@ pub async fn install(no_service: bool) -> Result<()> {
fs::create_dir_all(parent)?;
set_mode(parent, 0o700)?;
}
- config.worker.python = ensure_worker_environment(&paths.worker, &config.worker.python)
- .await?
- .to_string_lossy()
- .into_owned();
+ discover_codex(&mut config);
let current = std::env::current_exe().context("failed to locate current Agent executable")?;
let replaced_binary = install_binary(¤t, &paths.binary, &paths.previous)?;
if let Some(parent) = paths.config.parent() {
@@ -146,7 +143,40 @@ pub async fn restart() -> Result<()> {
pub fn status() -> Result<()> {
require_user()?;
if service_is_active(ServiceScope::User, UNIT_NAME)? {
- println!("FarHelm Agent {PRODUCT_VERSION} is active.");
+ println!("Service: active (FarHelm Agent CLI {PRODUCT_VERSION})");
+ let paths = AgentPaths::discover()?;
+ let config = AgentFileConfig::load(&paths.config)?;
+ let value = fs::read(config.agent.database.with_extension("status.json"))
+ .ok()
+ .and_then(|bytes| serde_json::from_slice::(&bytes).ok());
+ if let Some(value) = value.filter(|v| {
+ v["updated_at_unix"]
+ .as_u64()
+ .is_some_and(|t| crate::unix_time().saturating_sub(t) <= 45)
+ }) {
+ println!(
+ "Hub: {}",
+ if value["hub_connected"] == true {
+ "connected"
+ } else {
+ "disconnected; local work is retained"
+ }
+ );
+ println!(
+ "Codex: {} ({})",
+ value["codex"]["state"].as_str().unwrap_or("unknown"),
+ value["codex"]["version"]
+ .as_str()
+ .unwrap_or("version unavailable")
+ );
+ if let Some(reason) = value["codex"]["reason"].as_str() {
+ println!("Codex detail: {reason}");
+ }
+ } else {
+ println!(
+ "Hub and Codex: waiting for current service health; run farhelm-agent doctor for diagnostics"
+ );
+ }
Ok(())
} else {
anyhow::bail!("FarHelm Agent service is not active")
@@ -160,15 +190,14 @@ pub async fn doctor(config_path: Option<&Path>) -> Result<(AgentFileConfig, Agen
let config = AgentFileConfig::load(path)?;
let mode = fs::metadata(path)?.permissions().mode() & 0o777;
ensure!(mode & 0o077 == 0, "Agent config must use mode 0600");
- let python_ok = Command::new(&config.worker.python)
- .args(["-c", "import openai_codex"])
- .output()
- .is_ok_and(|output| output.status.success());
- ensure!(
- python_ok,
- "Codex Worker dependency is unavailable in `{}`; run `farhelm-agent install` to repair it",
- config.worker.python
+ let codex = codex::Codex::new(config.codex.bin.clone());
+ codex.warm().await?;
+ println!(
+ "Codex: {} ({})",
+ codex.status().state,
+ codex.status().version.as_deref().unwrap_or("unknown")
);
+ codex.shutdown().await;
let client = reqwest::Client::builder()
.timeout(Duration::from_secs(15))
.build()?;
@@ -225,7 +254,7 @@ pub async fn doctor(config_path: Option<&Path>) -> Result<(AgentFileConfig, Agen
event_response.status()
);
println!("FarHelm Agent {PRODUCT_VERSION} configuration is valid.");
- println!("Worker runtime: {}", paths.worker.display());
+ println!("Codex credentials and configuration remain on this host.");
Ok((config, paths))
}
@@ -246,6 +275,7 @@ pub async fn pair() -> Result<()> {
.await?;
if let Some(existing) = existing {
enrolled.worker = existing.worker;
+ enrolled.codex = existing.codex;
enrolled.projects = existing.projects;
enrolled.agent.database = existing.agent.database;
enrolled.agent.hostname = existing.agent.hostname;
@@ -385,10 +415,7 @@ pub async fn update(check: bool, requested: Option<&str>, allow_major: bool) ->
println!("Downloading verified {}...", candidate.asset_name());
let executable = updater.download(Role::Agent, &candidate).await?;
let mut config = AgentFileConfig::load(&paths.config)?;
- config.worker.python = ensure_worker_environment(&paths.worker, &config.worker.python)
- .await?
- .to_string_lossy()
- .into_owned();
+ discover_codex(&mut config);
write_atomic(&paths.config, config.encode()?.as_bytes(), 0o600)?;
install_binary(&executable.path, &paths.binary, &paths.previous)?;
if let Err(error) = restart_and_check().await {
@@ -594,6 +621,15 @@ fn warn_if_linger_disabled() {
}
}
+fn discover_codex(config: &mut AgentFileConfig) {
+ if config.codex.bin.is_none() {
+ match codex::transport::discover(None) {
+ Ok(bin) => config.codex.bin = Some(bin),
+ Err(error) => eprintln!("Codex setup pending: {error}. Experiments remain available."),
+ }
+ }
+}
+
#[cfg(test)]
mod tests {
use super::*;
@@ -606,7 +642,6 @@ mod tests {
config: "/tmp/home/.config/farhelm/agent.toml".into(),
data: "/tmp/home/.local/share/farhelm".into(),
database: "/tmp/home/.local/share/farhelm/state/agent.db".into(),
- worker: "/tmp/home/.local/share/farhelm/runtime/codex-worker/0.7.1".into(),
unit: "/tmp/home/.config/systemd/user/farhelm-agent.service".into(),
legacy_root: "/tmp/home/.local/share/farhelm-agent".into(),
};
diff --git a/farhelm-agent/src/resources.rs b/farhelm-agent/src/resources.rs
deleted file mode 100644
index 20ebbfa..0000000
--- a/farhelm-agent/src/resources.rs
+++ /dev/null
@@ -1,251 +0,0 @@
-use std::{
- fs,
- io::Read,
- os::unix::fs::PermissionsExt,
- path::{Path, PathBuf},
- process::Command,
-};
-
-use anyhow::{Context, Result, bail, ensure};
-use farhelm_lifecycle::write_atomic;
-use farhelm_updater::Updater;
-use flate2::read::GzDecoder;
-use sha2::{Digest, Sha256};
-
-const MAX_RUNTIME_ARCHIVE_BYTES: u64 = 512 * 1024 * 1024;
-const MAX_RUNTIME_UNPACKED_BYTES: u64 = 2 * 1024 * 1024 * 1024;
-const CODEX_SDK_VERSION: &str = "0.147.0";
-
-const WORKER_FILES: &[(&str, &[u8])] = &[
- (
- "README.md",
- include_bytes!("../../farhelm-worker-codex/README.md"),
- ),
- (
- "pyproject.toml",
- include_bytes!("../../farhelm-worker-codex/pyproject.toml"),
- ),
- (
- "uv.lock",
- include_bytes!("../../farhelm-worker-codex/uv.lock"),
- ),
- (
- "src/farhelm_worker_codex/__init__.py",
- include_bytes!("../../farhelm-worker-codex/src/farhelm_worker_codex/__init__.py"),
- ),
- (
- "src/farhelm_worker_codex/__main__.py",
- include_bytes!("../../farhelm-worker-codex/src/farhelm_worker_codex/__main__.py"),
- ),
- (
- "src/farhelm_worker_codex/framing.py",
- include_bytes!("../../farhelm-worker-codex/src/farhelm_worker_codex/framing.py"),
- ),
- (
- "src/farhelm_worker_codex/worker.py",
- include_bytes!("../../farhelm-worker-codex/src/farhelm_worker_codex/worker.py"),
- ),
- (
- "src/farhelm_worker_codex/history.py",
- include_bytes!("../../farhelm-worker-codex/src/farhelm_worker_codex/history.py"),
- ),
- (
- "src/farhelm_worker_codex/display.py",
- include_bytes!("../../farhelm-worker-codex/src/farhelm_worker_codex/display.py"),
- ),
- (
- "src/farhelm_worker_codex/text.py",
- include_bytes!("../../farhelm-worker-codex/src/farhelm_worker_codex/text.py"),
- ),
-];
-
-pub fn materialize_worker(root: &Path) -> Result<()> {
- fs::create_dir_all(root)
- .with_context(|| format!("failed to create Worker runtime {}", root.display()))?;
- for (relative, contents) in WORKER_FILES {
- let path = root.join(relative);
- if fs::read(&path).ok().as_deref() == Some(*contents) {
- continue;
- }
- write_atomic(&path, contents, 0o600)?;
- }
- Ok(())
-}
-
-pub async fn ensure_worker_environment(root: &Path, _configured_python: &str) -> Result {
- materialize_worker(root)?;
- let managed_python = root.join(".venv/bin/python");
- if validate_runtime(root, &managed_python).is_ok() {
- return Ok(managed_python);
- }
-
- if let Some(archive) = std::env::var_os("FARHELM_CODEX_RUNTIME_ARCHIVE") {
- let archive = PathBuf::from(archive);
- let expected_size = std::env::var("FARHELM_CODEX_RUNTIME_SIZE")
- .context("offline runtime import requires FARHELM_CODEX_RUNTIME_SIZE")?
- .parse::()
- .context("FARHELM_CODEX_RUNTIME_SIZE is invalid")?;
- let expected_digest = parse_sha256(
- &std::env::var("FARHELM_CODEX_RUNTIME_SHA256")
- .context("offline runtime import requires FARHELM_CODEX_RUNTIME_SHA256")?,
- )?;
- verify_local_archive(&archive, expected_size, expected_digest)?;
- install_runtime_archive(root, &archive)?;
- } else {
- let updater = Updater::new()?;
- let candidate = updater.runtime(farhelm_core::PRODUCT_VERSION).await?;
- let archive = updater.download_runtime(&candidate).await?;
- install_runtime_archive(root, &archive.path)?;
- }
-
- validate_runtime(root, &managed_python)?;
- Ok(managed_python)
-}
-
-fn verify_local_archive(path: &Path, expected_size: u64, expected_digest: [u8; 32]) -> Result<()> {
- ensure!(path.is_file(), "offline Codex runtime archive is missing");
- ensure!(
- expected_size > 0 && expected_size <= MAX_RUNTIME_ARCHIVE_BYTES,
- "offline Codex runtime size is outside the allowed range"
- );
- let metadata = fs::metadata(path)?;
- ensure!(
- metadata.len() == expected_size,
- "offline Codex runtime length does not match trusted metadata"
- );
- let mut file = fs::File::open(path)?;
- let mut hasher = Sha256::new();
- let mut buffer = [0_u8; 64 * 1024];
- loop {
- let read = file.read(&mut buffer)?;
- if read == 0 {
- break;
- }
- hasher.update(&buffer[..read]);
- }
- let actual: [u8; 32] = hasher.finalize().into();
- ensure!(
- actual == expected_digest,
- "offline Codex runtime SHA-256 does not match trusted metadata"
- );
- Ok(())
-}
-
-fn install_runtime_archive(root: &Path, archive_path: &Path) -> Result<()> {
- let parent = root.parent().context("Worker runtime path has no parent")?;
- fs::create_dir_all(parent)?;
- fs::set_permissions(parent, fs::Permissions::from_mode(0o700))?;
- let staging = tempfile::Builder::new()
- .prefix(".codex-runtime-")
- .tempdir_in(parent)?;
- let archive = fs::File::open(archive_path)?;
- let mut archive = tar::Archive::new(GzDecoder::new(archive));
- let mut unpacked = 0_u64;
- for entry in archive.entries()? {
- let mut entry = entry?;
- unpacked = unpacked
- .checked_add(entry.size())
- .context("Codex runtime unpacked size overflowed")?;
- ensure!(
- unpacked <= MAX_RUNTIME_UNPACKED_BYTES,
- "Codex runtime expands beyond its size limit"
- );
- ensure!(
- entry.unpack_in(staging.path())?,
- "Codex runtime contains an unsafe path"
- );
- }
- sanitize_permissions(staging.path())?;
- validate_runtime(staging.path(), &staging.path().join(".venv/bin/python"))?;
-
- if root.exists() {
- let metadata = fs::symlink_metadata(root)?;
- ensure!(metadata.is_dir(), "Worker runtime path is not a directory");
- fs::remove_dir_all(root)?;
- }
- let staged_path = staging.keep();
- fs::rename(&staged_path, root).inspect_err(|_| {
- let _ = fs::remove_dir_all(&staged_path);
- })?;
- Ok(())
-}
-
-fn sanitize_permissions(path: &Path) -> Result<()> {
- for entry in fs::read_dir(path)? {
- let entry = entry?;
- let path = entry.path();
- let metadata = fs::symlink_metadata(&path)?;
- if metadata.file_type().is_symlink() {
- continue;
- }
- if metadata.is_dir() {
- fs::set_permissions(&path, fs::Permissions::from_mode(0o700))?;
- sanitize_permissions(&path)?;
- } else if metadata.is_file() {
- let mode = if metadata.permissions().mode() & 0o111 == 0 {
- 0o600
- } else {
- 0o700
- };
- fs::set_permissions(&path, fs::Permissions::from_mode(mode))?;
- }
- }
- Ok(())
-}
-
-fn validate_runtime(root: &Path, python: &Path) -> Result<()> {
- ensure!(python.is_file(), "managed Codex runtime omitted Python");
- let status = Command::new(python)
- .arg("-c")
- .arg(format!(
- "import importlib.metadata as m,sys; assert sys.version_info[:2] == (3, 12); assert m.version('openai-codex') == '{CODEX_SDK_VERSION}'; assert m.version('farhelm-worker-codex') == '{}'; import openai_codex,farhelm_worker_codex",
- farhelm_core::PRODUCT_VERSION
- ))
- .env("PYTHONPATH", root.join("src"))
- .status()
- .context("failed to execute managed Codex runtime")?;
- ensure!(
- status.success(),
- "managed Codex runtime failed version or import validation"
- );
- Ok(())
-}
-
-fn parse_sha256(value: &str) -> Result<[u8; 32]> {
- let value = value.strip_prefix("sha256:").unwrap_or(value);
- if value.len() != 64 {
- bail!("FARHELM_CODEX_RUNTIME_SHA256 must contain 64 hexadecimal characters");
- }
- let mut digest = [0_u8; 32];
- for (index, pair) in value.as_bytes().as_chunks::<2>().0.iter().enumerate() {
- digest[index] = u8::from_str_radix(std::str::from_utf8(pair)?, 16)
- .context("FARHELM_CODEX_RUNTIME_SHA256 is invalid")?;
- }
- Ok(digest)
-}
-
-#[cfg(test)]
-mod tests {
- use super::*;
-
- #[test]
- fn embedded_worker_can_be_materialized() {
- let directory = tempfile::tempdir().unwrap();
- materialize_worker(directory.path()).unwrap();
- assert!(
- directory
- .path()
- .join("src/farhelm_worker_codex/__main__.py")
- .is_file()
- );
- assert!(directory.path().join("uv.lock").is_file());
- assert!(directory.path().join("README.md").is_file());
- }
-
- #[test]
- fn offline_digest_parser_is_strict() {
- assert_eq!(parse_sha256(&"ab".repeat(32)).unwrap(), [0xab; 32]);
- assert!(parse_sha256("ab").is_err());
- assert!(parse_sha256(&"zz".repeat(32)).is_err());
- }
-}
diff --git a/farhelm-agent/src/runtime_tasks.rs b/farhelm-agent/src/runtime_tasks.rs
index 63a8087..8ec63c4 100644
--- a/farhelm-agent/src/runtime_tasks.rs
+++ b/farhelm-agent/src/runtime_tasks.rs
@@ -1,7 +1,7 @@
//! Fixed service lanes and bounded execution workers keep SQLite off the I/O executor.
use std::{
future::Future,
- sync::{Arc, Mutex},
+ sync::{Arc, LazyLock, Mutex},
};
use tokio::{
sync::{OwnedSemaphorePermit, Semaphore, watch},
@@ -35,14 +35,11 @@ impl RuntimeTasks {
pub fn spawn(&self, future: impl Future + Send + 'static) {
let mut stop = self.stop.subscribe();
- let runtime = tokio::runtime::Handle::current();
- let handle = tokio::task::spawn_blocking(move || {
- runtime.block_on(async move {
- if *stop.borrow() {
- return;
- }
- tokio::select! { () = future => {}, _ = stop.changed() => {} }
- })
+ let handle = tokio::spawn(async move {
+ if *stop.borrow() {
+ return;
+ }
+ tokio::select! { () = future => {}, _ = stop.changed() => {} }
});
let mut handles = self.handles.lock().expect("runtime task registry poisoned");
handles.retain(|handle| !handle.is_finished());
@@ -59,6 +56,92 @@ impl RuntimeTasks {
}
}
+static DATABASE_PERMITS: LazyLock> = LazyLock::new(|| Arc::new(Semaphore::new(8)));
+/// Only synchronous work occupies this bounded executor, never network futures.
+pub async fn blocking(
+ work: impl FnOnce() -> anyhow::Result + Send + 'static,
+) -> anyhow::Result {
+ let permit = DATABASE_PERMITS.clone().acquire_owned().await?;
+ tokio::task::spawn_blocking(move || {
+ let _permit = permit;
+ work()
+ })
+ .await?
+}
+
+pub async fn watch_database(path: std::path::PathBuf, wake: Arc) {
+ use std::os::fd::{AsRawFd, FromRawFd, OwnedFd};
+ let Some(parent) = path.parent() else { return };
+ let parent = if parent.as_os_str().is_empty() {
+ std::path::Path::new(".")
+ } else {
+ parent
+ };
+ let Ok(name) = std::ffi::CString::new(parent.as_os_str().as_encoded_bytes()) else {
+ return;
+ };
+ let raw = unsafe { libc::inotify_init1(libc::IN_NONBLOCK | libc::IN_CLOEXEC) };
+ if raw < 0 {
+ return;
+ }
+ let owned = unsafe { OwnedFd::from_raw_fd(raw) };
+ if unsafe {
+ libc::inotify_add_watch(
+ raw,
+ name.as_ptr(),
+ libc::IN_MODIFY | libc::IN_CLOSE_WRITE | libc::IN_CREATE,
+ )
+ } < 0
+ {
+ return;
+ }
+ let Ok(fd) = tokio::io::unix::AsyncFd::new(owned) else {
+ return;
+ };
+ let filename = path.file_name().unwrap_or_default().as_encoded_bytes();
+ loop {
+ let Ok(mut ready) = fd.readable().await else {
+ return;
+ };
+ let read = ready.try_io(|fd| {
+ let mut bytes = [0u8; 8192];
+ let size = unsafe {
+ libc::read(
+ fd.get_ref().as_raw_fd(),
+ bytes.as_mut_ptr().cast(),
+ bytes.len(),
+ )
+ };
+ if size < 0 {
+ return Err(std::io::Error::last_os_error());
+ }
+ let mut offset = 0;
+ let mut changed = false;
+ while offset + 16 <= size as usize {
+ let len = u32::from_ne_bytes(
+ bytes[offset + 12..offset + 16]
+ .try_into()
+ .expect("inotify name length"),
+ ) as usize;
+ if offset + 16 + len > size as usize {
+ break;
+ }
+ let name = &bytes[offset + 16..offset + 16 + len];
+ let name = &name[..name.iter().position(|b| *b == 0).unwrap_or(name.len())];
+ changed |=
+ name == filename || (name.starts_with(filename) && name.ends_with(b"-wal"));
+ offset += 16 + len;
+ }
+ Ok(changed)
+ });
+ match read {
+ Ok(Ok(true)) => wake.notify_one(),
+ Ok(Err(_)) => return,
+ _ => {}
+ }
+ }
+}
+
#[cfg(test)]
mod tests {
use super::*;
@@ -74,7 +157,11 @@ mod tests {
let (ready, started) = tokio::sync::oneshot::channel();
tasks.spawn(async move {
let _ = ready.send(());
- std::thread::sleep(Duration::from_millis(150));
+ let _ = blocking(|| {
+ std::thread::sleep(Duration::from_millis(150));
+ Ok(())
+ })
+ .await;
});
started.await.unwrap();
let (done, receipt) = tokio::sync::oneshot::channel();
diff --git a/farhelm-console/e2e/codex.spec.ts b/farhelm-console/e2e/codex.spec.ts
index 4973173..8e3e232 100644
--- a/farhelm-console/e2e/codex.spec.ts
+++ b/farhelm-console/e2e/codex.spec.ts
@@ -111,6 +111,7 @@ test('history recovers when Agent heartbeat follows Hub restart', async ({ page
await page.goto('/codex?session=ses-a')
await expect(page.getByText('Agent 离线,暂时无法读取历史')).toBeVisible()
model.historyStatus = 200
+ await emit(page, 'agent.status', { agent_id: 'gpu-a', hostname: 'gpu-a', online: true, codex: { state: 'ready' } })
await expect(page.locator('.markdown-table')).toHaveCount(1, { timeout: 10000 })
await expect(page.getByText('Agent 离线,暂时无法读取历史')).toHaveCount(0)
expect(model.historyReads).toBeLessThanOrEqual(4)
@@ -127,10 +128,10 @@ test('session metadata arriving after its creation receipt retries initial histo
})
for (const cancelled of [false, true]) test(`created session follows its receipt; dialog cancelled=${cancelled}`, async ({ page }) => {
- const model = await setup(page); let ready = false; let reads = 0; let completed = false
+ const model = await setup(page); let ready = false; let reads = 0
await page.route('**/projects', (route) => route.fulfill({ json: { protocol: 'farhelm/1', projects: [{ candidate_id: 'project-a', agent_id: 'gpu-a', suggested_project_id: 'cc08', display_name: '训练项目', state: 'approved' }] } }))
await page.route('**/codex/sessions', (route) => route.request().method() === 'POST' ? route.fulfill({ json: { command_id: 'create-receipt', state: 'accepted' } }) : route.fallback())
- await page.route('**/commands/create-receipt', (route) => { reads++; completed ||= ready; return route.fulfill({ json: { command_id: 'create-receipt', state: ready ? 'completed' : 'running', data: ready ? { session_id: 'ses-created' } : undefined } }) })
+ await page.route('**/commands/create-receipt', (route) => { reads++; return route.fulfill({ json: { command_id: 'create-receipt', state: ready ? 'completed' : 'running', data: ready ? { session_id: 'ses-created' } : undefined } }) })
await page.route('**/ses-created/transcript?*', (route) => route.fulfill({ json: { session_id: 'ses-created', turns: [] } }))
await page.goto('/codex?session=ses-a'); await expect(page.locator('.conversation-title')).toContainText('训练结果分析')
await page.getByLabel('给 Codex 发送指令').fill('原会话草稿')
@@ -140,7 +141,8 @@ for (const cancelled of [false, true]) test(`created session follows its receipt
await page.getByRole('button', { name: /创\s*建/, exact: true }).click(); await expect.poll(() => reads).toBeGreaterThan(0)
if (cancelled) { const dialog = page.getByRole('dialog', { name: '创建 Codex 会话' }); await dialog.getByRole('button', { name: 'Close', exact: true }).press('Enter'); await expect(dialog).toBeHidden() }
model.sessions.push({ ...session('ses-created'), title: '新建验收会话' }); ready = true
- await expect.poll(() => completed).toBe(true)
+ await emit(page, 'command.updated', { command_id: 'create-receipt', state: 'completed', data: { session_id: 'ses-created' } })
+ expect(reads).toBe(1)
if (cancelled) { await expect(page).toHaveURL(/session=ses-a/); await expect(page.getByLabel('给 Codex 发送指令')).toHaveValue('原会话草稿') }
else { await expect(page).toHaveURL(/session=ses-created/); await expect(page.locator('.conversation-title')).toContainText('新建验收会话'); await expect(page.getByLabel('给 Codex 发送指令')).toHaveValue('') }
})
@@ -209,3 +211,85 @@ test('one MiB message resumes separately from earlier turns without duplicate te
expect(await page.locator('.markdown-body').textContent()).toBe(text.trimEnd())
await expect(page.getByLabel('给 Codex 发送指令')).toHaveValue('大消息解析期间仍可输入'); await composerFits(page)
})
+
+test('2000 turns load through every page and retain the visible anchor when virtualization starts', async ({ page }, info) => {
+ test.skip(info.project.name !== 'desktop', 'Long-history acceptance runs once')
+ test.setTimeout(90000)
+ await setup(page)
+ const cursors: number[] = []
+ await page.route('**/ses-a/transcript*', (route) => {
+ const offset = Number(new URL(route.request().url()).searchParams.get('cursor') ?? 0); cursors.push(offset)
+ return route.fulfill({ json: { session_id: 'ses-a', turns: Array.from({ length: 20 }, (_, index) => {
+ const number = 1999 - offset - index
+ return { turn_id: `long-${number}`, status: 'completed', items: [{ item_id: 'user', kind: 'user_message', text: `合成问题 ${number}` }, { item_id: 'assistant', kind: 'assistant_message', text: `合成回复 ${number}` }] }
+ }), next_cursor: offset < 1980 ? String(offset + 20) : null } })
+ })
+ await page.goto('/codex?session=ses-a')
+ await expect(page.locator('[data-turn-id="long-1999"]')).toBeVisible()
+ for (let index = 1; index < 100; index++) {
+ const button = page.getByRole('button', { name: '加载更早对话' })
+ await button.scrollIntoViewIfNeeded()
+ const previous = await page.locator('.conversation-scroll').evaluate((node) => {
+ const top = node.getBoundingClientRect().top
+ const first = [...node.querySelectorAll('[data-message-key]')].find((item) => item.getBoundingClientRect().bottom > top)
+ return first && { key: first.dataset.messageKey, offset: first.getBoundingClientRect().top - top }
+ })
+ await button.click()
+ await expect.poll(() => cursors.length).toBe(index + 1)
+ await expect(page.locator('.conversation-scroll [aria-busy="true"]')).toHaveCount(0)
+ if (index < 99) await expect(button).toBeEnabled()
+ if (index === 2 && previous) await expect.poll(async () => {
+ return page.locator('.conversation-scroll').evaluate((node, old) => {
+ const item = node.querySelector(`[data-message-key="${CSS.escape(old.key!)}"]`)
+ return item ? Math.abs(item.getBoundingClientRect().top - node.getBoundingClientRect().top - old.offset) : 99999
+ }, previous)
+ }).toBeLessThan(5)
+ }
+ expect(new Set(cursors).size).toBe(100)
+ await expect(page.getByRole('button', { name: '加载更早对话' })).toHaveCount(0)
+ await page.locator('.conversation-scroll').evaluate((node) => { node.scrollTop = 0 })
+ await expect(page.locator('[data-turn-id="long-0"]')).toBeVisible()
+ expect(await page.locator('.codex-turn').count()).toBeLessThan(35)
+ await page.getByLabel('给 Codex 发送指令').fill('长历史中仍可输入中文🙂')
+ await page.getByRole('button', { name: /回到底部/ }).click()
+ await expect(page.locator('[data-turn-id="long-1999"]')).toBeVisible()
+ await composerFits(page)
+})
+
+test('Enter submits identical prompts with independent identities and no command polling', async ({ page }, info) => {
+ test.skip(info.project.name !== 'desktop', 'Submission acceptance runs once')
+ await setup(page)
+ const identities: string[] = []; let statusReads = 0
+ await page.route('**/commands/*', (route) => { statusReads++; return route.fulfill({ json: { state: 'completed' } }) })
+ await page.route('**/ses-a/messages', (route) => {
+ identities.push(route.request().headers()['idempotency-key'])
+ return route.fulfill({ json: { command_id: `enter-${identities.length}`, state: 'accepted' } })
+ })
+ await page.goto('/codex?session=ses-a')
+ const input = page.getByLabel('给 Codex 发送指令')
+ for (let index = 1; index <= 20; index++) {
+ await input.fill('相同的中文指令'); await input.press('Enter'); await expect(input).toHaveValue('')
+ await emit(page, 'command.updated', { command_id: `enter-${index}`, state: 'completed' })
+ }
+ expect(new Set(identities).size).toBe(20); expect(statusReads).toBe(0)
+})
+
+test('separated terminal events and receipts reconcile each execution once in either order', async ({ page }, info) => {
+ test.skip(info.project.name !== 'desktop', 'Transport reconciliation runs once')
+ const model = await setup(page)
+ await page.goto('/codex?session=ses-a')
+ await expect(page.locator('.markdown-body strong')).toContainText('完整测量')
+ for (const receiptFirst of [false, true]) {
+ const operation = `dedupe-${receiptFirst}`; const turnId = `turn-${operation}`
+ const before = model.historyReads
+ const terminal = () => emit(page, 'codex.turn.completed', { session_id: 'ses-a', operation_id: operation, data: { turn_id: turnId } })
+ const receipt = () => emit(page, 'command.updated', { command_id: operation, state: 'completed', data: { session_id: 'ses-a', turn_id: turnId } })
+ await (receiptFirst ? receipt() : terminal())
+ await expect.poll(() => model.historyReads).toBe(before + 1)
+ // The second event arrives after the first read, beyond the coalescing window.
+ await (receiptFirst ? terminal() : receipt())
+ await terminal(); await receipt()
+ await page.waitForTimeout(180)
+ expect(model.historyReads).toBe(before + 1)
+ }
+})
diff --git a/farhelm-console/e2e/layout.spec.ts b/farhelm-console/e2e/layout.spec.ts
index 390f298..561a7b6 100644
--- a/farhelm-console/e2e/layout.spec.ts
+++ b/farhelm-console/e2e/layout.spec.ts
@@ -113,7 +113,9 @@ test('experiment deep link and Codex manual queue use the mobile-safe workflow',
await route.fulfill({ status: 200, contentType: 'application/json', body: '{}' })
})
await page.goto('/codex?session=ses-a')
- await expect(page.getByRole('button', { name: '复制会话 ID' })).toBeVisible()
+ await page.getByRole('button', { name: '会话操作' }).click()
+ await expect(page.getByRole('menuitem', { name: '复制会话 ID' })).toBeVisible()
+ await page.keyboard.press('Escape')
await expect(page.getByText('结果正常')).toBeVisible()
await page.getByLabel('给 Codex 发送指令').fill('继续分析结果')
await page.getByRole('button', { name: '发送指令' }).click()
@@ -150,7 +152,7 @@ test('browser reminders deduplicate IDs and do not toast restored history', asyn
constructor() { super(); sources.push(this); setTimeout(() => this.dispatchEvent(new Event('open')), 0) }
close() { sources.splice(sources.indexOf(this), 1) }
}
- Object.assign(window, { EventSource: FakeEventSource, emitNotice: (kind: string) => { for (const source of sources) source.dispatchEvent(new MessageEvent(kind, { data: '{}' })) } })
+ Object.assign(window, { EventSource: FakeEventSource, emitNotice: (kind: string) => { for (const source of sources) source.dispatchEvent(new MessageEvent(kind, { data: JSON.stringify({ payload: { id: 2, agent_id: 'gpu-a', category: 'test', state: 'succeeded', title: '新的页面测试', created_at_unix: Math.floor(Date.now() / 1000) } }) })) } })
})
let latest = 1; let lists = 0
await page.route('**/api/v1/notifications?*', (route) => {
@@ -164,26 +166,25 @@ test('browser reminders deduplicate IDs and do not toast restored history', asyn
await expect(page.locator('.ant-notification-notice')).toHaveCount(0)
await page.getByRole('button', { name: '发送页面测试通知' }).click()
const emit = (kind: string) => page.evaluate((event) => { (window as unknown as { emitNotice: (kind: string) => void }).emitNotice(event) }, kind)
- await emit('notification.changed')
+ await emit('notification.created')
await expect(page.locator('.ant-notification-notice')).toHaveCount(1)
await expect(page.locator('.ant-notification-notice')).toContainText('新的页面测试')
- await emit('notification.changed'); await emit('open'); await emit('notification.changed')
+ await emit('notification.created'); await emit('open'); await emit('notification.created')
await expect(page.locator('.ant-notification-notice')).toHaveCount(1)
})
test('switching sessions ignores late history responses', async ({ page }, testInfo) => {
await page.route('**/api/v1/codex/sessions?*', (route) => route.fulfill({ json: { protocol: 'farhelm/1', sessions: ['ses-a', 'ses-b'].map((session_id) => ({ session_id, agent_id: 'gpu-a', project_id: 'cc08', mode: 'inspect', state: 'idle', title: session_id, updated_at_unix: 2000000000 })) } }))
- let release: () => void = () => {}
+ let release: () => void = () => {}; let returned = false
const gate = new Promise((resolve) => { release = resolve })
- await page.route('**/api/v1/codex/sessions/ses-a/transcript?*', async (route) => { await gate; await route.fulfill({ json: { session_id: 'ses-a', turns: [{ turn_id: 'old', status: 'completed', items: [{ item_id: 'old', kind: 'assistant_message', text: '迟到的旧会话正文' }] }] } }) })
+ await page.route('**/api/v1/codex/sessions/ses-a/transcript?*', async (route) => { await gate; await route.fulfill({ json: { session_id: 'ses-a', turns: [{ turn_id: 'old', status: 'completed', items: [{ item_id: 'old', kind: 'assistant_message', text: '迟到的旧会话正文' }] }] } }); returned = true })
await page.route('**/api/v1/codex/sessions/ses-b/transcript?*', (route) => route.fulfill({ json: { session_id: 'ses-b', turns: [{ turn_id: 'new', status: 'completed', items: [{ item_id: 'new', kind: 'assistant_message', text: '当前会话正文' }] }] } }))
await page.goto('/codex?session=ses-a')
if (testInfo.project.name === 'mobile') await page.getByRole('button', { name: '打开会话列表' }).click()
await page.getByRole('button', { name: /ses-b/ }).filter({ visible: true }).click()
await expect(page.getByText('当前会话正文')).toBeVisible()
- const oldResponse = page.waitForResponse((response) => response.url().includes('/ses-a/transcript'))
release()
- await oldResponse
+ await expect.poll(() => returned).toBe(true)
await expect(page.getByText('迟到的旧会话正文')).toHaveCount(0)
await expect(page.getByText('当前会话正文')).toBeVisible()
})
diff --git a/farhelm-console/package.json b/farhelm-console/package.json
index dde6a95..bcd96cb 100644
--- a/farhelm-console/package.json
+++ b/farhelm-console/package.json
@@ -1,7 +1,7 @@
{
"name": "farhelm-console",
"private": true,
- "version": "0.7.1",
+ "version": "0.8.0",
"type": "module",
"packageManager": "pnpm@10.17.1",
"engines": {
@@ -18,6 +18,9 @@
},
"dependencies": {
"@ant-design/icons": "6.3.4",
+ "@ant-design/x": "2.9.0",
+ "@tanstack/react-query": "5.102.8",
+ "@tanstack/react-virtual": "3.14.10",
"antd": "6.6.2",
"hast-util-to-jsx-runtime": "2.3.6",
"highlight.js": "11.12.0",
diff --git a/farhelm-console/pnpm-lock.yaml b/farhelm-console/pnpm-lock.yaml
index 1cf9413..6bf5c54 100644
--- a/farhelm-console/pnpm-lock.yaml
+++ b/farhelm-console/pnpm-lock.yaml
@@ -11,6 +11,15 @@ importers:
'@ant-design/icons':
specifier: 6.3.4
version: 6.3.4(react-dom@19.2.8(react@19.2.8))(react@19.2.8)
+ '@ant-design/x':
+ specifier: 2.9.0
+ version: 2.9.0(antd@6.6.2(react-dom@19.2.8(react@19.2.8))(react@19.2.8))(react-dom@19.2.8(react@19.2.8))(react@19.2.8)
+ '@tanstack/react-query':
+ specifier: 5.102.8
+ version: 5.102.8(react@19.2.8)
+ '@tanstack/react-virtual':
+ specifier: 3.14.10
+ version: 3.14.10(react-dom@19.2.8(react@19.2.8))(react@19.2.8)
antd:
specifier: 6.6.2
version: 6.6.2(react-dom@19.2.8(react@19.2.8))(react@19.2.8)
@@ -167,6 +176,16 @@ packages:
react: ^0.14.0 || ^15.0.1 || ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0
react-dom: ^0.14.0 || ^15.0.1 || ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0
+ '@ant-design/x@2.9.0':
+ resolution: {integrity: sha512-m3N2nprrPG/D0n93B/fBFhjpvSaJAb2IDF6w0rX0ycYXSGxtgrJQvqSI33Jhx6gxUHj495zdIzqA1NMFld9iQA==}
+ peerDependencies:
+ antd: ^6.1.1
+ react: '>=18.0.0'
+ react-dom: '>=18.0.0'
+
+ '@antfu/install-pkg@2.0.1':
+ resolution: {integrity: sha512-iCKVQcIC0e3oDxEfs3SHQGW+ovhBMZmS1TE+bTk50rVyMCBmCfClv7Qi3HQKlumYwvjb/iIMeWCW2i67q6kFfQ==}
+
'@apideck/better-ajv-errors@0.3.7':
resolution: {integrity: sha512-TajUJwGWbDwkCx/CZi7tRE8PVB7simCvKJfHUsSdvps+aTM/PDPP4gkLmKnc+x3CE//y9i/nj74GqdL/hwk7Iw==}
engines: {node: '>=10'}
@@ -690,10 +709,16 @@ packages:
resolution: {integrity: sha512-Vj1jF3cPfxg7OAfoI7QnVKLoILlm2JF9pnVHrX8qx7AHMiYWT+NDAA7jChlNgRS4WTLc/fD1lXLmPixluj+3Gg==}
engines: {node: '>=6.9.0'}
+ '@braintree/sanitize-url@7.1.2':
+ resolution: {integrity: sha512-jigsZK+sMF/cuiB7sERuo9V7N9jx+dhmHHnQyDSVdpZwVutaBu7WvNYqMDLSgFgfB30n452TP3vjDAvFC973mA==}
+
'@bramus/specificity@2.4.2':
resolution: {integrity: sha512-ctxtJ/eA+t+6q2++vj5j7FYX3nRu311q1wfYH3xjlLOsczhlhxAg2FWNUXhpGvAw3BWo1xBcvOV6/YLc2r5FJw==}
hasBin: true
+ '@chevrotain/types@11.1.2':
+ resolution: {integrity: sha512-U+HFai5+zmJCkK86QsaJtoITlboZHBqrVketcO2ROv865xfCMSFpELQoz1GkX5GzME8pTa+3kbKrZHQtI0gdbw==}
+
'@csstools/color-helpers@6.1.1':
resolution: {integrity: sha512-gLNsunvwf3mCi5u5o46/Z/JcJMnhbHSaZ69rkgPzNM3J4s8hWwpPUQB6/tt0EDFyCiWzxANlx+2LJwpYj4zS1w==}
engines: {node: '>=20.19.0'}
@@ -804,6 +829,12 @@ packages:
resolution: {integrity: sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==}
engines: {node: '>=18.18'}
+ '@iconify/types@2.0.0':
+ resolution: {integrity: sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg==}
+
+ '@iconify/utils@3.1.7':
+ resolution: {integrity: sha512-JZHlwdID+dy+lTgbYC8NEC4zeugqeYsc6jewvzb4c58kHauJn+X7rNwQjxz5p2qSjqaEeQoLkCIQ9v/H4PK0/w==}
+
'@isaacs/cliui@9.0.0':
resolution: {integrity: sha512-AokJm4tuBHillT+FpMtxQ60n8ObyXBatq7jD2/JA9dxbDDokKQm8KMht5ibGzLVU9IJDIKK4TPKgMHEYMn3lMg==}
engines: {node: '>=18'}
@@ -827,6 +858,9 @@ packages:
'@jridgewell/trace-mapping@0.3.31':
resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==}
+ '@mermaid-js/parser@1.2.1':
+ resolution: {integrity: sha512-n12NohV3mrUyUL2o93IgG/ifeW9FTyeJn3zDxkhwa8MJ9Fxg3HQMlA3RiGmD/3UnJvheztkjjQAjA2T4LmUcpw==}
+
'@napi-rs/lzma-linux-x64-gnu@1.5.1':
resolution: {integrity: sha512-oTXEIha4SsuXdTA4Iyskj0kpdx2yVXdhd75c2v3xGrHFfVMsbhTPZU/nMPL4sWKo4pBHm3aucLaqGlF696dTyQ==}
engines: {node: ^22.20 || ^24.12 || >=25}
@@ -1394,6 +1428,23 @@ packages:
'@standard-schema/spec@1.1.0':
resolution: {integrity: sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==}
+ '@tanstack/query-core@5.102.8':
+ resolution: {integrity: sha512-ZNjkJ33CqvPNec/6lZBnHqLc3EVGPZ9ySLhYahU9TcuRFdmwXewuj0c4hwSWcGHqEUwcSrKeZ+oGcvPBqXcQcg==}
+
+ '@tanstack/react-query@5.102.8':
+ resolution: {integrity: sha512-TYBea4OuXWD7MhaSHq069TWbFe7rcwWN6kzT7JF0OKi1K6c1gTv2IzD6A6ExJsCMozdkqBWeuIUZmu4KQg0O5A==}
+ peerDependencies:
+ react: ^18 || ^19
+
+ '@tanstack/react-virtual@3.14.10':
+ resolution: {integrity: sha512-SRyoUbdFMRHuYXMijV5H4ZarQWpXkj3iANq8OFre+pybeVap8ZJjZ3Nz9bVjx4d8PfobVUQUdKyyyHYk3E+djw==}
+ peerDependencies:
+ react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0
+ react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0
+
+ '@tanstack/virtual-core@3.17.8':
+ resolution: {integrity: sha512-BfEvehNpOT75r5Ksc5xW6NZuXujTfb7nlSEyVu4XHG3gdxNg1KqXruWbDewXOUaUYIo4oRbSfkjIajz4MAT8tA==}
+
'@testing-library/dom@10.4.1':
resolution: {integrity: sha512-o4PXJQidqJl82ckFaXUeoAW+XysPLauYI43Abki5hABd853iMhitooc6znOnczgbTYmEP6U6/y1ZyKAIsvMKGg==}
engines: {node: '>=18'}
@@ -1439,6 +1490,99 @@ packages:
'@types/chai@5.2.3':
resolution: {integrity: sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==}
+ '@types/d3-array@3.2.2':
+ resolution: {integrity: sha512-hOLWVbm7uRza0BYXpIIW5pxfrKe0W+D5lrFiAEYR+pb6w3N2SwSMaJbXdUfSEv+dT4MfHBLtn5js0LAWaO6otw==}
+
+ '@types/d3-axis@3.0.6':
+ resolution: {integrity: sha512-pYeijfZuBd87T0hGn0FO1vQ/cgLk6E1ALJjfkC0oJ8cbwkZl3TpgS8bVBLZN+2jjGgg38epgxb2zmoGtSfvgMw==}
+
+ '@types/d3-brush@3.0.6':
+ resolution: {integrity: sha512-nH60IZNNxEcrh6L1ZSMNA28rj27ut/2ZmI3r96Zd+1jrZD++zD3LsMIjWlvg4AYrHn/Pqz4CF3veCxGjtbqt7A==}
+
+ '@types/d3-chord@3.0.6':
+ resolution: {integrity: sha512-LFYWWd8nwfwEmTZG9PfQxd17HbNPksHBiJHaKuY1XeqscXacsS2tyoo6OdRsjf+NQYeB6XrNL3a25E3gH69lcg==}
+
+ '@types/d3-color@3.1.3':
+ resolution: {integrity: sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A==}
+
+ '@types/d3-contour@3.0.6':
+ resolution: {integrity: sha512-BjzLgXGnCWjUSYGfH1cpdo41/hgdWETu4YxpezoztawmqsvCeep+8QGfiY6YbDvfgHz/DkjeIkkZVJavB4a3rg==}
+
+ '@types/d3-delaunay@6.0.4':
+ resolution: {integrity: sha512-ZMaSKu4THYCU6sV64Lhg6qjf1orxBthaC161plr5KuPHo3CNm8DTHiLw/5Eq2b6TsNP0W0iJrUOFscY6Q450Hw==}
+
+ '@types/d3-dispatch@3.0.7':
+ resolution: {integrity: sha512-5o9OIAdKkhN1QItV2oqaE5KMIiXAvDWBDPrD85e58Qlz1c1kI/J0NcqbEG88CoTwJrYe7ntUCVfeUl2UJKbWgA==}
+
+ '@types/d3-drag@3.0.7':
+ resolution: {integrity: sha512-HE3jVKlzU9AaMazNufooRJ5ZpWmLIoc90A37WU2JMmeq28w1FQqCZswHZ3xR+SuxYftzHq6WU6KJHvqxKzTxxQ==}
+
+ '@types/d3-dsv@3.0.7':
+ resolution: {integrity: sha512-n6QBF9/+XASqcKK6waudgL0pf/S5XHPPI8APyMLLUHd8NqouBGLsU8MgtO7NINGtPBtk9Kko/W4ea0oAspwh9g==}
+
+ '@types/d3-ease@3.0.2':
+ resolution: {integrity: sha512-NcV1JjO5oDzoK26oMzbILE6HW7uVXOHLQvHshBUW4UMdZGfiY6v5BeQwh9a9tCzv+CeefZQHJt5SRgK154RtiA==}
+
+ '@types/d3-fetch@3.0.7':
+ resolution: {integrity: sha512-fTAfNmxSb9SOWNB9IoG5c8Hg6R+AzUHDRlsXsDZsNp6sxAEOP0tkP3gKkNSO/qmHPoBFTxNrjDprVHDQDvo5aA==}
+
+ '@types/d3-force@3.0.10':
+ resolution: {integrity: sha512-ZYeSaCF3p73RdOKcjj+swRlZfnYpK1EbaDiYICEEp5Q6sUiqFaFQ9qgoshp5CzIyyb/yD09kD9o2zEltCexlgw==}
+
+ '@types/d3-format@3.0.4':
+ resolution: {integrity: sha512-fALi2aI6shfg7vM5KiR1wNJnZ7r6UuggVqtDA+xiEdPZQwy/trcQaHnwShLuLdta2rTymCNpxYTiMZX/e09F4g==}
+
+ '@types/d3-geo@3.1.1':
+ resolution: {integrity: sha512-65Emv9fQiQQqphLlRkuQ5ypPsOmWPhtBGCMv61JDPEPMvsx+gzhGf74yw1a78xFKPj6zw4AgQICJoQv0vK9M2w==}
+
+ '@types/d3-hierarchy@3.1.7':
+ resolution: {integrity: sha512-tJFtNoYBtRtkNysX1Xq4sxtjK8YgoWUNpIiUee0/jHGRwqvzYxkq0hGVbbOGSz+JgFxxRu4K8nb3YpG3CMARtg==}
+
+ '@types/d3-interpolate@3.0.4':
+ resolution: {integrity: sha512-mgLPETlrpVV1YRJIglr4Ez47g7Yxjl1lj7YKsiMCb27VJH9W8NVM6Bb9d8kkpG/uAQS5AmbA48q2IAolKKo1MA==}
+
+ '@types/d3-path@3.1.1':
+ resolution: {integrity: sha512-VMZBYyQvbGmWyWVea0EHs/BwLgxc+MKi1zLDCONksozI4YJMcTt8ZEuIR4Sb1MMTE8MMW49v0IwI5+b7RmfWlg==}
+
+ '@types/d3-polygon@3.0.2':
+ resolution: {integrity: sha512-ZuWOtMaHCkN9xoeEMr1ubW2nGWsp4nIql+OPQRstu4ypeZ+zk3YKqQT0CXVe/PYqrKpZAi+J9mTs05TKwjXSRA==}
+
+ '@types/d3-quadtree@3.0.6':
+ resolution: {integrity: sha512-oUzyO1/Zm6rsxKRHA1vH0NEDG58HrT5icx/azi9MF1TWdtttWl0UIUsjEQBBh+SIkrpd21ZjEv7ptxWys1ncsg==}
+
+ '@types/d3-random@3.0.4':
+ resolution: {integrity: sha512-UHYId5WTCx4L4YNel7NU00XUXXgvgpgZOvp10PuvsQENjMDXhh2RyFc0KBjO7B45ne4Ha1yVH7ii0vnzKkuzWA==}
+
+ '@types/d3-scale-chromatic@3.1.0':
+ resolution: {integrity: sha512-iWMJgwkK7yTRmWqRB5plb1kadXyQ5Sj8V/zYlFGMUBbIPKQScw+Dku9cAAMgJG+z5GYDoMjWGLVOvjghDEFnKQ==}
+
+ '@types/d3-scale@4.0.9':
+ resolution: {integrity: sha512-dLmtwB8zkAeO/juAMfnV+sItKjlsw2lKdZVVy6LRr0cBmegxSABiLEpGVmSJJ8O08i4+sGR6qQtb6WtuwJdvVw==}
+
+ '@types/d3-selection@3.0.11':
+ resolution: {integrity: sha512-bhAXu23DJWsrI45xafYpkQ4NtcKMwWnAC/vKrd2l+nxMFuvOT3XMYTIj2opv8vq8AO5Yh7Qac/nSeP/3zjTK0w==}
+
+ '@types/d3-shape@3.2.0':
+ resolution: {integrity: sha512-kVd74ta9eof3eJOvbNd1vGKS/XERRyQbT26Og63hIsvDO84cjD5gEOhsXf26w3FSoNlPVz84DOFcKv/oou+fMw==}
+
+ '@types/d3-time-format@4.0.3':
+ resolution: {integrity: sha512-5xg9rC+wWL8kdDj153qZcsJ0FWiFt0J5RB6LYUNZjwSnesfblqrI/bJ1wBdJ8OQfncgbJG5+2F+qfqnqyzYxyg==}
+
+ '@types/d3-time@3.0.4':
+ resolution: {integrity: sha512-yuzZug1nkAAaBlBBikKZTgzCeA+k1uy4ZFwWANOfKw5z5LRhV0gNA7gNkKm7HoK+HRN0wX3EkxGk0fpbWhmB7g==}
+
+ '@types/d3-timer@3.0.2':
+ resolution: {integrity: sha512-Ps3T8E8dZDam6fUyNiMkekK3XUsaUEik+idO9/YjPtfj2qruF8tFBXS7XhtE4iIXBLxhmLjP3SXpLhVf21I9Lw==}
+
+ '@types/d3-transition@3.0.9':
+ resolution: {integrity: sha512-uZS5shfxzO3rGlu0cC3bjmMFKsXv+SmZZcgp0KD22ts4uGXp5EVYGzu/0YdwZeKmddhcAccYtREJKkPfXkZuCg==}
+
+ '@types/d3-zoom@3.0.8':
+ resolution: {integrity: sha512-iqMC4/YlFCSlO8+2Ii1GGGliCAY4XdeG748w5vQUbevlbDu0zSjH/+jojorQVBK/se0j6DUFNPBGSqD3YWYnDw==}
+
+ '@types/d3@7.4.3':
+ resolution: {integrity: sha512-lZXZ9ckh5R8uiFVt8ogUNf+pIrK4EsWrx2Np75WvF/eTpJ0FMHNhjXk8CKEx/+gpHbNQyJWehbFaTvqmHWB3ww==}
+
'@types/debug@4.1.13':
resolution: {integrity: sha512-KSVgmQmzMwPlmtljOomayoR89W4FynCAi3E8PPs7vmDVPe84hT+vGPKkJfThkmXs0x0jAaa9U8uW8bbfyS2fWw==}
@@ -1454,6 +1598,9 @@ packages:
'@types/estree@1.0.9':
resolution: {integrity: sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==}
+ '@types/geojson@7946.0.16':
+ resolution: {integrity: sha512-6C8nqWur3j98U6+lXDfTUWIfgvZU+EumvpHKcYjujKH7woYyLj2sUmff0tRhrqM7BohUw7Pz3ZB1jj2gW9Fvmg==}
+
'@types/hast@3.0.5':
resolution: {integrity: sha512-rp/ezSWaD1m44dPKICGhiskI13nVr7qTloFwDa/IYkhhf5nzwP+zIQcIJh3WIFSBOy/H1PzB40jPjMDksN4F+g==}
@@ -1472,6 +1619,9 @@ packages:
'@types/node@24.12.0':
resolution: {integrity: sha512-GYDxsZi3ChgmckRT9HPU0WEhKLP08ev/Yfcq2AstjrDASOYCSXeyjDsHg4v5t4jOj7cyDX3vmprafKlWIG9MXQ==}
+ '@types/prismjs@1.26.6':
+ resolution: {integrity: sha512-vqlvI7qlMvcCBbVe0AKAb4f97//Hy0EBTaiW8AalRnG/xAN5zOiWWyrNqNXeq8+KAuvRewjCVY1+IPxk4RdNYw==}
+
'@types/react-dom@19.2.5':
resolution: {integrity: sha512-fMPwH9v7r/pp43yUd2/Mbiex5KouJwwR3dzHkhLREUC6764VyDsqxhAxv6OFEYR1RhjOyD1naqba8ECDBe7ZQg==}
peerDependencies:
@@ -1554,6 +1704,9 @@ packages:
'@ungap/structured-clone@1.4.0':
resolution: {integrity: sha512-1mEZtMKPM09vDmQt5y7YvmN2+DFTP7Tg0EWXdic8/C6VRnpb33e4ghisCIE3WZjsE2N8mf+QV1Zqh7ZFYLWInQ==}
+ '@upsetjs/venn.js@2.0.0':
+ resolution: {integrity: sha512-WbBhLrooyePuQ1VZxrJjtLvTc4NVfpOyKx0sKqioq9bX1C1m7Jgykkn8gLrtwumBioXIqam8DLxp88Adbue6Hw==}
+
'@vitejs/plugin-react@6.1.1':
resolution: {integrity: sha512-yxLaQV9gkhS8ezJqCM6+ndU7mDY6gqAg75NQ+0IjwEI8IYOmQCgkRwHKVSfWXW076DsqMo0Dk+0FK1U+M5RgFw==}
engines: {node: ^20.19.0 || >=22.12.0}
@@ -1759,6 +1912,10 @@ packages:
commander@2.20.3:
resolution: {integrity: sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==}
+ commander@7.2.0:
+ resolution: {integrity: sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==}
+ engines: {node: '>= 10'}
+
commander@8.3.0:
resolution: {integrity: sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==}
engines: {node: '>= 12'}
@@ -1781,6 +1938,12 @@ packages:
resolution: {integrity: sha512-XGpFGbMLHwSt74YLTKho7Ib242qi6O8MSX+sRokV4oz7iKXvQWGYZthjIhjRGMxjzVkAubBO512dKGYcefmX3Q==}
engines: {node: '>=6.4.0'}
+ cose-base@1.0.3:
+ resolution: {integrity: sha512-s9whTXInMSgAp/NVXVNuVxVKzGH2qck3aQlVHxDCdAEPgtMKwc4Wq6/QKhgdEdgbLSi9rBTAcPoRa6JpiG4ksg==}
+
+ cose-base@2.2.0:
+ resolution: {integrity: sha512-AzlgcsCbUMymkADOJtQm3wO9S3ltPfYOFD5033keQn9NJzIbtnZj+UdBJe7DYml/8TdbtHJW3j58SOnKhWY/5g==}
+
cross-spawn@7.0.6:
resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==}
engines: {node: '>= 8'}
@@ -1799,6 +1962,162 @@ packages:
csstype@3.2.3:
resolution: {integrity: sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==}
+ cytoscape-cose-bilkent@4.1.0:
+ resolution: {integrity: sha512-wgQlVIUJF13Quxiv5e1gstZ08rnZj2XaLHGoFMYXz7SkNfCDOOteKBE6SYRfA9WxxI/iBc3ajfDoc6hb/MRAHQ==}
+ peerDependencies:
+ cytoscape: ^3.2.0
+
+ cytoscape-fcose@2.2.0:
+ resolution: {integrity: sha512-ki1/VuRIHFCzxWNrsshHYPs6L7TvLu3DL+TyIGEsRcvVERmxokbf5Gdk7mFxZnTdiGtnA4cfSmjZJMviqSuZrQ==}
+ peerDependencies:
+ cytoscape: ^3.2.0
+
+ cytoscape@3.34.2:
+ resolution: {integrity: sha512-Cm2jaj1X/PBNlzV9yH8zcfGOxO7U+CJ/+mxSBVPSchLaugdp4jtlGx5qaHtPRZ6tgiZ5P+o1XoRfJA+ba6KM3g==}
+ engines: {node: '>=0.10'}
+
+ d3-array@2.12.1:
+ resolution: {integrity: sha512-B0ErZK/66mHtEsR1TkPEEkwdy+WDesimkM5gpZr5Dsg54BiTA5RXtYW5qTLIAcekaS9xfZrzBLF/OAkB3Qn1YQ==}
+
+ d3-array@3.2.4:
+ resolution: {integrity: sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==}
+ engines: {node: '>=12'}
+
+ d3-axis@3.0.0:
+ resolution: {integrity: sha512-IH5tgjV4jE/GhHkRV0HiVYPDtvfjHQlQfJHs0usq7M30XcSBvOotpmH1IgkcXsO/5gEQZD43B//fc7SRT5S+xw==}
+ engines: {node: '>=12'}
+
+ d3-brush@3.0.0:
+ resolution: {integrity: sha512-ALnjWlVYkXsVIGlOsuWH1+3udkYFI48Ljihfnh8FZPF2QS9o+PzGLBslO0PjzVoHLZ2KCVgAM8NVkXPJB2aNnQ==}
+ engines: {node: '>=12'}
+
+ d3-chord@3.0.1:
+ resolution: {integrity: sha512-VE5S6TNa+j8msksl7HwjxMHDM2yNK3XCkusIlpX5kwauBfXuyLAtNg9jCp/iHH61tgI4sb6R/EIMWCqEIdjT/g==}
+ engines: {node: '>=12'}
+
+ d3-color@3.1.0:
+ resolution: {integrity: sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==}
+ engines: {node: '>=12'}
+
+ d3-contour@4.0.2:
+ resolution: {integrity: sha512-4EzFTRIikzs47RGmdxbeUvLWtGedDUNkTcmzoeyg4sP/dvCexO47AaQL7VKy/gul85TOxw+IBgA8US2xwbToNA==}
+ engines: {node: '>=12'}
+
+ d3-delaunay@6.0.4:
+ resolution: {integrity: sha512-mdjtIZ1XLAM8bm/hx3WwjfHt6Sggek7qH043O8KEjDXN40xi3vx/6pYSVTwLjEgiXQTbvaouWKynLBiUZ6SK6A==}
+ engines: {node: '>=12'}
+
+ d3-dispatch@3.0.1:
+ resolution: {integrity: sha512-rzUyPU/S7rwUflMyLc1ETDeBj0NRuHKKAcvukozwhshr6g6c5d8zh4c2gQjY2bZ0dXeGLWc1PF174P2tVvKhfg==}
+ engines: {node: '>=12'}
+
+ d3-drag@3.0.0:
+ resolution: {integrity: sha512-pWbUJLdETVA8lQNJecMxoXfH6x+mO2UQo8rSmZ+QqxcbyA3hfeprFgIT//HW2nlHChWeIIMwS2Fq+gEARkhTkg==}
+ engines: {node: '>=12'}
+
+ d3-dsv@3.0.1:
+ resolution: {integrity: sha512-UG6OvdI5afDIFP9w4G0mNq50dSOsXHJaRE8arAS5o9ApWnIElp8GZw1Dun8vP8OyHOZ/QJUKUJwxiiCCnUwm+Q==}
+ engines: {node: '>=12'}
+ hasBin: true
+
+ d3-ease@3.0.1:
+ resolution: {integrity: sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==}
+ engines: {node: '>=12'}
+
+ d3-fetch@3.0.1:
+ resolution: {integrity: sha512-kpkQIM20n3oLVBKGg6oHrUchHM3xODkTzjMoj7aWQFq5QEM+R6E4WkzT5+tojDY7yjez8KgCBRoj4aEr99Fdqw==}
+ engines: {node: '>=12'}
+
+ d3-force@3.0.0:
+ resolution: {integrity: sha512-zxV/SsA+U4yte8051P4ECydjD/S+qeYtnaIyAs9tgHCqfguma/aAQDjo85A9Z6EKhBirHRJHXIgJUlffT4wdLg==}
+ engines: {node: '>=12'}
+
+ d3-format@3.1.2:
+ resolution: {integrity: sha512-AJDdYOdnyRDV5b6ArilzCPPwc1ejkHcoyFarqlPqT7zRYjhavcT3uSrqcMvsgh2CgoPbK3RCwyHaVyxYcP2Arg==}
+ engines: {node: '>=12'}
+
+ d3-geo@3.1.1:
+ resolution: {integrity: sha512-637ln3gXKXOwhalDzinUgY83KzNWZRKbYubaG+fGVuc/dxO64RRljtCTnf5ecMyE1RIdtqpkVcq0IbtU2S8j2Q==}
+ engines: {node: '>=12'}
+
+ d3-hierarchy@3.1.2:
+ resolution: {integrity: sha512-FX/9frcub54beBdugHjDCdikxThEqjnR93Qt7PvQTOHxyiNCAlvMrHhclk3cD5VeAaq9fxmfRp+CnWw9rEMBuA==}
+ engines: {node: '>=12'}
+
+ d3-interpolate@3.0.1:
+ resolution: {integrity: sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==}
+ engines: {node: '>=12'}
+
+ d3-path@1.0.9:
+ resolution: {integrity: sha512-VLaYcn81dtHVTjEHd8B+pbe9yHWpXKZUC87PzoFmsFrJqgFwDe/qxfp5MlfsfM1V5E/iVt0MmEbWQ7FVIXh/bg==}
+
+ d3-path@3.1.0:
+ resolution: {integrity: sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ==}
+ engines: {node: '>=12'}
+
+ d3-polygon@3.0.1:
+ resolution: {integrity: sha512-3vbA7vXYwfe1SYhED++fPUQlWSYTTGmFmQiany/gdbiWgU/iEyQzyymwL9SkJjFFuCS4902BSzewVGsHHmHtXg==}
+ engines: {node: '>=12'}
+
+ d3-quadtree@3.0.1:
+ resolution: {integrity: sha512-04xDrxQTDTCFwP5H6hRhsRcb9xxv2RzkcsygFzmkSIOJy3PeRJP7sNk3VRIbKXcog561P9oU0/rVH6vDROAgUw==}
+ engines: {node: '>=12'}
+
+ d3-random@3.0.1:
+ resolution: {integrity: sha512-FXMe9GfxTxqd5D6jFsQ+DJ8BJS4E/fT5mqqdjovykEB2oFbTMDVdg1MGFxfQW+FBOGoB++k8swBrgwSHT1cUXQ==}
+ engines: {node: '>=12'}
+
+ d3-sankey@0.12.3:
+ resolution: {integrity: sha512-nQhsBRmM19Ax5xEIPLMY9ZmJ/cDvd1BG3UVvt5h3WRxKg5zGRbvnteTyWAbzeSvlh3tW7ZEmq4VwR5mB3tutmQ==}
+
+ d3-scale-chromatic@3.1.0:
+ resolution: {integrity: sha512-A3s5PWiZ9YCXFye1o246KoscMWqf8BsD9eRiJ3He7C9OBaxKhAd5TFCdEx/7VbKtxxTsu//1mMJFrEt572cEyQ==}
+ engines: {node: '>=12'}
+
+ d3-scale@4.0.2:
+ resolution: {integrity: sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==}
+ engines: {node: '>=12'}
+
+ d3-selection@3.0.0:
+ resolution: {integrity: sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==}
+ engines: {node: '>=12'}
+
+ d3-shape@1.3.7:
+ resolution: {integrity: sha512-EUkvKjqPFUAZyOlhY5gzCxCeI0Aep04LwIRpsZ/mLFelJiUfnK56jo5JMDSE7yyP2kLSb6LtF+S5chMk7uqPqw==}
+
+ d3-shape@3.2.0:
+ resolution: {integrity: sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==}
+ engines: {node: '>=12'}
+
+ d3-time-format@4.1.0:
+ resolution: {integrity: sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==}
+ engines: {node: '>=12'}
+
+ d3-time@3.1.0:
+ resolution: {integrity: sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q==}
+ engines: {node: '>=12'}
+
+ d3-timer@3.0.1:
+ resolution: {integrity: sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==}
+ engines: {node: '>=12'}
+
+ d3-transition@3.0.1:
+ resolution: {integrity: sha512-ApKvfjsSR6tg06xrL434C0WydLr7JewBB3V+/39RMHsaXTOG0zmt/OAXeng5M5LBm0ojmxJrpomQVZ1aPvBL4w==}
+ engines: {node: '>=12'}
+ peerDependencies:
+ d3-selection: 2 - 3
+
+ d3-zoom@3.0.0:
+ resolution: {integrity: sha512-b8AmV3kfQaqWAuacbPuNbL6vahnOJflOhexLzMMNLga62+/nh0JzvJ0aO/5a5MVgUFGS7Hu1P9P03o3fJkDCyw==}
+ engines: {node: '>=12'}
+
+ d3@7.9.0:
+ resolution: {integrity: sha512-e1U46jVP+w7Iut8Jt8ri1YsPOvFpg46k+K8TpCb0P+zjCkjkPnV7WzfDJzMHy1LnA+wj5pLT1wjO901gLXeEhA==}
+ engines: {node: '>=12'}
+
+ dagre-d3-es@7.0.14:
+ resolution: {integrity: sha512-P4rFMVq9ESWqmOgK+dlXvOtLwYg0i7u0HBGJER0LZDJT2VHIPAMZ/riPxqJceWMStH5+E61QxFra9kIS3AqdMg==}
+
data-urls@7.0.0:
resolution: {integrity: sha512-23XHcCF+coGYevirZceTVD7NdJOqVn+49IHyxgszm+JIiHLoB2TkmPtsYkNWT1pvRSGkc35L6NHs0yHkN2SumA==}
engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0}
@@ -1848,6 +2167,9 @@ packages:
resolution: {integrity: sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==}
engines: {node: '>= 0.4'}
+ delaunator@5.1.0:
+ resolution: {integrity: sha512-AGrQ4QSgssa1NGmWmLPqN5NY2KajF5MqxetNEO+o0n3ZwZZeTmt7bBnvzHWrmkZFxGgr4HdyFgelzgi06otLuQ==}
+
dequal@2.0.3:
resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==}
engines: {node: '>=6'}
@@ -1865,6 +2187,9 @@ packages:
dom-accessibility-api@0.6.3:
resolution: {integrity: sha512-7ZgogeTnjuHbo+ct10G9Ffp0mif17idi0IyWNVA/wcwcm7NPOD/WEHVP3n7n3MhXqxoIYm8d6MuZohYWIZ4T3w==}
+ dompurify@3.4.15:
+ resolution: {integrity: sha512-EUBjM+B+lkDE41iE82DDSCfkoPGfXx8IxFxPMjNzm/Uk4xDet77rTN9wqlxlVg71kK7XGuUMv6wUxJUwwv+Xyw==}
+
dunder-proto@1.0.1:
resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==}
engines: {node: '>= 0.4'}
@@ -1916,6 +2241,9 @@ packages:
resolution: {integrity: sha512-yPDz7wqpg1/mmHLmS3tcfTfbw5f1eryXvyghYBffGdERwe+mV7ZcWzTR8LR17Kvqt3qfPurjlonmnq3MKXIOXw==}
engines: {node: '>= 0.4'}
+ es-toolkit@1.52.0:
+ resolution: {integrity: sha512-XTNEJQh1tY1ZJVcf6ayP/2n4ZPyaHlW2FWs7xvw5ddPuhUVjLD3olQVQS7kf58JbAB48iL0uL/jerTrjtV3lDA==}
+
escalade@3.2.0:
resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==}
engines: {node: '>=6'}
@@ -2013,6 +2341,12 @@ packages:
fast-uri@3.1.7:
resolution: {integrity: sha512-dOvZVzjdZdz7phd9v6jCbwxrBW3fK6n8Rc0CtdmM4bumzMnxywBYhuph6J819RRw/ku+rLbelwfMunktuzVVHg==}
+ fastdom@1.0.12:
+ resolution: {integrity: sha512-LB+xjSTEbjHE1cWsxu+tN2Xqr1kpi+V9aADI7sVM5ZMaXyYGPHULQMzpJMYqOTULK/73pUkWVzzObFRBkPr+hg==}
+
+ fault@1.0.4:
+ resolution: {integrity: sha512-CJ0HCB5tL5fYTEA7ToAq5+kTwd++Borf1/bifxd9iT70QcXr4MRrO3Llf8Ifs70q+SJcGHFtnIE/Nw6giCtECA==}
+
fdir@6.5.0:
resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==}
engines: {node: '>=12.0.0'}
@@ -2048,6 +2382,10 @@ packages:
resolution: {integrity: sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==}
engines: {node: '>=14'}
+ format@0.2.2:
+ resolution: {integrity: sha512-wzsgA6WOq+09wrU1tsJ09udeR/YZRaeArL9e1wPbFg3GG2yDnC2ldKpxs4xunpFF9DgqCqOIra3bc1HWrJ37Ww==}
+ engines: {node: '>=0.4.x'}
+
fs-extra@9.1.0:
resolution: {integrity: sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==}
engines: {node: '>=10'}
@@ -2120,6 +2458,9 @@ packages:
graceful-fs@4.2.11:
resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==}
+ hachure-fill@0.5.2:
+ resolution: {integrity: sha512-3GKBOn+m2LX9iq+JC1064cSFprJY4jL1jCXTcpnfER5HYE2l/4EfWSGzkPa/ZDBmYI0ZOEj5VHV/eKnPGkHuOg==}
+
has-bigints@1.1.0:
resolution: {integrity: sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg==}
engines: {node: '>= 0.4'}
@@ -2179,10 +2520,16 @@ packages:
hermes-parser@0.25.1:
resolution: {integrity: sha512-6pEjquH3rqaI6cYAXYPcz9MS4rY6R4ngRgrgfDshRptUZIc3lw0MCIJIGDj9++mfySOuPTHB4nrSW99BCvOPIA==}
+ highlight.js@10.7.3:
+ resolution: {integrity: sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A==}
+
highlight.js@11.12.0:
resolution: {integrity: sha512-nbfWpyRMcMrPMmDwJB+dhX/eiaPKtc2RB+0QZskqJ3WjRA/FDS0e9hZrx8EC/lbEv8gXy98FcDbNa/dspAaJMg==}
engines: {node: '>=12.0.0'}
+ highlightjs-vue@1.0.0:
+ resolution: {integrity: sha512-PDEfEF102G23vHmPhLyPboFCD+BkMGu+GuJe2d9/eH4FsCwvgBpnc9n0pGE+ffKdph38s6foEZiEjdgHdzp+IA==}
+
html-encoding-sniffer@6.0.0:
resolution: {integrity: sha512-CV9TW3Y3f8/wT0BRFc1/KAVQ3TUHiXmaAb6VW9vtiMFf7SLoMd1PdAc4W3KFOFETBJUb90KatHqlsZMWV+R9Gg==}
engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0}
@@ -2190,6 +2537,10 @@ packages:
html-url-attributes@3.0.1:
resolution: {integrity: sha512-ol6UPyBWqsrO6EJySPz2O7ZSr856WDrEzM5zMqp+FJJLGMW35cLYmmZnl0vztAZxRUoNZJFTCohfjuIJ8I4QBQ==}
+ iconv-lite@0.6.3:
+ resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==}
+ engines: {node: '>=0.10.0'}
+
idb@7.1.1:
resolution: {integrity: sha512-gchesWBzyvGHRO9W8tzUWFDycow5gwjvFKfyV9FF32Y7F50yZMp7mP+T2mJIWFx49zicqyC4uefHM17o6xKIVQ==}
@@ -2201,6 +2552,9 @@ packages:
resolution: {integrity: sha512-YYNsSlXBjMk92SKnkwvB5LOVSa6OznlFUGcsvrFgNJbJCd0M1XKeFVRc8ZByeCqz32FivYNHJVooLmdqrmvp/Q==}
engines: {node: '>= 4'}
+ import-meta-resolve@4.2.0:
+ resolution: {integrity: sha512-Iqv2fzaTQN28s/FwZAoFq0ZSs/7hMAHJVX+w8PZl3cY19Pxk6jFFalxQoIfW2826i/fDLXv8IiEZRIT0lDuWcg==}
+
imurmurhash@0.1.4:
resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==}
engines: {node: '>=0.8.19'}
@@ -2216,6 +2570,13 @@ packages:
resolution: {integrity: sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==}
engines: {node: '>= 0.4'}
+ internmap@1.0.1:
+ resolution: {integrity: sha512-lDB5YccMydFBtasVtxnZ3MRBHuaoE8GKsppq+EchKL2U4nK/DmEpPHNH8MZe5HkMtpSiTSOZwfN0tzYjO/lJEw==}
+
+ internmap@2.0.3:
+ resolution: {integrity: sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==}
+ engines: {node: '>=12'}
+
is-alphabetical@2.0.1:
resolution: {integrity: sha512-FWyyY60MeTNyeSRpkM2Iry0G9hpr7/9kD40mD/cGQEuilcZYS4okz8SN2Q6rLCJ8gbCt6fN+rC+6tMGS99LaxQ==}
@@ -2419,6 +2780,15 @@ packages:
keyv@4.5.4:
resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==}
+ khroma@2.1.0:
+ resolution: {integrity: sha512-Ls993zuzfayK269Svk9hzpeGUKob/sIgZzyHYdjQoAdQetRKpOLj+k/QQQ/6Qi0Yz65mlROrfd+Ev+1+7dz9Kw==}
+
+ layout-base@1.0.2:
+ resolution: {integrity: sha512-8h2oVEZNktL4BH2JCOI90iD1yXwL6iNW7KcCKT2QZgQJR2vbqDsldCTPRU9NifTCqHZci57XvQQ15YTu+sTYPg==}
+
+ layout-base@2.0.1:
+ resolution: {integrity: sha512-dp3s92+uNI1hWIpPGH3jK2kxE2lMjdXdr+DH8ynZHpd6PUlH6x6cbuXnoMmiNumznqaNO31xu9e79F0uuZ0JFg==}
+
leven@3.1.0:
resolution: {integrity: sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==}
engines: {node: '>=6'}
@@ -2501,12 +2871,21 @@ packages:
resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==}
engines: {node: '>=10'}
+ lodash-es@4.18.1:
+ resolution: {integrity: sha512-J8xewKD/Gk22OZbhpOVSwcs60zhd95ESDwezOFuA3/099925PdHJ7OFHNTGtajL3AlZkykD32HykiMo+BIBI8A==}
+
lodash.debounce@4.0.8:
resolution: {integrity: sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==}
+ lodash.throttle@4.1.1:
+ resolution: {integrity: sha512-wIkUCfVKpVsWo3JSZlc+8MB5it+2AN5W8J7YVMST30UrvcQNZ1Okbj+rbVniijTWE6FGYy4XJq/rHkas8qJMLQ==}
+
longest-streak@3.1.0:
resolution: {integrity: sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g==}
+ lowlight@1.20.0:
+ resolution: {integrity: sha512-8Ktj+prEb1RoCPkEOrPMYUN/nCggB7qAWe3a7OpMjWQkh3l2RD5wKRQ+o8Q8YuI9RG/xs95waaI/E6ym/7NsTw==}
+
lru-cache@11.5.2:
resolution: {integrity: sha512-4pfM1Ff0x50o0tQwb5ucw/RzNyD0/YJME6IVcStalZuMWxdt3sR3huStTtxz4PUmvZfRguvDejasvQ2kifR11g==}
engines: {node: 20 || >=22}
@@ -2524,6 +2903,11 @@ packages:
markdown-table@3.0.4:
resolution: {integrity: sha512-wiYz4+JrLyb/DqW2hkFJxP7Vd7JuTDm77fvbM8VfEQdmSMqcImWeeRbHwZjBjIFki/VaMK2BhFi7oUUZeM5bqw==}
+ marked@16.4.2:
+ resolution: {integrity: sha512-TI3V8YYWvkVf3KJe1dRkpnjs68JUPyEa5vjKrp1XEEJUAOaQc+Qj+L1qWbPd0SJuAdQkFU0h73sXXqwDYxsiDA==}
+ engines: {node: '>= 20'}
+ hasBin: true
+
math-intrinsics@1.1.0:
resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==}
engines: {node: '>= 0.4'}
@@ -2579,6 +2963,9 @@ packages:
mdn-data@2.27.1:
resolution: {integrity: sha512-9Yubnt3e8A0OKwxYSXyhLymGW4sCufcLG6VdiDdUGVkPhpqLxlvP5vl1983gQjJl3tqbrM731mjaZaP68AgosQ==}
+ mermaid@11.17.2:
+ resolution: {integrity: sha512-V6K3C8EBdEsPFZXSKMJe6ppQOENxuHARr9GvHX4hh47lAbhMRD9qf4oEK7LoaRQxULMa80/qt5gHO73aCleBBg==}
+
micromark-core-commonmark@2.0.3:
resolution: {integrity: sha512-RDBrHEMSxVFLg6xvnXmb1Ayr2WzLAWjeSATAoxwKYJV94TeNavgoIdA0a9ytzDSVzBy2YKFK+emCPOEibLeCrg==}
@@ -2732,6 +3119,9 @@ packages:
package-json-from-dist@1.0.1:
resolution: {integrity: sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==}
+ package-manager-detector@1.8.0:
+ resolution: {integrity: sha512-yQA4H19AmPEoMUeavPMDIe1higySl/gH/yaQrkT/s07Qp+7pp2hYz30N3z2l5BkjVkF9Ow6o0wjJamm2y7Sn0A==}
+
parse-entities@4.0.2:
resolution: {integrity: sha512-GG2AQYWoLgL877gQIKeRPGO1xF9+eG1ujIb5soS5gPvLQ1y2o8FL90w2QWNdf9I361Mpp7726c+lj3U0qK1uGw==}
@@ -2741,6 +3131,9 @@ packages:
parse5@8.0.1:
resolution: {integrity: sha512-z1e/HMG90obSGeidlli3hj7cbocou0/wa5HacvI3ASx34PecNjNQeaHNo5WIZpWofN9kgkqV1q5YvXe3F0FoPw==}
+ path-data-parser@0.1.0:
+ resolution: {integrity: sha512-NOnmBpt5Y2RWbuv0LMzsayp3lVylAHLPUTut412ZA3l+C4uw4ZVkQbjShYCQ8TCpUMdPapr4YjUqLYD6v68j+w==}
+
path-exists@4.0.0:
resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==}
engines: {node: '>=8'}
@@ -2776,6 +3169,12 @@ packages:
engines: {node: '>=20'}
hasBin: true
+ points-on-curve@0.2.0:
+ resolution: {integrity: sha512-0mYKnYYe9ZcqMCWhUjItv/oHjvgEsfKvnUTg8sAtnHr3GVy7rGkXCb6d5cSyqrWqL4k81b9CPg3urd+T7aop3A==}
+
+ points-on-path@0.2.1:
+ resolution: {integrity: sha512-25ClnWWuw7JbWZcgqY/gJ4FQWadKxGWk+3kR/7kD0tCaDtPPMj7oHu2ToLaVhfpnHrZzYby2w6tUA0eOIuUg8g==}
+
possible-typed-array-names@1.1.0:
resolution: {integrity: sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==}
engines: {node: '>= 0.4'}
@@ -2800,6 +3199,10 @@ packages:
resolution: {integrity: sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==}
engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0}
+ prismjs@1.30.0:
+ resolution: {integrity: sha512-DEvV2ZF2r2/63V+tK8hQvrR2ZGn10srHbXviTlcv7Kpzw8jWiNTqbVgjO3IY8RxrrOUF8VPMQQFysYYYv0YZxw==}
+ engines: {node: '>=6'}
+
property-information@7.2.0:
resolution: {integrity: sha512-IAtzIB6sUiWaJYrX9smp3V46pBGbBeLFRGdh25kg1334VcBlD8HzhPeNIWQH9zhGmo2itIe25EHt9dQP7G5hmg==}
@@ -2841,6 +3244,12 @@ packages:
react-dom:
optional: true
+ react-syntax-highlighter@16.1.1:
+ resolution: {integrity: sha512-PjVawBGy80C6YbC5DDZJeUjBmC7skaoEUdvfFQediQHgCL7aKyVHe57SaJGfQsloGDac+gCpTfRdtxzWWKmCXA==}
+ engines: {node: '>= 16.20.2'}
+ peerDependencies:
+ react: '>= 0.14.0'
+
react@19.2.8:
resolution: {integrity: sha512-PWaYA1L/q9u2u7xYQi+Y3L3Yfnie7XyLeaJICV1MGD6LprsBxcAqGjYyr0eY3p+QdsA+x/Irkt4Qif8D63+Sbw==}
engines: {node: '>=0.10.0'}
@@ -2853,6 +3262,9 @@ packages:
resolution: {integrity: sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw==}
engines: {node: '>= 0.4'}
+ refractor@5.0.0:
+ resolution: {integrity: sha512-QXOrHQF5jOpjjLfiNk5GFnWhRXvxjUVnlFxkeDmewR5sXkr3iM46Zo+CnRR8B+MDVqkULW4EcLVcRBNOPXHosw==}
+
regenerate-unicode-properties@10.2.2:
resolution: {integrity: sha512-m03P+zhBeQd1RGnYxrGyDAPpWX/epKirLrp8e3qevZdVkKtnCrjjWczIbYc8+xd6vcTStVlqfycTx1KR4LOr0g==}
engines: {node: '>=4'}
@@ -2902,6 +3314,9 @@ packages:
engines: {node: '>= 0.4'}
hasBin: true
+ robust-predicates@3.0.3:
+ resolution: {integrity: sha512-NS3levdsRIUOmiJ8FZWCP7LG3QpJyrs/TE0Zpf1yvZu8cAJJ6QMW92H1c7kWpdIHo8RvmLxN/o2JXTKHp74lUA==}
+
rolldown@1.2.7:
resolution: {integrity: sha512-g0EtLvBjTUB7jhyV0S/TCup3v/XSVl45vUIGbOGU4QPiyjTenCe4mKuFvW9fEgYmS2Fo42AUssRmNuMziXdrig==}
engines: {node: ^20.19.0 || >=22.12.0}
@@ -2912,6 +3327,12 @@ packages:
engines: {node: '>=18.0.0', npm: '>=8.0.0'}
hasBin: true
+ roughjs@4.6.6:
+ resolution: {integrity: sha512-ZUz/69+SYpFN/g/lUlo2FXcIjRkSu3nDarreVdGGndHEBJ6cXPdKguS8JGxwj5HA5xIbVKSmLgr5b3AWxtRfvQ==}
+
+ rw@1.3.3:
+ resolution: {integrity: sha512-PdhdWy89SiZogBLaw42zdeqtRJ//zFd2PgQavcICDUgJT5oW10QCRKbJ6bg4r0/UY2M6BWd5tkxuGFRvCkgfHQ==}
+
safe-array-concat@1.1.4:
resolution: {integrity: sha512-wtZlHyOje6OZTGqAoaDKxFkgRtkF9CnHAVnCHKfuj200wAgL+bSJhdsCD2l0Qx/2ekEXjPWcyKkfGb5CPboslg==}
engines: {node: '>=0.4'}
@@ -2924,6 +3345,9 @@ packages:
resolution: {integrity: sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==}
engines: {node: '>= 0.4'}
+ safer-buffer@2.1.2:
+ resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==}
+
saxes@6.0.0:
resolution: {integrity: sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==}
engines: {node: '>=v12.22.7'}
@@ -3025,6 +3449,9 @@ packages:
resolution: {integrity: sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ==}
engines: {node: '>= 0.4'}
+ strictdom@1.0.1:
+ resolution: {integrity: sha512-cEmp9QeXXRmjj/rVp9oyiqcvyocWab/HaoN4+bwFeZ7QzykJD6L3yD4v12K1x0tHpqRqVpJevN3gW7kyM39Bqg==}
+
string-convert@0.2.1:
resolution: {integrity: sha512-u/1tdPl4yQnPBjnVrmdLo9gtuLvELKsAoRapekWggdiQNvvvum+jYF329d84NAa660KQw7pB2n36KrIKVoXa3A==}
@@ -3134,6 +3561,10 @@ packages:
peerDependencies:
typescript: '>=4.8.4'
+ ts-dedent@2.3.0:
+ resolution: {integrity: sha512-JfJeIHke7y2egdGGgRAvpCwYFUsHlM2gPcrVOxFkznt/4uzQ7HFmvE63iFHVLBJNDuyDOQgijDK/tXH/f6Msjg==}
+ engines: {node: '>=6.10'}
+
type-check@0.4.0:
resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==}
engines: {node: '>= 0.8.0'}
@@ -3242,6 +3673,10 @@ packages:
uri-js@4.4.1:
resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==}
+ uuid@14.0.2:
+ resolution: {integrity: sha512-xZe/16rV4aa+HGSOCiY2YeLT1OybRLrrkL/Rqaq7p7GMVXjFh+6wN4oMYgjFmnSnhY8t6Xpdl2l9qmnHYuMHwQ==}
+ hasBin: true
+
vfile-location@5.0.3:
resolution: {integrity: sha512-5yXvWDEgqeiYiBe1lbxYF7UMAIm/IcopxMHrMQDq3nvKcjPKIhZklUKL+AE7J7uApI4kwe2snsK+eI6UTj9EHg==}
@@ -3525,6 +3960,30 @@ snapshots:
react-dom: 19.2.8(react@19.2.8)
throttle-debounce: 5.0.2
+ '@ant-design/x@2.9.0(antd@6.6.2(react-dom@19.2.8(react@19.2.8))(react@19.2.8))(react-dom@19.2.8(react@19.2.8))(react@19.2.8)':
+ dependencies:
+ '@ant-design/colors': 8.0.1
+ '@ant-design/cssinjs': 2.1.2(react-dom@19.2.8(react@19.2.8))(react@19.2.8)
+ '@ant-design/cssinjs-utils': 2.1.2(react-dom@19.2.8(react@19.2.8))(react@19.2.8)
+ '@ant-design/fast-color': 3.0.1
+ '@ant-design/icons': 6.3.4(react-dom@19.2.8(react@19.2.8))(react@19.2.8)
+ '@babel/runtime': 7.29.7
+ '@rc-component/motion': 1.3.3(react-dom@19.2.8(react@19.2.8))(react@19.2.8)
+ '@rc-component/resize-observer': 1.1.2(react-dom@19.2.8(react@19.2.8))(react@19.2.8)
+ '@rc-component/util': 1.13.0(react-dom@19.2.8(react@19.2.8))(react@19.2.8)
+ antd: 6.6.2(react-dom@19.2.8(react@19.2.8))(react@19.2.8)
+ clsx: 2.1.1
+ lodash.throttle: 4.1.1
+ mermaid: 11.17.2
+ react: 19.2.8
+ react-dom: 19.2.8(react@19.2.8)
+ react-syntax-highlighter: 16.1.1(react@19.2.8)
+
+ '@antfu/install-pkg@2.0.1':
+ dependencies:
+ package-manager-detector: 1.8.0
+ tinyexec: 1.3.0
+
'@apideck/better-ajv-errors@0.3.7(ajv@8.20.0)':
dependencies:
ajv: 8.20.0
@@ -4216,10 +4675,14 @@ snapshots:
'@babel/helper-string-parser': 7.29.7
'@babel/helper-validator-identifier': 7.29.7
+ '@braintree/sanitize-url@7.1.2': {}
+
'@bramus/specificity@2.4.2':
dependencies:
css-tree: 3.2.1
+ '@chevrotain/types@11.1.2': {}
+
'@csstools/color-helpers@6.1.1': {}
'@csstools/css-calc@3.3.0(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0)':
@@ -4300,6 +4763,14 @@ snapshots:
'@humanwhocodes/retry@0.4.3': {}
+ '@iconify/types@2.0.0': {}
+
+ '@iconify/utils@3.1.7':
+ dependencies:
+ '@antfu/install-pkg': 2.0.1
+ '@iconify/types': 2.0.0
+ import-meta-resolve: 4.2.0
+
'@isaacs/cliui@9.0.0': {}
'@jridgewell/gen-mapping@0.3.13':
@@ -4326,6 +4797,10 @@ snapshots:
'@jridgewell/resolve-uri': 3.1.2
'@jridgewell/sourcemap-codec': 1.6.0
+ '@mermaid-js/parser@1.2.1':
+ dependencies:
+ '@chevrotain/types': 11.1.2
+
'@napi-rs/lzma-linux-x64-gnu@1.5.1':
optional: true
@@ -4850,6 +5325,21 @@ snapshots:
'@standard-schema/spec@1.1.0': {}
+ '@tanstack/query-core@5.102.8': {}
+
+ '@tanstack/react-query@5.102.8(react@19.2.8)':
+ dependencies:
+ '@tanstack/query-core': 5.102.8
+ react: 19.2.8
+
+ '@tanstack/react-virtual@3.14.10(react-dom@19.2.8(react@19.2.8))(react@19.2.8)':
+ dependencies:
+ '@tanstack/virtual-core': 3.17.8
+ react: 19.2.8
+ react-dom: 19.2.8(react@19.2.8)
+
+ '@tanstack/virtual-core@3.17.8': {}
+
'@testing-library/dom@10.4.1':
dependencies:
'@babel/code-frame': 7.29.7
@@ -4901,6 +5391,123 @@ snapshots:
'@types/deep-eql': 4.0.2
assertion-error: 2.0.1
+ '@types/d3-array@3.2.2': {}
+
+ '@types/d3-axis@3.0.6':
+ dependencies:
+ '@types/d3-selection': 3.0.11
+
+ '@types/d3-brush@3.0.6':
+ dependencies:
+ '@types/d3-selection': 3.0.11
+
+ '@types/d3-chord@3.0.6': {}
+
+ '@types/d3-color@3.1.3': {}
+
+ '@types/d3-contour@3.0.6':
+ dependencies:
+ '@types/d3-array': 3.2.2
+ '@types/geojson': 7946.0.16
+
+ '@types/d3-delaunay@6.0.4': {}
+
+ '@types/d3-dispatch@3.0.7': {}
+
+ '@types/d3-drag@3.0.7':
+ dependencies:
+ '@types/d3-selection': 3.0.11
+
+ '@types/d3-dsv@3.0.7': {}
+
+ '@types/d3-ease@3.0.2': {}
+
+ '@types/d3-fetch@3.0.7':
+ dependencies:
+ '@types/d3-dsv': 3.0.7
+
+ '@types/d3-force@3.0.10': {}
+
+ '@types/d3-format@3.0.4': {}
+
+ '@types/d3-geo@3.1.1':
+ dependencies:
+ '@types/geojson': 7946.0.16
+
+ '@types/d3-hierarchy@3.1.7': {}
+
+ '@types/d3-interpolate@3.0.4':
+ dependencies:
+ '@types/d3-color': 3.1.3
+
+ '@types/d3-path@3.1.1': {}
+
+ '@types/d3-polygon@3.0.2': {}
+
+ '@types/d3-quadtree@3.0.6': {}
+
+ '@types/d3-random@3.0.4': {}
+
+ '@types/d3-scale-chromatic@3.1.0': {}
+
+ '@types/d3-scale@4.0.9':
+ dependencies:
+ '@types/d3-time': 3.0.4
+
+ '@types/d3-selection@3.0.11': {}
+
+ '@types/d3-shape@3.2.0':
+ dependencies:
+ '@types/d3-path': 3.1.1
+
+ '@types/d3-time-format@4.0.3': {}
+
+ '@types/d3-time@3.0.4': {}
+
+ '@types/d3-timer@3.0.2': {}
+
+ '@types/d3-transition@3.0.9':
+ dependencies:
+ '@types/d3-selection': 3.0.11
+
+ '@types/d3-zoom@3.0.8':
+ dependencies:
+ '@types/d3-interpolate': 3.0.4
+ '@types/d3-selection': 3.0.11
+
+ '@types/d3@7.4.3':
+ dependencies:
+ '@types/d3-array': 3.2.2
+ '@types/d3-axis': 3.0.6
+ '@types/d3-brush': 3.0.6
+ '@types/d3-chord': 3.0.6
+ '@types/d3-color': 3.1.3
+ '@types/d3-contour': 3.0.6
+ '@types/d3-delaunay': 6.0.4
+ '@types/d3-dispatch': 3.0.7
+ '@types/d3-drag': 3.0.7
+ '@types/d3-dsv': 3.0.7
+ '@types/d3-ease': 3.0.2
+ '@types/d3-fetch': 3.0.7
+ '@types/d3-force': 3.0.10
+ '@types/d3-format': 3.0.4
+ '@types/d3-geo': 3.1.1
+ '@types/d3-hierarchy': 3.1.7
+ '@types/d3-interpolate': 3.0.4
+ '@types/d3-path': 3.1.1
+ '@types/d3-polygon': 3.0.2
+ '@types/d3-quadtree': 3.0.6
+ '@types/d3-random': 3.0.4
+ '@types/d3-scale': 4.0.9
+ '@types/d3-scale-chromatic': 3.1.0
+ '@types/d3-selection': 3.0.11
+ '@types/d3-shape': 3.2.0
+ '@types/d3-time': 3.0.4
+ '@types/d3-time-format': 4.0.3
+ '@types/d3-timer': 3.0.2
+ '@types/d3-transition': 3.0.9
+ '@types/d3-zoom': 3.0.8
+
'@types/debug@4.1.13':
dependencies:
'@types/ms': 2.1.0
@@ -4915,6 +5522,8 @@ snapshots:
'@types/estree@1.0.9': {}
+ '@types/geojson@7946.0.16': {}
+
'@types/hast@3.0.5':
dependencies:
'@types/unist': 3.0.3
@@ -4933,6 +5542,8 @@ snapshots:
dependencies:
undici-types: 7.16.0
+ '@types/prismjs@1.26.6': {}
+
'@types/react-dom@19.2.5(@types/react@19.2.18)':
dependencies:
'@types/react': 19.2.18
@@ -5042,6 +5653,11 @@ snapshots:
'@ungap/structured-clone@1.4.0': {}
+ '@upsetjs/venn.js@2.0.0':
+ optionalDependencies:
+ d3-selection: 3.0.0
+ d3-transition: 3.0.1(d3-selection@3.0.0)
+
'@vitejs/plugin-react@6.1.1(vite@8.2.2(@types/node@24.12.0)(terser@5.51.2))':
dependencies:
'@rolldown/pluginutils': 1.0.1
@@ -5295,6 +5911,8 @@ snapshots:
commander@2.20.3: {}
+ commander@7.2.0: {}
+
commander@8.3.0: {}
common-tags@1.8.2: {}
@@ -5309,6 +5927,14 @@ snapshots:
dependencies:
browserslist: 4.28.8
+ cose-base@1.0.3:
+ dependencies:
+ layout-base: 1.0.2
+
+ cose-base@2.2.0:
+ dependencies:
+ layout-base: 2.0.1
+
cross-spawn@7.0.6:
dependencies:
path-key: 3.1.1
@@ -5326,6 +5952,190 @@ snapshots:
csstype@3.2.3: {}
+ cytoscape-cose-bilkent@4.1.0(cytoscape@3.34.2):
+ dependencies:
+ cose-base: 1.0.3
+ cytoscape: 3.34.2
+
+ cytoscape-fcose@2.2.0(cytoscape@3.34.2):
+ dependencies:
+ cose-base: 2.2.0
+ cytoscape: 3.34.2
+
+ cytoscape@3.34.2: {}
+
+ d3-array@2.12.1:
+ dependencies:
+ internmap: 1.0.1
+
+ d3-array@3.2.4:
+ dependencies:
+ internmap: 2.0.3
+
+ d3-axis@3.0.0: {}
+
+ d3-brush@3.0.0:
+ dependencies:
+ d3-dispatch: 3.0.1
+ d3-drag: 3.0.0
+ d3-interpolate: 3.0.1
+ d3-selection: 3.0.0
+ d3-transition: 3.0.1(d3-selection@3.0.0)
+
+ d3-chord@3.0.1:
+ dependencies:
+ d3-path: 3.1.0
+
+ d3-color@3.1.0: {}
+
+ d3-contour@4.0.2:
+ dependencies:
+ d3-array: 3.2.4
+
+ d3-delaunay@6.0.4:
+ dependencies:
+ delaunator: 5.1.0
+
+ d3-dispatch@3.0.1: {}
+
+ d3-drag@3.0.0:
+ dependencies:
+ d3-dispatch: 3.0.1
+ d3-selection: 3.0.0
+
+ d3-dsv@3.0.1:
+ dependencies:
+ commander: 7.2.0
+ iconv-lite: 0.6.3
+ rw: 1.3.3
+
+ d3-ease@3.0.1: {}
+
+ d3-fetch@3.0.1:
+ dependencies:
+ d3-dsv: 3.0.1
+
+ d3-force@3.0.0:
+ dependencies:
+ d3-dispatch: 3.0.1
+ d3-quadtree: 3.0.1
+ d3-timer: 3.0.1
+
+ d3-format@3.1.2: {}
+
+ d3-geo@3.1.1:
+ dependencies:
+ d3-array: 3.2.4
+
+ d3-hierarchy@3.1.2: {}
+
+ d3-interpolate@3.0.1:
+ dependencies:
+ d3-color: 3.1.0
+
+ d3-path@1.0.9: {}
+
+ d3-path@3.1.0: {}
+
+ d3-polygon@3.0.1: {}
+
+ d3-quadtree@3.0.1: {}
+
+ d3-random@3.0.1: {}
+
+ d3-sankey@0.12.3:
+ dependencies:
+ d3-array: 2.12.1
+ d3-shape: 1.3.7
+
+ d3-scale-chromatic@3.1.0:
+ dependencies:
+ d3-color: 3.1.0
+ d3-interpolate: 3.0.1
+
+ d3-scale@4.0.2:
+ dependencies:
+ d3-array: 3.2.4
+ d3-format: 3.1.2
+ d3-interpolate: 3.0.1
+ d3-time: 3.1.0
+ d3-time-format: 4.1.0
+
+ d3-selection@3.0.0: {}
+
+ d3-shape@1.3.7:
+ dependencies:
+ d3-path: 1.0.9
+
+ d3-shape@3.2.0:
+ dependencies:
+ d3-path: 3.1.0
+
+ d3-time-format@4.1.0:
+ dependencies:
+ d3-time: 3.1.0
+
+ d3-time@3.1.0:
+ dependencies:
+ d3-array: 3.2.4
+
+ d3-timer@3.0.1: {}
+
+ d3-transition@3.0.1(d3-selection@3.0.0):
+ dependencies:
+ d3-color: 3.1.0
+ d3-dispatch: 3.0.1
+ d3-ease: 3.0.1
+ d3-interpolate: 3.0.1
+ d3-selection: 3.0.0
+ d3-timer: 3.0.1
+
+ d3-zoom@3.0.0:
+ dependencies:
+ d3-dispatch: 3.0.1
+ d3-drag: 3.0.0
+ d3-interpolate: 3.0.1
+ d3-selection: 3.0.0
+ d3-transition: 3.0.1(d3-selection@3.0.0)
+
+ d3@7.9.0:
+ dependencies:
+ d3-array: 3.2.4
+ d3-axis: 3.0.0
+ d3-brush: 3.0.0
+ d3-chord: 3.0.1
+ d3-color: 3.1.0
+ d3-contour: 4.0.2
+ d3-delaunay: 6.0.4
+ d3-dispatch: 3.0.1
+ d3-drag: 3.0.0
+ d3-dsv: 3.0.1
+ d3-ease: 3.0.1
+ d3-fetch: 3.0.1
+ d3-force: 3.0.0
+ d3-format: 3.1.2
+ d3-geo: 3.1.1
+ d3-hierarchy: 3.1.2
+ d3-interpolate: 3.0.1
+ d3-path: 3.1.0
+ d3-polygon: 3.0.1
+ d3-quadtree: 3.0.1
+ d3-random: 3.0.1
+ d3-scale: 4.0.2
+ d3-scale-chromatic: 3.1.0
+ d3-selection: 3.0.0
+ d3-shape: 3.2.0
+ d3-time: 3.1.0
+ d3-time-format: 4.1.0
+ d3-timer: 3.0.1
+ d3-transition: 3.0.1(d3-selection@3.0.0)
+ d3-zoom: 3.0.0
+
+ dagre-d3-es@7.0.14:
+ dependencies:
+ d3: 7.9.0
+ lodash-es: 4.18.1
+
data-urls@7.0.0:
dependencies:
whatwg-mimetype: 5.0.0
@@ -5379,6 +6189,10 @@ snapshots:
has-property-descriptors: 1.0.2
object-keys: 1.1.1
+ delaunator@5.1.0:
+ dependencies:
+ robust-predicates: 3.0.3
+
dequal@2.0.3: {}
detect-libc@2.1.2: {}
@@ -5391,6 +6205,10 @@ snapshots:
dom-accessibility-api@0.6.3: {}
+ dompurify@3.4.15:
+ optionalDependencies:
+ '@types/trusted-types': 2.0.7
+
dunder-proto@1.0.1:
dependencies:
call-bind-apply-helpers: 1.0.2
@@ -5497,6 +6315,8 @@ snapshots:
is-date-object: 1.1.0
is-symbol: 1.1.1
+ es-toolkit@1.52.0: {}
+
escalade@3.2.0: {}
escape-string-regexp@4.0.0: {}
@@ -5604,6 +6424,14 @@ snapshots:
fast-uri@3.1.7: {}
+ fastdom@1.0.12:
+ dependencies:
+ strictdom: 1.0.1
+
+ fault@1.0.4:
+ dependencies:
+ format: 0.2.2
+
fdir@6.5.0(picomatch@4.0.7):
optionalDependencies:
picomatch: 4.0.7
@@ -5637,6 +6465,8 @@ snapshots:
cross-spawn: 7.0.6
signal-exit: 4.1.0
+ format@0.2.2: {}
+
fs-extra@9.1.0:
dependencies:
at-least-node: 1.0.0
@@ -5720,6 +6550,8 @@ snapshots:
graceful-fs@4.2.11: {}
+ hachure-fill@0.5.2: {}
+
has-bigints@1.1.0: {}
has-property-descriptors@1.0.2:
@@ -5826,8 +6658,12 @@ snapshots:
dependencies:
hermes-estree: 0.25.1
+ highlight.js@10.7.3: {}
+
highlight.js@11.12.0: {}
+ highlightjs-vue@1.0.0: {}
+
html-encoding-sniffer@6.0.0:
dependencies:
'@exodus/bytes': 1.15.1
@@ -5836,12 +6672,18 @@ snapshots:
html-url-attributes@3.0.1: {}
+ iconv-lite@0.6.3:
+ dependencies:
+ safer-buffer: 2.1.2
+
idb@7.1.1: {}
ignore@5.3.2: {}
ignore@7.0.8: {}
+ import-meta-resolve@4.2.0: {}
+
imurmurhash@0.1.4: {}
indent-string@4.0.0: {}
@@ -5854,6 +6696,10 @@ snapshots:
hasown: 2.0.4
side-channel: 1.1.1
+ internmap@1.0.1: {}
+
+ internmap@2.0.3: {}
+
is-alphabetical@2.0.1: {}
is-alphanumerical@2.0.1:
@@ -6063,6 +6909,12 @@ snapshots:
dependencies:
json-buffer: 3.0.1
+ khroma@2.1.0: {}
+
+ layout-base@1.0.2: {}
+
+ layout-base@2.0.1: {}
+
leven@3.1.0: {}
levn@0.4.1:
@@ -6123,10 +6975,19 @@ snapshots:
dependencies:
p-locate: 5.0.0
+ lodash-es@4.18.1: {}
+
lodash.debounce@4.0.8: {}
+ lodash.throttle@4.1.1: {}
+
longest-streak@3.1.0: {}
+ lowlight@1.20.0:
+ dependencies:
+ fault: 1.0.4
+ highlight.js: 10.7.3
+
lru-cache@11.5.2: {}
lru-cache@5.1.1:
@@ -6141,6 +7002,8 @@ snapshots:
markdown-table@3.0.4: {}
+ marked@16.4.2: {}
+
math-intrinsics@1.1.0: {}
mdast-util-find-and-replace@3.0.2:
@@ -6310,6 +7173,31 @@ snapshots:
mdn-data@2.27.1: {}
+ mermaid@11.17.2:
+ dependencies:
+ '@braintree/sanitize-url': 7.1.2
+ '@iconify/utils': 3.1.7
+ '@mermaid-js/parser': 1.2.1
+ '@types/d3': 7.4.3
+ '@upsetjs/venn.js': 2.0.0
+ cytoscape: 3.34.2
+ cytoscape-cose-bilkent: 4.1.0(cytoscape@3.34.2)
+ cytoscape-fcose: 2.2.0(cytoscape@3.34.2)
+ d3: 7.9.0
+ d3-sankey: 0.12.3
+ dagre-d3-es: 7.0.14
+ dayjs: 1.11.23
+ dompurify: 3.4.15
+ es-toolkit: 1.52.0
+ fastdom: 1.0.12
+ katex: 0.16.47
+ khroma: 2.1.0
+ marked: 16.4.2
+ roughjs: 4.6.6
+ stylis: 4.4.0
+ ts-dedent: 2.3.0
+ uuid: 14.0.2
+
micromark-core-commonmark@2.0.3:
dependencies:
decode-named-character-reference: 1.3.0
@@ -6572,6 +7460,8 @@ snapshots:
package-json-from-dist@1.0.1: {}
+ package-manager-detector@1.8.0: {}
+
parse-entities@4.0.2:
dependencies:
'@types/unist': 2.0.11
@@ -6590,6 +7480,8 @@ snapshots:
dependencies:
entities: 8.0.0
+ path-data-parser@0.1.0: {}
+
path-exists@4.0.0: {}
path-key@3.1.1: {}
@@ -6615,6 +7507,13 @@ snapshots:
optionalDependencies:
fsevents: 2.3.2
+ points-on-curve@0.2.0: {}
+
+ points-on-path@0.2.1:
+ dependencies:
+ path-data-parser: 0.1.0
+ points-on-curve: 0.2.0
+
possible-typed-array-names@1.1.0: {}
postcss@8.5.26:
@@ -6635,6 +7534,8 @@ snapshots:
ansi-styles: 5.2.0
react-is: 17.0.2
+ prismjs@1.30.0: {}
+
property-information@7.2.0: {}
punycode@2.3.1: {}
@@ -6680,6 +7581,16 @@ snapshots:
optionalDependencies:
react-dom: 19.2.8(react@19.2.8)
+ react-syntax-highlighter@16.1.1(react@19.2.8):
+ dependencies:
+ '@babel/runtime': 7.29.7
+ highlight.js: 10.7.3
+ highlightjs-vue: 1.0.0
+ lowlight: 1.20.0
+ prismjs: 1.30.0
+ react: 19.2.8
+ refractor: 5.0.0
+
react@19.2.8: {}
redent@3.0.0:
@@ -6698,6 +7609,13 @@ snapshots:
get-proto: 1.0.1
which-builtin-type: 1.2.1
+ refractor@5.0.0:
+ dependencies:
+ '@types/hast': 3.0.5
+ '@types/prismjs': 1.26.6
+ hastscript: 9.0.1
+ parse-entities: 4.0.2
+
regenerate-unicode-properties@10.2.2:
dependencies:
regenerate: 1.4.2
@@ -6790,6 +7708,8 @@ snapshots:
path-parse: 1.0.7
supports-preserve-symlinks-flag: 1.0.0
+ robust-predicates@3.0.3: {}
+
rolldown@1.2.7:
dependencies:
'@oxc-project/types': 0.148.0
@@ -6843,6 +7763,15 @@ snapshots:
'@rollup/rollup-win32-x64-msvc': 4.63.1
fsevents: 2.3.3
+ roughjs@4.6.6:
+ dependencies:
+ hachure-fill: 0.5.2
+ path-data-parser: 0.1.0
+ points-on-curve: 0.2.0
+ points-on-path: 0.2.1
+
+ rw@1.3.3: {}
+
safe-array-concat@1.1.4:
dependencies:
call-bind: 1.0.9
@@ -6862,6 +7791,8 @@ snapshots:
es-errors: 1.3.0
is-regex: 1.2.1
+ safer-buffer@2.1.2: {}
+
saxes@6.0.0:
dependencies:
xmlchars: 2.2.0
@@ -6964,6 +7895,8 @@ snapshots:
es-errors: 1.3.0
internal-slot: 1.1.0
+ strictdom@1.0.1: {}
+
string-convert@0.2.1: {}
string.prototype.matchall@4.1.0:
@@ -7088,6 +8021,8 @@ snapshots:
dependencies:
typescript: 6.0.3
+ ts-dedent@2.3.0: {}
+
type-check@0.4.0:
dependencies:
prelude-ls: 1.2.1
@@ -7223,6 +8158,8 @@ snapshots:
dependencies:
punycode: 2.3.1
+ uuid@14.0.2: {}
+
vfile-location@5.0.3:
dependencies:
'@types/unist': 3.0.3
diff --git a/farhelm-console/public/third-party-notices.txt b/farhelm-console/public/third-party-notices.txt
new file mode 100644
index 0000000..cc90c68
--- /dev/null
+++ b/farhelm-console/public/third-party-notices.txt
@@ -0,0 +1,11263 @@
+FarHelm V0.8.0 third-party notices
+
+FarHelm is distributed under its repository LICENSE. The following notices cover Console production dependencies; only modules selected by the build are bundled.
+
+Codex is not bundled. Agent uses the locally installed OpenAI Codex executable and its public App Server protocol. Protocol reference: openai/codex rust-v0.153.4, commit 3d2ee51ca2d5db578f328aa75e20aa22c0197c9a (Apache-2.0).
+
+Direct adopted components: @ant-design/x 2.9.0 (614a63ada11f8afca781739b5240fe1268bc5833), @tanstack/react-query 5.102.8 (2969edf32f7e0c48e2a108d84712d6e01edfde21), @tanstack/react-virtual 3.14.10 (e9874f033c74afd3251eeb9f3e60b2530cc7ae88). These packages are used without vendored business-code changes. OpenCode, assistant-ui and HuggingChat informed behavior; their application source and assets are not included.
+
+========================================================================
+@ant-design/colors 8.0.1
+License: MIT
+https://github.com/ant-design/ant-design-colors#readme
+
+LICENSE
+MIT LICENSE
+
+Copyright (c) 2018-present Ant UED, https://xtech.antfin.com/
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+
+========================================================================
+@ant-design/cssinjs 2.1.2
+License: MIT
+https://github.com/ant-design/cssinjs
+
+LICENSE.md
+The MIT License (MIT)
+
+Copyright (c) 2019-present afc163
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+
+========================================================================
+@ant-design/cssinjs-utils 2.1.2
+License: MIT
+https://github.com/ant-design/cssinjs-util
+
+LICENSE
+The MIT License (MIT)
+Copyright (c) 2015-present Alipay.com, https://www.alipay.com/
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+
+========================================================================
+@ant-design/fast-color 3.0.1
+License: MIT
+https://github.com/ant-design/fast-color
+
+LICENSE
+The MIT License (MIT)
+Copyright (c) 2015-present Alipay.com, https://www.alipay.com/
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+
+========================================================================
+@ant-design/icons 6.3.4
+License: MIT
+https://github.com/ant-design/ant-design-icons/tree/master#readme
+
+LICENSE
+MIT LICENSE
+
+Copyright (c) 2018-present Ant UED, https://xtech.antfin.com/
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+
+========================================================================
+@ant-design/icons-svg 4.6.0
+License: MIT
+https://github.com/ant-design/ant-design-icons/
+
+Source: https://github.com/ant-design/ant-design-icons/blob/7f2516ac91226d2b41f93b35cb5197c8d94f7189/LICENSE
+
+MIT LICENSE
+
+Copyright (c) 2018-present Ant UED, https://xtech.antfin.com/
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+
+========================================================================
+@ant-design/react-slick 2.0.0
+License: MIT
+https://react-slick.neostack.com
+
+LICENSE
+The MIT License (MIT)
+
+Copyright (c) 2014 Kiran Abburi
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+
+
+
+========================================================================
+@ant-design/x 2.9.0
+License: MIT
+https://x.ant.design
+
+LICENSE
+MIT LICENSE
+
+Copyright (c) 2015-present Ant UED, https://xtech.antfin.com/
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+
+========================================================================
+@antfu/install-pkg 2.0.1
+License: MIT
+https://github.com/antfu-collective/install-pkg#readme
+
+LICENSE
+MIT License
+
+Copyright (c) 2021 Anthony Fu
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+
+
+========================================================================
+@babel/runtime 7.29.7, 8.0.0
+License: MIT
+https://babel.dev/docs/en/next/babel-runtime
+
+LICENSE
+MIT License
+
+Copyright (c) 2014-present Sebastian McKenzie and other contributors
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+
+========================================================================
+@braintree/sanitize-url 7.1.2
+License: MIT
+https://github.com/braintree/sanitize-url#readme
+
+LICENSE
+MIT License
+
+Copyright (c) 2017 Braintree
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+
+
+========================================================================
+@chevrotain/types 11.1.2
+License: Apache-2.0
+https://chevrotain.io/documentation/
+
+LICENSE.txt
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+
+========================================================================
+@emotion/hash 0.8.0
+License: MIT
+https://github.com/emotion-js/emotion/tree/master#readme
+
+LICENSE
+MIT License
+
+Copyright (c) Emotion team and other contributors
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+
+
+========================================================================
+@emotion/unitless 0.7.5
+License: MIT
+https://github.com/emotion-js/emotion/tree/master#readme
+
+LICENSE
+MIT License
+
+Copyright (c) Emotion team and other contributors
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+
+
+========================================================================
+@iconify/types 2.0.0
+License: MIT
+https://github.com/iconify/iconify
+
+license.txt
+MIT License
+
+Copyright (c) 2021 - 2022 Vjacheslav Trushkin / Iconify OÜ
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+
+========================================================================
+@iconify/utils 3.1.7
+License: MIT
+https://iconify.design/docs/libraries/utils/
+
+license.txt
+MIT License
+
+Copyright (c) 2021-PRESENT Vjacheslav Trushkin
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+
+========================================================================
+@mermaid-js/parser 1.2.1
+License: MIT
+https://github.com/mermaid-js/mermaid/tree/develop/packages/mermaid/parser/#readme
+
+LICENSE
+The MIT License (MIT)
+
+Copyright (c) 2023 Yokozuna59
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+
+
+========================================================================
+@rc-component/async-validator 6.0.0
+License: MIT
+http://github.com/react-component/async-validator
+
+LICENSE.md
+The MIT License (MIT)
+
+Copyright (c) 2014-present yiminghe
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+
+========================================================================
+@rc-component/cascader 1.22.0
+License: MIT
+https://react-component.github.io/cascader
+
+LICENSE
+MIT License
+
+Copyright (c) 2019-present react-component
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+
+
+LICENSE.md
+The MIT License (MIT)
+
+Copyright (c) 2014-present alipay.com
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+
+========================================================================
+@rc-component/checkbox 2.0.0
+License: MIT
+https://react-component.github.io/checkbox
+
+LICENSE.md
+The MIT License (MIT) Copyright (c) 2016 React Components
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+
+========================================================================
+@rc-component/collapse 1.2.0
+License: MIT
+http://github.com/react-component/collapse
+
+LICENSE.md
+The MIT License (MIT)
+
+Copyright (c) 2014-present yiminghe
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+
+========================================================================
+@rc-component/color-picker 3.1.1
+License: MIT
+http://github.com/react-component/color-picker
+
+LICENSE.md
+The MIT License (MIT)
+
+Copyright (c) 2019-present alipay.com
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+
+========================================================================
+@rc-component/context 2.0.2
+License: MIT
+http://github.com/react-component/context
+
+LICENSE.md
+The MIT License (MIT)
+
+Copyright (c) 2014-present yiminghe
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+
+========================================================================
+@rc-component/dialog 1.10.0
+License: MIT
+http://github.com/react-component/dialog
+
+LICENSE.md
+The MIT License (MIT)
+
+Copyright (c) 2014-present yiminghe
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+
+========================================================================
+@rc-component/drawer 1.4.2
+License: MIT
+https://github.com/react-component/drawer
+
+LICENSE
+MIT LICENSE
+
+Copyright (c) 2015-present Alipay.com, https://www.alipay.com/
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+
+========================================================================
+@rc-component/dropdown 1.0.3
+License: MIT
+https://react-component.github.io/dropdown
+
+LICENSE
+The MIT License (MIT)
+Copyright (c) 2015-present Alipay.com, https://www.alipay.com/
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+
+========================================================================
+@rc-component/form 1.8.6
+License: MIT
+https://react-component.github.io/field-form
+
+LICENSE
+MIT License
+
+Copyright (c) 2019-present react-component
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+
+========================================================================
+@rc-component/image 1.10.0
+License: MIT
+https://react-component.github.io/image
+
+LICENSE
+MIT License
+
+Copyright (c) 2019-present react-component
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+
+
+LICENSE.md
+MIT LICENSE
+
+Copyright (c) 2015-present Alipay.com, https://www.alipay.com/
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+
+========================================================================
+@rc-component/input 1.3.1
+License: MIT
+https://github.com/react-component/input
+
+LICENSE.md
+The MIT License (MIT)
+
+Copyright (c) 2019-present afc163
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+
+========================================================================
+@rc-component/input-number 1.6.2
+License: MIT
+https://github.com/react-component/input-number
+
+LICENSE.md
+The MIT License (MIT)
+
+Copyright (c) 2014-present yiminghe
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+
+========================================================================
+@rc-component/listy 1.2.3
+License: MIT
+https://github.com/react-component/listy
+
+LICENSE
+The MIT License (MIT)
+Copyright (c) 2015-present Alipay.com, https://www.alipay.com/
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+
+========================================================================
+@rc-component/mentions 1.12.0
+License: MIT
+https://react-component.github.io/mentions
+
+LICENSE
+MIT License
+
+Copyright (c) 2019-present react-component
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+
+
+LICENSE.md
+The MIT License (MIT)
+
+Copyright (c) 2019-present alipay.com
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+
+========================================================================
+@rc-component/menu 1.5.0
+License: MIT
+https://react-component.github.io/menu
+
+LICENSE
+MIT License
+
+Copyright (c) 2019-present react-component
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+
+
+LICENSE.md
+The MIT License (MIT)
+
+Copyright (c) 2014-present yiminghe
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+
+========================================================================
+@rc-component/mini-decimal 1.1.4
+License: MIT
+https://github.com/react-component/mini-decimal
+
+LICENSE
+MIT License
+
+Copyright (c) 2019-present react-component
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+
+========================================================================
+@rc-component/motion 1.3.3
+License: MIT
+https://react-component.github.io/motion
+
+LICENSE.md
+The MIT License (MIT)
+
+Copyright (c) 2019-present afc163
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+
+========================================================================
+@rc-component/mutate-observer 2.0.1
+License: MIT
+https://github.com/react-component/mutate-observer
+
+LICENSE
+MIT License
+
+Copyright (c) 2019-present react-component
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+
+========================================================================
+@rc-component/notification 2.0.8
+License: MIT
+https://react-component.github.io/notification
+
+LICENSE
+MIT License
+
+Copyright (c) 2019-present react-component
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+
+
+LICENSE.md
+The MIT License (MIT)
+
+Copyright (c) 2014-present yiminghe
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+
+========================================================================
+@rc-component/overflow 1.0.1
+License: MIT
+https://react-component.github.io/overflow
+
+LICENSE.md
+The MIT License (MIT)
+
+Copyright (c) 2019-present afc163
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+
+========================================================================
+@rc-component/pagination 1.4.0
+License: MIT
+https://react-component.github.io/pagination
+
+LICENSE.md
+The MIT License (MIT)
+
+Copyright (c) 2014-present yiminghe
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+
+========================================================================
+@rc-component/picker 1.12.0
+License: MIT
+https://react-component.github.io/picker
+
+LICENSE
+MIT License
+
+Copyright (c) 2019-present react-component
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+
+
+LICENSE.md
+The MIT License (MIT)
+
+Copyright (c) 2019-present afc163
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+
+========================================================================
+@rc-component/portal 2.2.1
+License: MIT
+https://github.com/react-component/portal
+
+LICENSE
+MIT License
+
+Copyright (c) 2019-present react-component
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+
+========================================================================
+@rc-component/progress 1.0.3
+License: MIT
+https://react-component.github.io/progress
+
+LICENSE
+MIT License
+
+Copyright (c) 2019-present react-component
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+
+
+LICENSE.md
+The MIT License (MIT)
+
+Copyright (c) 2014-present yiminghe
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+
+========================================================================
+@rc-component/qrcode 2.0.0
+License: MIT
+https://github.com/react-component/qrcode
+
+LICENSE
+The MIT License (MIT)
+Copyright (c) 2015-present Alipay.com, https://www.alipay.com/
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+
+========================================================================
+@rc-component/rate 1.0.1
+License: MIT
+https://github.com/react-component/rate
+
+LICENSE.md
+The MIT License (MIT)
+
+Copyright (c) 2014-present yiminghe
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+
+========================================================================
+@rc-component/resize-observer 1.1.2
+License: MIT
+https://react-component.github.io/resize-observer
+
+LICENSE.md
+The MIT License (MIT)
+
+Copyright (c) 2019-present afc163
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+
+========================================================================
+@rc-component/segmented 1.3.0
+License: MIT
+https://react-component.github.io/segmented
+
+LICENSE.md
+The MIT License (MIT)
+
+Copyright (c) 2019-present afc163
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+
+========================================================================
+@rc-component/select 1.10.1
+License: MIT
+https://react-component.github.io/select
+
+LICENSE
+MIT License
+
+Copyright (c) 2019-present react-component
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+
+
+LICENSE.md
+The MIT License (MIT)
+
+Copyright (c) 2014-present alipay.com
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+
+========================================================================
+@rc-component/slider 1.1.1
+License: MIT
+https://react-component.github.io/slider
+
+LICENSE
+The MIT License (MIT)
+Copyright (c) 2015-present Alipay.com, https://www.alipay.com/
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+
+========================================================================
+@rc-component/steps 1.2.3
+License: MIT
+https://react-component.github.io/steps
+
+LICENSE
+MIT License
+
+Copyright (c) 2019-present react-component
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+
+
+LICENSE.md
+The MIT License (MIT)
+
+Copyright (c) 2014-present yiminghe
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+
+========================================================================
+@rc-component/switch 1.0.3
+License: MIT
+http://github.com/react-component/switch
+
+LICENSE.md
+The MIT License (MIT)
+
+Copyright (c) 2014-present yiminghe
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+
+========================================================================
+@rc-component/table 1.11.1
+License: MIT
+https://react-component.github.io/table
+
+LICENSE
+MIT License
+
+Copyright (c) 2019-present react-component
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+
+
+LICENSE.md
+MIT LICENSE
+
+Copyright (c) 2015-present Alipay.com, https://www.alipay.com/
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+
+========================================================================
+@rc-component/tabs 1.13.0
+License: MIT
+https://react-component.github.io/tabs
+
+LICENSE
+MIT License
+
+Copyright (c) 2019-present react-component
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+
+
+LICENSE.md
+The MIT License (MIT)
+
+Copyright (c) 2014-present yiminghe
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+
+========================================================================
+@rc-component/tooltip 1.5.2
+License: MIT
+https://react-component.github.io/tooltip
+
+LICENSE
+The MIT License (MIT)
+
+Copyright (c) 2015-present Alipay.com, https://www.alipay.com/
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+
+========================================================================
+@rc-component/tour 2.4.0
+License: MIT
+https://github.com/react-component/tour
+
+LICENSE
+MIT License
+
+Copyright (c) 2019-present react-component
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+
+========================================================================
+@rc-component/tree 1.4.0
+License: MIT
+https://react-component.github.io/tree
+
+LICENSE
+MIT License
+
+Copyright (c) 2019-present react-component
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+
+
+LICENSE.md
+MIT LICENSE
+
+Copyright (c) 2015-present Alipay.com, https://www.alipay.com/
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+
+========================================================================
+@rc-component/tree-select 1.16.1
+License: MIT
+https://react-component.github.io/tree-select
+
+LICENSE
+MIT License
+
+Copyright (c) 2019-present react-component
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+
+
+LICENSE.md
+MIT LICENSE
+
+Copyright (c) 2015-present Alipay.com, https://www.alipay.com/
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+
+========================================================================
+@rc-component/trigger 3.10.1
+License: MIT
+https://react-component.github.io/trigger
+
+LICENSE
+The MIT License (MIT)
+Copyright (c) 2015-present Alipay.com, https://www.alipay.com/
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+
+========================================================================
+@rc-component/upload 1.1.1
+License: MIT
+http://github.com/react-component/upload
+
+LICENSE
+The MIT License (MIT)
+
+Copyright (c) 2016-present react-component
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+
+========================================================================
+@rc-component/util 1.13.0
+License: MIT
+https://react-component.github.io/util
+
+LICENSE
+The MIT License (MIT)
+
+Copyright (c) 2014-present yiminghe
+Copyright (c) 2015-present Alipay.com, https://www.alipay.com/
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+
+========================================================================
+@rc-component/virtual-list 1.5.1
+License: MIT
+https://react-component.github.io/virtual-list
+
+LICENSE
+The MIT License (MIT)
+
+Copyright (c) 2015-present Alipay.com, https://www.alipay.com/
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+
+========================================================================
+@tanstack/query-core 5.102.8
+License: MIT
+https://tanstack.com/query
+
+LICENSE
+MIT License
+
+Copyright (c) 2021-present Tanner Linsley
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+
+
+========================================================================
+@tanstack/react-query 5.102.8
+License: MIT
+https://tanstack.com/query
+
+LICENSE
+MIT License
+
+Copyright (c) 2021-present Tanner Linsley
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+
+
+========================================================================
+@tanstack/react-virtual 3.14.10
+License: MIT
+https://tanstack.com/virtual
+
+LICENSE
+MIT License
+
+Copyright (c) 2021-present Tanner Linsley
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+
+
+========================================================================
+@tanstack/virtual-core 3.17.8
+License: MIT
+https://tanstack.com/virtual
+
+LICENSE
+MIT License
+
+Copyright (c) 2021-present Tanner Linsley
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+
+
+========================================================================
+@types/d3 7.4.3
+License: MIT
+https://github.com/DefinitelyTyped/DefinitelyTyped/tree/master/types/d3
+
+LICENSE
+ MIT License
+
+ Copyright (c) Microsoft Corporation.
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to deal
+ in the Software without restriction, including without limitation the rights
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in all
+ copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ SOFTWARE
+
+
+========================================================================
+@types/d3-array 3.2.2
+License: MIT
+https://github.com/DefinitelyTyped/DefinitelyTyped/tree/master/types/d3-array
+
+LICENSE
+ MIT License
+
+ Copyright (c) Microsoft Corporation.
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to deal
+ in the Software without restriction, including without limitation the rights
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in all
+ copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ SOFTWARE
+
+
+========================================================================
+@types/d3-axis 3.0.6
+License: MIT
+https://github.com/DefinitelyTyped/DefinitelyTyped/tree/master/types/d3-axis
+
+LICENSE
+ MIT License
+
+ Copyright (c) Microsoft Corporation.
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to deal
+ in the Software without restriction, including without limitation the rights
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in all
+ copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ SOFTWARE
+
+
+========================================================================
+@types/d3-brush 3.0.6
+License: MIT
+https://github.com/DefinitelyTyped/DefinitelyTyped/tree/master/types/d3-brush
+
+LICENSE
+ MIT License
+
+ Copyright (c) Microsoft Corporation.
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to deal
+ in the Software without restriction, including without limitation the rights
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in all
+ copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ SOFTWARE
+
+
+========================================================================
+@types/d3-chord 3.0.6
+License: MIT
+https://github.com/DefinitelyTyped/DefinitelyTyped/tree/master/types/d3-chord
+
+LICENSE
+ MIT License
+
+ Copyright (c) Microsoft Corporation.
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to deal
+ in the Software without restriction, including without limitation the rights
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in all
+ copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ SOFTWARE
+
+
+========================================================================
+@types/d3-color 3.1.3
+License: MIT
+https://github.com/DefinitelyTyped/DefinitelyTyped/tree/master/types/d3-color
+
+LICENSE
+ MIT License
+
+ Copyright (c) Microsoft Corporation.
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to deal
+ in the Software without restriction, including without limitation the rights
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in all
+ copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ SOFTWARE
+
+
+========================================================================
+@types/d3-contour 3.0.6
+License: MIT
+https://github.com/DefinitelyTyped/DefinitelyTyped/tree/master/types/d3-contour
+
+LICENSE
+ MIT License
+
+ Copyright (c) Microsoft Corporation.
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to deal
+ in the Software without restriction, including without limitation the rights
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in all
+ copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ SOFTWARE
+
+
+========================================================================
+@types/d3-delaunay 6.0.4
+License: MIT
+https://github.com/DefinitelyTyped/DefinitelyTyped/tree/master/types/d3-delaunay
+
+LICENSE
+ MIT License
+
+ Copyright (c) Microsoft Corporation.
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to deal
+ in the Software without restriction, including without limitation the rights
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in all
+ copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ SOFTWARE
+
+
+========================================================================
+@types/d3-dispatch 3.0.7
+License: MIT
+https://github.com/DefinitelyTyped/DefinitelyTyped/tree/master/types/d3-dispatch
+
+LICENSE
+ MIT License
+
+ Copyright (c) Microsoft Corporation.
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to deal
+ in the Software without restriction, including without limitation the rights
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in all
+ copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ SOFTWARE
+
+
+========================================================================
+@types/d3-drag 3.0.7
+License: MIT
+https://github.com/DefinitelyTyped/DefinitelyTyped/tree/master/types/d3-drag
+
+LICENSE
+ MIT License
+
+ Copyright (c) Microsoft Corporation.
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to deal
+ in the Software without restriction, including without limitation the rights
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in all
+ copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ SOFTWARE
+
+
+========================================================================
+@types/d3-dsv 3.0.7
+License: MIT
+https://github.com/DefinitelyTyped/DefinitelyTyped/tree/master/types/d3-dsv
+
+LICENSE
+ MIT License
+
+ Copyright (c) Microsoft Corporation.
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to deal
+ in the Software without restriction, including without limitation the rights
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in all
+ copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ SOFTWARE
+
+
+========================================================================
+@types/d3-ease 3.0.2
+License: MIT
+https://github.com/DefinitelyTyped/DefinitelyTyped/tree/master/types/d3-ease
+
+LICENSE
+ MIT License
+
+ Copyright (c) Microsoft Corporation.
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to deal
+ in the Software without restriction, including without limitation the rights
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in all
+ copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ SOFTWARE
+
+
+========================================================================
+@types/d3-fetch 3.0.7
+License: MIT
+https://github.com/DefinitelyTyped/DefinitelyTyped/tree/master/types/d3-fetch
+
+LICENSE
+ MIT License
+
+ Copyright (c) Microsoft Corporation.
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to deal
+ in the Software without restriction, including without limitation the rights
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in all
+ copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ SOFTWARE
+
+
+========================================================================
+@types/d3-force 3.0.10
+License: MIT
+https://github.com/DefinitelyTyped/DefinitelyTyped/tree/master/types/d3-force
+
+LICENSE
+ MIT License
+
+ Copyright (c) Microsoft Corporation.
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to deal
+ in the Software without restriction, including without limitation the rights
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in all
+ copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ SOFTWARE
+
+
+========================================================================
+@types/d3-format 3.0.4
+License: MIT
+https://github.com/DefinitelyTyped/DefinitelyTyped/tree/master/types/d3-format
+
+LICENSE
+ MIT License
+
+ Copyright (c) Microsoft Corporation.
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to deal
+ in the Software without restriction, including without limitation the rights
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in all
+ copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ SOFTWARE
+
+
+========================================================================
+@types/d3-geo 3.1.1
+License: MIT
+https://github.com/DefinitelyTyped/DefinitelyTyped/tree/master/types/d3-geo
+
+LICENSE
+ MIT License
+
+ Copyright (c) Microsoft Corporation.
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to deal
+ in the Software without restriction, including without limitation the rights
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in all
+ copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ SOFTWARE
+
+
+========================================================================
+@types/d3-hierarchy 3.1.7
+License: MIT
+https://github.com/DefinitelyTyped/DefinitelyTyped/tree/master/types/d3-hierarchy
+
+LICENSE
+ MIT License
+
+ Copyright (c) Microsoft Corporation.
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to deal
+ in the Software without restriction, including without limitation the rights
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in all
+ copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ SOFTWARE
+
+
+========================================================================
+@types/d3-interpolate 3.0.4
+License: MIT
+https://github.com/DefinitelyTyped/DefinitelyTyped/tree/master/types/d3-interpolate
+
+LICENSE
+ MIT License
+
+ Copyright (c) Microsoft Corporation.
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to deal
+ in the Software without restriction, including without limitation the rights
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in all
+ copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ SOFTWARE
+
+
+========================================================================
+@types/d3-path 3.1.1
+License: MIT
+https://github.com/DefinitelyTyped/DefinitelyTyped/tree/master/types/d3-path
+
+LICENSE
+ MIT License
+
+ Copyright (c) Microsoft Corporation.
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to deal
+ in the Software without restriction, including without limitation the rights
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in all
+ copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ SOFTWARE
+
+
+========================================================================
+@types/d3-polygon 3.0.2
+License: MIT
+https://github.com/DefinitelyTyped/DefinitelyTyped/tree/master/types/d3-polygon
+
+LICENSE
+ MIT License
+
+ Copyright (c) Microsoft Corporation.
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to deal
+ in the Software without restriction, including without limitation the rights
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in all
+ copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ SOFTWARE
+
+
+========================================================================
+@types/d3-quadtree 3.0.6
+License: MIT
+https://github.com/DefinitelyTyped/DefinitelyTyped/tree/master/types/d3-quadtree
+
+LICENSE
+ MIT License
+
+ Copyright (c) Microsoft Corporation.
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to deal
+ in the Software without restriction, including without limitation the rights
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in all
+ copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ SOFTWARE
+
+
+========================================================================
+@types/d3-random 3.0.4
+License: MIT
+https://github.com/DefinitelyTyped/DefinitelyTyped/tree/master/types/d3-random
+
+LICENSE
+ MIT License
+
+ Copyright (c) Microsoft Corporation.
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to deal
+ in the Software without restriction, including without limitation the rights
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in all
+ copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ SOFTWARE
+
+
+========================================================================
+@types/d3-scale 4.0.9
+License: MIT
+https://github.com/DefinitelyTyped/DefinitelyTyped/tree/master/types/d3-scale
+
+LICENSE
+ MIT License
+
+ Copyright (c) Microsoft Corporation.
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to deal
+ in the Software without restriction, including without limitation the rights
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in all
+ copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ SOFTWARE
+
+
+========================================================================
+@types/d3-scale-chromatic 3.1.0
+License: MIT
+https://github.com/DefinitelyTyped/DefinitelyTyped/tree/master/types/d3-scale-chromatic
+
+LICENSE
+ MIT License
+
+ Copyright (c) Microsoft Corporation.
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to deal
+ in the Software without restriction, including without limitation the rights
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in all
+ copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ SOFTWARE
+
+
+========================================================================
+@types/d3-selection 3.0.11
+License: MIT
+https://github.com/DefinitelyTyped/DefinitelyTyped/tree/master/types/d3-selection
+
+LICENSE
+ MIT License
+
+ Copyright (c) Microsoft Corporation.
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to deal
+ in the Software without restriction, including without limitation the rights
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in all
+ copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ SOFTWARE
+
+
+========================================================================
+@types/d3-shape 3.2.0
+License: MIT
+https://github.com/DefinitelyTyped/DefinitelyTyped/tree/master/types/d3-shape
+
+LICENSE
+ MIT License
+
+ Copyright (c) Microsoft Corporation.
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to deal
+ in the Software without restriction, including without limitation the rights
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in all
+ copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ SOFTWARE
+
+
+========================================================================
+@types/d3-time 3.0.4
+License: MIT
+https://github.com/DefinitelyTyped/DefinitelyTyped/tree/master/types/d3-time
+
+LICENSE
+ MIT License
+
+ Copyright (c) Microsoft Corporation.
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to deal
+ in the Software without restriction, including without limitation the rights
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in all
+ copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ SOFTWARE
+
+
+========================================================================
+@types/d3-time-format 4.0.3
+License: MIT
+https://github.com/DefinitelyTyped/DefinitelyTyped/tree/master/types/d3-time-format
+
+LICENSE
+ MIT License
+
+ Copyright (c) Microsoft Corporation.
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to deal
+ in the Software without restriction, including without limitation the rights
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in all
+ copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ SOFTWARE
+
+
+========================================================================
+@types/d3-timer 3.0.2
+License: MIT
+https://github.com/DefinitelyTyped/DefinitelyTyped/tree/master/types/d3-timer
+
+LICENSE
+ MIT License
+
+ Copyright (c) Microsoft Corporation.
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to deal
+ in the Software without restriction, including without limitation the rights
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in all
+ copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ SOFTWARE
+
+
+========================================================================
+@types/d3-transition 3.0.9
+License: MIT
+https://github.com/DefinitelyTyped/DefinitelyTyped/tree/master/types/d3-transition
+
+LICENSE
+ MIT License
+
+ Copyright (c) Microsoft Corporation.
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to deal
+ in the Software without restriction, including without limitation the rights
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in all
+ copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ SOFTWARE
+
+
+========================================================================
+@types/d3-zoom 3.0.8
+License: MIT
+https://github.com/DefinitelyTyped/DefinitelyTyped/tree/master/types/d3-zoom
+
+LICENSE
+ MIT License
+
+ Copyright (c) Microsoft Corporation.
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to deal
+ in the Software without restriction, including without limitation the rights
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in all
+ copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ SOFTWARE
+
+
+========================================================================
+@types/debug 4.1.13
+License: MIT
+https://github.com/DefinitelyTyped/DefinitelyTyped/tree/master/types/debug
+
+LICENSE
+ MIT License
+
+ Copyright (c) Microsoft Corporation.
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to deal
+ in the Software without restriction, including without limitation the rights
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in all
+ copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ SOFTWARE
+
+
+========================================================================
+@types/estree 1.0.9
+License: MIT
+https://github.com/DefinitelyTyped/DefinitelyTyped/tree/master/types/estree
+
+LICENSE
+ MIT License
+
+ Copyright (c) Microsoft Corporation.
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to deal
+ in the Software without restriction, including without limitation the rights
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in all
+ copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ SOFTWARE
+
+
+========================================================================
+@types/estree-jsx 1.0.5
+License: MIT
+https://github.com/DefinitelyTyped/DefinitelyTyped/tree/master/types/estree-jsx
+
+LICENSE
+ MIT License
+
+ Copyright (c) Microsoft Corporation.
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to deal
+ in the Software without restriction, including without limitation the rights
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in all
+ copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ SOFTWARE
+
+
+========================================================================
+@types/geojson 7946.0.16
+License: MIT
+https://github.com/DefinitelyTyped/DefinitelyTyped/tree/master/types/geojson
+
+LICENSE
+ MIT License
+
+ Copyright (c) Microsoft Corporation.
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to deal
+ in the Software without restriction, including without limitation the rights
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in all
+ copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ SOFTWARE
+
+
+========================================================================
+@types/hast 3.0.5
+License: MIT
+https://github.com/DefinitelyTyped/DefinitelyTyped/tree/master/types/hast
+
+LICENSE
+ MIT License
+
+ Copyright (c) Microsoft Corporation.
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to deal
+ in the Software without restriction, including without limitation the rights
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in all
+ copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ SOFTWARE
+
+
+========================================================================
+@types/katex 0.16.8
+License: MIT
+https://github.com/DefinitelyTyped/DefinitelyTyped/tree/master/types/katex
+
+LICENSE
+ MIT License
+
+ Copyright (c) Microsoft Corporation.
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to deal
+ in the Software without restriction, including without limitation the rights
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in all
+ copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ SOFTWARE
+
+
+========================================================================
+@types/mdast 4.0.4
+License: MIT
+https://github.com/DefinitelyTyped/DefinitelyTyped/tree/master/types/mdast
+
+LICENSE
+ MIT License
+
+ Copyright (c) Microsoft Corporation.
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to deal
+ in the Software without restriction, including without limitation the rights
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in all
+ copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ SOFTWARE
+
+
+========================================================================
+@types/ms 2.1.0
+License: MIT
+https://github.com/DefinitelyTyped/DefinitelyTyped/tree/master/types/ms
+
+LICENSE
+ MIT License
+
+ Copyright (c) Microsoft Corporation.
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to deal
+ in the Software without restriction, including without limitation the rights
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in all
+ copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ SOFTWARE
+
+
+========================================================================
+@types/prismjs 1.26.6
+License: MIT
+https://github.com/DefinitelyTyped/DefinitelyTyped/tree/master/types/prismjs
+
+LICENSE
+ MIT License
+
+ Copyright (c) Microsoft Corporation.
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to deal
+ in the Software without restriction, including without limitation the rights
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in all
+ copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ SOFTWARE
+
+
+========================================================================
+@types/react 19.2.18
+License: MIT
+https://github.com/DefinitelyTyped/DefinitelyTyped/tree/master/types/react
+
+LICENSE
+ MIT License
+
+ Copyright (c) Microsoft Corporation.
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to deal
+ in the Software without restriction, including without limitation the rights
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in all
+ copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ SOFTWARE
+
+
+========================================================================
+@types/trusted-types 2.0.7
+License: MIT
+https://github.com/DefinitelyTyped/DefinitelyTyped/tree/master/types/trusted-types
+
+LICENSE
+ MIT License
+
+ Copyright (c) Microsoft Corporation.
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to deal
+ in the Software without restriction, including without limitation the rights
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in all
+ copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ SOFTWARE
+
+
+========================================================================
+@types/unist 2.0.11, 3.0.3
+License: MIT
+https://github.com/DefinitelyTyped/DefinitelyTyped/tree/master/types/unist
+
+LICENSE
+ MIT License
+
+ Copyright (c) Microsoft Corporation.
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to deal
+ in the Software without restriction, including without limitation the rights
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in all
+ copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ SOFTWARE
+
+
+========================================================================
+@ungap/structured-clone 1.4.0
+License: ISC
+https://github.com/ungap/structured-clone#readme
+
+LICENSE
+ISC License
+
+Copyright (c) 2021, Andrea Giammarchi, @WebReflection
+
+Permission to use, copy, modify, and/or distribute this software for any
+purpose with or without fee is hereby granted, provided that the above
+copyright notice and this permission notice appear in all copies.
+
+THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
+REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
+INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+PERFORMANCE OF THIS SOFTWARE.
+
+
+========================================================================
+@upsetjs/venn.js 2.0.0
+License: MIT
+https://github.com/upsetjs/venn.js
+
+LICENSE
+MIT License
+
+Copyright (c) 2013 Ben Frederickson
+Copyright (c) 2021 Samuel Gratzl
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+
+
+========================================================================
+antd 6.6.2
+License: MIT
+https://ant.design
+
+LICENSE
+MIT LICENSE
+
+Copyright (c) 2015-present Ant UED, https://xtech.antfin.com/
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+
+========================================================================
+bail 2.0.2
+License: MIT
+https://github.com/wooorm/bail#readme
+
+license
+(The MIT License)
+
+Copyright (c) 2015 Titus Wormer
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+'Software'), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+
+========================================================================
+ccount 2.0.1
+License: MIT
+https://github.com/wooorm/ccount#readme
+
+license
+(The MIT License)
+
+Copyright (c) 2015 Titus Wormer
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+'Software'), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+
+========================================================================
+character-entities 2.0.2
+License: MIT
+https://github.com/wooorm/character-entities#readme
+
+license
+(The MIT License)
+
+Copyright (c) 2015 Titus Wormer
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+'Software'), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+
+========================================================================
+character-entities-html4 2.1.0
+License: MIT
+https://github.com/wooorm/character-entities-html4#readme
+
+license
+(The MIT License)
+
+Copyright (c) 2015 Titus Wormer
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+'Software'), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+
+========================================================================
+character-entities-legacy 3.0.0
+License: MIT
+https://github.com/wooorm/character-entities-legacy#readme
+
+license
+(The MIT License)
+
+Copyright (c) 2015 Titus Wormer
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+'Software'), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+
+========================================================================
+character-reference-invalid 2.0.1
+License: MIT
+https://github.com/wooorm/character-reference-invalid#readme
+
+license
+(The MIT License)
+
+Copyright (c) 2015 Titus Wormer
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+'Software'), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+
+========================================================================
+clsx 2.1.1
+License: MIT
+https://github.com/lukeed/clsx#readme
+
+license
+MIT License
+
+Copyright (c) Luke Edwards (lukeed.com)
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+
+========================================================================
+comma-separated-tokens 2.0.3
+License: MIT
+https://github.com/wooorm/comma-separated-tokens#readme
+
+license
+(The MIT License)
+
+Copyright (c) 2016 Titus Wormer
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+'Software'), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+
+========================================================================
+commander 7.2.0, 8.3.0
+License: MIT
+https://github.com/tj/commander.js#readme
+
+LICENSE
+(The MIT License)
+
+Copyright (c) 2011 TJ Holowaychuk
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+'Software'), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+
+========================================================================
+compute-scroll-into-view 3.1.1
+License: MIT
+https://scroll-into-view.dev
+
+LICENSE
+MIT License
+
+Copyright (c) 2025 Cody Olsen
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+
+
+========================================================================
+cookie 1.1.1
+License: MIT
+https://github.com/jshttp/cookie#readme
+
+LICENSE
+(The MIT License)
+
+Copyright (c) 2012-2014 Roman Shtylman
+Copyright (c) 2015 Douglas Christopher Wilson
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+'Software'), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+
+
+========================================================================
+cose-base 1.0.3, 2.2.0
+License: MIT
+https://github.com/iVis-at-Bilkent/cose-base#readme
+
+LICENSE
+MIT License
+
+Copyright (c) 2019 - present, iVis@Bilkent.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+
+
+========================================================================
+csstype 3.2.3
+License: MIT
+https://github.com/frenic/csstype#readme
+
+LICENSE
+Copyright (c) 2017-2018 Fredrik Nicol
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+
+
+========================================================================
+cytoscape 3.34.2
+License: MIT
+http://js.cytoscape.org
+
+LICENSE
+Copyright (c) 2016-2026, The Cytoscape Consortium.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the “Software”), to deal in
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+of the Software, and to permit persons to whom the Software is furnished to do
+so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+
+license-update.mjs
+import fs from 'fs';
+import path from 'path';
+
+import { fileURLToPath } from 'url';
+import { dirname } from 'path';
+
+const __filename = fileURLToPath(import.meta.url);
+const __dirname = dirname(__filename);
+
+const year = (new Date()).getFullYear();
+
+const license = `Copyright (c) 2016-${year}, The Cytoscape Consortium.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the “Software”), to deal in
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+of the Software, and to permit persons to whom the Software is furnished to do
+so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.`;
+
+fs.writeFileSync(path.join(__dirname, 'LICENSE'), license);
+
+
+========================================================================
+cytoscape-cose-bilkent 4.1.0
+License: MIT
+https://github.com/cytoscape/cytoscape.js-cose-bilkent
+
+LICENSE
+
+
+Copyright (c) 2016-2018, The Cytoscape Consortium.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the “Software”), to deal in
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+of the Software, and to permit persons to whom the Software is furnished to do
+so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+
+========================================================================
+cytoscape-fcose 2.2.0
+License: MIT
+https://github.com/iVis-at-Bilkent/cytoscape.js-fcose
+
+LICENSE
+Copyright (c) 2018 - present, iVis-at-Bilkent.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the “Software”), to deal in
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+of the Software, and to permit persons to whom the Software is furnished to do
+so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+
+
+========================================================================
+d3 7.9.0
+License: ISC
+https://d3js.org
+
+LICENSE
+Copyright 2010-2023 Mike Bostock
+
+Permission to use, copy, modify, and/or distribute this software for any purpose
+with or without fee is hereby granted, provided that the above copyright notice
+and this permission notice appear in all copies.
+
+THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
+REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
+INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
+OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
+TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
+THIS SOFTWARE.
+
+
+========================================================================
+d3-array 3.2.4
+License: ISC
+https://d3js.org/d3-array/
+
+LICENSE
+Copyright 2010-2023 Mike Bostock
+
+Permission to use, copy, modify, and/or distribute this software for any purpose
+with or without fee is hereby granted, provided that the above copyright notice
+and this permission notice appear in all copies.
+
+THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
+REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
+INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
+OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
+TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
+THIS SOFTWARE.
+
+
+========================================================================
+d3-array 2.12.1
+License: BSD-3-Clause
+https://d3js.org/d3-array/
+
+LICENSE
+Copyright 2010-2020 Mike Bostock
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without modification,
+are permitted provided that the following conditions are met:
+
+* Redistributions of source code must retain the above copyright notice, this
+ list of conditions and the following disclaimer.
+
+* Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+
+* Neither the name of the author nor the names of contributors may be used to
+ endorse or promote products derived from this software without specific prior
+ written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+
+========================================================================
+d3-axis 3.0.0
+License: ISC
+https://d3js.org/d3-axis/
+
+LICENSE
+Copyright 2010-2021 Mike Bostock
+
+Permission to use, copy, modify, and/or distribute this software for any purpose
+with or without fee is hereby granted, provided that the above copyright notice
+and this permission notice appear in all copies.
+
+THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
+REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
+INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
+OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
+TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
+THIS SOFTWARE.
+
+
+========================================================================
+d3-brush 3.0.0
+License: ISC
+https://d3js.org/d3-brush/
+
+LICENSE
+Copyright 2010-2021 Mike Bostock
+
+Permission to use, copy, modify, and/or distribute this software for any purpose
+with or without fee is hereby granted, provided that the above copyright notice
+and this permission notice appear in all copies.
+
+THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
+REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
+INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
+OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
+TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
+THIS SOFTWARE.
+
+
+========================================================================
+d3-chord 3.0.1
+License: ISC
+https://d3js.org/d3-chord/
+
+LICENSE
+Copyright 2010-2021 Mike Bostock
+
+Permission to use, copy, modify, and/or distribute this software for any purpose
+with or without fee is hereby granted, provided that the above copyright notice
+and this permission notice appear in all copies.
+
+THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
+REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
+INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
+OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
+TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
+THIS SOFTWARE.
+
+
+========================================================================
+d3-color 3.1.0
+License: ISC
+https://d3js.org/d3-color/
+
+LICENSE
+Copyright 2010-2022 Mike Bostock
+
+Permission to use, copy, modify, and/or distribute this software for any purpose
+with or without fee is hereby granted, provided that the above copyright notice
+and this permission notice appear in all copies.
+
+THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
+REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
+INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
+OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
+TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
+THIS SOFTWARE.
+
+
+========================================================================
+d3-contour 4.0.2
+License: ISC
+https://d3js.org/d3-contour/
+
+LICENSE
+Copyright 2012-2023 Mike Bostock
+
+Permission to use, copy, modify, and/or distribute this software for any purpose
+with or without fee is hereby granted, provided that the above copyright notice
+and this permission notice appear in all copies.
+
+THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
+REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
+INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
+OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
+TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
+THIS SOFTWARE.
+
+
+========================================================================
+d3-delaunay 6.0.4
+License: ISC
+https://github.com/d3/d3-delaunay
+
+LICENSE
+Copyright 2018-2021 Observable, Inc.
+Copyright 2021 Mapbox
+
+Permission to use, copy, modify, and/or distribute this software for any purpose
+with or without fee is hereby granted, provided that the above copyright notice
+and this permission notice appear in all copies.
+
+THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
+REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
+INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
+OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
+TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
+THIS SOFTWARE.
+
+
+========================================================================
+d3-dispatch 3.0.1
+License: ISC
+https://d3js.org/d3-dispatch/
+
+LICENSE
+Copyright 2010-2021 Mike Bostock
+
+Permission to use, copy, modify, and/or distribute this software for any purpose
+with or without fee is hereby granted, provided that the above copyright notice
+and this permission notice appear in all copies.
+
+THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
+REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
+INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
+OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
+TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
+THIS SOFTWARE.
+
+
+========================================================================
+d3-drag 3.0.0
+License: ISC
+https://d3js.org/d3-drag/
+
+LICENSE
+Copyright 2010-2021 Mike Bostock
+
+Permission to use, copy, modify, and/or distribute this software for any purpose
+with or without fee is hereby granted, provided that the above copyright notice
+and this permission notice appear in all copies.
+
+THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
+REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
+INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
+OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
+TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
+THIS SOFTWARE.
+
+
+========================================================================
+d3-dsv 3.0.1
+License: ISC
+https://d3js.org/d3-dsv/
+
+LICENSE
+Copyright 2013-2021 Mike Bostock
+
+Permission to use, copy, modify, and/or distribute this software for any purpose
+with or without fee is hereby granted, provided that the above copyright notice
+and this permission notice appear in all copies.
+
+THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
+REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
+INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
+OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
+TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
+THIS SOFTWARE.
+
+
+========================================================================
+d3-ease 3.0.1
+License: BSD-3-Clause
+https://d3js.org/d3-ease/
+
+LICENSE
+Copyright 2010-2021 Mike Bostock
+Copyright 2001 Robert Penner
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without modification,
+are permitted provided that the following conditions are met:
+
+* Redistributions of source code must retain the above copyright notice, this
+ list of conditions and the following disclaimer.
+
+* Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+
+* Neither the name of the author nor the names of contributors may be used to
+ endorse or promote products derived from this software without specific prior
+ written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+
+========================================================================
+d3-fetch 3.0.1
+License: ISC
+https://d3js.org/d3-fetch/
+
+LICENSE
+Copyright 2016-2021 Mike Bostock
+
+Permission to use, copy, modify, and/or distribute this software for any purpose
+with or without fee is hereby granted, provided that the above copyright notice
+and this permission notice appear in all copies.
+
+THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
+REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
+INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
+OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
+TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
+THIS SOFTWARE.
+
+
+========================================================================
+d3-force 3.0.0
+License: ISC
+https://d3js.org/d3-force/
+
+LICENSE
+Copyright 2010-2021 Mike Bostock
+
+Permission to use, copy, modify, and/or distribute this software for any purpose
+with or without fee is hereby granted, provided that the above copyright notice
+and this permission notice appear in all copies.
+
+THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
+REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
+INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
+OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
+TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
+THIS SOFTWARE.
+
+
+========================================================================
+d3-format 3.1.2
+License: ISC
+https://d3js.org/d3-format/
+
+LICENSE
+Copyright 2010-2026 Mike Bostock
+
+Permission to use, copy, modify, and/or distribute this software for any purpose
+with or without fee is hereby granted, provided that the above copyright notice
+and this permission notice appear in all copies.
+
+THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
+REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
+INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
+OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
+TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
+THIS SOFTWARE.
+
+
+========================================================================
+d3-geo 3.1.1
+License: ISC
+https://d3js.org/d3-geo/
+
+LICENSE
+Copyright 2010-2024 Mike Bostock
+
+Permission to use, copy, modify, and/or distribute this software for any purpose
+with or without fee is hereby granted, provided that the above copyright notice
+and this permission notice appear in all copies.
+
+THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
+REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
+INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
+OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
+TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
+THIS SOFTWARE.
+
+This license applies to GeographicLib, versions 1.12 and later.
+
+Copyright 2008-2012 Charles Karney
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal in
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+the Software, and to permit persons to whom the Software is furnished to do so,
+subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+
+========================================================================
+d3-hierarchy 3.1.2
+License: ISC
+https://d3js.org/d3-hierarchy/
+
+LICENSE
+Copyright 2010-2021 Mike Bostock
+
+Permission to use, copy, modify, and/or distribute this software for any purpose
+with or without fee is hereby granted, provided that the above copyright notice
+and this permission notice appear in all copies.
+
+THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
+REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
+INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
+OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
+TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
+THIS SOFTWARE.
+
+
+========================================================================
+d3-interpolate 3.0.1
+License: ISC
+https://d3js.org/d3-interpolate/
+
+LICENSE
+Copyright 2010-2021 Mike Bostock
+
+Permission to use, copy, modify, and/or distribute this software for any purpose
+with or without fee is hereby granted, provided that the above copyright notice
+and this permission notice appear in all copies.
+
+THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
+REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
+INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
+OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
+TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
+THIS SOFTWARE.
+
+
+========================================================================
+d3-path 3.1.0
+License: ISC
+https://d3js.org/d3-path/
+
+LICENSE
+Copyright 2015-2022 Mike Bostock
+
+Permission to use, copy, modify, and/or distribute this software for any purpose
+with or without fee is hereby granted, provided that the above copyright notice
+and this permission notice appear in all copies.
+
+THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
+REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
+INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
+OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
+TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
+THIS SOFTWARE.
+
+
+========================================================================
+d3-path 1.0.9
+License: BSD-3-Clause
+https://d3js.org/d3-path/
+
+LICENSE
+Copyright 2015-2016 Mike Bostock
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without modification,
+are permitted provided that the following conditions are met:
+
+* Redistributions of source code must retain the above copyright notice, this
+ list of conditions and the following disclaimer.
+
+* Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+
+* Neither the name of the author nor the names of contributors may be used to
+ endorse or promote products derived from this software without specific prior
+ written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+
+========================================================================
+d3-polygon 3.0.1
+License: ISC
+https://d3js.org/d3-polygon/
+
+LICENSE
+Copyright 2010-2021 Mike Bostock
+
+Permission to use, copy, modify, and/or distribute this software for any purpose
+with or without fee is hereby granted, provided that the above copyright notice
+and this permission notice appear in all copies.
+
+THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
+REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
+INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
+OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
+TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
+THIS SOFTWARE.
+
+
+========================================================================
+d3-quadtree 3.0.1
+License: ISC
+https://d3js.org/d3-quadtree/
+
+LICENSE
+Copyright 2010-2021 Mike Bostock
+
+Permission to use, copy, modify, and/or distribute this software for any purpose
+with or without fee is hereby granted, provided that the above copyright notice
+and this permission notice appear in all copies.
+
+THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
+REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
+INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
+OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
+TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
+THIS SOFTWARE.
+
+
+========================================================================
+d3-random 3.0.1
+License: ISC
+https://d3js.org/d3-random/
+
+LICENSE
+Copyright 2010-2021 Mike Bostock
+
+Permission to use, copy, modify, and/or distribute this software for any purpose
+with or without fee is hereby granted, provided that the above copyright notice
+and this permission notice appear in all copies.
+
+THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
+REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
+INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
+OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
+TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
+THIS SOFTWARE.
+
+
+========================================================================
+d3-sankey 0.12.3
+License: BSD-3-Clause
+https://github.com/d3/d3-sankey
+
+LICENSE
+Copyright 2015, Mike Bostock
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without modification,
+are permitted provided that the following conditions are met:
+
+* Redistributions of source code must retain the above copyright notice, this
+ list of conditions and the following disclaimer.
+
+* Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+
+* Neither the name of the author nor the names of contributors may be used to
+ endorse or promote products derived from this software without specific prior
+ written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+
+========================================================================
+d3-scale 4.0.2
+License: ISC
+https://d3js.org/d3-scale/
+
+LICENSE
+Copyright 2010-2021 Mike Bostock
+
+Permission to use, copy, modify, and/or distribute this software for any purpose
+with or without fee is hereby granted, provided that the above copyright notice
+and this permission notice appear in all copies.
+
+THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
+REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
+INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
+OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
+TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
+THIS SOFTWARE.
+
+
+========================================================================
+d3-scale-chromatic 3.1.0
+License: ISC
+https://d3js.org/d3-scale-chromatic/
+
+LICENSE
+Copyright 2010-2024 Mike Bostock
+
+Permission to use, copy, modify, and/or distribute this software for any purpose
+with or without fee is hereby granted, provided that the above copyright notice
+and this permission notice appear in all copies.
+
+THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
+REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
+INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
+OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
+TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
+THIS SOFTWARE.
+
+Apache-Style Software License for ColorBrewer software and ColorBrewer Color Schemes
+
+Copyright 2002 Cynthia Brewer, Mark Harrower, and The Pennsylvania State University
+
+Licensed under the Apache License, Version 2.0 (the "License"); you may not use
+this file except in compliance with the License. You may obtain a copy of the
+License at
+
+http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software distributed
+under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
+CONDITIONS OF ANY KIND, either express or implied. See the License for the
+specific language governing permissions and limitations under the License.
+
+
+========================================================================
+d3-selection 3.0.0
+License: ISC
+https://d3js.org/d3-selection/
+
+LICENSE
+Copyright 2010-2021 Mike Bostock
+
+Permission to use, copy, modify, and/or distribute this software for any purpose
+with or without fee is hereby granted, provided that the above copyright notice
+and this permission notice appear in all copies.
+
+THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
+REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
+INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
+OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
+TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
+THIS SOFTWARE.
+
+
+========================================================================
+d3-shape 3.2.0
+License: ISC
+https://d3js.org/d3-shape/
+
+LICENSE
+Copyright 2010-2022 Mike Bostock
+
+Permission to use, copy, modify, and/or distribute this software for any purpose
+with or without fee is hereby granted, provided that the above copyright notice
+and this permission notice appear in all copies.
+
+THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
+REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
+INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
+OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
+TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
+THIS SOFTWARE.
+
+
+========================================================================
+d3-shape 1.3.7
+License: BSD-3-Clause
+https://d3js.org/d3-shape/
+
+LICENSE
+Copyright 2010-2015 Mike Bostock
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without modification,
+are permitted provided that the following conditions are met:
+
+* Redistributions of source code must retain the above copyright notice, this
+ list of conditions and the following disclaimer.
+
+* Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+
+* Neither the name of the author nor the names of contributors may be used to
+ endorse or promote products derived from this software without specific prior
+ written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+
+========================================================================
+d3-time 3.1.0
+License: ISC
+https://d3js.org/d3-time/
+
+LICENSE
+Copyright 2010-2022 Mike Bostock
+
+Permission to use, copy, modify, and/or distribute this software for any purpose
+with or without fee is hereby granted, provided that the above copyright notice
+and this permission notice appear in all copies.
+
+THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
+REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
+INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
+OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
+TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
+THIS SOFTWARE.
+
+
+========================================================================
+d3-time-format 4.1.0
+License: ISC
+https://d3js.org/d3-time-format/
+
+LICENSE
+Copyright 2010-2021 Mike Bostock
+
+Permission to use, copy, modify, and/or distribute this software for any purpose
+with or without fee is hereby granted, provided that the above copyright notice
+and this permission notice appear in all copies.
+
+THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
+REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
+INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
+OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
+TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
+THIS SOFTWARE.
+
+
+========================================================================
+d3-timer 3.0.1
+License: ISC
+https://d3js.org/d3-timer/
+
+LICENSE
+Copyright 2010-2021 Mike Bostock
+
+Permission to use, copy, modify, and/or distribute this software for any purpose
+with or without fee is hereby granted, provided that the above copyright notice
+and this permission notice appear in all copies.
+
+THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
+REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
+INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
+OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
+TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
+THIS SOFTWARE.
+
+
+========================================================================
+d3-transition 3.0.1
+License: ISC
+https://d3js.org/d3-transition/
+
+LICENSE
+Copyright 2010-2021 Mike Bostock
+
+Permission to use, copy, modify, and/or distribute this software for any purpose
+with or without fee is hereby granted, provided that the above copyright notice
+and this permission notice appear in all copies.
+
+THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
+REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
+INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
+OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
+TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
+THIS SOFTWARE.
+
+
+========================================================================
+d3-zoom 3.0.0
+License: ISC
+https://d3js.org/d3-zoom/
+
+LICENSE
+Copyright 2010-2021 Mike Bostock
+
+Permission to use, copy, modify, and/or distribute this software for any purpose
+with or without fee is hereby granted, provided that the above copyright notice
+and this permission notice appear in all copies.
+
+THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
+REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
+INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
+OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
+TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
+THIS SOFTWARE.
+
+
+========================================================================
+dagre-d3-es 7.0.14
+License: MIT
+https://github.com/tbo47/dagre-es#readme
+
+LICENSE.md
+Original dagre-d3 copyright: Copyright (c) 2013 Chris Pettitt
+Original dagre copyright: Copyright (c) 2012-2014 Chris Pettitt
+Original graphlib copyright: Copyright (c) 2012-2014 Chris Pettitt
+
+Copyright (c) 2022-2024 Thibaut Lassalle, David Newell, Alois Klink, Sidharth Vinod and dagre-es contributors
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+
+
+========================================================================
+dayjs 1.11.23
+License: MIT
+https://day.js.org
+
+LICENSE
+MIT License
+
+Copyright (c) 2018-present, iamkun
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+
+
+========================================================================
+debug 4.4.3
+License: MIT
+https://github.com/debug-js/debug#readme
+
+LICENSE
+(The MIT License)
+
+Copyright (c) 2014-2017 TJ Holowaychuk
+Copyright (c) 2018-2021 Josh Junon
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of this software
+and associated documentation files (the 'Software'), to deal in the Software without restriction,
+including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
+and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so,
+subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all copies or substantial
+portions of the Software.
+
+THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
+LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+
+
+========================================================================
+decode-named-character-reference 1.3.0
+License: MIT
+https://github.com/wooorm/decode-named-character-reference#readme
+
+license
+(The MIT License)
+
+Copyright (c) Titus Wormer
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+'Software'), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+
+========================================================================
+delaunator 5.1.0
+License: ISC
+https://github.com/mapbox/delaunator#readme
+
+LICENSE
+ISC License
+
+Copyright (c) 2026, Mapbox
+
+Permission to use, copy, modify, and/or distribute this software for any purpose
+with or without fee is hereby granted, provided that the above copyright notice
+and this permission notice appear in all copies.
+
+THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
+REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
+INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
+OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
+TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
+THIS SOFTWARE.
+
+
+========================================================================
+dequal 2.0.3
+License: MIT
+https://github.com/lukeed/dequal#readme
+
+license
+The MIT License (MIT)
+
+Copyright (c) Luke Edwards (lukeed.com)
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+
+
+========================================================================
+devlop 1.1.0
+License: MIT
+https://github.com/wooorm/devlop#readme
+
+license
+(The MIT License)
+
+Copyright (c) 2023 Titus Wormer
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+'Software'), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+
+========================================================================
+dompurify 3.4.15
+License: (MPL-2.0 OR Apache-2.0)
+https://github.com/cure53/DOMPurify
+
+LICENSE
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+
+LICENSE-MPL
+Mozilla Public License Version 2.0
+==================================
+
+1. Definitions
+--------------
+
+1.1. "Contributor"
+ means each individual or legal entity that creates, contributes to
+ the creation of, or owns Covered Software.
+
+1.2. "Contributor Version"
+ means the combination of the Contributions of others (if any) used
+ by a Contributor and that particular Contributor's Contribution.
+
+1.3. "Contribution"
+ means Covered Software of a particular Contributor.
+
+1.4. "Covered Software"
+ means Source Code Form to which the initial Contributor has attached
+ the notice in Exhibit A, the Executable Form of such Source Code
+ Form, and Modifications of such Source Code Form, in each case
+ including portions thereof.
+
+1.5. "Incompatible With Secondary Licenses"
+ means
+
+ (a) that the initial Contributor has attached the notice described
+ in Exhibit B to the Covered Software; or
+
+ (b) that the Covered Software was made available under the terms of
+ version 1.1 or earlier of the License, but not also under the
+ terms of a Secondary License.
+
+1.6. "Executable Form"
+ means any form of the work other than Source Code Form.
+
+1.7. "Larger Work"
+ means a work that combines Covered Software with other material, in
+ a separate file or files, that is not Covered Software.
+
+1.8. "License"
+ means this document.
+
+1.9. "Licensable"
+ means having the right to grant, to the maximum extent possible,
+ whether at the time of the initial grant or subsequently, any and
+ all of the rights conveyed by this License.
+
+1.10. "Modifications"
+ means any of the following:
+
+ (a) any file in Source Code Form that results from an addition to,
+ deletion from, or modification of the contents of Covered
+ Software; or
+
+ (b) any new file in Source Code Form that contains any Covered
+ Software.
+
+1.11. "Patent Claims" of a Contributor
+ means any patent claim(s), including without limitation, method,
+ process, and apparatus claims, in any patent Licensable by such
+ Contributor that would be infringed, but for the grant of the
+ License, by the making, using, selling, offering for sale, having
+ made, import, or transfer of either its Contributions or its
+ Contributor Version.
+
+1.12. "Secondary License"
+ means either the GNU General Public License, Version 2.0, the GNU
+ Lesser General Public License, Version 2.1, the GNU Affero General
+ Public License, Version 3.0, or any later versions of those
+ licenses.
+
+1.13. "Source Code Form"
+ means the form of the work preferred for making modifications.
+
+1.14. "You" (or "Your")
+ means an individual or a legal entity exercising rights under this
+ License. For legal entities, "You" includes any entity that
+ controls, is controlled by, or is under common control with You. For
+ purposes of this definition, "control" means (a) the power, direct
+ or indirect, to cause the direction or management of such entity,
+ whether by contract or otherwise, or (b) ownership of more than
+ fifty percent (50%) of the outstanding shares or beneficial
+ ownership of such entity.
+
+2. License Grants and Conditions
+--------------------------------
+
+2.1. Grants
+
+Each Contributor hereby grants You a world-wide, royalty-free,
+non-exclusive license:
+
+(a) under intellectual property rights (other than patent or trademark)
+ Licensable by such Contributor to use, reproduce, make available,
+ modify, display, perform, distribute, and otherwise exploit its
+ Contributions, either on an unmodified basis, with Modifications, or
+ as part of a Larger Work; and
+
+(b) under Patent Claims of such Contributor to make, use, sell, offer
+ for sale, have made, import, and otherwise transfer either its
+ Contributions or its Contributor Version.
+
+2.2. Effective Date
+
+The licenses granted in Section 2.1 with respect to any Contribution
+become effective for each Contribution on the date the Contributor first
+distributes such Contribution.
+
+2.3. Limitations on Grant Scope
+
+The licenses granted in this Section 2 are the only rights granted under
+this License. No additional rights or licenses will be implied from the
+distribution or licensing of Covered Software under this License.
+Notwithstanding Section 2.1(b) above, no patent license is granted by a
+Contributor:
+
+(a) for any code that a Contributor has removed from Covered Software;
+ or
+
+(b) for infringements caused by: (i) Your and any other third party's
+ modifications of Covered Software, or (ii) the combination of its
+ Contributions with other software (except as part of its Contributor
+ Version); or
+
+(c) under Patent Claims infringed by Covered Software in the absence of
+ its Contributions.
+
+This License does not grant any rights in the trademarks, service marks,
+or logos of any Contributor (except as may be necessary to comply with
+the notice requirements in Section 3.4).
+
+2.4. Subsequent Licenses
+
+No Contributor makes additional grants as a result of Your choice to
+distribute the Covered Software under a subsequent version of this
+License (see Section 10.2) or under the terms of a Secondary License (if
+permitted under the terms of Section 3.3).
+
+2.5. Representation
+
+Each Contributor represents that the Contributor believes its
+Contributions are its original creation(s) or it has sufficient rights
+to grant the rights to its Contributions conveyed by this License.
+
+2.6. Fair Use
+
+This License is not intended to limit any rights You have under
+applicable copyright doctrines of fair use, fair dealing, or other
+equivalents.
+
+2.7. Conditions
+
+Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted
+in Section 2.1.
+
+3. Responsibilities
+-------------------
+
+3.1. Distribution of Source Form
+
+All distribution of Covered Software in Source Code Form, including any
+Modifications that You create or to which You contribute, must be under
+the terms of this License. You must inform recipients that the Source
+Code Form of the Covered Software is governed by the terms of this
+License, and how they can obtain a copy of this License. You may not
+attempt to alter or restrict the recipients' rights in the Source Code
+Form.
+
+3.2. Distribution of Executable Form
+
+If You distribute Covered Software in Executable Form then:
+
+(a) such Covered Software must also be made available in Source Code
+ Form, as described in Section 3.1, and You must inform recipients of
+ the Executable Form how they can obtain a copy of such Source Code
+ Form by reasonable means in a timely manner, at a charge no more
+ than the cost of distribution to the recipient; and
+
+(b) You may distribute such Executable Form under the terms of this
+ License, or sublicense it under different terms, provided that the
+ license for the Executable Form does not attempt to limit or alter
+ the recipients' rights in the Source Code Form under this License.
+
+3.3. Distribution of a Larger Work
+
+You may create and distribute a Larger Work under terms of Your choice,
+provided that You also comply with the requirements of this License for
+the Covered Software. If the Larger Work is a combination of Covered
+Software with a work governed by one or more Secondary Licenses, and the
+Covered Software is not Incompatible With Secondary Licenses, this
+License permits You to additionally distribute such Covered Software
+under the terms of such Secondary License(s), so that the recipient of
+the Larger Work may, at their option, further distribute the Covered
+Software under the terms of either this License or such Secondary
+License(s).
+
+3.4. Notices
+
+You may not remove or alter the substance of any license notices
+(including copyright notices, patent notices, disclaimers of warranty,
+or limitations of liability) contained within the Source Code Form of
+the Covered Software, except that You may alter any license notices to
+the extent required to remedy known factual inaccuracies.
+
+3.5. Application of Additional Terms
+
+You may choose to offer, and to charge a fee for, warranty, support,
+indemnity or liability obligations to one or more recipients of Covered
+Software. However, You may do so only on Your own behalf, and not on
+behalf of any Contributor. You must make it absolutely clear that any
+such warranty, support, indemnity, or liability obligation is offered by
+You alone, and You hereby agree to indemnify every Contributor for any
+liability incurred by such Contributor as a result of warranty, support,
+indemnity or liability terms You offer. You may include additional
+disclaimers of warranty and limitations of liability specific to any
+jurisdiction.
+
+4. Inability to Comply Due to Statute or Regulation
+---------------------------------------------------
+
+If it is impossible for You to comply with any of the terms of this
+License with respect to some or all of the Covered Software due to
+statute, judicial order, or regulation then You must: (a) comply with
+the terms of this License to the maximum extent possible; and (b)
+describe the limitations and the code they affect. Such description must
+be placed in a text file included with all distributions of the Covered
+Software under this License. Except to the extent prohibited by statute
+or regulation, such description must be sufficiently detailed for a
+recipient of ordinary skill to be able to understand it.
+
+5. Termination
+--------------
+
+5.1. The rights granted under this License will terminate automatically
+if You fail to comply with any of its terms. However, if You become
+compliant, then the rights granted under this License from a particular
+Contributor are reinstated (a) provisionally, unless and until such
+Contributor explicitly and finally terminates Your grants, and (b) on an
+ongoing basis, if such Contributor fails to notify You of the
+non-compliance by some reasonable means prior to 60 days after You have
+come back into compliance. Moreover, Your grants from a particular
+Contributor are reinstated on an ongoing basis if such Contributor
+notifies You of the non-compliance by some reasonable means, this is the
+first time You have received notice of non-compliance with this License
+from such Contributor, and You become compliant prior to 30 days after
+Your receipt of the notice.
+
+5.2. If You initiate litigation against any entity by asserting a patent
+infringement claim (excluding declaratory judgment actions,
+counter-claims, and cross-claims) alleging that a Contributor Version
+directly or indirectly infringes any patent, then the rights granted to
+You by any and all Contributors for the Covered Software under Section
+2.1 of this License shall terminate.
+
+5.3. In the event of termination under Sections 5.1 or 5.2 above, all
+end user license agreements (excluding distributors and resellers) which
+have been validly granted by You or Your distributors under this License
+prior to termination shall survive termination.
+
+************************************************************************
+* *
+* 6. Disclaimer of Warranty *
+* ------------------------- *
+* *
+* Covered Software is provided under this License on an "as is" *
+* basis, without warranty of any kind, either expressed, implied, or *
+* statutory, including, without limitation, warranties that the *
+* Covered Software is free of defects, merchantable, fit for a *
+* particular purpose or non-infringing. The entire risk as to the *
+* quality and performance of the Covered Software is with You. *
+* Should any Covered Software prove defective in any respect, You *
+* (not any Contributor) assume the cost of any necessary servicing, *
+* repair, or correction. This disclaimer of warranty constitutes an *
+* essential part of this License. No use of any Covered Software is *
+* authorized under this License except under this disclaimer. *
+* *
+************************************************************************
+
+************************************************************************
+* *
+* 7. Limitation of Liability *
+* -------------------------- *
+* *
+* Under no circumstances and under no legal theory, whether tort *
+* (including negligence), contract, or otherwise, shall any *
+* Contributor, or anyone who distributes Covered Software as *
+* permitted above, be liable to You for any direct, indirect, *
+* special, incidental, or consequential damages of any character *
+* including, without limitation, damages for lost profits, loss of *
+* goodwill, work stoppage, computer failure or malfunction, or any *
+* and all other commercial damages or losses, even if such party *
+* shall have been informed of the possibility of such damages. This *
+* limitation of liability shall not apply to liability for death or *
+* personal injury resulting from such party's negligence to the *
+* extent applicable law prohibits such limitation. Some *
+* jurisdictions do not allow the exclusion or limitation of *
+* incidental or consequential damages, so this exclusion and *
+* limitation may not apply to You. *
+* *
+************************************************************************
+
+8. Litigation
+-------------
+
+Any litigation relating to this License may be brought only in the
+courts of a jurisdiction where the defendant maintains its principal
+place of business and such litigation shall be governed by laws of that
+jurisdiction, without reference to its conflict-of-law provisions.
+Nothing in this Section shall prevent a party's ability to bring
+cross-claims or counter-claims.
+
+9. Miscellaneous
+----------------
+
+This License represents the complete agreement concerning the subject
+matter hereof. If any provision of this License is held to be
+unenforceable, such provision shall be reformed only to the extent
+necessary to make it enforceable. Any law or regulation which provides
+that the language of a contract shall be construed against the drafter
+shall not be used to construe this License against a Contributor.
+
+10. Versions of the License
+---------------------------
+
+10.1. New Versions
+
+Mozilla Foundation is the license steward. Except as provided in Section
+10.3, no one other than the license steward has the right to modify or
+publish new versions of this License. Each version will be given a
+distinguishing version number.
+
+10.2. Effect of New Versions
+
+You may distribute the Covered Software under the terms of the version
+of the License under which You originally received the Covered Software,
+or under the terms of any subsequent version published by the license
+steward.
+
+10.3. Modified Versions
+
+If you create software not governed by this License, and you want to
+create a new license for such software, you may create and use a
+modified version of this License if you rename the license and remove
+any references to the name of the license steward (except to note that
+such modified license differs from this License).
+
+10.4. Distributing Source Code Form that is Incompatible With Secondary
+Licenses
+
+If You choose to distribute Source Code Form that is Incompatible With
+Secondary Licenses under the terms of this version of the License, the
+notice described in Exhibit B of this License must be attached.
+
+Exhibit A - Source Code Form License Notice
+-------------------------------------------
+
+ This Source Code Form is subject to the terms of the Mozilla Public
+ License, v. 2.0. If a copy of the MPL was not distributed with this
+ file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+If it is not possible or desirable to put the notice in a particular
+file, then You may include the notice in a location (such as a LICENSE
+file in a relevant directory) where a recipient would be likely to look
+for such a notice.
+
+You may add additional accurate notices of copyright ownership.
+
+Exhibit B - "Incompatible With Secondary Licenses" Notice
+---------------------------------------------------------
+
+ This Source Code Form is "Incompatible With Secondary Licenses", as
+ defined by the Mozilla Public License, v. 2.0.
+
+
+========================================================================
+entities 6.0.1
+License: BSD-2-Clause
+https://github.com/fb55/entities#readme
+
+LICENSE
+Copyright (c) Felix Böhm
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+
+Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
+
+Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
+
+THIS IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS,
+EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+
+========================================================================
+es-toolkit 1.52.0
+License: MIT
+https://es-toolkit.dev
+
+LICENSE
+MIT License
+
+Copyright (c) 2024 Viva Republica, Inc.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+
+
+NOTICE
+es-toolkit
+
+Parts of the test suite and compatibility layer in es-toolkit/compat are
+derived from Lodash (https://github.com/lodash/lodash).
+
+Lodash copyright notice and MIT permission notice:
+
+Copyright OpenJS Foundation and other contributors
+
+Based on Underscore.js, copyright Jeremy Ashkenas,
+DocumentCloud and Investigative Reporters & Editors
+
+This software consists of voluntary contributions made by many
+individuals. For exact contribution history, see the revision history
+available at https://github.com/lodash/lodash
+
+The following license applies to all parts of this software except as
+documented below:
+
+====
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+
+========================================================================
+escape-string-regexp 5.0.0
+License: MIT
+https://github.com/sindresorhus/escape-string-regexp#readme
+
+license
+MIT License
+
+Copyright (c) Sindre Sorhus (https://sindresorhus.com)
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+
+========================================================================
+estree-util-is-identifier-name 3.0.0
+License: MIT
+https://github.com/syntax-tree/estree-util-is-identifier-name#readme
+
+license
+(The MIT License)
+
+Copyright (c) 2020 Titus Wormer
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+'Software'), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+
+========================================================================
+extend 3.0.2
+License: MIT
+https://github.com/justmoon/node-extend#readme
+
+LICENSE
+The MIT License (MIT)
+
+Copyright (c) 2014 Stefan Thomas
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+
+
+========================================================================
+fastdom 1.0.12
+License: MIT
+https://github.com/wilsonpage/fastdom
+
+License text from published package README.md:
+
+## License
+
+(The MIT License)
+
+Copyright (c) 2016 Wilson Page
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the 'Software'), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+
+========================================================================
+fault 1.0.4
+License: MIT
+https://github.com/wooorm/fault#readme
+
+license
+(The MIT License)
+
+Copyright (c) 2015 Titus Wormer
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+'Software'), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+
+========================================================================
+format 0.2.2
+License: MIT
+http://samhuri.net/proj/format
+
+License text from published package Readme.md:
+
+License
+-------
+
+Copyright 2010 - 2014 Sami Samhuri sami@samhuri.net
+
+[MIT license](http://sjs.mit-license.org)
+
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the 'Software'), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+
+========================================================================
+hachure-fill 0.5.2
+License: MIT
+https://github.com/pshihn/hachure-fill#readme
+
+LICENSE
+MIT License
+
+Copyright (c) 2023 Preet Shihn
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+
+
+========================================================================
+hast-util-from-dom 5.0.1
+License: ISC
+https://github.com/syntax-tree/hast-util-from-dom#readme
+
+license
+(ISC License)
+
+Copyright (c) Keith McKnight
+
+Permission to use, copy, modify, and/or distribute this software for any purpose
+with or without fee is hereby granted, provided that the above copyright notice
+and this permission notice appear in all copies.
+
+THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
+REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
+INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
+OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
+TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
+THIS SOFTWARE.
+
+
+========================================================================
+hast-util-from-html 2.0.3
+License: MIT
+https://github.com/syntax-tree/hast-util-from-html#readme
+
+license
+(The MIT License)
+
+Copyright (c) 2022 Titus Wormer
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+'Software'), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+
+========================================================================
+hast-util-from-html-isomorphic 2.0.0
+License: MIT
+https://github.com/syntax-tree/hast-util-from-html-isomorphic#readme
+
+license
+(The MIT License)
+
+Copyright (c) 2023 Remco Haszing
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+'Software'), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+
+========================================================================
+hast-util-from-parse5 8.0.3
+License: MIT
+https://github.com/syntax-tree/hast-util-from-parse5#readme
+
+license
+(The MIT License)
+
+Copyright (c) Titus Wormer
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+'Software'), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+
+========================================================================
+hast-util-is-element 3.0.0
+License: MIT
+https://github.com/syntax-tree/hast-util-is-element#readme
+
+license
+(The MIT License)
+
+Copyright (c) 2016 Titus Wormer
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+'Software'), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+
+========================================================================
+hast-util-parse-selector 4.0.0
+License: MIT
+https://github.com/syntax-tree/hast-util-parse-selector#readme
+
+license
+(The MIT License)
+
+Copyright (c) 2016 Titus Wormer
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+'Software'), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+
+========================================================================
+hast-util-to-jsx-runtime 2.3.6
+License: MIT
+https://github.com/syntax-tree/hast-util-to-jsx-runtime#readme
+
+license
+(The MIT License)
+
+Copyright (c) Titus Wormer
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+'Software'), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+
+========================================================================
+hast-util-to-text 4.0.2
+License: MIT
+https://github.com/syntax-tree/hast-util-to-text#readme
+
+license
+(The MIT License)
+
+Copyright (c) 2019 Titus Wormer
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+'Software'), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+
+========================================================================
+hast-util-whitespace 3.0.0
+License: MIT
+https://github.com/syntax-tree/hast-util-whitespace#readme
+
+license
+(The MIT License)
+
+Copyright (c) 2016 Titus Wormer
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+'Software'), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+
+========================================================================
+hastscript 9.0.1
+License: MIT
+https://github.com/syntax-tree/hastscript#readme
+
+license
+(The MIT License)
+
+Copyright (c) Titus Wormer
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+'Software'), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+
+========================================================================
+highlight.js 10.7.3, 11.12.0
+License: BSD-3-Clause
+https://highlightjs.org/
+
+LICENSE
+BSD 3-Clause License
+
+Copyright (c) 2006, Ivan Sagalaev.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+* Redistributions of source code must retain the above copyright notice, this
+ list of conditions and the following disclaimer.
+
+* Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+
+* Neither the name of the copyright holder nor the names of its
+ contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+
+========================================================================
+highlightjs-vue 1.0.0
+License: CC0-1.0
+https://github.com/highlightjs/highlightjs-vue#readme
+
+========================================================================
+html-url-attributes 3.0.1
+License: MIT
+https://github.com/rehypejs/rehype-minify/tree/main#readme
+
+license
+(The MIT License)
+
+Copyright (c) Titus Wormer
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+
+
+========================================================================
+iconv-lite 0.6.3
+License: MIT
+https://github.com/ashtuchkin/iconv-lite
+
+LICENSE
+Copyright (c) 2011 Alexander Shtuchkin
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+
+
+========================================================================
+import-meta-resolve 4.2.0
+License: MIT
+https://github.com/wooorm/import-meta-resolve#readme
+
+license
+(The MIT License)
+
+Copyright (c) Titus Wormer
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+'Software'), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+---
+
+This is a derivative work based on:
+.
+Which is licensed:
+
+"""
+Copyright Node.js contributors. All rights reserved.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to
+deal in the Software without restriction, including without limitation the
+rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+sell copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+IN THE SOFTWARE.
+"""
+
+This license applies to parts of Node.js originating from the
+https://github.com/joyent/node repository:
+
+"""
+Copyright Joyent, Inc. and other Node contributors. All rights reserved.
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to
+deal in the Software without restriction, including without limitation the
+rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+sell copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+IN THE SOFTWARE.
+"""
+
+
+========================================================================
+inline-style-parser 0.2.7
+License: MIT
+https://github.com/remarkablemark/inline-style-parser#readme
+
+LICENSE
+(The MIT License)
+
+Copyright (c) 2012 TJ Holowaychuk
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the 'Software'), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+
+========================================================================
+internmap 1.0.1, 2.0.3
+License: ISC
+https://github.com/mbostock/internmap/
+
+LICENSE
+Copyright 2021 Mike Bostock
+
+Permission to use, copy, modify, and/or distribute this software for any purpose
+with or without fee is hereby granted, provided that the above copyright notice
+and this permission notice appear in all copies.
+
+THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
+REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
+INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
+OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
+TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
+THIS SOFTWARE.
+
+
+========================================================================
+is-alphabetical 2.0.1
+License: MIT
+https://github.com/wooorm/is-alphabetical#readme
+
+license
+(The MIT License)
+
+Copyright (c) 2016 Titus Wormer
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+'Software'), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+
+========================================================================
+is-alphanumerical 2.0.1
+License: MIT
+https://github.com/wooorm/is-alphanumerical#readme
+
+license
+(The MIT License)
+
+Copyright (c) 2016 Titus Wormer
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+'Software'), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+
+========================================================================
+is-decimal 2.0.1
+License: MIT
+https://github.com/wooorm/is-decimal#readme
+
+license
+(The MIT License)
+
+Copyright (c) 2016 Titus Wormer
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+'Software'), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+
+========================================================================
+is-hexadecimal 2.0.1
+License: MIT
+https://github.com/wooorm/is-hexadecimal#readme
+
+license
+(The MIT License)
+
+Copyright (c) 2016 Titus Wormer
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+'Software'), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+
+========================================================================
+is-mobile 5.0.0
+License: MIT
+https://github.com/juliangruber/is-mobile
+
+License text from published package README.md:
+
+## License
+
+(MIT)
+
+Copyright (c) 2013 Julian Gruber <julian@juliangruber.com>
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal in
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+of the Software, and to permit persons to whom the Software is furnished to do
+so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+
+
+========================================================================
+is-plain-obj 4.1.0
+License: MIT
+https://github.com/sindresorhus/is-plain-obj#readme
+
+license
+MIT License
+
+Copyright (c) Sindre Sorhus (https://sindresorhus.com)
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+
+========================================================================
+json2mq 0.2.0
+License: MIT
+https://github.com/akiran/json2mq
+
+LICENSE
+The MIT License (MIT)
+
+Copyright (c) 2014 Kiran Abburi
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+
+
+
+========================================================================
+katex 0.16.47
+License: MIT
+https://katex.org
+
+LICENSE
+The MIT License (MIT)
+
+Copyright (c) 2013-2020 Khan Academy and other contributors
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+
+
+========================================================================
+khroma 2.1.0
+License: MIT (license file; package metadata omitted SPDX)
+https://github.com/fabiospampinato/khroma#readme
+
+license
+The MIT License (MIT)
+
+Copyright (c) 2019-present Fabio Spampinato, Andrew Maney
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and associated documentation files (the "Software"),
+to deal in the Software without restriction, including without limitation
+the rights to use, copy, modify, merge, publish, distribute, sublicense,
+and/or sell copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
+
+
+========================================================================
+layout-base 1.0.2, 2.0.1
+License: MIT
+https://github.com/iVis-at-Bilkent/layout-base#readme
+
+LICENSE
+MIT License
+
+Copyright (c) 2019 iVis@Bilkent
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+
+
+========================================================================
+lodash-es 4.18.1
+License: MIT
+https://lodash.com/custom-builds
+
+LICENSE
+Copyright OpenJS Foundation and other contributors
+
+Based on Underscore.js, copyright Jeremy Ashkenas,
+DocumentCloud and Investigative Reporters & Editors
+
+This software consists of voluntary contributions made by many
+individuals. For exact contribution history, see the revision history
+available at https://github.com/lodash/lodash
+
+The following license applies to all parts of this software except as
+documented below:
+
+====
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+====
+
+Copyright and related rights for sample code are waived via CC0. Sample
+code is defined as all source code displayed within the prose of the
+documentation.
+
+CC0: http://creativecommons.org/publicdomain/zero/1.0/
+
+====
+
+Files located in the node_modules and vendor directories are externally
+maintained libraries used by this software which have their own
+licenses; we recommend you read them, as their terms may differ from the
+terms above.
+
+
+========================================================================
+lodash.throttle 4.1.1
+License: MIT
+https://lodash.com/
+
+LICENSE
+Copyright jQuery Foundation and other contributors
+
+Based on Underscore.js, copyright Jeremy Ashkenas,
+DocumentCloud and Investigative Reporters & Editors
+
+This software consists of voluntary contributions made by many
+individuals. For exact contribution history, see the revision history
+available at https://github.com/lodash/lodash
+
+The following license applies to all parts of this software except as
+documented below:
+
+====
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+====
+
+Copyright and related rights for sample code are waived via CC0. Sample
+code is defined as all source code displayed within the prose of the
+documentation.
+
+CC0: http://creativecommons.org/publicdomain/zero/1.0/
+
+====
+
+Files located in the node_modules and vendor directories are externally
+maintained libraries used by this software which have their own
+licenses; we recommend you read them, as their terms may differ from the
+terms above.
+
+
+========================================================================
+longest-streak 3.1.0
+License: MIT
+https://github.com/wooorm/longest-streak#readme
+
+license
+(The MIT License)
+
+Copyright (c) 2015 Titus Wormer
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+'Software'), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+
+========================================================================
+lowlight 1.20.0
+License: MIT
+https://github.com/wooorm/lowlight#readme
+
+license
+(The MIT License)
+
+Copyright (c) 2016 Titus Wormer
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+'Software'), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+
+========================================================================
+markdown-table 3.0.4
+License: MIT
+https://github.com/wooorm/markdown-table#readme
+
+license
+(The MIT License)
+
+Copyright (c) Titus Wormer
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+'Software'), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+
+========================================================================
+marked 16.4.2
+License: MIT
+https://marked.js.org
+
+LICENSE.md
+# License information
+
+## Contribution License Agreement
+
+If you contribute code to this project, you are implicitly allowing your code
+to be distributed under the MIT license. You are also implicitly verifying that
+all code is your original work. ``
+
+## Marked
+
+Copyright (c) 2018+, MarkedJS (https://github.com/markedjs/)
+Copyright (c) 2011-2018, Christopher Jeffrey (https://github.com/chjj/)
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+
+## Markdown
+
+Copyright © 2004, John Gruber
+http://daringfireball.net/
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+
+* Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
+* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
+* Neither the name “Markdown” nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
+
+This software is provided by the copyright holders and contributors “as is” and any express or implied warranties, including, but not limited to, the implied warranties of merchantability and fitness for a particular purpose are disclaimed. In no event shall the copyright owner or contributors be liable for any direct, indirect, incidental, special, exemplary, or consequential damages (including, but not limited to, procurement of substitute goods or services; loss of use, data, or profits; or business interruption) however caused and on any theory of liability, whether in contract, strict liability, or tort (including negligence or otherwise) arising in any way out of the use of this software, even if advised of the possibility of such damage.
+
+
+========================================================================
+mdast-util-find-and-replace 3.0.2
+License: MIT
+https://github.com/syntax-tree/mdast-util-find-and-replace#readme
+
+license
+(The MIT License)
+
+Copyright (c) Titus Wormer
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+'Software'), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+
+========================================================================
+mdast-util-from-markdown 2.0.3
+License: MIT
+https://github.com/syntax-tree/mdast-util-from-markdown#readme
+
+license
+(The MIT License)
+
+Copyright (c) Titus Wormer
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+'Software'), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+
+========================================================================
+mdast-util-gfm 3.1.0
+License: MIT
+https://github.com/syntax-tree/mdast-util-gfm#readme
+
+license
+(The MIT License)
+
+Copyright (c) Titus Wormer
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+'Software'), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+
+========================================================================
+mdast-util-gfm-autolink-literal 2.0.1
+License: MIT
+https://github.com/syntax-tree/mdast-util-gfm-autolink-literal#readme
+
+license
+(The MIT License)
+
+Copyright (c) 2020 Titus Wormer
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+'Software'), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+
+========================================================================
+mdast-util-gfm-footnote 2.1.0
+License: MIT
+https://github.com/syntax-tree/mdast-util-gfm-footnote#readme
+
+license
+(The MIT License)
+
+Copyright (c) Titus Wormer
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+'Software'), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+
+========================================================================
+mdast-util-gfm-strikethrough 2.0.0
+License: MIT
+https://github.com/syntax-tree/mdast-util-gfm-strikethrough#readme
+
+license
+(The MIT License)
+
+Copyright (c) 2020 Titus Wormer
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+'Software'), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+
+========================================================================
+mdast-util-gfm-table 2.0.0
+License: MIT
+https://github.com/syntax-tree/mdast-util-gfm-table#readme
+
+license
+(The MIT License)
+
+Copyright (c) 2020 Titus Wormer
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+'Software'), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+
+========================================================================
+mdast-util-gfm-task-list-item 2.0.0
+License: MIT
+https://github.com/syntax-tree/mdast-util-gfm-task-list-item#readme
+
+license
+(The MIT License)
+
+Copyright (c) 2020 Titus Wormer
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+'Software'), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+
+========================================================================
+mdast-util-math 3.0.0
+License: MIT
+https://github.com/syntax-tree/mdast-util-math#readme
+
+license
+(The MIT License)
+
+Copyright (c) 2020 Titus Wormer
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+'Software'), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+
+========================================================================
+mdast-util-mdx-expression 2.0.1
+License: MIT
+https://github.com/syntax-tree/mdast-util-mdx-expression#readme
+
+license
+(The MIT License)
+
+Copyright (c) 2020 Titus Wormer
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+'Software'), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+
+========================================================================
+mdast-util-mdx-jsx 3.2.0
+License: MIT
+https://github.com/syntax-tree/mdast-util-mdx-jsx#readme
+
+license
+(The MIT License)
+
+Copyright (c) 2020 Titus Wormer
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+'Software'), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+
+========================================================================
+mdast-util-mdxjs-esm 2.0.1
+License: MIT
+https://github.com/syntax-tree/mdast-util-mdxjs-esm#readme
+
+license
+(The MIT License)
+
+Copyright (c) 2020 Titus Wormer
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+'Software'), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+
+========================================================================
+mdast-util-phrasing 4.1.0
+License: MIT
+https://github.com/syntax-tree/mdast-util-phrasing#readme
+
+license
+(The MIT License)
+
+Copyright (c) 2017 Titus Wormer
+Copyright (c) 2017 Victor Felder
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+'Software'), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+
+========================================================================
+mdast-util-to-hast 13.2.1
+License: MIT
+https://github.com/syntax-tree/mdast-util-to-hast#readme
+
+license
+(The MIT License)
+
+Copyright (c) 2016 Titus Wormer
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+'Software'), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+
+========================================================================
+mdast-util-to-markdown 2.1.2
+License: MIT
+https://github.com/syntax-tree/mdast-util-to-markdown#readme
+
+license
+(The MIT License)
+
+Copyright (c) Titus Wormer
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+'Software'), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+
+========================================================================
+mdast-util-to-string 4.0.0
+License: MIT
+https://github.com/syntax-tree/mdast-util-to-string#readme
+
+license
+(The MIT License)
+
+Copyright (c) 2015 Titus Wormer
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+'Software'), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+
+========================================================================
+mermaid 11.17.2
+License: MIT
+https://github.com/mermaid-js/mermaid#readme
+
+LICENSE
+The MIT License (MIT)
+
+Copyright (c) 2014 - 2022 Knut Sveidqvist
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+
+
+========================================================================
+micromark 4.0.2
+License: MIT
+https://github.com/micromark/micromark/tree/main#readme
+
+license
+(The MIT License)
+
+Copyright (c) Titus Wormer
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+'Software'), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+
+========================================================================
+micromark-core-commonmark 2.0.3
+License: MIT
+https://github.com/micromark/micromark/tree/main#readme
+
+license
+(The MIT License)
+
+Copyright (c) Titus Wormer
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+'Software'), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+
+========================================================================
+micromark-extension-gfm 3.0.0
+License: MIT
+https://github.com/micromark/micromark-extension-gfm#readme
+
+license
+(The MIT License)
+
+Copyright (c) 2020 Titus Wormer
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+'Software'), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+
+========================================================================
+micromark-extension-gfm-autolink-literal 2.1.0
+License: MIT
+https://github.com/micromark/micromark-extension-gfm-autolink-literal#readme
+
+license
+(The MIT License)
+
+Copyright (c) 2020 Titus Wormer
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+'Software'), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+
+========================================================================
+micromark-extension-gfm-footnote 2.1.0
+License: MIT
+https://github.com/micromark/micromark-extension-gfm-footnote#readme
+
+license
+(The MIT License)
+
+Copyright (c) 2021 Titus Wormer
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+'Software'), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+
+========================================================================
+micromark-extension-gfm-strikethrough 2.1.0
+License: MIT
+https://github.com/micromark/micromark-extension-gfm-strikethrough#readme
+
+license
+(The MIT License)
+
+Copyright (c) 2020 Titus Wormer
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+'Software'), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+
+========================================================================
+micromark-extension-gfm-table 2.1.1
+License: MIT
+https://github.com/micromark/micromark-extension-gfm-table#readme
+
+license
+(The MIT License)
+
+Copyright (c) Titus Wormer
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+'Software'), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+
+========================================================================
+micromark-extension-gfm-tagfilter 2.0.0
+License: MIT
+https://github.com/micromark/micromark-extension-gfm-tagfilter#readme
+
+license
+(The MIT License)
+
+Copyright (c) 2020 Titus Wormer
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+'Software'), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+
+========================================================================
+micromark-extension-gfm-task-list-item 2.1.0
+License: MIT
+https://github.com/micromark/micromark-extension-gfm-task-list-item#readme
+
+license
+(The MIT License)
+
+Copyright (c) 2020 Titus Wormer
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+'Software'), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+
+========================================================================
+micromark-extension-math 3.1.0
+License: MIT
+https://github.com/micromark/micromark-extension-math#readme
+
+license
+(The MIT License)
+
+Copyright (c) 2020 Titus Wormer
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+'Software'), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+
+========================================================================
+micromark-factory-destination 2.0.1
+License: MIT
+https://github.com/micromark/micromark/tree/main#readme
+
+license
+(The MIT License)
+
+Copyright (c) Titus Wormer
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+'Software'), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+
+========================================================================
+micromark-factory-label 2.0.1
+License: MIT
+https://github.com/micromark/micromark/tree/main#readme
+
+license
+(The MIT License)
+
+Copyright (c) Titus Wormer
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+'Software'), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+
+========================================================================
+micromark-factory-space 2.0.1
+License: MIT
+https://github.com/micromark/micromark/tree/main#readme
+
+license
+(The MIT License)
+
+Copyright (c) Titus Wormer
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+'Software'), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+
+========================================================================
+micromark-factory-title 2.0.1
+License: MIT
+https://github.com/micromark/micromark/tree/main#readme
+
+license
+(The MIT License)
+
+Copyright (c) Titus Wormer
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+'Software'), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+
+========================================================================
+micromark-factory-whitespace 2.0.1
+License: MIT
+https://github.com/micromark/micromark/tree/main#readme
+
+license
+(The MIT License)
+
+Copyright (c) Titus Wormer
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+'Software'), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+
+========================================================================
+micromark-util-character 2.1.1
+License: MIT
+https://github.com/micromark/micromark/tree/main#readme
+
+license
+(The MIT License)
+
+Copyright (c) Titus Wormer
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+'Software'), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+
+========================================================================
+micromark-util-chunked 2.0.1
+License: MIT
+https://github.com/micromark/micromark/tree/main#readme
+
+license
+(The MIT License)
+
+Copyright (c) Titus Wormer
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+'Software'), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+
+========================================================================
+micromark-util-classify-character 2.0.1
+License: MIT
+https://github.com/micromark/micromark/tree/main#readme
+
+license
+(The MIT License)
+
+Copyright (c) Titus Wormer
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+'Software'), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+
+========================================================================
+micromark-util-combine-extensions 2.0.1
+License: MIT
+https://github.com/micromark/micromark/tree/main#readme
+
+license
+(The MIT License)
+
+Copyright (c) Titus Wormer
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+'Software'), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+
+========================================================================
+micromark-util-decode-numeric-character-reference 2.0.2
+License: MIT
+https://github.com/micromark/micromark/tree/main#readme
+
+license
+(The MIT License)
+
+Copyright (c) Titus Wormer
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+'Software'), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+
+========================================================================
+micromark-util-decode-string 2.0.1
+License: MIT
+https://github.com/micromark/micromark/tree/main#readme
+
+license
+(The MIT License)
+
+Copyright (c) Titus Wormer
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+'Software'), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+
+========================================================================
+micromark-util-encode 2.0.1
+License: MIT
+https://github.com/micromark/micromark/tree/main#readme
+
+license
+(The MIT License)
+
+Copyright (c) Titus Wormer
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+'Software'), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+
+========================================================================
+micromark-util-html-tag-name 2.0.1
+License: MIT
+https://github.com/micromark/micromark/tree/main#readme
+
+license
+(The MIT License)
+
+Copyright (c) Titus Wormer
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+'Software'), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+
+========================================================================
+micromark-util-normalize-identifier 2.0.1
+License: MIT
+https://github.com/micromark/micromark/tree/main#readme
+
+license
+(The MIT License)
+
+Copyright (c) Titus Wormer
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+'Software'), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+
+========================================================================
+micromark-util-resolve-all 2.0.1
+License: MIT
+https://github.com/micromark/micromark/tree/main#readme
+
+license
+(The MIT License)
+
+Copyright (c) Titus Wormer
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+'Software'), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+
+========================================================================
+micromark-util-sanitize-uri 2.0.1
+License: MIT
+https://github.com/micromark/micromark/tree/main#readme
+
+license
+(The MIT License)
+
+Copyright (c) Titus Wormer
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+'Software'), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+
+========================================================================
+micromark-util-subtokenize 2.1.0
+License: MIT
+https://github.com/micromark/micromark/tree/main#readme
+
+license
+(The MIT License)
+
+Copyright (c) Titus Wormer
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+'Software'), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+
+========================================================================
+micromark-util-symbol 2.0.1
+License: MIT
+https://github.com/micromark/micromark/tree/main#readme
+
+license
+(The MIT License)
+
+Copyright (c) Titus Wormer
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+'Software'), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+
+========================================================================
+micromark-util-types 2.0.2
+License: MIT
+https://github.com/micromark/micromark/tree/main#readme
+
+license
+(The MIT License)
+
+Copyright (c) Titus Wormer
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+'Software'), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+
+========================================================================
+ms 2.1.3
+License: MIT
+https://github.com/vercel/ms#readme
+
+license.md
+The MIT License (MIT)
+
+Copyright (c) 2020 Vercel, Inc.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+
+
+========================================================================
+package-manager-detector 1.8.0
+License: MIT
+https://github.com/antfu-collective/package-manager-detector#readme
+
+LICENSE
+MIT License
+
+Copyright (c) 2020-PRESENT Anthony Fu
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+
+
+========================================================================
+parse-entities 4.0.2
+License: MIT
+https://github.com/wooorm/parse-entities#readme
+
+license
+(The MIT License)
+
+Copyright (c) Titus Wormer
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+'Software'), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+
+========================================================================
+parse5 7.3.0
+License: MIT
+https://parse5.js.org
+
+LICENSE
+Copyright (c) 2013-2019 Ivan Nikulin (ifaaan@gmail.com, https://github.com/inikulin)
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+
+
+========================================================================
+path-data-parser 0.1.0
+License: MIT
+https://github.com/pshihn/path-data-parser#readme
+
+LICENSE
+MIT License
+
+Copyright (c) 2020 Preet Shihn
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+
+
+========================================================================
+points-on-curve 0.2.0
+License: MIT
+https://github.com/pshihn/bezier-points#readme
+
+LICENSE
+MIT License
+
+Copyright (c) 2020 Preet Shihn
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+
+
+========================================================================
+points-on-path 0.2.1
+License: MIT
+https://github.com/pshihn/points-on-path#readme
+
+LICENSE
+MIT License
+
+Copyright (c) 2020 Preet
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+
+
+========================================================================
+prismjs 1.30.0
+License: MIT
+https://github.com/PrismJS/prism#readme
+
+LICENSE
+MIT LICENSE
+
+Copyright (c) 2012 Lea Verou
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+
+
+========================================================================
+property-information 7.2.0
+License: MIT
+https://github.com/wooorm/property-information#readme
+
+license
+(The MIT License)
+
+Copyright (c) Titus Wormer
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+'Software'), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+
+========================================================================
+react 19.2.8
+License: MIT
+https://react.dev/
+
+LICENSE
+MIT License
+
+Copyright (c) Meta Platforms, Inc. and affiliates.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+
+
+========================================================================
+react-dom 19.2.8
+License: MIT
+https://react.dev/
+
+LICENSE
+MIT License
+
+Copyright (c) Meta Platforms, Inc. and affiliates.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+
+
+========================================================================
+react-is 19.2.8
+License: MIT
+https://react.dev/
+
+LICENSE
+MIT License
+
+Copyright (c) Meta Platforms, Inc. and affiliates.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+
+
+========================================================================
+react-markdown 10.1.0
+License: MIT
+https://github.com/remarkjs/react-markdown#readme
+
+license
+The MIT License (MIT)
+
+Copyright (c) Espen Hovlandsdal
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+
+
+========================================================================
+react-router 7.18.3
+License: MIT
+https://github.com/remix-run/react-router#readme
+
+LICENSE.md
+MIT License
+
+Copyright (c) React Training LLC 2015-2019
+Copyright (c) Remix Software Inc. 2020-2021
+Copyright (c) Shopify Inc. 2022-2023
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+
+
+========================================================================
+react-router-dom 7.18.3
+License: MIT
+https://github.com/remix-run/react-router#readme
+
+LICENSE.md
+MIT License
+
+Copyright (c) React Training LLC 2015-2019
+Copyright (c) Remix Software Inc. 2020-2021
+Copyright (c) Shopify Inc. 2022-2023
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+
+
+========================================================================
+react-syntax-highlighter 16.1.1
+License: MIT
+https://github.com/react-syntax-highlighter/react-syntax-highlighter#readme
+
+LICENSE
+MIT License
+
+Copyright (c) 2019 Conor Hastings
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+
+
+========================================================================
+refractor 5.0.0
+License: MIT
+https://github.com/wooorm/refractor#readme
+
+license
+(The MIT License)
+
+Copyright (c) Titus Wormer
+Copyright (c) Lea Verou
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+'Software'), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+
+========================================================================
+rehype-katex 7.0.1
+License: MIT
+https://github.com/remarkjs/remark-math/tree/main#readme
+
+Source: https://github.com/remarkjs/remark-math/blob/88a9497e1ede93b958237c85edbf5651faeca7af/license
+
+(The MIT License)
+
+Copyright (c) 2017 Junyoung Choi