From 84c66ccaa7893d1195b355c9cc400212d2ea9914 Mon Sep 17 00:00:00 2001 From: Echo <1711788888@qq.com> Date: Thu, 5 Mar 2026 21:28:41 +0800 Subject: [PATCH] =?UTF-8?q?:art:=20=E5=A2=9E=E5=8A=A0=E5=B9=B6=E5=8F=91?= =?UTF-8?q?=E8=AE=BF=E9=97=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .dockerignore | 54 + .gitignore | 34 + CHANGELOG.md | 779 ++++ CLAUDE.md | 100 + Cargo.lock | 2587 ++++++++++++++ Cargo.toml | 42 + Dockerfile | 51 + Makefile | 68 + README.md | 525 +++ VERSION | 1 + admin-ui/bun.lock | 480 +++ admin-ui/index.html | 13 + admin-ui/postcss.config.js | 6 + admin-ui/public/vite.svg | 1 + admin-ui/src/App.tsx | 37 + admin-ui/src/api/credentials.ts | 147 + .../src/components/add-credential-dialog.tsx | 279 ++ admin-ui/src/components/balance-dialog.tsx | 106 + .../src/components/batch-import-dialog.tsx | 434 +++ .../src/components/batch-verify-dialog.tsx | 152 + admin-ui/src/components/credential-card.tsx | 476 +++ admin-ui/src/components/dashboard.tsx | 700 ++++ .../components/import-token-json-dialog.tsx | 671 ++++ admin-ui/src/components/kam-import-dialog.tsx | 491 +++ admin-ui/src/components/login-page.tsx | 62 + admin-ui/src/components/ui/badge.tsx | 39 + admin-ui/src/components/ui/button.tsx | 55 + admin-ui/src/components/ui/card.tsx | 78 + admin-ui/src/components/ui/checkbox.tsx | 28 + admin-ui/src/components/ui/dialog.tsx | 119 + admin-ui/src/components/ui/input.tsx | 24 + admin-ui/src/components/ui/progress.tsx | 35 + admin-ui/src/components/ui/sonner.tsx | 25 + admin-ui/src/components/ui/switch.tsx | 26 + admin-ui/src/hooks/use-credentials.ts | 175 + admin-ui/src/index.css | 90 + admin-ui/src/lib/format.ts | 44 + admin-ui/src/lib/storage.ts | 7 + admin-ui/src/lib/utils.ts | 202 ++ admin-ui/src/main.tsx | 22 + admin-ui/src/types/api.ts | 311 ++ admin-ui/tailwind.config.js | 53 + admin-ui/vite.config.ts | 28 + docker-compose.yml | 13 + docs/media.md | 75 + docs/qrcode_1769645166806.png | Bin 0 -> 104150 bytes examples/test_compression.rs | 146 + src/admin/error.rs | 64 + src/admin/handlers.rs | 139 + src/admin/middleware.rs | 50 + src/admin/mod.rs | 28 + src/admin/router.rs | 53 + src/admin/service.rs | 612 ++++ src/admin/types.rs | 335 ++ src/admin_ui/mod.rs | 7 + src/admin_ui/router.rs | 109 + src/anthropic/compressor.rs | 1063 ++++++ src/anthropic/converter.rs | 2852 +++++++++++++++ src/anthropic/handlers.rs | 1731 +++++++++ src/anthropic/middleware.rs | 94 + src/anthropic/mod.rs | 36 + src/anthropic/router.rs | 79 + src/anthropic/stream.rs | 1937 ++++++++++ src/anthropic/tool_compression.rs | 276 ++ src/anthropic/truncation.rs | 282 ++ src/anthropic/types.rs | 315 ++ src/anthropic/websearch.rs | 987 ++++++ src/common/auth.rs | 41 + src/common/mod.rs | 5 + src/common/redact.rs | 97 + src/common/utf8.rs | 46 + src/debug.rs | 210 ++ src/http_client.rs | 105 + src/image.rs | 559 +++ src/kiro/affinity.rs | 86 + src/kiro/background_refresh.rs | 346 ++ src/kiro/cooldown.rs | 373 ++ src/kiro/fingerprint.rs | 301 ++ src/kiro/machine_id.rs | 171 + src/kiro/mod.rs | 13 + src/kiro/model/common/mod.rs | 4 + src/kiro/model/credentials.rs | 849 +++++ src/kiro/model/events/assistant.rs | 115 + src/kiro/model/events/base.rs | 186 + src/kiro/model/events/context_usage.rs | 40 + src/kiro/model/events/mod.rs | 13 + src/kiro/model/events/tool_use.rs | 52 + src/kiro/model/mod.rs | 16 + src/kiro/model/requests/conversation.rs | 408 +++ src/kiro/model/requests/kiro.rs | 68 + src/kiro/model/requests/mod.rs | 7 + src/kiro/model/requests/tool.rs | 192 + src/kiro/model/token_refresh.rs | 44 + src/kiro/model/usage_limits.rs | 213 ++ src/kiro/parser/crc.rs | 37 + src/kiro/parser/decoder.rs | 476 +++ src/kiro/parser/error.rs | 94 + src/kiro/parser/frame.rs | 178 + src/kiro/parser/header.rs | 317 ++ src/kiro/parser/mod.rs | 10 + src/kiro/provider.rs | 1335 +++++++ src/kiro/rate_limiter.rs | 461 +++ src/kiro/token_manager.rs | 3152 +++++++++++++++++ src/kiro/web_portal.rs | 554 +++ src/main.rs | 196 + src/model/arg.rs | 14 + src/model/config.rs | 341 ++ src/model/mod.rs | 4 + src/test.rs | 107 + src/token.rs | 242 ++ tools/analyze_compression.py | 691 ++++ tools/diagnose_improper_request.py | 646 ++++ tools/event-viewer.html | 896 +++++ tools/test_empty_content.py | 145 + 114 files changed, 35396 insertions(+) create mode 100644 .dockerignore create mode 100644 .gitignore create mode 100644 CHANGELOG.md create mode 100644 CLAUDE.md create mode 100644 Cargo.lock create mode 100644 Cargo.toml create mode 100644 Dockerfile create mode 100644 Makefile create mode 100644 README.md create mode 100644 VERSION create mode 100644 admin-ui/bun.lock create mode 100644 admin-ui/index.html create mode 100644 admin-ui/postcss.config.js create mode 100644 admin-ui/public/vite.svg create mode 100644 admin-ui/src/App.tsx create mode 100644 admin-ui/src/api/credentials.ts create mode 100644 admin-ui/src/components/add-credential-dialog.tsx create mode 100644 admin-ui/src/components/balance-dialog.tsx create mode 100644 admin-ui/src/components/batch-import-dialog.tsx create mode 100644 admin-ui/src/components/batch-verify-dialog.tsx create mode 100644 admin-ui/src/components/credential-card.tsx create mode 100644 admin-ui/src/components/dashboard.tsx create mode 100644 admin-ui/src/components/import-token-json-dialog.tsx create mode 100644 admin-ui/src/components/kam-import-dialog.tsx create mode 100644 admin-ui/src/components/login-page.tsx create mode 100644 admin-ui/src/components/ui/badge.tsx create mode 100644 admin-ui/src/components/ui/button.tsx create mode 100644 admin-ui/src/components/ui/card.tsx create mode 100644 admin-ui/src/components/ui/checkbox.tsx create mode 100644 admin-ui/src/components/ui/dialog.tsx create mode 100644 admin-ui/src/components/ui/input.tsx create mode 100644 admin-ui/src/components/ui/progress.tsx create mode 100644 admin-ui/src/components/ui/sonner.tsx create mode 100644 admin-ui/src/components/ui/switch.tsx create mode 100644 admin-ui/src/hooks/use-credentials.ts create mode 100644 admin-ui/src/index.css create mode 100644 admin-ui/src/lib/format.ts create mode 100644 admin-ui/src/lib/storage.ts create mode 100644 admin-ui/src/lib/utils.ts create mode 100644 admin-ui/src/main.tsx create mode 100644 admin-ui/src/types/api.ts create mode 100644 admin-ui/tailwind.config.js create mode 100644 admin-ui/vite.config.ts create mode 100644 docker-compose.yml create mode 100644 docs/media.md create mode 100644 docs/qrcode_1769645166806.png create mode 100644 examples/test_compression.rs create mode 100644 src/admin/error.rs create mode 100644 src/admin/handlers.rs create mode 100644 src/admin/middleware.rs create mode 100644 src/admin/mod.rs create mode 100644 src/admin/router.rs create mode 100644 src/admin/service.rs create mode 100644 src/admin/types.rs create mode 100644 src/admin_ui/mod.rs create mode 100644 src/admin_ui/router.rs create mode 100644 src/anthropic/compressor.rs create mode 100644 src/anthropic/converter.rs create mode 100644 src/anthropic/handlers.rs create mode 100644 src/anthropic/middleware.rs create mode 100644 src/anthropic/mod.rs create mode 100644 src/anthropic/router.rs create mode 100644 src/anthropic/stream.rs create mode 100644 src/anthropic/tool_compression.rs create mode 100644 src/anthropic/truncation.rs create mode 100644 src/anthropic/types.rs create mode 100644 src/anthropic/websearch.rs create mode 100644 src/common/auth.rs create mode 100644 src/common/mod.rs create mode 100644 src/common/redact.rs create mode 100644 src/common/utf8.rs create mode 100644 src/debug.rs create mode 100644 src/http_client.rs create mode 100644 src/image.rs create mode 100644 src/kiro/affinity.rs create mode 100644 src/kiro/background_refresh.rs create mode 100644 src/kiro/cooldown.rs create mode 100644 src/kiro/fingerprint.rs create mode 100644 src/kiro/machine_id.rs create mode 100644 src/kiro/mod.rs create mode 100644 src/kiro/model/common/mod.rs create mode 100644 src/kiro/model/credentials.rs create mode 100644 src/kiro/model/events/assistant.rs create mode 100644 src/kiro/model/events/base.rs create mode 100644 src/kiro/model/events/context_usage.rs create mode 100644 src/kiro/model/events/mod.rs create mode 100644 src/kiro/model/events/tool_use.rs create mode 100644 src/kiro/model/mod.rs create mode 100644 src/kiro/model/requests/conversation.rs create mode 100644 src/kiro/model/requests/kiro.rs create mode 100644 src/kiro/model/requests/mod.rs create mode 100644 src/kiro/model/requests/tool.rs create mode 100644 src/kiro/model/token_refresh.rs create mode 100644 src/kiro/model/usage_limits.rs create mode 100644 src/kiro/parser/crc.rs create mode 100644 src/kiro/parser/decoder.rs create mode 100644 src/kiro/parser/error.rs create mode 100644 src/kiro/parser/frame.rs create mode 100644 src/kiro/parser/header.rs create mode 100644 src/kiro/parser/mod.rs create mode 100644 src/kiro/provider.rs create mode 100644 src/kiro/rate_limiter.rs create mode 100644 src/kiro/token_manager.rs create mode 100644 src/kiro/web_portal.rs create mode 100644 src/main.rs create mode 100644 src/model/arg.rs create mode 100644 src/model/config.rs create mode 100644 src/model/mod.rs create mode 100644 src/test.rs create mode 100644 src/token.rs create mode 100644 tools/analyze_compression.py create mode 100644 tools/diagnose_improper_request.py create mode 100644 tools/event-viewer.html create mode 100644 tools/test_empty_content.py diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..311d8d5 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,54 @@ +# Rust build artifacts +target/ +Cargo.lock + +# Node.js dependencies and build artifacts +admin-ui/node_modules/ +admin-ui/dist/ +admin-ui/pnpm-lock.yaml +admin-ui/tsconfig.tsbuildinfo +admin-ui/.vite/ + +# Version control +.git/ +.gitignore + +# IDE and editor files +.idea/ +.vscode/ +*.swp +*.swo +*~ + +# Claude/AI +.claude/ +CLAUDE.md +AGENTS.md + +# CI/CD +.github/ + +# Documentation and examples +*.md +README.md +config.example.json +credentials.example.*.json + +# Development and test files +src/test.rs +src/debug.rs +test.json +tools/ + +# OS-specific files +.DS_Store +Thumbs.db + +# Local configuration (keep templates only) +config.json +credentials.json + +# Docker files +Dockerfile +.dockerignore +docker-compose*.yml \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..38f37c3 --- /dev/null +++ b/.gitignore @@ -0,0 +1,34 @@ +/target + +/config.json +/credentials.json +/.idea +/test.json + +/admin-ui/node_modules/ +/admin-ui/dist/ +/admin-ui/pnpm-lock.yaml +/admin-ui/package-lock.json +/admin-ui/tsconfig.tsbuildinfo + +.claude/ +!.claude/skills/ + +/credentials.* + +*.json +logs/ +data/ + +/kiro_balance_cache.json +/kiro_stats.json + +refs/ + +# Python +__pycache__/ +*.pyc +*.pyo +*.pyd + +config/ diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..41ea9f0 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,779 @@ +# Changelog + +## [v1.1.5] - 2026-03-02 + +### Fixed +- **防止孤立 tool_result 导致空消息** — `convert_user_message` 新增最终兜底逻辑,当 tool_result 被过滤为孤立块后 content 变空时插入占位符 ".",避免上游返回 400 "Improperly formed request";新增回归测试覆盖孤立 tool_result 场景 (`src/anthropic/converter.rs`) + +## [v1.1.4] - 2026-03-02 + +### Fixed +- **WebSearch 历史消息上下文保留** — `convert_assistant_message` 新增对 `server_tool_use`(忽略)和 `web_search_tool_result`(提取 title、url、snippet、page_age 为纯文本)的处理,修复多轮对话中搜索结果被静默丢弃导致 Kiro 丢失上下文的问题;使用纯文本格式彻底规避特殊字符破坏格式的风险;新增 2 个单元测试覆盖上述路径 (`src/anthropic/converter.rs`) + +## [v1.1.3] - 2026-02-27 + +### Changed +- **合并三个导入对话框为一个** — 删除冗余的 `KamImportDialog` 和 `BatchImportDialog`,统一使用 `ImportTokenJsonDialog`,自动识别 KAM 嵌套格式、扁平凭据格式和 Token JSON 格式 (`admin-ui/src/components/`) +- **上游网络错误归类为瞬态错误** — `error sending request`/`connection closed`/`connection reset` 纳入 `is_transient_upstream_error`,返回 502 且不输出请求体 (`src/anthropic/handlers.rs`) +- **上游报错不再输出完整请求体** — `sensitive-logs` 模式下仅输出请求体字节数 (`src/anthropic/handlers.rs`) +- **瞬态错误匹配大小写归一化** — `is_transient_upstream_error` 统一 `.to_lowercase()` 后匹配,提取 `NETWORK_ERROR_PATTERNS` 常量消除重复 (`src/anthropic/handlers.rs`) + +### Removed +- **移除负载均衡模式切换** — 删除前端按钮/API/hooks 和后端路由/handler/service/types/config 字段/token_manager 方法及测试,该设置实际未被使用 (`admin-ui/`, `src/admin/`, `src/kiro/token_manager.rs`, `src/model/config.rs`) + +### Added +- **Usage 诊断日志** — 流式/缓冲流式/非流式三条路径均增加 `sensitive-logs` 保护的 usage 日志,输出 estimated/context/final input_tokens 及来源、output_tokens (`src/anthropic/stream.rs`, `src/anthropic/handlers.rs`) + +## [v1.1.2] - 2026-02-27 + +### Added +- **Kiro Account Manager 导入** — 支持导入 KAM 导出的 JSON 凭据文件 (`admin-ui/src/components/kam-import-dialog.tsx`) +- **批量导入对话框** — 新增独立的批量导入凭据组件 (`admin-ui/src/components/batch-import-dialog.tsx`) +- **凭证 disabled 字段持久化** — 从配置文件读取 disabled 状态,支持手动禁用凭据跨重启保留 (`src/kiro/model/credentials.rs`, `src/kiro/token_manager.rs`) +- **凭据级 Region 编辑** — Admin UI 支持对已有凭据在线修改 `region` 和 `apiRegion`,点击凭据卡片内联编辑,保存后持久化 (`src/admin/`, `admin-ui/src/components/credential-card.tsx`) +- **Admin API `POST /credentials/:id/region`** — 新增 Region 修改接口,支持清除(传 null)或覆盖两个 region 字段 (`src/admin/handlers.rs`, `src/admin/router.rs`) + +### Fixed +- **WebSearch SSE 事件序列修正** — 调整 server_tool_use 位置、content block index、page_age 转换、usage 统计 (`src/anthropic/websearch.rs`) +- **Token Manager 统计回写** — 立即回写统计数据,清除已删除凭据残留 (`src/kiro/token_manager.rs`) +- **HTTP 非安全地址批量导入** — 修复 admin-ui 在 HTTP 下的导入错误 (`admin-ui/src/lib/utils.ts`) +- **Docker 端口绑定优化** — 修正端口绑定和配置目录挂载 (`docker-compose.yml`) +- **移除重复 Sonnet 4.6 模型项** — 删除 `/v1/models` 中重复的 claude-sonnet-4-6 条目,避免 id 冲突 (`src/anthropic/handlers.rs`) +- **防止自动禁用状态被持久化** — `persist_credentials()` 仅持久化手动禁用,避免重启后自动禁用变为手动禁用导致无法自愈 (`src/kiro/token_manager.rs`) +- **sha256Hex digest 异常回退** — 在 `crypto.subtle.digest` 外围加 try/catch,失败时回退到纯 JS 实现 (`admin-ui/src/lib/utils.ts`) +- **parseKamJson null 输入保护** — 对 JSON null 输入增加类型检查,避免 TypeError (`admin-ui/src/components/kam-import-dialog.tsx`) +- **额度查询 region 修复** — `get_usage_limits` 改用 `effective_api_region`,凭据指定 region 时不再因走错 endpoint 而 403 (`src/kiro/token_manager.rs`) +- **批量导入丢失 apiRegion** — `TokenJsonItem` 补充 `api_region` 字段,导入 JSON 中的 `apiRegion` 不再被丢弃 (`src/admin/types.rs`, `src/admin/service.rs`) +- **API 请求使用凭据级 region** — `provider.rs` 的 `base_url`/`mcp_url`/`base_domain` 改用 `credentials.effective_api_region()`,凭据配置了 region 时不再错误地走全局 config region 导致 403 (`src/kiro/provider.rs`) +- **Region 编辑 stale state** — 点击编辑 Region 时同步最新 props 到 input,避免后台刷新后提交旧值覆盖服务端数据 (`admin-ui/src/components/credential-card.tsx`) +- **Region 值未 trim** — `set_region` 保存前对 region/apiRegion 做 trim,防止带空格的值持久化后生成无效 URL (`src/admin/service.rs`) +- **过滤超长工具名** — `convert_tools` 过滤掉 name 超过 64 字符的工具,避免上游拒绝整个请求 (`src/anthropic/converter.rs`) +- **429 错误不再输出完整请求体** — 瞬态上游错误(429/5xx)走独立分支返回 429,不触发 sensitive-logs 的请求体诊断日志 (`src/anthropic/handlers.rs`) +- **兼容旧 authRegion 配置** — `credentials.region` 增加 `#[serde(alias = "authRegion")]`,旧配置文件中的 `authRegion` 字段不再被静默忽略 (`src/kiro/model/credentials.rs`) +- **导入凭据 region 规范化** — token.json 导入路径对 region/apiRegion 做 trim + 空字符串转 None,与 `set_region` 逻辑一致 (`src/admin/service.rs`) + +### Changed +- **默认 kiro_version 更新至 0.10.0** (`src/model/config.rs`) +- **Opus 模型映射调整** — opus 默认映射到 claude-opus-4.6,仅 4.5/4-5 显式映射到 claude-opus-4.5 (`src/anthropic/converter.rs`) +- **Sonnet 4.6 Model 字段补全** — 添加 context_length、max_completion_tokens、thinking 字段 (`src/anthropic/handlers.rs`) +- **Region 配置精简** — 删除 `credentials.auth_region` 和 `config.auth_region` 冗余字段;凭据的 `region` 同时用于 Token 刷新和 API 请求默认值,`api_region` 可单独覆盖 API 路由 (`src/kiro/model/credentials.rs`, `src/model/config.rs`) +- **添加凭据 UI Region 字段语义调整** — 前端"Auth Region"改为"Region"(对应 `credentials.region`),"API Region"保持,去除无意义的 `authRegion` 前端字段 (`admin-ui/`) + +## [v1.1.1] - 2026-02-18 + +### Fixed +- **修复 Sonnet 4.6 thinking 配置变量名错误** (`src/anthropic/handlers.rs`) + - 修正 thinking 配置覆写逻辑中的变量名拼写错误 + +## [v1.1.0] - 2026-02-18 + +### Added +- **Sonnet 4.6 模型支持** (`src/anthropic/handlers.rs`, `src/anthropic/converter.rs`, `src/anthropic/types.rs`) + - 添加 claude-sonnet-4-6 及其 thinking/agentic 变体到模型列表 + - 更新模型映射逻辑以正确识别 Sonnet 4.6 版本 + - 为 Sonnet 4.6 启用 1M 上下文窗口和 64K 最大输出 tokens + - 更新 thinking 配置覆写逻辑以支持 Sonnet 4.6 的 adaptive thinking + +## [v1.0.21] - 2026-02-17 + +### Changed +- **图片处理限制放宽** (`src/model/config.rs`) + - 单图总像素限制从 1.15M 放宽至 4M,支持更高分辨率图片直接透传 + - 图片长边限制从 1568 放宽至 4000,减少不必要的缩放压缩 + +## [v1.0.20] - 2026-02-17 + +### Fixed +- **空消息内容验证与错误分类改进** (`src/anthropic/converter.rs`, `src/anthropic/handlers.rs`) + - 新增 `ConversionError::EmptyMessageContent` 错误类型,在请求转换阶段验证消息内容不为空 + - 在 prefill 处理后验证最后一条消息内容有效性,支持字符串和数组两种 content 格式 + - 检测空字符串、空白字符、空数组等情况,避免向上游发送无效请求 + - 修复 `is_input_too_long_error` 错误地将 "Improperly formed request" 归类为上下文过长错误 + - 新增 `is_improperly_formed_request_error` 函数专门处理格式错误,返回准确的错误信息 + - 新增 3 个测试用例验证空消息内容检测功能 +- **图片文件大小压缩优化** (`src/image.rs`) + - 新增基于文件大小的强制重新编码逻辑:即使图片尺寸符合要求,如果文件大小超过 200KB 也会重新编码降低质量 + - 修复小尺寸高质量图片(如 483x480 但 631KB)直接透传导致请求体过大的问题 + - 新增日志输出追踪大文件重新编码过程和压缩率 + +## [v1.0.19] - 2026-02-17 + +### Changed +- **自适应压缩策略优化** (`src/anthropic/handlers.rs`) + - 请求体大小校验改为以实际序列化后的总字节数为准,不再扣除图片 base64 字节(上游存在约 5MiB 的硬性请求体大小限制,图片也必须计入) + - 压缩层级重排:当单条 user content 已超过阈值时优先截断超长消息(第三层),再移除历史消息(第四层),避免移除历史后仍无法降到阈值内 + - 新增 `has_any_tool_results_or_tools` / `has_any_tool_uses` 预检,跳过无效的 tool 阈值降低迭代 + - 历史消息移除改为批量 drain(单轮最多 16 条),提升大上下文场景的压缩效率 +- **请求体大小阈值默认上调至 4.5MiB** (`src/model/config.rs`, `config.example.json`) + - `compression.maxRequestBodyBytes` 从 400KB 上调至 4,718,592 字节(4.5MiB),匹配上游实际限制 + +### Fixed +- **cargo fmt 格式化** (`src/anthropic/converter.rs`, `src/image.rs`) + +## [v1.0.18] - 2026-02-17 + +### Added +- **GIF 动图抽帧采样与强制重编码** (`src/image.rs`, `src/anthropic/converter.rs`) + - 新增 `process_gif_frames()` 函数,将 GIF 动图抽帧为多张静态 JPEG,避免动图 base64 体积巨大导致 upstream 400 错误 + - 采样策略:总帧数不超过 20 帧,每秒最多 5 帧,超长 GIF 自动降低采样频率均匀抽取 + - 新增 `process_image_to_format()` 函数,支持将任意图片强制重编码为指定格式 + - GIF 抽帧失败时多级回退:JPEG 重编码 → 静态 GIF 处理 → 原始数据透传 + - `process_image()` 对 GIF 格式强制重编码为静态帧,即使无需缩放也避免透传体积巨大的动图 + - `ImageProcessResult` 新增 `was_reencoded`、`original_bytes_len`、`final_bytes_len` 字段 + +### Changed +- **请求体大小阈值默认上调** (`src/model/config.rs`, `config.example.json`) + - 上游存在请求体大小硬限制(实测约 5MiB 左右会触发 400),默认将 `compression.maxRequestBodyBytes` 上调至 4.5MiB 预留安全余量 +- **日志分析脚本同步更新** (`tools/analyze_compression.py`, `tools/diagnose_improper_request.py`) + - 修复 ANSI 序列污染解析,并增加“自适应二次压缩/本地超限拒绝”的统计输出 + +## [v1.0.17] - 2026-02-15 + +### Added +- **自适应压缩第四层:超长用户消息内容截断** (`src/anthropic/compressor.rs`, `src/anthropic/handlers.rs`) + - 新增 `compress_long_messages_pass()` 函数,截断超长的 User 消息 content(保留头部,尾部附加省略标记) + - 在 `adaptive_shrink_request_body` 的三层策略之后增加第四层兜底,解决单条消息过大(如粘贴整个文件)导致自适应压缩空转的问题 + - 动态计算截断阈值:初始为最大消息字符数的 3/4,每轮递减 3/4,最低 8192 字符 + - 日志新增 `final_message_content_max_chars` 字段便于排查 + +## [v1.0.16] - 2026-02-15 + +### Fixed +- **请求体日志仅在 upstream 报错时输出完整内容** (`src/anthropic/handlers.rs`) + - 移除发送前的完整请求体 DEBUG 日志(`sensitive-logs` 模式下每次请求都输出几十 KB JSON),统一只输出字节大小 + - upstream 报错时在 `sensitive-logs` 模式下以 ERROR 级别输出完整请求体(截断 base64),用于诊断 400/502 等错误 + +## [v1.0.15] - 2026-02-15 + +### Added +- **Opus 4.6 1M 上下文窗口支持** (`src/anthropic/types.rs`, `src/anthropic/handlers.rs`, `src/anthropic/stream.rs`) + - 新增 `get_context_window_size()` 函数,Opus 4.6 返回 1,000,000 tokens,其他模型返回 200,000 tokens + - 删除硬编码 `CONTEXT_WINDOW_SIZE` 常量,改用动态计算 + - `MAX_BUDGET_TOKENS` 从 24,576 提升到 128,000 + - `Model` 结构体新增 `context_length`、`max_completion_tokens`、`thinking` 字段 +- **Agentic 模型变体** (`src/anthropic/converter.rs`, `src/anthropic/handlers.rs`) + - 新增 sonnet-agentic、opus-4.5-agentic、opus-4.6-agentic、haiku-agentic 四个模型变体 + - `map_model()` 自动剥离 `-agentic` 后缀再映射 + - Agentic 模型注入专用系统提示,引导自主工作模式 +- **Thinking level 后缀** (`src/anthropic/handlers.rs`) + - 支持 `-thinking-minimal`(512)、`-thinking-low`(1024)、`-thinking-medium`(8192)、`-thinking-high`(24576)、`-thinking-xhigh`(32768) 后缀 +- **工具压缩** (`src/anthropic/tool_compression.rs` 新建) + - 20KB 阈值两步压缩:简化 input_schema → 按比例截断 description(最短 50 字符) +- **截断检测** (`src/anthropic/truncation.rs` 新建) + - 4 种截断类型的启发式检测(空输入、无效 JSON、缺少字段、未闭合字符串) + - 工具 JSON 解析失败时自动检测截断并生成软失败消息 + +### Changed +- 工具调用仅含 tool_use 时占位符从 `" "` 改为 `"."`,提升语义清晰度 + +## [v1.0.14] - 2026-02-15 + +### Fixed +- **sensitive-logs 模式下请求体日志截断** (`src/kiro/provider.rs`, `src/anthropic/handlers.rs`) + - 400 错误日志中的 `request_body` 字段改用 `truncate_body_for_log()` 截断(保留头尾各 1200 字符),避免输出包含大量 base64 图片数据的完整请求体 + - 工具输入 JSON 解析失败日志中的 `request_body` 字段改用 `truncate_middle()` 截断 + - 新增 `KiroProvider::truncate_body_for_log()` 函数,正确处理 UTF-8 多字节字符边界 + +## [v1.0.13] - 2026-02-14 + +### Fixed +- **请求体大小预检输出 image_bytes 归因信息** (`src/anthropic/handlers.rs`) + - 新增 `total_image_bytes()` 函数,计算 KiroRequest 中所有图片 base64 数据的总字节数 + - 错误提示信息增加 image_bytes 和 non-image bytes 字段,便于排查请求体大小归因 + +## [v1.0.12] - 2026-02-14 + +### Fixed +- **WebSearch 仅纯搜索请求走本地处理** (`src/anthropic/websearch.rs`, `src/anthropic/handlers.rs`) + - 新增 `should_handle_websearch_request()` 精确判断:仅当 tool_choice 强制选择 web_search、tools 仅含 web_search 单工具、或用户消息包含 `Perform a web search for the query:` 前缀时,才路由到本地 WebSearch 处理 + - 混合工具场景(web_search + 其他工具)改为剔除 web_search 后转发 upstream,避免普通对话被误当成搜索查询 + - 新增 `strip_web_search_tools()` 从 tools 列表中移除 web_search 工具 + - 搜索查询提取增加空白归一化处理 + +## [v1.0.11] - 2026-02-14 + +### Added +- **自适应二次压缩策略** (`src/anthropic/handlers.rs`) + - 请求体超过 `max_request_body_bytes` 阈值时,自动迭代压缩:逐步降低 tool_result/tool_use_input 截断阈值,最后按轮移除最老历史消息 + - 最多迭代 32 轮,避免极端输入导致过长 CPU 消耗 + - `post_messages` 和 `post_messages_cc` 均支持自适应压缩 +- **压缩后 tool_use/tool_result 配对修复** (`src/anthropic/compressor.rs`) + - 新增 `repair_tool_pairing_pass()`:历史截断后自动移除孤立的 tool_use 和 tool_result + - 解决截断破坏跨消息 tool_use→tool_result 配对导致 upstream 返回 400 "Improperly formed request" 的问题 +- **stable 版 `floor_char_boundary` 工具函数** (`src/common/utf8.rs`) + - 新增 `common::utf8` 模块,提供 stable Rust 下的 `floor_char_boundary()` 实现 + - 统一替换项目中散落的 `str::floor_char_boundary()` nightly 调用 + +### Fixed +- **WebSearch 支持混合工具列表** (`src/anthropic/websearch.rs`) + - `has_web_search_tool()` 改为只要 tools 中包含 web_search(按 name 或 type 判断)即走本地处理,不再要求 tools 仅有一个 + - `extract_search_query()` 改为取最后一条 user 消息,更符合多轮对话场景 + - 新增非流式(`stream: false`)响应支持,返回完整 JSON 而非 SSE 流 + +### Changed +- **迁移 `floor_char_boundary` 调用到 `common::utf8` 模块** (`src/anthropic/compressor.rs`, `src/anthropic/stream.rs`, `src/admin/service.rs`, `src/kiro/token_manager.rs`, `src/kiro/provider.rs`) + - 移除各文件中重复的 `floor_char_boundary` 内联实现,统一使用 `crate::common::utf8::floor_char_boundary` + +## [v1.0.10] - 2026-02-12 + +### Fixed +- **配额耗尽返回 429 而非 502** (`src/anthropic/handlers.rs`, `src/kiro/provider.rs`) + - 所有凭据配额耗尽时返回 `429 Too Many Requests`(`rate_limit_error`),而非 `502 Bad Gateway` + - 余额刷新时主动禁用低余额凭据(余额 < 1.0),402 分支同步清零余额缓存 +- **亲和性检查不再触发限流** (`src/kiro/token_manager.rs`) + - 亲和性检查改用 `check_rate_limit` 只读探测,消除"检查本身消耗速率配额"的恶性循环 + - 亲和性分流日志提升至 info 级别并脱敏 user_id,便于生产监控热点凭据 + +### Added +- **请求体大小预检** (`src/anthropic/handlers.rs`, `src/model/config.rs`) + - 新增 `max_request_body_bytes` 配置项,序列化后拦截超大请求避免无效 upstream 往返 + +### Changed +- **移除无意义的 max_tokens 调整逻辑** (`src/anthropic/handlers.rs`) + - 删除 max_tokens 超限警告日志和调整逻辑,因为该值实际不传递给 Kiro upstream + +## [v1.0.9] - 2026-02-12 + +### Fixed +- **修复 upstream 合并丢失的功能** (`src/anthropic/stream.rs`) + - 恢复 `stop_reason` 优先级逻辑:高优先级原因可覆盖低优先级(model_context_window_exceeded > max_tokens > tool_use > end_turn) + - 注释掉重复的 `content_block_start` 日志,避免日志噪音 + - 修复 `contextUsageEvent` 日志格式(保留4位小数) + - 移除冗余的 `find_char_boundary` 函数,改用标准库 `str::floor_char_boundary()` + +## [v1.0.8] - 2026-02-12 + +### Added +- **批量导入 token.json** (`src/admin/service.rs`, `src/admin/types.rs`) + - 新增 `import_token_json` 接口,支持批量导入官方 token.json 格式凭据 + - 自动映射 provider/authMethod 字段,支持 dry-run 预览模式 + - 去重检测:通过 refreshToken 前缀匹配避免重复导入 + +- **缓存余额查询接口** (`src/admin/handlers.rs`, `src/admin/router.rs`) + - 新增 `GET /admin/credentials/balances/cached` 端点 + - 返回所有凭据的缓存余额信息(含 TTL 和缓存时间) + +### Changed +- **用户亲和性支持** (`src/kiro/token_manager.rs`, `src/kiro/provider.rs`) + - 新增 `UserAffinityManager`:用户与凭据绑定,保持会话连续性 + - `acquire_context_for_user` 方法支持按 user_id 获取绑定凭据 + - 亲和性过期时间可配置,默认 30 分钟 + +- **余额缓存动态 TTL** (`src/kiro/token_manager.rs`) + - 基于使用频率动态调整缓存 TTL(高频用户更短 TTL) + - 新增 `update_balance_cache`、`get_all_cached_balances` 方法 + - 缓存持久化到 `kiro_balance_cache.json` + +- **请求压缩管道** (`src/anthropic/converter.rs`, `src/anthropic/middleware.rs`) + - `AppState` 新增 `compression_config` 字段 + - `convert_request` 支持 `CompressionConfig` 参数 + - 图片压缩、空白压缩、上下文截断等功能集成 + +- **凭据级代理配置** (`src/kiro/provider.rs`) + - KiroProvider 支持按凭据动态选择代理 + - 新增 `get_client_for_credential` 方法,缓存凭据级 client + - API 请求和 MCP 请求均使用凭据的 `effective_proxy()` + +- **API Region 分离** (`src/kiro/provider.rs`) + - `base_url`、`mcp_url`、`base_domain` 使用 `effective_api_region()` + - 支持 Token 刷新和 API 调用使用不同 region + +- **handlers 传递 user_id** (`src/anthropic/handlers.rs`) + - 从请求 metadata 提取 user_id 并传递给 provider + - 支持用户亲和性功能 + +### Fixed +- **修复 mask_user_id UTF-8 panic** (`src/anthropic/handlers.rs`) + - 使用 `chars()` 按字符而非字节切片,避免多字节字符导致 panic + +## [v1.0.7] - 2026-02-10 + +### Added +- **图片 Token 估算与压缩模块** (`src/image.rs`) + - 新增 `estimate_image_tokens()`: 从 base64 数据解析图片尺寸并估算 token 数 + - 新增 `process_image()`: 根据配置对大图进行缩放压缩 + - 实现 Anthropic 官方公式: `tokens = (width × height) / 750` + - 支持 JPEG/PNG/GIF/WebP 格式 + +### Changed +- **Token 计算支持图片** (`src/token.rs`) + - `count_all_tokens_local()` 现在处理 `type: "image"` 的 ContentBlock + - 调用 `estimate_image_tokens()` 计算图片 token,解决之前图片 token 被计为 0 的问题 +- **协议转换支持图片压缩** (`src/anthropic/converter.rs`) + - `process_message_content()` 新增图片压缩处理,根据配置自动缩放超限图片 + - 新增 `count_images_in_request()` 统计请求中图片总数,用于判断多图模式 + - 缩放后记录日志: `图片已缩放: (原始尺寸) -> (缩放后尺寸), tokens: xxx` +- **压缩配置新增图片参数** (`src/model/config.rs`) + - `image_max_long_edge`: 长边最大像素,默认 1568(Anthropic 推荐值) + - `image_max_pixels_single`: 单张图片最大总像素,默认 1,150,000(约 1600 tokens) + - `image_max_pixels_multi`: 多图模式下单张最大像素,默认 4,000,000(2000×2000) + - `image_multi_threshold`: 触发多图限制的图片数量阈值,默认 20 + +### Dependencies +- 新增 `image` crate (0.25): 图片处理(支持 JPEG/PNG/GIF/WebP) +- 新增 `base64` crate (0.22): Base64 编解码 + +## [v1.0.6] - 2026-02-10 + +### Changed +- **压缩统计日志改用字节单位** (`src/anthropic/handlers.rs`) + - 移除不准确的 token 估算(`compressed_input_tokens`、`tokens_saved`),改为直接输出字节数 + - 字段重命名:`whitespace_saved` → `whitespace_bytes_saved` 等,明确单位语义 + - 注释更新:说明字节统计用于排查 upstream 请求体大小限制 + +### Added +- **日志脱敏工具模块** (`src/common/redact.rs`) + - `redact_opt_string`: Option 脱敏为存在性表示 + - `mask_email`: 邮箱脱敏(保留首字符) + - `mask_aws_account_id_in_arn`: AWS ARN 中 account id 脱敏 + - `mask_url_userinfo`: URL userinfo 脱敏 + - `mask_user_agent_machine_id`: User-Agent 中 machine_id 脱敏 + +## [v1.0.5] - 2026-02-09 + +### Changed +- **请求日志输出压缩前后 token 估算** (`src/anthropic/converter.rs`, `src/anthropic/handlers.rs`) + - `ConversionResult` 新增 `compression_stats` 字段,将压缩统计从 converter 内部返回给调用方 + - `post_messages` / `post_messages_cc` 在 `convert_request()` 之前估算 input tokens,`Received` 日志追加 `estimated_input_tokens` + - 压缩完成后输出 token 对比日志:`estimated_input_tokens`、`compressed_input_tokens`、`tokens_saved` 及各项压缩明细 + - WebSearch 分支复用已有估算值,消除重复的 `count_all_tokens` 调用 +- **`count_all_tokens` 改为接受借用** (`src/token.rs`) + - 参数从按值传递改为引用(`&str`、`&[Message]`、`&Option>`),消除 handlers 中的深拷贝开销 +- **历史截断统计增加字节数** (`src/anthropic/compressor.rs`) + - `CompressionStats` 新增 `history_bytes_saved` 字段,`total_saved()` 包含历史截断字节数 + - `compress_history_pass` 返回 `(turns_removed, bytes_saved)` 元组,token 估算准确计入历史截断 + +## [v1.0.4] - 2026-02-09 + +### Changed +- **Docker 构建优化**: 引入 `cargo-chef` 实现依赖层缓存,大幅加速重复构建 + - 新增 `planner` 阶段生成依赖 recipe,`builder` 阶段先编译依赖再编译源码 + - Docker 层缓存使得仅源码变更时无需重新编译所有依赖 +- **GitHub Actions Docker 构建缓存**: 启用 GHA 缓存(`cache-from/cache-to: type=gha`) +- **Cargo.toml 优化**: + - `lto` 从 `true`(fat LTO)改为 `"thin"`,加快编译速度同时保持较好的优化效果 + - `tokio` features 从 `full` 精简为实际使用的 5 个 feature(`rt-multi-thread`, `macros`, `net`, `time`, `sync`),减小编译体积 + +## [v1.0.3] - 2026-02-09 + +### Fixed +- **opus4-6 上下文超限错误识别** (`src/anthropic/handlers.rs`) + - 将 `Improperly formed request` 纳入输入过长错误检测,返回 Claude Code 可识别的 `400 Input is too long` 而非 `502 Bad Gateway` +- **Opus 4.6 模型 ID 移除时间日期** (`src/anthropic/handlers.rs`) + - `claude-opus-4-6-20260206` → `claude-opus-4-6` +- **post_messages 借用冲突编译错误** (`src/anthropic/handlers.rs`) + - 将 `override_thinking_from_model_name` 调用移至 `user_id` 提取之前,修复 E0502 借用冲突 + +### Added +- **模型 "thinking" 后缀支持** (`src/anthropic/handlers.rs`, `src/anthropic/converter.rs`) + - 模型名含 `-thinking` 后缀时自动覆写 thinking 配置(opus4.6 用 adaptive + high effort,其他用 enabled) + - 模型列表新增 sonnet/opus4.5/opus4.6/haiku 的 thinking 变体 + +### Docs +- **README.md 补充输入压缩配置文档** + - 新增"输入压缩"章节,说明 5 层压缩管道机制和 10 个可配置参数 + +## [v1.0.2] - 2026-02-09 + +### Fixed +- **opus4-6 上下文超限错误识别** (`src/anthropic/handlers.rs`) + - 将 `Improperly formed request` 纳入输入过长错误检测,返回 Claude Code 可识别的 `400 Input is too long` 而非 `502 Bad Gateway` +- **Opus 4.6 模型 ID 移除时间日期** (`src/anthropic/handlers.rs`) + - `claude-opus-4-6-20260206` → `claude-opus-4-6` +- **post_messages 借用冲突编译错误** (`src/anthropic/handlers.rs`) + - 将 `override_thinking_from_model_name` 调用移至 `user_id` 提取之前,修复 E0502 借用冲突 + +### Added +- **模型 "thinking" 后缀支持** (`src/anthropic/handlers.rs`, `src/anthropic/converter.rs`) + - 模型名含 `-thinking` 后缀时自动覆写 thinking 配置(opus4.6 用 adaptive + high effort,其他用 enabled) + - 模型列表新增 sonnet/opus4.5/opus4.6/haiku 的 thinking 变体 + +### Docs +- **README.md 补充输入压缩配置文档** + - 新增"输入压缩"章节,说明 5 层压缩管道机制和 10 个可配置参数 + +## [v1.0.1] - 2026-02-08 + +### Fixed +- **历史截断字符统计口径修正** (`src/anthropic/compressor.rs`) + - `max_history_chars` 从按字节 `.len()` 改为按字符 `.chars().count()`,与配置语义一致 +- **remove_thinking_blocks 不再全局 trim** (`src/anthropic/compressor.rs`) + - 移除末尾 `.trim()`,避免意外吞掉原始内容的首尾空白 +- **token.json 导入注释/逻辑统一** (`src/admin/service.rs`) + - 更新注释:`builder-id → idc`(与实际映射一致) + - 删除 `auth_method == "builder-id"` 死分支 +- **Admin UI 验活开关 added=0 卡住修复** (`admin-ui/src/components/import-token-json-dialog.tsx`) + - `enableVerify` 开启但无新增凭据时,直接跳转 result 步骤而非卡在 preview +- **工具描述截断 max_description_chars=0 语义修正** (`src/anthropic/converter.rs`) + - `0` 现在表示"不截断",而非截断为空字符串 + +### Added +- **输入压缩管道** (`src/anthropic/compressor.rs`) + - 新增 5 层压缩管道,规避 Kiro upstream 请求体大小限制 + - 空白压缩:连续空行(3+)→2行,行尾空格移除,保留行首缩进 + - thinking 块处理:支持 discard/truncate/keep 三种策略 + - tool_result 智能截断:按行截断保留头尾,行数不足时回退字符级截断 + - tool_use input 截断:递归截断 JSON 值中的大字符串 + - 历史截断:保留系统消息对,按轮数/字符数从前往后成对移除 + - 12 个单元测试覆盖所有压缩层和边界条件 +- **CompressionConfig 配置结构体** (`src/model/config.rs`) + - 新增 `compression` 配置字段,支持通过 JSON 配置文件调整参数 + - 10 个可配置参数:总开关、空白压缩、thinking 策略、各截断阈值、历史限制 + - 工具描述截断阈值从硬编码 10000 改为可配置(默认 4000) +- **合并 upstream 新功能**: 从 upstream/master 拉取并融合大量新特性 + - **负载均衡模式** (`src/model/config.rs`, `src/kiro/token_manager.rs`, `src/admin/`) + - 新增 `loadBalancingMode` 配置项,支持 `priority`(默认)和 `balanced`(Least-Used)两种模式 + - Admin API 新增 `GET/PUT /config/load-balancing` 端点 + - 前端新增负载均衡模式切换开关 + - **凭据统计与持久化** (`src/kiro/token_manager.rs`) + - 新增 `success_count`、`last_used_at` 字段,跟踪每个凭据的调用统计 + - 新增 `save_stats_debounced` 防抖持久化机制,`Drop` 时自动保存 + - 新增 `refreshTokenHash` 字段用于前端重复检测 + - **前端批量操作** (`admin-ui/src/components/dashboard.tsx`) + - 批量导入对话框 (`BatchImportDialog`) + - 批量验活对话框 (`BatchVerifyDialog`) + - 分页控件、批量选择/删除/恢复/验活功能 + - 凭据卡片新增 Checkbox 选择、email 显示、订阅等级、成功次数、剩余用量等信息 + - **配置文件路径管理** (`src/model/config.rs`) + - `Config` 新增 `config_path` 字段和 `load()`/`save()` 方法,支持配置回写 + - **前端依赖**: 新增 `@radix-ui/react-checkbox` 组件 + +### Changed +- `convert_request()` 签名新增 `&CompressionConfig` 参数 (`src/anthropic/converter.rs`) +- `convert_tools()` 描述截断阈值参数化 (`src/anthropic/converter.rs`) +- `AppState` 新增 `compression_config` 字段 (`src/anthropic/middleware.rs`) +- `create_router_with_provider()` 新增 `CompressionConfig` 参数 (`src/anthropic/router.rs`) +- 重构 README.md 配置文档,提升新用户上手体验 + - 明确配置文件默认路径:当前工作目录(或通过 `-c`/`--config` 和 `--credentials` 参数指定) + - 添加 JSON 注释警告:移除所有带 `//` 注释的示例,提供可直接复制的配置 + - 修正字段必填性:仅 `apiKey` 为必填,其他字段均有默认值 + - 新增命令行参数说明表格(`-c`, `--credentials`, `-h`, `-V`) + - 补充遗漏的 `credentialRpm` 字段说明(凭据级 RPM 限流) + - 使用表格形式展示配置字段,标注必填/可选和默认值 +- 优化 debug 日志中请求体的输出长度 (`src/anthropic/handlers.rs`) + - 新增 `truncate_middle()` 函数:截断字符串中间部分,保留头尾各 1200 字符 + - 正确处理 UTF-8 多字节字符边界,不会截断中文 + - 仅在启用 `sensitive-logs` feature 时生效,减少日志噪音 + +### Fixed +- **[P0] API Key 日志泄露修复** (`src/main.rs`) + - info 级别不再打印 API Key 前半段,仅显示末 4 位和长度 + - 完整前缀仅在 `sensitive-logs` feature 的 debug 级别输出 +- **[P2] 占位工具大小写变体重复插入** (`src/anthropic/converter.rs`) + - `collect_history_tool_names` 改为小写去重,避免 `read`/`Read` 等变体重复 + - 占位工具 push 后同步更新 `existing_tool_names` 集合 +- **[P1] 统计与缓存写盘非原子操作** (`src/kiro/token_manager.rs`, `src/admin/service.rs`) + - 统计数据和余额缓存改为临时文件 + 原子重命名,防止写入中断导致文件损坏 +- **[P1] stop_reason 覆盖策略可能丢失信息** (`src/anthropic/stream.rs`) + - `set_stop_reason()` 改为基于优先级覆盖,高优先级原因可覆盖低优先级原因 +- **[P2] snapshot 重复计算 SHA-256** (`src/kiro/token_manager.rs`) + - `CredentialEntry` 新增 `refresh_token_hash` 缓存字段 + - Token 刷新时自动更新哈希,`snapshot()` 优先使用缓存避免重复计算 +- **Clippy 警告修复** (`src/model/config.rs`) + - 修复 `field_reassign_with_default` 警告,改用结构体初始化语法 +- **[P2] Assistant Prefill 静默丢弃** (`src/anthropic/converter.rs`, `src/anthropic/handlers.rs`) + - 末尾 `assistant` 消息(prefill 场景)不再返回 400 错误,改为静默丢弃并回退到最后一条 `user` 消息 + - Claude 4.x 已弃用 assistant prefill,Kiro API 也不支持,转换器在入口处截断消息列表 + - 移除 `InvalidLastMessageRole` 错误变体,`build_history` 接受预处理后的消息切片 +- **[P2] 凭据回写原子性** (`src/kiro/token_manager.rs`) + - `persist_credentials` 改为临时文件 + `rename` 原子替换 + - 新增 `resolve_symlink_target` 辅助函数:优先 `canonicalize`,失败时用 `read_link` 解析 symlink + - 保留原文件权限,防止 umask 导致凭据文件权限放宽 + - Windows 兼容:`rename` 前先删除已存在的目标文件 + - 避免进程崩溃或并发调用导致凭据文件损坏 +- 限制 `max_tokens` 最大值为 32000(Kiro upstream 限制) + - 当用户设置超出限制的值时自动调整为 32000 + - 记录 WARN 级别日志,包含原始值和调整后的值 + - 涉及文件:`src/anthropic/handlers.rs` +- 工具输入 JSON 解析失败时的日志输出改为受 `sensitive-logs` feature 控制 + - 默认仅输出 `buffer_len` 和 `request_body_bytes`(长度信息) + - 启用 `--features sensitive-logs` 时输出完整 `buffer` 和 `request_body` + - 涉及文件:`src/anthropic/handlers.rs` +- 修复 Kiro upstream 请求兼容性问题 + - 空 content(仅 tool_result/image)时使用占位符避免 400 + - 规范化工具 JSON Schema、补全空 description + - 禁用 reqwest 系统代理探测(仅支持显式 `config.proxy_url`) + - 新增离线诊断脚本:`tools/diagnose_improper_request.py` + - 涉及文件:`src/anthropic/converter.rs`、`src/http_client.rs`、`tools/diagnose_improper_request.py`、`.gitignore` +- 优化 400 Bad Request "输入过长" 错误的日志输出 (`src/kiro/provider.rs`) + - 对于 `CONTENT_LENGTH_EXCEEDS_THRESHOLD` / `Input is too long` 错误,不再输出完整请求体(太占空间且无调试价值) + - 改为记录 `request_body_bytes`(字节数)和 `estimated_input_tokens`(估算 token 数) + - 新增 `estimate_tokens()` 函数:基于 CJK/非 CJK 字符比例估算 token 数量 + - CJK 字符(中/日/韩): token 数 = 字符数 / 1.5 + - 其他字符(英文等): token 数 = 字符数 / 3.5 + - 新增 `is_input_too_long()` 和 `is_cjk_char()` 辅助函数 + +- 新增多维度设备指纹系统 (`src/kiro/fingerprint.rs`) + - 每个凭据生成独立的确定性指纹,模拟真实 Kiro IDE 客户端 + - 支持 10+ 维度设备信息:SDK 版本、Kiro 版本、Node.js 版本、操作系统、屏幕分辨率、CPU 核心数、时区等 + - 提供 `user_agent()` 和 `x_amz_user_agent()` 方法构建请求头 + - 参考 CLIProxyAPIPlus 实现,降低被检测风险 + +- 新增精细化速率限制系统 (`src/kiro/rate_limiter.rs`) + - 每日请求限制(默认 500 次/天) + - 请求间隔控制(1-2 秒 + 30% 抖动) + - 指数退避策略(30s → 5min,倍数 1.5) + - 暂停检测(关键词匹配:suspended, banned, quota exceeded 等) + +- 新增独立冷却管理模块 (`src/kiro/cooldown.rs`) + - 分类冷却原因(7 种类型:速率限制、账户暂停、配额耗尽、Token 刷新失败等) + - 差异化冷却时长:短冷却(1-5 分钟)vs 长冷却(1-24 小时) + - 递增冷却机制(连续触发时延长冷却时间) + - 自动清理过期冷却 + +- 新增后台 Token 刷新模块 (`src/kiro/background_refresh.rs`) + - 独立后台任务定期检查即将过期的 Token + - 支持批量并发刷新(信号量控制) + - 可配置检查间隔、批处理大小、并发数 + - 优雅关闭机制 + +- `MultiTokenManager` 新增增强方法 + - `get_fingerprint()`: 获取凭据的设备指纹 + - `is_credential_available()`: 综合检查凭据可用性(未禁用、未冷却、未超速率限制) + - `set_credential_cooldown()` / `clear_credential_cooldown()`: 冷却管理 + - `get_expiring_credential_ids()`: 获取即将过期的凭据列表 + - `start_background_refresh()`: 启动后台 Token 刷新任务 + - `refresh_token_for_credential()`: 带优雅降级的 Token 刷新 + - `record_api_success()` / `record_api_failure()`: 更新速率限制器状态 +- `CredentialEntry` 结构体新增 `fingerprint` 字段,每个凭据独立生成设备指纹 + +- 修复 IDC 凭据 `fetch_profile_arn` 在某些 region 返回 `UnknownOperationException` 的问题 + - 新增 `ListAvailableCustomizations` API 作为 `ListProfiles` 的回退方案 + - 支持多 region 尝试:先尝试用户配置的 region,失败则回退到 `us-east-1` + - 涉及文件:`src/kiro/token_manager.rs` + +- 修复 `start_background_refresh` 后台刷新器生命周期问题(Codex Review P1) + - 问题:`refresher` 作为局部变量在函数返回后被 drop,导致后台任务立即停止 + - 解决:方法现在返回 `Arc`,调用方需保持引用以维持任务运行 + - 涉及文件:`src/kiro/token_manager.rs` + +- 修复 `calculate_backoff` 退避时间可能超过配置上限的问题(Codex Review P2) + - 问题:添加抖动后未再次进行上限约束,可能导致实际等待时间超过 `backoff_max_ms` + - 解决:在添加抖动后再进行 `.min(max)` 约束 + - 涉及文件:`src/kiro/rate_limiter.rs` + +- 改进 `persist_credentials` 并发写入安全性(Codex Review P1) + - 问题:在锁外执行文件写入可能导致并发写入时旧快照覆盖新数据 + - 解决:在持有 entries 锁的情况下完成序列化,确保快照一致性 + - 涉及文件:`src/kiro/token_manager.rs` +- 修复 IDC 凭据返回 403 "The bearer token included in the request is invalid" 的问题 + - 根本原因:`profile_arn` 只从第一个凭据获取并存储在全局 `AppState` 中,当使用 IDC 凭据时,Bearer Token 来自 IDC 凭据,但 `profile_arn` 来自第一个凭据(可能是 Social 类型),导致 Token 和 profile_arn 不匹配 + - 解决方案 1:在 `call_api_with_retry` 中动态注入当前凭据的 `profile_arn`,确保 Token 和 profile_arn 始终匹配 + - 解决方案 2:IDC Token 刷新后自动调用 `ListProfiles` API 获取 `profileArn`(IDC 的 OIDC 刷新不返回此字段) + - 新增 `inject_profile_arn()` 辅助方法,解析请求体 JSON 并覆盖 `profileArn` 字段 + - 新增 `fetch_profile_arn()` 方法,通过 CodeWhisperer ListProfiles API 获取 profileArn + - 涉及文件:`src/kiro/provider.rs`, `src/kiro/token_manager.rs` +- 新增批量导入 token.json 功能 + - 后端:新增 `POST /api/admin/credentials/import-token-json` 端点 + - 支持解析官方 token.json 格式(含 `provider`、`refreshToken`、`clientId`、`clientSecret` 等字段) + - 按 `provider` 字段自动映射 `authMethod`(BuilderId → idc, IdC → idc, Social → social) + - 支持 dry-run 预览模式,返回详细的导入结果(成功/跳过/无效) + - 通过 refreshToken 前缀匹配自动去重,避免重复导入 + - 前端:新增"导入 token.json"对话框组件 + - 支持拖放上传 JSON 文件或直接粘贴 JSON 内容 + - 三步流程:输入 → 预览 → 结果 + - 涉及文件: + - `src/admin/types.rs`(新增 `TokenJsonItem`、`ImportTokenJsonRequest`、`ImportTokenJsonResponse` 等类型) + - `src/admin/service.rs`(新增 `import_token_json()` 方法) + - `src/admin/handlers.rs`(新增 `import_token_json` handler) + - `src/admin/router.rs`(添加路由) + - `src/kiro/token_manager.rs`(新增 `has_refresh_token_prefix()` 方法) + - `admin-ui/src/types/api.ts`(新增导入相关类型) + - `admin-ui/src/api/credentials.ts`(新增 `importTokenJson()` 函数) + - `admin-ui/src/hooks/use-credentials.ts`(新增 `useImportTokenJson()` hook) + - `admin-ui/src/components/import-token-json-dialog.tsx`(新建) + - `admin-ui/src/components/dashboard.tsx`(添加导入按钮) + +- 修复字符串切片在多字节字符中间切割导致 panic 的风险(DoS 漏洞) + - `generate_fingerprint()` 和 `has_refresh_token_prefix()` 使用 `floor_char_boundary()` 安全截断 + - 涉及文件:`src/admin/service.rs`, `src/kiro/token_manager.rs` +- 修复日志截断在多字节字符中间切割导致 panic 的问题 + - `truncate_for_log()` 使用 `floor_char_boundary()` 安全截断 UTF-8 字符串 + - 删除 `stream.rs` 中冗余的 `find_char_boundary()` 函数,直接使用标准库方法 + - 涉及文件:`src/kiro/provider.rs`, `src/anthropic/stream.rs` +- 移除历史消息中孤立的 tool_use(无对应 tool_result) + - Kiro API 要求 tool_use 必须有配对的 tool_result,否则返回 400 Bad Request + - 新增 `remove_orphaned_tool_uses()` 函数清理孤立的 tool_use + - 涉及文件:`src/anthropic/converter.rs` +- 修复 `/cc/v1/messages` 缓冲流 ping 定时器首次立即触发的问题 + - 将 `interval()` 改为 `interval_at(Instant::now() + ping_period, ping_period)` + - 现在首个 ping 会在 25 秒后触发,与 `/v1/messages` 行为一致 + - 涉及文件:`src/anthropic/handlers.rs` +- 修复 Clippy `collapsible_if` 警告 + - 使用 let-chains 语法合并嵌套 if 语句 + - 涉及文件:`src/anthropic/stream.rs` + +- 增强 400 Bad Request 错误日志,记录完整请求信息 + - 移除请求体截断限制,记录完整的 `request_body` + - 新增 `request_url` 和 `request_headers` 字段 + - 新增 `format_headers_for_log()` 辅助函数,对 Authorization 头进行脱敏处理 + - 删除不再使用的 `truncate_for_log()` 函数(YAGNI 原则) + - 涉及文件:`src/kiro/provider.rs` +- 改进凭据选择算法:同优先级内实现负载均衡 + - 第一优先级:使用次数最少 + - 第二优先级:余额最多(使用次数相同时) + - 第三优先级:轮询选择(使用次数和余额完全相同时,避免总选第一个) + - 新增 `selection_rr` 原子计数器用于轮询抖动 + - 新增 `select_best_candidate_id()` 方法实现三级排序逻辑 + - 涉及文件:`src/kiro/token_manager.rs` + +- 修复测试代码使用 `serde_json::json!` 构造 Tool 对象导致的类型不匹配问题 + - 改用 `Tool` 结构体直接构造,确保类型安全 + - 涉及文件:`src/anthropic/websearch.rs` +- 修复 `select_best_candidate_id()` 中 NaN 余额处理问题 + - 在评分阶段将 NaN/Infinity 余额归一化为 0.0 + - 避免 NaN 被 `total_cmp` 视为最大值导致错误的凭据选择 + - 避免 NaN 导致 `scored` 被完全过滤后除零 panic + - 涉及文件:`src/kiro/token_manager.rs` +- 新增 `system` 字段格式兼容性支持(`src/anthropic/types.rs`) + - 支持字符串格式:`"system": "You are a helpful assistant"`(new-api 等网关添加的系统提示词) + - 支持数组格式:`"system": [{"type": "text", "text": "..."}]`(Claude Code 原生格式) + - 自动将字符串格式转换为单元素数组,保持内部处理一致性 + - 新增 6 个单元测试验证格式兼容性 +- 新增请求体大小限制:50MB(`DefaultBodyLimit::max(50 * 1024 * 1024)`) + - 涉及文件:`src/anthropic/router.rs` +- 调整全局禁用恢复时间:`GLOBAL_DISABLE_RECOVERY_MINUTES` 从 10 分钟降至 5 分钟 + - 加快模型暂时不可用后的自动恢复速度 +- 调整总重试次数硬上限:`MAX_TOTAL_RETRIES` 从 5 降至 3 + - 进一步减少无效重试开销,加快故障转移速度 +- 余额初始化改为顺序查询,每次间隔 0.5 秒避免触发限流 + - 从并发查询改为顺序查询(`initialize_balances()`) + - 移除 30 秒整体超时机制 + - 涉及文件:`src/kiro/token_manager.rs` + +- 修复 assistant 消息仅包含 tool_use 时 content 为空导致 Kiro API 报错的问题 + - 当 text_content 为空且存在 tool_uses 时,使用 "OK" 作为占位符 + - 涉及文件:`src/anthropic/converter.rs` +- 修复 `MODEL_TEMPORARILY_UNAVAILABLE` 错误检测逻辑未实际调用的问题 + - 在 `call_mcp()` 和 `call_api()` 中添加错误检测和熔断触发逻辑 + - 移除 `report_model_unavailable()` 和 `disable_all_credentials()` 的 `#[allow(dead_code)]` 标记 + - 现在当检测到该错误时会正确触发全局熔断机制 + +- 新增 WebSearch 工具支持(`src/anthropic/websearch.rs`) + - 实现 Anthropic WebSearch 请求到 Kiro MCP 的转换 + - 支持 SSE 流式响应,生成完整的搜索结果事件序列 + - 自动检测纯 WebSearch 请求(tools 仅包含 web_search)并路由到专用处理器 +- 新增 MCP API 调用支持(`src/kiro/provider.rs`) + - 新增 `call_mcp()` 方法,支持 WebSearch 等工具调用 + - 新增 `mcp_url()` 和 `build_mcp_headers()` 方法 + - 完整的重试和故障转移逻辑 +- 新增凭据级 `region` 字段,用于 OIDC token 刷新时指定 endpoint 区域 + - 未配置时回退到 config.json 的全局 region + - API 调用仍使用 config.json 的 region +- 新增凭据级 `machineId` 字段,支持每个凭据使用独立的机器码 + - 支持 64 字符十六进制和 UUID 格式(自动标准化) + - 未配置时回退到 config.json 的 machineId,都未配置时由 refreshToken 派生 + - 启动时自动补全并持久化到配置文件 +- 新增 GitHub Actions Docker 构建工作流(`.github/workflows/docker-build.yaml`) + - 支持 linux/amd64 和 linux/arm64 双架构 + - 推送到 GitHub Container Registry +- 版本号升级至 2026.1.5 +- TLS 库从 native-tls 切换至 rustls(reqwest 依赖调整) +- `authMethod` 自动推断:未指定时根据是否有 clientId/clientSecret 自动判断为 idc 或 social +- 移除 web_search/websearch 工具过滤(`is_unsupported_tool` 现在返回 false) + +- 修复 machineId 格式兼容性问题,支持 UUID 格式自动转换为 64 字符十六进制 +### Removed +- 移除 `current_id` 概念(后端和前端) + - 后端:移除 `MultiTokenManager.current_id` 字段和相关方法(`switch_to_next`、`select_highest_priority`、`select_by_balance`、`credentials`) + - 后端:移除 `ManagerSnapshot.current_id` 字段 + - 后端:移除 `CredentialStatusItem.is_current` 字段 + - 前端:移除 `CredentialsStatusResponse.currentId` 和 `CredentialStatusItem.isCurrent` + - 原因:多用户并发访问时,"当前凭据"概念无意义,凭据选择由 `acquire_context_for_user()` 动态决定 + +- 新增启动时余额初始化功能 + - `initialize_balances()`: 启动时并发查询所有凭据余额并更新缓存 + - 整体超时 30 秒,避免阻塞启动流程 + - 初始化失败或超时时输出警告日志 +- 改进凭据选择算法:从单一"使用次数最少"改为两级排序 + - 第一优先级:使用次数最少 + - 第二优先级:余额最多(使用次数相同时) + - 未初始化余额的凭据会被降级处理,避免被优先选中 +- 移除前端"当前活跃"凭据展示 + - 前端:移除凭据卡片的"当前"高亮和 Badge + - 前端:移除 Dashboard 中的"当前活跃"统计卡片 + - 统计卡片布局从 3 列调整为 2 列 + +- 新增 `sensitive-logs` feature flag,显式启用才允许打印潜在敏感信息(仅用于排障) + - 默认关闭:Kiro 请求体只输出长度,凭证只输出摘要信息 + - 启用方式:`cargo build --features sensitive-logs` + +- 修复 SSE 流 ping 保活首次立即触发的问题 + - 使用 `interval_at(Instant::now() + ping_period, ping_period)` 延迟首次触发 + - 避免连接建立后立即发送无意义的 ping 事件 +- 改进服务启动错误处理 + - 绑定监听地址失败时输出错误日志并退出(exit code 1) + - HTTP 服务异常退出时输出错误日志并退出(exit code 1) + +- 修复合并 upstream 后 `CredentialEntry` 结构体字段缺失导致的编译错误 + - 添加 `disable_reason: Option` 字段(公共 API 展示用) + - 添加 `auto_heal_reason: Option` 字段(内部自愈逻辑用) +- 修复禁用原因字段不同步问题 + - `report_failure()`: 禁用时同步设置两个字段 + - `set_disabled()`: 启用/禁用时同步设置/清除两个字段 + - `reset_and_enable()`: 重置时同步清除两个字段 + - 自愈循环:重新启用凭据时同步清除 `disable_reason` + - `mark_insufficient_balance()`: 清除 `auto_heal_reason` 防止被自愈循环错误恢复 +- 重命名内部字段以提高可读性 + - `DisabledReason` → `AutoHealReason`(自愈原因枚举) + - `disabled_reason` → `auto_heal_reason`(自愈原因字段) +- 日志中的 `user_id` 现在会进行掩码处理,保护用户隐私 + - 长度 > 25:保留前13后8字符(如 `user_f516339a***897ac7`) + - 长度 13-25:保留前4后4字符 + - 长度 ≤ 12:完全掩码为 `***` + +- 新增缓存余额查询 API(`GET /credentials/balances/cached`) + - 后端:`CachedBalanceInfo` 结构体、`get_all_cached_balances()` 方法 + - 前端:凭据卡片直接显示缓存余额和更新时间 + - 30 秒自动轮询更新,缓存超过 1 分钟时点击强制刷新 +- 新增 Bonus 用量包支持(`src/kiro/model/usage_limits.rs`) + - 新增 `Bonus` 结构体,支持 GIFT 类型的额外用量包 + - 新增 `Bonus::is_active()` 方法,按状态/过期时间判断是否激活 + - `usage_limit()` 和 `current_usage()` 现在会合并基础额度、免费试用额度和所有激活的 bonuses +- 新增 Kiro Web Portal API 模块(`src/kiro/web_portal.rs`) + - 支持 CBOR 协议与 app.kiro.dev 通信 + - 实现 `get_user_info()` 和 `get_user_usage_and_limits()` API + - 新增 `aggregate_account_info()` 聚合账号信息(套餐/用量/邮箱等) +- Admin UI 前端增强 + - 新增数字格式化工具(`admin-ui/src/lib/format.ts`):K/M/B 显示、Token 对格式化、过期时间格式化 + - 新增统计相关 API 和 Hooks:`getCredentialStats`, `resetCredentialStats`, `resetAllStats` + - 新增账号信息 API:`getCredentialAccountInfo`, `useCredentialAccountInfo` + - 扩展 `CredentialStatusItem` 添加统计字段(调用次数、Token 用量、最后调用时间等) + - 新增完整的账号信息类型定义(`AccountAggregateInfo`, `CreditsUsageSummary` 等) +- 新增 `serde_cbor` 依赖用于 CBOR 编解码 + +- 修复手动查询余额后列表页面不显示缓存余额的问题 + - `get_balance()` 成功后调用 `update_balance_cache()` 更新缓存 + - 现在点击"查看余额"后,列表页面会正确显示缓存的余额值 +- 修复关闭余额弹窗后卡片不更新缓存余额的问题 + - 弹窗关闭时调用 `queryClient.invalidateQueries({ queryKey: ['cached-balances'] })` + - 确保卡片和弹窗使用的两个独立数据源保持同步 +- 增强 Token 刷新日志,添加凭证 ID 追踪 + - 新增 `refresh_token_with_id()` 函数支持传入凭证 ID + - 日志现在包含 `credential_id` 字段,便于多凭据环境下的问题排查 +- 调整重试策略:单凭据最大重试次数 3→2,单请求最大重试次数 9→5 + - `MAX_RETRIES_PER_CREDENTIAL`: 3 → 2 + - `MAX_TOTAL_RETRIES`: 9 → 5 + - `MAX_FAILURES_PER_CREDENTIAL`: 3 → 2 + - 减少无效凭据的重试开销,加快故障转移速度 + +- 新增用户亲和性绑定功能:连续对话优先使用同一凭据(基于 `metadata.user_id`) + - 新增 `src/kiro/affinity.rs` 模块,实现 `UserAffinityManager` + - 新增 `acquire_context_for_user()` 方法支持亲和性查询 + - 亲和性绑定 TTL 为 30 分钟 +- 新增余额感知故障转移:凭据失效时自动切换到余额最高的可用凭据 +- 新增动态余额缓存 TTL 策略: + - 高频渠道(10分钟内 ≥20 次调用):10 分钟刷新 + - 低频渠道:30 分钟刷新 + - 低余额渠道(余额 < 1.0):24 小时刷新 +- 新增 `record_usage()` 方法自动记录凭据使用频率 +- 新增负载均衡:无亲和性绑定时优先分配到使用频率最低的凭据 +- 新增 `DisableReason` 枚举,区分凭据禁用原因(失败次数、余额不足、模型不可用、手动禁用) +- 成功请求时自动重置 `MODEL_TEMPORARILY_UNAVAILABLE` 计数器,避免跨时间累计触发 +- 新增 `MODEL_TEMPORARILY_UNAVAILABLE` 错误检测和全局禁用机制 + - 当该 500 错误发生 2 次时,自动禁用所有凭据 + - 5 分钟后自动恢复(余额不足的凭据除外) +- `CredentialEntrySnapshot` 新增 `disable_reason` 字段,支持查询禁用原因 +- 新增自动余额刷新:成功请求后自动在后台刷新余额缓存(基于动态 TTL 策略) + - 新增 `spawn_balance_refresh()` 方法,使用 `tokio::spawn` 异步刷新 + - 新增 `should_refresh_balance()` 方法,根据 TTL 判断是否需要刷新 \ No newline at end of file diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..27b9b44 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,100 @@ +# CLAUDE.md + +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. + +## 项目概述 + +kiro-rs 是一个用 Rust 编写的 Anthropic Claude API 兼容代理服务,将 Anthropic API 请求转换为 Kiro API 请求。支持多凭据管理、自动故障转移、流式响应和 Web 管理界面。 + +**技术栈**: Rust (Axum 0.8 + Tokio) + React 18 + TypeScript + Tailwind CSS + +## 常用命令 + +```bash +# 构建(必须先构建前端) +cd admin-ui && pnpm install && pnpm build +cargo build --release + +# 开发运行 +cargo run -- -c config.json --credentials credentials.json + +# 测试 +cargo test +cargo test # 运行单个测试 + +# 代码检查 +cargo fmt # 格式化 +cargo clippy # lint + +# 启用敏感日志构建(排障用,输出 token 用量等诊断信息) +cargo run --features sensitive-logs -- -c config.json --credentials credentials.json + +# 前端开发 +cd admin-ui +pnpm install +pnpm dev # 开发服务器 +pnpm build # 生产构建 +``` + +## 请求处理流程 + +``` +POST /v1/messages (Anthropic 格式) + → auth_middleware: 验证 x-api-key / Bearer token(subtle 常量时间比较) + → post_messages handler: + 1. 判断 WebSearch 触发条件,决定本地处理或剔除后转发 + 2. converter::convert_request() 转换为 Kiro 请求格式 + 3. provider.call_api() 发送请求(含重试和故障转移) + 4. stream.rs 解析 AWS Event Stream → 转换为 Anthropic SSE 格式返回 +``` + +## 核心设计模式 + +1. **Provider Pattern** - `kiro/provider.rs`: 统一的 API 提供者接口,处理请求转发和重试。支持凭据级代理(每个凭据可配独立 HTTP/SOCKS5 代理,缓存对应 HTTP Client 避免重复创建) +2. **Multi-Token Manager** - `kiro/token_manager.rs`: 多凭据管理,按优先级故障转移,后台异步刷新 Token(支持 Social 和 IdC 两种认证方式)。余额缓存动态 TTL:高频用户 10 分钟、低频用户 30 分钟、低余额用户 24 小时,过期时异步刷新不阻塞请求 +3. **Protocol Converter** - `anthropic/converter.rs`: Anthropic ↔ Kiro 双向协议转换,包括模型映射(sonnet/opus/haiku → Kiro 模型 ID)、JSON Schema 规范化(修复 MCP 工具的 `required: null` / `properties: null`)、工具占位符生成、图片格式转换 +4. **Event Stream Parser** - `kiro/parser/`: AWS Event Stream 二进制协议解析(header + payload + CRC32C 校验) +5. **Buffered Stream** - `anthropic/stream.rs`: 两种流模式 — `StreamContext`(直接转发)和 `BufferedStreamContext`(缓冲所有事件,等 `contextUsageEvent` 到达后修正 input_tokens 再一次性发送) + +## 共享状态 + +```rust +AppState { + api_key: String, // Anthropic API 认证密钥 + kiro_provider: Option>, // 核心 API 提供者(Arc 线程安全共享) + profile_arn: Option, // AWS Profile ARN + compression_config: CompressionConfig, // 输入压缩配置 +} +``` + +通过 Axum `State` extractor 注入到所有 handler 中。 + +## 凭据故障转移与冷却 + +- 凭据按 `priority` 字段排序,优先使用高优先级凭据 +- 请求失败时 `report_failure()` 触发故障转移到下一个可用凭据 +- 冷却分类管理:`FailureLimit` / `InsufficientBalance` / `ModelUnavailable` / `QuotaExceeded` +- `MODEL_TEMPORARILY_UNAVAILABLE` 触发全局熔断,禁用所有凭据 + +## API 端点 + +**代理端点**: +- `GET /v1/models` - 获取可用模型列表 +- `POST /v1/messages` - 创建消息(Anthropic 格式) +- `POST /v1/messages/count_tokens` - Token 计数 +- `/cc/v1/*` - Claude Code 兼容端点(同上,路径别名) + +**Admin API** (需配置 `adminApiKey`): +- 凭据 CRUD、状态监控、余额查询 + +## 重要注意事项 + +1. **构建顺序**: 必须先构建前端 `admin-ui`,再编译 Rust 后端(静态文件通过 `rust-embed` 嵌入,derive 宏为 `#[derive(Embed)]`) +2. **凭据格式**: 支持单凭据(向后兼容)和多凭据(数组格式,支持 priority 字段) +3. **重试策略**: 单凭据最多重试 2 次,单请求最多重试 3 次 +4. **WebSearch 工具**: 仅当请求明确触发 WebSearch(`tool_choice` 强制 / 仅提供 `web_search` 单工具 / 消息前缀匹配)时走本地 WebSearch;否则从 `tools` 中剔除 `web_search` 后转发上游(避免误路由) +5. **安全**: 使用 `subtle` 库进行常量时间比较防止时序攻击;Admin API Key 空字符串视为未配置 +6. **Prefill 处理**: Claude 4.x 已弃用 assistant prefill,末尾 assistant 消息被静默丢弃 +7. **sensitive-logs 特性**: 编译时 feature flag,启用后输出 token 用量诊断日志和请求体大小(默认关闭,仅用于排障) +8. **网络错误分类**: 连接关闭/重置、发送失败等网络错误被归类为瞬态上游错误,返回 502(不记录请求体) +9. **Rust edition**: 项目使用 Rust 2024 edition diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..a841afc --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,2587 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "adler2" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" + +[[package]] +name = "aho-corasick" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301" +dependencies = [ + "memchr", +] + +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + +[[package]] +name = "anstream" +version = "0.6.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43d5b281e737544384e969a5ccad3f1cdd24b48086a0fc1b2a5262a26b8f4f4a" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "is_terminal_polyfill", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5192cca8006f1fd4f7237516f40fa183bb07f8fbdfedaa0036de5ea9b0b45e78" + +[[package]] +name = "anstyle-parse" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e7644824f0aa2c7b9384579234ef10eb7efb6a0deb83f9630a49594dd9c15c2" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc" +dependencies = [ + "windows-sys 0.61.2", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d" +dependencies = [ + "anstyle", + "once_cell_polyfill", + "windows-sys 0.61.2", +] + +[[package]] +name = "anyhow" +version = "1.0.101" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f0e0fee31ef5ed1ba1316088939cea399010ed7731dba877ed44aeb407a75ea" + +[[package]] +name = "atomic" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a89cbf775b137e9b968e67227ef7f775587cde3fd31b0d8599dbd0f598a48340" +dependencies = [ + "bytemuck", +] + +[[package]] +name = "atomic-waker" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" + +[[package]] +name = "autocfg" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" + +[[package]] +name = "axum" +version = "0.8.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b52af3cb4058c895d37317bb27508dccc8e5f2d39454016b297bf4a400597b8" +dependencies = [ + "axum-core", + "bytes", + "form_urlencoded", + "futures-util", + "http", + "http-body", + "http-body-util", + "hyper", + "hyper-util", + "itoa", + "matchit", + "memchr", + "mime", + "percent-encoding", + "pin-project-lite", + "serde_core", + "serde_json", + "serde_path_to_error", + "serde_urlencoded", + "sync_wrapper", + "tokio", + "tower", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "axum-core" +version = "0.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08c78f31d7b1291f7ee735c1c6780ccde7785daae9a9206026862dab7d8792d1" +dependencies = [ + "bytes", + "futures-core", + "http", + "http-body", + "http-body-util", + "mime", + "pin-project-lite", + "sync_wrapper", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "base64" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" + +[[package]] +name = "bitflags" +version = "2.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "843867be96c8daad0d758b57df9392b6d8d271134fce549de6ce169ff98a92af" + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "bumpalo" +version = "3.19.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5dd9dc738b7a8311c7ade152424974d8115f2cdad61e8dab8dac9f2362298510" + +[[package]] +name = "bytemuck" +version = "1.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8efb64bd706a16a1bdde310ae86b351e4d21550d98d056f22f8a7f7a2183fec" + +[[package]] +name = "byteorder-lite" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f1fe948ff07f4bd06c30984e69f5b4899c516a3ef74f34df92a2df2ab535495" + +[[package]] +name = "bytes" +version = "1.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33" + +[[package]] +name = "cc" +version = "1.2.56" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aebf35691d1bfb0ac386a69bac2fde4dd276fb618cf8bf4f5318fe285e821bb2" +dependencies = [ + "find-msvc-tools", + "shlex", +] + +[[package]] +name = "cfg-if" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" + +[[package]] +name = "cfg_aliases" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" + +[[package]] +name = "chrono" +version = "0.4.43" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fac4744fb15ae8337dc853fee7fb3f4e48c0fbaa23d0afe49c447b4fab126118" +dependencies = [ + "iana-time-zone", + "js-sys", + "num-traits", + "serde", + "wasm-bindgen", + "windows-link", +] + +[[package]] +name = "clap" +version = "4.5.58" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63be97961acde393029492ce0be7a1af7e323e6bae9511ebfac33751be5e6806" +dependencies = [ + "clap_builder", + "clap_derive", +] + +[[package]] +name = "clap_builder" +version = "4.5.58" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f13174bda5dfd69d7e947827e5af4b0f2f94a4a3ee92912fba07a66150f21e2" +dependencies = [ + "anstream", + "anstyle", + "clap_lex", + "strsim", +] + +[[package]] +name = "clap_derive" +version = "4.5.55" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a92793da1a46a5f2a02a6f4c46c6496b28c43638adea8306fcb0caa1634f24e5" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "clap_lex" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a822ea5bc7590f9d40f1ba12c0dc3c2760f3482c6984db1573ad11031420831" + +[[package]] +name = "color_quant" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b" + +[[package]] +name = "colorchoice" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75" + +[[package]] +name = "core-foundation-sys" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" + +[[package]] +name = "cpufeatures" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" +dependencies = [ + "libc", +] + +[[package]] +name = "crc" +version = "3.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5eb8a2a1cd12ab0d987a5d5e825195d372001a4094a0376319d5a0ad71c1ba0d" +dependencies = [ + "crc-catalog", +] + +[[package]] +name = "crc-catalog" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19d374276b40fb8bbdee95aef7c7fa6b5316ec764510eb64b8dd0e2ed0d7e7f5" + +[[package]] +name = "crc32fast" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "crypto-common" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "crypto-common", +] + +[[package]] +name = "displaydoc" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "equivalent" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" + +[[package]] +name = "fastrand" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" + +[[package]] +name = "fdeflate" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e6853b52649d4ac5c0bd02320cddc5ba956bdb407c4b75a2c6b75bf51500f8c" +dependencies = [ + "simd-adler32", +] + +[[package]] +name = "find-msvc-tools" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" + +[[package]] +name = "flate2" +version = "1.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "843fba2746e448b37e26a819579957415c8cef339bf08564fe8b7ddbd959573c" +dependencies = [ + "crc32fast", + "miniz_oxide", +] + +[[package]] +name = "foldhash" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" + +[[package]] +name = "form_urlencoded" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb4cb245038516f5f85277875cdaa4f7d2c9a0fa0468de06ed190163b1581fcf" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "futures" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" + +[[package]] +name = "futures-executor" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-io" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" + +[[package]] +name = "futures-macro" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "futures-sink" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" + +[[package]] +name = "futures-task" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" + +[[package]] +name = "futures-util" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "pin-utils", + "slab", +] + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "getrandom" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0" +dependencies = [ + "cfg-if", + "js-sys", + "libc", + "wasi", + "wasm-bindgen", +] + +[[package]] +name = "getrandom" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" +dependencies = [ + "cfg-if", + "js-sys", + "libc", + "r-efi", + "wasip2", + "wasm-bindgen", +] + +[[package]] +name = "getrandom" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "139ef39800118c7683f2fd3c98c1b23c09ae076556b435f8e9064ae108aaeeec" +dependencies = [ + "cfg-if", + "libc", + "r-efi", + "wasip2", + "wasip3", +] + +[[package]] +name = "gif" +version = "0.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f5df2ba84018d80c213569363bdcd0c64e6933c67fe4c1d60ecf822971a3c35e" +dependencies = [ + "color_quant", + "weezl", +] + +[[package]] +name = "half" +version = "1.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b43ede17f21864e81be2fa654110bf1e793774238d86ef8555c37e6519c0403" + +[[package]] +name = "hashbrown" +version = "0.15.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" +dependencies = [ + "foldhash", +] + +[[package]] +name = "hashbrown" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + +[[package]] +name = "http" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3ba2a386d7f85a81f119ad7498ebe444d2e22c2af0b86b069416ace48b3311a" +dependencies = [ + "bytes", + "itoa", +] + +[[package]] +name = "http-body" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" +dependencies = [ + "bytes", + "http", +] + +[[package]] +name = "http-body-util" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a" +dependencies = [ + "bytes", + "futures-core", + "http", + "http-body", + "pin-project-lite", +] + +[[package]] +name = "httparse" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" + +[[package]] +name = "httpdate" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" + +[[package]] +name = "hyper" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ab2d4f250c3d7b1c9fcdff1cece94ea4e2dfbec68614f7b87cb205f24ca9d11" +dependencies = [ + "atomic-waker", + "bytes", + "futures-channel", + "futures-core", + "http", + "http-body", + "httparse", + "httpdate", + "itoa", + "pin-project-lite", + "pin-utils", + "smallvec", + "tokio", + "want", +] + +[[package]] +name = "hyper-rustls" +version = "0.27.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3c93eb611681b207e1fe55d5a71ecf91572ec8a6705cdb6857f7d8d5242cf58" +dependencies = [ + "http", + "hyper", + "hyper-util", + "rustls", + "rustls-pki-types", + "tokio", + "tokio-rustls", + "tower-service", + "webpki-roots", +] + +[[package]] +name = "hyper-util" +version = "0.1.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96547c2556ec9d12fb1578c4eaf448b04993e7fb79cbaad930a656880a6bdfa0" +dependencies = [ + "base64", + "bytes", + "futures-channel", + "futures-util", + "http", + "http-body", + "hyper", + "ipnet", + "libc", + "percent-encoding", + "pin-project-lite", + "socket2", + "tokio", + "tower-service", + "tracing", +] + +[[package]] +name = "iana-time-zone" +version = "0.1.65" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e31bc9ad994ba00e440a8aa5c9ef0ec67d5cb5e5cb0cc7f8b744a35b389cc470" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "log", + "wasm-bindgen", + "windows-core", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", +] + +[[package]] +name = "icu_collections" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c6b649701667bbe825c3b7e6388cb521c23d88644678e83c0c4d0a621a34b43" +dependencies = [ + "displaydoc", + "potential_utf", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_locale_core" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edba7861004dd3714265b4db54a3c390e880ab658fec5f7db895fae2046b5bb6" +dependencies = [ + "displaydoc", + "litemap", + "tinystr", + "writeable", + "zerovec", +] + +[[package]] +name = "icu_normalizer" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f6c8828b67bf8908d82127b2054ea1b4427ff0230ee9141c54251934ab1b599" +dependencies = [ + "icu_collections", + "icu_normalizer_data", + "icu_properties", + "icu_provider", + "smallvec", + "zerovec", +] + +[[package]] +name = "icu_normalizer_data" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7aedcccd01fc5fe81e6b489c15b247b8b0690feb23304303a9e560f37efc560a" + +[[package]] +name = "icu_properties" +version = "2.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "020bfc02fe870ec3a66d93e677ccca0562506e5872c650f893269e08615d74ec" +dependencies = [ + "icu_collections", + "icu_locale_core", + "icu_properties_data", + "icu_provider", + "zerotrie", + "zerovec", +] + +[[package]] +name = "icu_properties_data" +version = "2.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "616c294cf8d725c6afcd8f55abc17c56464ef6211f9ed59cccffe534129c77af" + +[[package]] +name = "icu_provider" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85962cf0ce02e1e0a629cc34e7ca3e373ce20dda4c4d7294bbd0bf1fdb59e614" +dependencies = [ + "displaydoc", + "icu_locale_core", + "writeable", + "yoke", + "zerofrom", + "zerotrie", + "zerovec", +] + +[[package]] +name = "id-arena" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d3067d79b975e8844ca9eb072e16b31c3c1c36928edf9c6789548c524d0d954" + +[[package]] +name = "idna" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b0875f23caa03898994f6ddc501886a45c7d3d62d04d2d90788d47be1b1e4de" +dependencies = [ + "idna_adapter", + "smallvec", + "utf8_iter", +] + +[[package]] +name = "idna_adapter" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3acae9609540aa318d1bc588455225fb2085b9ed0c4f6bd0d9d5bcd86f1a0344" +dependencies = [ + "icu_normalizer", + "icu_properties", +] + +[[package]] +name = "image" +version = "0.25.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6506c6c10786659413faa717ceebcb8f70731c0a60cbae39795fdf114519c1a" +dependencies = [ + "bytemuck", + "byteorder-lite", + "color_quant", + "gif", + "image-webp", + "moxcms", + "num-traits", + "png", + "zune-core", + "zune-jpeg", +] + +[[package]] +name = "image-webp" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "525e9ff3e1a4be2fbea1fdf0e98686a6d98b4d8f937e1bf7402245af1909e8c3" +dependencies = [ + "byteorder-lite", + "quick-error", +] + +[[package]] +name = "indexmap" +version = "2.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7714e70437a7dc3ac8eb7e6f8df75fd8eb422675fc7678aff7364301092b1017" +dependencies = [ + "equivalent", + "hashbrown 0.16.1", + "serde", + "serde_core", +] + +[[package]] +name = "ipnet" +version = "2.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "469fb0b9cefa57e3ef31275ee7cacb78f2fdca44e4765491884a2b119d4eb130" + +[[package]] +name = "iri-string" +version = "0.7.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c91338f0783edbd6195decb37bae672fd3b165faffb89bf7b9e6942f8b1a731a" +dependencies = [ + "memchr", + "serde", +] + +[[package]] +name = "is_terminal_polyfill" +version = "1.70.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695" + +[[package]] +name = "itoa" +version = "1.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92ecc6618181def0457392ccd0ee51198e065e016d1d527a7ac1b6dc7c1f09d2" + +[[package]] +name = "js-sys" +version = "0.3.85" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c942ebf8e95485ca0d52d97da7c5a2c387d0e7f0ba4c35e93bfcaee045955b3" +dependencies = [ + "once_cell", + "wasm-bindgen", +] + +[[package]] +name = "kiro-rs" +version = "1.1.5" +dependencies = [ + "anyhow", + "axum", + "base64", + "bytes", + "chrono", + "clap", + "crc", + "fastrand", + "futures", + "hex", + "http", + "image", + "mime_guess", + "parking_lot", + "reqwest", + "rust-embed", + "serde", + "serde_cbor", + "serde_json", + "sha2", + "subtle", + "tokio", + "tower-http", + "tracing", + "tracing-subscriber", + "urlencoding", + "uuid", +] + +[[package]] +name = "lazy_static" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" + +[[package]] +name = "leb128fmt" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2" + +[[package]] +name = "libc" +version = "0.2.182" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6800badb6cb2082ffd7b6a67e6125bb39f18782f793520caee8cb8846be06112" + +[[package]] +name = "litemap" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6373607a59f0be73a39b6fe456b8192fcc3585f602af20751600e974dd455e77" + +[[package]] +name = "lock_api" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965" +dependencies = [ + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" + +[[package]] +name = "lru-slab" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "112b39cec0b298b6c1999fee3e31427f74f676e4cb9879ed1a121b43661a4154" + +[[package]] +name = "matchers" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1525a2a28c7f4fa0fc98bb91ae755d1e2d1505079e05539e35bc876b5d65ae9" +dependencies = [ + "regex-automata", +] + +[[package]] +name = "matchit" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47e1ffaa40ddd1f3ed91f717a33c8c0ee23fff369e3aa8772b9605cc1d22f4c3" + +[[package]] +name = "memchr" +version = "2.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79" + +[[package]] +name = "mime" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" + +[[package]] +name = "mime_guess" +version = "2.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7c44f8e672c00fe5308fa235f821cb4198414e1c77935c1ab6948d3fd78550e" +dependencies = [ + "mime", + "unicase", +] + +[[package]] +name = "miniz_oxide" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" +dependencies = [ + "adler2", + "simd-adler32", +] + +[[package]] +name = "mio" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a69bcab0ad47271a0234d9422b131806bf3968021e5dc9328caf2d4cd58557fc" +dependencies = [ + "libc", + "wasi", + "windows-sys 0.61.2", +] + +[[package]] +name = "moxcms" +version = "0.7.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac9557c559cd6fc9867e122e20d2cbefc9ca29d80d027a8e39310920ed2f0a97" +dependencies = [ + "num-traits", + "pxfm", +] + +[[package]] +name = "nu-ansi-term" +version = "0.50.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5" +dependencies = [ + "windows-sys 0.61.2", +] + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + +[[package]] +name = "once_cell" +version = "1.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" + +[[package]] +name = "once_cell_polyfill" +version = "1.70.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe" + +[[package]] +name = "parking_lot" +version = "0.12.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-link", +] + +[[package]] +name = "percent-encoding" +version = "2.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" + +[[package]] +name = "pin-project-lite" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "png" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97baced388464909d42d89643fe4361939af9b7ce7a31ee32a168f832a70f2a0" +dependencies = [ + "bitflags", + "crc32fast", + "fdeflate", + "flate2", + "miniz_oxide", +] + +[[package]] +name = "potential_utf" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b73949432f5e2a09657003c25bca5e19a0e9c84f8058ca374f49e0ebe605af77" +dependencies = [ + "zerovec", +] + +[[package]] +name = "ppv-lite86" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" +dependencies = [ + "zerocopy", +] + +[[package]] +name = "prettyplease" +version = "0.2.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" +dependencies = [ + "proc-macro2", + "syn", +] + +[[package]] +name = "proc-macro2" +version = "1.0.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "pxfm" +version = "0.1.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7186d3822593aa4393561d186d1393b3923e9d6163d3fbfd6e825e3e6cf3e6a8" +dependencies = [ + "num-traits", +] + +[[package]] +name = "quick-error" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a993555f31e5a609f617c12db6250dedcac1b0a85076912c436e6fc9b2c8e6a3" + +[[package]] +name = "quinn" +version = "0.11.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e20a958963c291dc322d98411f541009df2ced7b5a4f2bd52337638cfccf20" +dependencies = [ + "bytes", + "cfg_aliases", + "pin-project-lite", + "quinn-proto", + "quinn-udp", + "rustc-hash", + "rustls", + "socket2", + "thiserror", + "tokio", + "tracing", + "web-time", +] + +[[package]] +name = "quinn-proto" +version = "0.11.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1906b49b0c3bc04b5fe5d86a77925ae6524a19b816ae38ce1e426255f1d8a31" +dependencies = [ + "bytes", + "getrandom 0.3.4", + "lru-slab", + "rand", + "ring", + "rustc-hash", + "rustls", + "rustls-pki-types", + "slab", + "thiserror", + "tinyvec", + "tracing", + "web-time", +] + +[[package]] +name = "quinn-udp" +version = "0.5.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "addec6a0dcad8a8d96a771f815f0eaf55f9d1805756410b39f5fa81332574cbd" +dependencies = [ + "cfg_aliases", + "libc", + "once_cell", + "socket2", + "tracing", + "windows-sys 0.60.2", +] + +[[package]] +name = "quote" +version = "1.0.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21b2ebcf727b7760c461f091f9f0f539b77b8e87f2fd88131e7f1b433b3cece4" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "r-efi" +version = "5.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" + +[[package]] +name = "rand" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1" +dependencies = [ + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76afc826de14238e6e8c374ddcc1fa19e374fd8dd986b0d2af0d02377261d83c" +dependencies = [ + "getrandom 0.3.4", +] + +[[package]] +name = "redox_syscall" +version = "0.5.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" +dependencies = [ + "bitflags", +] + +[[package]] +name = "regex-automata" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a96887878f22d7bad8a3b6dc5b7440e0ada9a245242924394987b21cf2210a4c" + +[[package]] +name = "reqwest" +version = "0.12.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eddd3ca559203180a307f12d114c268abf583f59b03cb906fd0b3ff8646c1147" +dependencies = [ + "base64", + "bytes", + "futures-core", + "futures-util", + "http", + "http-body", + "http-body-util", + "hyper", + "hyper-rustls", + "hyper-util", + "js-sys", + "log", + "percent-encoding", + "pin-project-lite", + "quinn", + "rustls", + "rustls-pki-types", + "serde", + "serde_json", + "serde_urlencoded", + "sync_wrapper", + "tokio", + "tokio-rustls", + "tokio-util", + "tower", + "tower-http", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "wasm-streams", + "web-sys", + "webpki-roots", +] + +[[package]] +name = "ring" +version = "0.17.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" +dependencies = [ + "cc", + "cfg-if", + "getrandom 0.2.17", + "libc", + "untrusted", + "windows-sys 0.52.0", +] + +[[package]] +name = "rust-embed" +version = "8.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04113cb9355a377d83f06ef1f0a45b8ab8cd7d8b1288160717d66df5c7988d27" +dependencies = [ + "rust-embed-impl", + "rust-embed-utils", + "walkdir", +] + +[[package]] +name = "rust-embed-impl" +version = "8.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da0902e4c7c8e997159ab384e6d0fc91c221375f6894346ae107f47dd0f3ccaa" +dependencies = [ + "proc-macro2", + "quote", + "rust-embed-utils", + "syn", + "walkdir", +] + +[[package]] +name = "rust-embed-utils" +version = "8.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5bcdef0be6fe7f6fa333b1073c949729274b05f123a0ad7efcb8efd878e5c3b1" +dependencies = [ + "sha2", + "walkdir", +] + +[[package]] +name = "rustc-hash" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d" + +[[package]] +name = "rustls" +version = "0.23.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c665f33d38cea657d9614f766881e4d510e0eda4239891eea56b4cadcf01801b" +dependencies = [ + "once_cell", + "ring", + "rustls-pki-types", + "rustls-webpki", + "subtle", + "zeroize", +] + +[[package]] +name = "rustls-pki-types" +version = "1.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be040f8b0a225e40375822a563fa9524378b9d63112f53e19ffff34df5d33fdd" +dependencies = [ + "web-time", + "zeroize", +] + +[[package]] +name = "rustls-webpki" +version = "0.103.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7df23109aa6c1567d1c575b9952556388da57401e4ace1d15f79eedad0d8f53" +dependencies = [ + "ring", + "rustls-pki-types", + "untrusted", +] + +[[package]] +name = "rustversion" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" + +[[package]] +name = "ryu" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9774ba4a74de5f7b1c1451ed6cd5285a32eddb5cccb8cc655a4e50009e06477f" + +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "semver" +version = "1.0.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2" + +[[package]] +name = "serde" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" +dependencies = [ + "serde_core", + "serde_derive", +] + +[[package]] +name = "serde_cbor" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bef2ebfde456fb76bbcf9f59315333decc4fda0b2b44b420243c11e0f5ec1f5" +dependencies = [ + "half", + "serde", +] + +[[package]] +name = "serde_core" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.149" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86" +dependencies = [ + "itoa", + "memchr", + "serde", + "serde_core", + "zmij", +] + +[[package]] +name = "serde_path_to_error" +version = "0.1.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10a9ff822e371bb5403e391ecd83e182e0e77ba7f6fe0160b795797109d1b457" +dependencies = [ + "itoa", + "serde", + "serde_core", +] + +[[package]] +name = "serde_urlencoded" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" +dependencies = [ + "form_urlencoded", + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "sha2" +version = "0.10.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "sharded-slab" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" +dependencies = [ + "lazy_static", +] + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "simd-adler32" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e320a6c5ad31d271ad523dcf3ad13e2767ad8b1cb8f047f75a8aeaf8da139da2" + +[[package]] +name = "slab" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" + +[[package]] +name = "smallvec" +version = "1.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" + +[[package]] +name = "socket2" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86f4aa3ad99f2088c990dfa82d367e19cb29268ed67c574d10d0a4bfe71f07e0" +dependencies = [ + "libc", + "windows-sys 0.60.2", +] + +[[package]] +name = "stable_deref_trait" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" + +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + +[[package]] +name = "subtle" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" + +[[package]] +name = "syn" +version = "2.0.115" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e614ed320ac28113fa64972c4262d5dbc89deacdfd00c34a3e4cea073243c12" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "sync_wrapper" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263" +dependencies = [ + "futures-core", +] + +[[package]] +name = "synstructure" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "thiserror" +version = "2.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "thread_local" +version = "1.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f60246a4944f24f6e018aa17cdeffb7818b76356965d03b07d6a9886e8962185" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "tinystr" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42d3e9c45c09de15d06dd8acf5f4e0e399e85927b7f00711024eb7ae10fa4869" +dependencies = [ + "displaydoc", + "zerovec", +] + +[[package]] +name = "tinyvec" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa5fdc3bce6191a1dbc8c02d5c8bffcf557bafa17c124c5264a458f1b0613fa" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + +[[package]] +name = "tokio" +version = "1.49.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72a2903cd7736441aac9df9d7688bd0ce48edccaadf181c3b90be801e81d3d86" +dependencies = [ + "bytes", + "libc", + "mio", + "pin-project-lite", + "socket2", + "tokio-macros", + "windows-sys 0.61.2", +] + +[[package]] +name = "tokio-macros" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af407857209536a95c8e56f8231ef2c2e2aff839b22e07a1ffcbc617e9db9fa5" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tokio-rustls" +version = "0.26.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1729aa945f29d91ba541258c8df89027d5792d85a8841fb65e8bf0f4ede4ef61" +dependencies = [ + "rustls", + "tokio", +] + +[[package]] +name = "tokio-util" +version = "0.7.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ae9cec805b01e8fc3fd2fe289f89149a9b66dd16786abd8b19cfa7b48cb0098" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "tower" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebe5ef63511595f1344e2d5cfa636d973292adc0eec1f0ad45fae9f0851ab1d4" +dependencies = [ + "futures-core", + "futures-util", + "pin-project-lite", + "sync_wrapper", + "tokio", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "tower-http" +version = "0.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4e6559d53cc268e5031cd8429d05415bc4cb4aefc4aa5d6cc35fbf5b924a1f8" +dependencies = [ + "bitflags", + "bytes", + "futures-util", + "http", + "http-body", + "iri-string", + "pin-project-lite", + "tower", + "tower-layer", + "tower-service", +] + +[[package]] +name = "tower-layer" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e" + +[[package]] +name = "tower-service" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" + +[[package]] +name = "tracing" +version = "0.1.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63e71662fa4b2a2c3a26f570f037eb95bb1f85397f3cd8076caed2f026a6d100" +dependencies = [ + "log", + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tracing-core" +version = "0.1.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a" +dependencies = [ + "once_cell", + "valuable", +] + +[[package]] +name = "tracing-log" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3" +dependencies = [ + "log", + "once_cell", + "tracing-core", +] + +[[package]] +name = "tracing-subscriber" +version = "0.3.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f30143827ddab0d256fd843b7a66d164e9f271cfa0dde49142c5ca0ca291f1e" +dependencies = [ + "matchers", + "nu-ansi-term", + "once_cell", + "regex-automata", + "sharded-slab", + "smallvec", + "thread_local", + "tracing", + "tracing-core", + "tracing-log", +] + +[[package]] +name = "try-lock" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" + +[[package]] +name = "typenum" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb" + +[[package]] +name = "unicase" +version = "2.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbc4bc3a9f746d862c45cb89d705aa10f187bb96c76001afab07a0d35ce60142" + +[[package]] +name = "unicode-ident" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "537dd038a89878be9b64dd4bd1b260315c1bb94f4d784956b81e27a088d9a09e" + +[[package]] +name = "unicode-xid" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" + +[[package]] +name = "untrusted" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" + +[[package]] +name = "url" +version = "2.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff67a8a4397373c3ef660812acab3268222035010ab8680ec4215f38ba3d0eed" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", + "serde", +] + +[[package]] +name = "urlencoding" +version = "2.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "daf8dba3b7eb870caf1ddeed7bc9d2a049f3cfdfae7cb521b087cc33ae4c49da" + +[[package]] +name = "utf8_iter" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" + +[[package]] +name = "utf8parse" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" + +[[package]] +name = "uuid" +version = "1.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b672338555252d43fd2240c714dc444b8c6fb0a5c5335e65a07bba7742735ddb" +dependencies = [ + "atomic", + "getrandom 0.4.1", + "js-sys", + "rand", + "wasm-bindgen", +] + +[[package]] +name = "valuable" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65" + +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + +[[package]] +name = "walkdir" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" +dependencies = [ + "same-file", + "winapi-util", +] + +[[package]] +name = "want" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" +dependencies = [ + "try-lock", +] + +[[package]] +name = "wasi" +version = "0.11.1+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" + +[[package]] +name = "wasip2" +version = "1.0.2+wasi-0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9517f9239f02c069db75e65f174b3da828fe5f5b945c4dd26bd25d89c03ebcf5" +dependencies = [ + "wit-bindgen", +] + +[[package]] +name = "wasip3" +version = "0.4.0+wasi-0.3.0-rc-2026-01-06" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5428f8bf88ea5ddc08faddef2ac4a67e390b88186c703ce6dbd955e1c145aca5" +dependencies = [ + "wit-bindgen", +] + +[[package]] +name = "wasm-bindgen" +version = "0.2.108" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64024a30ec1e37399cf85a7ffefebdb72205ca1c972291c51512360d90bd8566" +dependencies = [ + "cfg-if", + "once_cell", + "rustversion", + "wasm-bindgen-macro", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.58" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70a6e77fd0ae8029c9ea0063f87c46fde723e7d887703d74ad2616d792e51e6f" +dependencies = [ + "cfg-if", + "futures-util", + "js-sys", + "once_cell", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.108" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "008b239d9c740232e71bd39e8ef6429d27097518b6b30bdf9086833bd5b6d608" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.108" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5256bae2d58f54820e6490f9839c49780dff84c65aeab9e772f15d5f0e913a55" +dependencies = [ + "bumpalo", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.108" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f01b580c9ac74c8d8f0c0e4afb04eeef2acf145458e52c03845ee9cd23e3d12" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "wasm-encoder" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "990065f2fe63003fe337b932cfb5e3b80e0b4d0f5ff650e6985b1048f62c8319" +dependencies = [ + "leb128fmt", + "wasmparser", +] + +[[package]] +name = "wasm-metadata" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb0e353e6a2fbdc176932bbaab493762eb1255a7900fe0fea1a2f96c296cc909" +dependencies = [ + "anyhow", + "indexmap", + "wasm-encoder", + "wasmparser", +] + +[[package]] +name = "wasm-streams" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15053d8d85c7eccdbefef60f06769760a563c7f0a9d6902a13d35c7800b0ad65" +dependencies = [ + "futures-util", + "js-sys", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + +[[package]] +name = "wasmparser" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe" +dependencies = [ + "bitflags", + "hashbrown 0.15.5", + "indexmap", + "semver", +] + +[[package]] +name = "web-sys" +version = "0.3.85" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "312e32e551d92129218ea9a2452120f4aabc03529ef03e4d0d82fb2780608598" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "web-time" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "webpki-roots" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22cfaf3c063993ff62e73cb4311efde4db1efb31ab78a3e5c457939ad5cc0bed" +dependencies = [ + "rustls-pki-types", +] + +[[package]] +name = "weezl" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a28ac98ddc8b9274cb41bb4d9d4d5c425b6020c50c46f25559911905610b4a88" + +[[package]] +name = "winapi-util" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" +dependencies = [ + "windows-sys 0.61.2", +] + +[[package]] +name = "windows-core" +version = "0.62.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb" +dependencies = [ + "windows-implement", + "windows-interface", + "windows-link", + "windows-result", + "windows-strings", +] + +[[package]] +name = "windows-implement" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "windows-interface" +version = "0.59.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" + +[[package]] +name = "windows-result" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-strings" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-sys" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" +dependencies = [ + "windows-targets 0.53.5", +] + +[[package]] +name = "windows-sys" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", + "windows_i686_gnullvm 0.52.6", + "windows_i686_msvc 0.52.6", + "windows_x86_64_gnu 0.52.6", + "windows_x86_64_gnullvm 0.52.6", + "windows_x86_64_msvc 0.52.6", +] + +[[package]] +name = "windows-targets" +version = "0.53.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3" +dependencies = [ + "windows-link", + "windows_aarch64_gnullvm 0.53.1", + "windows_aarch64_msvc 0.53.1", + "windows_i686_gnu 0.53.1", + "windows_i686_gnullvm 0.53.1", + "windows_i686_msvc 0.53.1", + "windows_x86_64_gnu 0.53.1", + "windows_x86_64_gnullvm 0.53.1", + "windows_x86_64_msvc 0.53.1", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnu" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "960e6da069d81e09becb0ca57a65220ddff016ff2d6af6a223cf372a506593a3" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_i686_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650" + +[[package]] +name = "wit-bindgen" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5" +dependencies = [ + "wit-bindgen-rust-macro", +] + +[[package]] +name = "wit-bindgen-core" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea61de684c3ea68cb082b7a88508a8b27fcc8b797d738bfc99a82facf1d752dc" +dependencies = [ + "anyhow", + "heck", + "wit-parser", +] + +[[package]] +name = "wit-bindgen-rust" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7c566e0f4b284dd6561c786d9cb0142da491f46a9fbed79ea69cdad5db17f21" +dependencies = [ + "anyhow", + "heck", + "indexmap", + "prettyplease", + "syn", + "wasm-metadata", + "wit-bindgen-core", + "wit-component", +] + +[[package]] +name = "wit-bindgen-rust-macro" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c0f9bfd77e6a48eccf51359e3ae77140a7f50b1e2ebfe62422d8afdaffab17a" +dependencies = [ + "anyhow", + "prettyplease", + "proc-macro2", + "quote", + "syn", + "wit-bindgen-core", + "wit-bindgen-rust", +] + +[[package]] +name = "wit-component" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2" +dependencies = [ + "anyhow", + "bitflags", + "indexmap", + "log", + "serde", + "serde_derive", + "serde_json", + "wasm-encoder", + "wasm-metadata", + "wasmparser", + "wit-parser", +] + +[[package]] +name = "wit-parser" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ecc8ac4bc1dc3381b7f59c34f00b67e18f910c2c0f50015669dde7def656a736" +dependencies = [ + "anyhow", + "id-arena", + "indexmap", + "log", + "semver", + "serde", + "serde_derive", + "serde_json", + "unicode-xid", + "wasmparser", +] + +[[package]] +name = "writeable" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9edde0db4769d2dc68579893f2306b26c6ecfbe0ef499b013d731b7b9247e0b9" + +[[package]] +name = "yoke" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72d6e5c6afb84d73944e5cedb052c4680d5657337201555f9f2a16b7406d4954" +dependencies = [ + "stable_deref_trait", + "yoke-derive", + "zerofrom", +] + +[[package]] +name = "yoke-derive" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b659052874eb698efe5b9e8cf382204678a0086ebf46982b79d6ca3182927e5d" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] + +[[package]] +name = "zerocopy" +version = "0.8.39" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db6d35d663eadb6c932438e763b262fe1a70987f9ae936e60158176d710cae4a" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.8.39" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4122cd3169e94605190e77839c9a40d40ed048d305bfdc146e7df40ab0f3e517" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "zerofrom" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50cc42e0333e05660c3587f3bf9d0478688e15d870fab3346451ce7f8c9fbea5" +dependencies = [ + "zerofrom-derive", +] + +[[package]] +name = "zerofrom-derive" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] + +[[package]] +name = "zeroize" +version = "1.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0" + +[[package]] +name = "zerotrie" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a59c17a5562d507e4b54960e8569ebee33bee890c70aa3fe7b97e85a9fd7851" +dependencies = [ + "displaydoc", + "yoke", + "zerofrom", +] + +[[package]] +name = "zerovec" +version = "0.11.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c28719294829477f525be0186d13efa9a3c602f7ec202ca9e353d310fb9a002" +dependencies = [ + "yoke", + "zerofrom", + "zerovec-derive", +] + +[[package]] +name = "zerovec-derive" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eadce39539ca5cb3985590102671f2567e659fca9666581ad3411d59207951f3" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "zmij" +version = "1.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa" + +[[package]] +name = "zune-core" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb8a0807f7c01457d0379ba880ba6322660448ddebc890ce29bb64da71fb40f9" + +[[package]] +name = "zune-jpeg" +version = "0.5.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "410e9ecef634c709e3831c2cfdb8d9c32164fae1c67496d5b68fff728eec37fe" +dependencies = [ + "zune-core", +] diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..d5e630c --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,42 @@ +[package] +name = "kiro-rs" +version = "1.1.5" +edition = "2024" + +[profile.release] +lto = "thin" +strip = true + +[features] +default = [] +# 显式启用才允许打印潜在敏感信息(仅用于排障) +sensitive-logs = [] + +[dependencies] +axum = "0.8" +tokio = { version = "1.0", features = ["rt-multi-thread", "macros", "net", "time", "sync"] } +reqwest = { version = "0.12", default-features = false, features = ["stream", "json", "socks", "rustls-tls"] } +serde = { version = "1.0", features = ["derive"] } +serde_json = "1.0" +serde_cbor = "0.11" # CBOR 编解码(Web Portal API) +tracing = "0.1" +tracing-subscriber = { version = "0.3", features = ["env-filter"] } +anyhow = "1.0" +http = "1.0" +futures = "0.3" +chrono = { version = "0.4", features = ["serde"] } +uuid = { version = "1.10", features = ["v1", "v4", "fast-rng"] } +fastrand = "2" +sha2 = "0.10" +hex = "0.4" +crc = "3" # CRC32C 计算 +bytes = "1" # 高效的字节缓冲区 +tower-http = { version = "0.6", features = ["cors"] } +clap = { version = "4.5", features = ["derive"] } +urlencoding = "2" +parking_lot = "0.12" # 高性能同步原语 +subtle = "2.6" # 常量时间比较(防止时序攻击) +rust-embed = "8" # 嵌入静态文件 +mime_guess = "2" # MIME 类型推断 +image = { version = "0.25", default-features = false, features = ["jpeg", "png", "gif", "webp"] } # 图片处理 +base64 = "0.22" # Base64 编解码 diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..6d10c55 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,51 @@ +FROM rust:1.93-alpine AS chef +RUN apk add --no-cache musl-dev openssl-dev openssl-libs-static +RUN cargo install cargo-chef +WORKDIR /app + +FROM chef AS planner +COPY Cargo.toml Cargo.lock* ./ +COPY src ./src +RUN cargo chef prepare --recipe-path recipe.json + +FROM node:24-alpine AS frontend-builder +WORKDIR /app/admin-ui +COPY admin-ui/package.json ./ +RUN npm install -g pnpm && pnpm install +COPY admin-ui ./ +RUN pnpm build + +FROM chef AS builder + +# 可选:启用敏感日志输出(仅用于排障) +ARG ENABLE_SENSITIVE_LOGS=false + +COPY --from=planner /app/recipe.json recipe.json +RUN if [ "$ENABLE_SENSITIVE_LOGS" = "true" ]; then \ + cargo chef cook --release --features sensitive-logs --recipe-path recipe.json; \ + else \ + cargo chef cook --release --recipe-path recipe.json; \ + fi + +COPY Cargo.toml Cargo.lock* ./ +COPY src ./src +COPY --from=frontend-builder /app/admin-ui/dist /app/admin-ui/dist + +RUN if [ "$ENABLE_SENSITIVE_LOGS" = "true" ]; then \ + cargo build --release --features sensitive-logs; \ + else \ + cargo build --release; \ + fi + +FROM alpine:3.21 + +RUN apk add --no-cache ca-certificates + +WORKDIR /app +COPY --from=builder /app/target/release/kiro-rs /app/kiro-rs + +VOLUME ["/app/config"] + +EXPOSE 8990 + +CMD ["./kiro-rs", "-c", "/app/config/config.json", "--credentials", "/app/config/credentials.json"] diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..3115809 --- /dev/null +++ b/Makefile @@ -0,0 +1,68 @@ +.PHONY: dev build release clean test lint fmt ui ui-dev docker help + +# 默认目标 +help: + @echo "Usage: make " + @echo "" + @echo "开发:" + @echo " dev cargo run(debug 模式,需先 make ui)" + @echo " ui-dev 启动前端 dev server" + @echo "" + @echo "构建:" + @echo " ui 构建前端" + @echo " build 构建前端 + 后端(debug)" + @echo " release 构建前端 + 后端(release)" + @echo " docker 构建 Docker 镜像" + @echo "" + @echo "质量:" + @echo " test 运行测试" + @echo " lint cargo clippy" + @echo " fmt cargo fmt" + @echo " check fmt + clippy + test" + @echo "" + @echo "其他:" + @echo " clean 清理构建产物" + +# --- 前端 --- + +ui: + cd admin-ui && pnpm install && pnpm build + +ui-dev: + @echo "启动前端 dev server: http://localhost:5173" + cd admin-ui && pnpm install && pnpm dev + +# --- 后端 --- + +dev: ui + cargo run --features sensitive-logs -- -c config/config.json --credentials config/credentials.json + +build: ui + cargo build + +release: ui + cargo build --release + +# --- 质量 --- + +test: + cargo test + +lint: + cargo clippy -- -D warnings + +fmt: + cargo fmt + +check: fmt lint test + +# --- Docker --- + +docker: + docker build -t kiro-rs . + +# --- 清理 --- + +clean: + cargo clean + rm -rf admin-ui/dist admin-ui/node_modules diff --git a/README.md b/README.md new file mode 100644 index 0000000..4ed60d0 --- /dev/null +++ b/README.md @@ -0,0 +1,525 @@ +# kiro-rs + +一个用 Rust 编写的 Anthropic Claude API 兼容代理服务,将 Anthropic API 请求转换为 Kiro API 请求。 + +--- + +## 免责声明 + +本项目仅供研究使用, Use at your own risk, 使用本项目所导致的任何后果由使用人承担, 与本项目无关。 +本项目与 AWS/KIRO/Anthropic/Claude 等官方无关, 本项目不代表官方立场。 + +## 注意! + +因 TLS 默认从 native-tls 切换至 rustls,你可能需要专门安装证书后才能配置 HTTP 代理。可通过 `config.json` 的 `tlsBackend` 切回 `native-tls`。 +如果遇到请求报错, 尤其是无法刷新 token, 或者是直接返回 error request, 请尝试切换 tls 后端为 `native-tls`, 一般即可解决。 + +**Write Failed/会话卡死**: 如果遇到持续的 Write File / Write Failed 并导致会话不可用,参考 Issue [#22](https://github.com/hank9999/kiro.rs/issues/22) 和 [#49](https://github.com/hank9999/kiro.rs/issues/49) 的说明与临时解决方案(通常与输出过长被截断有关,可尝试调低输出相关 token 上限) + +## 功能特性 + +- **Anthropic API 兼容**: 完整支持 Anthropic Claude API 格式 +- **流式响应**: 支持 SSE (Server-Sent Events) 流式输出 +- **Token 自动刷新**: 自动管理和刷新 OAuth Token +- **多凭据支持**: 支持配置多个凭据,按优先级自动故障转移 +- **智能重试**: 单凭据最多重试 2 次,单请求最多重试 3 次 +- **凭据回写**: 多凭据格式下自动回写刷新后的 Token +- **Thinking 模式**: 支持 Claude 的 extended thinking 功能 +- **工具调用**: 完整支持 function calling / tool use +- **WebSearch**: 内置 WebSearch 工具转换逻辑 +- **多模型支持**: 支持 Sonnet、Opus、Haiku 系列模型 +- **Admin 管理**: 可选的 Web 管理界面和 API,支持凭据管理、余额查询等 +- **多级 Region 配置**: 支持全局和凭据级别的 Auth Region / API Region 配置 +- **凭据级代理**: 支持为每个凭据单独配置 HTTP/SOCKS5 代理,优先级:凭据代理 > 全局代理 > 无代理 + +--- + +- [开始](#开始) + - [1. 编译](#1-编译) + - [2. 最小配置](#2-最小配置) + - [3. 启动](#3-启动) + - [4. 验证](#4-验证) + - [Docker](#docker) +- [配置详解](#配置详解) + - [config.json](#configjson) + - [credentials.json](#credentialsjson) + - [Region 配置](#region-配置) + - [代理配置](#代理配置) + - [认证方式](#认证方式) + - [环境变量](#环境变量) +- [API 端点](#api-端点) + - [标准端点 (/v1)](#标准端点-v1) + - [Claude Code 兼容端点 (/cc/v1)](#claude-code-兼容端点-ccv1) + - [Thinking 模式](#thinking-模式) + - [工具调用](#工具调用) +- [模型映射](#模型映射) +- [Admin(可选)](#admin可选) +- [注意事项](#注意事项) +- [项目结构](#项目结构) +- [技术栈](#技术栈) +- [License](#license) +- [致谢](#致谢) + +## 开始 + +### 1. 编译 + +> PS: 如果不想编辑可以直接前往 Release 下载二进制文件 + +> **前置步骤**:编译前需要先构建前端 Admin UI(用于嵌入到二进制中): +> ```bash +> cd admin-ui && pnpm install && pnpm build +> ``` + +```bash +cargo build --release +``` + +### 2. 最小配置 + +创建 `config.json`: + +```json +{ + "host": "127.0.0.1", + "port": 8990, + "apiKey": "sk-kiro-rs-qazWSXedcRFV123456", + "region": "us-east-1" +} +``` +> PS: 如果你需要 Web 管理面板, 请注意配置 `adminApiKey` + +创建 `credentials.json`(从 Kiro IDE 等中获取凭证信息): +> PS: 可以前往 Web 管理面板配置跳过本步骤 +> 如果你对凭据地域有疑惑, 请查看 [Region 配置](#region-配置) + +Social 认证: +```json +{ + "refreshToken": "你的刷新token", + "expiresAt": "2025-12-31T02:32:45.144Z", + "authMethod": "social" +} +``` + +IdC 认证: +```json +{ + "refreshToken": "你的刷新token", + "expiresAt": "2025-12-31T02:32:45.144Z", + "authMethod": "idc", + "clientId": "你的clientId", + "clientSecret": "你的clientSecret" +} +``` + +### 3. 启动 + +```bash +./target/release/kiro-rs +``` + +或指定配置文件路径: + +```bash +./target/release/kiro-rs -c /path/to/config.json --credentials /path/to/credentials.json +``` + +### 4. 验证 + +```bash +curl http://127.0.0.1:8990/v1/messages \ + -H "Content-Type: application/json" \ + -H "x-api-key: sk-kiro-rs-qazWSXedcRFV123456" \ + -d '{ + "model": "claude-sonnet-4-20250514", + "max_tokens": 1024, + "stream": true, + "messages": [ + {"role": "user", "content": "Hello, Claude!"} + ] + }' +``` + +### Docker + +也可以通过 Docker 启动: + +```bash +docker-compose up +``` + +需要将 `config.json` 和 `credentials.json` 挂载到容器中,具体参见 `docker-compose.yml`。 + +## 配置详解 + +### config.json + +| 字段 | 类型 | 默认值 | 描述 | +|------|------|--------|------| +| `host` | string | `127.0.0.1` | 服务监听地址 | +| `port` | number | `8080` | 服务监听端口 | +| `apiKey` | string | - | 自定义 API Key(用于客户端认证,必配) | +| `region` | string | `us-east-1` | AWS 区域 | +| `authRegion` | string | - | Auth Region(用于 Token 刷新),未配置时回退到 region | +| `apiRegion` | string | - | API Region(用于 API 请求),未配置时回退到 region | +| `kiroVersion` | string | `0.10.0` | Kiro 版本号 | +| `machineId` | string | - | 自定义机器码(64位十六进制),不定义则自动生成 | +| `systemVersion` | string | 随机 | 系统版本标识 | +| `nodeVersion` | string | `22.21.1` | Node.js 版本标识 | +| `tlsBackend` | string | `rustls` | TLS 后端:`rustls` 或 `native-tls` | +| `countTokensApiUrl` | string | - | 外部 count_tokens API 地址 | +| `countTokensApiKey` | string | - | 外部 count_tokens API 密钥 | +| `countTokensAuthType` | string | `x-api-key` | 外部 API 认证类型:`x-api-key` 或 `bearer` | +| `proxyUrl` | string | - | HTTP/SOCKS5 代理地址 | +| `proxyUsername` | string | - | 代理用户名 | +| `proxyPassword` | string | - | 代理密码 | +| `adminApiKey` | string | - | Admin API 密钥,配置后启用凭据管理 API 和 Web 管理界面 | + +完整配置示例: + +```json +{ + "host": "127.0.0.1", + "port": 8990, + "apiKey": "sk-kiro-rs-qazWSXedcRFV123456", + "region": "us-east-1", + "tlsBackend": "rustls", + "kiroVersion": "0.10.0", + "machineId": "64位十六进制机器码", + "systemVersion": "darwin#24.6.0", + "nodeVersion": "22.21.1", + "authRegion": "us-east-1", + "apiRegion": "us-east-1", + "countTokensApiUrl": "https://api.example.com/v1/messages/count_tokens", + "countTokensApiKey": "sk-your-count-tokens-api-key", + "countTokensAuthType": "x-api-key", + "proxyUrl": "http://127.0.0.1:7890", + "proxyUsername": "user", + "proxyPassword": "pass", + "adminApiKey": "sk-admin-your-secret-key" +} +``` + +### credentials.json + +支持单对象格式(向后兼容)或数组格式(多凭据)。 + +#### 字段说明 + +| 字段 | 类型 | 描述 | +|----------------|--------|---------------------------------------------| +| `id` | number | 凭据唯一 ID(可选,仅用于 Admin API 管理;手写文件可不填) | +| `accessToken` | string | OAuth 访问令牌(可选,可自动刷新) | +| `refreshToken` | string | OAuth 刷新令牌 | +| `profileArn` | string | AWS Profile ARN(可选,登录时返回) | +| `expiresAt` | string | Token 过期时间 (RFC3339) | +| `authMethod` | string | 认证方式:`social` 或 `idc` | +| `clientId` | string | IdC 登录的客户端 ID(IdC 认证必填) | +| `clientSecret` | string | IdC 登录的客户端密钥(IdC 认证必填) | +| `priority` | number | 凭据优先级,数字越小越优先,默认为 0 | +| `region` | string | 凭据级 Auth Region, 兼容字段 | +| `authRegion` | string | 凭据级 Auth Region,用于 Token 刷新, 未配置时回退到 region | +| `apiRegion` | string | 凭据级 API Region,用于 API 请求 | +| `machineId` | string | 凭据级机器码(64位十六进制) | +| `email` | string | 用户邮箱(可选,从 API 获取) | +| `proxyUrl` | string | 凭据级代理 URL(可选,特殊值 `direct` 表示不使用代理) | +| `proxyUsername`| string | 凭据级代理用户名(可选) | +| `proxyPassword`| string | 凭据级代理密码(可选) | + +说明: +- IdC / Builder-ID / IAM 在本项目里属于同一种登录方式,配置时统一使用 `authMethod: "idc"` +- 为兼容旧配置,`builder-id` / `iam` 仍可被识别,但会按 `idc` 处理 + +#### 单凭据格式(旧格式,向后兼容) + +```json +{ + "accessToken": "请求token,一般有效期一小时,可选", + "refreshToken": "刷新token,一般有效期7-30天不等", + "profileArn": "arn:aws:codewhisperer:us-east-1:111112222233:profile/QWER1QAZSDFGH", + "expiresAt": "2025-12-31T02:32:45.144Z", + "authMethod": "social", + "clientId": "IdC 登录需要", + "clientSecret": "IdC 登录需要" +} +``` + +#### 多凭据格式(支持故障转移和自动回写) + +```json +[ + { + "refreshToken": "第一个凭据的刷新token", + "expiresAt": "2025-12-31T02:32:45.144Z", + "authMethod": "social", + "priority": 0 + }, + { + "refreshToken": "第二个凭据的刷新token", + "expiresAt": "2025-12-31T02:32:45.144Z", + "authMethod": "idc", + "clientId": "xxxxxxxxx", + "clientSecret": "xxxxxxxxx", + "region": "us-east-2", + "priority": 1, + "proxyUrl": "socks5://proxy.example.com:1080", + "proxyUsername": "user", + "proxyPassword": "pass" + }, + { + "refreshToken": "第三个凭据(显式不走代理)", + "expiresAt": "2025-12-31T02:32:45.144Z", + "authMethod": "social", + "priority": 2, + "proxyUrl": "direct" + } +] +``` + +多凭据特性: +- 按 `priority` 字段排序,数字越小优先级越高(默认为 0) +- 单凭据最多重试 3 次,单请求最多重试 9 次 +- 自动故障转移到下一个可用凭据 +- 多凭据格式下 Token 刷新后自动回写到源文件 + +### Region 配置 + +支持多级 Region 配置,分别控制 Token 刷新和 API 请求使用的区域。 + +**Auth Region**(Token 刷新)优先级: +`凭据.authRegion` > `凭据.region` > `config.authRegion` > `config.region` + +**API Region**(API 请求)优先级: +`凭据.apiRegion` > `config.apiRegion` > `config.region` + +### 代理配置 + +支持全局代理和凭据级代理,凭据级代理会覆盖该凭据产生的所有出站连接(API 请求、Token 刷新、额度查询)。 + +**代理优先级**:`凭据.proxyUrl` > `config.proxyUrl` > 无代理 + +| 凭据 `proxyUrl` 值 | 行为 | +|---|---| +| 具体 URL(如 `http://proxy:8080`、`socks5://proxy:1080`) | 使用凭据指定的代理 | +| `direct` | 显式不使用代理(即使全局配置了代理) | +| 未配置(留空) | 回退到全局代理配置 | + +凭据级代理示例: + +```json +[ + { + "refreshToken": "凭据A:使用自己的代理", + "authMethod": "social", + "proxyUrl": "socks5://proxy-a.example.com:1080", + "proxyUsername": "user_a", + "proxyPassword": "pass_a" + }, + { + "refreshToken": "凭据B:显式不走代理(直连)", + "authMethod": "social", + "proxyUrl": "direct" + }, + { + "refreshToken": "凭据C:使用全局代理(或直连,取决于 config.json)", + "authMethod": "social" + } +] +``` + +### 认证方式 + +客户端请求本服务时,支持两种认证方式: + +1. **x-api-key Header** + ``` + x-api-key: sk-your-api-key + ``` + +2. **Authorization Bearer** + ``` + Authorization: Bearer sk-your-api-key + ``` + +### 环境变量 + +可通过环境变量配置日志级别: + +```bash +RUST_LOG=debug ./target/release/kiro-rs +``` + +## API 端点 + +### 标准端点 (/v1) + +| 端点 | 方法 | 描述 | +|------|------|------| +| `/v1/models` | GET | 获取可用模型列表 | +| `/v1/messages` | POST | 创建消息(对话) | +| `/v1/messages/count_tokens` | POST | 估算 Token 数量 | + +### Claude Code 兼容端点 (/cc/v1) + +| 端点 | 方法 | 描述 | +|------|------|------| +| `/cc/v1/messages` | POST | 创建消息(缓冲模式,确保 `input_tokens` 准确) | +| `/cc/v1/messages/count_tokens` | POST | 估算 Token 数量(与 `/v1` 相同) | + +> **`/cc/v1/messages` 与 `/v1/messages` 的区别**: +> - `/v1/messages`:实时流式返回,`message_start` 中的 `input_tokens` 是估算值 +> - `/cc/v1/messages`:缓冲模式,等待上游流完成后,用从 `contextUsageEvent` 计算的准确 `input_tokens` 更正 `message_start`,然后一次性返回所有事件 +> - 等待期间会每 25 秒发送 `ping` 事件保活 + +### Thinking 模式 + +支持 Claude 的 extended thinking 功能: + +```json +{ + "model": "claude-sonnet-4-20250514", + "max_tokens": 16000, + "thinking": { + "type": "enabled", + "budget_tokens": 10000 + }, + "messages": [...] +} +``` + +### 工具调用 + +完整支持 Anthropic 的 tool use 功能: + +```json +{ + "model": "claude-sonnet-4-20250514", + "max_tokens": 1024, + "tools": [ + { + "name": "get_weather", + "description": "获取指定城市的天气", + "input_schema": { + "type": "object", + "properties": { + "city": {"type": "string"} + }, + "required": ["city"] + } + } + ], + "messages": [...] +} +``` + +## 模型映射 + +| Anthropic 模型 | Kiro 模型 | +|----------------|-----------| +| `*sonnet*`(含 4-6/4.6) | `claude-sonnet-4.6` | +| `*sonnet*`(其他) | `claude-sonnet-4.5` | +| `*opus*`(含 4-5/4.5) | `claude-opus-4.5` | +| `*opus*`(其他) | `claude-opus-4.6` | +| `*haiku*` | `claude-haiku-4.5` | + +## Admin(可选) + +当 `config.json` 配置了非空 `adminApiKey` 时,会启用: + +- **Admin API(认证同 API Key)** + - `GET /api/admin/credentials` - 获取所有凭据状态 + - `POST /api/admin/credentials` - 添加新凭据 + - `DELETE /api/admin/credentials/:id` - 删除凭据 + - `POST /api/admin/credentials/:id/disabled` - 设置凭据禁用状态 + - `POST /api/admin/credentials/:id/priority` - 设置凭据优先级 + - `POST /api/admin/credentials/:id/region` - 设置凭据 Region + - `POST /api/admin/credentials/:id/reset` - 重置失败计数 + - `GET /api/admin/credentials/:id/balance` - 获取凭据余额 + +- **Admin UI** + - `GET /admin` - 访问管理页面(需要在编译前构建 `admin-ui/dist`) + +## 注意事项 + +1. **凭证安全**: 请妥善保管 `credentials.json` 文件,不要提交到版本控制 +2. **Token 刷新**: 服务会自动刷新过期的 Token,无需手动干预 +3. **WebSearch 工具**: 只要 `tools` 中包含 `web_search`(按 name 或 type 判断),就走内置 WebSearch 处理逻辑 + +## 项目结构 + +``` +kiro-rs/ +├── src/ +│ ├── main.rs # 程序入口 +│ ├── http_client.rs # HTTP 客户端构建 +│ ├── token.rs # Token 计算模块 +│ ├── debug.rs # 调试工具 +│ ├── test.rs # 测试 +│ ├── model/ # 配置和参数模型 +│ │ ├── config.rs # 应用配置 +│ │ └── arg.rs # 命令行参数 +│ ├── anthropic/ # Anthropic API 兼容层 +│ │ ├── router.rs # 路由配置 +│ │ ├── handlers.rs # 请求处理器 +│ │ ├── middleware.rs # 认证中间件 +│ │ ├── types.rs # 类型定义 +│ │ ├── converter.rs # 协议转换器 +│ │ ├── stream.rs # 流式响应处理 +│ │ └── websearch.rs # WebSearch 工具处理 +│ ├── kiro/ # Kiro API 客户端 +│ │ ├── provider.rs # API 提供者 +│ │ ├── token_manager.rs # Token 管理 +│ │ ├── machine_id.rs # 设备指纹生成 +│ │ ├── model/ # 数据模型 +│ │ │ ├── credentials.rs # OAuth 凭证 +│ │ │ ├── events/ # 响应事件类型 +│ │ │ ├── requests/ # 请求类型 +│ │ │ ├── common/ # 共享类型 +│ │ │ ├── token_refresh.rs # Token 刷新模型 +│ │ │ └── usage_limits.rs # 使用额度模型 +│ │ └── parser/ # AWS Event Stream 解析器 +│ │ ├── decoder.rs # 流式解码器 +│ │ ├── frame.rs # 帧解析 +│ │ ├── header.rs # 头部解析 +│ │ ├── error.rs # 错误类型 +│ │ └── crc.rs # CRC 校验 +│ ├── admin/ # Admin API 模块 +│ │ ├── router.rs # 路由配置 +│ │ ├── handlers.rs # 请求处理器 +│ │ ├── service.rs # 业务逻辑服务 +│ │ ├── types.rs # 类型定义 +│ │ ├── middleware.rs # 认证中间件 +│ │ └── error.rs # 错误处理 +│ ├── admin_ui/ # Admin UI 静态文件嵌入 +│ │ └── router.rs # 静态文件路由 +│ └── common/ # 公共模块 +│ └── auth.rs # 认证工具函数 +├── admin-ui/ # Admin UI 前端工程(构建产物会嵌入二进制) +├── tools/ # 辅助工具 +├── Cargo.toml # 项目配置 +├── config.example.json # 配置示例 +├── docker-compose.yml # Docker Compose 配置 +└── Dockerfile # Docker 构建文件 +``` + +## 技术栈 + +- **Web 框架**: [Axum](https://github.com/tokio-rs/axum) 0.8 +- **异步运行时**: [Tokio](https://tokio.rs/) +- **HTTP 客户端**: [Reqwest](https://github.com/seanmonstar/reqwest) +- **序列化**: [Serde](https://serde.rs/) +- **日志**: [tracing](https://github.com/tokio-rs/tracing) +- **命令行**: [Clap](https://github.com/clap-rs/clap) + +## License + +MIT + +## 致谢 + +本项目的实现离不开前辈的努力: + - [kiro2api](https://github.com/caidaoli/kiro2api) + - [proxycast](https://github.com/aiclientproxy/proxycast) + +本项目部分逻辑参考了以上的项目, 再次由衷的感谢! diff --git a/VERSION b/VERSION new file mode 100644 index 0000000..261f347 --- /dev/null +++ b/VERSION @@ -0,0 +1 @@ +v1.1.5 \ No newline at end of file diff --git a/admin-ui/bun.lock b/admin-ui/bun.lock new file mode 100644 index 0000000..4aed436 --- /dev/null +++ b/admin-ui/bun.lock @@ -0,0 +1,480 @@ +{ + "lockfileVersion": 1, + "configVersion": 1, + "workspaces": { + "": { + "name": "kiro-admin-ui", + "dependencies": { + "@radix-ui/react-dialog": "^1.1.15", + "@radix-ui/react-dropdown-menu": "^2.1.16", + "@radix-ui/react-slot": "^1.2.4", + "@radix-ui/react-switch": "^1.2.6", + "@radix-ui/react-toast": "^1.2.15", + "@radix-ui/react-tooltip": "^1.2.8", + "@tanstack/react-query": "^5.90.20", + "axios": "^1.13.4", + "class-variance-authority": "^0.7.1", + "clsx": "^2.1.1", + "lucide-react": "^0.563.0", + "react": "^19.2.4", + "react-dom": "^19.2.4", + "sonner": "^2.0.7", + "tailwind-merge": "^3.4.0", + }, + "devDependencies": { + "@tailwindcss/postcss": "^4.1.18", + "@types/react": "^19.2.10", + "@types/react-dom": "^19.2.3", + "@vitejs/plugin-react-swc": "^4.2.2", + "autoprefixer": "^10.4.24", + "postcss": "^8.5.6", + "tailwindcss": "^4.1.18", + "typescript": "^5.9.3", + "vite": "^7.3.1", + }, + }, + }, + "packages": { + "@alloc/quick-lru": ["@alloc/quick-lru@5.2.0", "https://registry.npmmirror.com/@alloc/quick-lru/-/quick-lru-5.2.0.tgz", {}, "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw=="], + + "@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.27.2", "https://registry.npmmirror.com/@esbuild/aix-ppc64/-/aix-ppc64-0.27.2.tgz", { "os": "aix", "cpu": "ppc64" }, "sha512-GZMB+a0mOMZs4MpDbj8RJp4cw+w1WV5NYD6xzgvzUJ5Ek2jerwfO2eADyI6ExDSUED+1X8aMbegahsJi+8mgpw=="], + + "@esbuild/android-arm": ["@esbuild/android-arm@0.27.2", "https://registry.npmmirror.com/@esbuild/android-arm/-/android-arm-0.27.2.tgz", { "os": "android", "cpu": "arm" }, "sha512-DVNI8jlPa7Ujbr1yjU2PfUSRtAUZPG9I1RwW4F4xFB1Imiu2on0ADiI/c3td+KmDtVKNbi+nffGDQMfcIMkwIA=="], + + "@esbuild/android-arm64": ["@esbuild/android-arm64@0.27.2", "https://registry.npmmirror.com/@esbuild/android-arm64/-/android-arm64-0.27.2.tgz", { "os": "android", "cpu": "arm64" }, "sha512-pvz8ZZ7ot/RBphf8fv60ljmaoydPU12VuXHImtAs0XhLLw+EXBi2BLe3OYSBslR4rryHvweW5gmkKFwTiFy6KA=="], + + "@esbuild/android-x64": ["@esbuild/android-x64@0.27.2", "https://registry.npmmirror.com/@esbuild/android-x64/-/android-x64-0.27.2.tgz", { "os": "android", "cpu": "x64" }, "sha512-z8Ank4Byh4TJJOh4wpz8g2vDy75zFL0TlZlkUkEwYXuPSgX8yzep596n6mT7905kA9uHZsf/o2OJZubl2l3M7A=="], + + "@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.27.2", "https://registry.npmmirror.com/@esbuild/darwin-arm64/-/darwin-arm64-0.27.2.tgz", { "os": "darwin", "cpu": "arm64" }, "sha512-davCD2Zc80nzDVRwXTcQP/28fiJbcOwvdolL0sOiOsbwBa72kegmVU0Wrh1MYrbuCL98Omp5dVhQFWRKR2ZAlg=="], + + "@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.27.2", "https://registry.npmmirror.com/@esbuild/darwin-x64/-/darwin-x64-0.27.2.tgz", { "os": "darwin", "cpu": "x64" }, "sha512-ZxtijOmlQCBWGwbVmwOF/UCzuGIbUkqB1faQRf5akQmxRJ1ujusWsb3CVfk/9iZKr2L5SMU5wPBi1UWbvL+VQA=="], + + "@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.27.2", "https://registry.npmmirror.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.2.tgz", { "os": "freebsd", "cpu": "arm64" }, "sha512-lS/9CN+rgqQ9czogxlMcBMGd+l8Q3Nj1MFQwBZJyoEKI50XGxwuzznYdwcav6lpOGv5BqaZXqvBSiB/kJ5op+g=="], + + "@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.27.2", "https://registry.npmmirror.com/@esbuild/freebsd-x64/-/freebsd-x64-0.27.2.tgz", { "os": "freebsd", "cpu": "x64" }, "sha512-tAfqtNYb4YgPnJlEFu4c212HYjQWSO/w/h/lQaBK7RbwGIkBOuNKQI9tqWzx7Wtp7bTPaGC6MJvWI608P3wXYA=="], + + "@esbuild/linux-arm": ["@esbuild/linux-arm@0.27.2", "https://registry.npmmirror.com/@esbuild/linux-arm/-/linux-arm-0.27.2.tgz", { "os": "linux", "cpu": "arm" }, "sha512-vWfq4GaIMP9AIe4yj1ZUW18RDhx6EPQKjwe7n8BbIecFtCQG4CfHGaHuh7fdfq+y3LIA2vGS/o9ZBGVxIDi9hw=="], + + "@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.27.2", "https://registry.npmmirror.com/@esbuild/linux-arm64/-/linux-arm64-0.27.2.tgz", { "os": "linux", "cpu": "arm64" }, "sha512-hYxN8pr66NsCCiRFkHUAsxylNOcAQaxSSkHMMjcpx0si13t1LHFphxJZUiGwojB1a/Hd5OiPIqDdXONia6bhTw=="], + + "@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.27.2", "https://registry.npmmirror.com/@esbuild/linux-ia32/-/linux-ia32-0.27.2.tgz", { "os": "linux", "cpu": "ia32" }, "sha512-MJt5BRRSScPDwG2hLelYhAAKh9imjHK5+NE/tvnRLbIqUWa+0E9N4WNMjmp/kXXPHZGqPLxggwVhz7QP8CTR8w=="], + + "@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.27.2", "https://registry.npmmirror.com/@esbuild/linux-loong64/-/linux-loong64-0.27.2.tgz", { "os": "linux", "cpu": "none" }, "sha512-lugyF1atnAT463aO6KPshVCJK5NgRnU4yb3FUumyVz+cGvZbontBgzeGFO1nF+dPueHD367a2ZXe1NtUkAjOtg=="], + + "@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.27.2", "https://registry.npmmirror.com/@esbuild/linux-mips64el/-/linux-mips64el-0.27.2.tgz", { "os": "linux", "cpu": "none" }, "sha512-nlP2I6ArEBewvJ2gjrrkESEZkB5mIoaTswuqNFRv/WYd+ATtUpe9Y09RnJvgvdag7he0OWgEZWhviS1OTOKixw=="], + + "@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.27.2", "https://registry.npmmirror.com/@esbuild/linux-ppc64/-/linux-ppc64-0.27.2.tgz", { "os": "linux", "cpu": "ppc64" }, "sha512-C92gnpey7tUQONqg1n6dKVbx3vphKtTHJaNG2Ok9lGwbZil6DrfyecMsp9CrmXGQJmZ7iiVXvvZH6Ml5hL6XdQ=="], + + "@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.27.2", "https://registry.npmmirror.com/@esbuild/linux-riscv64/-/linux-riscv64-0.27.2.tgz", { "os": "linux", "cpu": "none" }, "sha512-B5BOmojNtUyN8AXlK0QJyvjEZkWwy/FKvakkTDCziX95AowLZKR6aCDhG7LeF7uMCXEJqwa8Bejz5LTPYm8AvA=="], + + "@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.27.2", "https://registry.npmmirror.com/@esbuild/linux-s390x/-/linux-s390x-0.27.2.tgz", { "os": "linux", "cpu": "s390x" }, "sha512-p4bm9+wsPwup5Z8f4EpfN63qNagQ47Ua2znaqGH6bqLlmJ4bx97Y9JdqxgGZ6Y8xVTixUnEkoKSHcpRlDnNr5w=="], + + "@esbuild/linux-x64": ["@esbuild/linux-x64@0.27.2", "https://registry.npmmirror.com/@esbuild/linux-x64/-/linux-x64-0.27.2.tgz", { "os": "linux", "cpu": "x64" }, "sha512-uwp2Tip5aPmH+NRUwTcfLb+W32WXjpFejTIOWZFw/v7/KnpCDKG66u4DLcurQpiYTiYwQ9B7KOeMJvLCu/OvbA=="], + + "@esbuild/netbsd-arm64": ["@esbuild/netbsd-arm64@0.27.2", "https://registry.npmmirror.com/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.2.tgz", { "os": "none", "cpu": "arm64" }, "sha512-Kj6DiBlwXrPsCRDeRvGAUb/LNrBASrfqAIok+xB0LxK8CHqxZ037viF13ugfsIpePH93mX7xfJp97cyDuTZ3cw=="], + + "@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.27.2", "https://registry.npmmirror.com/@esbuild/netbsd-x64/-/netbsd-x64-0.27.2.tgz", { "os": "none", "cpu": "x64" }, "sha512-HwGDZ0VLVBY3Y+Nw0JexZy9o/nUAWq9MlV7cahpaXKW6TOzfVno3y3/M8Ga8u8Yr7GldLOov27xiCnqRZf0tCA=="], + + "@esbuild/openbsd-arm64": ["@esbuild/openbsd-arm64@0.27.2", "https://registry.npmmirror.com/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.2.tgz", { "os": "openbsd", "cpu": "arm64" }, "sha512-DNIHH2BPQ5551A7oSHD0CKbwIA/Ox7+78/AWkbS5QoRzaqlev2uFayfSxq68EkonB+IKjiuxBFoV8ESJy8bOHA=="], + + "@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.27.2", "https://registry.npmmirror.com/@esbuild/openbsd-x64/-/openbsd-x64-0.27.2.tgz", { "os": "openbsd", "cpu": "x64" }, "sha512-/it7w9Nb7+0KFIzjalNJVR5bOzA9Vay+yIPLVHfIQYG/j+j9VTH84aNB8ExGKPU4AzfaEvN9/V4HV+F+vo8OEg=="], + + "@esbuild/openharmony-arm64": ["@esbuild/openharmony-arm64@0.27.2", "https://registry.npmmirror.com/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.2.tgz", { "os": "none", "cpu": "arm64" }, "sha512-LRBbCmiU51IXfeXk59csuX/aSaToeG7w48nMwA6049Y4J4+VbWALAuXcs+qcD04rHDuSCSRKdmY63sruDS5qag=="], + + "@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.27.2", "https://registry.npmmirror.com/@esbuild/sunos-x64/-/sunos-x64-0.27.2.tgz", { "os": "sunos", "cpu": "x64" }, "sha512-kMtx1yqJHTmqaqHPAzKCAkDaKsffmXkPHThSfRwZGyuqyIeBvf08KSsYXl+abf5HDAPMJIPnbBfXvP2ZC2TfHg=="], + + "@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.27.2", "https://registry.npmmirror.com/@esbuild/win32-arm64/-/win32-arm64-0.27.2.tgz", { "os": "win32", "cpu": "arm64" }, "sha512-Yaf78O/B3Kkh+nKABUF++bvJv5Ijoy9AN1ww904rOXZFLWVc5OLOfL56W+C8F9xn5JQZa3UX6m+IktJnIb1Jjg=="], + + "@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.27.2", "https://registry.npmmirror.com/@esbuild/win32-ia32/-/win32-ia32-0.27.2.tgz", { "os": "win32", "cpu": "ia32" }, "sha512-Iuws0kxo4yusk7sw70Xa2E2imZU5HoixzxfGCdxwBdhiDgt9vX9VUCBhqcwY7/uh//78A1hMkkROMJq9l27oLQ=="], + + "@esbuild/win32-x64": ["@esbuild/win32-x64@0.27.2", "https://registry.npmmirror.com/@esbuild/win32-x64/-/win32-x64-0.27.2.tgz", { "os": "win32", "cpu": "x64" }, "sha512-sRdU18mcKf7F+YgheI/zGf5alZatMUTKj/jNS6l744f9u3WFu4v7twcUI9vu4mknF4Y9aDlblIie0IM+5xxaqQ=="], + + "@floating-ui/core": ["@floating-ui/core@1.7.4", "https://registry.npmmirror.com/@floating-ui/core/-/core-1.7.4.tgz", { "dependencies": { "@floating-ui/utils": "^0.2.10" } }, "sha512-C3HlIdsBxszvm5McXlB8PeOEWfBhcGBTZGkGlWc2U0KFY5IwG5OQEuQ8rq52DZmcHDlPLd+YFBK+cZcytwIFWg=="], + + "@floating-ui/dom": ["@floating-ui/dom@1.7.5", "https://registry.npmmirror.com/@floating-ui/dom/-/dom-1.7.5.tgz", { "dependencies": { "@floating-ui/core": "^1.7.4", "@floating-ui/utils": "^0.2.10" } }, "sha512-N0bD2kIPInNHUHehXhMke1rBGs1dwqvC9O9KYMyyjK7iXt7GAhnro7UlcuYcGdS/yYOlq0MAVgrow8IbWJwyqg=="], + + "@floating-ui/react-dom": ["@floating-ui/react-dom@2.1.7", "https://registry.npmmirror.com/@floating-ui/react-dom/-/react-dom-2.1.7.tgz", { "dependencies": { "@floating-ui/dom": "^1.7.5" }, "peerDependencies": { "react": ">=16.8.0", "react-dom": ">=16.8.0" } }, "sha512-0tLRojf/1Go2JgEVm+3Frg9A3IW8bJgKgdO0BN5RkF//ufuz2joZM63Npau2ff3J6lUVYgDSNzNkR+aH3IVfjg=="], + + "@floating-ui/utils": ["@floating-ui/utils@0.2.10", "https://registry.npmmirror.com/@floating-ui/utils/-/utils-0.2.10.tgz", {}, "sha512-aGTxbpbg8/b5JfU1HXSrbH3wXZuLPJcNEcZQFMxLs3oSzgtVu6nFPkbbGGUvBcUjKV2YyB9Wxxabo+HEH9tcRQ=="], + + "@jridgewell/gen-mapping": ["@jridgewell/gen-mapping@0.3.13", "https://registry.npmmirror.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.0", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA=="], + + "@jridgewell/remapping": ["@jridgewell/remapping@2.3.5", "https://registry.npmmirror.com/@jridgewell/remapping/-/remapping-2.3.5.tgz", { "dependencies": { "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ=="], + + "@jridgewell/resolve-uri": ["@jridgewell/resolve-uri@3.1.2", "https://registry.npmmirror.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", {}, "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw=="], + + "@jridgewell/sourcemap-codec": ["@jridgewell/sourcemap-codec@1.5.5", "https://registry.npmmirror.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", {}, "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og=="], + + "@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.31", "https://registry.npmmirror.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", { "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw=="], + + "@radix-ui/primitive": ["@radix-ui/primitive@1.1.3", "https://registry.npmmirror.com/@radix-ui/primitive/-/primitive-1.1.3.tgz", {}, "sha512-JTF99U/6XIjCBo0wqkU5sK10glYe27MRRsfwoiq5zzOEZLHU3A3KCMa5X/azekYRCJ0HlwI0crAXS/5dEHTzDg=="], + + "@radix-ui/react-arrow": ["@radix-ui/react-arrow@1.1.7", "https://registry.npmmirror.com/@radix-ui/react-arrow/-/react-arrow-1.1.7.tgz", { "dependencies": { "@radix-ui/react-primitive": "2.1.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-F+M1tLhO+mlQaOWspE8Wstg+z6PwxwRd8oQ8IXceWz92kfAmalTRf0EjrouQeo7QssEPfCn05B4Ihs1K9WQ/7w=="], + + "@radix-ui/react-collection": ["@radix-ui/react-collection@1.1.7", "https://registry.npmmirror.com/@radix-ui/react-collection/-/react-collection-1.1.7.tgz", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-Fh9rGN0MoI4ZFUNyfFVNU4y9LUz93u9/0K+yLgA2bwRojxM8JU1DyvvMBabnZPBgMWREAJvU2jjVzq+LrFUglw=="], + + "@radix-ui/react-compose-refs": ["@radix-ui/react-compose-refs@1.1.2", "https://registry.npmmirror.com/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.2.tgz", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg=="], + + "@radix-ui/react-context": ["@radix-ui/react-context@1.1.2", "https://registry.npmmirror.com/@radix-ui/react-context/-/react-context-1.1.2.tgz", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-jCi/QKUM2r1Ju5a3J64TH2A5SpKAgh0LpknyqdQ4m6DCV0xJ2HG1xARRwNGPQfi1SLdLWZ1OJz6F4OMBBNiGJA=="], + + "@radix-ui/react-dialog": ["@radix-ui/react-dialog@1.1.15", "https://registry.npmmirror.com/@radix-ui/react-dialog/-/react-dialog-1.1.15.tgz", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-dismissable-layer": "1.1.11", "@radix-ui/react-focus-guards": "1.1.3", "@radix-ui/react-focus-scope": "1.1.7", "@radix-ui/react-id": "1.1.1", "@radix-ui/react-portal": "1.1.9", "@radix-ui/react-presence": "1.1.5", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-slot": "1.2.3", "@radix-ui/react-use-controllable-state": "1.2.2", "aria-hidden": "^1.2.4", "react-remove-scroll": "^2.6.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-TCglVRtzlffRNxRMEyR36DGBLJpeusFcgMVD9PZEzAKnUs1lKCgX5u9BmC2Yg+LL9MgZDugFFs1Vl+Jp4t/PGw=="], + + "@radix-ui/react-direction": ["@radix-ui/react-direction@1.1.1", "https://registry.npmmirror.com/@radix-ui/react-direction/-/react-direction-1.1.1.tgz", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-1UEWRX6jnOA2y4H5WczZ44gOOjTEmlqv1uNW4GAJEO5+bauCBhv8snY65Iw5/VOS/ghKN9gr2KjnLKxrsvoMVw=="], + + "@radix-ui/react-dismissable-layer": ["@radix-ui/react-dismissable-layer@1.1.11", "https://registry.npmmirror.com/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.1.11.tgz", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-callback-ref": "1.1.1", "@radix-ui/react-use-escape-keydown": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-Nqcp+t5cTB8BinFkZgXiMJniQH0PsUt2k51FUhbdfeKvc4ACcG2uQniY/8+h1Yv6Kza4Q7lD7PQV0z0oicE0Mg=="], + + "@radix-ui/react-dropdown-menu": ["@radix-ui/react-dropdown-menu@2.1.16", "https://registry.npmmirror.com/@radix-ui/react-dropdown-menu/-/react-dropdown-menu-2.1.16.tgz", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-id": "1.1.1", "@radix-ui/react-menu": "2.1.16", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-controllable-state": "1.2.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-1PLGQEynI/3OX/ftV54COn+3Sud/Mn8vALg2rWnBLnRaGtJDduNW/22XjlGgPdpcIbiQxjKtb7BkcjP00nqfJw=="], + + "@radix-ui/react-focus-guards": ["@radix-ui/react-focus-guards@1.1.3", "https://registry.npmmirror.com/@radix-ui/react-focus-guards/-/react-focus-guards-1.1.3.tgz", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-0rFg/Rj2Q62NCm62jZw0QX7a3sz6QCQU0LpZdNrJX8byRGaGVTqbrW9jAoIAHyMQqsNpeZ81YgSizOt5WXq0Pw=="], + + "@radix-ui/react-focus-scope": ["@radix-ui/react-focus-scope@1.1.7", "https://registry.npmmirror.com/@radix-ui/react-focus-scope/-/react-focus-scope-1.1.7.tgz", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-callback-ref": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-t2ODlkXBQyn7jkl6TNaw/MtVEVvIGelJDCG41Okq/KwUsJBwQ4XVZsHAVUkK4mBv3ewiAS3PGuUWuY2BoK4ZUw=="], + + "@radix-ui/react-id": ["@radix-ui/react-id@1.1.1", "https://registry.npmmirror.com/@radix-ui/react-id/-/react-id-1.1.1.tgz", { "dependencies": { "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-kGkGegYIdQsOb4XjsfM97rXsiHaBwco+hFI66oO4s9LU+PLAC5oJ7khdOVFxkhsmlbpUqDAvXw11CluXP+jkHg=="], + + "@radix-ui/react-menu": ["@radix-ui/react-menu@2.1.16", "https://registry.npmmirror.com/@radix-ui/react-menu/-/react-menu-2.1.16.tgz", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-collection": "1.1.7", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-direction": "1.1.1", "@radix-ui/react-dismissable-layer": "1.1.11", "@radix-ui/react-focus-guards": "1.1.3", "@radix-ui/react-focus-scope": "1.1.7", "@radix-ui/react-id": "1.1.1", "@radix-ui/react-popper": "1.2.8", "@radix-ui/react-portal": "1.1.9", "@radix-ui/react-presence": "1.1.5", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-roving-focus": "1.1.11", "@radix-ui/react-slot": "1.2.3", "@radix-ui/react-use-callback-ref": "1.1.1", "aria-hidden": "^1.2.4", "react-remove-scroll": "^2.6.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-72F2T+PLlphrqLcAotYPp0uJMr5SjP5SL01wfEspJbru5Zs5vQaSHb4VB3ZMJPimgHHCHG7gMOeOB9H3Hdmtxg=="], + + "@radix-ui/react-popper": ["@radix-ui/react-popper@1.2.8", "https://registry.npmmirror.com/@radix-ui/react-popper/-/react-popper-1.2.8.tgz", { "dependencies": { "@floating-ui/react-dom": "^2.0.0", "@radix-ui/react-arrow": "1.1.7", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-callback-ref": "1.1.1", "@radix-ui/react-use-layout-effect": "1.1.1", "@radix-ui/react-use-rect": "1.1.1", "@radix-ui/react-use-size": "1.1.1", "@radix-ui/rect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-0NJQ4LFFUuWkE7Oxf0htBKS6zLkkjBH+hM1uk7Ng705ReR8m/uelduy1DBo0PyBXPKVnBA6YBlU94MBGXrSBCw=="], + + "@radix-ui/react-portal": ["@radix-ui/react-portal@1.1.9", "https://registry.npmmirror.com/@radix-ui/react-portal/-/react-portal-1.1.9.tgz", { "dependencies": { "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-bpIxvq03if6UNwXZ+HTK71JLh4APvnXntDc6XOX8UVq4XQOVl7lwok0AvIl+b8zgCw3fSaVTZMpAPPagXbKmHQ=="], + + "@radix-ui/react-presence": ["@radix-ui/react-presence@1.1.5", "https://registry.npmmirror.com/@radix-ui/react-presence/-/react-presence-1.1.5.tgz", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-/jfEwNDdQVBCNvjkGit4h6pMOzq8bHkopq458dPt2lMjx+eBQUohZNG9A7DtO/O5ukSbxuaNGXMjHicgwy6rQQ=="], + + "@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.3", "https://registry.npmmirror.com/@radix-ui/react-primitive/-/react-primitive-2.1.3.tgz", { "dependencies": { "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ=="], + + "@radix-ui/react-roving-focus": ["@radix-ui/react-roving-focus@1.1.11", "https://registry.npmmirror.com/@radix-ui/react-roving-focus/-/react-roving-focus-1.1.11.tgz", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-collection": "1.1.7", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-direction": "1.1.1", "@radix-ui/react-id": "1.1.1", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-callback-ref": "1.1.1", "@radix-ui/react-use-controllable-state": "1.2.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-7A6S9jSgm/S+7MdtNDSb+IU859vQqJ/QAtcYQcfFC6W8RS4IxIZDldLR0xqCFZ6DCyrQLjLPsxtTNch5jVA4lA=="], + + "@radix-ui/react-slot": ["@radix-ui/react-slot@1.2.4", "https://registry.npmmirror.com/@radix-ui/react-slot/-/react-slot-1.2.4.tgz", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-Jl+bCv8HxKnlTLVrcDE8zTMJ09R9/ukw4qBs/oZClOfoQk/cOTbDn+NceXfV7j09YPVQUryJPHurafcSg6EVKA=="], + + "@radix-ui/react-switch": ["@radix-ui/react-switch@1.2.6", "https://registry.npmmirror.com/@radix-ui/react-switch/-/react-switch-1.2.6.tgz", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-controllable-state": "1.2.2", "@radix-ui/react-use-previous": "1.1.1", "@radix-ui/react-use-size": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-bByzr1+ep1zk4VubeEVViV592vu2lHE2BZY5OnzehZqOOgogN80+mNtCqPkhn2gklJqOpxWgPoYTSnhBCqpOXQ=="], + + "@radix-ui/react-toast": ["@radix-ui/react-toast@1.2.15", "https://registry.npmmirror.com/@radix-ui/react-toast/-/react-toast-1.2.15.tgz", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-collection": "1.1.7", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-dismissable-layer": "1.1.11", "@radix-ui/react-portal": "1.1.9", "@radix-ui/react-presence": "1.1.5", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-callback-ref": "1.1.1", "@radix-ui/react-use-controllable-state": "1.2.2", "@radix-ui/react-use-layout-effect": "1.1.1", "@radix-ui/react-visually-hidden": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-3OSz3TacUWy4WtOXV38DggwxoqJK4+eDkNMl5Z/MJZaoUPaP4/9lf81xXMe1I2ReTAptverZUpbPY4wWwWyL5g=="], + + "@radix-ui/react-tooltip": ["@radix-ui/react-tooltip@1.2.8", "https://registry.npmmirror.com/@radix-ui/react-tooltip/-/react-tooltip-1.2.8.tgz", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-dismissable-layer": "1.1.11", "@radix-ui/react-id": "1.1.1", "@radix-ui/react-popper": "1.2.8", "@radix-ui/react-portal": "1.1.9", "@radix-ui/react-presence": "1.1.5", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-slot": "1.2.3", "@radix-ui/react-use-controllable-state": "1.2.2", "@radix-ui/react-visually-hidden": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-tY7sVt1yL9ozIxvmbtN5qtmH2krXcBCfjEiCgKGLqunJHvgvZG2Pcl2oQ3kbcZARb1BGEHdkLzcYGO8ynVlieg=="], + + "@radix-ui/react-use-callback-ref": ["@radix-ui/react-use-callback-ref@1.1.1", "https://registry.npmmirror.com/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.1.1.tgz", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-FkBMwD+qbGQeMu1cOHnuGB6x4yzPjho8ap5WtbEJ26umhgqVXbhekKUQO+hZEL1vU92a3wHwdp0HAcqAUF5iDg=="], + + "@radix-ui/react-use-controllable-state": ["@radix-ui/react-use-controllable-state@1.2.2", "https://registry.npmmirror.com/@radix-ui/react-use-controllable-state/-/react-use-controllable-state-1.2.2.tgz", { "dependencies": { "@radix-ui/react-use-effect-event": "0.0.2", "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-BjasUjixPFdS+NKkypcyyN5Pmg83Olst0+c6vGov0diwTEo6mgdqVR6hxcEgFuh4QrAs7Rc+9KuGJ9TVCj0Zzg=="], + + "@radix-ui/react-use-effect-event": ["@radix-ui/react-use-effect-event@0.0.2", "https://registry.npmmirror.com/@radix-ui/react-use-effect-event/-/react-use-effect-event-0.0.2.tgz", { "dependencies": { "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-Qp8WbZOBe+blgpuUT+lw2xheLP8q0oatc9UpmiemEICxGvFLYmHm9QowVZGHtJlGbS6A6yJ3iViad/2cVjnOiA=="], + + "@radix-ui/react-use-escape-keydown": ["@radix-ui/react-use-escape-keydown@1.1.1", "https://registry.npmmirror.com/@radix-ui/react-use-escape-keydown/-/react-use-escape-keydown-1.1.1.tgz", { "dependencies": { "@radix-ui/react-use-callback-ref": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-Il0+boE7w/XebUHyBjroE+DbByORGR9KKmITzbR7MyQ4akpORYP/ZmbhAr0DG7RmmBqoOnZdy2QlvajJ2QA59g=="], + + "@radix-ui/react-use-layout-effect": ["@radix-ui/react-use-layout-effect@1.1.1", "https://registry.npmmirror.com/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-1.1.1.tgz", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ=="], + + "@radix-ui/react-use-previous": ["@radix-ui/react-use-previous@1.1.1", "https://registry.npmmirror.com/@radix-ui/react-use-previous/-/react-use-previous-1.1.1.tgz", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-2dHfToCj/pzca2Ck724OZ5L0EVrr3eHRNsG/b3xQJLA2hZpVCS99bLAX+hm1IHXDEnzU6by5z/5MIY794/a8NQ=="], + + "@radix-ui/react-use-rect": ["@radix-ui/react-use-rect@1.1.1", "https://registry.npmmirror.com/@radix-ui/react-use-rect/-/react-use-rect-1.1.1.tgz", { "dependencies": { "@radix-ui/rect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-QTYuDesS0VtuHNNvMh+CjlKJ4LJickCMUAqjlE3+j8w+RlRpwyX3apEQKGFzbZGdo7XNG1tXa+bQqIE7HIXT2w=="], + + "@radix-ui/react-use-size": ["@radix-ui/react-use-size@1.1.1", "https://registry.npmmirror.com/@radix-ui/react-use-size/-/react-use-size-1.1.1.tgz", { "dependencies": { "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-ewrXRDTAqAXlkl6t/fkXWNAhFX9I+CkKlw6zjEwk86RSPKwZr3xpBRso655aqYafwtnbpHLj6toFzmd6xdVptQ=="], + + "@radix-ui/react-visually-hidden": ["@radix-ui/react-visually-hidden@1.2.3", "https://registry.npmmirror.com/@radix-ui/react-visually-hidden/-/react-visually-hidden-1.2.3.tgz", { "dependencies": { "@radix-ui/react-primitive": "2.1.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-pzJq12tEaaIhqjbzpCuv/OypJY/BPavOofm+dbab+MHLajy277+1lLm6JFcGgF5eskJ6mquGirhXY2GD/8u8Ug=="], + + "@radix-ui/rect": ["@radix-ui/rect@1.1.1", "https://registry.npmmirror.com/@radix-ui/rect/-/rect-1.1.1.tgz", {}, "sha512-HPwpGIzkl28mWyZqG52jiqDJ12waP11Pa1lGoiyUkIEuMLBP0oeK/C89esbXrxsky5we7dfd8U58nm0SgAWpVw=="], + + "@rolldown/pluginutils": ["@rolldown/pluginutils@1.0.0-beta.47", "https://registry.npmmirror.com/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.47.tgz", {}, "sha512-8QagwMH3kNCuzD8EWL8R2YPW5e4OrHNSAHRFDdmFqEwEaD/KcNKjVoumo+gP2vW5eKB2UPbM6vTYiGZX0ixLnw=="], + + "@rollup/rollup-android-arm-eabi": ["@rollup/rollup-android-arm-eabi@4.57.1", "https://registry.npmmirror.com/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.57.1.tgz", { "os": "android", "cpu": "arm" }, "sha512-A6ehUVSiSaaliTxai040ZpZ2zTevHYbvu/lDoeAteHI8QnaosIzm4qwtezfRg1jOYaUmnzLX1AOD6Z+UJjtifg=="], + + "@rollup/rollup-android-arm64": ["@rollup/rollup-android-arm64@4.57.1", "https://registry.npmmirror.com/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.57.1.tgz", { "os": "android", "cpu": "arm64" }, "sha512-dQaAddCY9YgkFHZcFNS/606Exo8vcLHwArFZ7vxXq4rigo2bb494/xKMMwRRQW6ug7Js6yXmBZhSBRuBvCCQ3w=="], + + "@rollup/rollup-darwin-arm64": ["@rollup/rollup-darwin-arm64@4.57.1", "https://registry.npmmirror.com/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.57.1.tgz", { "os": "darwin", "cpu": "arm64" }, "sha512-crNPrwJOrRxagUYeMn/DZwqN88SDmwaJ8Cvi/TN1HnWBU7GwknckyosC2gd0IqYRsHDEnXf328o9/HC6OkPgOg=="], + + "@rollup/rollup-darwin-x64": ["@rollup/rollup-darwin-x64@4.57.1", "https://registry.npmmirror.com/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.57.1.tgz", { "os": "darwin", "cpu": "x64" }, "sha512-Ji8g8ChVbKrhFtig5QBV7iMaJrGtpHelkB3lsaKzadFBe58gmjfGXAOfI5FV0lYMH8wiqsxKQ1C9B0YTRXVy4w=="], + + "@rollup/rollup-freebsd-arm64": ["@rollup/rollup-freebsd-arm64@4.57.1", "https://registry.npmmirror.com/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.57.1.tgz", { "os": "freebsd", "cpu": "arm64" }, "sha512-R+/WwhsjmwodAcz65guCGFRkMb4gKWTcIeLy60JJQbXrJ97BOXHxnkPFrP+YwFlaS0m+uWJTstrUA9o+UchFug=="], + + "@rollup/rollup-freebsd-x64": ["@rollup/rollup-freebsd-x64@4.57.1", "https://registry.npmmirror.com/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.57.1.tgz", { "os": "freebsd", "cpu": "x64" }, "sha512-IEQTCHeiTOnAUC3IDQdzRAGj3jOAYNr9kBguI7MQAAZK3caezRrg0GxAb6Hchg4lxdZEI5Oq3iov/w/hnFWY9Q=="], + + "@rollup/rollup-linux-arm-gnueabihf": ["@rollup/rollup-linux-arm-gnueabihf@4.57.1", "https://registry.npmmirror.com/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.57.1.tgz", { "os": "linux", "cpu": "arm" }, "sha512-F8sWbhZ7tyuEfsmOxwc2giKDQzN3+kuBLPwwZGyVkLlKGdV1nvnNwYD0fKQ8+XS6hp9nY7B+ZeK01EBUE7aHaw=="], + + "@rollup/rollup-linux-arm-musleabihf": ["@rollup/rollup-linux-arm-musleabihf@4.57.1", "https://registry.npmmirror.com/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.57.1.tgz", { "os": "linux", "cpu": "arm" }, "sha512-rGfNUfn0GIeXtBP1wL5MnzSj98+PZe/AXaGBCRmT0ts80lU5CATYGxXukeTX39XBKsxzFpEeK+Mrp9faXOlmrw=="], + + "@rollup/rollup-linux-arm64-gnu": ["@rollup/rollup-linux-arm64-gnu@4.57.1", "https://registry.npmmirror.com/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.57.1.tgz", { "os": "linux", "cpu": "arm64" }, "sha512-MMtej3YHWeg/0klK2Qodf3yrNzz6CGjo2UntLvk2RSPlhzgLvYEB3frRvbEF2wRKh1Z2fDIg9KRPe1fawv7C+g=="], + + "@rollup/rollup-linux-arm64-musl": ["@rollup/rollup-linux-arm64-musl@4.57.1", "https://registry.npmmirror.com/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.57.1.tgz", { "os": "linux", "cpu": "arm64" }, "sha512-1a/qhaaOXhqXGpMFMET9VqwZakkljWHLmZOX48R0I/YLbhdxr1m4gtG1Hq7++VhVUmf+L3sTAf9op4JlhQ5u1Q=="], + + "@rollup/rollup-linux-loong64-gnu": ["@rollup/rollup-linux-loong64-gnu@4.57.1", "https://registry.npmmirror.com/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.57.1.tgz", { "os": "linux", "cpu": "none" }, "sha512-QWO6RQTZ/cqYtJMtxhkRkidoNGXc7ERPbZN7dVW5SdURuLeVU7lwKMpo18XdcmpWYd0qsP1bwKPf7DNSUinhvA=="], + + "@rollup/rollup-linux-loong64-musl": ["@rollup/rollup-linux-loong64-musl@4.57.1", "https://registry.npmmirror.com/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.57.1.tgz", { "os": "linux", "cpu": "none" }, "sha512-xpObYIf+8gprgWaPP32xiN5RVTi/s5FCR+XMXSKmhfoJjrpRAjCuuqQXyxUa/eJTdAE6eJ+KDKaoEqjZQxh3Gw=="], + + "@rollup/rollup-linux-ppc64-gnu": ["@rollup/rollup-linux-ppc64-gnu@4.57.1", "https://registry.npmmirror.com/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.57.1.tgz", { "os": "linux", "cpu": "ppc64" }, "sha512-4BrCgrpZo4hvzMDKRqEaW1zeecScDCR+2nZ86ATLhAoJ5FQ+lbHVD3ttKe74/c7tNT9c6F2viwB3ufwp01Oh2w=="], + + "@rollup/rollup-linux-ppc64-musl": ["@rollup/rollup-linux-ppc64-musl@4.57.1", "https://registry.npmmirror.com/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.57.1.tgz", { "os": "linux", "cpu": "ppc64" }, "sha512-NOlUuzesGauESAyEYFSe3QTUguL+lvrN1HtwEEsU2rOwdUDeTMJdO5dUYl/2hKf9jWydJrO9OL/XSSf65R5+Xw=="], + + "@rollup/rollup-linux-riscv64-gnu": ["@rollup/rollup-linux-riscv64-gnu@4.57.1", "https://registry.npmmirror.com/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.57.1.tgz", { "os": "linux", "cpu": "none" }, "sha512-ptA88htVp0AwUUqhVghwDIKlvJMD/fmL/wrQj99PRHFRAG6Z5nbWoWG4o81Nt9FT+IuqUQi+L31ZKAFeJ5Is+A=="], + + "@rollup/rollup-linux-riscv64-musl": ["@rollup/rollup-linux-riscv64-musl@4.57.1", "https://registry.npmmirror.com/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.57.1.tgz", { "os": "linux", "cpu": "none" }, "sha512-S51t7aMMTNdmAMPpBg7OOsTdn4tySRQvklmL3RpDRyknk87+Sp3xaumlatU+ppQ+5raY7sSTcC2beGgvhENfuw=="], + + "@rollup/rollup-linux-s390x-gnu": ["@rollup/rollup-linux-s390x-gnu@4.57.1", "https://registry.npmmirror.com/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.57.1.tgz", { "os": "linux", "cpu": "s390x" }, "sha512-Bl00OFnVFkL82FHbEqy3k5CUCKH6OEJL54KCyx2oqsmZnFTR8IoNqBF+mjQVcRCT5sB6yOvK8A37LNm/kPJiZg=="], + + "@rollup/rollup-linux-x64-gnu": ["@rollup/rollup-linux-x64-gnu@4.57.1", "https://registry.npmmirror.com/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.57.1.tgz", { "os": "linux", "cpu": "x64" }, "sha512-ABca4ceT4N+Tv/GtotnWAeXZUZuM/9AQyCyKYyKnpk4yoA7QIAuBt6Hkgpw8kActYlew2mvckXkvx0FfoInnLg=="], + + "@rollup/rollup-linux-x64-musl": ["@rollup/rollup-linux-x64-musl@4.57.1", "https://registry.npmmirror.com/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.57.1.tgz", { "os": "linux", "cpu": "x64" }, "sha512-HFps0JeGtuOR2convgRRkHCekD7j+gdAuXM+/i6kGzQtFhlCtQkpwtNzkNj6QhCDp7DRJ7+qC/1Vg2jt5iSOFw=="], + + "@rollup/rollup-openbsd-x64": ["@rollup/rollup-openbsd-x64@4.57.1", "https://registry.npmmirror.com/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.57.1.tgz", { "os": "openbsd", "cpu": "x64" }, "sha512-H+hXEv9gdVQuDTgnqD+SQffoWoc0Of59AStSzTEj/feWTBAnSfSD3+Dql1ZruJQxmykT/JVY0dE8Ka7z0DH1hw=="], + + "@rollup/rollup-openharmony-arm64": ["@rollup/rollup-openharmony-arm64@4.57.1", "https://registry.npmmirror.com/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.57.1.tgz", { "os": "none", "cpu": "arm64" }, "sha512-4wYoDpNg6o/oPximyc/NG+mYUejZrCU2q+2w6YZqrAs2UcNUChIZXjtafAiiZSUc7On8v5NyNj34Kzj/Ltk6dQ=="], + + "@rollup/rollup-win32-arm64-msvc": ["@rollup/rollup-win32-arm64-msvc@4.57.1", "https://registry.npmmirror.com/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.57.1.tgz", { "os": "win32", "cpu": "arm64" }, "sha512-O54mtsV/6LW3P8qdTcamQmuC990HDfR71lo44oZMZlXU4tzLrbvTii87Ni9opq60ds0YzuAlEr/GNwuNluZyMQ=="], + + "@rollup/rollup-win32-ia32-msvc": ["@rollup/rollup-win32-ia32-msvc@4.57.1", "https://registry.npmmirror.com/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.57.1.tgz", { "os": "win32", "cpu": "ia32" }, "sha512-P3dLS+IerxCT/7D2q2FYcRdWRl22dNbrbBEtxdWhXrfIMPP9lQhb5h4Du04mdl5Woq05jVCDPCMF7Ub0NAjIew=="], + + "@rollup/rollup-win32-x64-gnu": ["@rollup/rollup-win32-x64-gnu@4.57.1", "https://registry.npmmirror.com/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.57.1.tgz", { "os": "win32", "cpu": "x64" }, "sha512-VMBH2eOOaKGtIJYleXsi2B8CPVADrh+TyNxJ4mWPnKfLB/DBUmzW+5m1xUrcwWoMfSLagIRpjUFeW5CO5hyciQ=="], + + "@rollup/rollup-win32-x64-msvc": ["@rollup/rollup-win32-x64-msvc@4.57.1", "https://registry.npmmirror.com/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.57.1.tgz", { "os": "win32", "cpu": "x64" }, "sha512-mxRFDdHIWRxg3UfIIAwCm6NzvxG0jDX/wBN6KsQFTvKFqqg9vTrWUE68qEjHt19A5wwx5X5aUi2zuZT7YR0jrA=="], + + "@swc/core": ["@swc/core@1.15.11", "https://registry.npmmirror.com/@swc/core/-/core-1.15.11.tgz", { "dependencies": { "@swc/counter": "^0.1.3", "@swc/types": "^0.1.25" }, "optionalDependencies": { "@swc/core-darwin-arm64": "1.15.11", "@swc/core-darwin-x64": "1.15.11", "@swc/core-linux-arm-gnueabihf": "1.15.11", "@swc/core-linux-arm64-gnu": "1.15.11", "@swc/core-linux-arm64-musl": "1.15.11", "@swc/core-linux-x64-gnu": "1.15.11", "@swc/core-linux-x64-musl": "1.15.11", "@swc/core-win32-arm64-msvc": "1.15.11", "@swc/core-win32-ia32-msvc": "1.15.11", "@swc/core-win32-x64-msvc": "1.15.11" }, "peerDependencies": { "@swc/helpers": ">=0.5.17" }, "optionalPeers": ["@swc/helpers"] }, "sha512-iLmLTodbYxU39HhMPaMUooPwO/zqJWvsqkrXv1ZI38rMb048p6N7qtAtTp37sw9NzSrvH6oli8EdDygo09IZ/w=="], + + "@swc/core-darwin-arm64": ["@swc/core-darwin-arm64@1.15.11", "https://registry.npmmirror.com/@swc/core-darwin-arm64/-/core-darwin-arm64-1.15.11.tgz", { "os": "darwin", "cpu": "arm64" }, "sha512-QoIupRWVH8AF1TgxYyeA5nS18dtqMuxNwchjBIwJo3RdwLEFiJq6onOx9JAxHtuPwUkIVuU2Xbp+jCJ7Vzmgtg=="], + + "@swc/core-darwin-x64": ["@swc/core-darwin-x64@1.15.11", "https://registry.npmmirror.com/@swc/core-darwin-x64/-/core-darwin-x64-1.15.11.tgz", { "os": "darwin", "cpu": "x64" }, "sha512-S52Gu1QtPSfBYDiejlcfp9GlN+NjTZBRRNsz8PNwBgSE626/FUf2PcllVUix7jqkoMC+t0rS8t+2/aSWlMuQtA=="], + + "@swc/core-linux-arm-gnueabihf": ["@swc/core-linux-arm-gnueabihf@1.15.11", "https://registry.npmmirror.com/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.15.11.tgz", { "os": "linux", "cpu": "arm" }, "sha512-lXJs8oXo6Z4yCpimpQ8vPeCjkgoHu5NoMvmJZ8qxDyU99KVdg6KwU9H79vzrmB+HfH+dCZ7JGMqMF//f8Cfvdg=="], + + "@swc/core-linux-arm64-gnu": ["@swc/core-linux-arm64-gnu@1.15.11", "https://registry.npmmirror.com/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.15.11.tgz", { "os": "linux", "cpu": "arm64" }, "sha512-chRsz1K52/vj8Mfq/QOugVphlKPWlMh10V99qfH41hbGvwAU6xSPd681upO4bKiOr9+mRIZZW+EfJqY42ZzRyA=="], + + "@swc/core-linux-arm64-musl": ["@swc/core-linux-arm64-musl@1.15.11", "https://registry.npmmirror.com/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.15.11.tgz", { "os": "linux", "cpu": "arm64" }, "sha512-PYftgsTaGnfDK4m6/dty9ryK1FbLk+LosDJ/RJR2nkXGc8rd+WenXIlvHjWULiBVnS1RsjHHOXmTS4nDhe0v0w=="], + + "@swc/core-linux-x64-gnu": ["@swc/core-linux-x64-gnu@1.15.11", "https://registry.npmmirror.com/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.15.11.tgz", { "os": "linux", "cpu": "x64" }, "sha512-DKtnJKIHiZdARyTKiX7zdRjiDS1KihkQWatQiCHMv+zc2sfwb4Glrodx2VLOX4rsa92NLR0Sw8WLcPEMFY1szQ=="], + + "@swc/core-linux-x64-musl": ["@swc/core-linux-x64-musl@1.15.11", "https://registry.npmmirror.com/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.15.11.tgz", { "os": "linux", "cpu": "x64" }, "sha512-mUjjntHj4+8WBaiDe5UwRNHuEzLjIWBTSGTw0JT9+C9/Yyuh4KQqlcEQ3ro6GkHmBGXBFpGIj/o5VMyRWfVfWw=="], + + "@swc/core-win32-arm64-msvc": ["@swc/core-win32-arm64-msvc@1.15.11", "https://registry.npmmirror.com/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.15.11.tgz", { "os": "win32", "cpu": "arm64" }, "sha512-ZkNNG5zL49YpaFzfl6fskNOSxtcZ5uOYmWBkY4wVAvgbSAQzLRVBp+xArGWh2oXlY/WgL99zQSGTv7RI5E6nzA=="], + + "@swc/core-win32-ia32-msvc": ["@swc/core-win32-ia32-msvc@1.15.11", "https://registry.npmmirror.com/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.15.11.tgz", { "os": "win32", "cpu": "ia32" }, "sha512-6XnzORkZCQzvTQ6cPrU7iaT9+i145oLwnin8JrfsLG41wl26+5cNQ2XV3zcbrnFEV6esjOceom9YO1w9mGJByw=="], + + "@swc/core-win32-x64-msvc": ["@swc/core-win32-x64-msvc@1.15.11", "https://registry.npmmirror.com/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.15.11.tgz", { "os": "win32", "cpu": "x64" }, "sha512-IQ2n6af7XKLL6P1gIeZACskSxK8jWtoKpJWLZmdXTDj1MGzktUy4i+FvpdtxFmJWNavRWH1VmTr6kAubRDHeKw=="], + + "@swc/counter": ["@swc/counter@0.1.3", "https://registry.npmmirror.com/@swc/counter/-/counter-0.1.3.tgz", {}, "sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ=="], + + "@swc/types": ["@swc/types@0.1.25", "https://registry.npmmirror.com/@swc/types/-/types-0.1.25.tgz", { "dependencies": { "@swc/counter": "^0.1.3" } }, "sha512-iAoY/qRhNH8a/hBvm3zKj9qQ4oc2+3w1unPJa2XvTK3XjeLXtzcCingVPw/9e5mn1+0yPqxcBGp9Jf0pkfMb1g=="], + + "@tailwindcss/node": ["@tailwindcss/node@4.1.18", "https://registry.npmmirror.com/@tailwindcss/node/-/node-4.1.18.tgz", { "dependencies": { "@jridgewell/remapping": "^2.3.4", "enhanced-resolve": "^5.18.3", "jiti": "^2.6.1", "lightningcss": "1.30.2", "magic-string": "^0.30.21", "source-map-js": "^1.2.1", "tailwindcss": "4.1.18" } }, "sha512-DoR7U1P7iYhw16qJ49fgXUlry1t4CpXeErJHnQ44JgTSKMaZUdf17cfn5mHchfJ4KRBZRFA/Coo+MUF5+gOaCQ=="], + + "@tailwindcss/oxide": ["@tailwindcss/oxide@4.1.18", "https://registry.npmmirror.com/@tailwindcss/oxide/-/oxide-4.1.18.tgz", { "optionalDependencies": { "@tailwindcss/oxide-android-arm64": "4.1.18", "@tailwindcss/oxide-darwin-arm64": "4.1.18", "@tailwindcss/oxide-darwin-x64": "4.1.18", "@tailwindcss/oxide-freebsd-x64": "4.1.18", "@tailwindcss/oxide-linux-arm-gnueabihf": "4.1.18", "@tailwindcss/oxide-linux-arm64-gnu": "4.1.18", "@tailwindcss/oxide-linux-arm64-musl": "4.1.18", "@tailwindcss/oxide-linux-x64-gnu": "4.1.18", "@tailwindcss/oxide-linux-x64-musl": "4.1.18", "@tailwindcss/oxide-wasm32-wasi": "4.1.18", "@tailwindcss/oxide-win32-arm64-msvc": "4.1.18", "@tailwindcss/oxide-win32-x64-msvc": "4.1.18" } }, "sha512-EgCR5tTS5bUSKQgzeMClT6iCY3ToqE1y+ZB0AKldj809QXk1Y+3jB0upOYZrn9aGIzPtUsP7sX4QQ4XtjBB95A=="], + + "@tailwindcss/oxide-android-arm64": ["@tailwindcss/oxide-android-arm64@4.1.18", "https://registry.npmmirror.com/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.1.18.tgz", { "os": "android", "cpu": "arm64" }, "sha512-dJHz7+Ugr9U/diKJA0W6N/6/cjI+ZTAoxPf9Iz9BFRF2GzEX8IvXxFIi/dZBloVJX/MZGvRuFA9rqwdiIEZQ0Q=="], + + "@tailwindcss/oxide-darwin-arm64": ["@tailwindcss/oxide-darwin-arm64@4.1.18", "https://registry.npmmirror.com/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.1.18.tgz", { "os": "darwin", "cpu": "arm64" }, "sha512-Gc2q4Qhs660bhjyBSKgq6BYvwDz4G+BuyJ5H1xfhmDR3D8HnHCmT/BSkvSL0vQLy/nkMLY20PQ2OoYMO15Jd0A=="], + + "@tailwindcss/oxide-darwin-x64": ["@tailwindcss/oxide-darwin-x64@4.1.18", "https://registry.npmmirror.com/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.1.18.tgz", { "os": "darwin", "cpu": "x64" }, "sha512-FL5oxr2xQsFrc3X9o1fjHKBYBMD1QZNyc1Xzw/h5Qu4XnEBi3dZn96HcHm41c/euGV+GRiXFfh2hUCyKi/e+yw=="], + + "@tailwindcss/oxide-freebsd-x64": ["@tailwindcss/oxide-freebsd-x64@4.1.18", "https://registry.npmmirror.com/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.1.18.tgz", { "os": "freebsd", "cpu": "x64" }, "sha512-Fj+RHgu5bDodmV1dM9yAxlfJwkkWvLiRjbhuO2LEtwtlYlBgiAT4x/j5wQr1tC3SANAgD+0YcmWVrj8R9trVMA=="], + + "@tailwindcss/oxide-linux-arm-gnueabihf": ["@tailwindcss/oxide-linux-arm-gnueabihf@4.1.18", "https://registry.npmmirror.com/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.1.18.tgz", { "os": "linux", "cpu": "arm" }, "sha512-Fp+Wzk/Ws4dZn+LV2Nqx3IilnhH51YZoRaYHQsVq3RQvEl+71VGKFpkfHrLM/Li+kt5c0DJe/bHXK1eHgDmdiA=="], + + "@tailwindcss/oxide-linux-arm64-gnu": ["@tailwindcss/oxide-linux-arm64-gnu@4.1.18", "https://registry.npmmirror.com/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.1.18.tgz", { "os": "linux", "cpu": "arm64" }, "sha512-S0n3jboLysNbh55Vrt7pk9wgpyTTPD0fdQeh7wQfMqLPM/Hrxi+dVsLsPrycQjGKEQk85Kgbx+6+QnYNiHalnw=="], + + "@tailwindcss/oxide-linux-arm64-musl": ["@tailwindcss/oxide-linux-arm64-musl@4.1.18", "https://registry.npmmirror.com/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.1.18.tgz", { "os": "linux", "cpu": "arm64" }, "sha512-1px92582HkPQlaaCkdRcio71p8bc8i/ap5807tPRDK/uw953cauQBT8c5tVGkOwrHMfc2Yh6UuxaH4vtTjGvHg=="], + + "@tailwindcss/oxide-linux-x64-gnu": ["@tailwindcss/oxide-linux-x64-gnu@4.1.18", "https://registry.npmmirror.com/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.1.18.tgz", { "os": "linux", "cpu": "x64" }, "sha512-v3gyT0ivkfBLoZGF9LyHmts0Isc8jHZyVcbzio6Wpzifg/+5ZJpDiRiUhDLkcr7f/r38SWNe7ucxmGW3j3Kb/g=="], + + "@tailwindcss/oxide-linux-x64-musl": ["@tailwindcss/oxide-linux-x64-musl@4.1.18", "https://registry.npmmirror.com/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.1.18.tgz", { "os": "linux", "cpu": "x64" }, "sha512-bhJ2y2OQNlcRwwgOAGMY0xTFStt4/wyU6pvI6LSuZpRgKQwxTec0/3Scu91O8ir7qCR3AuepQKLU/kX99FouqQ=="], + + "@tailwindcss/oxide-wasm32-wasi": ["@tailwindcss/oxide-wasm32-wasi@4.1.18", "https://registry.npmmirror.com/@tailwindcss/oxide-wasm32-wasi/-/oxide-wasm32-wasi-4.1.18.tgz", { "dependencies": { "@emnapi/core": "^1.7.1", "@emnapi/runtime": "^1.7.1", "@emnapi/wasi-threads": "^1.1.0", "@napi-rs/wasm-runtime": "^1.1.0", "@tybys/wasm-util": "^0.10.1", "tslib": "^2.4.0" }, "cpu": "none" }, "sha512-LffYTvPjODiP6PT16oNeUQJzNVyJl1cjIebq/rWWBF+3eDst5JGEFSc5cWxyRCJ0Mxl+KyIkqRxk1XPEs9x8TA=="], + + "@tailwindcss/oxide-win32-arm64-msvc": ["@tailwindcss/oxide-win32-arm64-msvc@4.1.18", "https://registry.npmmirror.com/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.1.18.tgz", { "os": "win32", "cpu": "arm64" }, "sha512-HjSA7mr9HmC8fu6bdsZvZ+dhjyGCLdotjVOgLA2vEqxEBZaQo9YTX4kwgEvPCpRh8o4uWc4J/wEoFzhEmjvPbA=="], + + "@tailwindcss/oxide-win32-x64-msvc": ["@tailwindcss/oxide-win32-x64-msvc@4.1.18", "https://registry.npmmirror.com/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.1.18.tgz", { "os": "win32", "cpu": "x64" }, "sha512-bJWbyYpUlqamC8dpR7pfjA0I7vdF6t5VpUGMWRkXVE3AXgIZjYUYAK7II1GNaxR8J1SSrSrppRar8G++JekE3Q=="], + + "@tailwindcss/postcss": ["@tailwindcss/postcss@4.1.18", "https://registry.npmmirror.com/@tailwindcss/postcss/-/postcss-4.1.18.tgz", { "dependencies": { "@alloc/quick-lru": "^5.2.0", "@tailwindcss/node": "4.1.18", "@tailwindcss/oxide": "4.1.18", "postcss": "^8.4.41", "tailwindcss": "4.1.18" } }, "sha512-Ce0GFnzAOuPyfV5SxjXGn0CubwGcuDB0zcdaPuCSzAa/2vII24JTkH+I6jcbXLb1ctjZMZZI6OjDaLPJQL1S0g=="], + + "@tanstack/query-core": ["@tanstack/query-core@5.90.20", "https://registry.npmmirror.com/@tanstack/query-core/-/query-core-5.90.20.tgz", {}, "sha512-OMD2HLpNouXEfZJWcKeVKUgQ5n+n3A2JFmBaScpNDUqSrQSjiveC7dKMe53uJUg1nDG16ttFPz2xfilz6i2uVg=="], + + "@tanstack/react-query": ["@tanstack/react-query@5.90.20", "https://registry.npmmirror.com/@tanstack/react-query/-/react-query-5.90.20.tgz", { "dependencies": { "@tanstack/query-core": "5.90.20" }, "peerDependencies": { "react": "^18 || ^19" } }, "sha512-vXBxa+qeyveVO7OA0jX1z+DeyCA4JKnThKv411jd5SORpBKgkcVnYKCiBgECvADvniBX7tobwBmg01qq9JmMJw=="], + + "@types/estree": ["@types/estree@1.0.8", "https://registry.npmmirror.com/@types/estree/-/estree-1.0.8.tgz", {}, "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w=="], + + "@types/react": ["@types/react@19.2.10", "https://registry.npmmirror.com/@types/react/-/react-19.2.10.tgz", { "dependencies": { "csstype": "^3.2.2" } }, "sha512-WPigyYuGhgZ/cTPRXB2EwUw+XvsRA3GqHlsP4qteqrnnjDrApbS7MxcGr/hke5iUoeB7E/gQtrs9I37zAJ0Vjw=="], + + "@types/react-dom": ["@types/react-dom@19.2.3", "https://registry.npmmirror.com/@types/react-dom/-/react-dom-19.2.3.tgz", { "peerDependencies": { "@types/react": "^19.2.0" } }, "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ=="], + + "@vitejs/plugin-react-swc": ["@vitejs/plugin-react-swc@4.2.2", "https://registry.npmmirror.com/@vitejs/plugin-react-swc/-/plugin-react-swc-4.2.2.tgz", { "dependencies": { "@rolldown/pluginutils": "1.0.0-beta.47", "@swc/core": "^1.13.5" }, "peerDependencies": { "vite": "^4 || ^5 || ^6 || ^7" } }, "sha512-x+rE6tsxq/gxrEJN3Nv3dIV60lFflPj94c90b+NNo6n1QV1QQUTLoL0MpaOVasUZ0zqVBn7ead1B5ecx1JAGfA=="], + + "aria-hidden": ["aria-hidden@1.2.6", "https://registry.npmmirror.com/aria-hidden/-/aria-hidden-1.2.6.tgz", { "dependencies": { "tslib": "^2.0.0" } }, "sha512-ik3ZgC9dY/lYVVM++OISsaYDeg1tb0VtP5uL3ouh1koGOaUMDPpbFIei4JkFimWUFPn90sbMNMXQAIVOlnYKJA=="], + + "asynckit": ["asynckit@0.4.0", "https://registry.npmmirror.com/asynckit/-/asynckit-0.4.0.tgz", {}, "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="], + + "autoprefixer": ["autoprefixer@10.4.24", "https://registry.npmmirror.com/autoprefixer/-/autoprefixer-10.4.24.tgz", { "dependencies": { "browserslist": "^4.28.1", "caniuse-lite": "^1.0.30001766", "fraction.js": "^5.3.4", "picocolors": "^1.1.1", "postcss-value-parser": "^4.2.0" }, "peerDependencies": { "postcss": "^8.1.0" }, "bin": { "autoprefixer": "bin/autoprefixer" } }, "sha512-uHZg7N9ULTVbutaIsDRoUkoS8/h3bdsmVJYZ5l3wv8Cp/6UIIoRDm90hZ+BwxUj/hGBEzLxdHNSKuFpn8WOyZw=="], + + "axios": ["axios@1.13.4", "https://registry.npmmirror.com/axios/-/axios-1.13.4.tgz", { "dependencies": { "follow-redirects": "^1.15.6", "form-data": "^4.0.4", "proxy-from-env": "^1.1.0" } }, "sha512-1wVkUaAO6WyaYtCkcYCOx12ZgpGf9Zif+qXa4n+oYzK558YryKqiL6UWwd5DqiH3VRW0GYhTZQ/vlgJrCoNQlg=="], + + "baseline-browser-mapping": ["baseline-browser-mapping@2.9.19", "https://registry.npmmirror.com/baseline-browser-mapping/-/baseline-browser-mapping-2.9.19.tgz", { "bin": { "baseline-browser-mapping": "dist/cli.js" } }, "sha512-ipDqC8FrAl/76p2SSWKSI+H9tFwm7vYqXQrItCuiVPt26Km0jS+NzSsBWAaBusvSbQcfJG+JitdMm+wZAgTYqg=="], + + "browserslist": ["browserslist@4.28.1", "https://registry.npmmirror.com/browserslist/-/browserslist-4.28.1.tgz", { "dependencies": { "baseline-browser-mapping": "^2.9.0", "caniuse-lite": "^1.0.30001759", "electron-to-chromium": "^1.5.263", "node-releases": "^2.0.27", "update-browserslist-db": "^1.2.0" }, "bin": { "browserslist": "cli.js" } }, "sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA=="], + + "call-bind-apply-helpers": ["call-bind-apply-helpers@1.0.2", "https://registry.npmmirror.com/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", { "dependencies": { "es-errors": "^1.3.0", "function-bind": "^1.1.2" } }, "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ=="], + + "caniuse-lite": ["caniuse-lite@1.0.30001766", "https://registry.npmmirror.com/caniuse-lite/-/caniuse-lite-1.0.30001766.tgz", {}, "sha512-4C0lfJ0/YPjJQHagaE9x2Elb69CIqEPZeG0anQt9SIvIoOH4a4uaRl73IavyO+0qZh6MDLH//DrXThEYKHkmYA=="], + + "class-variance-authority": ["class-variance-authority@0.7.1", "https://registry.npmmirror.com/class-variance-authority/-/class-variance-authority-0.7.1.tgz", { "dependencies": { "clsx": "^2.1.1" } }, "sha512-Ka+9Trutv7G8M6WT6SeiRWz792K5qEqIGEGzXKhAE6xOWAY6pPH8U+9IY3oCMv6kqTmLsv7Xh/2w2RigkePMsg=="], + + "clsx": ["clsx@2.1.1", "https://registry.npmmirror.com/clsx/-/clsx-2.1.1.tgz", {}, "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA=="], + + "combined-stream": ["combined-stream@1.0.8", "https://registry.npmmirror.com/combined-stream/-/combined-stream-1.0.8.tgz", { "dependencies": { "delayed-stream": "~1.0.0" } }, "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg=="], + + "csstype": ["csstype@3.2.3", "https://registry.npmmirror.com/csstype/-/csstype-3.2.3.tgz", {}, "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ=="], + + "delayed-stream": ["delayed-stream@1.0.0", "https://registry.npmmirror.com/delayed-stream/-/delayed-stream-1.0.0.tgz", {}, "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ=="], + + "detect-libc": ["detect-libc@2.1.2", "https://registry.npmmirror.com/detect-libc/-/detect-libc-2.1.2.tgz", {}, "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ=="], + + "detect-node-es": ["detect-node-es@1.1.0", "https://registry.npmmirror.com/detect-node-es/-/detect-node-es-1.1.0.tgz", {}, "sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ=="], + + "dunder-proto": ["dunder-proto@1.0.1", "https://registry.npmmirror.com/dunder-proto/-/dunder-proto-1.0.1.tgz", { "dependencies": { "call-bind-apply-helpers": "^1.0.1", "es-errors": "^1.3.0", "gopd": "^1.2.0" } }, "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A=="], + + "electron-to-chromium": ["electron-to-chromium@1.5.283", "https://registry.npmmirror.com/electron-to-chromium/-/electron-to-chromium-1.5.283.tgz", {}, "sha512-3vifjt1HgrGW/h76UEeny+adYApveS9dH2h3p57JYzBSXJIKUJAvtmIytDKjcSCt9xHfrNCFJ7gts6vkhuq++w=="], + + "enhanced-resolve": ["enhanced-resolve@5.18.4", "https://registry.npmmirror.com/enhanced-resolve/-/enhanced-resolve-5.18.4.tgz", { "dependencies": { "graceful-fs": "^4.2.4", "tapable": "^2.2.0" } }, "sha512-LgQMM4WXU3QI+SYgEc2liRgznaD5ojbmY3sb8LxyguVkIg5FxdpTkvk72te2R38/TGKxH634oLxXRGY6d7AP+Q=="], + + "es-define-property": ["es-define-property@1.0.1", "https://registry.npmmirror.com/es-define-property/-/es-define-property-1.0.1.tgz", {}, "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g=="], + + "es-errors": ["es-errors@1.3.0", "https://registry.npmmirror.com/es-errors/-/es-errors-1.3.0.tgz", {}, "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw=="], + + "es-object-atoms": ["es-object-atoms@1.1.1", "https://registry.npmmirror.com/es-object-atoms/-/es-object-atoms-1.1.1.tgz", { "dependencies": { "es-errors": "^1.3.0" } }, "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA=="], + + "es-set-tostringtag": ["es-set-tostringtag@2.1.0", "https://registry.npmmirror.com/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", { "dependencies": { "es-errors": "^1.3.0", "get-intrinsic": "^1.2.6", "has-tostringtag": "^1.0.2", "hasown": "^2.0.2" } }, "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA=="], + + "esbuild": ["esbuild@0.27.2", "https://registry.npmmirror.com/esbuild/-/esbuild-0.27.2.tgz", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.27.2", "@esbuild/android-arm": "0.27.2", "@esbuild/android-arm64": "0.27.2", "@esbuild/android-x64": "0.27.2", "@esbuild/darwin-arm64": "0.27.2", "@esbuild/darwin-x64": "0.27.2", "@esbuild/freebsd-arm64": "0.27.2", "@esbuild/freebsd-x64": "0.27.2", "@esbuild/linux-arm": "0.27.2", "@esbuild/linux-arm64": "0.27.2", "@esbuild/linux-ia32": "0.27.2", "@esbuild/linux-loong64": "0.27.2", "@esbuild/linux-mips64el": "0.27.2", "@esbuild/linux-ppc64": "0.27.2", "@esbuild/linux-riscv64": "0.27.2", "@esbuild/linux-s390x": "0.27.2", "@esbuild/linux-x64": "0.27.2", "@esbuild/netbsd-arm64": "0.27.2", "@esbuild/netbsd-x64": "0.27.2", "@esbuild/openbsd-arm64": "0.27.2", "@esbuild/openbsd-x64": "0.27.2", "@esbuild/openharmony-arm64": "0.27.2", "@esbuild/sunos-x64": "0.27.2", "@esbuild/win32-arm64": "0.27.2", "@esbuild/win32-ia32": "0.27.2", "@esbuild/win32-x64": "0.27.2" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-HyNQImnsOC7X9PMNaCIeAm4ISCQXs5a5YasTXVliKv4uuBo1dKrG0A+uQS8M5eXjVMnLg3WgXaKvprHlFJQffw=="], + + "escalade": ["escalade@3.2.0", "https://registry.npmmirror.com/escalade/-/escalade-3.2.0.tgz", {}, "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA=="], + + "fdir": ["fdir@6.5.0", "https://registry.npmmirror.com/fdir/-/fdir-6.5.0.tgz", { "peerDependencies": { "picomatch": "^3 || ^4" }, "optionalPeers": ["picomatch"] }, "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg=="], + + "follow-redirects": ["follow-redirects@1.15.11", "https://registry.npmmirror.com/follow-redirects/-/follow-redirects-1.15.11.tgz", {}, "sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ=="], + + "form-data": ["form-data@4.0.5", "https://registry.npmmirror.com/form-data/-/form-data-4.0.5.tgz", { "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", "es-set-tostringtag": "^2.1.0", "hasown": "^2.0.2", "mime-types": "^2.1.12" } }, "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w=="], + + "fraction.js": ["fraction.js@5.3.4", "https://registry.npmmirror.com/fraction.js/-/fraction.js-5.3.4.tgz", {}, "sha512-1X1NTtiJphryn/uLQz3whtY6jK3fTqoE3ohKs0tT+Ujr1W59oopxmoEh7Lu5p6vBaPbgoM0bzveAW4Qi5RyWDQ=="], + + "fsevents": ["fsevents@2.3.3", "https://registry.npmmirror.com/fsevents/-/fsevents-2.3.3.tgz", { "os": "darwin" }, "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="], + + "function-bind": ["function-bind@1.1.2", "https://registry.npmmirror.com/function-bind/-/function-bind-1.1.2.tgz", {}, "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA=="], + + "get-intrinsic": ["get-intrinsic@1.3.0", "https://registry.npmmirror.com/get-intrinsic/-/get-intrinsic-1.3.0.tgz", { "dependencies": { "call-bind-apply-helpers": "^1.0.2", "es-define-property": "^1.0.1", "es-errors": "^1.3.0", "es-object-atoms": "^1.1.1", "function-bind": "^1.1.2", "get-proto": "^1.0.1", "gopd": "^1.2.0", "has-symbols": "^1.1.0", "hasown": "^2.0.2", "math-intrinsics": "^1.1.0" } }, "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ=="], + + "get-nonce": ["get-nonce@1.0.1", "https://registry.npmmirror.com/get-nonce/-/get-nonce-1.0.1.tgz", {}, "sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q=="], + + "get-proto": ["get-proto@1.0.1", "https://registry.npmmirror.com/get-proto/-/get-proto-1.0.1.tgz", { "dependencies": { "dunder-proto": "^1.0.1", "es-object-atoms": "^1.0.0" } }, "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g=="], + + "gopd": ["gopd@1.2.0", "https://registry.npmmirror.com/gopd/-/gopd-1.2.0.tgz", {}, "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg=="], + + "graceful-fs": ["graceful-fs@4.2.11", "https://registry.npmmirror.com/graceful-fs/-/graceful-fs-4.2.11.tgz", {}, "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ=="], + + "has-symbols": ["has-symbols@1.1.0", "https://registry.npmmirror.com/has-symbols/-/has-symbols-1.1.0.tgz", {}, "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ=="], + + "has-tostringtag": ["has-tostringtag@1.0.2", "https://registry.npmmirror.com/has-tostringtag/-/has-tostringtag-1.0.2.tgz", { "dependencies": { "has-symbols": "^1.0.3" } }, "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw=="], + + "hasown": ["hasown@2.0.2", "https://registry.npmmirror.com/hasown/-/hasown-2.0.2.tgz", { "dependencies": { "function-bind": "^1.1.2" } }, "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ=="], + + "jiti": ["jiti@2.6.1", "https://registry.npmmirror.com/jiti/-/jiti-2.6.1.tgz", { "bin": { "jiti": "lib/jiti-cli.mjs" } }, "sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ=="], + + "lightningcss": ["lightningcss@1.30.2", "https://registry.npmmirror.com/lightningcss/-/lightningcss-1.30.2.tgz", { "dependencies": { "detect-libc": "^2.0.3" }, "optionalDependencies": { "lightningcss-android-arm64": "1.30.2", "lightningcss-darwin-arm64": "1.30.2", "lightningcss-darwin-x64": "1.30.2", "lightningcss-freebsd-x64": "1.30.2", "lightningcss-linux-arm-gnueabihf": "1.30.2", "lightningcss-linux-arm64-gnu": "1.30.2", "lightningcss-linux-arm64-musl": "1.30.2", "lightningcss-linux-x64-gnu": "1.30.2", "lightningcss-linux-x64-musl": "1.30.2", "lightningcss-win32-arm64-msvc": "1.30.2", "lightningcss-win32-x64-msvc": "1.30.2" } }, "sha512-utfs7Pr5uJyyvDETitgsaqSyjCb2qNRAtuqUeWIAKztsOYdcACf2KtARYXg2pSvhkt+9NfoaNY7fxjl6nuMjIQ=="], + + "lightningcss-android-arm64": ["lightningcss-android-arm64@1.30.2", "https://registry.npmmirror.com/lightningcss-android-arm64/-/lightningcss-android-arm64-1.30.2.tgz", { "os": "android", "cpu": "arm64" }, "sha512-BH9sEdOCahSgmkVhBLeU7Hc9DWeZ1Eb6wNS6Da8igvUwAe0sqROHddIlvU06q3WyXVEOYDZ6ykBZQnjTbmo4+A=="], + + "lightningcss-darwin-arm64": ["lightningcss-darwin-arm64@1.30.2", "https://registry.npmmirror.com/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.30.2.tgz", { "os": "darwin", "cpu": "arm64" }, "sha512-ylTcDJBN3Hp21TdhRT5zBOIi73P6/W0qwvlFEk22fkdXchtNTOU4Qc37SkzV+EKYxLouZ6M4LG9NfZ1qkhhBWA=="], + + "lightningcss-darwin-x64": ["lightningcss-darwin-x64@1.30.2", "https://registry.npmmirror.com/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.30.2.tgz", { "os": "darwin", "cpu": "x64" }, "sha512-oBZgKchomuDYxr7ilwLcyms6BCyLn0z8J0+ZZmfpjwg9fRVZIR5/GMXd7r9RH94iDhld3UmSjBM6nXWM2TfZTQ=="], + + "lightningcss-freebsd-x64": ["lightningcss-freebsd-x64@1.30.2", "https://registry.npmmirror.com/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.30.2.tgz", { "os": "freebsd", "cpu": "x64" }, "sha512-c2bH6xTrf4BDpK8MoGG4Bd6zAMZDAXS569UxCAGcA7IKbHNMlhGQ89eRmvpIUGfKWNVdbhSbkQaWhEoMGmGslA=="], + + "lightningcss-linux-arm-gnueabihf": ["lightningcss-linux-arm-gnueabihf@1.30.2", "https://registry.npmmirror.com/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.30.2.tgz", { "os": "linux", "cpu": "arm" }, "sha512-eVdpxh4wYcm0PofJIZVuYuLiqBIakQ9uFZmipf6LF/HRj5Bgm0eb3qL/mr1smyXIS1twwOxNWndd8z0E374hiA=="], + + "lightningcss-linux-arm64-gnu": ["lightningcss-linux-arm64-gnu@1.30.2", "https://registry.npmmirror.com/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.30.2.tgz", { "os": "linux", "cpu": "arm64" }, "sha512-UK65WJAbwIJbiBFXpxrbTNArtfuznvxAJw4Q2ZGlU8kPeDIWEX1dg3rn2veBVUylA2Ezg89ktszWbaQnxD/e3A=="], + + "lightningcss-linux-arm64-musl": ["lightningcss-linux-arm64-musl@1.30.2", "https://registry.npmmirror.com/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.30.2.tgz", { "os": "linux", "cpu": "arm64" }, "sha512-5Vh9dGeblpTxWHpOx8iauV02popZDsCYMPIgiuw97OJ5uaDsL86cnqSFs5LZkG3ghHoX5isLgWzMs+eD1YzrnA=="], + + "lightningcss-linux-x64-gnu": ["lightningcss-linux-x64-gnu@1.30.2", "https://registry.npmmirror.com/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.30.2.tgz", { "os": "linux", "cpu": "x64" }, "sha512-Cfd46gdmj1vQ+lR6VRTTadNHu6ALuw2pKR9lYq4FnhvgBc4zWY1EtZcAc6EffShbb1MFrIPfLDXD6Xprbnni4w=="], + + "lightningcss-linux-x64-musl": ["lightningcss-linux-x64-musl@1.30.2", "https://registry.npmmirror.com/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.30.2.tgz", { "os": "linux", "cpu": "x64" }, "sha512-XJaLUUFXb6/QG2lGIW6aIk6jKdtjtcffUT0NKvIqhSBY3hh9Ch+1LCeH80dR9q9LBjG3ewbDjnumefsLsP6aiA=="], + + "lightningcss-win32-arm64-msvc": ["lightningcss-win32-arm64-msvc@1.30.2", "https://registry.npmmirror.com/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.30.2.tgz", { "os": "win32", "cpu": "arm64" }, "sha512-FZn+vaj7zLv//D/192WFFVA0RgHawIcHqLX9xuWiQt7P0PtdFEVaxgF9rjM/IRYHQXNnk61/H/gb2Ei+kUQ4xQ=="], + + "lightningcss-win32-x64-msvc": ["lightningcss-win32-x64-msvc@1.30.2", "https://registry.npmmirror.com/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.30.2.tgz", { "os": "win32", "cpu": "x64" }, "sha512-5g1yc73p+iAkid5phb4oVFMB45417DkRevRbt/El/gKXJk4jid+vPFF/AXbxn05Aky8PapwzZrdJShv5C0avjw=="], + + "lucide-react": ["lucide-react@0.563.0", "https://registry.npmmirror.com/lucide-react/-/lucide-react-0.563.0.tgz", { "peerDependencies": { "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-8dXPB2GI4dI8jV4MgUDGBeLdGk8ekfqVZ0BdLcrRzocGgG75ltNEmWS+gE7uokKF/0oSUuczNDT+g9hFJ23FkA=="], + + "magic-string": ["magic-string@0.30.21", "https://registry.npmmirror.com/magic-string/-/magic-string-0.30.21.tgz", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.5" } }, "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ=="], + + "math-intrinsics": ["math-intrinsics@1.1.0", "https://registry.npmmirror.com/math-intrinsics/-/math-intrinsics-1.1.0.tgz", {}, "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g=="], + + "mime-db": ["mime-db@1.52.0", "https://registry.npmmirror.com/mime-db/-/mime-db-1.52.0.tgz", {}, "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg=="], + + "mime-types": ["mime-types@2.1.35", "https://registry.npmmirror.com/mime-types/-/mime-types-2.1.35.tgz", { "dependencies": { "mime-db": "1.52.0" } }, "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw=="], + + "nanoid": ["nanoid@3.3.11", "https://registry.npmmirror.com/nanoid/-/nanoid-3.3.11.tgz", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w=="], + + "node-releases": ["node-releases@2.0.27", "https://registry.npmmirror.com/node-releases/-/node-releases-2.0.27.tgz", {}, "sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA=="], + + "picocolors": ["picocolors@1.1.1", "https://registry.npmmirror.com/picocolors/-/picocolors-1.1.1.tgz", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="], + + "picomatch": ["picomatch@4.0.3", "https://registry.npmmirror.com/picomatch/-/picomatch-4.0.3.tgz", {}, "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q=="], + + "postcss": ["postcss@8.5.6", "https://registry.npmmirror.com/postcss/-/postcss-8.5.6.tgz", { "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg=="], + + "postcss-value-parser": ["postcss-value-parser@4.2.0", "https://registry.npmmirror.com/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", {}, "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ=="], + + "proxy-from-env": ["proxy-from-env@1.1.0", "https://registry.npmmirror.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz", {}, "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg=="], + + "react": ["react@19.2.4", "https://registry.npmmirror.com/react/-/react-19.2.4.tgz", {}, "sha512-9nfp2hYpCwOjAN+8TZFGhtWEwgvWHXqESH8qT89AT/lWklpLON22Lc8pEtnpsZz7VmawabSU0gCjnj8aC0euHQ=="], + + "react-dom": ["react-dom@19.2.4", "https://registry.npmmirror.com/react-dom/-/react-dom-19.2.4.tgz", { "dependencies": { "scheduler": "^0.27.0" }, "peerDependencies": { "react": "^19.2.4" } }, "sha512-AXJdLo8kgMbimY95O2aKQqsz2iWi9jMgKJhRBAxECE4IFxfcazB2LmzloIoibJI3C12IlY20+KFaLv+71bUJeQ=="], + + "react-remove-scroll": ["react-remove-scroll@2.7.2", "https://registry.npmmirror.com/react-remove-scroll/-/react-remove-scroll-2.7.2.tgz", { "dependencies": { "react-remove-scroll-bar": "^2.3.7", "react-style-singleton": "^2.2.3", "tslib": "^2.1.0", "use-callback-ref": "^1.3.3", "use-sidecar": "^1.1.3" }, "peerDependencies": { "@types/react": "*", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-Iqb9NjCCTt6Hf+vOdNIZGdTiH1QSqr27H/Ek9sv/a97gfueI/5h1s3yRi1nngzMUaOOToin5dI1dXKdXiF+u0Q=="], + + "react-remove-scroll-bar": ["react-remove-scroll-bar@2.3.8", "https://registry.npmmirror.com/react-remove-scroll-bar/-/react-remove-scroll-bar-2.3.8.tgz", { "dependencies": { "react-style-singleton": "^2.2.2", "tslib": "^2.0.0" }, "peerDependencies": { "@types/react": "*", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" }, "optionalPeers": ["@types/react"] }, "sha512-9r+yi9+mgU33AKcj6IbT9oRCO78WriSj6t/cF8DWBZJ9aOGPOTEDvdUDz1FwKim7QXWwmHqtdHnRJfhAxEG46Q=="], + + "react-style-singleton": ["react-style-singleton@2.2.3", "https://registry.npmmirror.com/react-style-singleton/-/react-style-singleton-2.2.3.tgz", { "dependencies": { "get-nonce": "^1.0.0", "tslib": "^2.0.0" }, "peerDependencies": { "@types/react": "*", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-b6jSvxvVnyptAiLjbkWLE/lOnR4lfTtDAl+eUC7RZy+QQWc6wRzIV2CE6xBuMmDxc2qIihtDCZD5NPOFl7fRBQ=="], + + "rollup": ["rollup@4.57.1", "https://registry.npmmirror.com/rollup/-/rollup-4.57.1.tgz", { "dependencies": { "@types/estree": "1.0.8" }, "optionalDependencies": { "@rollup/rollup-android-arm-eabi": "4.57.1", "@rollup/rollup-android-arm64": "4.57.1", "@rollup/rollup-darwin-arm64": "4.57.1", "@rollup/rollup-darwin-x64": "4.57.1", "@rollup/rollup-freebsd-arm64": "4.57.1", "@rollup/rollup-freebsd-x64": "4.57.1", "@rollup/rollup-linux-arm-gnueabihf": "4.57.1", "@rollup/rollup-linux-arm-musleabihf": "4.57.1", "@rollup/rollup-linux-arm64-gnu": "4.57.1", "@rollup/rollup-linux-arm64-musl": "4.57.1", "@rollup/rollup-linux-loong64-gnu": "4.57.1", "@rollup/rollup-linux-loong64-musl": "4.57.1", "@rollup/rollup-linux-ppc64-gnu": "4.57.1", "@rollup/rollup-linux-ppc64-musl": "4.57.1", "@rollup/rollup-linux-riscv64-gnu": "4.57.1", "@rollup/rollup-linux-riscv64-musl": "4.57.1", "@rollup/rollup-linux-s390x-gnu": "4.57.1", "@rollup/rollup-linux-x64-gnu": "4.57.1", "@rollup/rollup-linux-x64-musl": "4.57.1", "@rollup/rollup-openbsd-x64": "4.57.1", "@rollup/rollup-openharmony-arm64": "4.57.1", "@rollup/rollup-win32-arm64-msvc": "4.57.1", "@rollup/rollup-win32-ia32-msvc": "4.57.1", "@rollup/rollup-win32-x64-gnu": "4.57.1", "@rollup/rollup-win32-x64-msvc": "4.57.1", "fsevents": "~2.3.2" }, "bin": { "rollup": "dist/bin/rollup" } }, "sha512-oQL6lgK3e2QZeQ7gcgIkS2YZPg5slw37hYufJ3edKlfQSGGm8ICoxswK15ntSzF/a8+h7ekRy7k7oWc3BQ7y8A=="], + + "scheduler": ["scheduler@0.27.0", "https://registry.npmmirror.com/scheduler/-/scheduler-0.27.0.tgz", {}, "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q=="], + + "sonner": ["sonner@2.0.7", "https://registry.npmmirror.com/sonner/-/sonner-2.0.7.tgz", { "peerDependencies": { "react": "^18.0.0 || ^19.0.0 || ^19.0.0-rc", "react-dom": "^18.0.0 || ^19.0.0 || ^19.0.0-rc" } }, "sha512-W6ZN4p58k8aDKA4XPcx2hpIQXBRAgyiWVkYhT7CvK6D3iAu7xjvVyhQHg2/iaKJZ1XVJ4r7XuwGL+WGEK37i9w=="], + + "source-map-js": ["source-map-js@1.2.1", "https://registry.npmmirror.com/source-map-js/-/source-map-js-1.2.1.tgz", {}, "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA=="], + + "tailwind-merge": ["tailwind-merge@3.4.0", "https://registry.npmmirror.com/tailwind-merge/-/tailwind-merge-3.4.0.tgz", {}, "sha512-uSaO4gnW+b3Y2aWoWfFpX62vn2sR3skfhbjsEnaBI81WD1wBLlHZe5sWf0AqjksNdYTbGBEd0UasQMT3SNV15g=="], + + "tailwindcss": ["tailwindcss@4.1.18", "https://registry.npmmirror.com/tailwindcss/-/tailwindcss-4.1.18.tgz", {}, "sha512-4+Z+0yiYyEtUVCScyfHCxOYP06L5Ne+JiHhY2IjR2KWMIWhJOYZKLSGZaP5HkZ8+bY0cxfzwDE5uOmzFXyIwxw=="], + + "tapable": ["tapable@2.3.0", "https://registry.npmmirror.com/tapable/-/tapable-2.3.0.tgz", {}, "sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg=="], + + "tinyglobby": ["tinyglobby@0.2.15", "https://registry.npmmirror.com/tinyglobby/-/tinyglobby-0.2.15.tgz", { "dependencies": { "fdir": "^6.5.0", "picomatch": "^4.0.3" } }, "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ=="], + + "tslib": ["tslib@2.8.1", "https://registry.npmmirror.com/tslib/-/tslib-2.8.1.tgz", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="], + + "typescript": ["typescript@5.9.3", "https://registry.npmmirror.com/typescript/-/typescript-5.9.3.tgz", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="], + + "update-browserslist-db": ["update-browserslist-db@1.2.3", "https://registry.npmmirror.com/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz", { "dependencies": { "escalade": "^3.2.0", "picocolors": "^1.1.1" }, "peerDependencies": { "browserslist": ">= 4.21.0" }, "bin": { "update-browserslist-db": "cli.js" } }, "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w=="], + + "use-callback-ref": ["use-callback-ref@1.3.3", "https://registry.npmmirror.com/use-callback-ref/-/use-callback-ref-1.3.3.tgz", { "dependencies": { "tslib": "^2.0.0" }, "peerDependencies": { "@types/react": "*", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-jQL3lRnocaFtu3V00JToYz/4QkNWswxijDaCVNZRiRTO3HQDLsdu1ZtmIUvV4yPp+rvWm5j0y0TG/S61cuijTg=="], + + "use-sidecar": ["use-sidecar@1.1.3", "https://registry.npmmirror.com/use-sidecar/-/use-sidecar-1.1.3.tgz", { "dependencies": { "detect-node-es": "^1.1.0", "tslib": "^2.0.0" }, "peerDependencies": { "@types/react": "*", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-Fedw0aZvkhynoPYlA5WXrMCAMm+nSWdZt6lzJQ7Ok8S6Q+VsHmHpRWndVRJ8Be0ZbkfPc5LRYH+5XrzXcEeLRQ=="], + + "vite": ["vite@7.3.1", "https://registry.npmmirror.com/vite/-/vite-7.3.1.tgz", { "dependencies": { "esbuild": "^0.27.0", "fdir": "^6.5.0", "picomatch": "^4.0.3", "postcss": "^8.5.6", "rollup": "^4.43.0", "tinyglobby": "^0.2.15" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", "jiti": ">=1.21.0", "less": "^4.0.0", "lightningcss": "^1.21.0", "sass": "^1.70.0", "sass-embedded": "^1.70.0", "stylus": ">=0.54.8", "sugarss": "^5.0.0", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-w+N7Hifpc3gRjZ63vYBXA56dvvRlNWRczTdmCBBa+CotUzAPf5b7YMdMR/8CQoeYE5LX3W4wj6RYTgonm1b9DA=="], + + "@radix-ui/react-collection/@radix-ui/react-slot": ["@radix-ui/react-slot@1.2.3", "https://registry.npmmirror.com/@radix-ui/react-slot/-/react-slot-1.2.3.tgz", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A=="], + + "@radix-ui/react-dialog/@radix-ui/react-slot": ["@radix-ui/react-slot@1.2.3", "https://registry.npmmirror.com/@radix-ui/react-slot/-/react-slot-1.2.3.tgz", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A=="], + + "@radix-ui/react-menu/@radix-ui/react-slot": ["@radix-ui/react-slot@1.2.3", "https://registry.npmmirror.com/@radix-ui/react-slot/-/react-slot-1.2.3.tgz", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A=="], + + "@radix-ui/react-primitive/@radix-ui/react-slot": ["@radix-ui/react-slot@1.2.3", "https://registry.npmmirror.com/@radix-ui/react-slot/-/react-slot-1.2.3.tgz", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A=="], + + "@radix-ui/react-tooltip/@radix-ui/react-slot": ["@radix-ui/react-slot@1.2.3", "https://registry.npmmirror.com/@radix-ui/react-slot/-/react-slot-1.2.3.tgz", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A=="], + + "@tailwindcss/oxide-wasm32-wasi/@emnapi/core": ["@emnapi/core@1.8.1", "https://registry.npmmirror.com/@emnapi/core/-/core-1.8.1.tgz", { "dependencies": { "@emnapi/wasi-threads": "1.1.0", "tslib": "^2.4.0" }, "bundled": true }, "sha512-AvT9QFpxK0Zd8J0jopedNm+w/2fIzvtPKPjqyw9jwvBaReTTqPBk9Hixaz7KbjimP+QNz605/XnjFcDAL2pqBg=="], + + "@tailwindcss/oxide-wasm32-wasi/@emnapi/runtime": ["@emnapi/runtime@1.8.1", "https://registry.npmmirror.com/@emnapi/runtime/-/runtime-1.8.1.tgz", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-mehfKSMWjjNol8659Z8KxEMrdSJDDot5SXMq00dM8BN4o+CLNXQ0xH2V7EchNHV4RmbZLmmPdEaXZc5H2FXmDg=="], + + "@tailwindcss/oxide-wasm32-wasi/@emnapi/wasi-threads": ["@emnapi/wasi-threads@1.1.0", "https://registry.npmmirror.com/@emnapi/wasi-threads/-/wasi-threads-1.1.0.tgz", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-WI0DdZ8xFSbgMjR1sFsKABJ/C5OnRrjT06JXbZKexJGrDuPTzZdDYfFlsgcCXCyf+suG5QU2e/y1Wo2V/OapLQ=="], + + "@tailwindcss/oxide-wasm32-wasi/@napi-rs/wasm-runtime": ["@napi-rs/wasm-runtime@1.1.1", "https://registry.npmmirror.com/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.1.tgz", { "dependencies": { "@emnapi/core": "^1.7.1", "@emnapi/runtime": "^1.7.1", "@tybys/wasm-util": "^0.10.1" }, "bundled": true }, "sha512-p64ah1M1ld8xjWv3qbvFwHiFVWrq1yFvV4f7w+mzaqiR4IlSgkqhcRdHwsGgomwzBH51sRY4NEowLxnaBjcW/A=="], + + "@tailwindcss/oxide-wasm32-wasi/@tybys/wasm-util": ["@tybys/wasm-util@0.10.1", "https://registry.npmmirror.com/@tybys/wasm-util/-/wasm-util-0.10.1.tgz", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg=="], + + "@tailwindcss/oxide-wasm32-wasi/tslib": ["tslib@2.8.1", "https://registry.npmmirror.com/tslib/-/tslib-2.8.1.tgz", { "bundled": true }, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="], + } +} diff --git a/admin-ui/index.html b/admin-ui/index.html new file mode 100644 index 0000000..fa27c3e --- /dev/null +++ b/admin-ui/index.html @@ -0,0 +1,13 @@ + + + + + + + Kiro Admin + + +
+ + + diff --git a/admin-ui/postcss.config.js b/admin-ui/postcss.config.js new file mode 100644 index 0000000..1c87846 --- /dev/null +++ b/admin-ui/postcss.config.js @@ -0,0 +1,6 @@ +export default { + plugins: { + '@tailwindcss/postcss': {}, + autoprefixer: {}, + }, +} diff --git a/admin-ui/public/vite.svg b/admin-ui/public/vite.svg new file mode 100644 index 0000000..6a41099 --- /dev/null +++ b/admin-ui/public/vite.svg @@ -0,0 +1 @@ + diff --git a/admin-ui/src/App.tsx b/admin-ui/src/App.tsx new file mode 100644 index 0000000..f2fab45 --- /dev/null +++ b/admin-ui/src/App.tsx @@ -0,0 +1,37 @@ +import { useState, useEffect } from 'react' +import { storage } from '@/lib/storage' +import { LoginPage } from '@/components/login-page' +import { Dashboard } from '@/components/dashboard' +import { Toaster } from '@/components/ui/sonner' + +function App() { + const [isLoggedIn, setIsLoggedIn] = useState(false) + + useEffect(() => { + // 检查是否已经有保存的 API Key + if (storage.getApiKey()) { + setIsLoggedIn(true) + } + }, []) + + const handleLogin = () => { + setIsLoggedIn(true) + } + + const handleLogout = () => { + setIsLoggedIn(false) + } + + return ( + <> + {isLoggedIn ? ( + + ) : ( + + )} + + + ) +} + +export default App diff --git a/admin-ui/src/api/credentials.ts b/admin-ui/src/api/credentials.ts new file mode 100644 index 0000000..d7aca3a --- /dev/null +++ b/admin-ui/src/api/credentials.ts @@ -0,0 +1,147 @@ +import axios from 'axios' +import { storage } from '@/lib/storage' +import type { + CredentialsStatusResponse, + BalanceResponse, + CachedBalancesResponse, + SuccessResponse, + SetDisabledRequest, + SetPriorityRequest, + AddCredentialRequest, + AddCredentialResponse, + CredentialStatsResponse, + CredentialAccountInfoResponse, + ImportTokenJsonRequest, + ImportTokenJsonResponse, +} from '@/types/api' + +// 创建 axios 实例 +const api = axios.create({ + baseURL: '/api/admin', + headers: { + 'Content-Type': 'application/json', + }, +}) + +// 请求拦截器添加 API Key +api.interceptors.request.use((config) => { + const apiKey = storage.getApiKey() + if (apiKey) { + config.headers['x-api-key'] = apiKey + } + return config +}) + +// 获取所有凭据状态 +export async function getCredentials(): Promise { + const { data } = await api.get('/credentials') + return data +} + +// 设置凭据禁用状态 +export async function setCredentialDisabled( + id: number, + disabled: boolean +): Promise { + const { data } = await api.post( + `/credentials/${id}/disabled`, + { disabled } as SetDisabledRequest + ) + return data +} + +// 设置凭据优先级 +export async function setCredentialPriority( + id: number, + priority: number +): Promise { + const { data } = await api.post( + `/credentials/${id}/priority`, + { priority } as SetPriorityRequest + ) + return data +} + +// 重置失败计数 +export async function resetCredentialFailure( + id: number +): Promise { + const { data } = await api.post(`/credentials/${id}/reset`) + return data +} + +// 设置凭据 Region +export async function setCredentialRegion( + id: number, + region: string | null, + apiRegion: string | null +): Promise { + const { data } = await api.post(`/credentials/${id}/region`, { + region: region || null, + apiRegion: apiRegion || null, + }) + return data +} + +// 获取凭据余额 +export async function getCredentialBalance(id: number): Promise { + const { data } = await api.get(`/credentials/${id}/balance`) + return data +} + +// 获取所有凭据的缓存余额 +export async function getCachedBalances(): Promise { + const { data } = await api.get('/credentials/balances/cached') + return data +} + +// 获取凭据账号信息(套餐/用量/邮箱等) +export async function getCredentialAccountInfo( + id: number +): Promise { + const { data } = await api.get(`/credentials/${id}/account`) + return data +} + +// 添加新凭据 +export async function addCredential( + req: AddCredentialRequest +): Promise { + const { data } = await api.post('/credentials', req) + return data +} + +// 删除凭据 +export async function deleteCredential(id: number): Promise { + const { data } = await api.delete(`/credentials/${id}`) + return data +} + +// 获取指定凭据统计 +export async function getCredentialStats(id: number): Promise { + const { data } = await api.get(`/credentials/${id}/stats`) + return data +} + +// 清空指定凭据统计 +export async function resetCredentialStats(id: number): Promise { + const { data } = await api.post(`/credentials/${id}/stats/reset`) + return data +} + +// 清空全部统计 +export async function resetAllStats(): Promise { + const { data } = await api.post('/stats/reset') + return data +} + +// 批量导入 token.json +export async function importTokenJson( + req: ImportTokenJsonRequest +): Promise { + const { data } = await api.post( + '/credentials/import-token-json', + req + ) + return data +} diff --git a/admin-ui/src/components/add-credential-dialog.tsx b/admin-ui/src/components/add-credential-dialog.tsx new file mode 100644 index 0000000..5c09fe4 --- /dev/null +++ b/admin-ui/src/components/add-credential-dialog.tsx @@ -0,0 +1,279 @@ +import { useState } from 'react' +import { toast } from 'sonner' +import { + Dialog, + DialogContent, + DialogHeader, + DialogTitle, + DialogFooter, +} from '@/components/ui/dialog' +import { Button } from '@/components/ui/button' +import { Input } from '@/components/ui/input' +import { useAddCredential } from '@/hooks/use-credentials' +import { extractErrorMessage } from '@/lib/utils' + +interface AddCredentialDialogProps { + open: boolean + onOpenChange: (open: boolean) => void +} + +type AuthMethod = 'social' | 'idc' + +export function AddCredentialDialog({ open, onOpenChange }: AddCredentialDialogProps) { + const [refreshToken, setRefreshToken] = useState('') + const [authMethod, setAuthMethod] = useState('social') + const [region, setRegion] = useState('') + const [apiRegion, setApiRegion] = useState('') + const [clientId, setClientId] = useState('') + const [clientSecret, setClientSecret] = useState('') + const [priority, setPriority] = useState('0') + const [machineId, setMachineId] = useState('') + const [proxyUrl, setProxyUrl] = useState('') + const [proxyUsername, setProxyUsername] = useState('') + const [proxyPassword, setProxyPassword] = useState('') + + const { mutate, isPending } = useAddCredential() + + const resetForm = () => { + setRefreshToken('') + setAuthMethod('social') + setRegion('') + setApiRegion('') + setClientId('') + setClientSecret('') + setPriority('0') + setMachineId('') + setProxyUrl('') + setProxyUsername('') + setProxyPassword('') + } + + const handleSubmit = (e: React.FormEvent) => { + e.preventDefault() + + // 验证必填字段 + if (!refreshToken.trim()) { + toast.error('请输入 Refresh Token') + return + } + + // IdC/Builder-ID/IAM 需要额外字段 + if (authMethod === 'idc' && (!clientId.trim() || !clientSecret.trim())) { + toast.error('IdC/Builder-ID/IAM 认证需要填写 Client ID 和 Client Secret') + return + } + + mutate( + { + refreshToken: refreshToken.trim(), + authMethod, + region: region.trim() || undefined, + apiRegion: apiRegion.trim() || undefined, + clientId: clientId.trim() || undefined, + clientSecret: clientSecret.trim() || undefined, + priority: parseInt(priority) || 0, + machineId: machineId.trim() || undefined, + proxyUrl: proxyUrl.trim() || undefined, + proxyUsername: proxyUsername.trim() || undefined, + proxyPassword: proxyPassword.trim() || undefined, + }, + { + onSuccess: (data) => { + toast.success(data.message) + onOpenChange(false) + resetForm() + }, + onError: (error: unknown) => { + toast.error(`添加失败: ${extractErrorMessage(error)}`) + }, + } + ) + } + + return ( + + + + 添加凭据 + + +
+
+ {/* Refresh Token */} +
+ + setRefreshToken(e.target.value)} + disabled={isPending} + /> +
+ + {/* 认证方式 */} +
+ + +
+ + {/* Region 配置 */} +
+ +
+
+ setRegion(e.target.value)} + disabled={isPending} + /> +
+
+ setApiRegion(e.target.value)} + disabled={isPending} + /> +
+
+

+ Region 用于 Token 刷新,留空使用全局配置。API Region 可单独覆盖 API 请求所用的 Region +

+
+ + {/* IdC/Builder-ID/IAM 额外字段 */} + {authMethod === 'idc' && ( + <> +
+ + setClientId(e.target.value)} + disabled={isPending} + /> +
+
+ + setClientSecret(e.target.value)} + disabled={isPending} + /> +
+ + )} + + {/* 优先级 */} +
+ + setPriority(e.target.value)} + disabled={isPending} + /> +

+ 数字越小优先级越高,默认为 0 +

+
+ + {/* Machine ID */} +
+ + setMachineId(e.target.value)} + disabled={isPending} + /> +

+ 可选,64 位十六进制字符串,留空使用配置中字段, 否则由刷新Token自动派生 +

+
+ + {/* 代理配置 */} +
+ + setProxyUrl(e.target.value)} + disabled={isPending} + /> +
+ setProxyUsername(e.target.value)} + disabled={isPending} + /> + setProxyPassword(e.target.value)} + disabled={isPending} + /> +
+

+ 留空使用全局代理。输入 "direct" 可显式不使用代理 +

+
+
+ + + + + +
+
+
+ ) +} diff --git a/admin-ui/src/components/balance-dialog.tsx b/admin-ui/src/components/balance-dialog.tsx new file mode 100644 index 0000000..70c441d --- /dev/null +++ b/admin-ui/src/components/balance-dialog.tsx @@ -0,0 +1,106 @@ +import { + Dialog, + DialogContent, + DialogHeader, + DialogTitle, +} from '@/components/ui/dialog' +import { Progress } from '@/components/ui/progress' +import { useCredentialBalance } from '@/hooks/use-credentials' +import { parseError } from '@/lib/utils' + +interface BalanceDialogProps { + credentialId: number | null + open: boolean + onOpenChange: (open: boolean) => void + forceRefresh?: boolean +} + +export function BalanceDialog({ credentialId, open, onOpenChange, forceRefresh }: BalanceDialogProps) { + const { data: balance, isLoading, isFetching, error } = useCredentialBalance(credentialId) + const showLoading = isLoading || (forceRefresh && isFetching) + + const formatDate = (timestamp: number | null) => { + if (!timestamp) return '未知' + return new Date(timestamp * 1000).toLocaleString('zh-CN') + } + + const formatNumber = (num: number) => { + return num.toLocaleString('zh-CN', { minimumFractionDigits: 2, maximumFractionDigits: 2 }) + } + + return ( + + + + + 凭据 #{credentialId} 余额信息 + + + + {showLoading && ( +
+
+
+ )} + + {error && (() => { + const parsed = parseError(error) + return ( +
+
+ + + + {parsed.title} +
+ {parsed.detail && ( +
+ {parsed.detail} +
+ )} +
+ ) + })()} + + {balance && ( +
+ {/* 订阅类型 */} +
+ + {balance.subscriptionTitle || '未知订阅类型'} + +
+ + {/* 使用进度 */} +
+
+ 已使用: ${formatNumber(balance.currentUsage)} + 限额: ${formatNumber(balance.usageLimit)} +
+ +
+ {balance.usagePercentage.toFixed(1)}% 已使用 +
+
+ + {/* 详细信息 */} +
+
+ 剩余额度: + + ${formatNumber(balance.remaining)} + +
+
+ 下次重置: + + {formatDate(balance.nextResetAt)} + +
+
+
+ )} +
+
+ ) +} diff --git a/admin-ui/src/components/batch-import-dialog.tsx b/admin-ui/src/components/batch-import-dialog.tsx new file mode 100644 index 0000000..e98cbf2 --- /dev/null +++ b/admin-ui/src/components/batch-import-dialog.tsx @@ -0,0 +1,434 @@ +import { useState } from 'react' +import { toast } from 'sonner' +import { CheckCircle2, XCircle, AlertCircle, Loader2 } from 'lucide-react' +import { + Dialog, + DialogContent, + DialogHeader, + DialogTitle, + DialogFooter, +} from '@/components/ui/dialog' +import { Button } from '@/components/ui/button' +import { useCredentials, useAddCredential, useDeleteCredential } from '@/hooks/use-credentials' +import { getCredentialBalance, setCredentialDisabled } from '@/api/credentials' +import { extractErrorMessage, sha256Hex } from '@/lib/utils' + +interface BatchImportDialogProps { + open: boolean + onOpenChange: (open: boolean) => void +} + +interface CredentialInput { + refreshToken: string + clientId?: string + clientSecret?: string + region?: string + authRegion?: string + apiRegion?: string + priority?: number + machineId?: string +} + +interface VerificationResult { + index: number + status: 'pending' | 'checking' | 'verifying' | 'verified' | 'duplicate' | 'failed' + error?: string + usage?: string + email?: string + credentialId?: number + rollbackStatus?: 'success' | 'failed' | 'skipped' + rollbackError?: string +} + + + +export function BatchImportDialog({ open, onOpenChange }: BatchImportDialogProps) { + const [jsonInput, setJsonInput] = useState('') + const [importing, setImporting] = useState(false) + const [progress, setProgress] = useState({ current: 0, total: 0 }) + const [currentProcessing, setCurrentProcessing] = useState('') + const [results, setResults] = useState([]) + + const { data: existingCredentials } = useCredentials() + const { mutateAsync: addCredential } = useAddCredential() + const { mutateAsync: deleteCredential } = useDeleteCredential() + + const rollbackCredential = async (id: number): Promise<{ success: boolean; error?: string }> => { + try { + await setCredentialDisabled(id, true) + } catch (error) { + return { + success: false, + error: `禁用失败: ${extractErrorMessage(error)}`, + } + } + + try { + await deleteCredential(id) + return { success: true } + } catch (error) { + return { + success: false, + error: `删除失败: ${extractErrorMessage(error)}`, + } + } + } + + const resetForm = () => { + setJsonInput('') + setProgress({ current: 0, total: 0 }) + setCurrentProcessing('') + setResults([]) + } + + const handleBatchImport = async () => { + // 先单独解析 JSON,给出精准的错误提示 + let credentials: CredentialInput[] + try { + const parsed = JSON.parse(jsonInput) + credentials = Array.isArray(parsed) ? parsed : [parsed] + } catch (error) { + toast.error('JSON 格式错误: ' + extractErrorMessage(error)) + return + } + + if (credentials.length === 0) { + toast.error('没有可导入的凭据') + return + } + + try { + setImporting(true) + setProgress({ current: 0, total: credentials.length }) + + // 2. 初始化结果 + const initialResults: VerificationResult[] = credentials.map((_, i) => ({ + index: i + 1, + status: 'pending' + })) + setResults(initialResults) + + // 3. 检测重复 + const existingTokenHashes = new Set( + existingCredentials?.credentials + .map(c => c.refreshTokenHash) + .filter((hash): hash is string => Boolean(hash)) || [] + ) + + let successCount = 0 + let duplicateCount = 0 + let failCount = 0 + let rollbackSuccessCount = 0 + let rollbackFailedCount = 0 + let rollbackSkippedCount = 0 + + // 4. 导入并验活 + for (let i = 0; i < credentials.length; i++) { + const cred = credentials[i] + const token = cred.refreshToken.trim() + const tokenHash = await sha256Hex(token) + + // 更新状态为检查中 + setCurrentProcessing(`正在处理凭据 ${i + 1}/${credentials.length}`) + setResults(prev => { + const newResults = [...prev] + newResults[i] = { ...newResults[i], status: 'checking' } + return newResults + }) + + // 检查重复 + if (existingTokenHashes.has(tokenHash)) { + duplicateCount++ + const existingCred = existingCredentials?.credentials.find(c => c.refreshTokenHash === tokenHash) + setResults(prev => { + const newResults = [...prev] + newResults[i] = { + ...newResults[i], + status: 'duplicate', + error: '该凭据已存在', + email: existingCred?.email || undefined + } + return newResults + }) + setProgress({ current: i + 1, total: credentials.length }) + continue + } + + // 更新状态为验活中 + setResults(prev => { + const newResults = [...prev] + newResults[i] = { ...newResults[i], status: 'verifying' } + return newResults + }) + + let addedCredId: number | null = null + + try { + // 添加凭据 + const clientId = cred.clientId?.trim() || undefined + const clientSecret = cred.clientSecret?.trim() || undefined + const authMethod = clientId && clientSecret ? 'idc' : 'social' + + // idc 模式下必须同时提供 clientId 和 clientSecret + if (authMethod === 'social' && (clientId || clientSecret)) { + throw new Error('idc 模式需要同时提供 clientId 和 clientSecret') + } + + const addedCred = await addCredential({ + refreshToken: token, + authMethod, + authRegion: cred.authRegion?.trim() || cred.region?.trim() || undefined, + apiRegion: cred.apiRegion?.trim() || undefined, + clientId, + clientSecret, + priority: cred.priority || 0, + machineId: cred.machineId?.trim() || undefined, + }) + + addedCredId = addedCred.credentialId + + // 延迟 1 秒 + await new Promise(resolve => setTimeout(resolve, 1000)) + + // 验活 + const balance = await getCredentialBalance(addedCred.credentialId) + + // 验活成功 + successCount++ + existingTokenHashes.add(tokenHash) + setCurrentProcessing(addedCred.email ? `验活成功: ${addedCred.email}` : `验活成功: 凭据 ${i + 1}`) + setResults(prev => { + const newResults = [...prev] + newResults[i] = { + ...newResults[i], + status: 'verified', + usage: `${balance.currentUsage}/${balance.usageLimit}`, + email: addedCred.email || undefined, + credentialId: addedCred.credentialId + } + return newResults + }) + } catch (error) { + // 验活失败,尝试回滚(先禁用再删除) + let rollbackStatus: VerificationResult['rollbackStatus'] = 'skipped' + let rollbackError: string | undefined + + if (addedCredId) { + const rollbackResult = await rollbackCredential(addedCredId) + if (rollbackResult.success) { + rollbackStatus = 'success' + rollbackSuccessCount++ + } else { + rollbackStatus = 'failed' + rollbackFailedCount++ + rollbackError = rollbackResult.error + } + } else { + rollbackSkippedCount++ + } + + failCount++ + setResults(prev => { + const newResults = [...prev] + newResults[i] = { + ...newResults[i], + status: 'failed', + error: extractErrorMessage(error), + email: undefined, + rollbackStatus, + rollbackError, + } + return newResults + }) + } + + setProgress({ current: i + 1, total: credentials.length }) + } + + // 显示结果 + if (failCount === 0 && duplicateCount === 0) { + toast.success(`成功导入并验活 ${successCount} 个凭据`) + } else { + const failureSummary = failCount > 0 + ? `,失败 ${failCount} 个(已排除 ${rollbackSuccessCount},未排除 ${rollbackFailedCount},无需排除 ${rollbackSkippedCount})` + : '' + toast.info(`验活完成:成功 ${successCount} 个,重复 ${duplicateCount} 个${failureSummary}`) + + if (rollbackFailedCount > 0) { + toast.warning(`有 ${rollbackFailedCount} 个失败凭据回滚未完成,请手动禁用并删除`) + } + } + } catch (error) { + toast.error('导入失败: ' + extractErrorMessage(error)) + } finally { + setImporting(false) + } + } + + const getStatusIcon = (status: VerificationResult['status']) => { + switch (status) { + case 'pending': + return
+ case 'checking': + case 'verifying': + return + case 'verified': + return + case 'duplicate': + return + case 'failed': + return + } + } + + const getStatusText = (result: VerificationResult) => { + switch (result.status) { + case 'pending': + return '等待中' + case 'checking': + return '检查重复...' + case 'verifying': + return '验活中...' + case 'verified': + return '验活成功' + case 'duplicate': + return '重复凭据' + case 'failed': + if (result.rollbackStatus === 'success') return '验活失败(已排除)' + if (result.rollbackStatus === 'failed') return '验活失败(未排除)' + return '验活失败(未创建)' + } + } + + return ( + { + // 关闭时清空表单(但不在导入过程中清空) + if (!newOpen && !importing) { + resetForm() + } + onOpenChange(newOpen) + }} + > + + + 批量导入凭据(自动验活) + + +
+
+ + +
+ + + + + + +
+ + + + diff --git a/tools/test_empty_content.py b/tools/test_empty_content.py new file mode 100644 index 0000000..5269724 --- /dev/null +++ b/tools/test_empty_content.py @@ -0,0 +1,145 @@ +#!/usr/bin/env python3 +"""测试空消息内容和 prefill 处理的改进""" + +import json +import requests + +BASE_URL = "http://localhost:8080" +API_KEY = "test-key" + +def safe_print_response(response): + """安全打印响应,处理非 JSON 情况""" + try: + data = response.json() + print(f"响应: {json.dumps(data, indent=2, ensure_ascii=False)}") + return data + except (json.JSONDecodeError, ValueError): + print(f"响应 (非 JSON): {response.text}") + return None + +def test_empty_content(): + """测试空消息内容应返回 400 错误""" + print("测试 1: 空消息内容") + response = requests.post( + f"{BASE_URL}/v1/messages", + headers={ + "x-api-key": API_KEY, + "anthropic-version": "2023-06-01", + "content-type": "application/json", + }, + json={ + "model": "claude-sonnet-4", + "max_tokens": 1024, + "messages": [ + {"role": "user", "content": ""} + ] + } + ) + print(f"状态码: {response.status_code}") + data = safe_print_response(response) + assert response.status_code == 400, "应返回 400 错误" + if data: + assert "消息内容为空" in data.get("error", {}).get("message", ""), "错误消息应包含'消息内容为空'" + print("✓ 测试通过\n") + +def test_empty_text_blocks(): + """测试仅包含空白文本块的消息""" + print("测试 2: 仅包含空白文本块") + response = requests.post( + f"{BASE_URL}/v1/messages", + headers={ + "x-api-key": API_KEY, + "anthropic-version": "2023-06-01", + "content-type": "application/json", + }, + json={ + "model": "claude-sonnet-4", + "max_tokens": 1024, + "messages": [ + { + "role": "user", + "content": [ + {"type": "text", "text": " "}, + {"type": "text", "text": "\n\t"} + ] + } + ] + } + ) + print(f"状态码: {response.status_code}") + data = safe_print_response(response) + assert response.status_code == 400, "应返回 400 错误" + if data: + assert "消息内容为空" in data.get("error", {}).get("message", ""), "错误消息应包含'消息内容为空'" + print("✓ 测试通过\n") + +def test_prefill_with_empty_user(): + """测试 prefill 场景下空 user 消息""" + print("测试 3: Prefill 场景下空 user 消息") + response = requests.post( + f"{BASE_URL}/v1/messages", + headers={ + "x-api-key": API_KEY, + "anthropic-version": "2023-06-01", + "content-type": "application/json", + }, + json={ + "model": "claude-sonnet-4", + "max_tokens": 1024, + "messages": [ + {"role": "user", "content": ""}, + {"role": "assistant", "content": "Hi there"} + ] + } + ) + print(f"状态码: {response.status_code}") + data = safe_print_response(response) + assert response.status_code == 400, "应返回 400 错误" + if data: + assert "消息内容为空" in data.get("error", {}).get("message", ""), "错误消息应包含'消息内容为空'" + print("✓ 测试通过\n") + +def test_valid_message(): + """测试正常消息应该成功""" + print("测试 4: 正常消息(对照组)") + response = requests.post( + f"{BASE_URL}/v1/messages", + headers={ + "x-api-key": API_KEY, + "anthropic-version": "2023-06-01", + "content-type": "application/json", + }, + json={ + "model": "claude-sonnet-4", + "max_tokens": 50, + "messages": [ + {"role": "user", "content": "Say 'test' only"} + ] + } + ) + print(f"状态码: {response.status_code}") + if response.status_code == 200: + print("✓ 测试通过:正常消息处理成功\n") + else: + safe_print_response(response) + print() + +if __name__ == "__main__": + print("=" * 60) + print("空消息内容验证测试") + print("=" * 60 + "\n") + + try: + test_empty_content() + test_empty_text_blocks() + test_prefill_with_empty_user() + test_valid_message() + print("=" * 60) + print("所有测试通过!") + print("=" * 60) + except AssertionError as e: + print(f"\n✗ 测试失败: {e}") + except requests.exceptions.ConnectionError: + print("\n✗ 无法连接到服务器,请确保服务正在运行") + except Exception as e: + print(f"\n✗ 发生错误: {e}")