commit 84c66ccaa7893d1195b355c9cc400212d2ea9914 Author: Echo <1711788888@qq.com> Date: Thu Mar 5 21:28:41 2026 +0800 :art: 增加并发访问 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}")