diff --git a/.agents/documentation-maintainer.md b/.agents/documentation-maintainer.md
new file mode 100644
index 0000000..822d86f
--- /dev/null
+++ b/.agents/documentation-maintainer.md
@@ -0,0 +1,59 @@
+# Documentation Maintainer
+
+Own the accuracy, consistency, and upkeep of the repository's user-facing documentation. Keep the root README and the VitePress site aligned with the current CodeBuddy2API implementation.
+
+## Scope
+
+This agent maintains:
+
+- `README.md` and other tracked Markdown files that describe the project.
+- The VitePress site under `docs/`, including its configuration, localized pages, navigation, and shared theme text.
+- Documentation for Docker deployment, storage backends, local development, configuration, API usage, and the management interface.
+- Links, commands, environment variables, examples, screenshots, and other references included in documentation.
+
+This agent does not modify application code, generated build output, vendored files, or unrelated documentation outside the repository.
+
+## When to Invoke
+
+Invoke this agent when a change affects:
+
+- Application behavior, public APIs, routes, UI labels, configuration, environment variables, storage, Docker deployment, or supported workflows.
+- Repository layout, local development, documentation deployment, or GitHub Pages configuration.
+- Any existing Markdown guidance, localized copy, navigation item, or documented command.
+
+Invoke it for documentation-only changes as well. Skip documentation edits only when a change is demonstrably internal and leaves every tracked document accurate; record that decision in the handoff summary.
+
+## Workflow
+
+1. Inspect the implementation diff and list every affected document.
+2. Read affected pages before editing and verify claims against the current source, UI labels, scripts, and deployment configuration.
+3. Update the root README and VitePress pages together when project-level setup or supported behavior changes.
+4. Keep Chinese, English, and Japanese pages equivalent in meaning, with natural localized wording and localized navigation and theme UI text.
+5. Keep commands, paths, environment variables, endpoint examples, storage behavior, and deployment instructions identical to the implementation.
+6. Review links, headings, sidebar nesting, code blocks, tables, and examples for stale or ambiguous content.
+7. Build the VitePress site and run the repository's formatting checks before handoff.
+
+## Writing Rules
+
+- Write this agent file entirely in English.
+- Write each document in its established locale; do not replace localized content with English.
+- Prefer task-oriented explanations that tell users what to do, what they should expect, and how to recover from common failures.
+- Use the exact labels shown by the application in the corresponding locale.
+- Avoid documenting internal implementation details unless users need them to complete a task.
+- Do not invent features, defaults, commands, links, or security requirements.
+- Keep examples safe to copy and never include real credentials or secrets.
+- Use Chinese corner brackets `「」` for quoted UI labels in Chinese documentation.
+
+## Validation
+
+Before handoff, run the most focused checks available, including:
+
+- `bun run docs:build`
+- `bun run format:check`
+- `git diff --check`
+
+When application code is part of the same change, also run the repository checks required by `AGENTS.md`.
+
+## Handoff
+
+Summarize the documents changed, locale parity, validation commands, and any check that could not run because of the environment. Do not commit or push unless the user explicitly requests it.
diff --git a/.github/dependabot.yml b/.github/dependabot.yml
index 5718028..797a74e 100644
--- a/.github/dependabot.yml
+++ b/.github/dependabot.yml
@@ -99,3 +99,14 @@ updates:
- github/*
- docker/*
- oven-sh/*
+
+ - package-ecosystem: bun
+ directory: /docs
+ schedule:
+ interval: weekly
+ day: monday
+ time: '03:00'
+ timezone: Asia/Shanghai
+ open-pull-requests-limit: 5
+ commit-message:
+ prefix: chore(deps)
diff --git a/.github/workflows/docs-pages.yml b/.github/workflows/docs-pages.yml
new file mode 100644
index 0000000..7f84ab1
--- /dev/null
+++ b/.github/workflows/docs-pages.yml
@@ -0,0 +1,57 @@
+name: Deploy Documentation
+
+on:
+ push:
+ branches: [main]
+ paths:
+ - 'docs/**'
+ - '.github/workflows/docs-pages.yml'
+ workflow_dispatch:
+
+permissions:
+ contents: read
+ pages: write
+ id-token: write
+
+concurrency:
+ group: pages
+ cancel-in-progress: true
+
+jobs:
+ build:
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout repository
+ uses: actions/checkout@v4
+
+ - name: Set up Bun
+ uses: oven-sh/setup-bun@v2
+ with:
+ bun-version: 1.3.14
+
+ - name: Install documentation dependencies
+ working-directory: docs
+ run: bun install
+
+ - name: Build documentation
+ working-directory: docs
+ run: bun run build
+
+ - name: Configure Pages
+ uses: actions/configure-pages@v5
+
+ - name: Upload Pages artifact
+ uses: actions/upload-pages-artifact@v3
+ with:
+ path: docs/.vitepress/dist
+
+ deploy:
+ needs: build
+ runs-on: ubuntu-latest
+ environment:
+ name: github-pages
+ url: ${{ steps.deployment.outputs.page_url }}
+ steps:
+ - name: Deploy to GitHub Pages
+ id: deployment
+ uses: actions/deploy-pages@v4
diff --git a/.gitignore b/.gitignore
index 4e4b8bc..3a81fff 100644
--- a/.gitignore
+++ b/.gitignore
@@ -3,6 +3,9 @@ node_modules/
# Next.js / TypeScript build outputs
.next/
+docs/.vitepress/cache/
+docs/.vitepress/dist/
+docs/node_modules/
out/
dist/
build/
@@ -32,6 +35,8 @@ config/usage/
.tmp-test-debug-usage-root/
.tmp-test-runtime-root/
.tmp-e2e/
+playwright-report/
+test-results/
# Credentials
.codebuddy_creds/
diff --git a/README.md b/README.md
index 534ca17..d4a5fc1 100644
--- a/README.md
+++ b/README.md
@@ -2,245 +2,59 @@
[](https://github.com/orangeboyChen/codebuddy2api/actions/workflows/ci-main.yml)
[](https://codecov.io/gh/orangeboyChen/codebuddy2api)
-[](https://deepwiki.com/orangeboyChen/codebuddy2api)
-Proxy CodeBuddy through OpenAI-compatible and Anthropic-compatible APIs, with a built-in admin console for credentials, access keys, usage, debug traces, and runtime settings.
-
-
-
-
+CodeBuddy2API is a self-hosted CodeBuddy gateway with OpenAI-compatible and
+Anthropic-compatible APIs, plus an admin console for credentials, access keys,
+usage, debugging, and runtime settings.
> Forked from [Sliverkiss/CodeBuddy2api](https://github.com/Sliverkiss/CodeBuddy2api).
## Quick Start
+The following Docker command uses SQLite, recommended for a single instance.
+Replace the encryption key with a long random value before production use.
+
```bash
docker run -d \
--name codebuddy2api \
--restart unless-stopped \
-p 8001:8001 \
- -v "$(pwd)/.codebuddy_data:/app/.codebuddy_data" \
- -v "$(pwd)/.codebuddy_creds:/app/.codebuddy_creds" \
+ -e CODEBUDDY_STORAGE_BACKEND=sqlite \
+ -e CODEBUDDY_STORAGE_ENCRYPTION_KEY='replace-with-a-long-random-secret' \
+ -e CODEBUDDY_STORAGE_IMPORT_LEGACY_FILES=false \
ghcr.io/orangeboychen/codebuddy2api:latest
```
-Open `http://127.0.0.1:8001/` after startup.
-
-## Storage Backends
-
-Persistence is now routed through a storage abstraction layer. Business modules do not read or write files directly.
-
-- Default backend: `file`
-- Optional database backends: `pg`, `sqlite`
-- Backend selection:
- - explicit `CODEBUDDY_STORAGE_BACKEND=file|pg|sqlite`
- - explicit `CODEBUDDY_STORAGE_PERSISTENCE=file|pg|sqlite`
- - otherwise auto-switch to `pg` when `CODEBUDDY_STORAGE_PG_URL` or `DATABASE_URL` is set
- - otherwise fall back to `file`
-
-### File Backend
-
-The file backend now writes into a dedicated data directory instead of a `config/` tree:
-
-- `.codebuddy_data/runtime.json`
-- `.codebuddy_data/access-keys.json`
-- `.codebuddy_data/debug-settings.json`
-- `.codebuddy_data/debug-logs.json`
-- `.codebuddy_data/usage-history.json`
-- `.codebuddy_data/admin-auth.json`
-- `.codebuddy_creds/*.json`
-
-Usage records, debug records, and credential rotation checkpoints are flushed in
-batches (at most once per second or after 100 pending records). File storage is
-therefore intended for a single application instance; multiple instances can
-overwrite each other's JSON documents.
-
-Optional overrides:
-
-- `CODEBUDDY_STORAGE_FILE_DIR` changes the file-backend data directory
-- `CODEBUDDY_CONFIG_PATH` is kept only for legacy compatibility with the old runtime config file path
-
-### Database Backends
-
-When `pg` or `sqlite` storage is active, runtime persistence is stored in a database instead of local files.
-
-- PostgreSQL uses `DATABASE_URL` or `CODEBUDDY_STORAGE_PG_URL` and the `codebuddy2api` schema
-- SQLite uses `CODEBUDDY_STORAGE_SQLITE_PATH`; the default is `.codebuddy_data/storage.sqlite`
-- `CODEBUDDY_STORAGE_ENCRYPTION_KEY` is required for either database backend and encrypts sensitive stored documents
-
-In database mode, local files are not used for normal runtime persistence. Runtime
-configuration, credentials, access keys, and debug settings can be read as an
-optional one-time legacy import source during initialization. Historical usage
-and debug log JSON files are not imported.
-
-Usage and debug records are stored as append-only, indexed event rows with retention
-pruning. PostgreSQL supports concurrent application instances. SQLite is a local,
-single-instance backend. Credential rotation checkpoints remain eventually
-consistent, so PostgreSQL replicas can occasionally select the same credential.
-
-The application runs the bundled Drizzle migrations during database-backend
-initialization, so deployments do not require a separate schema-creation step.
-Keep legacy files available for the first application start so its idempotent
-import can copy configuration, credentials, access keys, and admin state. Only
-set `CODEBUDDY_STORAGE_IMPORT_LEGACY_FILES=false` after that start has completed.
-
-## Run Locally
+Open `http://127.0.0.1:8001/dashboard`, add a CodeBuddy credential, then create
+an access key for API clients. Use PostgreSQL instead of SQLite for multiple
+application instances.
-### Requirements
+## Documentation
-- Bun 1.3.14+
-- Node.js 20+
+- [中文文档](https://orangeboychen.github.io/codebuddy2api/)
+- [English documentation](https://orangeboychen.github.io/codebuddy2api/en/)
+- [日本語ドキュメント](https://orangeboychen.github.io/codebuddy2api/ja/)
-### Development Mode
+Run the documentation site locally from the repository root:
```bash
bun install
-mkdir -p .codebuddy_data .codebuddy_creds
-bun run dev
-```
-
-Then open `http://127.0.0.1:3000/`.
-
-Use the admin console to complete the CodeBuddy OAuth flow or add credentials manually. Local settings and console data are stored under `.codebuddy_data/`; credentials are stored under `.codebuddy_creds/`.
-
-### Production-like Local Run
-
-```bash
-bun install
-mkdir -p .codebuddy_data .codebuddy_creds
-bun run build
-bun start
-```
-
-Then open `http://127.0.0.1:8001/`.
-
-## Admin Console
-
-The admin console uses stable routes and supports English, Japanese, and Simplified Chinese.
-
-- console route: `http://:8001/dashboard`
-- login route: `http://:8001/login`
-- locale persistence: cookie-based, no locale path prefixes
-- mobile tab bar supports horizontal scrolling
-- authenticated admins can log out from the top-right action
-
-### Admin Authentication
-
-Admin authentication is optional.
-
-- If no admin password and no passkey are configured, the admin console remains open.
-- Once an admin password or passkey is configured, `/admin-api/*` requires the admin session cookie.
-- Session transport: HTTP-only cookie
-- Session TTL: 8 hours
-
-### Password Login
-
-- first-time bootstrap: `POST /admin-api/auth/setup`
-- sign-in: `POST /admin-api/auth/session`
-- sign-out: `DELETE /admin-api/auth/session`
-
-### Passkey Login
-
-Passkey endpoints are available under `/admin-api/auth/passkeys/*`.
-
-- registration options: `POST /admin-api/auth/passkeys/registration/options`
-- registration verify: `POST /admin-api/auth/passkeys/registration/verify`
-- authentication options: `POST /admin-api/auth/passkeys/authentication/options`
-- authentication verify: `POST /admin-api/auth/passkeys/authentication/verify`
-
-Passkeys use WebAuthn and therefore depend on both a valid origin and a valid RP ID.
-
-- `expectedOrigin` is derived from the live request protocol and host, including `x-forwarded-proto` and `x-forwarded-host` when present.
-- `CODEBUDDY_ADMIN_PASSKEY_RP_ID` optionally overrides the WebAuthn RP ID used for admin passkey registration and authentication.
-- If `CODEBUDDY_ADMIN_PASSKEY_RP_ID` is empty, the server falls back to the current request hostname with the port removed.
-- The RP ID must be a hostname only, with no scheme, port, or path. Typical values are `example.com`, `admin.example.com`, or `localhost`.
-- The RP ID must match the browser-visible origin domain or be a registrable parent-domain suffix accepted by the browser for that origin. If it does not match, passkey registration or authentication will fail.
-- Production deployments should use HTTPS on the hostname the browser actually visits. If TLS terminates at a reverse proxy or ingress, forwarded host/protocol headers must reflect that public origin.
-- `http://localhost` remains usable for local browser testing because browsers treat localhost as a secure-context exception. Plain HTTP on non-localhost hosts is not sufficient for WebAuthn.
-
-## Public APIs
-
-Base path:
-
-```text
-http://:8001/v1
+bun install --cwd docs
+bun run docs:dev
```
-Supported endpoints:
-
-- `POST /v1/chat/completions`
-- `POST /v1/responses`
-- `POST /v1/messages`
-- `GET /v1/models`
-
-### Authentication
-
-- inference routes accept managed access keys
-- send either `Authorization: Bearer ` or `x-api-key: `
-- `/admin-api/*` is controlled separately by the admin session cookie when admin auth is configured
-
-## Runtime Settings
-
-Settings resolve in this order:
-
-1. persisted admin settings
-2. environment variables
-3. built-in defaults
-
-Current persisted runtime settings:
-
-- `CODEBUDDY_ADMIN_PASSKEY_RP_ID`
-- `CODEBUDDY_API_ENDPOINT`
-- `CODEBUDDY_AUTH_MODE`
-- `CODEBUDDY_INTERNET_ENVIRONMENT`
-- `CODEBUDDY_LOG_LEVEL`
-
-Models are discovered from CodeBuddy for each active credential. The credentials page shows one row per credential with its supported models and a refresh action. `/v1/models` only merges models from credentials bound to the requesting API key.
-
-## Logging
-
-The server now includes key runtime and security logging for upstream failures and admin-sensitive flows.
-
-- debug snapshots redact tokens, cookies, API keys, auth headers, and user identifiers
-- unreadable access-key storage is surfaced as a 503 instead of silently degrading
+Use `bun run docs:build` to build the static site and `bun run docs:preview` to
+preview it.
## Development
-Primary quality gates:
-
```bash
+bun install
bun run lint
bun run format:check
bun run typecheck
-bun run test
-bun run build
-```
-
-Targeted suites used during this refactor:
-
-```bash
-bun run test -- tests/admin/console.test.tsx
-bun run test -- tests/server/runtime.test.ts
-bun run test -- tests/server/debug-usage.test.ts
-bun run test -- tests/server/access-keys-credentials.test.ts
-bun run test -- tests/server/units.test.ts
-```
-
-## Deployment Notes
-
-### Docker Compose
-
-```bash
-cp .env.example .env
-mkdir -p .codebuddy_data .codebuddy_creds
-docker compose -f deploy/docker-compose.yml up -d
-```
-
-### Kubernetes
-
-```bash
-kubectl apply -f deploy/k8s/codebuddy2api.yaml
-kubectl port-forward service/codebuddy2api 8001:8001
+bun run test:coverage
+bunx next build --webpack
```
## License
diff --git a/docs/.vitepress/config.ts b/docs/.vitepress/config.ts
new file mode 100644
index 0000000..2206357
--- /dev/null
+++ b/docs/.vitepress/config.ts
@@ -0,0 +1,167 @@
+import { defineConfig } from 'vitepress';
+
+const githubLink = 'https://github.com/orangeboyChen/codebuddy2api';
+const consolePaths = [
+ 'dashboard',
+ 'usage',
+ 'credentials',
+ 'account-status',
+ 'api-test',
+ 'debug',
+ 'settings',
+];
+const sharedTheme = {
+ socialLinks: [{ icon: 'github', link: githubLink }],
+};
+
+const createSidebar = (
+ prefix: string,
+ guide: string,
+ config: string,
+ quickStart: string,
+ configPage: string,
+ storagePage: string,
+ localPage: string,
+ labels: string[],
+) => [
+ {
+ text: config,
+ items: [
+ { text: quickStart, link: `${prefix}/guide/quick-start` },
+ { text: configPage, link: `${prefix}/config/docker` },
+ { text: storagePage, link: `${prefix}/config/storage` },
+ { text: localPage, link: `${prefix}/config/local` },
+ ],
+ },
+ {
+ text: guide,
+ items: consolePaths.map((path, index) => ({
+ text: labels[index],
+ link: `${prefix}/guide/${path}`,
+ })),
+ },
+];
+
+const createTheme = (
+ prefix: string,
+ guide: string,
+ config: string,
+ quickStart: string,
+ configPage: string,
+ storagePage: string,
+ localPage: string,
+ labels: string[],
+ ui: { footer: string; menu: string; top: string; prev: string; next: string },
+) => ({
+ ...sharedTheme,
+ docFooter: { prev: ui.prev, next: ui.next },
+ footer: { message: ui.footer },
+ outline: { label: ui.menu },
+ returnToTopLabel: ui.top,
+ sidebarMenuLabel: ui.menu,
+ nav: [{ text: guide, link: `${prefix}/guide/dashboard` }],
+ sidebar: createSidebar(
+ prefix,
+ guide,
+ config,
+ quickStart,
+ configPage,
+ storagePage,
+ localPage,
+ labels,
+ ),
+});
+
+export default defineConfig({
+ base: '/codebuddy2api/',
+ description: 'CodeBuddy2API 使用文档。',
+ lang: 'zh-CN',
+ locales: {
+ root: {
+ label: '简体中文',
+ lang: 'zh-CN',
+ themeConfig: createTheme(
+ '',
+ '指南',
+ '配置',
+ '快速开始',
+ 'Docker 与存储',
+ '存储选择',
+ '本地运行(开发)',
+ ['仪表盘', '用量', '凭据', '账号状态', 'API 测试', '调试', '设置'],
+ {
+ footer:
+ '基于 MIT License 发布。',
+ menu: '目录',
+ top: '返回顶部',
+ prev: '上一页',
+ next: '下一页',
+ },
+ ),
+ },
+ en: {
+ label: 'English',
+ lang: 'en-US',
+ link: '/en/',
+ themeConfig: createTheme(
+ '/en',
+ 'Guide',
+ 'Configuration',
+ 'Quick Start',
+ 'Docker and Storage',
+ 'Storage Choices',
+ 'Local Development',
+ [
+ 'Dashboard',
+ 'Usage',
+ 'Credentials',
+ 'Account Status',
+ 'API Test',
+ 'Debug',
+ 'Settings',
+ ],
+ {
+ footer:
+ 'Released under the MIT License.',
+ menu: 'On this page',
+ top: 'Return to top',
+ prev: 'Previous page',
+ next: 'Next page',
+ },
+ ),
+ },
+ ja: {
+ label: '日本語',
+ lang: 'ja-JP',
+ link: '/ja/',
+ themeConfig: createTheme(
+ '/ja',
+ 'ガイド',
+ '設定',
+ 'クイックスタート',
+ 'Docker とストレージ',
+ 'ストレージの選択',
+ 'ローカル開発',
+ [
+ 'ダッシュボード',
+ '利用量',
+ '認証情報',
+ 'アカウント状態',
+ 'API テスト',
+ 'デバッグ',
+ '設定',
+ ],
+ {
+ footer:
+ 'MIT License(ライセンス全文)。',
+ menu: 'このページの内容',
+ top: 'トップへ戻る',
+ prev: '前のページ',
+ next: '次のページ',
+ },
+ ),
+ },
+ },
+ title: 'CodeBuddy2API',
+ themeConfig: sharedTheme,
+});
diff --git a/docs/bun.lock b/docs/bun.lock
new file mode 100644
index 0000000..b48b802
--- /dev/null
+++ b/docs/bun.lock
@@ -0,0 +1,358 @@
+{
+ "lockfileVersion": 1,
+ "workspaces": {
+ "": {
+ "name": "codebuddy2api-docs",
+ "devDependencies": {
+ "vitepress": "^1.6.4",
+ },
+ },
+ },
+ "packages": {
+ "@algolia/abtesting": ["@algolia/abtesting@1.23.0", "", { "dependencies": { "@algolia/client-common": "5.57.0", "@algolia/requester-browser-xhr": "5.57.0", "@algolia/requester-fetch": "5.57.0", "@algolia/requester-node-http": "5.57.0" } }, "sha512-j45MBISstltys9QyQ4xf6quRiN1g7vMuwQL9VM4dx8YuRZvCQ173b9royZAx6iAbRX3IB1VnG1z//NuwyQ8jpQ=="],
+
+ "@algolia/autocomplete-core": ["@algolia/autocomplete-core@1.17.7", "", { "dependencies": { "@algolia/autocomplete-plugin-algolia-insights": "1.17.7", "@algolia/autocomplete-shared": "1.17.7" } }, "sha512-BjiPOW6ks90UKl7TwMv7oNQMnzU+t/wk9mgIDi6b1tXpUek7MW0lbNOUHpvam9pe3lVCf4xPFT+lK7s+e+fs7Q=="],
+
+ "@algolia/autocomplete-plugin-algolia-insights": ["@algolia/autocomplete-plugin-algolia-insights@1.17.7", "", { "dependencies": { "@algolia/autocomplete-shared": "1.17.7" }, "peerDependencies": { "search-insights": ">= 1 < 3" } }, "sha512-Jca5Ude6yUOuyzjnz57og7Et3aXjbwCSDf/8onLHSQgw1qW3ALl9mrMWaXb5FmPVkV3EtkD2F/+NkT6VHyPu9A=="],
+
+ "@algolia/autocomplete-preset-algolia": ["@algolia/autocomplete-preset-algolia@1.17.7", "", { "dependencies": { "@algolia/autocomplete-shared": "1.17.7" }, "peerDependencies": { "@algolia/client-search": ">= 4.9.1 < 6", "algoliasearch": ">= 4.9.1 < 6" } }, "sha512-ggOQ950+nwbWROq2MOCIL71RE0DdQZsceqrg32UqnhDz8FlO9rL8ONHNsI2R1MH0tkgVIDKI/D0sMiUchsFdWA=="],
+
+ "@algolia/autocomplete-shared": ["@algolia/autocomplete-shared@1.17.7", "", { "peerDependencies": { "@algolia/client-search": ">= 4.9.1 < 6", "algoliasearch": ">= 4.9.1 < 6" } }, "sha512-o/1Vurr42U/qskRSuhBH+VKxMvkkUVTLU6WZQr+L5lGZZLYWyhdzWjW0iGXY7EkwRTjBqvN2EsR81yCTGV/kmg=="],
+
+ "@algolia/client-abtesting": ["@algolia/client-abtesting@5.57.0", "", { "dependencies": { "@algolia/client-common": "5.57.0", "@algolia/requester-browser-xhr": "5.57.0", "@algolia/requester-fetch": "5.57.0", "@algolia/requester-node-http": "5.57.0" } }, "sha512-JVFFujiZUCguk5tz3LZr4fTQxqpIrj4/Jw3SI7kMljSqtfLxYn/s/TWH0J2s4iNfsDpxPhgFGMotCpmDI4kZ8w=="],
+
+ "@algolia/client-analytics": ["@algolia/client-analytics@5.57.0", "", { "dependencies": { "@algolia/client-common": "5.57.0", "@algolia/requester-browser-xhr": "5.57.0", "@algolia/requester-fetch": "5.57.0", "@algolia/requester-node-http": "5.57.0" } }, "sha512-6KqECK4ED3JJQEoDrQWnGPQzElA828xAD4qK5ceawNNyP/LcSvzAoLHjFkoTPksZ/kxj6VUtCRH+IHZesLltng=="],
+
+ "@algolia/client-common": ["@algolia/client-common@5.57.0", "", {}, "sha512-uqpGF3oXYsoCbQq5d7BzNrNTfIfuvJyGP1CKvSW27T9boUg7KOwyxsAw1AX0a3jSW2HrYEJ/NN+Z4MiGivbpeQ=="],
+
+ "@algolia/client-insights": ["@algolia/client-insights@5.57.0", "", { "dependencies": { "@algolia/client-common": "5.57.0", "@algolia/requester-browser-xhr": "5.57.0", "@algolia/requester-fetch": "5.57.0", "@algolia/requester-node-http": "5.57.0" } }, "sha512-u5NboJVJXDEFplvNnqqX4CxkXPYysjJRj47hOSh9329H8kG5gFLKJBIiS5utMQ+GZm8xQl3Te7NInDk6elEADQ=="],
+
+ "@algolia/client-personalization": ["@algolia/client-personalization@5.57.0", "", { "dependencies": { "@algolia/client-common": "5.57.0", "@algolia/requester-browser-xhr": "5.57.0", "@algolia/requester-fetch": "5.57.0", "@algolia/requester-node-http": "5.57.0" } }, "sha512-uzc0b2LmHAK9/QID4xeo35OG84AkZl4YewkCqawqAOGLjT2eZpM/OZx45ESygMHG30Ws+ZTSdluPtMJcUnrbWQ=="],
+
+ "@algolia/client-query-suggestions": ["@algolia/client-query-suggestions@5.57.0", "", { "dependencies": { "@algolia/client-common": "5.57.0", "@algolia/requester-browser-xhr": "5.57.0", "@algolia/requester-fetch": "5.57.0", "@algolia/requester-node-http": "5.57.0" } }, "sha512-dIAhnM6ue/ssa5PjgNfu4g8A4yTojl9ZOUzZU3wIaIKRerL2R/3Emuf9n/D6ICXXP167KC6XCeC7nliSw7cuSw=="],
+
+ "@algolia/client-search": ["@algolia/client-search@5.57.0", "", { "dependencies": { "@algolia/client-common": "5.57.0", "@algolia/requester-browser-xhr": "5.57.0", "@algolia/requester-fetch": "5.57.0", "@algolia/requester-node-http": "5.57.0" } }, "sha512-2TTPTTKSJmCptvhCm4Xf3bBYMqZni+Pgc2hVdqc4l9wsBpSJNVTVIKpnd10OubUgkGcmppVDj1XQqYaf6EnPSQ=="],
+
+ "@algolia/ingestion": ["@algolia/ingestion@1.57.0", "", { "dependencies": { "@algolia/client-common": "5.57.0", "@algolia/requester-browser-xhr": "5.57.0", "@algolia/requester-fetch": "5.57.0", "@algolia/requester-node-http": "5.57.0" } }, "sha512-W4JseHKt+pzOxlFV+T3MWEG0h4Z2Se5zjoXUD0ewlw8aOWMG/yjRdopUdLQsXULepB/My2tDuZjkwk2sMfsUrQ=="],
+
+ "@algolia/monitoring": ["@algolia/monitoring@1.57.0", "", { "dependencies": { "@algolia/client-common": "5.57.0", "@algolia/requester-browser-xhr": "5.57.0", "@algolia/requester-fetch": "5.57.0", "@algolia/requester-node-http": "5.57.0" } }, "sha512-BrxJVE0/eLinEPICCD7BKN/2xnt0nkjge70u8zzE2ISP3fuB3tjLgcwpanUycvlHBFLI4gK0l5ol54p6IYuR/Q=="],
+
+ "@algolia/recommend": ["@algolia/recommend@5.57.0", "", { "dependencies": { "@algolia/client-common": "5.57.0", "@algolia/requester-browser-xhr": "5.57.0", "@algolia/requester-fetch": "5.57.0", "@algolia/requester-node-http": "5.57.0" } }, "sha512-Gc29jkeiLKlVfHvyrIgyUHHE+aYTdXEeLfK42rjr5/1TTVsYwUJz0XkvoIBIqfMjcDg6gXeHb1jTUZ0H+SYIlQ=="],
+
+ "@algolia/requester-browser-xhr": ["@algolia/requester-browser-xhr@5.57.0", "", { "dependencies": { "@algolia/client-common": "5.57.0" } }, "sha512-PIPnPN7MP3fp2VAi01BVXhCWmD366ZB2Hkq5TlYKtThd4KxUtMmaaNDpFgVCTXtSIqWVZLJntOHRvxg/sIPd8Q=="],
+
+ "@algolia/requester-fetch": ["@algolia/requester-fetch@5.57.0", "", { "dependencies": { "@algolia/client-common": "5.57.0" } }, "sha512-AX3RlOudXMdTwtwUqdAf5hAVLvXfOZZH1FZh6ALDdrhVLT0TtAIe48N6nYcWcTnwoTxK/wDIQqZ19IMjK8zJAA=="],
+
+ "@algolia/requester-node-http": ["@algolia/requester-node-http@5.57.0", "", { "dependencies": { "@algolia/client-common": "5.57.0" } }, "sha512-cWZc1dKb7wy9/wPpwMtL1y89gK2G7y2A47Coa7zwf1ydtIeJm4+S+XxoQ2b/ZRiQnrC1YHavLjYUPDCdnT7Khg=="],
+
+ "@babel/helper-string-parser": ["@babel/helper-string-parser@7.29.7", "", {}, "sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw=="],
+
+ "@babel/helper-validator-identifier": ["@babel/helper-validator-identifier@7.29.7", "", {}, "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg=="],
+
+ "@babel/parser": ["@babel/parser@7.29.8", "", { "dependencies": { "@babel/types": "^7.29.8" }, "bin": "./bin/babel-parser.js" }, "sha512-E8lTAYNB1KW+FH+VGJuZM1ioAx2E6oVlvQFRrf5P8ZZmsiJXYAD9vTFV7yyEURNzgh1dFqMZuO6tUwcARbqFCA=="],
+
+ "@babel/types": ["@babel/types@7.29.8", "", { "dependencies": { "@babel/helper-string-parser": "^7.29.7", "@babel/helper-validator-identifier": "^7.29.7" } }, "sha512-Vj1jF3cPfxg7OAfoI7QnVKLoILlm2JF9pnVHrX8qx7AHMiYWT+NDAA7jChlNgRS4WTLc/fD1lXLmPixluj+3Gg=="],
+
+ "@docsearch/css": ["@docsearch/css@3.8.2", "", {}, "sha512-y05ayQFyUmCXze79+56v/4HpycYF3uFqB78pLPrSV5ZKAlDuIAAJNhaRi8tTdRNXh05yxX/TyNnzD6LwSM89vQ=="],
+
+ "@docsearch/js": ["@docsearch/js@3.8.2", "", { "dependencies": { "@docsearch/react": "3.8.2", "preact": "^10.0.0" } }, "sha512-Q5wY66qHn0SwA7Taa0aDbHiJvaFJLOJyHmooQ7y8hlwwQLQ/5WwCcoX0g7ii04Qi2DJlHsd0XXzJ8Ypw9+9YmQ=="],
+
+ "@docsearch/react": ["@docsearch/react@3.8.2", "", { "dependencies": { "@algolia/autocomplete-core": "1.17.7", "@algolia/autocomplete-preset-algolia": "1.17.7", "@docsearch/css": "3.8.2", "algoliasearch": "^5.14.2" }, "peerDependencies": { "@types/react": ">= 16.8.0 < 19.0.0", "react": ">= 16.8.0 < 19.0.0", "react-dom": ">= 16.8.0 < 19.0.0", "search-insights": ">= 1 < 3" }, "optionalPeers": ["@types/react", "react", "react-dom", "search-insights"] }, "sha512-xCRrJQlTt8N9GU0DG4ptwHRkfnSnD/YpdeaXe02iKfqs97TkZJv60yE+1eq/tjPcVnTW8dP5qLP7itifFVV5eg=="],
+
+ "@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.21.5", "", { "os": "aix", "cpu": "ppc64" }, "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ=="],
+
+ "@esbuild/android-arm": ["@esbuild/android-arm@0.21.5", "", { "os": "android", "cpu": "arm" }, "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg=="],
+
+ "@esbuild/android-arm64": ["@esbuild/android-arm64@0.21.5", "", { "os": "android", "cpu": "arm64" }, "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A=="],
+
+ "@esbuild/android-x64": ["@esbuild/android-x64@0.21.5", "", { "os": "android", "cpu": "x64" }, "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA=="],
+
+ "@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.21.5", "", { "os": "darwin", "cpu": "arm64" }, "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ=="],
+
+ "@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.21.5", "", { "os": "darwin", "cpu": "x64" }, "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw=="],
+
+ "@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.21.5", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g=="],
+
+ "@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.21.5", "", { "os": "freebsd", "cpu": "x64" }, "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ=="],
+
+ "@esbuild/linux-arm": ["@esbuild/linux-arm@0.21.5", "", { "os": "linux", "cpu": "arm" }, "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA=="],
+
+ "@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.21.5", "", { "os": "linux", "cpu": "arm64" }, "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q=="],
+
+ "@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.21.5", "", { "os": "linux", "cpu": "ia32" }, "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg=="],
+
+ "@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.21.5", "", { "os": "linux", "cpu": "none" }, "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg=="],
+
+ "@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.21.5", "", { "os": "linux", "cpu": "none" }, "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg=="],
+
+ "@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.21.5", "", { "os": "linux", "cpu": "ppc64" }, "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w=="],
+
+ "@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.21.5", "", { "os": "linux", "cpu": "none" }, "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA=="],
+
+ "@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.21.5", "", { "os": "linux", "cpu": "s390x" }, "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A=="],
+
+ "@esbuild/linux-x64": ["@esbuild/linux-x64@0.21.5", "", { "os": "linux", "cpu": "x64" }, "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ=="],
+
+ "@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.21.5", "", { "os": "none", "cpu": "x64" }, "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg=="],
+
+ "@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.21.5", "", { "os": "openbsd", "cpu": "x64" }, "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow=="],
+
+ "@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.21.5", "", { "os": "sunos", "cpu": "x64" }, "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg=="],
+
+ "@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.21.5", "", { "os": "win32", "cpu": "arm64" }, "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A=="],
+
+ "@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.21.5", "", { "os": "win32", "cpu": "ia32" }, "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA=="],
+
+ "@esbuild/win32-x64": ["@esbuild/win32-x64@0.21.5", "", { "os": "win32", "cpu": "x64" }, "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw=="],
+
+ "@iconify-json/simple-icons": ["@iconify-json/simple-icons@1.2.94", "", { "dependencies": { "@iconify/types": "*" } }, "sha512-l8UWzVxKaqZd9ABsE/M/9p6NyGkQnmCnOoZyhQmjlXCtY5PuL2rcWxOFk2l9pk7ux3ERMPkTLE4jl6kQpTkwxA=="],
+
+ "@iconify/types": ["@iconify/types@2.0.0", "", {}, "sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg=="],
+
+ "@jridgewell/sourcemap-codec": ["@jridgewell/sourcemap-codec@1.6.0", "", {}, "sha512-T7jf+5zgsZHwNJ4lvQ7/aezbyk0nNX+zJVWpmHA7VYsEx7a7qr5Rg5IbtJFqkgze5Y2sruq1RUY8Q837Od7iFw=="],
+
+ "@napi-rs/lzma-linux-x64-gnu": ["@napi-rs/lzma-linux-x64-gnu@1.5.1", "", { "os": "linux", "cpu": "x64" }, "sha512-oTXEIha4SsuXdTA4Iyskj0kpdx2yVXdhd75c2v3xGrHFfVMsbhTPZU/nMPL4sWKo4pBHm3aucLaqGlF696dTyQ=="],
+
+ "@rollup/rollup-android-arm-eabi": ["@rollup/rollup-android-arm-eabi@4.63.1", "", { "os": "android", "cpu": "arm" }, "sha512-UZ8sUxPTiHWYX9QNdJedb1kDZSpS1t/VPWBWGSgqHNi9w3Cu6IXvu2mzbhiTiPvtrqgTQJ+zqiAq2iPIPilpaQ=="],
+
+ "@rollup/rollup-android-arm64": ["@rollup/rollup-android-arm64@4.63.1", "", { "os": "android", "cpu": "arm64" }, "sha512-cQ4nFQABN5cDvDpbvJ7bMStCpnaVxynZrRMfUJYgxcIk9Sh54FIO1vtfkg0B69REjER77ioZ/ov+eAApx/KmLQ=="],
+
+ "@rollup/rollup-darwin-arm64": ["@rollup/rollup-darwin-arm64@4.63.1", "", { "os": "darwin", "cpu": "arm64" }, "sha512-FQNqd1lRy/0QhDk3xeRIkSBiCpXCiDnZO3YLVdcDKN1UBiKToNftCzcXYNLshmPDUMlu2TdeS8tGcsU6f3YF1Q=="],
+
+ "@rollup/rollup-darwin-x64": ["@rollup/rollup-darwin-x64@4.63.1", "", { "os": "darwin", "cpu": "x64" }, "sha512-pvD16V939D3CloK0+qikpGaxiPrDUXTe7Y5cWOMkMSy7m1cawa8EGy/kXYi/G/cKAC4HDAbSnzCIk1WmsoOKXg=="],
+
+ "@rollup/rollup-freebsd-arm64": ["@rollup/rollup-freebsd-arm64@4.63.1", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-pcFGeL2345VwdTnJhA6zLbew+YgWB0qBG2+dMtXjCicf6+rm6kO6cOoh5VnTe0ZMrMRgRyuHmCJxZWrIdzYuOw=="],
+
+ "@rollup/rollup-freebsd-x64": ["@rollup/rollup-freebsd-x64@4.63.1", "", { "os": "freebsd", "cpu": "x64" }, "sha512-mRJlqSRulVzcKq/LKA6ICSIc3K/l4fzlVn/gePn2nXIHy8seRi5z/eeRE0d/XMBxcMldiXtQTSpRj0tkkC3g8Q=="],
+
+ "@rollup/rollup-linux-arm-gnueabihf": ["@rollup/rollup-linux-arm-gnueabihf@4.63.1", "", { "os": "linux", "cpu": "arm" }, "sha512-YDUNvVM85TI3g/1OpnqKP1h4NeW/j64DfWMf+G3M809xNk1bJSnpFp4sh83NpmVE5DXnkh8ULor4LTVZKoYLHw=="],
+
+ "@rollup/rollup-linux-arm-musleabihf": ["@rollup/rollup-linux-arm-musleabihf@4.63.1", "", { "os": "linux", "cpu": "arm" }, "sha512-7Mcn71p9ZuQFAj+h+dhQXy/yeLePRS2yKRnmW1DijA9thKO5qap0GNOIQK4yQ6iP3SU0Mrb/yWo8h8vgRba8lw=="],
+
+ "@rollup/rollup-linux-arm64-gnu": ["@rollup/rollup-linux-arm64-gnu@4.63.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-4YiLQTX6U4CSl0L9cluep9A9W6UmTfqBDc2/CH6wlu54pl4E7Jn3cOD8oxzvBDEGk/JMKgJ47C8g+radF7mwvg=="],
+
+ "@rollup/rollup-linux-arm64-musl": ["@rollup/rollup-linux-arm64-musl@4.63.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-2ra8F7w8OquwZN9z2/fKFnli69wa8PLwaVzRMIPGb13ByMJwC28Fbp8YcVGoUhlYMTt7j5j9bNgpysrN2UM+vw=="],
+
+ "@rollup/rollup-linux-loong64-gnu": ["@rollup/rollup-linux-loong64-gnu@4.63.1", "", { "os": "linux", "cpu": "none" }, "sha512-Sy20ncyhjmBP0Ml+UvQbimjlk6VFgjW5uNP+qqwHB00mTE8Bl2C1TuHTlRwK2YoXeZbee5lP2XevBWVkAQAtSQ=="],
+
+ "@rollup/rollup-linux-loong64-musl": ["@rollup/rollup-linux-loong64-musl@4.63.1", "", { "os": "linux", "cpu": "none" }, "sha512-noITLp8oNjYliPnGWmLyelIHwULGqbHloQHGw1rtxbWhTuWooRpnZarZQJ1y9EUC4szuCusCc+HEpUtxpIwYvA=="],
+
+ "@rollup/rollup-linux-ppc64-gnu": ["@rollup/rollup-linux-ppc64-gnu@4.63.1", "", { "os": "linux", "cpu": "ppc64" }, "sha512-hlxxXd+F1mWiAcaFR7Sv9ZQT6m6UfI8+Vy/kFJzztq2pDMU/0wZ9sish0iszNZvsQDo8Gc0i5yuFEOz5dDf6fA=="],
+
+ "@rollup/rollup-linux-ppc64-musl": ["@rollup/rollup-linux-ppc64-musl@4.63.1", "", { "os": "linux", "cpu": "ppc64" }, "sha512-EF7OpqQTQ/BvGqLzUi4rEHuagCV9MugAUXSHemwPW5vxZ75RR+jxO/2j95Ph2dalMpFHSVECjRoioHZgA9zOYA=="],
+
+ "@rollup/rollup-linux-riscv64-gnu": ["@rollup/rollup-linux-riscv64-gnu@4.63.1", "", { "os": "linux", "cpu": "none" }, "sha512-wQO3JesW9PRkwlabQ27y7sPfVOOTLRG73I4F2UYHG5PXun3J9U3y+b7ezVKSYbsvSKGQ1k1cq8Qlun4C9kLt3w=="],
+
+ "@rollup/rollup-linux-riscv64-musl": ["@rollup/rollup-linux-riscv64-musl@4.63.1", "", { "os": "linux", "cpu": "none" }, "sha512-ouAGwhO6wHRXdnOVCOsB0tRFkA7nhNB2Nwax6oECXN0YiN8EYUTBAOudADOB1PI+yDL61TeNx/u7MVCzksNbkQ=="],
+
+ "@rollup/rollup-linux-s390x-gnu": ["@rollup/rollup-linux-s390x-gnu@4.63.1", "", { "os": "linux", "cpu": "s390x" }, "sha512-q2R38Sn+1J8RxhfJ+T54wSWmyKXWec+9jgDfqO2AtArEqHO5R2aeayp5H5OYLr5UYDVGsVaZPEFUooMhYCdz5A=="],
+
+ "@rollup/rollup-linux-x64-gnu": ["@rollup/rollup-linux-x64-gnu@4.63.1", "", { "os": "linux", "cpu": "x64" }, "sha512-gfI5T24WLLuFfSKw7Go/zDXjAAV0fny0swTaDv+WjK7vqcw4cRhFfdsyKL1n+ukI+ooBxn3bVQnyrn06WpI50w=="],
+
+ "@rollup/rollup-linux-x64-musl": ["@rollup/rollup-linux-x64-musl@4.63.1", "", { "os": "linux", "cpu": "x64" }, "sha512-4h6XqthmB4Hspji84wvgk+ElodTsGj+dbZqHJHHtKxj4mYq0ANSEEPX9ys3moJueqsRjwpaJYH7874Itwnj2ow=="],
+
+ "@rollup/rollup-openbsd-x64": ["@rollup/rollup-openbsd-x64@4.63.1", "", { "os": "openbsd", "cpu": "x64" }, "sha512-dlfCOa87o1VAYegLQ9EKilx2JCeRofiyPGhTCmqnuXZ6bMPiycO1rq1+sKoulAp7pGLIsTIw+1x5R+zgh5LhhA=="],
+
+ "@rollup/rollup-openharmony-arm64": ["@rollup/rollup-openharmony-arm64@4.63.1", "", { "os": "none", "cpu": "arm64" }, "sha512-cjkLbOlfcm3QGhMM1J5zaZjsw1GggbN6rw9UTSSRrPrR1KkcXnN7Uq9rPw34xImQ9VOY9GN+6u2Zj80B9ptkcw=="],
+
+ "@rollup/rollup-win32-arm64-msvc": ["@rollup/rollup-win32-arm64-msvc@4.63.1", "", { "os": "win32", "cpu": "arm64" }, "sha512-Li1KdUnWGE4N3e1F/B4RTB1ms+nG4WBgjByO46pkeBVX/2UBsY53xf5vK9WygVmnH3RwncIST7lkSdLSY6P9lg=="],
+
+ "@rollup/rollup-win32-ia32-msvc": ["@rollup/rollup-win32-ia32-msvc@4.63.1", "", { "os": "win32", "cpu": "ia32" }, "sha512-t4ZYOSoLTgwhuFMrmTMLx/+i1DQVK7HYqMc6kY46EApwi8X0nIVphzdNoThU3xt6n+N5urG1/gxBdCaKDLavfg=="],
+
+ "@rollup/rollup-win32-x64-gnu": ["@rollup/rollup-win32-x64-gnu@4.63.1", "", { "os": "win32", "cpu": "x64" }, "sha512-RgroPfMmKlD1RzSDxvwgcPiy2HNQKoYV7OmwIXDsk73uKW5t6B/V8KIy27SMv/FNXFo/oSBtWc9J0X7t91ezZg=="],
+
+ "@rollup/rollup-win32-x64-msvc": ["@rollup/rollup-win32-x64-msvc@4.63.1", "", { "os": "win32", "cpu": "x64" }, "sha512-at8QVep6S3h5Y6gSbdGU06bRY5WJkf6WUduM9YtvYMbYhB1MOFfUgc6kehitQXzOtMSaT70q7f9ydPhpqu821w=="],
+
+ "@shikijs/core": ["@shikijs/core@2.5.0", "", { "dependencies": { "@shikijs/engine-javascript": "2.5.0", "@shikijs/engine-oniguruma": "2.5.0", "@shikijs/types": "2.5.0", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4", "hast-util-to-html": "^9.0.4" } }, "sha512-uu/8RExTKtavlpH7XqnVYBrfBkUc20ngXiX9NSrBhOVZYv/7XQRKUyhtkeflY5QsxC0GbJThCerruZfsUaSldg=="],
+
+ "@shikijs/engine-javascript": ["@shikijs/engine-javascript@2.5.0", "", { "dependencies": { "@shikijs/types": "2.5.0", "@shikijs/vscode-textmate": "^10.0.2", "oniguruma-to-es": "^3.1.0" } }, "sha512-VjnOpnQf8WuCEZtNUdjjwGUbtAVKuZkVQ/5cHy/tojVVRIRtlWMYVjyWhxOmIq05AlSOv72z7hRNRGVBgQOl0w=="],
+
+ "@shikijs/engine-oniguruma": ["@shikijs/engine-oniguruma@2.5.0", "", { "dependencies": { "@shikijs/types": "2.5.0", "@shikijs/vscode-textmate": "^10.0.2" } }, "sha512-pGd1wRATzbo/uatrCIILlAdFVKdxImWJGQ5rFiB5VZi2ve5xj3Ax9jny8QvkaV93btQEwR/rSz5ERFpC5mKNIw=="],
+
+ "@shikijs/langs": ["@shikijs/langs@2.5.0", "", { "dependencies": { "@shikijs/types": "2.5.0" } }, "sha512-Qfrrt5OsNH5R+5tJ/3uYBBZv3SuGmnRPejV9IlIbFH3HTGLDlkqgHymAlzklVmKBjAaVmkPkyikAV/sQ1wSL+w=="],
+
+ "@shikijs/themes": ["@shikijs/themes@2.5.0", "", { "dependencies": { "@shikijs/types": "2.5.0" } }, "sha512-wGrk+R8tJnO0VMzmUExHR+QdSaPUl/NKs+a4cQQRWyoc3YFbUzuLEi/KWK1hj+8BfHRKm2jNhhJck1dfstJpiw=="],
+
+ "@shikijs/transformers": ["@shikijs/transformers@2.5.0", "", { "dependencies": { "@shikijs/core": "2.5.0", "@shikijs/types": "2.5.0" } }, "sha512-SI494W5X60CaUwgi8u4q4m4s3YAFSxln3tzNjOSYqq54wlVgz0/NbbXEb3mdLbqMBztcmS7bVTaEd2w0qMmfeg=="],
+
+ "@shikijs/types": ["@shikijs/types@2.5.0", "", { "dependencies": { "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-ygl5yhxki9ZLNuNpPitBWvcy9fsSKKaRuO4BAlMyagszQidxcpLAr0qiW/q43DtSIDxO6hEbtYLiFZNXO/hdGw=="],
+
+ "@shikijs/vscode-textmate": ["@shikijs/vscode-textmate@10.0.2", "", {}, "sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg=="],
+
+ "@types/estree": ["@types/estree@1.0.9", "", {}, "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg=="],
+
+ "@types/hast": ["@types/hast@3.0.5", "", { "dependencies": { "@types/unist": "*" } }, "sha512-rp/ezSWaD1m44dPKICGhiskI13nVr7qTloFwDa/IYkhhf5nzwP+zIQcIJh3WIFSBOy/H1PzB40jPjMDksN4F+g=="],
+
+ "@types/linkify-it": ["@types/linkify-it@5.0.0", "", {}, "sha512-sVDA58zAw4eWAffKOaQH5/5j3XeayukzDk+ewSsnv3p4yJEZHCCzMDiZM8e0OUrRvmpGZ85jf4yDHkHsgBNr9Q=="],
+
+ "@types/markdown-it": ["@types/markdown-it@14.2.0", "", { "dependencies": { "@types/linkify-it": "^5", "@types/mdurl": "^2" } }, "sha512-NoQ2yGlLWj4wpxMs+TYmRKk3thDrQ97agr7sFqfLsAlvoS8SNQuTrlObhFqG9iugdTtgOE9jpJ6FNM4ZGsa5xQ=="],
+
+ "@types/mdast": ["@types/mdast@4.0.4", "", { "dependencies": { "@types/unist": "*" } }, "sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA=="],
+
+ "@types/mdurl": ["@types/mdurl@2.0.0", "", {}, "sha512-RGdgjQUZba5p6QEFAVx2OGb8rQDL/cPRG7GiedRzMcJ1tYnUANBncjbSB1NRGwbvjcPeikRABz2nshyPk1bhWg=="],
+
+ "@types/unist": ["@types/unist@3.0.3", "", {}, "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q=="],
+
+ "@types/web-bluetooth": ["@types/web-bluetooth@0.0.21", "", {}, "sha512-oIQLCGWtcFZy2JW77j9k8nHzAOpqMHLQejDA48XXMWH6tjCQHz5RCFz1bzsmROyL6PUm+LLnUiI4BCn221inxA=="],
+
+ "@ungap/structured-clone": ["@ungap/structured-clone@1.4.0", "", {}, "sha512-1mEZtMKPM09vDmQt5y7YvmN2+DFTP7Tg0EWXdic8/C6VRnpb33e4ghisCIE3WZjsE2N8mf+QV1Zqh7ZFYLWInQ=="],
+
+ "@vitejs/plugin-vue": ["@vitejs/plugin-vue@5.2.4", "", { "peerDependencies": { "vite": "^5.0.0 || ^6.0.0", "vue": "^3.2.25" } }, "sha512-7Yx/SXSOcQq5HiiV3orevHUFn+pmMB4cgbEkDYgnkUWb0WfeQ/wa2yFv6D5ICiCQOVpjA7vYDXrC7AGO8yjDHA=="],
+
+ "@vue/compiler-core": ["@vue/compiler-core@3.5.42", "", { "dependencies": { "@babel/parser": "^7.29.8", "@vue/shared": "3.5.42", "entities": "^7.0.1", "estree-walker": "^2.0.2", "source-map-js": "^1.2.1" } }, "sha512-2Ye1ilMtKXxl8qZUrQ5j0CdgenFp/HFQmta6rfRyfEsTG69L6Wk+tWuNoHYHMx9E8tF2Slvdg1FuwDvAXdy1LQ=="],
+
+ "@vue/compiler-dom": ["@vue/compiler-dom@3.5.42", "", { "dependencies": { "@vue/compiler-core": "3.5.42", "@vue/shared": "3.5.42" } }, "sha512-qbhQZEFmycr+ni/qyuccS4sucNN7VAbDfbkvNxWOX2VfgFm90MNs3/UhRNKoPMEIVn0F8gdlYjLPvqxHwHeQOA=="],
+
+ "@vue/compiler-sfc": ["@vue/compiler-sfc@3.5.42", "", { "dependencies": { "@babel/parser": "^7.29.8", "@vue/compiler-core": "3.5.42", "@vue/compiler-dom": "3.5.42", "@vue/compiler-ssr": "3.5.42", "@vue/shared": "3.5.42", "estree-walker": "^2.0.2", "magic-string": "^0.30.21", "postcss": "^8.5.19", "source-map-js": "^1.2.1" } }, "sha512-fkCAFB4okcAANGMThboWnScp/gzWjU0ZSkVnjTIiplmMDq2uq0tIB3j+xVu4rhv5rvOgBySCysudmbMd6xRRqw=="],
+
+ "@vue/compiler-ssr": ["@vue/compiler-ssr@3.5.42", "", { "dependencies": { "@vue/compiler-dom": "3.5.42", "@vue/shared": "3.5.42" } }, "sha512-xmLk3wLkbizPAiLyomjgFFosf2ys9b5Ghb+oh/k2tnvipNz8OFrQOiTcWCzyK7MpBp9KkyGtfvgfLUivbmuGYA=="],
+
+ "@vue/devtools-api": ["@vue/devtools-api@7.7.10", "", { "dependencies": { "@vue/devtools-kit": "^7.7.10" } }, "sha512-KxtEpUOOpFz/qOGRrAwA36QF7DqIA+FXgCYit9mk9wjbaZt0sXOFz81ElOZtKA4HbWHUdwNjZHBFsFFyp5BZiA=="],
+
+ "@vue/devtools-kit": ["@vue/devtools-kit@7.7.10", "", { "dependencies": { "@vue/devtools-shared": "^7.7.10", "birpc": "^2.3.0", "hookable": "^5.5.3", "mitt": "^3.0.1", "perfect-debounce": "^1.0.0", "speakingurl": "^14.0.1", "superjson": "^2.2.2" } }, "sha512-3WNi2Kq4tbpVbmhml7RiphmAt0279oh3fKNeWMQIrltfX8Q91b4i5PL8DtyNKdwmcsGrV4fg+erwWOmD05CLIw=="],
+
+ "@vue/devtools-shared": ["@vue/devtools-shared@7.7.10", "", { "dependencies": { "rfdc": "^1.4.1" } }, "sha512-wOPslzB8vTvpxwdaOcR2qAbwmuSP0L+rhpoC6Cf56V3Jip+HWb7PQQXOUPgBNQARpXsbQX/+mvi8kKucmBGRwQ=="],
+
+ "@vue/reactivity": ["@vue/reactivity@3.5.42", "", { "dependencies": { "@vue/shared": "3.5.42" } }, "sha512-TzNNfKpb7hDxbQltwAut8VDQA5YP+BuRlxntHUuRjyKwlMvmAPbs3unhCvieijifY6vFfVBwsS7wG/C7uq+bEQ=="],
+
+ "@vue/runtime-core": ["@vue/runtime-core@3.5.42", "", { "dependencies": { "@vue/reactivity": "3.5.42", "@vue/shared": "3.5.42" } }, "sha512-9uACtuHs7vJGkm5Bp3xu4xRDLFTIYy5DgxpToVjqGIAhAEKwQfsaLvKINhM6nFVp6bZPRFGdDqd1g52MqKsotA=="],
+
+ "@vue/runtime-dom": ["@vue/runtime-dom@3.5.42", "", { "dependencies": { "@vue/reactivity": "3.5.42", "@vue/runtime-core": "3.5.42", "@vue/shared": "3.5.42", "csstype": "^3.2.3" } }, "sha512-rsCmhiWLaRxGltLwhlCWyYkFn7WAbKRh0q17eZ1A6Dq6eqc2ACQ61IIryxz0LrsvCzHSilLA9JHovVwM8CNE2g=="],
+
+ "@vue/server-renderer": ["@vue/server-renderer@3.5.42", "", { "dependencies": { "@vue/compiler-ssr": "3.5.42", "@vue/runtime-dom": "3.5.42", "@vue/shared": "3.5.42" } }, "sha512-2++5dUyYS4gvo7xQXSECUDhB7TS0aOl5SeVfC5qSq1Jgfhjvegw1zqhwTIR3imZ+QYPJQw9gfcFvXGAjGZ7ajQ=="],
+
+ "@vue/shared": ["@vue/shared@3.5.42", "", {}, "sha512-2rPxex1jQf4jvl9MOHl6YaXCPcrNqz/FstMOEh3QWY+/OME9nQTvl9WYeCwhW7AFjaR0SnngZGlp/wkR6rkI6g=="],
+
+ "@vueuse/core": ["@vueuse/core@12.8.2", "", { "dependencies": { "@types/web-bluetooth": "^0.0.21", "@vueuse/metadata": "12.8.2", "@vueuse/shared": "12.8.2", "vue": "^3.5.13" } }, "sha512-HbvCmZdzAu3VGi/pWYm5Ut+Kd9mn1ZHnn4L5G8kOQTPs/IwIAmJoBrmYk2ckLArgMXZj0AW3n5CAejLUO+PhdQ=="],
+
+ "@vueuse/integrations": ["@vueuse/integrations@12.8.2", "", { "dependencies": { "@vueuse/core": "12.8.2", "@vueuse/shared": "12.8.2", "vue": "^3.5.13" }, "peerDependencies": { "async-validator": "^4", "axios": "^1", "change-case": "^5", "drauu": "^0.4", "focus-trap": "^7", "fuse.js": "^7", "idb-keyval": "^6", "jwt-decode": "^4", "nprogress": "^0.2", "qrcode": "^1.5", "sortablejs": "^1", "universal-cookie": "^7" }, "optionalPeers": ["async-validator", "axios", "change-case", "drauu", "focus-trap", "fuse.js", "idb-keyval", "jwt-decode", "nprogress", "qrcode", "sortablejs", "universal-cookie"] }, "sha512-fbGYivgK5uBTRt7p5F3zy6VrETlV9RtZjBqd1/HxGdjdckBgBM4ugP8LHpjolqTj14TXTxSK1ZfgPbHYyGuH7g=="],
+
+ "@vueuse/metadata": ["@vueuse/metadata@12.8.2", "", {}, "sha512-rAyLGEuoBJ/Il5AmFHiziCPdQzRt88VxR+Y/A/QhJ1EWtWqPBBAxTAFaSkviwEuOEZNtW8pvkPgoCZQ+HxqW1A=="],
+
+ "@vueuse/shared": ["@vueuse/shared@12.8.2", "", { "dependencies": { "vue": "^3.5.13" } }, "sha512-dznP38YzxZoNloI0qpEfpkms8knDtaoQ6Y/sfS0L7Yki4zh40LFHEhur0odJC6xTHG5dxWVPiUWBXn+wCG2s5w=="],
+
+ "algoliasearch": ["algoliasearch@5.57.0", "", { "dependencies": { "@algolia/abtesting": "1.23.0", "@algolia/client-abtesting": "5.57.0", "@algolia/client-analytics": "5.57.0", "@algolia/client-common": "5.57.0", "@algolia/client-insights": "5.57.0", "@algolia/client-personalization": "5.57.0", "@algolia/client-query-suggestions": "5.57.0", "@algolia/client-search": "5.57.0", "@algolia/ingestion": "1.57.0", "@algolia/monitoring": "1.57.0", "@algolia/recommend": "5.57.0", "@algolia/requester-browser-xhr": "5.57.0", "@algolia/requester-fetch": "5.57.0", "@algolia/requester-node-http": "5.57.0" } }, "sha512-HpND7MBGctOAkd1GoQoDZCGoCpqNTS5NG1LuhElFet3RdLJkwnyTYZXZhXwtpAQPrI36fqQ3eT6KQrdKDTKu3A=="],
+
+ "birpc": ["birpc@2.9.0", "", {}, "sha512-KrayHS5pBi69Xi9JmvoqrIgYGDkD6mcSe/i6YKi3w5kekCLzrX4+nawcXqrj2tIp50Kw/mT/s3p+GVK0A0sKxw=="],
+
+ "ccount": ["ccount@2.0.1", "", {}, "sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg=="],
+
+ "character-entities-html4": ["character-entities-html4@2.1.0", "", {}, "sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA=="],
+
+ "character-entities-legacy": ["character-entities-legacy@3.0.0", "", {}, "sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ=="],
+
+ "comma-separated-tokens": ["comma-separated-tokens@2.0.3", "", {}, "sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg=="],
+
+ "copy-anything": ["copy-anything@4.1.0", "", {}, "sha512-ufbM3smX/Jbnpk5wcQjzd1MgBpzmqfNETUAyZNrGwU9foRlyHoGzMMBBCRzEhQLBjZfFDE1W2ufPXX2vdWkV8Q=="],
+
+ "csstype": ["csstype@3.2.3", "", {}, "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ=="],
+
+ "dequal": ["dequal@2.0.3", "", {}, "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA=="],
+
+ "devlop": ["devlop@1.1.0", "", { "dependencies": { "dequal": "^2.0.0" } }, "sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA=="],
+
+ "emoji-regex-xs": ["emoji-regex-xs@1.0.0", "", {}, "sha512-LRlerrMYoIDrT6jgpeZ2YYl/L8EulRTt5hQcYjy5AInh7HWXKimpqx68aknBFpGL2+/IcogTcaydJEgaTmOpDg=="],
+
+ "entities": ["entities@7.0.1", "", {}, "sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA=="],
+
+ "esbuild": ["esbuild@0.21.5", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.21.5", "@esbuild/android-arm": "0.21.5", "@esbuild/android-arm64": "0.21.5", "@esbuild/android-x64": "0.21.5", "@esbuild/darwin-arm64": "0.21.5", "@esbuild/darwin-x64": "0.21.5", "@esbuild/freebsd-arm64": "0.21.5", "@esbuild/freebsd-x64": "0.21.5", "@esbuild/linux-arm": "0.21.5", "@esbuild/linux-arm64": "0.21.5", "@esbuild/linux-ia32": "0.21.5", "@esbuild/linux-loong64": "0.21.5", "@esbuild/linux-mips64el": "0.21.5", "@esbuild/linux-ppc64": "0.21.5", "@esbuild/linux-riscv64": "0.21.5", "@esbuild/linux-s390x": "0.21.5", "@esbuild/linux-x64": "0.21.5", "@esbuild/netbsd-x64": "0.21.5", "@esbuild/openbsd-x64": "0.21.5", "@esbuild/sunos-x64": "0.21.5", "@esbuild/win32-arm64": "0.21.5", "@esbuild/win32-ia32": "0.21.5", "@esbuild/win32-x64": "0.21.5" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw=="],
+
+ "estree-walker": ["estree-walker@2.0.2", "", {}, "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w=="],
+
+ "focus-trap": ["focus-trap@7.8.0", "", { "dependencies": { "tabbable": "^6.4.0" } }, "sha512-/yNdlIkpWbM0ptxno3ONTuf+2g318kh2ez3KSeZN5dZ8YC6AAmgeWz+GasYYiBJPFaYcSAPeu4GfhUaChzIJXA=="],
+
+ "fsevents": ["fsevents@2.3.3", "", { "os": "darwin" }, "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="],
+
+ "hast-util-to-html": ["hast-util-to-html@9.0.5", "", { "dependencies": { "@types/hast": "^3.0.0", "@types/unist": "^3.0.0", "ccount": "^2.0.0", "comma-separated-tokens": "^2.0.0", "hast-util-whitespace": "^3.0.0", "html-void-elements": "^3.0.0", "mdast-util-to-hast": "^13.0.0", "property-information": "^7.0.0", "space-separated-tokens": "^2.0.0", "stringify-entities": "^4.0.0", "zwitch": "^2.0.4" } }, "sha512-OguPdidb+fbHQSU4Q4ZiLKnzWo8Wwsf5bZfbvu7//a9oTYoqD/fWpe96NuHkoS9h0ccGOTe0C4NGXdtS0iObOw=="],
+
+ "hast-util-whitespace": ["hast-util-whitespace@3.0.0", "", { "dependencies": { "@types/hast": "^3.0.0" } }, "sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw=="],
+
+ "hookable": ["hookable@5.5.3", "", {}, "sha512-Yc+BQe8SvoXH1643Qez1zqLRmbA5rCL+sSmk6TVos0LWVfNIB7PGncdlId77WzLGSIB5KaWgTaNTs2lNVEI6VQ=="],
+
+ "html-void-elements": ["html-void-elements@3.0.0", "", {}, "sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg=="],
+
+ "magic-string": ["magic-string@0.30.21", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.5" } }, "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ=="],
+
+ "mark.js": ["mark.js@8.11.1", "", {}, "sha512-1I+1qpDt4idfgLQG+BNWmrqku+7/2bi5nLf4YwF8y8zXvmfiTBY3PV3ZibfrjBueCByROpuBjLLFCajqkgYoLQ=="],
+
+ "mdast-util-to-hast": ["mdast-util-to-hast@13.2.1", "", { "dependencies": { "@types/hast": "^3.0.0", "@types/mdast": "^4.0.0", "@ungap/structured-clone": "^1.0.0", "devlop": "^1.0.0", "micromark-util-sanitize-uri": "^2.0.0", "trim-lines": "^3.0.0", "unist-util-position": "^5.0.0", "unist-util-visit": "^5.0.0", "vfile": "^6.0.0" } }, "sha512-cctsq2wp5vTsLIcaymblUriiTcZd0CwWtCbLvrOzYCDZoWyMNV8sZ7krj09FSnsiJi3WVsHLM4k6Dq/yaPyCXA=="],
+
+ "micromark-util-character": ["micromark-util-character@2.1.1", "", { "dependencies": { "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q=="],
+
+ "micromark-util-encode": ["micromark-util-encode@2.0.1", "", {}, "sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw=="],
+
+ "micromark-util-sanitize-uri": ["micromark-util-sanitize-uri@2.0.1", "", { "dependencies": { "micromark-util-character": "^2.0.0", "micromark-util-encode": "^2.0.0", "micromark-util-symbol": "^2.0.0" } }, "sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ=="],
+
+ "micromark-util-symbol": ["micromark-util-symbol@2.0.1", "", {}, "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q=="],
+
+ "micromark-util-types": ["micromark-util-types@2.0.2", "", {}, "sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA=="],
+
+ "minisearch": ["minisearch@7.2.0", "", {}, "sha512-dqT2XBYUOZOiC5t2HRnwADjhNS2cecp9u+TJRiJ1Qp/f5qjkeT5APcGPjHw+bz89Ms8Jp+cG4AlE+QZ/QnDglg=="],
+
+ "mitt": ["mitt@3.0.1", "", {}, "sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw=="],
+
+ "nanoid": ["nanoid@3.3.18", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-DTg4MJbGMWkfi6VZFdNt2/caMbQy4Ou+Op/hJQvGEWcnVfoA1QA+xzRKAzw9jD6+GVOOeYr/mIcuDSdug6F6+w=="],
+
+ "oniguruma-to-es": ["oniguruma-to-es@3.1.1", "", { "dependencies": { "emoji-regex-xs": "^1.0.0", "regex": "^6.0.1", "regex-recursion": "^6.0.2" } }, "sha512-bUH8SDvPkH3ho3dvwJwfonjlQ4R80vjyvrU8YpxuROddv55vAEJrTuCuCVUhhsHbtlD9tGGbaNApGQckXhS8iQ=="],
+
+ "perfect-debounce": ["perfect-debounce@1.0.0", "", {}, "sha512-xCy9V055GLEqoFaHoC1SoLIaLmWctgCUaBaWxDZ7/Zx4CTyX7cJQLJOok/orfjZAh9kEYpjJa4d0KcJmCbctZA=="],
+
+ "picocolors": ["picocolors@1.1.1", "", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="],
+
+ "postcss": ["postcss@8.5.28", "", { "dependencies": { "nanoid": "^3.3.18", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-RRuzqDtt5Y9h3quz5hWhK+TPnsmVs6WwSU6LkJMeY4HstUEDuYTG8UJSdawMRzmzAtV+KEoG8N3Qg2qLy5vM/A=="],
+
+ "preact": ["preact@10.29.8", "", { "peerDependencies": { "preact-render-to-string": ">=5" }, "optionalPeers": ["preact-render-to-string"] }, "sha512-ej2aVZ+vZ8WO7tvlQWRM9N63A0KzF9q4mWJfDUHgYaIofWY9hu74QdnQrjoPMmZi2/nZ5gN0bJCQF49xQqx09Q=="],
+
+ "property-information": ["property-information@7.2.0", "", {}, "sha512-IAtzIB6sUiWaJYrX9smp3V46pBGbBeLFRGdh25kg1334VcBlD8HzhPeNIWQH9zhGmo2itIe25EHt9dQP7G5hmg=="],
+
+ "regex": ["regex@6.1.0", "", { "dependencies": { "regex-utilities": "^2.3.0" } }, "sha512-6VwtthbV4o/7+OaAF9I5L5V3llLEsoPyq9P1JVXkedTP33c7MfCG0/5NOPcSJn0TzXcG9YUrR0gQSWioew3LDg=="],
+
+ "regex-recursion": ["regex-recursion@6.0.2", "", { "dependencies": { "regex-utilities": "^2.3.0" } }, "sha512-0YCaSCq2VRIebiaUviZNs0cBz1kg5kVS2UKUfNIx8YVs1cN3AV7NTctO5FOKBA+UT2BPJIWZauYHPqJODG50cg=="],
+
+ "regex-utilities": ["regex-utilities@2.3.0", "", {}, "sha512-8VhliFJAWRaUiVvREIiW2NXXTmHs4vMNnSzuJVhscgmGav3g9VDxLrQndI3dZZVVdp0ZO/5v0xmX516/7M9cng=="],
+
+ "rfdc": ["rfdc@1.4.1", "", {}, "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA=="],
+
+ "rollup": ["rollup@4.63.1", "", { "dependencies": { "@types/estree": "1.0.9" }, "optionalDependencies": { "@napi-rs/lzma-linux-x64-gnu": "1.5.1", "@rollup/rollup-android-arm-eabi": "4.63.1", "@rollup/rollup-android-arm64": "4.63.1", "@rollup/rollup-darwin-arm64": "4.63.1", "@rollup/rollup-darwin-x64": "4.63.1", "@rollup/rollup-freebsd-arm64": "4.63.1", "@rollup/rollup-freebsd-x64": "4.63.1", "@rollup/rollup-linux-arm-gnueabihf": "4.63.1", "@rollup/rollup-linux-arm-musleabihf": "4.63.1", "@rollup/rollup-linux-arm64-gnu": "4.63.1", "@rollup/rollup-linux-arm64-musl": "4.63.1", "@rollup/rollup-linux-loong64-gnu": "4.63.1", "@rollup/rollup-linux-loong64-musl": "4.63.1", "@rollup/rollup-linux-ppc64-gnu": "4.63.1", "@rollup/rollup-linux-ppc64-musl": "4.63.1", "@rollup/rollup-linux-riscv64-gnu": "4.63.1", "@rollup/rollup-linux-riscv64-musl": "4.63.1", "@rollup/rollup-linux-s390x-gnu": "4.63.1", "@rollup/rollup-linux-x64-gnu": "4.63.1", "@rollup/rollup-linux-x64-musl": "4.63.1", "@rollup/rollup-openbsd-x64": "4.63.1", "@rollup/rollup-openharmony-arm64": "4.63.1", "@rollup/rollup-win32-arm64-msvc": "4.63.1", "@rollup/rollup-win32-ia32-msvc": "4.63.1", "@rollup/rollup-win32-x64-gnu": "4.63.1", "@rollup/rollup-win32-x64-msvc": "4.63.1", "fsevents": "~2.3.2" }, "bin": { "rollup": "dist/bin/rollup" } }, "sha512-3Df9jsstwhccuEfmAMi9l8XUh/GOkVObmFTU7CCVBysEbcOZLl84jCtaAZMcPiMz2EGKsATzQcU+Xr3n/wU6cg=="],
+
+ "search-insights": ["search-insights@2.17.3", "", {}, "sha512-RQPdCYTa8A68uM2jwxoY842xDhvx3E5LFL1LxvxCNMev4o5mLuokczhzjAgGwUZBAmOKZknArSxLKmXtIi2AxQ=="],
+
+ "shiki": ["shiki@2.5.0", "", { "dependencies": { "@shikijs/core": "2.5.0", "@shikijs/engine-javascript": "2.5.0", "@shikijs/engine-oniguruma": "2.5.0", "@shikijs/langs": "2.5.0", "@shikijs/themes": "2.5.0", "@shikijs/types": "2.5.0", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-mI//trrsaiCIPsja5CNfsyNOqgAZUb6VpJA+340toL42UpzQlXpwRV9nch69X6gaUxrr9kaOOa6e3y3uAkGFxQ=="],
+
+ "source-map-js": ["source-map-js@1.2.1", "", {}, "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA=="],
+
+ "space-separated-tokens": ["space-separated-tokens@2.0.2", "", {}, "sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q=="],
+
+ "speakingurl": ["speakingurl@14.0.1", "", {}, "sha512-1POYv7uv2gXoyGFpBCmpDVSNV74IfsWlDW216UPjbWufNf+bSU6GdbDsxdcxtfwb4xlI3yxzOTKClUosxARYrQ=="],
+
+ "stringify-entities": ["stringify-entities@4.0.4", "", { "dependencies": { "character-entities-html4": "^2.0.0", "character-entities-legacy": "^3.0.0" } }, "sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg=="],
+
+ "superjson": ["superjson@2.2.6", "", { "dependencies": { "copy-anything": "^4" } }, "sha512-H+ue8Zo4vJmV2nRjpx86P35lzwDT3nItnIsocgumgr0hHMQ+ZGq5vrERg9kJBo5AWGmxZDhzDo+WVIJqkB0cGA=="],
+
+ "tabbable": ["tabbable@6.5.0", "", {}, "sha512-wieBHXygIm7OyQOu5hQlkk62/WyCFYGlWg7L6/ZCUZwx0o398Zkn4pVmMyfYhfMG8kGrj/Krt8eIk6UKC6VzwA=="],
+
+ "trim-lines": ["trim-lines@3.0.1", "", {}, "sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg=="],
+
+ "unist-util-is": ["unist-util-is@6.0.1", "", { "dependencies": { "@types/unist": "^3.0.0" } }, "sha512-LsiILbtBETkDz8I9p1dQ0uyRUWuaQzd/cuEeS1hoRSyW5E5XGmTzlwY1OrNzzakGowI9Dr/I8HVaw4hTtnxy8g=="],
+
+ "unist-util-position": ["unist-util-position@5.0.0", "", { "dependencies": { "@types/unist": "^3.0.0" } }, "sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA=="],
+
+ "unist-util-stringify-position": ["unist-util-stringify-position@4.0.0", "", { "dependencies": { "@types/unist": "^3.0.0" } }, "sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ=="],
+
+ "unist-util-visit": ["unist-util-visit@5.1.0", "", { "dependencies": { "@types/unist": "^3.0.0", "unist-util-is": "^6.0.0", "unist-util-visit-parents": "^6.0.0" } }, "sha512-m+vIdyeCOpdr/QeQCu2EzxX/ohgS8KbnPDgFni4dQsfSCtpz8UqDyY5GjRru8PDKuYn7Fq19j1CQ+nJSsGKOzg=="],
+
+ "unist-util-visit-parents": ["unist-util-visit-parents@6.0.2", "", { "dependencies": { "@types/unist": "^3.0.0", "unist-util-is": "^6.0.0" } }, "sha512-goh1s1TBrqSqukSc8wrjwWhL0hiJxgA8m4kFxGlQ+8FYQ3C/m11FcTs4YYem7V664AhHVvgoQLk890Ssdsr2IQ=="],
+
+ "vfile": ["vfile@6.0.3", "", { "dependencies": { "@types/unist": "^3.0.0", "vfile-message": "^4.0.0" } }, "sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q=="],
+
+ "vfile-message": ["vfile-message@4.0.3", "", { "dependencies": { "@types/unist": "^3.0.0", "unist-util-stringify-position": "^4.0.0" } }, "sha512-QTHzsGd1EhbZs4AsQ20JX1rC3cOlt/IWJruk893DfLRr57lcnOeMaWG4K0JrRta4mIJZKth2Au3mM3u03/JWKw=="],
+
+ "vite": ["vite@5.4.21", "", { "dependencies": { "esbuild": "^0.21.3", "postcss": "^8.4.43", "rollup": "^4.20.0" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^18.0.0 || >=20.0.0", "less": "*", "lightningcss": "^1.21.0", "sass": "*", "sass-embedded": "*", "stylus": "*", "sugarss": "*", "terser": "^5.4.0" }, "optionalPeers": ["@types/node", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser"], "bin": { "vite": "bin/vite.js" } }, "sha512-o5a9xKjbtuhY6Bi5S3+HvbRERmouabWbyUcpXXUA1u+GNUKoROi9byOJ8M0nHbHYHkYICiMlqxkg1KkYmm25Sw=="],
+
+ "vitepress": ["vitepress@1.6.4", "", { "dependencies": { "@docsearch/css": "3.8.2", "@docsearch/js": "3.8.2", "@iconify-json/simple-icons": "^1.2.21", "@shikijs/core": "^2.1.0", "@shikijs/transformers": "^2.1.0", "@shikijs/types": "^2.1.0", "@types/markdown-it": "^14.1.2", "@vitejs/plugin-vue": "^5.2.1", "@vue/devtools-api": "^7.7.0", "@vue/shared": "^3.5.13", "@vueuse/core": "^12.4.0", "@vueuse/integrations": "^12.4.0", "focus-trap": "^7.6.4", "mark.js": "8.11.1", "minisearch": "^7.1.1", "shiki": "^2.1.0", "vite": "^5.4.14", "vue": "^3.5.13" }, "peerDependencies": { "markdown-it-mathjax3": "^4", "postcss": "^8" }, "optionalPeers": ["markdown-it-mathjax3", "postcss"], "bin": { "vitepress": "bin/vitepress.js" } }, "sha512-+2ym1/+0VVrbhNyRoFFesVvBvHAVMZMK0rw60E3X/5349M1GuVdKeazuksqopEdvkKwKGs21Q729jX81/bkBJg=="],
+
+ "vue": ["vue@3.5.42", "", { "dependencies": { "@vue/compiler-dom": "3.5.42", "@vue/compiler-sfc": "3.5.42", "@vue/runtime-dom": "3.5.42", "@vue/server-renderer": "3.5.42", "@vue/shared": "3.5.42" }, "peerDependencies": { "typescript": "*" }, "optionalPeers": ["typescript"] }, "sha512-4RyHQTbQvOPs3MfvUO1Sg0YRrKNnA0mAVtvpd12Tg1fKDN7OHBUl1IqSn8zGJjK9nI3NkNp8cgTpVrSZC5TTcA=="],
+
+ "zwitch": ["zwitch@2.0.4", "", {}, "sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A=="],
+ }
+}
diff --git a/docs/config/docker.md b/docs/config/docker.md
new file mode 100644
index 0000000..55eca17
--- /dev/null
+++ b/docs/config/docker.md
@@ -0,0 +1,52 @@
+# Docker 与存储配置
+
+## SQLite(单实例)
+
+SQLite 是单实例部署的推荐后端。下面的命令可以直接启动,不要求挂载 volume:
+
+```bash
+docker run -d --name codebuddy2api --restart unless-stopped -p 8001:8001 \
+ -e CODEBUDDY_STORAGE_BACKEND=sqlite \
+ -e CODEBUDDY_STORAGE_ENCRYPTION_KEY='replace-with-a-long-random-secret' \
+ -e CODEBUDDY_STORAGE_IMPORT_LEGACY_FILES=false \
+ ghcr.io/orangeboychen/codebuddy2api:latest
+```
+
+SQLite 文件默认位于容器内的 `/app/.codebuddy_data/storage.sqlite`。如果要在容器删除或重建后保留数据,请增加 `-v codebuddy2api-data:/app/.codebuddy_data`。
+
+启动后打开 `http://127.0.0.1:8001/dashboard`。
+
+## PostgreSQL(多实例)
+
+多实例部署使用 PostgreSQL:
+
+```bash
+-e CODEBUDDY_STORAGE_BACKEND=pg \
+-e DATABASE_URL='postgres://user:password@db:5432/codebuddy2api' \
+-e CODEBUDDY_STORAGE_ENCRYPTION_KEY='replace-with-a-long-random-secret'
+```
+
+## 旧文件迁移
+
+如果已有 `.codebuddy_creds` 或旧 JSON 配置,首次启动数据库后端时不要设置 `CODEBUDDY_STORAGE_IMPORT_LEGACY_FILES=false`,并临时挂载旧目录:
+
+```bash
+-v "$(pwd)/.codebuddy_creds:/app/.codebuddy_creds"
+```
+
+确认迁移完成后,设置 `CODEBUDDY_STORAGE_IMPORT_LEGACY_FILES=false`,并移除旧目录挂载。
+
+## 环境变量
+
+| 变量 | 作用 | 示例 / 默认值 |
+| --------------------------------------- | -------------------------------------- | ------------------------------------------------ |
+| `CODEBUDDY_STORAGE_BACKEND` | 存储后端 | `file`、`sqlite` 或 `pg` |
+| `CODEBUDDY_STORAGE_SQLITE_PATH` | SQLite 文件路径 | `.codebuddy_data/storage.sqlite` |
+| `CODEBUDDY_STORAGE_ENCRYPTION_KEY` | 加密数据库中的敏感数据;数据库后端必填 | 长随机字符串 |
+| `CODEBUDDY_STORAGE_IMPORT_LEGACY_FILES` | 是否从旧文件导入数据 | 新部署设为 `false` |
+| `DATABASE_URL` | PostgreSQL 连接字符串 | `postgres://user:password@db:5432/codebuddy2api` |
+| `CODEBUDDY_API_ENDPOINT` | 上游 CodeBuddy API 地址 | `https://copilot.tencent.com` |
+| `CODEBUDDY_AUTH_MODE` | 上游认证模式 | `auto` / `token` |
+| `CODEBUDDY_INTERNET_ENVIRONMENT` | 网络环境 | `internal` / `ioa` / `public` |
+| `CODEBUDDY_LOG_LEVEL` | 日志级别 | `INFO` |
+| `CODEBUDDY_ADMIN_PASSKEY_RP_ID` | 管理员 Passkey 使用的 hostname | `example.com` |
diff --git a/docs/config/local.md b/docs/config/local.md
new file mode 100644
index 0000000..9dd3732
--- /dev/null
+++ b/docs/config/local.md
@@ -0,0 +1,11 @@
+# 本地运行(开发)
+
+本地运行主要用于开发和调试,不是生产部署方式。
+
+```bash
+bun install
+mkdir -p .codebuddy_data .codebuddy_creds
+bun run dev
+```
+
+打开 `http://127.0.0.1:3000/dashboard`。`.codebuddy_creds` 用于兼容旧文件认证;SQLite 开发时可只使用 `.codebuddy_data`。
diff --git a/docs/config/storage.md b/docs/config/storage.md
new file mode 100644
index 0000000..6bc0dd1
--- /dev/null
+++ b/docs/config/storage.md
@@ -0,0 +1,9 @@
+# 存储选择
+
+| 后端 | 适用场景 | 持久化方式 |
+| -------- | ---------------------- | -------------------------------- |
+| `file` | 兼容已有部署或临时运行 | 文件目录 |
+| `sqlite` | 单实例生产部署(推荐) | `.codebuddy_data/storage.sqlite` |
+| `pg` | 多实例或高并发部署 | PostgreSQL 服务 |
+
+数据库后端都需要设置 `CODEBUDDY_STORAGE_ENCRYPTION_KEY`。新部署使用 SQLite 时,只需设置 `CODEBUDDY_STORAGE_BACKEND=sqlite`;是否挂载 `.codebuddy_data` 取决于你是否需要容器重建后保留数据。
diff --git a/docs/en/config/docker.md b/docs/en/config/docker.md
new file mode 100644
index 0000000..22cf45e
--- /dev/null
+++ b/docs/en/config/docker.md
@@ -0,0 +1,52 @@
+# Docker and Storage Configuration
+
+## SQLite (single instance)
+
+SQLite is the recommended backend for a single instance. This command starts without a volume:
+
+```bash
+docker run -d --name codebuddy2api --restart unless-stopped -p 8001:8001 \
+ -e CODEBUDDY_STORAGE_BACKEND=sqlite \
+ -e CODEBUDDY_STORAGE_ENCRYPTION_KEY='replace-with-a-long-random-secret' \
+ -e CODEBUDDY_STORAGE_IMPORT_LEGACY_FILES=false \
+ ghcr.io/orangeboychen/codebuddy2api:latest
+```
+
+The database defaults to `/app/.codebuddy_data/storage.sqlite`. Add `-v codebuddy2api-data:/app/.codebuddy_data` if data must survive container removal or recreation.
+
+Open `http://127.0.0.1:8001/dashboard` after startup.
+
+## PostgreSQL (multiple instances)
+
+Use PostgreSQL for multiple instances:
+
+```bash
+-e CODEBUDDY_STORAGE_BACKEND=pg \
+-e DATABASE_URL='postgres://user:password@db:5432/codebuddy2api' \
+-e CODEBUDDY_STORAGE_ENCRYPTION_KEY='replace-with-a-long-random-secret'
+```
+
+## Legacy file migration
+
+If you have `.codebuddy_creds` or legacy JSON configuration, omit `CODEBUDDY_STORAGE_IMPORT_LEGACY_FILES=false` on the first database-backend startup and temporarily mount the legacy directory:
+
+```bash
+-v "$(pwd)/.codebuddy_creds:/app/.codebuddy_creds"
+```
+
+After migration, set `CODEBUDDY_STORAGE_IMPORT_LEGACY_FILES=false` and remove the legacy mount.
+
+## Environment variables
+
+| Variable | Purpose | Example / default |
+| --------------------------------------- | ---------------------------------------------------------------- | ------------------------------------------------ |
+| `CODEBUDDY_STORAGE_BACKEND` | Storage backend | `file`, `sqlite`, or `pg` |
+| `CODEBUDDY_STORAGE_SQLITE_PATH` | SQLite file path | `.codebuddy_data/storage.sqlite` |
+| `CODEBUDDY_STORAGE_ENCRYPTION_KEY` | Encrypts sensitive database data; required for database backends | Long random string |
+| `CODEBUDDY_STORAGE_IMPORT_LEGACY_FILES` | Imports legacy files | Set to `false` for a new deployment |
+| `DATABASE_URL` | PostgreSQL connection string | `postgres://user:password@db:5432/codebuddy2api` |
+| `CODEBUDDY_API_ENDPOINT` | Upstream CodeBuddy API URL | `https://copilot.tencent.com` |
+| `CODEBUDDY_AUTH_MODE` | Upstream authentication mode | `auto` / `token` |
+| `CODEBUDDY_INTERNET_ENVIRONMENT` | Network environment | `internal` / `ioa` / `public` |
+| `CODEBUDDY_LOG_LEVEL` | Server log level | `INFO` |
+| `CODEBUDDY_ADMIN_PASSKEY_RP_ID` | Admin passkey hostname | `example.com` |
diff --git a/docs/en/config/local.md b/docs/en/config/local.md
new file mode 100644
index 0000000..1dc23cd
--- /dev/null
+++ b/docs/en/config/local.md
@@ -0,0 +1,11 @@
+# Local Development
+
+Run locally for development and debugging, not as the production deployment method.
+
+```bash
+bun install
+mkdir -p .codebuddy_data .codebuddy_creds
+bun run dev
+```
+
+Open `http://127.0.0.1:3000/dashboard`. `.codebuddy_creds` supports legacy file-based authentication; SQLite development can use only `.codebuddy_data`.
diff --git a/docs/en/config/storage.md b/docs/en/config/storage.md
new file mode 100644
index 0000000..2304443
--- /dev/null
+++ b/docs/en/config/storage.md
@@ -0,0 +1,9 @@
+# Storage Choices
+
+| Backend | Best for | Persistence |
+| -------- | ---------------------------------------- | -------------------------------- |
+| `file` | Existing deployments or temporary runs | File directories |
+| `sqlite` | Single-instance production (recommended) | `.codebuddy_data/storage.sqlite` |
+| `pg` | Multiple instances or high concurrency | PostgreSQL service |
+
+Database backends require `CODEBUDDY_STORAGE_ENCRYPTION_KEY`. For a new SQLite deployment, set `CODEBUDDY_STORAGE_BACKEND=sqlite`; mount `.codebuddy_data` only when data must survive container recreation.
diff --git a/docs/en/guide/account-status.md b/docs/en/guide/account-status.md
new file mode 100644
index 0000000..6152d81
--- /dev/null
+++ b/docs/en/guide/account-status.md
@@ -0,0 +1,10 @@
+# Account Status
+
+Account Status checks quota, check-in state, and available models for each CodeBuddy account.
+
+1. Click **Refresh all**, or click **Refresh** on one account.
+2. Review the plan, used / total quota, remaining percentage, and **Reset time**.
+3. Check the check-in state; click **Check in** or **Check in all** when available.
+4. Expand **Available models** before choosing a model in API Test or a client.
+
+If quota is unknown or the query fails, verify the credential in **Credentials**, then inspect **Debug**.
diff --git a/docs/en/guide/api-test.md b/docs/en/guide/api-test.md
new file mode 100644
index 0000000..6dbca00
--- /dev/null
+++ b/docs/en/guide/api-test.md
@@ -0,0 +1,11 @@
+# API Test
+
+API Test verifies the proxy path without configuring an external client.
+
+1. Select an active credential.
+2. Select one of its available models.
+3. Enter a **Test message**.
+4. Turn **Stream response** on or off.
+5. Click **Send test** and read **Response result**.
+
+The **API usage examples** section includes curl and Python. Use the Dashboard API endpoint as the client base URL and send the created API key as a bearer token.
diff --git a/docs/en/guide/credentials.md b/docs/en/guide/credentials.md
new file mode 100644
index 0000000..d887c56
--- /dev/null
+++ b/docs/en/guide/credentials.md
@@ -0,0 +1,23 @@
+# Credentials
+
+Credentials has three areas: automatic authentication, API keys, and saved credentials.
+
+## Automatic authentication
+
+1. Click **Start authentication** under **Automatic authentication**.
+2. Click **Open link** and finish signing in to CodeBuddy.
+3. Return to the console and wait for the status check, or click **Check authentication status**.
+4. If the callback does not complete, choose **Manual callback**, paste the full callback URL, and submit it.
+
+## Add manually
+
+Choose **Add credential manually**, enter a CodeBuddy Bearer Token, optionally enter a user ID, choose **Chat** or **Responses**, and click **Save**.
+
+## API keys
+
+1. Click **Create API key** in the **API key** area.
+2. Enter a name and select the active credentials to bind.
+3. Copy the secret immediately; click **Reveal key** only when needed.
+4. Clients send `Authorization: Bearer ` or `x-api-key: `.
+
+Use **Refresh**, **Edit**, and **Delete** in the saved-credentials list. Reauthenticate an expired credential.
diff --git a/docs/en/guide/dashboard.md b/docs/en/guide/dashboard.md
new file mode 100644
index 0000000..40b04ac
--- /dev/null
+++ b/docs/en/guide/dashboard.md
@@ -0,0 +1,16 @@
+# Dashboard
+
+Open Dashboard after signing in to confirm that the proxy and credential pool are ready.
+
+## What you see
+
+- **Total credentials** and the number marked **active**.
+- **Calls today**, **Tokens today**, and **Cache-hit tokens today**.
+- **API endpoint**, the `/v1` base URL for clients.
+
+## First use
+
+1. If no credentials are active, open **Credentials** and complete authentication.
+2. Copy the **API endpoint**, then create an API key in **Credentials**.
+3. Configure your client with the endpoint and key.
+4. Use **API Test** to send a first request.
diff --git a/docs/en/guide/debug.md b/docs/en/guide/debug.md
new file mode 100644
index 0000000..c64a3f9
--- /dev/null
+++ b/docs/en/guide/debug.md
@@ -0,0 +1,11 @@
+# Debug
+
+Debug helps diagnose failed requests, model errors, and upstream responses. Records are sanitized.
+
+1. Enable **Enable Debug capture**.
+2. Choose an **Auto-refresh interval**, or choose **Off** and click **Refresh** manually.
+3. Set **Max retained records** and click **Save**.
+4. Reproduce the problem from a client or **API Test**.
+5. Filter by interface type, model, credential, or API key. Expand a record to inspect requests, responses, status, token counts, TPS, and duration.
+
+**Clear** removes retained Debug records.
diff --git a/docs/en/guide/quick-start.md b/docs/en/guide/quick-start.md
new file mode 100644
index 0000000..c3b4d63
--- /dev/null
+++ b/docs/en/guide/quick-start.md
@@ -0,0 +1,21 @@
+# Quick Start
+
+## Docker with SQLite
+
+```bash
+docker run -d --name codebuddy2api --restart unless-stopped -p 8001:8001 \
+ -e CODEBUDDY_STORAGE_BACKEND=sqlite \
+ -e CODEBUDDY_STORAGE_ENCRYPTION_KEY='replace-with-a-long-random-secret' \
+ -e CODEBUDDY_STORAGE_IMPORT_LEGACY_FILES=false \
+ ghcr.io/orangeboychen/codebuddy2api:latest
+```
+
+Open `http://127.0.0.1:8001/dashboard`. SQLite is recommended for a single instance; use PostgreSQL for multiple instances.
+
+## First-use checklist
+
+1. Authorize a CodeBuddy account in **Credentials**.
+2. Refresh quota and confirm the model in **Account Status**.
+3. Create an API key in **Credentials**.
+4. Send a test message in **API Test**.
+5. Configure your client with the `/v1` endpoint and API key.
diff --git a/docs/en/guide/settings.md b/docs/en/guide/settings.md
new file mode 100644
index 0000000..43f2985
--- /dev/null
+++ b/docs/en/guide/settings.md
@@ -0,0 +1,24 @@
+# Settings
+
+Settings controls service parameters, credential models, usage data, and console security.
+
+## Service settings
+
+| Field | Purpose |
+| ----------------------------------------- | ----------------------------------------------------- |
+| CodeBuddy API endpoint | Upstream URL; default `https://copilot.tencent.com` |
+| Admin passkey RP ID / domain | WebAuthn hostname only; do not include scheme or port |
+| Authentication mode (auto/token) | Upstream authentication method |
+| Network environment (internal/ioa/public) | Upstream network environment |
+| Log level | Choose `DEBUG`, `INFO`, `WARNING`, or `ERROR` |
+
+Click **Save** after changing a field.
+
+## Models and usage
+
+- **Credential models** lists models for each credential; edit the list or click **Refresh**.
+- **Usage event cache** can be permanently cleared with **Clear usage event cache**.
+
+## Console security
+
+Set the administrator username, password, and confirmation password under **Console security**, then click **Save**. Disabling authentication makes the console directly accessible.
diff --git a/docs/en/guide/usage.md b/docs/en/guide/usage.md
new file mode 100644
index 0000000..c8973c8
--- /dev/null
+++ b/docs/en/guide/usage.md
@@ -0,0 +1,17 @@
+# Usage
+
+Usage shows request volume, token consumption, and model distribution.
+
+## Filter and refresh
+
+1. Choose **Time range**: `1 hour`, `3 hours`, `6 hours`, `12 hours`, `24 hours`, `3 days`, `7 days`, `Today`, or `Yesterday`.
+2. Select credentials and API keys, or choose **All credentials** and **All API keys**.
+3. Click **Refresh**, or choose **Off**, `5 sec`, `15 sec`, `30 sec`, `60 sec`, or `300 sec` under **Auto-refresh**.
+
+## Read the results
+
+- Summary cards show **Calls**, **Total tokens**, and **Cache-hit tokens**.
+- **Call trend** and **Token trend** show changes over time and by model.
+- **Credential usage** and **Model summary** provide table breakdowns.
+
+**Clear history** permanently deletes saved usage events.
diff --git a/docs/en/index.md b/docs/en/index.md
new file mode 100644
index 0000000..c0b20c3
--- /dev/null
+++ b/docs/en/index.md
@@ -0,0 +1,25 @@
+---
+layout: home
+
+hero:
+ name: CodeBuddy2API
+ tagline: Self-host CodeBuddy and manage credentials, keys, and usage.
+ image:
+ src: /codebuddy-dashboard.png
+ alt: CodeBuddy admin console
+ actions:
+ - theme: brand
+ text: Get started
+ link: /en/guide/quick-start
+ - theme: alt
+ text: View on GitHub
+ link: https://github.com/orangeboyChen/codebuddy2api
+
+features:
+ - title: Compatible APIs
+ details: OpenAI-compatible and Anthropic-compatible endpoints.
+ - title: Admin console
+ details: Manage credentials, access keys, usage, debug logs, and settings.
+ - title: Flexible storage
+ details: Zero-config file storage, SQLite for single instances, and PostgreSQL for multiple instances.
+---
diff --git a/docs/guide/account-status.md b/docs/guide/account-status.md
new file mode 100644
index 0000000..77ae9cb
--- /dev/null
+++ b/docs/guide/account-status.md
@@ -0,0 +1,10 @@
+# 账号状态
+
+账号状态用于检查每个 CodeBuddy 账号还能使用多少额度、是否完成签到,以及可以调用哪些模型。
+
+1. 点击「刷新全部」更新所有账号;只检查一个账号时,点击该账号的「刷新」。
+2. 查看账号、套餐、已使用 / 总额、剩余百分比和「重置时间」。
+3. 查看「签到」状态;按钮可用时点击「签到」,也可以使用「全部签到」。
+4. 展开「可用模型」,确认 API 测试或客户端要使用的模型名称。
+
+如果显示「额度未知」或查询失败,先到「凭证管理」确认账号仍然有效;如果账号正常,再到「Debug」查看最近的请求记录。
diff --git a/docs/guide/api-test.md b/docs/guide/api-test.md
new file mode 100644
index 0000000..4b58d68
--- /dev/null
+++ b/docs/guide/api-test.md
@@ -0,0 +1,11 @@
+# API 测试
+
+API 测试可以不配置外部客户端,直接从管理页面发送一条真实请求。它适合首次安装后确认账号、模型和 API 地址都正常。
+
+1. 在「凭证」下拉框选择一个有效凭证。
+2. 在「模型」下拉框选择该凭证支持的模型。
+3. 在「测试消息」输入要发送的内容。
+4. 按需打开「流式响应」。
+5. 点击「发送测试」,在「响应结果」中查看返回内容或错误信息。
+
+页面下方的「API 使用示例」提供 curl 和 Python 示例。客户端的 base URL 使用仪表板中的「API 服务端点」,并把创建的 API Key 放入 `Authorization: Bearer `。测试成功后,可以直接复制同样的地址和认证方式到客户端。
diff --git a/docs/guide/credentials.md b/docs/guide/credentials.md
new file mode 100644
index 0000000..891de15
--- /dev/null
+++ b/docs/guide/credentials.md
@@ -0,0 +1,23 @@
+# 凭证管理
+
+凭证管理用于添加 CodeBuddy 账号,并创建客户端访问服务所需的 API Key。
+
+## 自动认证
+
+1. 在「自动获取认证」区域点击「开始认证」。系统会生成一个登录链接。
+2. 点击生成的「打开链接」,在 CodeBuddy 页面完成登录。
+3. 返回控制台,等待系统自动检查认证状态;也可以点击「检查认证状态」。认证成功后,账号会出现在「已保存的凭证」列表中。
+4. 如果自动回调没有完成,点击「手动回调」,粘贴浏览器地址栏中的完整回调 URL 并提交。
+
+## 手动添加凭证
+
+如果不能使用自动认证,点击「手动添加凭证」,填写 CodeBuddy Bearer Token;「用户 ID」可以留空。根据账号支持的接口选择「Chat」或「Responses」,再点击「保存」。
+
+## API Key
+
+1. 在「API Key」区域点击「创建 API Key」。
+2. 输入名称,并选择要绑定的有效凭证。
+3. 保存后立即复制 secret;列表中默认只显示脱敏值,需要时点击「查看 Key」。
+4. 客户端请求使用 `Authorization: Bearer ` 或 `x-api-key: `。
+
+创建完成后,使用「已保存的凭证」区域的「刷新」「编辑」和「删除」维护账号。凭证过期后,重新完成认证。
diff --git a/docs/guide/dashboard.md b/docs/guide/dashboard.md
new file mode 100644
index 0000000..8b6538a
--- /dev/null
+++ b/docs/guide/dashboard.md
@@ -0,0 +1,17 @@
+# 仪表板
+
+登录后先打开仪表板,确认服务已经启动,并查看当前账号是否可以使用。
+
+## 你会看到什么
+
+- **总凭证数量**:已保存的 CodeBuddy 凭证总数;卡片下方显示其中的「有效」数量。
+- **今日调用次数**、**今日总 Tokens**、**今日缓存 Token**:当天的请求和 Token 汇总。
+- **API 服务端点**:客户端要填写的 API 地址,通常以 `/v1` 结尾。
+
+## 第一次使用
+
+1. 如果「有效」数量为 0,打开「凭证管理」添加一个 CodeBuddy 账号。
+2. 打开「账号状态」,确认账号有剩余额度,并找到要使用的模型。
+3. 回到「凭证管理」创建 API Key,复制生成的 secret。
+4. 把 API 服务端点和 API Key 填入你的客户端。
+5. 如果客户端还没有请求成功,先在「API 测试」发送一条消息。
diff --git a/docs/guide/debug.md b/docs/guide/debug.md
new file mode 100644
index 0000000..f83da40
--- /dev/null
+++ b/docs/guide/debug.md
@@ -0,0 +1,16 @@
+# Debug
+
+Debug 页面用于排查请求失败、模型选择错误和上游响应异常。只有开启采集后,新请求才会出现在列表中;记录内容会自动脱敏。
+
+## 开始采集
+
+1. 打开「启用 Debug 采集」。
+2. 在「自动刷新时间」选择刷新间隔,或选择「关闭」后手动点击「刷新」。
+3. 设置「最大保留条数」,点击「保存」。
+4. 回到客户端或「API 测试」重现一次问题。
+
+## 查找一条请求
+
+使用「接口类型」「模型」「凭据」和「API Key」筛选记录。展开一条记录后,可以按顺序查看原始请求、发送给上游的请求、上游回包和返回给客户端的内容,以及状态、Token、TPS 和耗时。
+
+点击「清空」会删除当前保留的 Debug 记录。
diff --git a/docs/guide/quick-start.md b/docs/guide/quick-start.md
new file mode 100644
index 0000000..fabdd1d
--- /dev/null
+++ b/docs/guide/quick-start.md
@@ -0,0 +1,24 @@
+# 快速开始
+
+## Docker 运行(SQLite)
+
+```bash
+docker run -d \
+ --name codebuddy2api \
+ --restart unless-stopped \
+ -p 8001:8001 \
+ -e CODEBUDDY_STORAGE_BACKEND=sqlite \
+ -e CODEBUDDY_STORAGE_ENCRYPTION_KEY='replace-with-a-long-random-secret' \
+ -e CODEBUDDY_STORAGE_IMPORT_LEGACY_FILES=false \
+ ghcr.io/orangeboyChen/codebuddy2api:latest
+```
+
+管理控制台地址为 `http://127.0.0.1:8001/dashboard`。
+
+首次使用建议按以下顺序操作:
+
+1. 在「凭据」页完成 CodeBuddy 自动认证。
+2. 在「账号状态」页刷新配额并确认目标模型可用。
+3. 在「凭据」页创建 API key。
+4. 在「API 测试」页发送一条测试消息。
+5. 将 `/v1` 地址和 API key 配置到你的客户端。
diff --git a/docs/guide/settings.md b/docs/guide/settings.md
new file mode 100644
index 0000000..6de914a
--- /dev/null
+++ b/docs/guide/settings.md
@@ -0,0 +1,24 @@
+# 设置
+
+设置页用于修改上游连接方式、模型列表、用量记录和控制台登录方式。
+
+## 服务配置
+
+填写或选择以下项目后点击「保存」:
+
+| 页面字段 | 说明 |
+| ------------------------------- | -------------------------------------------- |
+| CodeBuddy 官方 API 端点 | 上游地址,默认 `https://copilot.tencent.com` |
+| 管理员 Passkey RP ID / 域名 | WebAuthn 使用的 hostname,不要填写协议或端口 |
+| 认证模式(auto/token) | 上游认证方式 |
+| 网络环境(internal/ioa/public) | 上游网络环境 |
+| 日志级别 | 选择 `DEBUG`、`INFO`、`WARNING` 或 `ERROR` |
+
+## 凭证模型和用量
+
+- 「凭证模型」列出每个凭证支持的模型;可以编辑模型列表,或点击「刷新」重新获取。
+- 「用量统计缓存」中的「清空用量统计缓存」会删除全部用量记录,且无法撤销。
+
+## 控制台安全
+
+在「控制台安全」中设置管理员用户名、密码和确认密码,点击「保存」启用登录保护。关闭鉴权后,知道地址的用户可以直接打开控制台。
diff --git a/docs/guide/usage.md b/docs/guide/usage.md
new file mode 100644
index 0000000..30f167c
--- /dev/null
+++ b/docs/guide/usage.md
@@ -0,0 +1,18 @@
+# 用量统计
+
+用量统计用于查看服务用了多少请求和 Token,并找出主要消耗来自哪个账号或模型。
+
+## 筛选和刷新
+
+1. 在「时间范围」选择「1 小时」「3 小时」「6 小时」「12 小时」「24 小时」「3 天」「7 天」「今天」或「昨天」。
+2. 在「凭证」和「API Key」中选择要查看的对象;「全部凭证」和「全部 API Key」表示不筛选。
+3. 点击「刷新」立即重新加载数据,或在「自动刷新」中选择「关闭」「5 秒」「15 秒」「30 秒」「60 秒」或「300 秒」。
+
+## 阅读结果
+
+- 顶部汇总显示「调用次数」「总 Tokens」和「缓存 Token」。
+- 「调用次数趋势」和「Token 消耗趋势」按时间、模型展示变化。
+- 「凭证使用统计」按凭证汇总调用次数、总 Tokens 和缓存 Token。
+- 「模型汇总」按模型汇总调用次数、总 Tokens 和缓存 Token。
+
+点击「清空历史」会删除已保存的用量统计历史,无法恢复。
diff --git a/docs/index.md b/docs/index.md
new file mode 100644
index 0000000..8ac51b3
--- /dev/null
+++ b/docs/index.md
@@ -0,0 +1,25 @@
+---
+layout: home
+
+hero:
+ name: CodeBuddy2API
+ tagline: 自托管 CodeBuddy,统一管理凭据、密钥和用量。
+ image:
+ src: /codebuddy-dashboard.png
+ alt: CodeBuddy 管理界面
+ actions:
+ - theme: brand
+ text: 开始使用
+ link: /guide/quick-start
+ - theme: alt
+ text: 查看 GitHub
+ link: https://github.com/orangeboyChen/codebuddy2api
+
+features:
+ - title: 多协议兼容
+ details: 同时提供 OpenAI-compatible 和 Anthropic-compatible API。
+ - title: 内置管理控制台
+ details: 管理凭据、访问密钥、用量、调试日志和运行时设置。
+ - title: 灵活存储
+ details: 默认零配置文件存储,也支持 SQLite 和 PostgreSQL。
+---
diff --git a/docs/ja/config/docker.md b/docs/ja/config/docker.md
new file mode 100644
index 0000000..866de31
--- /dev/null
+++ b/docs/ja/config/docker.md
@@ -0,0 +1,52 @@
+# Docker とストレージの設定
+
+## SQLite(単一インスタンス)
+
+SQLite は単一インスタンス向けの推奨バックエンドです。以下のコマンドは volume なしで起動できます。
+
+```bash
+docker run -d --name codebuddy2api --restart unless-stopped -p 8001:8001 \
+ -e CODEBUDDY_STORAGE_BACKEND=sqlite \
+ -e CODEBUDDY_STORAGE_ENCRYPTION_KEY='replace-with-a-long-random-secret' \
+ -e CODEBUDDY_STORAGE_IMPORT_LEGACY_FILES=false \
+ ghcr.io/orangeboychen/codebuddy2api:latest
+```
+
+データベースの既定パスは `/app/.codebuddy_data/storage.sqlite` です。コンテナの削除や再作成後もデータを残す場合は `-v codebuddy2api-data:/app/.codebuddy_data` を追加します。
+
+起動後に `http://127.0.0.1:8001/dashboard` を開きます。
+
+## PostgreSQL(複数インスタンス)
+
+複数インスタンスでは PostgreSQL を使用します。
+
+```bash
+-e CODEBUDDY_STORAGE_BACKEND=pg \
+-e DATABASE_URL='postgres://user:password@db:5432/codebuddy2api' \
+-e CODEBUDDY_STORAGE_ENCRYPTION_KEY='replace-with-a-long-random-secret'
+```
+
+## 旧ファイルからの移行
+
+`.codebuddy_creds` や旧 JSON 設定がある場合、データベースバックエンドの初回起動時は `CODEBUDDY_STORAGE_IMPORT_LEGACY_FILES=false` を設定せず、旧ディレクトリを一時的にマウントします。
+
+```bash
+-v "$(pwd)/.codebuddy_creds:/app/.codebuddy_creds"
+```
+
+移行完了後に `CODEBUDDY_STORAGE_IMPORT_LEGACY_FILES=false` を設定し、旧ディレクトリのマウントを削除します。
+
+## 環境変数
+
+| 変数 | 用途 | 例 / 既定値 |
+| --------------------------------------- | -------------------------------------------------- | ------------------------------------------------ |
+| `CODEBUDDY_STORAGE_BACKEND` | ストレージバックエンド | `file`、`sqlite`、`pg` |
+| `CODEBUDDY_STORAGE_SQLITE_PATH` | SQLite ファイルパス | `.codebuddy_data/storage.sqlite` |
+| `CODEBUDDY_STORAGE_ENCRYPTION_KEY` | DB 内の機密データを暗号化。DB バックエンドでは必須 | 長いランダム文字列 |
+| `CODEBUDDY_STORAGE_IMPORT_LEGACY_FILES` | 旧ファイルを取り込むか | 新規構築では `false` |
+| `DATABASE_URL` | PostgreSQL 接続文字列 | `postgres://user:password@db:5432/codebuddy2api` |
+| `CODEBUDDY_API_ENDPOINT` | 上流 CodeBuddy API URL | `https://copilot.tencent.com` |
+| `CODEBUDDY_AUTH_MODE` | 上流認証モード | `auto` / `token` |
+| `CODEBUDDY_INTERNET_ENVIRONMENT` | ネットワーク環境 | `internal` / `ioa` / `public` |
+| `CODEBUDDY_LOG_LEVEL` | サーバーログレベル | `INFO` |
+| `CODEBUDDY_ADMIN_PASSKEY_RP_ID` | 管理者 Passkey の hostname | `example.com` |
diff --git a/docs/ja/config/local.md b/docs/ja/config/local.md
new file mode 100644
index 0000000..09bcb3f
--- /dev/null
+++ b/docs/ja/config/local.md
@@ -0,0 +1,11 @@
+# ローカル開発
+
+ローカル起動は開発とデバッグ用です。本番デプロイには Docker を使用してください。
+
+```bash
+bun install
+mkdir -p .codebuddy_data .codebuddy_creds
+bun run dev
+```
+
+`http://127.0.0.1:3000/dashboard` を開きます。`.codebuddy_creds` は旧ファイル認証との互換用で、SQLite の開発では `.codebuddy_data` だけでも実行できます。
diff --git a/docs/ja/config/storage.md b/docs/ja/config/storage.md
new file mode 100644
index 0000000..033065c
--- /dev/null
+++ b/docs/ja/config/storage.md
@@ -0,0 +1,9 @@
+# ストレージの選択
+
+| バックエンド | 用途 | 永続化 |
+| ------------ | ---------------------------------- | -------------------------------- |
+| `file` | 既存環境との互換性または一時実行 | ファイルディレクトリ |
+| `sqlite` | 単一インスタンスの本番環境(推奨) | `.codebuddy_data/storage.sqlite` |
+| `pg` | 複数インスタンスまたは高負荷 | PostgreSQL サービス |
+
+DB バックエンドでは `CODEBUDDY_STORAGE_ENCRYPTION_KEY` が必要です。新規 SQLite 構築では `CODEBUDDY_STORAGE_BACKEND=sqlite` を設定し、コンテナ再作成後もデータを残す場合だけ `.codebuddy_data` をマウントします。
diff --git a/docs/ja/guide/account-status.md b/docs/ja/guide/account-status.md
new file mode 100644
index 0000000..fd6afbc
--- /dev/null
+++ b/docs/ja/guide/account-status.md
@@ -0,0 +1,10 @@
+# アカウント状態
+
+アカウント状態では各 CodeBuddy アカウントの使用量、チェックイン、利用可能なモデルを確認します。
+
+1. 「すべて更新」を押すか、個別アカウントの「更新」を押します。
+2. プラン、使用済み / 合計、残りの割合、「リセット時刻」を確認します。
+3. チェックイン状態を確認し、可能な場合は「チェックイン」または「すべてチェックイン」を押します。
+4. API テストやクライアントで選ぶ前に「利用可能なモデル」を展開します。
+
+使用量が不明、または確認に失敗した場合は「認証情報」を確認し、「Debug」を開きます。
diff --git a/docs/ja/guide/api-test.md b/docs/ja/guide/api-test.md
new file mode 100644
index 0000000..e56ba61
--- /dev/null
+++ b/docs/ja/guide/api-test.md
@@ -0,0 +1,11 @@
+# API テスト
+
+API テストでは外部クライアントを設定せずにプロキシ経路を確認できます。
+
+1. 有効な認証情報を選びます。
+2. 利用可能なモデルを選びます。
+3. 「テストメッセージ」を入力します。
+4. 「ストリームレスポンス」をオンまたはオフにします。
+5. 「テストを送信」を押し、「レスポンス結果」を確認します。
+
+「API 使用例」には curl と Python の例があります。クライアントの base URL にはダッシュボードの API エンドポイントを使い、作成した API key を Bearer token として送信します。
diff --git a/docs/ja/guide/credentials.md b/docs/ja/guide/credentials.md
new file mode 100644
index 0000000..d652a0e
--- /dev/null
+++ b/docs/ja/guide/credentials.md
@@ -0,0 +1,23 @@
+# 認証情報
+
+認証情報ページには自動認証、API key、保存済み認証情報があります。
+
+## 自動認証
+
+1. 「自動取得認証」で「認証を開始」を押します。
+2. 生成された「リンクを開く」を押し、CodeBuddy にサインインします。
+3. コンソールに戻り、自動チェックを待つか「認証状態を確認」を押します。
+4. コールバックが完了しない場合は「手動コールバック」を選び、ブラウザの完全なコールバック URL を貼り付けて送信します。
+
+## 手動追加
+
+「認証情報を手動追加」で CodeBuddy Bearer Token を入力します。ユーザー ID は任意です。「Chat」または「Responses」を選択し、「保存」を押します。
+
+## API key
+
+1. 「API key」で「API key を作成」を押します。
+2. 名前を入力し、紐付ける有効な認証情報を選びます。
+3. 保存後すぐに secret をコピーします。必要な場合だけ「API key を表示」を押します。
+4. クライアントは `Authorization: Bearer ` または `x-api-key: ` を送信します。
+
+保存済み認証情報では「更新」「編集」「削除」を使えます。期限切れの認証情報は再認証してください。
diff --git a/docs/ja/guide/dashboard.md b/docs/ja/guide/dashboard.md
new file mode 100644
index 0000000..21103e3
--- /dev/null
+++ b/docs/ja/guide/dashboard.md
@@ -0,0 +1,16 @@
+# ダッシュボード
+
+ログイン後にダッシュボードを開き、プロキシと認証情報プールの状態を確認します。
+
+## 表示内容
+
+- **認証情報総数**と、そのうち**有効**な件数。
+- **本日の呼び出し回数**、**本日の総 Tokens**、**本日の Cache ヒット Tokens**。
+- クライアントが使用する `/v1` のベース URL である **API エンドポイント**。
+
+## 初回利用
+
+1. 有効な認証情報がない場合は「認証情報」を開いて認証します。
+2. **API エンドポイント**をコピーし、「認証情報」で API key を作成します。
+3. エンドポイントと API key をクライアントに設定します。
+4. 「API テスト」で最初のリクエストを送信します。
diff --git a/docs/ja/guide/debug.md b/docs/ja/guide/debug.md
new file mode 100644
index 0000000..5a726ad
--- /dev/null
+++ b/docs/ja/guide/debug.md
@@ -0,0 +1,11 @@
+# Debug
+
+Debug はリクエスト失敗、モデルエラー、上流レスポンスの調査に使います。記録は自動的にマスクされます。
+
+1. 「Debug 収集を有効化」をオンにします。
+2. 「自動更新間隔」を選ぶか、「オフ」にして「更新」を手動で押します。
+3. 「保持する最大件数」を設定し、「保存」を押します。
+4. クライアントまたは「API テスト」から問題を再現します。
+5. インターフェースの種類、モデル、認証情報、API キーで絞り込みます。記録を展開すると、リクエスト、レスポンス、ステータス、Token、TPS、時間を確認できます。
+
+「クリア」は保持中の Debug 記録を削除します。
diff --git a/docs/ja/guide/quick-start.md b/docs/ja/guide/quick-start.md
new file mode 100644
index 0000000..2ec9e98
--- /dev/null
+++ b/docs/ja/guide/quick-start.md
@@ -0,0 +1,21 @@
+# クイックスタート
+
+## SQLite を使った Docker 起動
+
+```bash
+docker run -d --name codebuddy2api --restart unless-stopped -p 8001:8001 \
+ -e CODEBUDDY_STORAGE_BACKEND=sqlite \
+ -e CODEBUDDY_STORAGE_ENCRYPTION_KEY='replace-with-a-long-random-secret' \
+ -e CODEBUDDY_STORAGE_IMPORT_LEGACY_FILES=false \
+ ghcr.io/orangeboyChen/codebuddy2api:latest
+```
+
+`http://127.0.0.1:8001/dashboard` を開きます。単一インスタンスには SQLite、複数インスタンスには PostgreSQL を推奨します。
+
+## 初回利用の手順
+
+1. 「認証情報」で CodeBuddy アカウントを認証します。
+2. 「アカウント状態」でクォータとモデルを確認します。
+3. 「認証情報」で API key を作成します。
+4. 「API テスト」でテストメッセージを送信します。
+5. `/v1` endpoint と API key をクライアントに設定します。
diff --git a/docs/ja/guide/settings.md b/docs/ja/guide/settings.md
new file mode 100644
index 0000000..87bc0bd
--- /dev/null
+++ b/docs/ja/guide/settings.md
@@ -0,0 +1,24 @@
+# 設定
+
+設定ではサービスパラメータ、認証情報モデル、使用量データ、コンソールのセキュリティを管理します。
+
+## サービス設定
+
+| 項目 | 用途 |
+| --------------------------------------- | ------------------------------------------------ |
+| CodeBuddy API エンドポイント | 上流 URL。既定値は `https://copilot.tencent.com` |
+| 管理者 passkey RP ID / ドメイン | WebAuthn の hostname。スキームとポートは含めない |
+| 認証モード(auto/token) | 上流認証方式 |
+| ネットワーク環境(internal/ioa/public) | 上流ネットワーク環境 |
+| ログレベル | `DEBUG`、`INFO`、`WARNING`、`ERROR` を選択 |
+
+変更後に「保存」を押します。
+
+## モデルと使用量
+
+- 「認証情報モデル」ではモデル一覧を編集するか、「更新」を押して再取得します。
+- 「使用量統計キャッシュ」の「使用量統計キャッシュを消去」はすべての使用量記録を完全に削除します。
+
+## コンソールのセキュリティ
+
+「コンソールのセキュリティ」で管理者ユーザー名、パスワード、確認用パスワードを設定し、「保存」を押します。認証を無効にするとコンソールへ直接アクセスできます。
diff --git a/docs/ja/guide/usage.md b/docs/ja/guide/usage.md
new file mode 100644
index 0000000..5c1247b
--- /dev/null
+++ b/docs/ja/guide/usage.md
@@ -0,0 +1,17 @@
+# 使用量
+
+使用量ではリクエスト数、Token 消費、モデル分布を確認できます。
+
+## 絞り込みと更新
+
+1. 「期間」で「1 時間」「3 時間」「6 時間」「12 時間」「24 時間」「3 日」「7 日」「今日」または「昨日」を選びます。
+2. 「認証情報」と「API key」を選択します。「すべての認証情報」と「すべての API key」は絞り込みなしです。
+3. 「更新」を押すか、「自動更新」で「オフ」「5 秒」「15 秒」「30 秒」「60 秒」「300 秒」を選びます。
+
+## 結果の見方
+
+- 上部には「呼び出し回数」「総 Tokens」「Cache ヒット Tokens」が表示されます。
+- 「呼び出し推移」と「Token 推移」で時間とモデルごとの変化を確認します。
+- 「認証情報利用状況」と「モデル集計」で内訳を確認します。
+
+「履歴をクリア」は保存済みの使用量イベントを完全に削除します。
diff --git a/docs/ja/index.md b/docs/ja/index.md
new file mode 100644
index 0000000..6f42df5
--- /dev/null
+++ b/docs/ja/index.md
@@ -0,0 +1,25 @@
+---
+layout: home
+
+hero:
+ name: CodeBuddy2API
+ tagline: CodeBuddy をセルフホストし、認証情報・キー・利用量を管理します。
+ image:
+ src: /codebuddy-dashboard.png
+ alt: CodeBuddy 管理コンソール
+ actions:
+ - theme: brand
+ text: はじめる
+ link: /ja/guide/quick-start
+ - theme: alt
+ text: GitHub
+ link: https://github.com/orangeboyChen/codebuddy2api
+
+features:
+ - title: 互換 API
+ details: OpenAI 互換と Anthropic 互換のエンドポイントを提供します。
+ - title: 管理コンソール
+ details: 認証情報、アクセスキー、利用量、デバッグログ、設定を管理します。
+ - title: 柔軟なストレージ
+ details: ゼロ設定のファイル、単一インスタンス向け SQLite、複数インスタンス向け PostgreSQL。
+---
diff --git a/docs/package.json b/docs/package.json
new file mode 100644
index 0000000..f48a600
--- /dev/null
+++ b/docs/package.json
@@ -0,0 +1,13 @@
+{
+ "name": "codebuddy2api-docs",
+ "private": true,
+ "type": "module",
+ "scripts": {
+ "dev": "vitepress dev .",
+ "build": "vitepress build .",
+ "preview": "vitepress preview ."
+ },
+ "devDependencies": {
+ "vitepress": "^1.6.4"
+ }
+}
diff --git a/docs/public/codebuddy-dashboard.png b/docs/public/codebuddy-dashboard.png
new file mode 100644
index 0000000..6ae5347
Binary files /dev/null and b/docs/public/codebuddy-dashboard.png differ
diff --git a/e2e/admin-console.spec.ts b/e2e/admin-console.spec.ts
index 59808d8..c15b095 100644
--- a/e2e/admin-console.spec.ts
+++ b/e2e/admin-console.spec.ts
@@ -169,12 +169,21 @@ test.describe('Admin console essentials', () => {
`[data-credential-filename="${filename}"]`,
);
await expect(credentialCard).toBeVisible();
- await credentialCard.getByRole('button', { name: 'Edit' }).click();
+ const editButton = credentialCard.getByRole('button', { name: 'Edit' });
const saveButton = credentialCard.getByRole('button', {
name: 'Save',
exact: true,
});
- await expect(saveButton).toBeVisible();
+ await expect
+ .poll(
+ async () => {
+ if (await saveButton.isVisible()) return true;
+ await editButton.click();
+ return saveButton.isVisible();
+ },
+ { intervals: [100, 250, 500], timeout: 10_000 },
+ )
+ .toBe(true);
await saveButton.click();
await expect(page.getByText('Credential saved:')).toBeVisible();
diff --git a/eslint.config.ts b/eslint.config.ts
index f326561..f4b1e8f 100644
--- a/eslint.config.ts
+++ b/eslint.config.ts
@@ -11,6 +11,8 @@ const eslintConfig = [
globalIgnores([
'.next/**',
'coverage/**',
+ 'docs/.vitepress/**',
+ '**/node_modules/**',
'node_modules/**',
'next-env.d.ts',
'tsconfig.tsbuildinfo',
diff --git a/package.json b/package.json
index f465ba7..3280382 100644
--- a/package.json
+++ b/package.json
@@ -4,6 +4,9 @@
"private": true,
"scripts": {
"dev": "next dev",
+ "docs:dev": "cd docs && bun run dev",
+ "docs:build": "cd docs && bun run build",
+ "docs:preview": "cd docs && bun run preview",
"prepare": "husky",
"build": "next build",
"test:e2e": "playwright test",
@@ -79,7 +82,7 @@
},
"lint-staged": {
"*.{ts,tsx}": [
- "eslint --max-warnings=0 --fix",
+ "eslint --max-warnings=0 --no-warn-ignored --fix",
"prettier --check"
],
"*.{json,md,yml,yaml,css}": [
diff --git a/tsconfig.json b/tsconfig.json
index a25b618..2986fe8 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -30,5 +30,5 @@
".next/types/**/*.ts",
".next/dev/types/**/*.ts"
],
- "exclude": ["node_modules"]
+ "exclude": ["node_modules", "docs"]
}
diff --git a/vitest.config.ts b/vitest.config.ts
index 2ae9e34..6d56f34 100644
--- a/vitest.config.ts
+++ b/vitest.config.ts
@@ -28,6 +28,8 @@ const vitestConfig = defineConfig({
exclude: [
'.next/**',
'coverage/**',
+ 'docs/.vitepress/**',
+ '**/node_modules/**',
'dist/**',
'e2e/**',
'node_modules/**',